getty: don't accept ancient '#' and '@' as backspace/kill line, it only confuses people. (Alexander Griesser
No binary (build failed)
—
(log yok)
| @@ -0,0 +1,474 @@ | |
| + | <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> |
| + | <html><head> |
| + | <!-- saved from http://www.win.tue.nl/~aeb/linux/lk/lk-10.html --> |
| + | <meta name="GENERATOR" content="SGML-Tools 1.0.9"><title>The Linux kernel: Processes</title> |
| + | </head> |
| + | <body> |
| + | <hr> |
| + | <h2><a name="s10">10. Processes</a></h2> |
| + | |
| + | <p>Before looking at the Linux implementation, first a general Unix |
| + | description of threads, processes, process groups and sessions. |
| + | </p><p>A session contains a number of process groups, and a process group |
| + | contains a number of processes, and a process contains a number |
| + | of threads. |
| + | </p><p>A session can have a controlling tty. |
| + | At most one process group in a session can be a foreground process group. |
| + | An interrupt character typed on a tty ("Teletype", i.e., terminal) |
| + | causes a signal to be sent to all members of the foreground process group |
| + | in the session (if any) that has that tty as controlling tty. |
| + | </p><p>All these objects have numbers, and we have thread IDs, process IDs, |
| + | process group IDs and session IDs. |
| + | </p><p> |
| + | </p><h2><a name="ss10.1">10.1 Processes</a> |
| + | </h2> |
| + | |
| + | <p> |
| + | </p><h3>Creation</h3> |
| + | |
| + | <p>A new process is traditionally started using the <code>fork()</code> |
| + | system call: |
| + | </p><blockquote> |
| + | <pre>pid_t p; |
| + | |
| + | p = fork(); |
| + | if (p == (pid_t) -1) |
| + | /* ERROR */ |
| + | else if (p == 0) |
| + | /* CHILD */ |
| + | else |
| + | /* PARENT */ |
| + | </pre> |
| + | </blockquote> |
| + | <p>This creates a child as a duplicate of its parent. |
| + | Parent and child are identical in almost all respects. |
| + | In the code they are distinguished by the fact that the parent |
| + | learns the process ID of its child, while <code>fork()</code> |
| + | returns 0 in the child. (It can find the process ID of its |
| + | parent using the <code>getppid()</code> system call.) |
| + | </p><p> |
| + | </p><h3>Termination</h3> |
| + | |
| + | <p>Normal termination is when the process does |
| + | </p><blockquote> |
| + | <pre>exit(n); |
| + | </pre> |
| + | </blockquote> |
| + | |
| + | or |
| + | <blockquote> |
| + | <pre>return n; |
| + | </pre> |
| + | </blockquote> |
| + | |
| + | from its <code>main()</code> procedure. It returns the single byte <code>n</code> |
| + | to its parent. |
| + | <p>Abnormal termination is usually caused by a signal. |
| + | </p><p> |
| + | </p><h3>Collecting the exit code. Zombies</h3> |
| + | |
| + | <p>The parent does |
| + | </p><blockquote> |
| + | <pre>pid_t p; |
| + | int status; |
| + | |
| + | p = wait(&status); |
| + | </pre> |
| + | </blockquote> |
| + | |
| + | and collects two bytes: |
| + | <p> |
| + | <figure> |
| + | <eps file="absent"> |
| + | <img src="ctty_files/exit_status.png"> |
| + | </eps> |
| + | </figure></p><p>A process that has terminated but has not yet been waited for |
| + | is a <i>zombie</i>. It need only store these two bytes: |
| + | exit code and reason for termination. |
| + | </p><p>On the other hand, if the parent dies first, <code>init</code> (process 1) |
| + | inherits the child and becomes its parent. |
| + | </p><p> |
| + | </p><h3>Signals</h3> |
| + | |
| + | <p> |
| + | </p><h3>Stopping</h3> |
| + | |
| + | <p>Some signals cause a process to stop: |
| + | <code>SIGSTOP</code> (stop!), |
| + | <code>SIGTSTP</code> (stop from tty: probably ^Z was typed), |
| + | <code>SIGTTIN</code> (tty input asked by background process), |
| + | <code>SIGTTOU</code> (tty output sent by background process, and this was |
| + | disallowed by <code>stty tostop</code>). |
| + | </p><p>Apart from ^Z there also is ^Y. The former stops the process |
| + | when it is typed, the latter stops it when it is read. |
| + | </p><p>Signals generated by typing the corresponding character on some tty |
| + | are sent to all processes that are in the foreground process group |
| + | of the session that has that tty as controlling tty. (Details below.) |
| + | </p><p>If a process is being traced, every signal will stop it. |
| + | </p><p> |
| + | </p><h3>Continuing</h3> |
| + | |
| + | <p><code>SIGCONT</code>: continue a stopped process. |
| + | </p><p> |
| + | </p><h3>Terminating</h3> |
| + | |
| + | <p><code>SIGKILL</code> (die! now!), |
| + | <code>SIGTERM</code> (please, go away), |
| + | <code>SIGHUP</code> (modem hangup), |
| + | <code>SIGINT</code> (^C), |
| + | <code>SIGQUIT</code> (^\), etc. |
| + | Many signals have as default action to kill the target. |
| + | (Sometimes with an additional core dump, when such is |
| + | allowed by rlimit.) |
| + | The signals <code>SIGCHLD</code> and <code>SIGWINCH</code> |
| + | are ignored by default. |
| + | All except <code>SIGKILL</code> and <code>SIGSTOP</code> can be |
| + | caught or ignored or blocked. |
| + | For details, see <code>signal(7)</code>. |
| + | </p><p> |
| + | </p><h2><a name="ss10.2">10.2 Process groups</a> |
| + | </h2> |
| + | |
| + | <p>Every process is member of a unique <i>process group</i>, |
| + | identified by its <i>process group ID</i>. |
| + | (When the process is created, it becomes a member of the process group |
| + | of its parent.) |
| + | By convention, the process group ID of a process group |
| + | equals the process ID of the first member of the process group, |
| + | called the <i>process group leader</i>. |
| + | A process finds the ID of its process group using the system call |
| + | <code>getpgrp()</code>, or, equivalently, <code>getpgid(0)</code>. |
| + | One finds the process group ID of process <code>p</code> using |
| + | <code>getpgid(p)</code>. |
| + | </p><p>One may use the command <code>ps j</code> to see PPID (parent process ID), |
| + | PID (process ID), PGID (process group ID) and SID (session ID) |
| + | of processes. With a shell that does not know about job control, |
| + | like <code>ash</code>, each of its children will be in the same session |
| + | and have the same process group as the shell. With a shell that knows |
| + | about job control, like <code>bash</code>, the processes of one pipeline. like |
| + | </p><blockquote> |
| + | <pre>% cat paper | ideal | pic | tbl | eqn | ditroff > out |
| + | </pre> |
| + | </blockquote> |
| + | |
| + | form a single process group. |
| + | <p> |
| + | </p><h3>Creation</h3> |
| + | |
| + | <p>A process <code>pid</code> is put into the process group <code>pgid</code> by |
| + | </p><blockquote> |
| + | <pre>setpgid(pid, pgid); |
| + | </pre> |
| + | </blockquote> |
| + | |
| + | If <code>pgid == pid</code> or <code>pgid == 0</code> then this creates |
| + | a new process group with process group leader <code>pid</code>. |
| + | Otherwise, this puts <code>pid</code> into the already existing |
| + | process group <code>pgid</code>. |
| + | A zero <code>pid</code> refers to the current process. |
| + | The call <code>setpgrp()</code> is equivalent to <code>setpgid(0,0)</code>. |
| + | <p> |
| + | </p><h3>Restrictions on setpgid()</h3> |
| + | |
| + | <p>The calling process must be <code>pid</code> itself, or its parent, |
| + | and the parent can only do this before <code>pid</code> has done |
| + | <code>exec()</code>, and only when both belong to the same session. |
| + | It is an error if process <code>pid</code> is a session leader |
| + | (and this call would change its <code>pgid</code>). |
| + | </p><p> |
| + | </p><h3>Typical sequence</h3> |
| + | |
| + | <p> |
| + | </p><blockquote> |
| + | <pre>p = fork(); |
| + | if (p == (pid_t) -1) { |
| + | /* ERROR */ |
| + | } else if (p == 0) { /* CHILD */ |
| + | setpgid(0, pgid); |
| + | ... |
| + | } else { /* PARENT */ |
| + | setpgid(p, pgid); |
| + | ... |
| + | } |
| + | </pre> |
| + | </blockquote> |
| + | |
| + | This ensures that regardless of whether parent or child is scheduled |
| + | first, the process group setting is as expected by both. |
| + | <p> |
| + | </p><h3>Signalling and waiting</h3> |
| + | |
| + | <p>One can signal all members of a process group: |
| + | </p><blockquote> |
| + | <pre>killpg(pgrp, sig); |
| + | </pre> |
| + | </blockquote> |
| + | <p>One can wait for children in ones own process group: |
| + | </p><blockquote> |
| + | <pre>waitpid(0, &status, ...); |
| + | </pre> |
| + | </blockquote> |
| + | |
| + | or in a specified process group: |
| + | <blockquote> |
| + | <pre>waitpid(-pgrp, &status, ...); |
| + | </pre> |
| + | </blockquote> |
| + | <p> |
| + | </p><h3>Foreground process group</h3> |
| + | |
| + | <p>Among the process groups in a session at most one can be |
| + | the <i>foreground process group</i> of that session. |
| + | The tty input and tty signals (signals generated by ^C, ^Z, etc.) |
| + | go to processes in this foreground process group. |
| + | </p><p>A process can determine the foreground process group in its session |
| + | using <code>tcgetpgrp(fd)</code>, where <code>fd</code> refers to its |
| + | controlling tty. If there is none, this returns a random value |
| + | larger than 1 that is not a process group ID. |
| + | </p><p>A process can set the foreground process group in its session |
| + | using <code>tcsetpgrp(fd,pgrp)</code>, where <code>fd</code> refers to its |
| + | controlling tty, and <code>pgrp</code> is a process group in the |
| + | its session, and this session still is associated to the controlling |
| + | tty of the calling process. |
| + | </p><p>How does one get <code>fd</code>? By definition, <code>/dev/tty</code> |
| + | refers to the controlling tty, entirely independent of redirects |
| + | of standard input and output. (There is also the function |
| + | <code>ctermid()</code> to get the name of the controlling terminal. |
| + | On a POSIX standard system it will return <code>/dev/tty</code>.) |
| + | Opening the name of the |
| + | controlling tty gives a file descriptor <code>fd</code>. |
| + | </p><p> |
| + | </p><h3>Background process groups</h3> |
| + | |
| + | <p>All process groups in a session that are not foreground |
| + | process group are <i>background process groups</i>. |
| + | Since the user at the keyboard is interacting with foreground |
| + | processes, background processes should stay away from it. |
| + | When a background process reads from the terminal it gets |
| + | a SIGTTIN signal. Normally, that will stop it, the job control shell |
| + | notices and tells the user, who can say <code>fg</code> to continue |
| + | this background process as a foreground process, and then this |
| + | process can read from the terminal. But if the background process |
| + | ignores or blocks the SIGTTIN signal, or if its process group |
| + | is orphaned (see below), then the read() returns an EIO error, |
| + | and no signal is sent. (Indeed, the idea is to tell the process |
| + | that reading from the terminal is not allowed right now. |
| + | If it wouldn't see the signal, then it will see the error return.) |
| + | </p><p>When a background process writes to the terminal, it may get |
| + | a SIGTTOU signal. May: namely, when the flag that this must happen |
| + | is set (it is off by default). One can set the flag by |
| + | </p><blockquote> |
| + | <pre>% stty tostop |
| + | </pre> |
| + | </blockquote> |
| + | |
| + | and clear it again by |
| + | <blockquote> |
| + | <pre>% stty -tostop |
| + | </pre> |
| + | </blockquote> |
| + | |
| + | and inspect it by |
| + | <blockquote> |
| + | <pre>% stty -a |
| + | </pre> |
| + | </blockquote> |
| + | |
| + | Again, if TOSTOP is set but the background process ignores or blocks |
| + | the SIGTTOU signal, or if its process group is orphaned (see below), |
| + | then the write() returns an EIO error, and no signal is sent. |
| + | <p> |
| + | </p><h3>Orphaned process groups</h3> |
| + | |
| + | <p>The process group leader is the first member of the process group. |
| + | It may terminate before the others, and then the process group is |
| + | without leader. |
| + | </p><p>A process group is called <i>orphaned</i> when <i>the |
| + | parent of every member is either in the process group |
| + | or outside the session</i>. |
| + | In particular, the process group of the session leader |
| + | is always orphaned. |
| + | </p><p>If termination of a process causes a process group to become |
| + | orphaned, and some member is stopped, then all are sent first SIGHUP |
| + | and then SIGCONT. |
| + | </p><p>The idea is that perhaps the parent of the process group leader |
| + | is a job control shell. (In the same session but a different |
| + | process group.) As long as this parent is alive, it can |
| + | handle the stopping and starting of members in the process group. |
| + | When it dies, there may be nobody to continue stopped processes. |
| + | Therefore, these stopped processes are sent SIGHUP, so that they |
| + | die unless they catch or ignore it, and then SIGCONT to continue them. |
| + | </p><p>Note that the process group of the session leader is already |
| + | orphaned, so no signals are sent when the session leader dies. |
| + | </p><p>Note also that a process group can become orphaned in two ways |
| + | by termination of a process: either it was a parent and not itself |
| + | in the process group, or it was the last element of the process group |
| + | with a parent outside but in the same session. |
| + | Furthermore, that a process group can become orphaned |
| + | other than by termination of a process, namely when some |
| + | member is moved to a different process group. |
| + | </p><p> |
| + | </p><h2><a name="ss10.3">10.3 Sessions</a> |
| + | </h2> |
| + | |
| + | <p>Every process group is in a unique <i>session</i>. |
| + | (When the process is created, it becomes a member of the session |
| + | of its parent.) |
| + | By convention, the session ID of a session |
| + | equals the process ID of the first member of the session, |
| + | called the <i>session leader</i>. |
| + | A process finds the ID of its session using the system call |
| + | <code>getsid()</code>. |
| + | </p><p>Every session may have a <i>controlling tty</i>, |
| + | that then also is called the controlling tty of each of |
| + | its member processes. |
| + | A file descriptor for the controlling tty is obtained by |
| + | opening <code>/dev/tty</code>. (And when that fails, there was no |
| + | controlling tty.) Given a file descriptor for the controlling tty, |
| + | one may obtain the SID using <code>tcgetsid(fd)</code>. |
| + | </p><p>A session is often set up by a login process. The terminal |
| + | on which one is logged in then becomes the controlling tty |
| + | of the session. All processes that are descendants of the |
| + | login process will in general be members of the session. |
| + | </p><p> |
| + | </p><h3>Creation</h3> |
| + | |
| + | <p>A new session is created by |
| + | </p><blockquote> |
| + | <pre>pid = setsid(); |
| + | </pre> |
| + | </blockquote> |
| + | |
| + | This is allowed only when the current process is not a process group leader. |
| + | In order to be sure of that we fork first: |
| + | <blockquote> |
| + | <pre>p = fork(); |
| + | if (p) exit(0); |
| + | pid = setsid(); |
| + | </pre> |
| + | </blockquote> |
| + | |
| + | The result is that the current process (with process ID <code>pid</code>) |
| + | becomes session leader of a new session with session ID <code>pid</code>. |
| + | Moreover, it becomes process group leader of a new process group. |
| + | Both session and process group contain only the single process <code>pid</code>. |
| + | Furthermore, this process has no controlling tty. |
| + | <p>The restriction that the current process must not be a process group leader |
| + | is needed: otherwise its PID serves as PGID of some existing process group |
| + | and cannot be used as the PGID of a new process group. |
| + | </p><p> |
| + | </p><h3>Getting a controlling tty</h3> |
| + | |
| + | <p>How does one get a controlling terminal? Nobody knows, |
| + | this is a great mystery. |
| + | </p><p>The System V approach is that the first tty opened by the process |
| + | becomes its controlling tty. |
| + | </p><p>The BSD approach is that one has to explicitly call |
| + | </p><blockquote> |
| + | <pre>ioctl(fd, TIOCSCTTY, ...); |
| + | </pre> |
| + | </blockquote> |
| + | |
| + | to get a controlling tty. |
| + | <p>Linux tries to be compatible with both, as always, and this |
| + | results in a very obscure complex of conditions. Roughly: |
| + | </p><p>The <code>TIOCSCTTY</code> ioctl will give us a controlling tty, |
| + | provided that (i) the current process is a session leader, |
| + | and (ii) it does not yet have a controlling tty, and |
| + | (iii) maybe the tty should not already control some other session; |
| + | if it does it is an error if we aren't root, or we steal the tty |
| + | if we are all-powerful. |
| + | </p><p>Opening some terminal will give us a controlling tty, |
| + | provided that (i) the current process is a session leader, and |
| + | (ii) it does not yet have a controlling tty, and |
| + | (iii) the tty does not already control some other session, and |
| + | (iv) the open did not have the <code>O_NOCTTY</code> flag, and |
| + | (v) the tty is not the foreground VT, and |
| + | (vi) the tty is not the console, and |
| + | (vii) maybe the tty should not be master or slave pty. |
| + | </p><p> |
| + | </p><h3>Getting rid of a controlling tty</h3> |
| + | |
| + | <p>If a process wants to continue as a daemon, it must detach itself |
| + | from its controlling tty. Above we saw that <code>setsid()</code> |
| + | will remove the controlling tty. Also the ioctl TIOCNOTTY does this. |
| + | Moreover, in order not to get a controlling tty again as soon as it |
| + | opens a tty, the process has to fork once more, to assure that it |
| + | is not a session leader. Typical code fragment: |
| + | </p><p> |
| + | </p><pre> if ((fork()) != 0) |
| + | exit(0); |
| + | setsid(); |
| + | if ((fork()) != 0) |
| + | exit(0); |
| + | </pre> |
| + | <p>See also <code>daemon(3)</code>. |
| + | </p><p> |
| + | </p><h3>Disconnect</h3> |
| + | |
| + | <p>If the terminal goes away by modem hangup, and the line was not local, |
| + | then a SIGHUP is sent to the session leader. |
| + | Any further reads from the gone terminal return EOF. |
| + | (Or possibly -1 with <code>errno</code> set to EIO.) |
| + | </p><p>If the terminal is the slave side of a pseudotty, and the master side |
| + | is closed (for the last time), then a SIGHUP is sent to the foreground |
| + | process group of the slave side. |
| + | </p><p>When the session leader dies, a SIGHUP is sent to all processes |
| + | in the foreground process group. Moreover, the terminal stops being |
| + | the controlling terminal of this session (so that it can become |
| + | the controlling terminal of another session). |
| + | </p><p>Thus, if the terminal goes away and the session leader is |
| + | a job control shell, then it can handle things for its descendants, |
| + | e.g. by sending them again a SIGHUP. |
| + | If on the other hand the session leader is an innocent process |
| + | that does not catch SIGHUP, it will die, and all foreground processes |
| + | get a SIGHUP. |
| + | </p><p> |
| + | </p><h2><a name="ss10.4">10.4 Threads</a> |
| + | </h2> |
| + | |
| + | <p>A process can have several threads. New threads (with the same PID |
| + | as the parent thread) are started using the <code>clone</code> system |
| + | call using the <code>CLONE_THREAD</code> flag. Threads are distinguished |
| + | by a <i>thread ID</i> (TID). An ordinary process has a single thread |
| + | with TID equal to PID. The system call <code>gettid()</code> returns the |
| + | TID. The system call <code>tkill()</code> sends a signal to a single thread. |
| + | </p><p>Example: a process with two threads. Both only print PID and TID and exit. |
| + | (Linux 2.4.19 or later.) |
| + | </p><pre>% cat << EOF > gettid-demo.c |
| + | #include <unistd.h> |
| + | #include <sys/types.h> |
| + | #define CLONE_SIGHAND 0x00000800 |
| + | #define CLONE_THREAD 0x00010000 |
| + | #include <linux/unistd.h> |
| + | #include <errno.h> |
| + | _syscall0(pid_t,gettid) |
| + | |
| + | int thread(void *p) { |
| + | printf("thread: %d %d\n", gettid(), getpid()); |
| + | } |
| + | |
| + | main() { |
| + | unsigned char stack[4096]; |
| + | int i; |
| + | |
| + | i = clone(thread, stack+2048, CLONE_THREAD | CLONE_SIGHAND, NULL); |
| + | if (i == -1) |
| + | perror("clone"); |
| + | else |
| + | printf("clone returns %d\n", i); |
| + | printf("parent: %d %d\n", gettid(), getpid()); |
| + | } |
| + | EOF |
| + | % cc -o gettid-demo gettid-demo.c |
| + | % ./gettid-demo |
| + | clone returns 21826 |
| + | parent: 21825 21825 |
| + | thread: 21826 21825 |
| + | % |
| + | </pre> |
| + | <p> |
| + | </p><p> |
| + | </p><hr> |
| + | |
| + | </body></html> |
| @@ -33,7 +33,6 @@ | |
| #include <time.h> | |
| #if ENABLE_FEATURE_WTMP | |
| extern void updwtmp(const char *filename, const struct utmp *ut); | |
| - | static void update_utmp(const char *line); |
| #endif | |
| #endif /* LOGIN_PROCESS */ | |
| @@ -47,6 +46,7 @@ static void update_utmp(const char *line); | |
| /* I doubt there are systems which still need this */ | |
| #undef HANDLE_ALLCAPS | |
| + | #undef ANCIENT_BS_KILL_CHARS |
| #define _PATH_LOGIN "/bin/login" | |
| @@ -76,36 +76,20 @@ static void update_utmp(const char *line); | |
| * When multiple baud rates are specified on the command line, the first one | |
| * we will try is the first one specified. | |
| */ | |
| - | #define FIRST_SPEED 0 |
| - | |
| - | /* Storage for command-line options. */ |
| - | |
| #define MAX_SPEED 10 /* max. nr. of baud rates */ | |
| + | /* Storage for command-line options. */ |
| struct options { | |
| int flags; /* toggle switches, see below */ | |
| unsigned timeout; /* time-out period */ | |
| - | const char *login; /* login program */ |
| - | const char *tty; /* name of tty */ |
| - | const char *initstring; /* modem init string */ |
| - | const char *issue; /* alternative issue file */ |
| + | const char *login; /* login program */ |
| + | const char *tty; /* name of tty */ |
| + | const char *initstring; /* modem init string */ |
| + | const char *issue; /* alternative issue file */ |
| int numspeed; /* number of baud rates to try */ | |
| int speeds[MAX_SPEED]; /* baud rates to be tried */ | |
| }; | |
| - | static const char opt_string[] ALIGN1 = "I:LH:f:hil:mt:wn"; |
| - | #define F_INITSTRING (1<<0) /* initstring is set */ |
| - | #define F_LOCAL (1<<1) /* force local */ |
| - | #define F_FAKEHOST (1<<2) /* force fakehost */ |
| - | #define F_CUSTISSUE (1<<3) /* give alternative issue file */ |
| - | #define F_RTSCTS (1<<4) /* enable RTS/CTS flow control */ |
| - | #define F_ISSUE (1<<5) /* display /etc/issue */ |
| - | #define F_LOGIN (1<<6) /* non-default login program */ |
| - | #define F_PARSE (1<<7) /* process modem status messages */ |
| - | #define F_TIMEOUT (1<<8) /* time out */ |
| - | #define F_WAITCRLF (1<<9) /* wait for CR or LF */ |
| - | #define F_NOPROMPT (1<<10) /* don't ask for login name! */ |
| - | |
| /* Storage for things detected while the login name was read. */ | |
| struct chardata { | |
| unsigned char erase; /* erase character */ | |
| @@ -117,6 +101,7 @@ struct chardata { | |
| #endif | |
| }; | |
| + | |
| /* Initial values for the above. */ | |
| static const struct chardata init_chardata = { | |
| DEF_ERASE, /* default erase character */ | |
| @@ -128,12 +113,25 @@ static const struct chardata init_chardata = { | |
| #endif | |
| }; | |
| - | /* The following is used for understandable diagnostics. */ |
| + | static const char opt_string[] ALIGN1 = "I:LH:f:hil:mt:wn"; |
| + | #define F_INITSTRING (1 << 0) /* -I initstring is set */ |
| + | #define F_LOCAL (1 << 1) /* -L force local */ |
| + | #define F_FAKEHOST (1 << 2) /* -H force fakehost */ |
| + | #define F_CUSTISSUE (1 << 3) /* -f give alternative issue file */ |
| + | #define F_RTSCTS (1 << 4) /* -h enable RTS/CTS flow control */ |
| + | #define F_ISSUE (1 << 5) /* -i display /etc/issue */ |
| + | #define F_LOGIN (1 << 6) /* -l non-default login program */ |
| + | #define F_PARSE (1 << 7) /* -m process modem status messages */ |
| + | #define F_TIMEOUT (1 << 8) /* -t time out */ |
| + | #define F_WAITCRLF (1 << 9) /* -w wait for CR or LF */ |
| + | #define F_NOPROMPT (1 << 10) /* -n don't ask for login name! */ |
| + | |
| /* Fake hostname for ut_host specified on command line. */ | |
| static char *fakehost = NULL; | |
| + | #define line_buf bb_common_bufsiz1 |
| - | /* ... */ |
| + | /* The following is used for understandable diagnostics. */ |
| #ifdef DEBUGGING | |
| #define debug(s) fprintf(dbf,s); fflush(dbf) | |
| #define DEBUGTERM "/dev/ttyp0" | |
| @@ -158,14 +156,14 @@ static int bcode(const char *s) | |
| return 0; | |
| } | |
| - | |
| /* parse_speeds - parse alternate baud rates */ | |
| static void parse_speeds(struct options *op, char *arg) | |
| { | |
| char *cp; | |
| + | /* NB: at least one iteration is always done */ |
| debug("entered parse_speeds\n"); | |
| - | for (cp = strtok(arg, ","); cp != 0; cp = strtok((char *) 0, ",")) { |
| + | while ((cp = strsep(&arg, ",")) != NULL) { |
| op->speeds[op->numspeed] = bcode(cp); | |
| if (op->speeds[op->numspeed] <= 0) | |
| bb_error_msg_and_die("bad speed: %s", cp); | |
| @@ -173,18 +171,19 @@ static void parse_speeds(struct options *op, char *arg) | |
| if (op->numspeed > MAX_SPEED) | |
| bb_error_msg_and_die("too many alternate speeds"); | |
| } | |
| - | debug("exiting parsespeeds\n"); |
| + | debug("exiting parse_speeds\n"); |
| } | |
| - | |
| /* parse_args - parse command-line arguments */ | |
| - | static void parse_args(int argc, char **argv, struct options *op) |
| + | static void parse_args(char **argv, struct options *op) |
| { | |
| char *ts; | |
| + | opt_complementary = "-2"; /* at least 2 args */ |
| op->flags = getopt32(argv, opt_string, | |
| &(op->initstring), &fakehost, &(op->issue), | |
| &(op->login), &ts); | |
| + | argv += optind; |
| if (op->flags & F_INITSTRING) { | |
| const char *p = op->initstring; | |
| char *q; | |
| @@ -202,45 +201,40 @@ static void parse_args(int argc, char **argv, struct options *op) | |
| } | |
| *q = '\0'; | |
| } | |
| - | op->flags ^= F_ISSUE; /* revert flag show /etc/issue */ |
| + | op->flags ^= F_ISSUE; /* invert flag show /etc/issue */ |
| if (op->flags & F_TIMEOUT) { | |
| - | op->timeout = xatoul_range(ts, 1, INT_MAX); |
| + | op->timeout = xatoi_u(ts); |
| } | |
| - | argv += optind; |
| - | argc -= optind; |
| - | debug("after getopt loop\n"); |
| - | if (argc < 2) /* check parameter count */ |
| - | bb_show_usage(); |
| + | debug("after getopt\n"); |
| /* we loosen up a bit and accept both "baudrate tty" and "tty baudrate" */ | |
| + | op->tty = argv[0]; /* tty name */ |
| + | ts = argv[1]; /* baud rate(s) */ |
| if (isdigit(argv[0][0])) { | |
| /* a number first, assume it's a speed (BSD style) */ | |
| - | parse_speeds(op, argv[0]); /* baud rate(s) */ |
| - | op->tty = argv[1]; /* tty name */ |
| - | } else { |
| - | op->tty = argv[0]; /* tty name */ |
| - | parse_speeds(op, argv[1]); /* baud rate(s) */ |
| + | op->tty = ts; /* tty name is in argv[1] */ |
| + | ts = argv[0]; /* baud rate(s) */ |
| } | |
| + | parse_speeds(op, ts); |
| if (argv[2]) | |
| setenv("TERM", argv[2], 1); | |
| - | debug("exiting parseargs\n"); |
| + | debug("exiting parse_args\n"); |
| } | |
| /* open_tty - set up tty as standard { input, output, error } */ | |
| - | static void open_tty(const char *tty, struct termios *tp, int local) |
| + | static void open_tty(const char *tty) |
| { | |
| - | int chdir_to_root = 0; |
| - | |
| /* Set up new standard input, unless we are given an already opened port. */ | |
| if (NOT_LONE_DASH(tty)) { | |
| struct stat st; | |
| + | int cur_dir_fd; |
| int fd; | |
| /* Sanity checks... */ | |
| + | cur_dir_fd = xopen(".", O_DIRECTORY | O_NONBLOCK); |
| xchdir("/dev"); | |
| - | chdir_to_root = 1; |
| xstat(tty, &st); | |
| if ((st.st_mode & S_IFMT) != S_IFCHR) | |
| bb_error_msg_and_die("%s: not a character device", tty); | |
| @@ -248,9 +242,23 @@ static void open_tty(const char *tty, struct termios *tp, int local) | |
| /* Open the tty as standard input. */ | |
| debug("open(2)\n"); | |
| fd = xopen(tty, O_RDWR | O_NONBLOCK); | |
| + | |
| + | /* Restore current directory */ |
| + | fchdir(cur_dir_fd); |
| + | |
| + | /* Open the tty as standard input, continued */ |
| xdup2(fd, 0); | |
| + | /* fd is >= cur_dir_fd, and cur_dir_fd gets closed too here: */ |
| while (fd > 2) | |
| close(fd--); | |
| + | |
| + | /* Set proper protections and ownership. Mode 0622 |
| + | * is suitable for SYSV < 4 because /bin/login does not change |
| + | * protections. SunOS 4 login will change the protections to 0620 |
| + | * (write access for group tty) after the login has succeeded. |
| + | */ |
| + | fchown(0, 0, 0); /* 0:0 */ |
| + | fchmod(0, 0622); /* crw--w--w- */ |
| } else { | |
| /* | |
| * Standard input should already be connected to an open port. Make | |
| @@ -259,71 +267,6 @@ static void open_tty(const char *tty, struct termios *tp, int local) | |
| if ((fcntl(0, F_GETFL) & O_RDWR) != O_RDWR) | |
| bb_error_msg_and_die("stdin is not open for read/write"); | |
| } | |
| - | |
| - | /* Replace current standard output/error fd's with new ones */ |
| - | debug("duping\n"); |
| - | xdup2(0, 1); |
| - | xdup2(0, 2); |
| - | |
| - | /* |
| - | * The following ioctl will fail if stdin is not a tty, but also when |
| - | * there is noise on the modem control lines. In the latter case, the |
| - | * common course of action is (1) fix your cables (2) give the modem more |
| - | * time to properly reset after hanging up. SunOS users can achieve (2) |
| - | * by patching the SunOS kernel variable "zsadtrlow" to a larger value; |
| - | * 5 seconds seems to be a good value. |
| - | */ |
| - | ioctl_or_perror_and_die(0, TCGETS, tp, "%s: TCGETS", tty); |
| - | |
| - | /* |
| - | * It seems to be a terminal. Set proper protections and ownership. Mode |
| - | * 0622 is suitable for SYSV <4 because /bin/login does not change |
| - | * protections. SunOS 4 login will change the protections to 0620 (write |
| - | * access for group tty) after the login has succeeded. |
| - | */ |
| - | |
| - | #ifdef DEBIAN |
| - | #warning Debian /dev/vcs[a]NN hack is deprecated and will be removed |
| - | { |
| - | /* tty to root.dialout 660 */ |
| - | struct group *gr; |
| - | int id; |
| - | |
| - | gr = getgrnam("dialout"); |
| - | id = gr ? gr->gr_gid : 0; |
| - | chown(tty, 0, id); |
| - | chmod(tty, 0660); |
| - | |
| - | /* vcs,vcsa to root.sys 600 */ |
| - | if (!strncmp(tty, "tty", 3) && isdigit(tty[3])) { |
| - | char *vcs, *vcsa; |
| - | |
| - | vcs = xstrdup(tty); |
| - | vcsa = xmalloc(strlen(tty) + 2); |
| - | strcpy(vcs, "vcs"); |
| - | strcpy(vcs + 3, tty + 3); |
| - | strcpy(vcsa, "vcsa"); |
| - | strcpy(vcsa + 4, tty + 3); |
| - | |
| - | gr = getgrnam("sys"); |
| - | id = gr ? gr->gr_gid : 0; |
| - | chown(vcs, 0, id); |
| - | chmod(vcs, 0600); |
| - | chown(vcsa, 0, id); |
| - | chmod(vcs, 0600); |
| - | |
| - | free(vcs); |
| - | free(vcsa); |
| - | } |
| - | } |
| - | #else |
| - | if (NOT_LONE_DASH(tty)) { |
| - | chown(tty, 0, 0); /* 0:0 */ |
| - | chmod(tty, 0622); /* crw--w--w- */ |
| - | } |
| - | #endif |
| - | if (chdir_to_root) |
| - | xchdir("/"); |
| } | |
| /* termios_init - initialize termios settings */ | |
| @@ -351,17 +294,13 @@ static void termios_init(struct termios *tp, int speed, struct options *op) | |
| tp->c_cc[VTIME] = 0; | |
| /* Optionally enable hardware flow control */ | |
| - | |
| - | #ifdef CRTSCTS |
| + | #ifdef CRTSCTS |
| if (op->flags & F_RTSCTS) | |
| tp->c_cflag |= CRTSCTS; | |
| #endif | |
| ioctl(0, TCSETS, tp); | |
| - | /* go to blocking input even in local mode */ |
| - | ndelay_off(0); |
| - | |
| debug("term_io 2\n"); | |
| } | |
| @@ -393,26 +332,24 @@ static void auto_baud(char *buf, unsigned size_buf, struct termios *tp) | |
| * Use 7-bit characters, don't block if input queue is empty. Errors will | |
| * be dealt with later on. | |
| */ | |
| - | |
| iflag = tp->c_iflag; | |
| tp->c_iflag |= ISTRIP; /* enable 8th-bit stripping */ | |
| vmin = tp->c_cc[VMIN]; | |
| - | tp->c_cc[VMIN] = 0; /* don't block if queue empty */ |
| + | tp->c_cc[VMIN] = 0; /* don't block if queue empty */ |
| ioctl(0, TCSETS, tp); | |
| /* | |
| * Wait for a while, then read everything the modem has said so far and | |
| * try to extract the speed of the dial-in call. | |
| */ | |
| - | |
| sleep(1); | |
| - | nread = read(0, buf, size_buf - 1); |
| + | nread = safe_read(0, buf, size_buf - 1); |
| if (nread > 0) { | |
| buf[nread] = '\0'; | |
| for (bp = buf; bp < buf + nread; bp++) { | |
| - | if (isascii(*bp) && isdigit(*bp)) { |
| + | if (isdigit(*bp)) { |
| speed = bcode(bp); | |
| - | if (speed) { |
| + | if (speed > 0) { |
| tp->c_cflag &= ~CBAUD; | |
| tp->c_cflag |= speed; | |
| } | |
| @@ -420,25 +357,13 @@ static void auto_baud(char *buf, unsigned size_buf, struct termios *tp) | |
| } | |
| } | |
| } | |
| - | /* Restore terminal settings. Errors will be dealt with later on. */ |
| + | /* Restore terminal settings. Errors will be dealt with later on. */ |
| tp->c_iflag = iflag; | |
| tp->c_cc[VMIN] = vmin; | |
| ioctl(0, TCSETS, tp); | |
| } | |
| - | /* next_speed - select next baud rate */ |
| - | static void next_speed(struct termios *tp, struct options *op) |
| - | { |
| - | static int baud_index = FIRST_SPEED; /* current speed index */ |
| - | |
| - | baud_index = (baud_index + 1) % op->numspeed; |
| - | tp->c_cflag &= ~CBAUD; |
| - | tp->c_cflag |= op->speeds[baud_index]; |
| - | ioctl(0, TCSETS, tp); |
| - | } |
| - | |
| - | |
| /* do_prompt - show login prompt, optionally preceded by /etc/issue contents */ | |
| static void do_prompt(struct options *op, struct termios *tp) | |
| { | |
| @@ -449,9 +374,9 @@ static void do_prompt(struct options *op, struct termios *tp) | |
| } | |
| #ifdef HANDLE_ALLCAPS | |
| - | /* caps_lock - string contains upper case without lower case */ |
| + | /* all_is_upcase - string contains upper case without lower case */ |
| /* returns 1 if true, 0 if false */ | |
| - | static int caps_lock(const char *s) |
| + | static int all_is_upcase(const char *s) |
| { | |
| while (*s) | |
| if (islower(*s++)) | |
| @@ -460,8 +385,8 @@ static int caps_lock(const char *s) | |
| } | |
| #endif | |
| - | /* get_logname - get user name, establish parity, speed, erase, kill, eol */ |
| - | /* return NULL on failure, logname on success */ |
| + | /* get_logname - get user name, establish parity, speed, erase, kill, eol; |
| + | * return NULL on BREAK, logname on success */ |
| static char *get_logname(char *logname, unsigned size_logname, | |
| struct options *op, struct chardata *cp, struct termios *tp) | |
| { | |
| @@ -477,26 +402,19 @@ static char *get_logname(char *logname, unsigned size_logname, | |
| "\210\240\210", /* no parity */ | |
| }; | |
| - | /* Initialize kill, erase, parity etc. (also after switching speeds). */ |
| - | |
| - | *cp = init_chardata; |
| + | /* NB: *cp is pre-initialized with init_chardata */ |
| /* Flush pending input (esp. after parsing or switching the baud rate). */ | |
| - | |
| sleep(1); | |
| ioctl(0, TCFLSH, TCIFLUSH); | |
| /* Prompt for and read a login name. */ | |
| - | |
| logname[0] = '\0'; | |
| while (!logname[0]) { | |
| - | |
| /* Write issue file and prompt, with "parity" bit == 0. */ | |
| - | |
| do_prompt(op, tp); | |
| /* Read name, watch for break, parity, erase, kill, end-of-line. */ | |
| - | |
| bp = logname; | |
| cp->eol = '\0'; | |
| while (cp->eol == '\0') { | |
| @@ -508,7 +426,8 @@ static char *get_logname(char *logname, unsigned size_logname, | |
| bb_perror_msg_and_die("%s: read", op->tty); | |
| } | |
| - | /* Do BREAK handling elsewhere. */ |
| + | /* BREAK. If we have speeds to try, |
| + | * return NULL (will switch speeds and return here) */ |
| if (c == '\0' && op->numspeed > 1) | |
| return NULL; | |
| @@ -535,18 +454,22 @@ static char *get_logname(char *logname, unsigned size_logname, | |
| break; | |
| case BS: | |
| case DEL: | |
| + | #ifdef ANCIENT_BS_KILL_CHARS |
| case '#': | |
| + | #endif |
| cp->erase = ascval; /* set erase character */ | |
| if (bp > logname) { | |
| - | write(1, erase[cp->parity], 3); |
| + | full_write(1, erase[cp->parity], 3); |
| bp--; | |
| } | |
| break; | |
| case CTL('U'): | |
| + | #ifdef ANCIENT_BS_KILL_CHARS |
| case '@': | |
| + | #endif |
| cp->kill = ascval; /* set kill character */ | |
| while (bp > logname) { | |
| - | write(1, erase[cp->parity], 3); |
| + | full_write(1, erase[cp->parity], 3); |
| bp--; | |
| } | |
| break; | |
| @@ -558,7 +481,7 @@ static char *get_logname(char *logname, unsigned size_logname, | |
| } else if (bp - logname >= size_logname - 1) { | |
| bb_error_msg_and_die("%s: input overrun", op->tty); | |
| } else { | |
| - | write(1, &c, 1); /* echo the character */ |
| + | full_write(1, &c, 1); /* echo the character */ |
| *bp++ = ascval; /* and store it */ | |
| } | |
| break; | |
| @@ -568,7 +491,7 @@ static char *get_logname(char *logname, unsigned size_logname, | |
| /* Handle names with upper case and no lower case. */ | |
| #ifdef HANDLE_ALLCAPS | |
| - | cp->capslock = caps_lock(logname); |
| + | cp->capslock = all_is_upcase(logname); |
| if (cp->capslock) { | |
| for (bp = logname; *bp; bp++) | |
| if (isupper(*bp)) | |
| @@ -641,7 +564,6 @@ static void termios_final(struct options *op, struct termios *tp, struct chardat | |
| ioctl_or_perror_and_die(0, TCSETS, tp, "%s: TCSETS", op->tty); | |
| } | |
| - | |
| #ifdef SYSV_STYLE | |
| #if ENABLE_FEATURE_UTMP | |
| /* update_utmp - update our utmp entry */ | |
| @@ -666,17 +588,16 @@ static void update_utmp(const char *line) | |
| utmpname(_PATH_UTMP); | |
| setutent(); | |
| while ((utp = getutent()) | |
| - | && !(utp->ut_type == INIT_PROCESS && utp->ut_pid == mypid)) |
| - | /* nothing */; |
| + | && !(utp->ut_type == INIT_PROCESS && utp->ut_pid == mypid) |
| + | ) { |
| + | continue; |
| + | } |
| - | if (utp) { |
| + | /* some inits don't initialize utmp... */ |
| + | memset(&ut, 0, sizeof(ut)); |
| + | safe_strncpy(ut.ut_id, line + 3, sizeof(ut.ut_id)); |
| + | if (utp) |
| memcpy(&ut, utp, sizeof(ut)); | |
| - | } else { |
| - | /* some inits don't initialize utmp... */ |
| - | memset(&ut, 0, sizeof(ut)); |
| - | safe_strncpy(ut.ut_id, line + 3, sizeof(ut.ut_id)); |
| - | } |
| - | /* endutent(); */ |
| strcpy(ut.ut_user, "LOGIN"); | |
| safe_strncpy(ut.ut_line, line, sizeof(ut.ut_line)); | |
| @@ -700,42 +621,43 @@ static void update_utmp(const char *line) | |
| #endif /* CONFIG_FEATURE_UTMP */ | |
| #endif /* SYSV_STYLE */ | |
| - | |
| int getty_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | |
| int getty_main(int argc, char **argv) | |
| { | |
| - | int nullfd; |
| - | char *logname = NULL; /* login name, given to /bin/login */ |
| + | int n; |
| + | char *logname; /* login name, given to /bin/login */ |
| /* Merging these into "struct local" may _seem_ to reduce | |
| * parameter passing, but today's gcc will inline | |
| * statics which are called once anyway, so don't do that */ | |
| struct chardata chardata; /* set by get_logname() */ | |
| - | struct termios termios; /* terminal mode bits */ |
| - | struct options options = { |
| - | 0, /* show /etc/issue (SYSV_STYLE) */ |
| - | 0, /* no timeout */ |
| - | _PATH_LOGIN, /* default login program */ |
| - | "tty1", /* default tty line */ |
| - | "", /* modem init string */ |
| + | struct termios termios; /* terminal mode bits */ |
| + | struct options options; |
| + | |
| + | memset(&options, 0, sizeof(options)); |
| + | options.login = _PATH_LOGIN; /* default login program */ |
| + | options.tty = "tty1"; /* default tty line */ |
| + | options.initstring = ""; /* modem init string */ |
| #ifdef ISSUE | |
| - | ISSUE, /* default issue file */ |
| - | #else |
| - | NULL, |
| + | options.issue = ISSUE; /* default issue file */ |
| #endif | |
| - | 0, /* no baud rates known yet */ |
| - | }; |
| /* Already too late because of theoretical | |
| * possibility of getty --help somehow triggered | |
| * inadvertently before we reach this. Oh well. */ | |
| logmode = LOGMODE_NONE; | |
| + | |
| + | /* Create new session, lose controlling tty, if any */ |
| + | /* docs/ctty.htm says: |
| + | * "This is allowed only when the current process |
| + | * is not a process group leader" - is this a problem? */ |
| setsid(); | |
| - | nullfd = xopen(bb_dev_null, O_RDWR); |
| - | /* dup2(nullfd, 0); - no, because of possible "getty - 9600" */ |
| - | /* open_tty() will take care of fd# 0 anyway */ |
| - | dup2(nullfd, 1); |
| - | dup2(nullfd, 2); |
| - | while (nullfd > 2) close(nullfd--); |
| + | |
| + | n = xopen(bb_dev_null, O_RDWR); |
| + | /* dup2(n, 0); - no, because of possible "getty - 9600" */ |
| + | dup2(n, 1); |
| + | dup2(n, 2); |
| + | while (n > 2) |
| + | close(n--); |
| /* We want special flavor of error_msg_and_die */ | |
| die_sleep = 10; | |
| msg_eol = "\r\n"; | |
| @@ -744,69 +666,74 @@ int getty_main(int argc, char **argv) | |
| #ifdef DEBUGGING | |
| dbf = xfopen(DEBUGTERM, "w"); | |
| - | |
| - | { |
| - | int i; |
| - | |
| - | for (i = 1; i < argc; i++) { |
| - | debug(argv[i]); |
| - | debug("\n"); |
| - | } |
| + | for (n = 1; n < argc; n++) { |
| + | debug(argv[n]); |
| + | debug("\n"); |
| } | |
| #endif | |
| /* Parse command-line arguments. */ | |
| - | parse_args(argc, argv, &options); |
| + | parse_args(argv, &options); |
| + | |
| + | debug("calling open_tty\n"); |
| + | /* Open the tty as standard input, if it is not "-" */ |
| + | open_tty(options.tty); |
| + | |
| + | debug("duping\n"); |
| + | ndelay_off(0); |
| + | xdup2(0, 1); |
| + | xdup2(0, 2); |
| + | |
| + | /* |
| + | * The following ioctl will fail if stdin is not a tty, but also when |
| + | * there is noise on the modem control lines. In the latter case, the |
| + | * common course of action is (1) fix your cables (2) give the modem more |
| + | * time to properly reset after hanging up. SunOS users can achieve (2) |
| + | * by patching the SunOS kernel variable "zsadtrlow" to a larger value; |
| + | * 5 seconds seems to be a good value. |
| + | */ |
| + | ioctl_or_perror_and_die(0, TCGETS, &termios, "%s: TCGETS", options.tty); |
| #ifdef SYSV_STYLE | |
| #if ENABLE_FEATURE_UTMP | |
| - | /* Update the utmp file. */ |
| + | /* Update the utmp file */ |
| update_utmp(options.tty); | |
| #endif | |
| #endif | |
| - | debug("calling open_tty\n"); |
| - | /* Open the tty as standard { input, output, error }. */ |
| - | open_tty(options.tty, &termios, options.flags & F_LOCAL); |
| - | |
| #ifdef __linux__ | |
| - | { |
| - | int iv; |
| - | |
| - | iv = getpid(); |
| - | ioctl(0, TIOCSPGRP, &iv); |
| - | } |
| + | /* Make ourself a foreground process group within our session */ |
| + | tcsetpgrp(0, getpid()); |
| + | // /* Forcibly make fd 0 our controlling tty, even if another session |
| + | // * has it as a ctty. (Another session loses ctty). */ |
| + | // ioctl(0, TIOCSCTTY, (void*)1); |
| #endif | |
| + | |
| /* Initialize the termios settings (raw mode, eight-bit, blocking i/o). */ | |
| debug("calling termios_init\n"); | |
| - | termios_init(&termios, options.speeds[FIRST_SPEED], &options); |
| + | termios_init(&termios, options.speeds[0], &options); |
| - | /* write the modem init string and DON'T flush the buffers */ |
| + | /* Write the modem init string and DON'T flush the buffers */ |
| if (options.flags & F_INITSTRING) { | |
| debug("writing init string\n"); | |
| - | write(1, options.initstring, strlen(options.initstring)); |
| - | } |
| - | |
| - | if (!(options.flags & F_LOCAL)) { |
| - | /* go to blocking write mode unless -L is specified */ |
| - | ndelay_off(1); |
| + | full_write(1, options.initstring, strlen(options.initstring)); |
| } | |
| - | /* Optionally detect the baud rate from the modem status message. */ |
| + | /* Optionally detect the baud rate from the modem status message */ |
| debug("before autobaud\n"); | |
| if (options.flags & F_PARSE) | |
| - | auto_baud(bb_common_bufsiz1, sizeof(bb_common_bufsiz1), &termios); |
| + | auto_baud(line_buf, sizeof(line_buf), &termios); |
| - | /* Set the optional timer. */ |
| + | /* Set the optional timer */ |
| if (options.timeout) | |
| alarm(options.timeout); | |
| - | /* optionally wait for CR or LF before writing /etc/issue */ |
| + | /* Optionally wait for CR or LF before writing /etc/issue */ |
| if (options.flags & F_WAITCRLF) { | |
| char ch; | |
| debug("waiting for cr-lf\n"); | |
| - | while (read(0, &ch, 1) == 1) { |
| + | while (safe_read(0, &ch, 1) == 1) { |
| ch &= 0x7f; /* strip "parity bit" */ | |
| #ifdef DEBUGGING | |
| fprintf(dbf, "read %c\n", ch); | |
| @@ -816,31 +743,42 @@ int getty_main(int argc, char **argv) | |
| } | |
| } | |
| + | logname = NULL; |
| chardata = init_chardata; | |
| if (!(options.flags & F_NOPROMPT)) { | |
| - | /* Read the login name. */ |
| - | debug("reading login name\n"); |
| - | logname = get_logname(bb_common_bufsiz1, sizeof(bb_common_bufsiz1), |
| + | /* NB:termios_init already set line speed |
| + | * to options.speeds[0] */ |
| + | int baud_index = 0; |
| + | |
| + | while (1) { |
| + | /* Read the login name. */ |
| + | debug("reading login name\n"); |
| + | logname = get_logname(line_buf, sizeof(line_buf), |
| &options, &chardata, &termios); | |
| - | while (logname == NULL) |
| - | next_speed(&termios, &options); |
| + | if (logname) |
| + | break; |
| + | /* we are here only if options.numspeed > 1 */ |
| + | baud_index = (baud_index + 1) % options.numspeed; |
| + | termios.c_cflag &= ~CBAUD; |
| + | termios.c_cflag |= options.speeds[baud_index]; |
| + | ioctl(0, TCSETS, &termios); |
| + | } |
| } | |
| /* Disable timer. */ | |
| - | |
| if (options.timeout) | |
| alarm(0); | |
| /* Finalize the termios settings. */ | |
| - | |
| termios_final(&options, &termios, &chardata); | |
| /* Now the newline character should be properly written. */ | |
| - | |
| - | write(1, "\n", 1); |
| + | full_write(1, "\n", 1); |
| /* Let the login program take care of password validation. */ | |
| - | |
| - | execl(options.login, options.login, "--", logname, (char *) 0); |
| + | /* We use PATH because we trust that root doesn't set "bad" PATH, |
| + | * and getty is not suid-root applet. */ |
| + | /* Hmm... with -n, logname == NULL! Is it ok? */ |
| + | BB_EXECLP(options.login, options.login, "--", logname, NULL); |
| bb_error_msg_and_die("%s: can't exec %s", options.tty, options.login); | |
| } | |