aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDenis Vlasenko <vda.linux@googlemail.com>2007-12-04 09:48:40 +0000
committerDenis Vlasenko <vda.linux@googlemail.com>2007-12-04 09:48:40 +0000
commitd0bbbdcd6eefd249637f153f9d29b37c7f545e33 (patch)
treed2a4453c90ae1d7fc1090a907ad8fe3aa21429fe
parent7221c8c22dd527700204eb5cc4d0651af4273f4f (diff)
downloadbusybox-w32-d0bbbdcd6eefd249637f153f9d29b37c7f545e33.tar.gz
busybox-w32-d0bbbdcd6eefd249637f153f9d29b37c7f545e33.tar.bz2
busybox-w32-d0bbbdcd6eefd249637f153f9d29b37c7f545e33.zip
getty: don't accept ancient '#' and '@' as backspace/kill line,
it only confuses people. (Alexander Griesser <alexander.griesser@lkh-vil.or.at> (LKH Villach)) various other cleanups. function old new delta getty_main 2526 2546 +20 static.baud_index 4 - -4 parse_speeds 91 - -91 ------------------------------------------------------------------------------ (add/remove: 0/2 grow/shrink: 1/0 up/down: 20/-95) Total: -75 bytes text data bss dec hex filename 773152 1086 9008 783246 bf38e busybox_old 773081 1086 9008 783175 bf347 busybox_unstripped
-rw-r--r--docs/ctty.htm474
-rw-r--r--loginutils/getty.c392
2 files changed, 639 insertions, 227 deletions
diff --git a/docs/ctty.htm b/docs/ctty.htm
new file mode 100644
index 000000000..26d2c7956
--- /dev/null
+++ b/docs/ctty.htm
@@ -0,0 +1,474 @@
1<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
2<html><head>
3 <!-- saved from http://www.win.tue.nl/~aeb/linux/lk/lk-10.html -->
4 <meta name="GENERATOR" content="SGML-Tools 1.0.9"><title>The Linux kernel: Processes</title>
5</head>
6<body>
7<hr>
8<h2><a name="s10">10. Processes</a></h2>
9
10<p>Before looking at the Linux implementation, first a general Unix
11description of threads, processes, process groups and sessions.
12</p><p>A session contains a number of process groups, and a process group
13contains a number of processes, and a process contains a number
14of threads.
15</p><p>A session can have a controlling tty.
16At most one process group in a session can be a foreground process group.
17An interrupt character typed on a tty ("Teletype", i.e., terminal)
18causes a signal to be sent to all members of the foreground process group
19in the session (if any) that has that tty as controlling tty.
20</p><p>All these objects have numbers, and we have thread IDs, process IDs,
21process group IDs and session IDs.
22</p><p>
23</p><h2><a name="ss10.1">10.1 Processes</a>
24</h2>
25
26<p>
27</p><h3>Creation</h3>
28
29<p>A new process is traditionally started using the <code>fork()</code>
30system call:
31</p><blockquote>
32<pre>pid_t p;
33
34p = fork();
35if (p == (pid_t) -1)
36 /* ERROR */
37else if (p == 0)
38 /* CHILD */
39else
40 /* PARENT */
41</pre>
42</blockquote>
43<p>This creates a child as a duplicate of its parent.
44Parent and child are identical in almost all respects.
45In the code they are distinguished by the fact that the parent
46learns the process ID of its child, while <code>fork()</code>
47returns 0 in the child. (It can find the process ID of its
48parent using the <code>getppid()</code> system call.)
49</p><p>
50</p><h3>Termination</h3>
51
52<p>Normal termination is when the process does
53</p><blockquote>
54<pre>exit(n);
55</pre>
56</blockquote>
57
58or
59<blockquote>
60<pre>return n;
61</pre>
62</blockquote>
63
64from its <code>main()</code> procedure. It returns the single byte <code>n</code>
65to its parent.
66<p>Abnormal termination is usually caused by a signal.
67</p><p>
68</p><h3>Collecting the exit code. Zombies</h3>
69
70<p>The parent does
71</p><blockquote>
72<pre>pid_t p;
73int status;
74
75p = wait(&amp;status);
76</pre>
77</blockquote>
78
79and collects two bytes:
80<p>
81<figure>
82<eps file="absent">
83<img src="ctty_files/exit_status.png">
84</eps>
85</figure></p><p>A process that has terminated but has not yet been waited for
86is a <i>zombie</i>. It need only store these two bytes:
87exit code and reason for termination.
88</p><p>On the other hand, if the parent dies first, <code>init</code> (process 1)
89inherits the child and becomes its parent.
90</p><p>
91</p><h3>Signals</h3>
92
93<p>
94</p><h3>Stopping</h3>
95
96<p>Some signals cause a process to stop:
97<code>SIGSTOP</code> (stop!),
98<code>SIGTSTP</code> (stop from tty: probably ^Z was typed),
99<code>SIGTTIN</code> (tty input asked by background process),
100<code>SIGTTOU</code> (tty output sent by background process, and this was
101disallowed by <code>stty tostop</code>).
102</p><p>Apart from ^Z there also is ^Y. The former stops the process
103when it is typed, the latter stops it when it is read.
104</p><p>Signals generated by typing the corresponding character on some tty
105are sent to all processes that are in the foreground process group
106of the session that has that tty as controlling tty. (Details below.)
107</p><p>If a process is being traced, every signal will stop it.
108</p><p>
109</p><h3>Continuing</h3>
110
111<p><code>SIGCONT</code>: continue a stopped process.
112</p><p>
113</p><h3>Terminating</h3>
114
115<p><code>SIGKILL</code> (die! now!),
116<code>SIGTERM</code> (please, go away),
117<code>SIGHUP</code> (modem hangup),
118<code>SIGINT</code> (^C),
119<code>SIGQUIT</code> (^\), etc.
120Many signals have as default action to kill the target.
121(Sometimes with an additional core dump, when such is
122allowed by rlimit.)
123The signals <code>SIGCHLD</code> and <code>SIGWINCH</code>
124are ignored by default.
125All except <code>SIGKILL</code> and <code>SIGSTOP</code> can be
126caught or ignored or blocked.
127For details, see <code>signal(7)</code>.
128</p><p>
129</p><h2><a name="ss10.2">10.2 Process groups</a>
130</h2>
131
132<p>Every process is member of a unique <i>process group</i>,
133identified by its <i>process group ID</i>.
134(When the process is created, it becomes a member of the process group
135of its parent.)
136By convention, the process group ID of a process group
137equals the process ID of the first member of the process group,
138called the <i>process group leader</i>.
139A process finds the ID of its process group using the system call
140<code>getpgrp()</code>, or, equivalently, <code>getpgid(0)</code>.
141One finds the process group ID of process <code>p</code> using
142<code>getpgid(p)</code>.
143</p><p>One may use the command <code>ps j</code> to see PPID (parent process ID),
144PID (process ID), PGID (process group ID) and SID (session ID)
145of processes. With a shell that does not know about job control,
146like <code>ash</code>, each of its children will be in the same session
147and have the same process group as the shell. With a shell that knows
148about job control, like <code>bash</code>, the processes of one pipeline. like
149</p><blockquote>
150<pre>% cat paper | ideal | pic | tbl | eqn | ditroff &gt; out
151</pre>
152</blockquote>
153
154form a single process group.
155<p>
156</p><h3>Creation</h3>
157
158<p>A process <code>pid</code> is put into the process group <code>pgid</code> by
159</p><blockquote>
160<pre>setpgid(pid, pgid);
161</pre>
162</blockquote>
163
164If <code>pgid == pid</code> or <code>pgid == 0</code> then this creates
165a new process group with process group leader <code>pid</code>.
166Otherwise, this puts <code>pid</code> into the already existing
167process group <code>pgid</code>.
168A zero <code>pid</code> refers to the current process.
169The call <code>setpgrp()</code> is equivalent to <code>setpgid(0,0)</code>.
170<p>
171</p><h3>Restrictions on setpgid()</h3>
172
173<p>The calling process must be <code>pid</code> itself, or its parent,
174and the parent can only do this before <code>pid</code> has done
175<code>exec()</code>, and only when both belong to the same session.
176It is an error if process <code>pid</code> is a session leader
177(and this call would change its <code>pgid</code>).
178</p><p>
179</p><h3>Typical sequence</h3>
180
181<p>
182</p><blockquote>
183<pre>p = fork();
184if (p == (pid_t) -1) {
185 /* ERROR */
186} else if (p == 0) { /* CHILD */
187 setpgid(0, pgid);
188 ...
189} else { /* PARENT */
190 setpgid(p, pgid);
191 ...
192}
193</pre>
194</blockquote>
195
196This ensures that regardless of whether parent or child is scheduled
197first, the process group setting is as expected by both.
198<p>
199</p><h3>Signalling and waiting</h3>
200
201<p>One can signal all members of a process group:
202</p><blockquote>
203<pre>killpg(pgrp, sig);
204</pre>
205</blockquote>
206<p>One can wait for children in ones own process group:
207</p><blockquote>
208<pre>waitpid(0, &amp;status, ...);
209</pre>
210</blockquote>
211
212or in a specified process group:
213<blockquote>
214<pre>waitpid(-pgrp, &amp;status, ...);
215</pre>
216</blockquote>
217<p>
218</p><h3>Foreground process group</h3>
219
220<p>Among the process groups in a session at most one can be
221the <i>foreground process group</i> of that session.
222The tty input and tty signals (signals generated by ^C, ^Z, etc.)
223go to processes in this foreground process group.
224</p><p>A process can determine the foreground process group in its session
225using <code>tcgetpgrp(fd)</code>, where <code>fd</code> refers to its
226controlling tty. If there is none, this returns a random value
227larger than 1 that is not a process group ID.
228</p><p>A process can set the foreground process group in its session
229using <code>tcsetpgrp(fd,pgrp)</code>, where <code>fd</code> refers to its
230controlling tty, and <code>pgrp</code> is a process group in the
231its session, and this session still is associated to the controlling
232tty of the calling process.
233</p><p>How does one get <code>fd</code>? By definition, <code>/dev/tty</code>
234refers to the controlling tty, entirely independent of redirects
235of standard input and output. (There is also the function
236<code>ctermid()</code> to get the name of the controlling terminal.
237On a POSIX standard system it will return <code>/dev/tty</code>.)
238Opening the name of the
239controlling tty gives a file descriptor <code>fd</code>.
240</p><p>
241</p><h3>Background process groups</h3>
242
243<p>All process groups in a session that are not foreground
244process group are <i>background process groups</i>.
245Since the user at the keyboard is interacting with foreground
246processes, background processes should stay away from it.
247When a background process reads from the terminal it gets
248a SIGTTIN signal. Normally, that will stop it, the job control shell
249notices and tells the user, who can say <code>fg</code> to continue
250this background process as a foreground process, and then this
251process can read from the terminal. But if the background process
252ignores or blocks the SIGTTIN signal, or if its process group
253is orphaned (see below), then the read() returns an EIO error,
254and no signal is sent. (Indeed, the idea is to tell the process
255that reading from the terminal is not allowed right now.
256If it wouldn't see the signal, then it will see the error return.)
257</p><p>When a background process writes to the terminal, it may get
258a SIGTTOU signal. May: namely, when the flag that this must happen
259is set (it is off by default). One can set the flag by
260</p><blockquote>
261<pre>% stty tostop
262</pre>
263</blockquote>
264
265and clear it again by
266<blockquote>
267<pre>% stty -tostop
268</pre>
269</blockquote>
270
271and inspect it by
272<blockquote>
273<pre>% stty -a
274</pre>
275</blockquote>
276
277Again, if TOSTOP is set but the background process ignores or blocks
278the SIGTTOU signal, or if its process group is orphaned (see below),
279then the write() returns an EIO error, and no signal is sent.
280<p>
281</p><h3>Orphaned process groups</h3>
282
283<p>The process group leader is the first member of the process group.
284It may terminate before the others, and then the process group is
285without leader.
286</p><p>A process group is called <i>orphaned</i> when <i>the
287parent of every member is either in the process group
288or outside the session</i>.
289In particular, the process group of the session leader
290is always orphaned.
291</p><p>If termination of a process causes a process group to become
292orphaned, and some member is stopped, then all are sent first SIGHUP
293and then SIGCONT.
294</p><p>The idea is that perhaps the parent of the process group leader
295is a job control shell. (In the same session but a different
296process group.) As long as this parent is alive, it can
297handle the stopping and starting of members in the process group.
298When it dies, there may be nobody to continue stopped processes.
299Therefore, these stopped processes are sent SIGHUP, so that they
300die unless they catch or ignore it, and then SIGCONT to continue them.
301</p><p>Note that the process group of the session leader is already
302orphaned, so no signals are sent when the session leader dies.
303</p><p>Note also that a process group can become orphaned in two ways
304by termination of a process: either it was a parent and not itself
305in the process group, or it was the last element of the process group
306with a parent outside but in the same session.
307Furthermore, that a process group can become orphaned
308other than by termination of a process, namely when some
309member is moved to a different process group.
310</p><p>
311</p><h2><a name="ss10.3">10.3 Sessions</a>
312</h2>
313
314<p>Every process group is in a unique <i>session</i>.
315(When the process is created, it becomes a member of the session
316of its parent.)
317By convention, the session ID of a session
318equals the process ID of the first member of the session,
319called the <i>session leader</i>.
320A process finds the ID of its session using the system call
321<code>getsid()</code>.
322</p><p>Every session may have a <i>controlling tty</i>,
323that then also is called the controlling tty of each of
324its member processes.
325A file descriptor for the controlling tty is obtained by
326opening <code>/dev/tty</code>. (And when that fails, there was no
327controlling tty.) Given a file descriptor for the controlling tty,
328one may obtain the SID using <code>tcgetsid(fd)</code>.
329</p><p>A session is often set up by a login process. The terminal
330on which one is logged in then becomes the controlling tty
331of the session. All processes that are descendants of the
332login process will in general be members of the session.
333</p><p>
334</p><h3>Creation</h3>
335
336<p>A new session is created by
337</p><blockquote>
338<pre>pid = setsid();
339</pre>
340</blockquote>
341
342This is allowed only when the current process is not a process group leader.
343In order to be sure of that we fork first:
344<blockquote>
345<pre>p = fork();
346if (p) exit(0);
347pid = setsid();
348</pre>
349</blockquote>
350
351The result is that the current process (with process ID <code>pid</code>)
352becomes session leader of a new session with session ID <code>pid</code>.
353Moreover, it becomes process group leader of a new process group.
354Both session and process group contain only the single process <code>pid</code>.
355Furthermore, this process has no controlling tty.
356<p>The restriction that the current process must not be a process group leader
357is needed: otherwise its PID serves as PGID of some existing process group
358and cannot be used as the PGID of a new process group.
359</p><p>
360</p><h3>Getting a controlling tty</h3>
361
362<p>How does one get a controlling terminal? Nobody knows,
363this is a great mystery.
364</p><p>The System V approach is that the first tty opened by the process
365becomes its controlling tty.
366</p><p>The BSD approach is that one has to explicitly call
367</p><blockquote>
368<pre>ioctl(fd, TIOCSCTTY, ...);
369</pre>
370</blockquote>
371
372to get a controlling tty.
373<p>Linux tries to be compatible with both, as always, and this
374results in a very obscure complex of conditions. Roughly:
375</p><p>The <code>TIOCSCTTY</code> ioctl will give us a controlling tty,
376provided that (i) the current process is a session leader,
377and (ii) it does not yet have a controlling tty, and
378(iii) maybe the tty should not already control some other session;
379if it does it is an error if we aren't root, or we steal the tty
380if we are all-powerful.
381</p><p>Opening some terminal will give us a controlling tty,
382provided that (i) the current process is a session leader, and
383(ii) it does not yet have a controlling tty, and
384(iii) the tty does not already control some other session, and
385(iv) the open did not have the <code>O_NOCTTY</code> flag, and
386(v) the tty is not the foreground VT, and
387(vi) the tty is not the console, and
388(vii) maybe the tty should not be master or slave pty.
389</p><p>
390</p><h3>Getting rid of a controlling tty</h3>
391
392<p>If a process wants to continue as a daemon, it must detach itself
393from its controlling tty. Above we saw that <code>setsid()</code>
394will remove the controlling tty. Also the ioctl TIOCNOTTY does this.
395Moreover, in order not to get a controlling tty again as soon as it
396opens a tty, the process has to fork once more, to assure that it
397is not a session leader. Typical code fragment:
398</p><p>
399</p><pre> if ((fork()) != 0)
400 exit(0);
401 setsid();
402 if ((fork()) != 0)
403 exit(0);
404</pre>
405<p>See also <code>daemon(3)</code>.
406</p><p>
407</p><h3>Disconnect</h3>
408
409<p>If the terminal goes away by modem hangup, and the line was not local,
410then a SIGHUP is sent to the session leader.
411Any further reads from the gone terminal return EOF.
412(Or possibly -1 with <code>errno</code> set to EIO.)
413</p><p>If the terminal is the slave side of a pseudotty, and the master side
414is closed (for the last time), then a SIGHUP is sent to the foreground
415process group of the slave side.
416</p><p>When the session leader dies, a SIGHUP is sent to all processes
417in the foreground process group. Moreover, the terminal stops being
418the controlling terminal of this session (so that it can become
419the controlling terminal of another session).
420</p><p>Thus, if the terminal goes away and the session leader is
421a job control shell, then it can handle things for its descendants,
422e.g. by sending them again a SIGHUP.
423If on the other hand the session leader is an innocent process
424that does not catch SIGHUP, it will die, and all foreground processes
425get a SIGHUP.
426</p><p>
427</p><h2><a name="ss10.4">10.4 Threads</a>
428</h2>
429
430<p>A process can have several threads. New threads (with the same PID
431as the parent thread) are started using the <code>clone</code> system
432call using the <code>CLONE_THREAD</code> flag. Threads are distinguished
433by a <i>thread ID</i> (TID). An ordinary process has a single thread
434with TID equal to PID. The system call <code>gettid()</code> returns the
435TID. The system call <code>tkill()</code> sends a signal to a single thread.
436</p><p>Example: a process with two threads. Both only print PID and TID and exit.
437(Linux 2.4.19 or later.)
438</p><pre>% cat &lt;&lt; EOF &gt; gettid-demo.c
439#include &lt;unistd.h&gt;
440#include &lt;sys/types.h&gt;
441#define CLONE_SIGHAND 0x00000800
442#define CLONE_THREAD 0x00010000
443#include &lt;linux/unistd.h&gt;
444#include &lt;errno.h&gt;
445_syscall0(pid_t,gettid)
446
447int thread(void *p) {
448 printf("thread: %d %d\n", gettid(), getpid());
449}
450
451main() {
452 unsigned char stack[4096];
453 int i;
454
455 i = clone(thread, stack+2048, CLONE_THREAD | CLONE_SIGHAND, NULL);
456 if (i == -1)
457 perror("clone");
458 else
459 printf("clone returns %d\n", i);
460 printf("parent: %d %d\n", gettid(), getpid());
461}
462EOF
463% cc -o gettid-demo gettid-demo.c
464% ./gettid-demo
465clone returns 21826
466parent: 21825 21825
467thread: 21826 21825
468%
469</pre>
470<p>
471</p><p>
472</p><hr>
473
474</body></html>
diff --git a/loginutils/getty.c b/loginutils/getty.c
index d32d18935..590a05de8 100644
--- a/loginutils/getty.c
+++ b/loginutils/getty.c
@@ -33,7 +33,6 @@
33#include <time.h> 33#include <time.h>
34#if ENABLE_FEATURE_WTMP 34#if ENABLE_FEATURE_WTMP
35extern void updwtmp(const char *filename, const struct utmp *ut); 35extern void updwtmp(const char *filename, const struct utmp *ut);
36static void update_utmp(const char *line);
37#endif 36#endif
38#endif /* LOGIN_PROCESS */ 37#endif /* LOGIN_PROCESS */
39 38
@@ -47,6 +46,7 @@ static void update_utmp(const char *line);
47 46
48/* I doubt there are systems which still need this */ 47/* I doubt there are systems which still need this */
49#undef HANDLE_ALLCAPS 48#undef HANDLE_ALLCAPS
49#undef ANCIENT_BS_KILL_CHARS
50 50
51#define _PATH_LOGIN "/bin/login" 51#define _PATH_LOGIN "/bin/login"
52 52
@@ -76,36 +76,20 @@ static void update_utmp(const char *line);
76 * When multiple baud rates are specified on the command line, the first one 76 * When multiple baud rates are specified on the command line, the first one
77 * we will try is the first one specified. 77 * we will try is the first one specified.
78 */ 78 */
79#define FIRST_SPEED 0
80
81/* Storage for command-line options. */
82
83#define MAX_SPEED 10 /* max. nr. of baud rates */ 79#define MAX_SPEED 10 /* max. nr. of baud rates */
84 80
81/* Storage for command-line options. */
85struct options { 82struct options {
86 int flags; /* toggle switches, see below */ 83 int flags; /* toggle switches, see below */
87 unsigned timeout; /* time-out period */ 84 unsigned timeout; /* time-out period */
88 const char *login; /* login program */ 85 const char *login; /* login program */
89 const char *tty; /* name of tty */ 86 const char *tty; /* name of tty */
90 const char *initstring; /* modem init string */ 87 const char *initstring; /* modem init string */
91 const char *issue; /* alternative issue file */ 88 const char *issue; /* alternative issue file */
92 int numspeed; /* number of baud rates to try */ 89 int numspeed; /* number of baud rates to try */
93 int speeds[MAX_SPEED]; /* baud rates to be tried */ 90 int speeds[MAX_SPEED]; /* baud rates to be tried */
94}; 91};
95 92
96static const char opt_string[] ALIGN1 = "I:LH:f:hil:mt:wn";
97#define F_INITSTRING (1<<0) /* initstring is set */
98#define F_LOCAL (1<<1) /* force local */
99#define F_FAKEHOST (1<<2) /* force fakehost */
100#define F_CUSTISSUE (1<<3) /* give alternative issue file */
101#define F_RTSCTS (1<<4) /* enable RTS/CTS flow control */
102#define F_ISSUE (1<<5) /* display /etc/issue */
103#define F_LOGIN (1<<6) /* non-default login program */
104#define F_PARSE (1<<7) /* process modem status messages */
105#define F_TIMEOUT (1<<8) /* time out */
106#define F_WAITCRLF (1<<9) /* wait for CR or LF */
107#define F_NOPROMPT (1<<10) /* don't ask for login name! */
108
109/* Storage for things detected while the login name was read. */ 93/* Storage for things detected while the login name was read. */
110struct chardata { 94struct chardata {
111 unsigned char erase; /* erase character */ 95 unsigned char erase; /* erase character */
@@ -117,6 +101,7 @@ struct chardata {
117#endif 101#endif
118}; 102};
119 103
104
120/* Initial values for the above. */ 105/* Initial values for the above. */
121static const struct chardata init_chardata = { 106static const struct chardata init_chardata = {
122 DEF_ERASE, /* default erase character */ 107 DEF_ERASE, /* default erase character */
@@ -128,12 +113,25 @@ static const struct chardata init_chardata = {
128#endif 113#endif
129}; 114};
130 115
131/* The following is used for understandable diagnostics. */ 116static const char opt_string[] ALIGN1 = "I:LH:f:hil:mt:wn";
117#define F_INITSTRING (1 << 0) /* -I initstring is set */
118#define F_LOCAL (1 << 1) /* -L force local */
119#define F_FAKEHOST (1 << 2) /* -H force fakehost */
120#define F_CUSTISSUE (1 << 3) /* -f give alternative issue file */
121#define F_RTSCTS (1 << 4) /* -h enable RTS/CTS flow control */
122#define F_ISSUE (1 << 5) /* -i display /etc/issue */
123#define F_LOGIN (1 << 6) /* -l non-default login program */
124#define F_PARSE (1 << 7) /* -m process modem status messages */
125#define F_TIMEOUT (1 << 8) /* -t time out */
126#define F_WAITCRLF (1 << 9) /* -w wait for CR or LF */
127#define F_NOPROMPT (1 << 10) /* -n don't ask for login name! */
128
132 129
133/* Fake hostname for ut_host specified on command line. */ 130/* Fake hostname for ut_host specified on command line. */
134static char *fakehost = NULL; 131static char *fakehost = NULL;
132#define line_buf bb_common_bufsiz1
135 133
136/* ... */ 134/* The following is used for understandable diagnostics. */
137#ifdef DEBUGGING 135#ifdef DEBUGGING
138#define debug(s) fprintf(dbf,s); fflush(dbf) 136#define debug(s) fprintf(dbf,s); fflush(dbf)
139#define DEBUGTERM "/dev/ttyp0" 137#define DEBUGTERM "/dev/ttyp0"
@@ -158,14 +156,14 @@ static int bcode(const char *s)
158 return 0; 156 return 0;
159} 157}
160 158
161
162/* parse_speeds - parse alternate baud rates */ 159/* parse_speeds - parse alternate baud rates */
163static void parse_speeds(struct options *op, char *arg) 160static void parse_speeds(struct options *op, char *arg)
164{ 161{
165 char *cp; 162 char *cp;
166 163
164 /* NB: at least one iteration is always done */
167 debug("entered parse_speeds\n"); 165 debug("entered parse_speeds\n");
168 for (cp = strtok(arg, ","); cp != 0; cp = strtok((char *) 0, ",")) { 166 while ((cp = strsep(&arg, ",")) != NULL) {
169 op->speeds[op->numspeed] = bcode(cp); 167 op->speeds[op->numspeed] = bcode(cp);
170 if (op->speeds[op->numspeed] <= 0) 168 if (op->speeds[op->numspeed] <= 0)
171 bb_error_msg_and_die("bad speed: %s", cp); 169 bb_error_msg_and_die("bad speed: %s", cp);
@@ -173,18 +171,19 @@ static void parse_speeds(struct options *op, char *arg)
173 if (op->numspeed > MAX_SPEED) 171 if (op->numspeed > MAX_SPEED)
174 bb_error_msg_and_die("too many alternate speeds"); 172 bb_error_msg_and_die("too many alternate speeds");
175 } 173 }
176 debug("exiting parsespeeds\n"); 174 debug("exiting parse_speeds\n");
177} 175}
178 176
179
180/* parse_args - parse command-line arguments */ 177/* parse_args - parse command-line arguments */
181static void parse_args(int argc, char **argv, struct options *op) 178static void parse_args(char **argv, struct options *op)
182{ 179{
183 char *ts; 180 char *ts;
184 181
182 opt_complementary = "-2"; /* at least 2 args */
185 op->flags = getopt32(argv, opt_string, 183 op->flags = getopt32(argv, opt_string,
186 &(op->initstring), &fakehost, &(op->issue), 184 &(op->initstring), &fakehost, &(op->issue),
187 &(op->login), &ts); 185 &(op->login), &ts);
186 argv += optind;
188 if (op->flags & F_INITSTRING) { 187 if (op->flags & F_INITSTRING) {
189 const char *p = op->initstring; 188 const char *p = op->initstring;
190 char *q; 189 char *q;
@@ -202,45 +201,40 @@ static void parse_args(int argc, char **argv, struct options *op)
202 } 201 }
203 *q = '\0'; 202 *q = '\0';
204 } 203 }
205 op->flags ^= F_ISSUE; /* revert flag show /etc/issue */ 204 op->flags ^= F_ISSUE; /* invert flag show /etc/issue */
206 if (op->flags & F_TIMEOUT) { 205 if (op->flags & F_TIMEOUT) {
207 op->timeout = xatoul_range(ts, 1, INT_MAX); 206 op->timeout = xatoi_u(ts);
208 } 207 }
209 argv += optind; 208 debug("after getopt\n");
210 argc -= optind;
211 debug("after getopt loop\n");
212 if (argc < 2) /* check parameter count */
213 bb_show_usage();
214 209
215 /* we loosen up a bit and accept both "baudrate tty" and "tty baudrate" */ 210 /* we loosen up a bit and accept both "baudrate tty" and "tty baudrate" */
211 op->tty = argv[0]; /* tty name */
212 ts = argv[1]; /* baud rate(s) */
216 if (isdigit(argv[0][0])) { 213 if (isdigit(argv[0][0])) {
217 /* a number first, assume it's a speed (BSD style) */ 214 /* a number first, assume it's a speed (BSD style) */
218 parse_speeds(op, argv[0]); /* baud rate(s) */ 215 op->tty = ts; /* tty name is in argv[1] */
219 op->tty = argv[1]; /* tty name */ 216 ts = argv[0]; /* baud rate(s) */
220 } else {
221 op->tty = argv[0]; /* tty name */
222 parse_speeds(op, argv[1]); /* baud rate(s) */
223 } 217 }
218 parse_speeds(op, ts);
224 219
225 if (argv[2]) 220 if (argv[2])
226 setenv("TERM", argv[2], 1); 221 setenv("TERM", argv[2], 1);
227 222
228 debug("exiting parseargs\n"); 223 debug("exiting parse_args\n");
229} 224}
230 225
231/* open_tty - set up tty as standard { input, output, error } */ 226/* open_tty - set up tty as standard { input, output, error } */
232static void open_tty(const char *tty, struct termios *tp, int local) 227static void open_tty(const char *tty)
233{ 228{
234 int chdir_to_root = 0;
235
236 /* Set up new standard input, unless we are given an already opened port. */ 229 /* Set up new standard input, unless we are given an already opened port. */
237 if (NOT_LONE_DASH(tty)) { 230 if (NOT_LONE_DASH(tty)) {
238 struct stat st; 231 struct stat st;
232 int cur_dir_fd;
239 int fd; 233 int fd;
240 234
241 /* Sanity checks... */ 235 /* Sanity checks... */
236 cur_dir_fd = xopen(".", O_DIRECTORY | O_NONBLOCK);
242 xchdir("/dev"); 237 xchdir("/dev");
243 chdir_to_root = 1;
244 xstat(tty, &st); 238 xstat(tty, &st);
245 if ((st.st_mode & S_IFMT) != S_IFCHR) 239 if ((st.st_mode & S_IFMT) != S_IFCHR)
246 bb_error_msg_and_die("%s: not a character device", tty); 240 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)
248 /* Open the tty as standard input. */ 242 /* Open the tty as standard input. */
249 debug("open(2)\n"); 243 debug("open(2)\n");
250 fd = xopen(tty, O_RDWR | O_NONBLOCK); 244 fd = xopen(tty, O_RDWR | O_NONBLOCK);
245
246 /* Restore current directory */
247 fchdir(cur_dir_fd);
248
249 /* Open the tty as standard input, continued */
251 xdup2(fd, 0); 250 xdup2(fd, 0);
251 /* fd is >= cur_dir_fd, and cur_dir_fd gets closed too here: */
252 while (fd > 2) 252 while (fd > 2)
253 close(fd--); 253 close(fd--);
254
255 /* Set proper protections and ownership. Mode 0622
256 * is suitable for SYSV < 4 because /bin/login does not change
257 * protections. SunOS 4 login will change the protections to 0620
258 * (write access for group tty) after the login has succeeded.
259 */
260 fchown(0, 0, 0); /* 0:0 */
261 fchmod(0, 0622); /* crw--w--w- */
254 } else { 262 } else {
255 /* 263 /*
256 * Standard input should already be connected to an open port. Make 264 * 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)
259 if ((fcntl(0, F_GETFL) & O_RDWR) != O_RDWR) 267 if ((fcntl(0, F_GETFL) & O_RDWR) != O_RDWR)
260 bb_error_msg_and_die("stdin is not open for read/write"); 268 bb_error_msg_and_die("stdin is not open for read/write");
261 } 269 }
262
263 /* Replace current standard output/error fd's with new ones */
264 debug("duping\n");
265 xdup2(0, 1);
266 xdup2(0, 2);
267
268 /*
269 * The following ioctl will fail if stdin is not a tty, but also when
270 * there is noise on the modem control lines. In the latter case, the
271 * common course of action is (1) fix your cables (2) give the modem more
272 * time to properly reset after hanging up. SunOS users can achieve (2)
273 * by patching the SunOS kernel variable "zsadtrlow" to a larger value;
274 * 5 seconds seems to be a good value.
275 */
276 ioctl_or_perror_and_die(0, TCGETS, tp, "%s: TCGETS", tty);
277
278 /*
279 * It seems to be a terminal. Set proper protections and ownership. Mode
280 * 0622 is suitable for SYSV <4 because /bin/login does not change
281 * protections. SunOS 4 login will change the protections to 0620 (write
282 * access for group tty) after the login has succeeded.
283 */
284
285#ifdef DEBIAN
286#warning Debian /dev/vcs[a]NN hack is deprecated and will be removed
287 {
288 /* tty to root.dialout 660 */
289 struct group *gr;
290 int id;
291
292 gr = getgrnam("dialout");
293 id = gr ? gr->gr_gid : 0;
294 chown(tty, 0, id);
295 chmod(tty, 0660);
296
297 /* vcs,vcsa to root.sys 600 */
298 if (!strncmp(tty, "tty", 3) && isdigit(tty[3])) {
299 char *vcs, *vcsa;
300
301 vcs = xstrdup(tty);
302 vcsa = xmalloc(strlen(tty) + 2);
303 strcpy(vcs, "vcs");
304 strcpy(vcs + 3, tty + 3);
305 strcpy(vcsa, "vcsa");
306 strcpy(vcsa + 4, tty + 3);
307
308 gr = getgrnam("sys");
309 id = gr ? gr->gr_gid : 0;
310 chown(vcs, 0, id);
311 chmod(vcs, 0600);
312 chown(vcsa, 0, id);
313 chmod(vcs, 0600);
314
315 free(vcs);
316 free(vcsa);
317 }
318 }
319#else
320 if (NOT_LONE_DASH(tty)) {
321 chown(tty, 0, 0); /* 0:0 */
322 chmod(tty, 0622); /* crw--w--w- */
323 }
324#endif
325 if (chdir_to_root)
326 xchdir("/");
327} 270}
328 271
329/* termios_init - initialize termios settings */ 272/* termios_init - initialize termios settings */
@@ -351,17 +294,13 @@ static void termios_init(struct termios *tp, int speed, struct options *op)
351 tp->c_cc[VTIME] = 0; 294 tp->c_cc[VTIME] = 0;
352 295
353 /* Optionally enable hardware flow control */ 296 /* Optionally enable hardware flow control */
354 297#ifdef CRTSCTS
355#ifdef CRTSCTS
356 if (op->flags & F_RTSCTS) 298 if (op->flags & F_RTSCTS)
357 tp->c_cflag |= CRTSCTS; 299 tp->c_cflag |= CRTSCTS;
358#endif 300#endif
359 301
360 ioctl(0, TCSETS, tp); 302 ioctl(0, TCSETS, tp);
361 303
362 /* go to blocking input even in local mode */
363 ndelay_off(0);
364
365 debug("term_io 2\n"); 304 debug("term_io 2\n");
366} 305}
367 306
@@ -393,26 +332,24 @@ static void auto_baud(char *buf, unsigned size_buf, struct termios *tp)
393 * Use 7-bit characters, don't block if input queue is empty. Errors will 332 * Use 7-bit characters, don't block if input queue is empty. Errors will
394 * be dealt with later on. 333 * be dealt with later on.
395 */ 334 */
396
397 iflag = tp->c_iflag; 335 iflag = tp->c_iflag;
398 tp->c_iflag |= ISTRIP; /* enable 8th-bit stripping */ 336 tp->c_iflag |= ISTRIP; /* enable 8th-bit stripping */
399 vmin = tp->c_cc[VMIN]; 337 vmin = tp->c_cc[VMIN];
400 tp->c_cc[VMIN] = 0; /* don't block if queue empty */ 338 tp->c_cc[VMIN] = 0; /* don't block if queue empty */
401 ioctl(0, TCSETS, tp); 339 ioctl(0, TCSETS, tp);
402 340
403 /* 341 /*
404 * Wait for a while, then read everything the modem has said so far and 342 * Wait for a while, then read everything the modem has said so far and
405 * try to extract the speed of the dial-in call. 343 * try to extract the speed of the dial-in call.
406 */ 344 */
407
408 sleep(1); 345 sleep(1);
409 nread = read(0, buf, size_buf - 1); 346 nread = safe_read(0, buf, size_buf - 1);
410 if (nread > 0) { 347 if (nread > 0) {
411 buf[nread] = '\0'; 348 buf[nread] = '\0';
412 for (bp = buf; bp < buf + nread; bp++) { 349 for (bp = buf; bp < buf + nread; bp++) {
413 if (isascii(*bp) && isdigit(*bp)) { 350 if (isdigit(*bp)) {
414 speed = bcode(bp); 351 speed = bcode(bp);
415 if (speed) { 352 if (speed > 0) {
416 tp->c_cflag &= ~CBAUD; 353 tp->c_cflag &= ~CBAUD;
417 tp->c_cflag |= speed; 354 tp->c_cflag |= speed;
418 } 355 }
@@ -420,25 +357,13 @@ static void auto_baud(char *buf, unsigned size_buf, struct termios *tp)
420 } 357 }
421 } 358 }
422 } 359 }
423 /* Restore terminal settings. Errors will be dealt with later on. */
424 360
361 /* Restore terminal settings. Errors will be dealt with later on. */
425 tp->c_iflag = iflag; 362 tp->c_iflag = iflag;
426 tp->c_cc[VMIN] = vmin; 363 tp->c_cc[VMIN] = vmin;
427 ioctl(0, TCSETS, tp); 364 ioctl(0, TCSETS, tp);
428} 365}
429 366
430/* next_speed - select next baud rate */
431static void next_speed(struct termios *tp, struct options *op)
432{
433 static int baud_index = FIRST_SPEED; /* current speed index */
434
435 baud_index = (baud_index + 1) % op->numspeed;
436 tp->c_cflag &= ~CBAUD;
437 tp->c_cflag |= op->speeds[baud_index];
438 ioctl(0, TCSETS, tp);
439}
440
441
442/* do_prompt - show login prompt, optionally preceded by /etc/issue contents */ 367/* do_prompt - show login prompt, optionally preceded by /etc/issue contents */
443static void do_prompt(struct options *op, struct termios *tp) 368static void do_prompt(struct options *op, struct termios *tp)
444{ 369{
@@ -449,9 +374,9 @@ static void do_prompt(struct options *op, struct termios *tp)
449} 374}
450 375
451#ifdef HANDLE_ALLCAPS 376#ifdef HANDLE_ALLCAPS
452/* caps_lock - string contains upper case without lower case */ 377/* all_is_upcase - string contains upper case without lower case */
453/* returns 1 if true, 0 if false */ 378/* returns 1 if true, 0 if false */
454static int caps_lock(const char *s) 379static int all_is_upcase(const char *s)
455{ 380{
456 while (*s) 381 while (*s)
457 if (islower(*s++)) 382 if (islower(*s++))
@@ -460,8 +385,8 @@ static int caps_lock(const char *s)
460} 385}
461#endif 386#endif
462 387
463/* get_logname - get user name, establish parity, speed, erase, kill, eol */ 388/* get_logname - get user name, establish parity, speed, erase, kill, eol;
464/* return NULL on failure, logname on success */ 389 * return NULL on BREAK, logname on success */
465static char *get_logname(char *logname, unsigned size_logname, 390static char *get_logname(char *logname, unsigned size_logname,
466 struct options *op, struct chardata *cp, struct termios *tp) 391 struct options *op, struct chardata *cp, struct termios *tp)
467{ 392{
@@ -477,26 +402,19 @@ static char *get_logname(char *logname, unsigned size_logname,
477 "\210\240\210", /* no parity */ 402 "\210\240\210", /* no parity */
478 }; 403 };
479 404
480 /* Initialize kill, erase, parity etc. (also after switching speeds). */ 405 /* NB: *cp is pre-initialized with init_chardata */
481
482 *cp = init_chardata;
483 406
484 /* Flush pending input (esp. after parsing or switching the baud rate). */ 407 /* Flush pending input (esp. after parsing or switching the baud rate). */
485
486 sleep(1); 408 sleep(1);
487 ioctl(0, TCFLSH, TCIFLUSH); 409 ioctl(0, TCFLSH, TCIFLUSH);
488 410
489 /* Prompt for and read a login name. */ 411 /* Prompt for and read a login name. */
490
491 logname[0] = '\0'; 412 logname[0] = '\0';
492 while (!logname[0]) { 413 while (!logname[0]) {
493
494 /* Write issue file and prompt, with "parity" bit == 0. */ 414 /* Write issue file and prompt, with "parity" bit == 0. */
495
496 do_prompt(op, tp); 415 do_prompt(op, tp);
497 416
498 /* Read name, watch for break, parity, erase, kill, end-of-line. */ 417 /* Read name, watch for break, parity, erase, kill, end-of-line. */
499
500 bp = logname; 418 bp = logname;
501 cp->eol = '\0'; 419 cp->eol = '\0';
502 while (cp->eol == '\0') { 420 while (cp->eol == '\0') {
@@ -508,7 +426,8 @@ static char *get_logname(char *logname, unsigned size_logname,
508 bb_perror_msg_and_die("%s: read", op->tty); 426 bb_perror_msg_and_die("%s: read", op->tty);
509 } 427 }
510 428
511 /* Do BREAK handling elsewhere. */ 429 /* BREAK. If we have speeds to try,
430 * return NULL (will switch speeds and return here) */
512 if (c == '\0' && op->numspeed > 1) 431 if (c == '\0' && op->numspeed > 1)
513 return NULL; 432 return NULL;
514 433
@@ -535,18 +454,22 @@ static char *get_logname(char *logname, unsigned size_logname,
535 break; 454 break;
536 case BS: 455 case BS:
537 case DEL: 456 case DEL:
457#ifdef ANCIENT_BS_KILL_CHARS
538 case '#': 458 case '#':
459#endif
539 cp->erase = ascval; /* set erase character */ 460 cp->erase = ascval; /* set erase character */
540 if (bp > logname) { 461 if (bp > logname) {
541 write(1, erase[cp->parity], 3); 462 full_write(1, erase[cp->parity], 3);
542 bp--; 463 bp--;
543 } 464 }
544 break; 465 break;
545 case CTL('U'): 466 case CTL('U'):
467#ifdef ANCIENT_BS_KILL_CHARS
546 case '@': 468 case '@':
469#endif
547 cp->kill = ascval; /* set kill character */ 470 cp->kill = ascval; /* set kill character */
548 while (bp > logname) { 471 while (bp > logname) {
549 write(1, erase[cp->parity], 3); 472 full_write(1, erase[cp->parity], 3);
550 bp--; 473 bp--;
551 } 474 }
552 break; 475 break;
@@ -558,7 +481,7 @@ static char *get_logname(char *logname, unsigned size_logname,
558 } else if (bp - logname >= size_logname - 1) { 481 } else if (bp - logname >= size_logname - 1) {
559 bb_error_msg_and_die("%s: input overrun", op->tty); 482 bb_error_msg_and_die("%s: input overrun", op->tty);
560 } else { 483 } else {
561 write(1, &c, 1); /* echo the character */ 484 full_write(1, &c, 1); /* echo the character */
562 *bp++ = ascval; /* and store it */ 485 *bp++ = ascval; /* and store it */
563 } 486 }
564 break; 487 break;
@@ -568,7 +491,7 @@ static char *get_logname(char *logname, unsigned size_logname,
568 /* Handle names with upper case and no lower case. */ 491 /* Handle names with upper case and no lower case. */
569 492
570#ifdef HANDLE_ALLCAPS 493#ifdef HANDLE_ALLCAPS
571 cp->capslock = caps_lock(logname); 494 cp->capslock = all_is_upcase(logname);
572 if (cp->capslock) { 495 if (cp->capslock) {
573 for (bp = logname; *bp; bp++) 496 for (bp = logname; *bp; bp++)
574 if (isupper(*bp)) 497 if (isupper(*bp))
@@ -641,7 +564,6 @@ static void termios_final(struct options *op, struct termios *tp, struct chardat
641 ioctl_or_perror_and_die(0, TCSETS, tp, "%s: TCSETS", op->tty); 564 ioctl_or_perror_and_die(0, TCSETS, tp, "%s: TCSETS", op->tty);
642} 565}
643 566
644
645#ifdef SYSV_STYLE 567#ifdef SYSV_STYLE
646#if ENABLE_FEATURE_UTMP 568#if ENABLE_FEATURE_UTMP
647/* update_utmp - update our utmp entry */ 569/* update_utmp - update our utmp entry */
@@ -666,17 +588,16 @@ static void update_utmp(const char *line)
666 utmpname(_PATH_UTMP); 588 utmpname(_PATH_UTMP);
667 setutent(); 589 setutent();
668 while ((utp = getutent()) 590 while ((utp = getutent())
669 && !(utp->ut_type == INIT_PROCESS && utp->ut_pid == mypid)) 591 && !(utp->ut_type == INIT_PROCESS && utp->ut_pid == mypid)
670 /* nothing */; 592 ) {
593 continue;
594 }
671 595
672 if (utp) { 596 /* some inits don't initialize utmp... */
597 memset(&ut, 0, sizeof(ut));
598 safe_strncpy(ut.ut_id, line + 3, sizeof(ut.ut_id));
599 if (utp)
673 memcpy(&ut, utp, sizeof(ut)); 600 memcpy(&ut, utp, sizeof(ut));
674 } else {
675 /* some inits don't initialize utmp... */
676 memset(&ut, 0, sizeof(ut));
677 safe_strncpy(ut.ut_id, line + 3, sizeof(ut.ut_id));
678 }
679 /* endutent(); */
680 601
681 strcpy(ut.ut_user, "LOGIN"); 602 strcpy(ut.ut_user, "LOGIN");
682 safe_strncpy(ut.ut_line, line, sizeof(ut.ut_line)); 603 safe_strncpy(ut.ut_line, line, sizeof(ut.ut_line));
@@ -700,42 +621,43 @@ static void update_utmp(const char *line)
700#endif /* CONFIG_FEATURE_UTMP */ 621#endif /* CONFIG_FEATURE_UTMP */
701#endif /* SYSV_STYLE */ 622#endif /* SYSV_STYLE */
702 623
703
704int getty_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 624int getty_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
705int getty_main(int argc, char **argv) 625int getty_main(int argc, char **argv)
706{ 626{
707 int nullfd; 627 int n;
708 char *logname = NULL; /* login name, given to /bin/login */ 628 char *logname; /* login name, given to /bin/login */
709 /* Merging these into "struct local" may _seem_ to reduce 629 /* Merging these into "struct local" may _seem_ to reduce
710 * parameter passing, but today's gcc will inline 630 * parameter passing, but today's gcc will inline
711 * statics which are called once anyway, so don't do that */ 631 * statics which are called once anyway, so don't do that */
712 struct chardata chardata; /* set by get_logname() */ 632 struct chardata chardata; /* set by get_logname() */
713 struct termios termios; /* terminal mode bits */ 633 struct termios termios; /* terminal mode bits */
714 struct options options = { 634 struct options options;
715 0, /* show /etc/issue (SYSV_STYLE) */ 635
716 0, /* no timeout */ 636 memset(&options, 0, sizeof(options));
717 _PATH_LOGIN, /* default login program */ 637 options.login = _PATH_LOGIN; /* default login program */
718 "tty1", /* default tty line */ 638 options.tty = "tty1"; /* default tty line */
719 "", /* modem init string */ 639 options.initstring = ""; /* modem init string */
720#ifdef ISSUE 640#ifdef ISSUE
721 ISSUE, /* default issue file */ 641 options.issue = ISSUE; /* default issue file */
722#else
723 NULL,
724#endif 642#endif
725 0, /* no baud rates known yet */
726 };
727 643
728 /* Already too late because of theoretical 644 /* Already too late because of theoretical
729 * possibility of getty --help somehow triggered 645 * possibility of getty --help somehow triggered
730 * inadvertently before we reach this. Oh well. */ 646 * inadvertently before we reach this. Oh well. */
731 logmode = LOGMODE_NONE; 647 logmode = LOGMODE_NONE;
648
649 /* Create new session, lose controlling tty, if any */
650 /* docs/ctty.htm says:
651 * "This is allowed only when the current process
652 * is not a process group leader" - is this a problem? */
732 setsid(); 653 setsid();
733 nullfd = xopen(bb_dev_null, O_RDWR); 654
734 /* dup2(nullfd, 0); - no, because of possible "getty - 9600" */ 655 n = xopen(bb_dev_null, O_RDWR);
735 /* open_tty() will take care of fd# 0 anyway */ 656 /* dup2(n, 0); - no, because of possible "getty - 9600" */
736 dup2(nullfd, 1); 657 dup2(n, 1);
737 dup2(nullfd, 2); 658 dup2(n, 2);
738 while (nullfd > 2) close(nullfd--); 659 while (n > 2)
660 close(n--);
739 /* We want special flavor of error_msg_and_die */ 661 /* We want special flavor of error_msg_and_die */
740 die_sleep = 10; 662 die_sleep = 10;
741 msg_eol = "\r\n"; 663 msg_eol = "\r\n";
@@ -744,69 +666,74 @@ int getty_main(int argc, char **argv)
744 666
745#ifdef DEBUGGING 667#ifdef DEBUGGING
746 dbf = xfopen(DEBUGTERM, "w"); 668 dbf = xfopen(DEBUGTERM, "w");
747 669 for (n = 1; n < argc; n++) {
748 { 670 debug(argv[n]);
749 int i; 671 debug("\n");
750
751 for (i = 1; i < argc; i++) {
752 debug(argv[i]);
753 debug("\n");
754 }
755 } 672 }
756#endif 673#endif
757 674
758 /* Parse command-line arguments. */ 675 /* Parse command-line arguments. */
759 parse_args(argc, argv, &options); 676 parse_args(argv, &options);
677
678 debug("calling open_tty\n");
679 /* Open the tty as standard input, if it is not "-" */
680 open_tty(options.tty);
681
682 debug("duping\n");
683 ndelay_off(0);
684 xdup2(0, 1);
685 xdup2(0, 2);
686
687 /*
688 * The following ioctl will fail if stdin is not a tty, but also when
689 * there is noise on the modem control lines. In the latter case, the
690 * common course of action is (1) fix your cables (2) give the modem more
691 * time to properly reset after hanging up. SunOS users can achieve (2)
692 * by patching the SunOS kernel variable "zsadtrlow" to a larger value;
693 * 5 seconds seems to be a good value.
694 */
695 ioctl_or_perror_and_die(0, TCGETS, &termios, "%s: TCGETS", options.tty);
760 696
761#ifdef SYSV_STYLE 697#ifdef SYSV_STYLE
762#if ENABLE_FEATURE_UTMP 698#if ENABLE_FEATURE_UTMP
763 /* Update the utmp file. */ 699 /* Update the utmp file */
764 update_utmp(options.tty); 700 update_utmp(options.tty);
765#endif 701#endif
766#endif 702#endif
767 703
768 debug("calling open_tty\n");
769 /* Open the tty as standard { input, output, error }. */
770 open_tty(options.tty, &termios, options.flags & F_LOCAL);
771
772#ifdef __linux__ 704#ifdef __linux__
773 { 705 /* Make ourself a foreground process group within our session */
774 int iv; 706 tcsetpgrp(0, getpid());
775 707 // /* Forcibly make fd 0 our controlling tty, even if another session
776 iv = getpid(); 708 // * has it as a ctty. (Another session loses ctty). */
777 ioctl(0, TIOCSPGRP, &iv); 709 // ioctl(0, TIOCSCTTY, (void*)1);
778 }
779#endif 710#endif
711
780 /* Initialize the termios settings (raw mode, eight-bit, blocking i/o). */ 712 /* Initialize the termios settings (raw mode, eight-bit, blocking i/o). */
781 debug("calling termios_init\n"); 713 debug("calling termios_init\n");
782 termios_init(&termios, options.speeds[FIRST_SPEED], &options); 714 termios_init(&termios, options.speeds[0], &options);
783 715
784 /* write the modem init string and DON'T flush the buffers */ 716 /* Write the modem init string and DON'T flush the buffers */
785 if (options.flags & F_INITSTRING) { 717 if (options.flags & F_INITSTRING) {
786 debug("writing init string\n"); 718 debug("writing init string\n");
787 write(1, options.initstring, strlen(options.initstring)); 719 full_write(1, options.initstring, strlen(options.initstring));
788 }
789
790 if (!(options.flags & F_LOCAL)) {
791 /* go to blocking write mode unless -L is specified */
792 ndelay_off(1);
793 } 720 }
794 721
795 /* Optionally detect the baud rate from the modem status message. */ 722 /* Optionally detect the baud rate from the modem status message */
796 debug("before autobaud\n"); 723 debug("before autobaud\n");
797 if (options.flags & F_PARSE) 724 if (options.flags & F_PARSE)
798 auto_baud(bb_common_bufsiz1, sizeof(bb_common_bufsiz1), &termios); 725 auto_baud(line_buf, sizeof(line_buf), &termios);
799 726
800 /* Set the optional timer. */ 727 /* Set the optional timer */
801 if (options.timeout) 728 if (options.timeout)
802 alarm(options.timeout); 729 alarm(options.timeout);
803 730
804 /* optionally wait for CR or LF before writing /etc/issue */ 731 /* Optionally wait for CR or LF before writing /etc/issue */
805 if (options.flags & F_WAITCRLF) { 732 if (options.flags & F_WAITCRLF) {
806 char ch; 733 char ch;
807 734
808 debug("waiting for cr-lf\n"); 735 debug("waiting for cr-lf\n");
809 while (read(0, &ch, 1) == 1) { 736 while (safe_read(0, &ch, 1) == 1) {
810 ch &= 0x7f; /* strip "parity bit" */ 737 ch &= 0x7f; /* strip "parity bit" */
811#ifdef DEBUGGING 738#ifdef DEBUGGING
812 fprintf(dbf, "read %c\n", ch); 739 fprintf(dbf, "read %c\n", ch);
@@ -816,31 +743,42 @@ int getty_main(int argc, char **argv)
816 } 743 }
817 } 744 }
818 745
746 logname = NULL;
819 chardata = init_chardata; 747 chardata = init_chardata;
820 if (!(options.flags & F_NOPROMPT)) { 748 if (!(options.flags & F_NOPROMPT)) {
821 /* Read the login name. */ 749 /* NB:termios_init already set line speed
822 debug("reading login name\n"); 750 * to options.speeds[0] */
823 logname = get_logname(bb_common_bufsiz1, sizeof(bb_common_bufsiz1), 751 int baud_index = 0;
752
753 while (1) {
754 /* Read the login name. */
755 debug("reading login name\n");
756 logname = get_logname(line_buf, sizeof(line_buf),
824 &options, &chardata, &termios); 757 &options, &chardata, &termios);
825 while (logname == NULL) 758 if (logname)
826 next_speed(&termios, &options); 759 break;
760 /* we are here only if options.numspeed > 1 */
761 baud_index = (baud_index + 1) % options.numspeed;
762 termios.c_cflag &= ~CBAUD;
763 termios.c_cflag |= options.speeds[baud_index];
764 ioctl(0, TCSETS, &termios);
765 }
827 } 766 }
828 767
829 /* Disable timer. */ 768 /* Disable timer. */
830
831 if (options.timeout) 769 if (options.timeout)
832 alarm(0); 770 alarm(0);
833 771
834 /* Finalize the termios settings. */ 772 /* Finalize the termios settings. */
835
836 termios_final(&options, &termios, &chardata); 773 termios_final(&options, &termios, &chardata);
837 774
838 /* Now the newline character should be properly written. */ 775 /* Now the newline character should be properly written. */
839 776 full_write(1, "\n", 1);
840 write(1, "\n", 1);
841 777
842 /* Let the login program take care of password validation. */ 778 /* Let the login program take care of password validation. */
843 779 /* We use PATH because we trust that root doesn't set "bad" PATH,
844 execl(options.login, options.login, "--", logname, (char *) 0); 780 * and getty is not suid-root applet. */
781 /* Hmm... with -n, logname == NULL! Is it ok? */
782 BB_EXECLP(options.login, options.login, "--", logname, NULL);
845 bb_error_msg_and_die("%s: can't exec %s", options.tty, options.login); 783 bb_error_msg_and_die("%s: can't exec %s", options.tty, options.login);
846} 784}