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