diff options
| author | Denis Vlasenko <vda.linux@googlemail.com> | 2007-12-04 09:48:40 +0000 |
|---|---|---|
| committer | Denis Vlasenko <vda.linux@googlemail.com> | 2007-12-04 09:48:40 +0000 |
| commit | d0bbbdcd6eefd249637f153f9d29b37c7f545e33 (patch) | |
| tree | d2a4453c90ae1d7fc1090a907ad8fe3aa21429fe | |
| parent | 7221c8c22dd527700204eb5cc4d0651af4273f4f (diff) | |
| download | busybox-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.htm | 474 | ||||
| -rw-r--r-- | loginutils/getty.c | 392 |
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 | ||
| 11 | description of threads, processes, process groups and sessions. | ||
| 12 | </p><p>A session contains a number of process groups, and a process group | ||
| 13 | contains a number of processes, and a process contains a number | ||
| 14 | of threads. | ||
| 15 | </p><p>A session can have a controlling tty. | ||
| 16 | At most one process group in a session can be a foreground process group. | ||
| 17 | An interrupt character typed on a tty ("Teletype", i.e., terminal) | ||
| 18 | causes a signal to be sent to all members of the foreground process group | ||
| 19 | in 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, | ||
| 21 | process 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> | ||
| 30 | system call: | ||
| 31 | </p><blockquote> | ||
| 32 | <pre>pid_t p; | ||
| 33 | |||
| 34 | p = fork(); | ||
| 35 | if (p == (pid_t) -1) | ||
| 36 | /* ERROR */ | ||
| 37 | else if (p == 0) | ||
| 38 | /* CHILD */ | ||
| 39 | else | ||
| 40 | /* PARENT */ | ||
| 41 | </pre> | ||
| 42 | </blockquote> | ||
| 43 | <p>This creates a child as a duplicate of its parent. | ||
| 44 | Parent and child are identical in almost all respects. | ||
| 45 | In the code they are distinguished by the fact that the parent | ||
| 46 | learns the process ID of its child, while <code>fork()</code> | ||
| 47 | returns 0 in the child. (It can find the process ID of its | ||
| 48 | parent 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 | |||
| 58 | or | ||
| 59 | <blockquote> | ||
| 60 | <pre>return n; | ||
| 61 | </pre> | ||
| 62 | </blockquote> | ||
| 63 | |||
| 64 | from its <code>main()</code> procedure. It returns the single byte <code>n</code> | ||
| 65 | to 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; | ||
| 73 | int status; | ||
| 74 | |||
| 75 | p = wait(&status); | ||
| 76 | </pre> | ||
| 77 | </blockquote> | ||
| 78 | |||
| 79 | and 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 | ||
| 86 | is a <i>zombie</i>. It need only store these two bytes: | ||
| 87 | exit code and reason for termination. | ||
| 88 | </p><p>On the other hand, if the parent dies first, <code>init</code> (process 1) | ||
| 89 | inherits 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 | ||
| 101 | disallowed by <code>stty tostop</code>). | ||
| 102 | </p><p>Apart from ^Z there also is ^Y. The former stops the process | ||
| 103 | when it is typed, the latter stops it when it is read. | ||
| 104 | </p><p>Signals generated by typing the corresponding character on some tty | ||
| 105 | are sent to all processes that are in the foreground process group | ||
| 106 | of 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. | ||
| 120 | Many signals have as default action to kill the target. | ||
| 121 | (Sometimes with an additional core dump, when such is | ||
| 122 | allowed by rlimit.) | ||
| 123 | The signals <code>SIGCHLD</code> and <code>SIGWINCH</code> | ||
| 124 | are ignored by default. | ||
| 125 | All except <code>SIGKILL</code> and <code>SIGSTOP</code> can be | ||
| 126 | caught or ignored or blocked. | ||
| 127 | For 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>, | ||
| 133 | identified by its <i>process group ID</i>. | ||
| 134 | (When the process is created, it becomes a member of the process group | ||
| 135 | of its parent.) | ||
| 136 | By convention, the process group ID of a process group | ||
| 137 | equals the process ID of the first member of the process group, | ||
| 138 | called the <i>process group leader</i>. | ||
| 139 | A process finds the ID of its process group using the system call | ||
| 140 | <code>getpgrp()</code>, or, equivalently, <code>getpgid(0)</code>. | ||
| 141 | One 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), | ||
| 144 | PID (process ID), PGID (process group ID) and SID (session ID) | ||
| 145 | of processes. With a shell that does not know about job control, | ||
| 146 | like <code>ash</code>, each of its children will be in the same session | ||
| 147 | and have the same process group as the shell. With a shell that knows | ||
| 148 | about job control, like <code>bash</code>, the processes of one pipeline. like | ||
| 149 | </p><blockquote> | ||
| 150 | <pre>% cat paper | ideal | pic | tbl | eqn | ditroff > out | ||
| 151 | </pre> | ||
| 152 | </blockquote> | ||
| 153 | |||
| 154 | form 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 | |||
| 164 | If <code>pgid == pid</code> or <code>pgid == 0</code> then this creates | ||
| 165 | a new process group with process group leader <code>pid</code>. | ||
| 166 | Otherwise, this puts <code>pid</code> into the already existing | ||
| 167 | process group <code>pgid</code>. | ||
| 168 | A zero <code>pid</code> refers to the current process. | ||
| 169 | The 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, | ||
| 174 | and 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. | ||
| 176 | It 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(); | ||
| 184 | if (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 | |||
| 196 | This ensures that regardless of whether parent or child is scheduled | ||
| 197 | first, 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, &status, ...); | ||
| 209 | </pre> | ||
| 210 | </blockquote> | ||
| 211 | |||
| 212 | or in a specified process group: | ||
| 213 | <blockquote> | ||
| 214 | <pre>waitpid(-pgrp, &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 | ||
| 221 | the <i>foreground process group</i> of that session. | ||
| 222 | The tty input and tty signals (signals generated by ^C, ^Z, etc.) | ||
| 223 | go to processes in this foreground process group. | ||
| 224 | </p><p>A process can determine the foreground process group in its session | ||
| 225 | using <code>tcgetpgrp(fd)</code>, where <code>fd</code> refers to its | ||
| 226 | controlling tty. If there is none, this returns a random value | ||
| 227 | larger than 1 that is not a process group ID. | ||
| 228 | </p><p>A process can set the foreground process group in its session | ||
| 229 | using <code>tcsetpgrp(fd,pgrp)</code>, where <code>fd</code> refers to its | ||
| 230 | controlling tty, and <code>pgrp</code> is a process group in the | ||
| 231 | its session, and this session still is associated to the controlling | ||
| 232 | tty of the calling process. | ||
| 233 | </p><p>How does one get <code>fd</code>? By definition, <code>/dev/tty</code> | ||
| 234 | refers to the controlling tty, entirely independent of redirects | ||
| 235 | of standard input and output. (There is also the function | ||
| 236 | <code>ctermid()</code> to get the name of the controlling terminal. | ||
| 237 | On a POSIX standard system it will return <code>/dev/tty</code>.) | ||
| 238 | Opening the name of the | ||
| 239 | controlling 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 | ||
| 244 | process group are <i>background process groups</i>. | ||
| 245 | Since the user at the keyboard is interacting with foreground | ||
| 246 | processes, background processes should stay away from it. | ||
| 247 | When a background process reads from the terminal it gets | ||
| 248 | a SIGTTIN signal. Normally, that will stop it, the job control shell | ||
| 249 | notices and tells the user, who can say <code>fg</code> to continue | ||
| 250 | this background process as a foreground process, and then this | ||
| 251 | process can read from the terminal. But if the background process | ||
| 252 | ignores or blocks the SIGTTIN signal, or if its process group | ||
| 253 | is orphaned (see below), then the read() returns an EIO error, | ||
| 254 | and no signal is sent. (Indeed, the idea is to tell the process | ||
| 255 | that reading from the terminal is not allowed right now. | ||
| 256 | If 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 | ||
| 258 | a SIGTTOU signal. May: namely, when the flag that this must happen | ||
| 259 | is set (it is off by default). One can set the flag by | ||
| 260 | </p><blockquote> | ||
| 261 | <pre>% stty tostop | ||
| 262 | </pre> | ||
| 263 | </blockquote> | ||
| 264 | |||
| 265 | and clear it again by | ||
| 266 | <blockquote> | ||
| 267 | <pre>% stty -tostop | ||
| 268 | </pre> | ||
| 269 | </blockquote> | ||
| 270 | |||
| 271 | and inspect it by | ||
| 272 | <blockquote> | ||
| 273 | <pre>% stty -a | ||
| 274 | </pre> | ||
| 275 | </blockquote> | ||
| 276 | |||
| 277 | Again, if TOSTOP is set but the background process ignores or blocks | ||
| 278 | the SIGTTOU signal, or if its process group is orphaned (see below), | ||
| 279 | then 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. | ||
| 284 | It may terminate before the others, and then the process group is | ||
| 285 | without leader. | ||
| 286 | </p><p>A process group is called <i>orphaned</i> when <i>the | ||
| 287 | parent of every member is either in the process group | ||
| 288 | or outside the session</i>. | ||
| 289 | In particular, the process group of the session leader | ||
| 290 | is always orphaned. | ||
| 291 | </p><p>If termination of a process causes a process group to become | ||
| 292 | orphaned, and some member is stopped, then all are sent first SIGHUP | ||
| 293 | and then SIGCONT. | ||
| 294 | </p><p>The idea is that perhaps the parent of the process group leader | ||
| 295 | is a job control shell. (In the same session but a different | ||
| 296 | process group.) As long as this parent is alive, it can | ||
| 297 | handle the stopping and starting of members in the process group. | ||
| 298 | When it dies, there may be nobody to continue stopped processes. | ||
| 299 | Therefore, these stopped processes are sent SIGHUP, so that they | ||
| 300 | die 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 | ||
| 302 | orphaned, 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 | ||
| 304 | by termination of a process: either it was a parent and not itself | ||
| 305 | in the process group, or it was the last element of the process group | ||
| 306 | with a parent outside but in the same session. | ||
| 307 | Furthermore, that a process group can become orphaned | ||
| 308 | other than by termination of a process, namely when some | ||
| 309 | member 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 | ||
| 316 | of its parent.) | ||
| 317 | By convention, the session ID of a session | ||
| 318 | equals the process ID of the first member of the session, | ||
| 319 | called the <i>session leader</i>. | ||
| 320 | A 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>, | ||
| 323 | that then also is called the controlling tty of each of | ||
| 324 | its member processes. | ||
| 325 | A file descriptor for the controlling tty is obtained by | ||
| 326 | opening <code>/dev/tty</code>. (And when that fails, there was no | ||
| 327 | controlling tty.) Given a file descriptor for the controlling tty, | ||
| 328 | one may obtain the SID using <code>tcgetsid(fd)</code>. | ||
| 329 | </p><p>A session is often set up by a login process. The terminal | ||
| 330 | on which one is logged in then becomes the controlling tty | ||
| 331 | of the session. All processes that are descendants of the | ||
| 332 | login 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 | |||
| 342 | This is allowed only when the current process is not a process group leader. | ||
| 343 | In order to be sure of that we fork first: | ||
| 344 | <blockquote> | ||
| 345 | <pre>p = fork(); | ||
| 346 | if (p) exit(0); | ||
| 347 | pid = setsid(); | ||
| 348 | </pre> | ||
| 349 | </blockquote> | ||
| 350 | |||
| 351 | The result is that the current process (with process ID <code>pid</code>) | ||
| 352 | becomes session leader of a new session with session ID <code>pid</code>. | ||
| 353 | Moreover, it becomes process group leader of a new process group. | ||
| 354 | Both session and process group contain only the single process <code>pid</code>. | ||
| 355 | Furthermore, this process has no controlling tty. | ||
| 356 | <p>The restriction that the current process must not be a process group leader | ||
| 357 | is needed: otherwise its PID serves as PGID of some existing process group | ||
| 358 | and 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, | ||
| 363 | this is a great mystery. | ||
| 364 | </p><p>The System V approach is that the first tty opened by the process | ||
| 365 | becomes 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 | |||
| 372 | to get a controlling tty. | ||
| 373 | <p>Linux tries to be compatible with both, as always, and this | ||
| 374 | results in a very obscure complex of conditions. Roughly: | ||
| 375 | </p><p>The <code>TIOCSCTTY</code> ioctl will give us a controlling tty, | ||
| 376 | provided that (i) the current process is a session leader, | ||
| 377 | and (ii) it does not yet have a controlling tty, and | ||
| 378 | (iii) maybe the tty should not already control some other session; | ||
| 379 | if it does it is an error if we aren't root, or we steal the tty | ||
| 380 | if we are all-powerful. | ||
| 381 | </p><p>Opening some terminal will give us a controlling tty, | ||
| 382 | provided 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 | ||
| 393 | from its controlling tty. Above we saw that <code>setsid()</code> | ||
| 394 | will remove the controlling tty. Also the ioctl TIOCNOTTY does this. | ||
| 395 | Moreover, in order not to get a controlling tty again as soon as it | ||
| 396 | opens a tty, the process has to fork once more, to assure that it | ||
| 397 | is 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, | ||
| 410 | then a SIGHUP is sent to the session leader. | ||
| 411 | Any 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 | ||
| 414 | is closed (for the last time), then a SIGHUP is sent to the foreground | ||
| 415 | process group of the slave side. | ||
| 416 | </p><p>When the session leader dies, a SIGHUP is sent to all processes | ||
| 417 | in the foreground process group. Moreover, the terminal stops being | ||
| 418 | the controlling terminal of this session (so that it can become | ||
| 419 | the controlling terminal of another session). | ||
| 420 | </p><p>Thus, if the terminal goes away and the session leader is | ||
| 421 | a job control shell, then it can handle things for its descendants, | ||
| 422 | e.g. by sending them again a SIGHUP. | ||
| 423 | If on the other hand the session leader is an innocent process | ||
| 424 | that does not catch SIGHUP, it will die, and all foreground processes | ||
| 425 | get 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 | ||
| 431 | as the parent thread) are started using the <code>clone</code> system | ||
| 432 | call using the <code>CLONE_THREAD</code> flag. Threads are distinguished | ||
| 433 | by a <i>thread ID</i> (TID). An ordinary process has a single thread | ||
| 434 | with TID equal to PID. The system call <code>gettid()</code> returns the | ||
| 435 | TID. 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 << EOF > gettid-demo.c | ||
| 439 | #include <unistd.h> | ||
| 440 | #include <sys/types.h> | ||
| 441 | #define CLONE_SIGHAND 0x00000800 | ||
| 442 | #define CLONE_THREAD 0x00010000 | ||
| 443 | #include <linux/unistd.h> | ||
| 444 | #include <errno.h> | ||
| 445 | _syscall0(pid_t,gettid) | ||
| 446 | |||
| 447 | int thread(void *p) { | ||
| 448 | printf("thread: %d %d\n", gettid(), getpid()); | ||
| 449 | } | ||
| 450 | |||
| 451 | main() { | ||
| 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 | } | ||
| 462 | EOF | ||
| 463 | % cc -o gettid-demo gettid-demo.c | ||
| 464 | % ./gettid-demo | ||
| 465 | clone returns 21826 | ||
| 466 | parent: 21825 21825 | ||
| 467 | thread: 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 |
| 35 | extern void updwtmp(const char *filename, const struct utmp *ut); | 35 | extern void updwtmp(const char *filename, const struct utmp *ut); |
| 36 | static 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. */ | ||
| 85 | struct options { | 82 | struct 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 | ||
| 96 | static 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. */ |
| 110 | struct chardata { | 94 | struct 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. */ |
| 121 | static const struct chardata init_chardata = { | 106 | static 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. */ | 116 | static 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. */ |
| 134 | static char *fakehost = NULL; | 131 | static 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 */ |
| 163 | static void parse_speeds(struct options *op, char *arg) | 160 | static 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 */ |
| 181 | static void parse_args(int argc, char **argv, struct options *op) | 178 | static 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 } */ |
| 232 | static void open_tty(const char *tty, struct termios *tp, int local) | 227 | static 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 */ | ||
| 431 | static 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 */ |
| 443 | static void do_prompt(struct options *op, struct termios *tp) | 368 | static 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 */ |
| 454 | static int caps_lock(const char *s) | 379 | static 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 */ |
| 465 | static char *get_logname(char *logname, unsigned size_logname, | 390 | static 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 | |||
| 704 | int getty_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | 624 | int getty_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
| 705 | int getty_main(int argc, char **argv) | 625 | int 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 | } |
