--- /dev/null
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
--- /dev/null
+Sysvinit is Copyright (C) 1991-2004 Miquel van Smoorenburg
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+Send patches to sysvinit@savannah.nognu.org
+
+The of the start-stop-daemon
+
+ * A rewrite of the original Debian's start-stop-daemon Perl script
+ * in C (faster - it is executed many times during system startup).
+ *
+ * Written by Marek Michalkiewicz <marekm@i17linuxb.ists.pwr.wroc.pl>,
+ * public domain.
+
--- /dev/null
+
+contrib Unofficial stuff
+doc Documentation, mostly obsolete
+man Manual pages, not obsolete
+obsolete Really obsolete stuff ;)
+src Source code
+
--- /dev/null
+
+There are several things on the wishlist. See also the "wishlist" bugs filed
+against sysvinit in the debian bugs system (http://www.debian.org/Bugs/).
+
+1. A special target for kbrequest, so that extra CHILDs are
+ created (each one needs its own utmp/wtmp bookkeeping)
+2. Extend the initreq.h interface?
+3. Add GNU last long options to last
+
+4. Write all boot messages to a logfile
+ Problem: TIOCCONS ioctl redirects console output, it doesn't copy it.
+ I think this is not easily possible without kernel support.
+ I do not like the idea of booting with a pseudo tty as console and
+ a redirect process behind it writing to both the real console and a
+ logfile - too fragile.
+
--- /dev/null
+I proposed moving some stuff to a seperate file, such as the
+re-exec routines. Alexander wrote:
+
+
+According to Alexander Viro <viro@math.psu.edu>:
+> As for the code separation - I think it's nice. Actually, read_inittab()
+> with get_part() and newFamily are also pretty separatable. Another good
+> set is any(), spawn(), startup(), spawn_emerg() and start_if_needed().
+> BTW, fail_check();process_signals(); is equivalent to process_signal();
+> fail_check();. I think that swapping them (in main loop) would be a good
+> idea - then we can move fail_check() into start_if_needed(). And one more
+> - I'ld propose to move start_if_needed to the beginning of the main loop,
+> as in
+> foo();
+> while(1) { bar();foo();
+> #if 0
+> baz();
+> #endif
+> }
+> to
+> while(1) { foo();bar();
+> #if 0
+> baz();
+> #endif
+> }
+>
+>
+> What do you think about it?
+
--- /dev/null
+Start-stop-daemon is the program that is used by the DEBIAN style init
+scripts to start and stop services. This program is part of the "dpkg"
+package by Ian Jackson. However there is also a seperate C version (the
+original is in perl) available written by Marek Michalkiewicz. I'm including
+it for your convinience.
+
+Note that the latest debian dpkg packages (4.0.18 and later) contain
+a much improved update-rc.d. This code is almost a year old.
+
+The original announcement follows:
+
+
+From: Marek Michalkiewicz <marekm@i17linuxb.ists.pwr.wroc.pl>
+Message-Id: <199606060324.FAA19493@i17linuxb.ists.pwr.wroc.pl>
+Subject: Fast start-stop-daemon in C
+To: debian-devel@lists.debian.org
+Date: Thu, 6 Jun 1996 05:24:18 +0200 (MET DST)
+
+Some time ago I wrote a faster C replacement for the start-stop-daemon
+perl script. I use it for some time now (the most recent changes were
+just a nicer help screen; the code is quite stable).
+
+This makes the system boot faster (especially on low end machines),
+and important system startup scripts no longer depend on another big
+package (perl). Maybe in the future we can get to the point where
+a minimal system will work without perl installed at all (packages
+which need it in {pre,post}{inst,rm} scripts would depend on perl).
+
+The only problem known so far to me is that I have to reinstall this
+program after every dpkg upgrade which overwrites it with that nice
+slooow perl script :-).
+
+Just compile this program and drop the binary in /usr/sbin instead
+of the original /usr/sbin/start-stop-daemon perl script (make a copy
+of it first, just in case). See below for source code. I placed it
+in the public domain, but if it has to be GPL-ed to be included in
+dpkg, just tell me. Including it in dpkg would close Bug#1670.
+
+I am posting it here so that it can be tested by more people than
+just me. Bugs are unlikely though.
+
+Have fun,
+
+Marek
+
--- /dev/null
+/*
+ * A rewrite of the original Debian's start-stop-daemon Perl script
+ * in C (faster - it is executed many times during system startup).
+ *
+ * Written by Marek Michalkiewicz <marekm@i17linuxb.ists.pwr.wroc.pl>,
+ * public domain.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <signal.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <dirent.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <pwd.h>
+
+#define VERSION "version 0.3, 1996-06-05"
+
+static int testmode = 0;
+static int quietmode = 0;
+static int exitnodo = 1;
+static int start = 0;
+static int stop = 0;
+static int signal_nr = 15;
+static int user_id = -1;
+static const char *userspec = NULL;
+static const char *cmdname = NULL;
+static char *execname = NULL;
+static char *startas = NULL;
+static const char *pidfile = NULL;
+static const char *progname = "";
+
+static struct stat exec_stat;
+
+struct pid_list {
+ struct pid_list *next;
+ int pid;
+};
+
+static struct pid_list *found = NULL;
+static struct pid_list *killed = NULL;
+
+static void *xmalloc(int size);
+static void push(struct pid_list **list, int pid);
+static void do_help(void);
+static void parse_options(int argc, char * const *argv);
+static int pid_is_exec(int pid, const struct stat *esb);
+static int pid_is_user(int pid, int uid);
+static int pid_is_cmd(int pid, const char *name);
+static void check(int pid);
+static void do_pidfile(const char *name);
+static void do_procfs(void);
+static void do_stop(void);
+
+#ifdef __GNUC__
+static void fatal(const char *format, ...)
+ __attribute__((noreturn, format(printf, 1, 2)));
+static void badusage(const char *msg)
+ __attribute__((noreturn));
+#else
+static void fatal(const char *format, ...);
+static void badusage(const char *msg);
+#endif
+
+static void
+fatal(const char *format, ...)
+{
+ va_list arglist;
+
+ fprintf(stderr, "%s: ", progname);
+ va_start(arglist, format);
+ vfprintf(stderr, format, arglist);
+ va_end(arglist);
+ putc('\n', stderr);
+ exit(2);
+}
+
+
+static void *
+xmalloc(int size)
+{
+ void *ptr;
+
+ ptr = malloc(size);
+ if (ptr)
+ return ptr;
+ fatal("malloc(%d) failed", size);
+}
+
+
+static void
+push(struct pid_list **list, int pid)
+{
+ struct pid_list *p;
+
+ p = xmalloc(sizeof(*p));
+ p->next = *list;
+ p->pid = pid;
+ *list = p;
+}
+
+
+static void
+do_help(void)
+{
+ printf("\
+start-stop-daemon for Debian Linux - small and fast C version written by\n\
+Marek Michalkiewicz <marekm@i17linuxb.ists.pwr.wroc.pl>, public domain.\n"
+VERSION "\n\
+\n\
+Usage:
+ start-stop-daemon -S|--start options ... -- arguments ...\n\
+ start-stop-daemon -K|--stop options ...\n\
+ start-stop-daemon -H|--help\n\
+ start-stop-daemon -V|--version\n\
+\n\
+Options (at least one of --exec|--pidfile|--user is required):
+ -x|--exec <executable> program to start/check if it is running\n\
+ -p|--pidfile <pid-file> pid file to check\n\
+ -u|--user <username>|<uid> stop this user's processes\n\
+ -n|--name <process-name> stop processes with this name\n\
+ -s|--signal <signal> signal to send (default 15)\n\
+ -a|--startas <pathname> program to start (default <executable>)\n\
+ -t|--test test mode, don't do anything\n\
+ -o|--oknodo exit status 0 (not 1) if nothing done\n\
+ -q|--quiet | -v, --verbose\n\
+\n\
+Exit status: 0 = done 1 = nothing done (=> 0 if --oknodo) 2 = trouble\n");
+}
+
+
+static void
+badusage(const char *msg)
+{
+ if (msg && *msg)
+ fprintf(stderr, "%s: %s\n", progname, msg);
+ fprintf(stderr, "Try `%s --help' for more information.\n", progname);
+ exit(2);
+}
+
+
+static void
+parse_options(int argc, char * const *argv)
+{
+ static struct option longopts[] = {
+ { "help", 0, NULL, 'H'},
+ { "stop", 0, NULL, 'K'},
+ { "start", 0, NULL, 'S'},
+ { "version", 0, NULL, 'V'},
+ { "startas", 1, NULL, 'a'},
+ { "name", 1, NULL, 'n'},
+ { "oknodo", 0, NULL, 'o'},
+ { "pidfile", 1, NULL, 'p'},
+ { "quiet", 0, NULL, 'q'},
+ { "signal", 1, NULL, 's'},
+ { "test", 0, NULL, 't'},
+ { "user", 1, NULL, 'u'},
+ { "verbose", 0, NULL, 'v'},
+ { "exec", 1, NULL, 'x'},
+ { NULL, 0, NULL, 0}
+ };
+ int c;
+
+ for (;;) {
+ c = getopt_long(argc, argv, "HKSVa:n:op:qs:tu:vx:",
+ longopts, (int *) 0);
+ if (c == -1)
+ break;
+ switch (c) {
+ case 'H': /* --help */
+ do_help();
+ exit(0);
+ case 'K': /* --stop */
+ stop = 1;
+ break;
+ case 'S': /* --start */
+ start = 1;
+ break;
+ case 'V': /* --version */
+ printf("start-stop-daemon " VERSION "\n");
+ exit(0);
+ case 'a': /* --startas <pathname> */
+ startas = optarg;
+ break;
+ case 'n': /* --name <process-name> */
+ cmdname = optarg;
+ break;
+ case 'o': /* --oknodo */
+ exitnodo = 0;
+ break;
+ case 'p': /* --pidfile <pid-file> */
+ pidfile = optarg;
+ break;
+ case 'q': /* --quiet */
+ quietmode = 1;
+ break;
+ case 's': /* --signal <signal> */
+ if (sscanf(optarg, "%d", &signal_nr) != 1)
+ badusage("--signal takes a numeric argument");
+ break;
+ case 't': /* --test */
+ testmode = 1;
+ break;
+ case 'u': /* --user <username>|<uid> */
+ userspec = optarg;
+ break;
+ case 'v': /* --verbose */
+ quietmode = -1;
+ break;
+ case 'x': /* --exec <executable> */
+ execname = optarg;
+ break;
+ default:
+ badusage(""); /* message printed by getopt */
+ }
+ }
+
+ if (start == stop)
+ badusage("need one of --start or --stop");
+
+ if (!execname && !pidfile && !userspec)
+ badusage("need at least one of --exec, --pidfile or --user");
+
+ if (!startas)
+ startas = execname;
+
+ if (start && !startas)
+ badusage("--start needs --exec or --startas");
+}
+
+
+static int
+pid_is_exec(int pid, const struct stat *esb)
+{
+ struct stat sb;
+ char buf[32];
+
+ sprintf(buf, "/proc/%d/exe", pid);
+ if (stat(buf, &sb) != 0)
+ return 0;
+ return (sb.st_dev == esb->st_dev && sb.st_ino == esb->st_ino);
+}
+
+
+static int
+pid_is_user(int pid, int uid)
+{
+ struct stat sb;
+ char buf[32];
+
+ sprintf(buf, "/proc/%d", pid);
+ if (stat(buf, &sb) != 0)
+ return 0;
+ return (sb.st_uid == uid);
+}
+
+
+static int
+pid_is_cmd(int pid, const char *name)
+{
+ char buf[32];
+ FILE *f;
+ int c;
+
+ sprintf(buf, "/proc/%d/stat", pid);
+ f = fopen(buf, "r");
+ if (!f)
+ return 0;
+ while ((c = getc(f)) != EOF && c != '(')
+ ;
+ if (c != '(') {
+ fclose(f);
+ return 0;
+ }
+ /* this hopefully handles command names containing ')' */
+ while ((c = getc(f)) != EOF && c == *name)
+ name++;
+ fclose(f);
+ return (c == ')' && *name == '\0');
+}
+
+
+static void
+check(int pid)
+{
+ if (execname && !pid_is_exec(pid, &exec_stat))
+ return;
+ if (userspec && !pid_is_user(pid, user_id))
+ return;
+ if (cmdname && !pid_is_cmd(pid, cmdname))
+ return;
+ push(&found, pid);
+}
+
+
+static void
+do_pidfile(const char *name)
+{
+ FILE *f;
+ int pid;
+
+ f = fopen(name, "r");
+ if (f) {
+ if (fscanf(f, "%d", &pid) == 1)
+ check(pid);
+ fclose(f);
+ }
+}
+
+
+static void
+do_procfs(void)
+{
+ DIR *procdir;
+ struct dirent *entry;
+ int foundany, pid;
+
+ procdir = opendir("/proc");
+ if (!procdir)
+ fatal("opendir /proc: %s", strerror(errno));
+
+ foundany = 0;
+ while ((entry = readdir(procdir)) != NULL) {
+ if (sscanf(entry->d_name, "%d", &pid) != 1)
+ continue;
+ foundany++;
+ check(pid);
+ }
+ closedir(procdir);
+ if (!foundany)
+ fatal("nothing in /proc - not mounted?");
+}
+
+
+static void
+do_stop(void)
+{
+ char what[1024];
+ struct pid_list *p;
+
+ if (cmdname)
+ strcpy(what, cmdname);
+ else if (execname)
+ strcpy(what, execname);
+ else if (pidfile)
+ sprintf(what, "process in pidfile `%s'", pidfile);
+ else if (userspec)
+ sprintf(what, "process(es) owned by `%s'", userspec);
+ else
+ fatal("internal error, please report");
+
+ if (!found) {
+ if (quietmode <= 0)
+ printf("no %s found; none killed.\n", what);
+ exit(exitnodo);
+ }
+ for (p = found; p; p = p->next) {
+ if (testmode)
+ printf("would send signal %d to %d.\n",
+ signal_nr, p->pid);
+ else if (kill(p->pid, signal_nr) == 0)
+ push(&killed, p->pid);
+ else
+ printf("%s: warning: failed to kill %d: %s\n",
+ progname, p->pid, strerror(errno));
+ }
+ if (quietmode < 0 && killed) {
+ printf("stopped %s (pid", what);
+ for (p = killed; p; p = p->next)
+ printf(" %d", p->pid);
+ printf(").\n");
+ }
+}
+
+
+int
+main(int argc, char **argv)
+{
+ progname = argv[0];
+
+ parse_options(argc, argv);
+ argc -= optind;
+ argv += optind;
+
+ if (execname && stat(execname, &exec_stat))
+ fatal("stat %s: %s", execname, strerror(errno));
+
+ if (userspec && sscanf(userspec, "%d", &user_id) != 1) {
+ struct passwd *pw;
+
+ pw = getpwnam(userspec);
+ if (!pw)
+ fatal("user `%s' not found\n", userspec);
+
+ user_id = pw->pw_uid;
+ }
+
+ if (pidfile)
+ do_pidfile(pidfile);
+ else
+ do_procfs();
+
+ if (stop) {
+ do_stop();
+ exit(0);
+ }
+
+ if (found) {
+ if (quietmode <= 0)
+ printf("%s already running.\n", execname);
+ exit(exitnodo);
+ }
+ if (testmode) {
+ printf("would start %s ", startas);
+ while (argc-- > 0)
+ printf("%s ", *argv++);
+ printf(".\n");
+ exit(0);
+ }
+ if (quietmode < 0)
+ printf("starting %s ...\n", startas);
+ *--argv = startas;
+ execv(startas, argv);
+ fatal("unable to start %s: %s", startas, strerror(errno));
+}
+
--- /dev/null
+Date: Mon, 14 Apr 1997 15:39:08 +0100 (BST)
+From: Zefram <zefram@dcs.warwick.ac.uk>
+Message-Id: <25042.199704141439@stone.dcs.warwick.ac.uk>
+Subject: SysVinit patch
+To: miquels@drinkel.cistron.nl
+
+Hi. Enclosed is a patch for SysVinit that I am finding tiresome to
+keep updating; you might like to fold it into your official releases.
+It does the following:
+
+* shutdown: use /etc/shutdown.allow if an option -s is given instead
+ of testing for a controlling tty. The tty check was inflexible and
+ unreliable.
+
+* sulogin(8): refer to inittab(5) instead of inittab(4).
+
+* sulogin: use libc passwd/shadow functions instead of home-grown ones
+ (there's just no point in using the special version).
+
+* shutdown/sulogin: don't rely on the username "root" -- it's the UID
+ that counts.
+
+-zefram
+
+begin 644 initdiffs.gz
+M'XL(``IG43,"`^T[:U/;R+*?E5_1>&L3&\G&#Y[FD%H")/%9)U"8G-0](44)
+M>62KD"6M1K+7)YO_?KM[1K)DFP"[V:T]=2^5`FFFIZ??W=.C##W7A;H3@YS+
+MJ1=X2;W=V#W8DN,T&8:SH+%?GC`+,\\V-S?O76:\CCWX9QI`:QO_=3N[W68;
+M6@<'N\_J]?K].(T!+CF.8FAUH-WI-G>Z.QU:MD>;%7]X\U;+:NT!OP(T7L&6
+MO/6"'!N-7;Z"3W!=3^BY=PE2./#YV48^'M^-`\?%(9J&Q)L(7M3#R9D=!UXP
+MJD^$E/9(*)CKRM>O7YD#M34^U?_0UO*I>Z\3P^Z!M;>MQ$"DX>M^*R--_!KY
+M-DHZC.>0H0L#2,8"G'`RL8,A^%X@<,!.8.;Y/MP*I#5((`G!]GU$D4H1R\:"
+M!),>OGSY`E"7]-*[@&OWU75=7KL7^'X6R#36"&U>#'::C,/8DV)(6$E(0%)B
+M*M`4$C$!3X(?CD8(X04-Q'(UQA'\%X3Q!,F8$Z(AS,8BH,TR.>..!.0@!,[>
+MSFF.[`K'J_LU"_'PBI,D]NO'?E(_%3[!1S%*0@P;``,A%!7"2;R0</>.'0=G
+MX20,DCCT%4^WP@]GX(8Q3$+DS0M<(HM6-)0X2"Z0RR53)DNF1V^$!N`C$:,E
+MP/:^WJJ;.U9K8=?(TLG59;]^W+^JGY[UB64;:=`\6,2U$PLD)AB!'8`=17$8
+MQ9Z="$`UHMJ]@+'TMD3B;)%T$OL6L324C"?"#J32EIB*>!ZB,<S&(8QME--X
+M+CT4+M@L$T2#Q"O3"63H"[@3\]O0CH>H@4#IM:!2YA"N0B)URA:%^UGH`DL*
+MI+7.6#AW+!I4B.<R(\IH_H.*92,J6@B:,.(A4D.7MYQZ<9(BH9HPV8">>[^A
+MN'$X*9@*T>0EB@99(((0NQZRF0DOUQNB"6<:)RD"N2,'Z3'_`7E6A#J2C`&)
+M1H(#&[V/*&=),]:9EXP5A"<38B028>23X2C?B46)872F)2ZAJAGI;4WM>"M.
+M@ZTTF41(5ZT!YX$_)[[<A9A"*9:E*DD,:-9Q&"::GWQ/BX1"(0'1H$TY@CSF
+M'`F.9^C*V23,8B]13J3C"UF^C*!%?P-767`FN2XZ]"H-^9;D)BJZMJU6>SL+
+M8O_O`FM=@#1+;O![7"`W/H10D1L11131K/^[OE"2+QLWBF+5]%6X^%.,?_A`
+M.>;<6SHYWRK''.,J%7"<CJBN:C6[.[O=UL'#Y9CSM'+LP&H5RA!\V\D<-
+M`\L%E,`P1"$D,"(GBL-T-`;:&&ZY)""QAFDLA3\57''H=4Z7/,3!Y$U.X:.K
+M#^>`*J8BJ9!,<_B$\KG$S81OSS%Y)S.!ECCHO;DZNWP'5//@\\^]?I]3.A%`
+M:5POQG43^PY=F*J8A1/*=4ZH]N1?*%V"ZQKOO%]2I'2*2P>3$,N%X#:-1Q9,
+M>%S^-(R]X$[X#0<M/<8"(O`5DG42W6M:>[M9(3`-/2(!C:Q:P]<OM,S%,!<D
+M;E4F0Q''%%F,R@<"Z5XGD(L&/M7OXK$;.)_Q20GG,Q<I\$F7FIGY?KX.*HC7
+MJ%PGA*!^UP7^&8;!BP10\%2*96@M="=\)12-I76Q7A>+6\PL8+L)2B[7U!+P
+M6`./;7\M*%G37LO:R^OM[R`)^=\IBK5&LF_M=Q9^MX?E?^YX.>(@IW[9_ZXK
+M7`95V`=QG,5$[(%KRV290$?CR?QQU0V7%JSWQ4S6Y(MW%$BE-PILGQ>;B\52
+M[_9XARSOCM+A'%?A&KR">6243B@/4];'O6TZ%VU`E6J$ZTJ`>:Q2PT6(I'9(
+M1H7')R^IMOAE_=FKO;MOM?<*XF^CS[;1:;/(AT1A:/.&08A$PA$T#_-1+</2
+M6(26S0.F'D#I$=\YT.M>_PPVW4B].6,[ADV2.R>33YWVY\+$;>I^:K7W<6@M
+MZ?O;5J=SD(47XI!^8U1RHGF5<-[X6+7X%E1:2A[/ZCB_M<FG)DRF,X%V.D6=
+M4!*FPY)/*D6;G7BD2]C<X@4(64V<D4BB41Q5FS78.(+LK0;/G_,+\HTO1T?0
+MPA%>1C]5-T+.W3`2075P>MSOGW]$:N)*C9&\_]#OU]#[ZT28@72]#]=28A&E
+M,X%2+-;_'/85C8:GY*NP7&)Z@34U3@8\&V/%4G61:EE%"5N`(K8P_M04+89!
+M#)/HFY^)H1<_O(#??H/"P'7PHL:$>D$J#O,E'KR$3FMY!C-459(4$,$A;,KH
+M$&1DFC46ZR;-9!CY1;-A+$S",TW<EM0Z3".BJ\807YEOWO8?T&G7H+#B<X['
+M=7RLE%`-O"@7\PP=V+_+0TBIY**BT?6HKY"74JJ#4!+?!HW=A'>D_RH&'C:)
+M-$'?K*Y3KK&PND1W!G1O@<LS/*]S4?BODVP79BU-ZB_3Y":91X)0?AB<7=Y<
+M7)Z?G`T&RV*6.GVP0BM;0S'=^E%6+-`HJ$=26ZA*)G:B0)_+I(8";*Y3*((U
+M9'(3(S)X#LU?7[\^4=;?_+6YW5Q:DG%Y24F"V/-G]ES"^<\ECL@Y)U'&&`F`
+M_`&75-AYFID)&EJ\IJG(,6XQHM^IYZ_?D"F55VCJ.K3F6Y,1*A\!92ZDMJ+!
+MX#C:I-J*Z-Q`0@.BM`B42U/1_>'JYOWQN[-![]]GN>NL$EZD7!DM_Q+!4%M+
+MP3`U-VP7,UMFO.1>3H1I_$5?+4:9D_/W@_/^&4IUMA)EJ#S,ZPPW0ICK^)&U
+M?>,Z5DDEPU+R*\V8D6<;S26%9&0.5-2]T)9.&4T=U91/T;1RJJJC_`AGJYCJ
+M'(L2WA3I='[!JB=PYTEWU/6ZFK&S\]<U53N!(?$`YHRK#C-*66Q_SVH?M%46
+M6Y<\.GMMJ[/?7N2]3GL'!_(C.RN+_RP2"<<_(AVIPN=#-:_UBUG?<&P\E;V0
+M+[HJR7#BFX1#05S2O)$GPY9:D"UF'HRA<.W43[KZU=`58C:M@0WU^E6G/!,X
+MU>*&IV'6"DSQ5#<GIPA&#;6WRF)Z>PI9CTI,)EOE1_&">X99*B>.+)#A@U7-
+M)B'`DT7IY*O/NPU0D6(8"LD5':X/J27I<%VJ?C($O^!!V7/G%J%@&7LJQ#AT
+M@D9HW#X-T*ZG:$2TD^TX81HDFO<L.YH/9T?S@>QH/CT[FO=F1_/W94?SP>QH
+MDG68#V5'LYP=V8[^8'8TGYP=U:Y/RX[FT[*C^>CL:#XJ.V90&X.;WN#D[:7.
+MDN05M75P3\BB]TMC35XSGY#7S#^2U\SEO&86\YJI0F5F<\6\EIO4M_*:N9K7
+MS"?E-?.[Y#5S-:^9H!E;Y#7-I8Z[.J]Q1*%S3^BNG"95J6ZK)J'.=<NL#4XO
+M>J=KHB\&>5?B&4OS].,0#?8Y[H-TW-=F2[E!N>;2,YM8UV3+YOC*<R`B@%WJ
+ML37;W>W._3VV?-636FS[>8L-H#%X"Z=G@Y/+WL55[_R]ND?4>/&%+#^8AG>E
+MB['J?HU:M7PK5NA3CT)NR%*[%Z6/HF93(Y=\1J=C[47#D"\*551#E\IZZ]1U
+MU4WUZG9-M5QQOYZZ74%KE2$U<F-/<$=9_(IY,.%.LJ8V)TGEI<B6?%6HN]/4
+MJ0?7MT>(A,V!)JB7,@GY8A.J8F1!O]<_K_$MY86Z0V!191>T?T=1[?PM1/6`
+M*SCW&:[S#5=PC(](U#]3']K;T-KIMMK=SNZ#KN`8[T+M"MO01`=J=EO?<(7V
+M-E:HA;[+MM5I9OK^P0L</\7"\1^N$R1^8_RR-*B;34NCT6Q(0V81<&QC%%H&
+MY+Y"*%>PSN66%SJ+[5RXN7GS_L--O_?J\OCR?VYNX.41[*XOISO6=C-S[!^P
+MD*4[^9.W9R<_WZ#=&JV5T7>G.SR*YX-LXN)X,/AX:E2X."/#F`TKA>G!V^/3
+M\X]Z6C%6*:!]U7L_>(NSZC,&FGF6M9?^A2&?+L>/H/)3]8>:UA>T&GO0VJ^C
+MFNND6G@W'30JA^HP@,HXN/_P0"5A>]%Y^JHVV]JD(\^F,1#TX0`639Y]Z^>7
+M3U/;3X7Z(B#A$-]@:#K8<3M8BJ2JR-W$I98F'5?1J?1+=ORC=QJ-D1E\II.6
+M/@YOJ3:^\4:H&SPZ5+-[S<)XJ/Q7=?LI"6$5D#IZ>@B;6)P1>#3CQ,T=Z0T\
+M4R54CB_!HI$=TN2BC;>`9)*IEOK4WMG]O#(CRU.*0>X#4JIC^K'8-UY3.S7$
+MHE&&$P'Z3,37;?9P2AW'A@+<(B1D\]'LAF[D2+W<23@L3&BJ<:HT/!).*&ET
+MD$88_3Y@""S-#ST2<&6K-"C'PO=74*59P[.`?3%$H\OY7MEYEO"/%@D?80TD
+M)\:B3L'4&*L18^T<!YC[2?;ZZ$<3*+(-+3(LR%GAGHKUN=ZI[F@H*):7/F<S
+M,7S$(9U8@$K19YR-,CFZ9\,EHP)E&7>Q%-E116R-X8Q%$<M<@'D$.^J9#/MY
+M21T6V904$0XC/CK.*\@J.4)M:>[!J2)^UNM]Z(N`J.#'@+'*RX!4+M8R+H_8
+MW-6;KH<W2#\;Y;/51EE=/1411.#$\RC!3).K"\,#>K4WU("8\"A:N'B81:$I
+M,RAJD[03+5O0TD42%HZ2BV"5PMWL8XD`57B_E65<Z)CC#:LE#:*A+!EER86O
+MBB:HBA/"P3?Z=`G-<=#6P"J4Y_`6<*61%+U<M_27C*BB+6"EGN9$<8]_K95.
+M.59J:A\IH16/DD]P*?FG^]23K;*H-YGP-P1*'I:&(UY0/Q;??7U_FU3:RWSQ
+MFXK/+71CO8G^+HT_<G_@S9><8%$(<"H^EG?,6U&L#7@?)OKKPMP5@E`MT,F.
+M[W,I`^)1>286GQ#*.R]2+8%A&M,IDRKC+*EOZ(J!+J.8SOQJV=`97)=]$/KD
+M9,G\4,TM$C2M^]3:58U-A0T91,.F"1ZD"SWO4*<?+=?*&V^Z7&[PEX8V3HN`
+M,G9V'YDOJ9)4J&^COU*LG_(2Q2F:G1TG:53K@E[FNGXJQZ1!E(J^R:.OK-I[
+M5FL_OZI<DOZ?7`BAKK(XL(1D:5A&-"C7Q<DGE3IJP_75SF)NN>!9S-Q;\RQ`
+MRF7/8GRI\EE,%(N?PDYKZI^(B2+CG.&B:O,1D?G$I@\5N-EHPX?>*30S$;,2
+ME5$5XW*FDE*)!`]GVSRU%D)8'E'J+]=DO;\BZ97W5G</S47"D[E$980&D9-*
+MQE$0[GK17A0=]4=9"H`E1+R?\"4?8[(X*QE$1C?X%Q/<NBU4`JL,RFP^?CN5
+M\4H2H"YW<>/#0@#^6\9?]<Y\_961^$?YK3AL@1;T=XW&:_L;!WM6IYEW^PP5
+M<`V4CX/"&6/$LV!W6Z'$>D8$TVKE[?F[,R113;:4.61S_?,WU):N9!?&*P!T
+M`;`T6T`]>'O6[^.P3'51K^:U"ZL.S+;5WLG_Y\%W)+AHX_>1O0KS:.+7-F3V
+M]ZW.P4XN_3Q,K76*0MA1]6RK6*UR<5NH*PJA.[^_5)!K8A8&+?CM-X;(OL[!
+MPI=C<35:L*TC[,J`^BI`W;1J[FF7FN[3=':LSO9"94_D<HFQ<@C5+?B_G*__
+)!9>7)7O2-```
+`
+end
+
--- /dev/null
+sysvinit (2.87dsf) world; urgency=low
+
+ * Fix typos and do minor updates in the manual pages.
+ * Correct section of mountpoint(1).
+ * Document -e and -t options for telinit in init(8).
+ * Update address of FSF in the COPYRIGHT file.
+ * Document in halt(8) that -n might not disable all syncing.
+ Patch by Bill Nottingham and Fedora
+ * Adjust output from "last -x". In reboot lines, print endpoint
+ of uptime too. In shutdown lines print downtimes rather than
+ the time between downs. Fix typo in string compare in last.c.
+ Patch by Thomas Hood.
+ * Improve handling of IPv6 addresses in last. Patch from Fedora.
+ * Document last options in usage information, previously only
+ mentioned in the manual page.
+ * Add new option -F to last, to output full date string instead
+ of the short form provided by default. Patch from Olaf Dabrunz
+ and SuSe.
+ * Adjust build rules to make sure the installed binaries
+ are stripped.
+ * Increase the compiler warning level when building.
+ * Fix utmp/wtmp updating on 64-bit platforms. Patch by Bill
+ Nottingham and Fedora.
+ * Avoid unchecked return value from malloc() in utmpdump.
+ Patch from Christian 'Dr. Disk' Hechelmann and Fedora.
+ * Make sure to use execle and no execl when passing environment to
+ the new process. Patch from RedHat.
+ * Correct init to make sure the waiting status is preserved across
+ re-exec. Patch from RedHat.
+ * Correct init to avoid race condition when starting programs during
+ boot. Patch from SuSe.
+ * Allow 'telinit u' in runlevels 0 and 6. Patch from Thomas Hood.
+ * Change install rules to make pidof an absolute symlink. Patch from
+ Thomas Hood.
+ * Improve error message from init if fork() fail. Patch found in Suse.
+ * Add support for SE Linux capability handling. Patch from Manoj
+ Srivastava, adjusted to avoid aborting if SE policy was loaded in
+ the initrd with patch from Bill Nottingham and Fedora.
+ * Add -c option to pidof for only matching processes with the same
+ process root. Ignore -c when not running as root. Patch from
+ Thomas Woerner and Fedora.
+ * Adjust init to terminate argv0 with one 0 rather than two so that
+ process name can be one character longer. Patch by Kir Kolyshkin.
+ * Make sure bootlogd exit with non-error exit code when forking of
+ the child successfully.
+ * Add bootlogd option -s to make it possible to control the use of
+ fdatasync(). Patch from Thomas Hood.
+ * Add bootlogd option -c to tell it to create the log file if it does
+ not exist. Patch from Thomas Hood.
+ * Let bootlogd also look at ttyB* devices to work on HPPA. Patch
+ from Thomas Hood.
+ * Change init to use setenv() instead of putenv, make sure the PATH
+ value is usable on re-exec. Patch from Thomas Hood.
+ * Add usleep in killall5 after killing processes, to force the kernel
+ to reschedule. Patch from SuSe.
+ * Modify pidof to not print empty line if no pid was found.
+ * Modify init and sulogin to fix emergency mode's tty, making sure ^C
+ and ^Z work when booting with 'emergency' kernel option. Patch from
+ Samuel Thibault.
+ * Modify init to allow some time for failed opens to resolve themselves.
+ Patch from Bill Nottingham and Fedora.
+ * Modify init to shut down IDE, SCSI and SATA disks properly. Patches
+ from Sebastian Reichelt, Werner Fink and SuSe.
+ * Modify wall to use UT_LINESIZE from <utmp.h> instead of hardcoded
+ string lengths. Patch from SuSe.
+ * Change wall to make halt include hostname in output.
+ * Change killall to avoid killing init by mistake. Patch from SuSe.
+ * Change killall5 to use the exit value to report if it found any
+ processes to kill. Patch from Debian.
+ * Add option -o opmitpid to killall5, to make it possible to skip
+ some pids during shutdown. Based on patch from Colin Watson and
+ Ubuntu.
+ * Add references between killall5 and pidof manual pages. Patch from Debian.
+ * Modify killall to work better with user space file system, by
+ changing cwd to /proc when stopping and killing processes, and
+ avoiding stat() when the value isn't used. Also, lock process
+ pages in memory to avoid paging when user processes are stopped.
+ Patch from Debian and Goswin von Brederlow with changes by Kel
+ Modderman.
+ * Change shutdown to only accept flags -H and -P with the -h flag,
+ and document this requirement in the manual page.
+ * Change reboot/halt to work properly when used as a login shell.
+ Patch by Dale R. Worley and Fedora.
+ * Let sulogin fall back to the staticly linked /bin/sash if both roots
+ shell and /bin/sh fail to execute.
+
+ -- Petter Reinholdtsen <pere@hungry.com> Sun, 12 Jul 2009 19:58:10 +0200
+
+sysvinit (2.86) cistron; urgency=low
+
+ * Fixed up bootlogd to read /proc/cmdline. Also keep an internal
+ linebuffer to process \r, \t and ^H. It is becoming useable.
+ * Applied trivial OWL patches
+ * Block signals in syslog(), since syslog() is not re-entrant
+ (James Olin Oden <joden@malachi.lee.k12.nc.us>, redhat bug #97534)
+ * Minor adjustements so that sysvinit compiles on the Hurd
+ * killall5 now skips kernel threads
+ * Inittab entries with both 'S' and other runlevels were broken.
+ Fix by Bryan Kadzban <bryan@kadzban.is-a-geek.net>
+ * Changed initreq.h to be more flexible and forwards-compatible.
+ * You can now through /dev/initctl set environment variables in
+ init that will be inherited by its children. For now, only
+ variables prefixed with INIT_ can be set and the maximum is
+ 16 variables. There's also a length limit due to the size
+ of struct init_request, so it should be safe from abuse.
+ * Option -P and -H to shutdown set INIT_HALT=POWERDOWN and
+ INIT_HALT=HALT as environment variables as described above
+ * Add "mountpoint" utility.
+ * Slightly better algorithm in killall5.c:pidof()
+ * Added some patches from fedora-core (halt-usage, last -t,
+ sulogin-message, user-console)
+
+ -- Miquel van Smoorenburg <miquels@cistron.nl> Fri, 30 Jul 2004 14:14:58 +0200
+
+sysvinit (2.85) cistron; urgency=low
+
+ * Add IPv6 support in last(1)
+ * Sulogin: even if the root password is empty, ask for a password-
+ otherwise there is no way to set a timeout.
+ * Removed support for ioctl.save.
+ * Turned of support for /etc/initrunlvl and /var/run/initrunlvl
+ * Fixed warts in dowall.c ("Dmitry V. Levin" <ldv@altlinux.org>)
+ * Fix init.c::spawn(). The "f" variable was used both as file descriptor
+ and waitpid(2) return code. In certain circumstances, this leads to
+ TIOCSCTTY with wrong file descriptor (Vladimir N. Oleynik).
+ * Fix fd leak in sulogin (Dmitry V. Levin).
+ * More error checking in all wait() calling code (Dmitry V. Levin).
+ * Fix argv[] initialization in spawn() (Dmitry V. Levin).
+ * Change strncpy to strncat in most places (Dmitry V. Levin).
+
+ -- Miquel van Smoorenburg <miquels@cistron.nl> Tue, 15 Apr 2003 16:37:57 +0200
+
+sysvinit (2.84) cistron; urgency=low
+
+ * Don't use /etc/initlvl interface for telinit; only use /dev/initctl,
+ and give a clear error when that fails.
+ * Add -i/--init command line flag to init - this tells init
+ 'behave as system init even if you're not PID#1'. Useful for
+ testing in chroot/jail type environments.
+
+ -- Miquel van Smoorenburg <miquels@cistron.nl> Tue, 27 Nov 2001 13:10:08 +0100
+
+sysvinit (2.83) cistron; urgency=low
+
+ * Fix bug in shutdown where it didn't check correctly for a
+ virtual console when checking /etc/shutdown.allow
+ * Fix race condition in waitpid() [Andrea Arcangeli]
+ * Call closelog() after openlog()/syslog() since recent libc's
+ keep the logging fd open and that is fd#0 aka stdin.
+
+ -- Miquel van Smoorenburg <miquels@cistron.nl> Tue, 2 Oct 2001 23:27:06 +0200
+
+sysvinit (2.82) cistron; urgency=low
+
+ * Print out correct version number at startup.
+ * Fix spelling of initttab in init(8)
+
+ -- Miquel van Smoorenburg <miquels@cistron.nl> Thu, 23 Aug 2001 17:50:44 +0200
+
+sysvinit (2.81) cistron; urgency=low
+
+ * Fix typo/bug in killall5/pidof, -o option failed to work since 2.79.
+ Reformatted source code to prevent this from happening again.
+ * shutdown.8: applied redhat manpage update
+ * sulogin: applied redhat sysvinit-2.78-sulogin-nologin.patch
+ * sulogin: applied redhat sysvinit-2.78-notty.patch
+ * sulogin: applied redhat sysvinit-2.78-sigint.patch
+
+sysvinit (2.80) cistron; urgency=low
+
+ * Grammar/spelling fixes in shutdown.c (Christian Steinrueck)
+ * Don't set controlling tty for non-(sysinit,boot,single) runlevels
+
+ -- Miquel van Smoorenburg <miquels@cistron.nl> Thu, 26 Jul 2001 13:26:56 +0200
+
+sysvinit (2.79) cistron; urgency=low
+
+ * New upstream version
+ * several fixes to wall by Tim Robbins <fyre@box3n.gumbynet.org>
+ * Several extra boundary checks by Solar Designer
+ * Make /dev/console controlling tty
+ * Stricter checks on ownership of tty by mesg(1)
+ * Documented and restricted -n option to wall(1)
+ * Make it compile with glibc 2.2.2
+ * Document IO redirection in wall manpage (closes: #79491)
+ * Update README (closes: #85650)
+ * Fix init.8 manpage (closes: #75268)
+ * Fix typo in halt(8) manpage (closes: #67875)
+ * Check time argument of shutdown(8) for correctness (closes: #67825)
+ * Check for stale sessions in last(1) (Chris Wolf <cwolf@starclass.com>)
+
+ -- Miquel van Smoorenburg <miquels@cistron.nl> Wed, 4 Jul 2001 15:04:36 +0200
+
+sysvinit (2.78-2) frozen unstable; urgency=high
+
+ * Change "booting" to "reloading" message at reload
+ * Add "-z xxx" dummy command line argument (closes: #54717)
+
+ -- Miquel van Smoorenburg <miquels@cistron.nl> Fri, 11 Feb 2000 12:17:54 +0100
+
+sysvinit (2.78-1) unstable; urgency=low
+
+ * 2.78 will be the new upstream version, I'm skipping 2.77
+ * Shutdown now calls sync before switching the runlevel to 0 or 6,
+ or before unmounting filesystems if -n was used (closes: #46461)
+ * Some cosmetic changes to init.c (closes: #32079)
+
+ -- Miquel van Smoorenburg <miquels@cistron.nl> Thu, 30 Dec 1999 20:40:23 +0100
+
+sysvinit (2.77-2) unstable; urgency=low
+
+ * Fix last -i option
+
+ -- Miquel van Smoorenburg <miquels@cistron.nl> Tue, 5 Oct 1999 21:51:50 +0200
+
+sysvinit (2.77-1) unstable; urgency=low
+
+ * Write reboot record into utmp file as well to make rms happy
+ * Fork and dump core in / if SIGSEGV is received for debugging purposes
+ * Patch by Craig Sanders <cas@vicnet.net.au> for "last" -i option
+
+ -- Miquel van Smoorenburg <miquels@cistron.nl> Wed, 4 Aug 1999 11:16:23 +0200
+
+sysvinit (2.76-4) unstable; urgency=low
+
+ * Change dowall.c to handle Unix98 ptys correctly
+ * Add comment in rcS about usage of setup.sh and unconfigured.sh
+ * Shutdown now removes nologin file just before calling telinit
+ * SEGV handler now tries to continue after sleep of 30 seconds.
+ On a 386-class processor it also prints out the value of EIP.
+ * Fix for racecondition in check_init_fifo() by Richard Gooch
+
+ -- Miquel van Smoorenburg <miquels@cistron.nl> Sat, 8 May 1999 17:22:57 +0200
+
+sysvinit (2.76-3) frozen unstable; urgency=high
+
+ * Small bugfix to last.c courtesy of Danek Duvall <duvall@emufarm.ml.org>
+
+ -- Miquel van Smoorenburg <miquels@cistron.nl> Tue, 12 Jan 1999 12:12:44 +0100
+
+sysvinit (2.76-1) frozen unstable; urgency=high
+
+ * Fix bug in check_pipe() which crashes init on the Alpha.
+
+ -- Miquel van Smoorenburg <miquels@cistron.nl> Tue, 3 Nov 1998 11:09:13 +0100
+
+sysvinit (2.75-4) unstable; urgency=low
+
+ * Change sulogin password buffer to 128 characters.
+ * Don't print control characters in dowall.c
+ * Try to open getenv ("CONSOLE"), /dev/console and /dev/tty0 in order.
+ For backwards compatibility when you try to boot a 2.0.x kernel
+ with a linux > 2.1.70 /dev/console device.
+ * Change src/Makefile for non-debian systems (mainly, RedHat)
+ * Try to create /dev/initctl if not present; check every time to see
+ if the dev/ino of /dev/initctl has changed and re-open it. This should
+ help devfs a bit.
+ * Send SIGUSR1 to init at bootup to let it re-open /dev/initctl;
+ again in support of devfs.
+ * Moved pidof to /bin (it's only a link to killall5 anyway)
+
+ -- Miquel van Smoorenburg <miquels@cistron.nl> Mon, 5 Oct 1998 14:03:14 +0200
+
+sysvinit (2.75-2) frozen unstable; urgency=medium
+
+ * Fix last.c again.
+ * Add check to see if /dev/initctl is really a FIFO
+ * In ifdown.c first down all shaper devices then the real devices
+
+ -- Miquel van Smoorenburg <miquels@cistron.nl> Tue, 2 Jun 1998 22:43:01 +0200
+
+sysvinit (2.75-1) frozen unstable; urgency=low
+
+ * Rewrote last.c to be much more memory friendly and correct,
+ thanks to Nick Andrew <nick@zeta.org.au> and
+ David Parrish <dparrish@zeta.org.au>
+ * Fixes bugs:
+ #21616: sysvinit: sulogin thinks md5 root password is bad
+ #21765: sysvinit: Typo in `killall5.c'
+ #21775: sysvinit: sysvinit does not support MD5 hashed passwords
+ #21990: /usr/bin/last: unnecessary memset and off-by-one bug
+ #22084: sysvinit 2.74-4: SIGPWR missing on sparc
+ #21900: init, powerfail events, and shutdown.allow
+ #21702: init 0 does not work as expected...
+ #21728: sysvinit: Typo in `init.c'
+ #22363: sysvinit: discrepance btw. manpage and /sbin/init
+
+ -- Miquel van Smoorenburg <miquels@cistron.nl> Tue, 19 May 1998 11:02:29 +0200
+
+sysvinit (2.74-4) frozen unstable; urgency=medium
+
+ * Add -o option to last to process libc5 utmp files.
+ * Buffer overflow fixed in init.c (not very serious; only exploitable
+ by root). Thanks to Chris Evans <chris@ferret.lmh.ox.ac.uk>
+
+ -- Miquel van Smoorenburg <miquels@cistron.nl> Wed, 15 Apr 1998 17:04:33 +0200
+
+sysvinit (2.74-1) unstable; urgency=low
+
+ * Should compile with glibc 1.99 :)
+ * Change behaviour of reboot(1) and halt(1) so that the default when
+ the runlevel can't be determined is to call shutdown.
+ * Added re-exec patch from Al Viro (21 Feb 1998):
+ 'U' flag added to telinit. It forces init to re-exec itself
+ (passing its state through exec, certainly).
+ May be useful for smoother (heh) upgrades.
+ 24 Feb 1998, AV:
+ did_boot made global and added to state - thanks, Miquel.
+ Yet another file descriptors leak - close state pipe if
+ re_exec fails.
+
+ -- Miquel van Smoorenburg <miquels@cistron.nl> Thu, 12 Mar 1998 17:42:46 +0100
+
+sysvinit (2.73-2) unstable; urgency=low
+
+ * Change _NSIG to NSIG for 2.1.x kernel includes.
+
+ -- Miquel van Smoorenburg <miquels@cistron.nl> Thu, 8 Jan 1998 16:01:02 +0100
+
+sysvinit (2.73-1) unstable; urgency=low
+
+ * Use siginterrupt, now that system calls are restarted by default.
+ Main symptom was that the sulogin timeout didn't work but there
+ might have been more hidden problems.
+ * Kill process immidiately if turned off in inittab
+ * Fixed sulogin check on tty arg.
+ * Use strerror() instead of sys_errlist
+ * wall now supports a '-n' option to suppress [most of] the banner.
+ Debian doesn't use sysvinit's wall, but apparently Redhat does.
+ * Add '-F' (forcefsck) option to shutdown
+ * Close and reopen /dev/initctl on SIGUSR1 (mainly for a /dev in ram)
+
+ -- Miquel van Smoorenburg <miquels@cistron.nl> Sat, 3 Jan 1998 16:32:39 +0100
+
+sysvinit (2.72-3) unstable; urgency=low
+
+ * Add extra fork() in dowall.c to avoid hanging in rare cases
+
+ -- Miquel van Smoorenburg <miquels@cistron.nl> Wed, 22 Oct 1997 14:44:00 +0200
+
+sysvinit (2.72) unstable; urgency=low
+
+ * Applied manual page patches by Bill Hawes <whawes@star.net>. Thanks Bill!
+ * Applied patches to the sample Slackware scripts by
+ "Jonathan I. Kamens" <jik@kamens.brookline.ma.us>
+ * Fix halt and reboot runlevels 0 & 6 check.
+ * Only say "no more processes left in runlevel x" once
+ * Fix race condition with SIGCHLD in spawn()
+ (thanks to Alon Ziv <alonz@CS.Technion.AC.IL>)
+ * Compress all manpages (missed 2)
+ * Compiled for libc6
+ * Added poweroff patch by Roderich Schupp <rsch@ExperTeam.de>
+
+ -- Miquel van Smoorenburg <miquels@cistron.nl> Sun, 12 Oct 1997 17:20:17 +0200
+
+sysvinit (2.71-2) frozen unstable; urgency=low
+
+ * Print 2.71 instead of 2.70 on startup :)
+
+ -- Miquel van Smoorenburg <miquels@cistron.nl> Mon, 5 May 1997 12:45:25 +0200
+
+sysvinit (2.71-1) frozen unstable; urgency=high
+
+ * Added code for updwtmp() in utmp.c for glibc (2.0.3)
+ * Fixed all programs to use functions from utmp.c and getutent()
+ * Do not try to clean up utmp in init itself (Bug#9022)
+ * Removed sync() from main loop.
+ * Hopefully fixes bug #8657 (shutdown signal handling)
+
+ -- Miquel van Smoorenburg <miquels@cistron.nl> Sat, 26 Apr 1997 19:57:27 +0200
+
+sysvinit (2.70-1) unstable; urgency=low
+
+ * Respawn fix
+ * Removed StUdLy CaPs from source code
+ * Moved files in source archive around
+ * Fixes for glibc (utmp handling, signal handling).
+ * Fixed '-d' option to last (now also works without '-a').
+ * Added extra checking in last.c to prevent showing dead entries
+
+ -- Miquel van Smoorenburg <miquels@cistron.nl> Fri, 7 Feb 1997 15:31:30 +0100
+
+sysvinit (2.69-1) frozen unstable; urgency=medium
+
+ * Fixed bug that can throw X in a loop (or any other app that reads from
+ /dev/tty0)
+
+ -- Miquel van Smoorenburg <miquels@cistron.nl> Sun, 1 Dec 1996 15:32:24 +0100
+
+sysvinit (2.67-1) frozen unstable; urgency=high
+
+ * Fixes problem with /dev/console being controlling terminal of some
+ daemons
+ * Puts copyright file in the right place
+
+ -- Miquel van Smoorenburg <miquels@cistron.nl> Fri, 15 Nov 1996 12:23:33 +0100
+
+sysvinit (2.66-1) unstable; urgency=medium
+
+ * Skipped 2.65. A development 2.65 got out by accident and is apparently
+ being used..
+ * Also compiles and runs with GNU libc (and on the Alpha)
+ * Fixed dowall.c not to exit when getpwuid() fails and uid == 0.
+ * Fixed init panic'ing on empty lines in /etc/inittab
+ * Changed default PATH to include /usr/local/sbin
+ * Set /dev/console as controlling terminal for sysinit,bootwait,wait,powerwait
+ This allows using ^C to interrupt some parts of eg the boot process.
+ * Remove old symlink in /var/log/initlvl; let init check both
+ /var/log and /etc itself.
+
+ -- Miquel van Smoorenburg <miquels@cistron.nl> Tue, 29 Oct 1996 13:46:54 +0100
+
+2.66 29-Oct-1996
+- Skipped 2.65. A development 2.65 got out by accident and is apparently
+ being used..
+- Fixed dowall.c not to exit when getpwuid() fails and uid == 0.
+- Fixed init panic'ing on empty lines in /etc/inittab
+- Changed default PATH to include /usr/local/sbin
+- Ported to Linux/Alpha and GNU libc.
+- Set /dev/console as controlling terminal for sysinit,bootwait,wait,powerwait.
+ This allows using ^C to interrupt some parts of eg the boot process.
+- Remove old symlink in /var/log/initlvl; let init check both
+ /var/log and /etc itself.
+
+2.64 28-Jun-1996
+- Init checks CONSOLE environment variable on startup (overrides /dev/console)
+- Init sets CONSOLE variable for all its children.
+- Wtmp(): when zeroing out old utmp entries, keep ut_id field
+- Wtmp(): try to re-use ut_id field if possible.
+- SetTerm(): only read from /etc/ioctl.save if written once.
+- Included start-stop-daemon, C version (source only).
+- Fixed wait() for the emergency shell.
+- killall5: ignore signal before doing kill(-1, pid).
+
+2.63 14-Jun-1996
+- Fixed preinst script for Debian
+- Fixed init.c to become init daemon if name is init.new
+- Fixed pidof to not return PIDs of shell scripts
+
+2.62-2 09-Jun-1996
+- Changed debian /etc/init.d/boot script to create a nologin file
+ at boot and to remove it just before going multiuser.
+
+2.62 31-May-1996
+- Decided to release a 2.62 version with a BIG WARNING about upgrading
+ init in it. Will send a patch to Linus for the linux/Documentation/Changes
+ file so that 2.62 or later is mentioned as the version to upgrade to.
+- Added docs for Slackware
+
+2.61-3 29-May-1996
+- Fixed debian/etc/init.d/network for the lo device.
+- Added "-xdev" to the cd /tmp && find in debian/etc/init.d/boot
+- Made remove time for files in /tmp configurable.
+
+2.61 29-Apr-1996
+- Changed /etc/init.d/boot script again
+- Fixed problem in init.c with trailing whitespace after entries in inittab
+- Fixed killall5 problems
+- Added manpage for lastb
+- Added SHELL= environment variable to sulogin
+- Fixed sulogin & shadow problems
+- Added timeout option to sulogin
+
+2.60-2 16-Apr-1996
+- Fixed sulogin (didn't work if root wasn't first entry in shadow file)
+- Fixed mesg for systems with "tty" group (such as Debian)
+- Fixed nsyslog() in killall5.c
+
+2.60 01-Apr-1996
+- Fixed race condition in init.c, resulting in hanging shutdowns.
+ Courtesy of Max Neunhoeffer <Max.Neunhoeffer@urz.uni-heidelberg.de>.
+- Fixed debian/etc/init.d/boot for swapon and mdadd
+- Added architecture to debian.control
+- Added manpages for rc.boot and rc.local
+- Updated inittab manpage for 4-character runlevel field
+- Added debian replaces for bsdutils < version_without_mesg
+- Fixed init.c so that it also works with kernels 1.3.81 and up
+
+2.59 10-Mar-1996
+- Init logs less to syslog (suspected to hang in syslog() or openlog() )
+- removed closelog() from init.c
+- removed time check of runlevel record in halt.
+- Added options to last to get hostname from ut_addr field
+- Added last and mesg to installation targets
+- rewrote /etc/init.d/boot a bit.
+
+2.58-2 04-Jan-1996
+- Changed etc/init.d/rc to do a stty onlcr
+- Added /var/log/initrunlvl symlink
+
+2.58-1 31-Dec-1995
+- Added the latest debian files.
+- Added support for 4-character id fields (if you have libc5).
+- Fixed pidof (in killall5) parsing of /proc/.../stat
+- Save restore GMT setting in /etc/init.d/boot
+
+2.57d 03-Dec-1995
+- Added sulogin
+- Added "-b" flag to init, gives a shell before
+ anything else (in case the startup scripts are screwed)
+- Moved fastboot to /fastboot
+- Folded in Debian patches.
+- Removed old scripts
+- Added debian /etc/directory.
+
+2.57c 08-Oct-1995
+- Changed over to init_request (with initreq.h)
+- Processes no longer killed when "process" field
+ changes, change takes effect after next respawn.
+
+2.57b xx-Aug-1995
+- Bugfix release for Debian and Slackware 3.0
+
+2.57a 10-Jul-1995
+- Fixed race condition init init.c wrt got_chld
+- Fixed one-off for malloc in killall5.c
+- Changed dowall.c
+- Console code: no relink to /dev/systty on CTRL-ALT-DEL)
+
+2.57 22-May-1995
+- Changed a few things here and there, didn't
+ really document it :)
+
+2.55 17-Jan-1995
+- Added option to shutdown to run standalone.
+
+2.54 12-Jan-1995
+- Added GNU copyrigh to all *.[ch] files.
+- added /etc/initscript
+- reboot and halt now call shutdown in runlevels 1-5
+- Can run from read-only root (CDROM)
+
+2.53 10-Oct-1994
+- Renamed pidof to killall5, updated all scripts to
+ use killall5 instead of kill -1 ....
+- Rewrote scripts to use this, and some general changes.
+- Added SysV command line compatibility to shutdown.
+
+2.52 30-Aug-1994
+- Added `powerfailnow' keyword, for when UPS battery is low.
+- Updated `last'.
+- Fixed utmp handling (wrt. CLEAN_UTMP)
+TODO:
+* Make last compatible with GNU/BSD (long options?)
+* update powerd
+* remote SIGPWR broadcast? in powerd? (with priv. port)
+* remote shutdown
+
+2.50 14-Feb-1994
+- Ignores unknown command line arguments.
+- Modelled more after the "real" sysVinit
+- Lots of changes all over the place.
+ (like showing runlevel in "ps" listing, logging
+ runlevel into utmp file etc)
+- now using "reliable" signals instead of V7 style.
+- Updated all scripts. Examples are in two directories:
+ etc (normal) and etc-sysv (sysv style scripts).
+- runlevel 0 = halt, 1 = single user, 6 = reboot.
+- added support for serial console.
+- updated Propaganda, manpages.
+- added shutdown access control.
+
+2.4 24-May-93
+- Send out the official version into the world as
+ SysVinit-2.4.tar.z.
+
+2.4g 15-May-93
+- Changed init to really catch SIGPWR 'cause we
+ hooked up an UPS to the Linux machine. The
+ keyword for catching the TreeFingerSalute is
+ now "ctrlaltdel" instead of "power{wait,fail}".
+
+2.4a 22-Apr-93
+- Fixed last to reckognize BSD style wtmp logging.
+- Changed init to write wtmp records that are
+ SysV compliant but are also reckognized by the
+ BSD last. Added a '+' option to the 'process'
+ field of inittab, for getties that want to do
+ their own utmp/wtmp housekeeping (kludge!).
+- Now accepts a runlevel on the command line,
+ and reckognizes the 'auto' argument. (Sets the
+ environment variable AUTOBOOT to YES)
+
+2.2.3 24-Mar-93
+- Ripped out the 'leave' action. To difficult, and
+ unneeded.
+- Going single user now kills _all_ processes.
+- Added '-t secs' option to all commands.
+- This version is stable enough to post.
+
+2.2 02-Mar-93
+- Made wait()'s asynchronous
+- Changed whole thing to one big state machine
+- Now using 'pseudo levels' # & * for SYSINIT & BOOT
+- Added a new type of 'action', called leave. This
+ process will be executed when the system goes from a
+ runlevel specified in it's runlevel field to a
+ level that's not. Nice to bring down NFS and the like.
+
+2.1 28-Jan-93
+- Fixed a bug with 'boot' and 'once'.
+- Check 'initdefault' for validity.
+- Reckognizes "single" as command line argument.
+- Retries execvp with 'sh -c exec ..' if command
+ is a shell script. (shouldn't execvp do this?)
+- Added patches to use syslog if defined.
+
+2.0 08-Dec-92
+- Rewrote the code totally, so started with a new
+ version number.
+- Dropped Minix support, this code now is Linux - specific.
+- With TEST switch on, this init & telinit can
+ run standalone for test purposes.
+
+1.3, 05-Jul-92
+- Got a 386, so installed Linux. Added 'soft' reboot
+ to be default under linux. Fixed some typos.
+
+1.2, 16-Jun-92
+- Bugreport from Michael Haardt ; removed deadlock
+ and added 'waitpid' instead of 'wait' for SYSV.
+
+1.1, 30-Apr-92
+- Read manual wrong: there is no 'action' field called
+ process, but all entries are of type process. Every
+ 'process' get exec'ed by /bin/sh -c 'exec command'.
+- Rapidly respawning processes are caught in the act.
+- _SYSV support is really Linux support,
+ done by poe@daimi.aau.dk on 25-Mar-92.
+
+ 1.0, 01-Feb-92
+- Initial version, very primitive for the Minix
+ operating system. Required some mods. to the
+ kernel.
+
--- /dev/null
+
+ README for the System V style init, version 2.86
+
+init, shutdown, halt, reboot, wall, last, mesg, runlevel,
+killall5, pidof, sulogin.
+
+All programs, files and scripts in this package are covered by
+the Gnu Public License, and copyrighted by me.
+
+If you are not using Debian and the debianized package,
+you will have to install the new init by hand. You should
+be able to drop the binaries into a Slackware or Redhat
+system, I think.
+
+Here is a list of preferred directories to install the progs & manpages:
+
+wall.1, last.1, mesg.1 /usr/man/man1
+inittab.5, initscript.5 /usr/man/man5
+init.8, halt.8, reboot.8,
+shutdown.8, powerd.8,
+killall5.8, pidof.8,
+runlevel.8, sulogin.8 /usr/man/man8
+
+init /sbin/init
+inittab /etc/inittab
+initscript.sample /etc/initscript.sample
+telinit a link (with ln(1) ) to init, either
+ in /bin or in /sbin.
+halt /sbin/halt
+reboot a link to /sbin/halt in the same directory
+killall5 /sbin/killall5
+pidof a link to /sbin/killall5 in the same directory.
+runlevel /sbin/runlevel
+shutdown /sbin/shutdown.
+wall /usr/bin/wall
+mesg /usr/bin/mesg
+last /usr/bin/last
+sulogin /sbin/sulogin
+bootlogd /sbin/bootlogd
+utmpdump don't install, it's just a debug thingy.
+
+If you already _have_ a "wall" in /bin (the SLS release had, for example)
+do _not_ install this wall. Chances are that the wall you are already
+using is linked to /bin/write. Either first _remove_ /bin/wall before
+installing the new one, or don't install the new one at all.
+
+You might want to create a file called "/etc/shutdown.allow". Read the
+manual page on shutdown to find out more about this.
+
+Running from a read-only file system (CDROM?):
+o All communication to init goes through the FIFO /dev/initctl.
+ There should be no problem using a read-only root file system
+ IF you use a Linux kernel > 1.3.66. Older kernels don't allow
+ writing to a FIFO on a read-only file system.
+
+Miquel van Smoorenburg <miquels@cistron.nl>
--- /dev/null
+
+
+ Propaganda for version 2.58 of sysvinit & utilities
+ ==================================================
+
+NOTE: If you use a standard distribution like Slackware, Debian
+or Redhat there probably is no need to upgrade. Installing sysvinit
+is only for those that upgrade their system by hand or for people
+that create Linux distributions.
+
+Sysvinit is probably the most widely used init package for Linux.
+Most distributions use it. sysvinit 2.4 is really a good package,
+and it was not the need for bugfixes but the need for more features
+that made me work on sysvinit again.
+
+Sysvinit is now a debian package. Debian source packages are not
+special in any way -- in fact you can just unpack and compile
+it on any other Linux distribution.
+
+There was a 2.50 release of sysvinit but that was not very popular-
+some of the included scripts broke with certain shells and other
+minor things like that. Unfortunately I was not able to fix this
+at the time because I was abroad for some time. Therefore the
+description below is a comparison of 2.4 and 2.58 (actually the
+same blurb as from the 2.50 announce but updated).
+
+Wrt 2.4, some of the code has been made simpler. Everything, from
+halt to reboot to single user mode is now done by shell scripts
+that are executed directly by init (from /etc/inittab), so shutdown
+does not kill processes anymore and then calls reboot - it merely
+does some wall's to the logged in users and then switches to
+runlevel 0 (halt), 1 (single user) or 6 (reboot).
+
+I have removed support for the old style scripts; the included
+example scripts are the Debian GNU/Linux distribution scripts.
+This does not mean that eg the Slackware scripts stop to work;
+you can probably drop this init into Slackware 3.0 without problems.
+
+Most people have an entry in inittab to run shutdown when CTRL-ALT-DEL
+is pressed; a feature has been added to shutdown to check if a
+authorized user is logged in on one of the consoles to see if a
+shutdown is allowed. This can be configured with an access file.
+
+Some other general changes:
+- utility "runlevel" to read the current and previous runlevel from
+ /var/run/utmp (it's also shown on the command line if you do a "ps").
+- unreckognized options are silently ignored (such as the infamous
+ "ro" - mount root file system read only).
+- if the file /etc/initscript is present it will be used to launch
+ all programs that init starts (so that you can set a generic
+ umask, ulimit eg for ALL processes - see initscript.sample).
+- A "sulogin" program added that always asks for the root
+ passsword before entering single user mode.
+- A "-b" flag to init that starts a shell at boot time before
+ _any_ other processing.
+- I moved /etc/fastboot to /fastboot - wonder what that's gonna break :)
+- I even updated the manpages!
+
+Right, now some boring stuff you already know since it's the same
+as in the 2.4 release:
+
+The sysvinit package includes
+
+* a sysv compatible /sbin/init program
+* a telinit program (er, just a link to /sbin/init) to change runlevels
+* a featureful shutdown
+* halt and reboot to assist shutdown
+* a very forgiving last utility
+* the wall & mesg programs (not installed by default)
+* manpages for everything
+
+The new sysv init can be found on:
+
+tsx-11.mit.edu:/pub/linux/sources/sbin as sysvinit-2.58-1.tar.gz
+sunsite.unc.edu:/pub/Linux/system/Daemons as sysvinit-2.58-1.tar.gz
+
+It will be moved there in a few days, in the mean time it is
+probably in the Incoming directory.
+
+Mike. (02-Jan-1996)
+
--- /dev/null
+
+bootlogd: a way to capture all console output during bootup
+ in a logfile.
+
+- bootlogd opens /dev/console and finds out what the real console is
+ with an ioctl() if TIOCCONS is available
+- otherwise bootlogd tries to parse /proc/cmdline for console=
+ kernel command line arguments
+- then opens the (most probable) real console
+- allocates a pty pair
+- redirects console I/O to the pty pair
+- then goes in a loop reading from the pty, writing to the real
+ console and a logfile as soon as a r/w partition is available,
+ buffering in memory until then.
+
+As soon as bootlogd exits or gets killed, the pty is closed and the
+redirection will be automatically undone by the kernel. So that's
+pretty safe.
+
--- /dev/null
+Begin3
+Title: sysvinit and utilities
+Version: 2.86
+Entered-Date: 30JUL2004
+Description: This is the Linux System V init.
+ This version can be compiled with glibc 2.0.6 and up.
+Author: miquels@cistron.nl (Miquel van Smoorenburg)
+Primary-Site: ftp.cistron.nl /pub/people/miquels/software
+ 92K sysvinit-2.86.tar.gz
+Alternate-Site: sunsite.unc.edu /pub/Linux/system/daemons/init
+ 92K sysvinit-2.86.tar.gz
+Copying-Policy: GPL
+Keywords: init shutdown halt reboot
+End
--- /dev/null
+'\" -*- coding: UTF-8 -*-
+.\" Copyright (C) 1998-2003 Miquel van Smoorenburg.
+.\"
+.\" This program is free software; you can redistribute it and/or modify
+.\" it under the terms of the GNU General Public License as published by
+.\" the Free Software Foundation; either version 2 of the License, or
+.\" (at your option) any later version.
+.\"
+.\" This program is distributed in the hope that it will be useful,
+.\" but WITHOUT ANY WARRANTY; without even the implied warranty of
+.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+.\" GNU General Public License for more details.
+.\"
+.\" You should have received a copy of the GNU General Public License
+.\" along with this program; if not, write to the Free Software
+.\" Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+.\"
+.TH BOOTLOGD 8 "Jul 21, 2003" "" "Linux System Administrator's Manual"
+.SH NAME
+bootlogd \- record boot messages
+.SH SYNOPSIS
+.B /sbin/bootlogd
+.RB [ \-c ]
+.RB [ \-d ]
+.RB [ \-r ]
+.RB [ \-s ]
+.RB [ \-v ]
+.RB [ " -l logfile " ]
+.RB [ " -p pidfile " ]
+.SH DESCRIPTION
+\fBBootlogd\fP runs in the background and copies all strings sent to the
+\fI/dev/console\fP device to a logfile. If the logfile is not accessible,
+the messages will be kept in memory until it is.
+.SH OPTIONS
+.IP \fB\-d\fP
+Do not fork and run in the background.
+.IP \fB\-c\fP
+Attempt to write to the logfile even if it does not yet exist.
+Without this option,
+.B bootlogd
+will wait for the logfile to appear before attempting to write to it.
+This behavior prevents bootlogd from creating logfiles under mount points.
+.IP \fB\-r\fP
+If there is an existing logfile called \fIlogfile\fP rename it to
+\fIlogfile~\fP unless \fIlogfile~\fP already exists.
+.IP \fB\-s\fP
+Ensure that the data is written to the file after each line by calling
+.BR fdatasync (3).
+This will slow down a
+.BR fsck (8)
+process running in parallel.
+.IP \fB\-v\fP
+Show version.
+.IP "\fB\-l\fP \fIlogfile\fP"
+Log to this logfile. The default is \fI/var/log/boot\fP.
+.IP "\fB\-p\fP \fIpidfile\fP"
+Put process-id in this file. The default is no pidfile.
+.SH BUGS
+Bootlogd works by redirecting the console output from the console device.
+(Consequently \fBbootlogd\fP requires PTY support in the kernel configuration.)
+It copies that output to the real console device and to a log file.
+There is no standard way of ascertaining the real console device
+if you have a new-style \fI/dev/console\fP device (major 5, minor 1)
+so \fBbootlogd\fP parses the kernel command line looking for
+\fBconsole=...\fP lines and deduces the real console device from that.
+If that syntax is ever changed by the kernel, or a console type is used that
+\fBbootlogd\fP does not know about then \fBbootlogd\fP will not work.
+
+.SH AUTHOR
+Miquel van Smoorenburg, miquels@cistron.nl
+.SH "SEE ALSO"
+.BR dmesg (8), fdatasync (3).
--- /dev/null
+'\" -*- coding: UTF-8 -*-
+.\" Copyright (C) 1998-1999 Miquel van Smoorenburg.
+.\"
+.\" This program is free software; you can redistribute it and/or modify
+.\" it under the terms of the GNU General Public License as published by
+.\" the Free Software Foundation; either version 2 of the License, or
+.\" (at your option) any later version.
+.\"
+.\" This program is distributed in the hope that it will be useful,
+.\" but WITHOUT ANY WARRANTY; without even the implied warranty of
+.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+.\" GNU General Public License for more details.
+.\"
+.\" You should have received a copy of the GNU General Public License
+.\" along with this program; if not, write to the Free Software
+.\" Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+.\"
+.TH BOOTLOGD 8 "Aug 24, 1999" "" "Linux System Administrator's Manual"
+.SH NAME
+bootlogd \- record boot messages
+.SH SYNOPSIS
+.B /sbin/bootlogd
+.RB [ \-d ]
+.RB [ \-r ]
+.RB [ \-v ]
+.RB [ " -l logfile " ]
+.RB [ " -p pidfile " ]
+.SH DESCRIPTION
+\fBBootlogd\fP runs in the background and copies all strings sent to the
+\fI/dev/console\fP device to a logfile. If the logfile is not accessible,
+the messages will be buffered in-memory until it is.
+.SH OPTIONS
+.IP \fB\-d\fP
+Do not fork and run in the background.
+.IP \fB\-r\fP
+If there is an existing logfile called \fIlogfile\fP rename it to
+\fIlogfile~\fP unless \fIlogfile~\fP already exists.
+.IP \fB\-v\fP
+Show version.
+.IP \fB\-l logfile\fP
+Log to this logfile. The default is \fI/var/log/boot.log\fP.
+.IP \fB\-p pidfile\fP
+Put process-id in this file. The default is no pidfile.
+.SH NOTES
+There is no standard way to find out the real console device if you have
+a new-style \fI/dev/console\fP device (major 5, minor 1). \fBBootlogd\fP
+might have some difficulties to do this, especially under very old
+or very new kernels.
+.SH AUTHOR
+Miquel van Smoorenburg, miquels@cistron.nl
+.SH "SEE ALSO"
+.BR dmesg (8)
--- /dev/null
+'\" -*- coding: UTF-8 -*-
+.\" Copyright (C) 1998-2001 Miquel van Smoorenburg.
+.\"
+.\" This program is free software; you can redistribute it and/or modify
+.\" it under the terms of the GNU General Public License as published by
+.\" the Free Software Foundation; either version 2 of the License, or
+.\" (at your option) any later version.
+.\"
+.\" This program is distributed in the hope that it will be useful,
+.\" but WITHOUT ANY WARRANTY; without even the implied warranty of
+.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+.\" GNU General Public License for more details.
+.\"
+.\" You should have received a copy of the GNU General Public License
+.\" along with this program; if not, write to the Free Software
+.\" Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+.\"
+.\"{{{}}}
+.\"{{{ Title
+.TH HALT 8 "Nov 6, 2001" "" "Linux System Administrator's Manual"
+.\"}}}
+.\"{{{ Name
+.SH NAME
+halt, reboot, poweroff \- stop the system.
+.\"}}}
+.\"{{{ Synopsis
+.SH SYNOPSIS
+.B /sbin/halt
+.RB [ \-n ]
+.RB [ \-w ]
+.RB [ \-d ]
+.RB [ \-f ]
+.RB [ \-i ]
+.RB [ \-p ]
+.RB [ \-h ]
+.br
+.B /sbin/reboot
+.RB [ \-n ]
+.RB [ \-w ]
+.RB [ \-d ]
+.RB [ \-f ]
+.RB [ \-i ]
+.br
+.B /sbin/poweroff
+.RB [ \-n ]
+.RB [ \-w ]
+.RB [ \-d ]
+.RB [ \-f ]
+.RB [ \-i ]
+.RB [ \-h ]
+.\"}}}
+.\"{{{ Description
+.SH DESCRIPTION
+\fBHalt\fP notes that the system is being brought down in the file
+\fI/var/log/wtmp\fP, and then either tells the kernel to halt, reboot or
+power-off the system.
+.PP
+If \fBhalt\fP or \fBreboot\fP is called when the system is
+\fInot\fP in runlevel \fB0\fP or \fB6\fP, in other words when it's running
+normally, \fBshutdown\fP will be invoked instead (with the \fB-h\fP
+or \fB-r\fP flag). For more info see the \fBshutdown\fP(8)
+manpage.
+.PP
+The rest of this manpage describes the behaviour in runlevels 0
+and 6, that is when the systems shutdown scripts are being run.
+.\"}}}
+.\"{{{ Options
+.SH OPTIONS
+.IP \fB\-n\fP
+Don't sync before reboot or halt. Note that the kernel and storage
+drivers may still sync.
+.IP \fB\-w\fP
+Don't actually reboot or halt but only write the wtmp record
+(in the \fI/var/log/wtmp\fP file).
+.IP \fB\-d\fP
+Don't write the wtmp record. The \fB\-n\fP flag implies \fB\-d\fP.
+.IP \fB\-f\fP
+Force halt or reboot, don't call \fBshutdown\fP(8).
+.IP \fB\-i\fP
+Shut down all network interfaces just before halt or reboot.
+.IP \fB\-h\fP
+Put all hard drives on the system in stand-by mode just before halt or power-off.
+.IP \fB\-p\fP
+When halting the system, switch off the power. This is the default when halt is
+called as \fBpoweroff\fP.
+.\"}}}
+.\"{{{ Diagnostics
+.SH DIAGNOSTICS
+If you're not the superuser, you will get the message `must be superuser'.
+.\"}}}
+.\"{{{ Notes
+.SH NOTES
+Under older \fBsysvinit\fP releases , \fBreboot\fP and \fBhalt\fP should
+never be called directly. From release 2.74 on \fBhalt\fP and \fBreboot\fP
+invoke \fBshutdown\fP(8) if the system is not in runlevel 0 or 6. This
+means that if \fBhalt\fP or \fBreboot\fP cannot find out the current
+runlevel (for example, when \fI/var/run/utmp\fP hasn't been initialized
+correctly) \fBshutdown\fP will be called, which might not be what you want.
+Use the \fB-f\fP flag if you want to do a hard \fBhalt\fP or \fBreboot\fP.
+.PP
+The \fB-h\fP flag puts all hard disks in standby mode just before halt
+or power-off. Right now this is only implemented for IDE drives. A side
+effect of putting the drive in stand-by mode is that the write cache
+on the disk is flushed. This is important for IDE drives, since the
+kernel doesn't flush the write cache itself before power-off.
+.PP
+The \fBhalt\fP program uses /proc/ide/hd* to find all IDE disk devices,
+which means that \fI/proc\fP needs to be mounted when \fBhalt\fP or
+\fBpoweroff\fP is called or the \fB-h\fP switch will do nothing.
+.PP
+.\"}}}
+.\"{{{ Author
+.SH AUTHOR
+Miquel van Smoorenburg, miquels@cistron.nl
+.\"}}}
+.\"{{{ See also
+.SH "SEE ALSO"
+.BR shutdown (8),
+.BR init (8)
+.\"}}}
--- /dev/null
+'\" -*- coding: UTF-8 -*-
+.\" Copyright (C) 1998-2004 Miquel van Smoorenburg.
+.\"
+.\" This program is free software; you can redistribute it and/or modify
+.\" it under the terms of the GNU General Public License as published by
+.\" the Free Software Foundation; either version 2 of the License, or
+.\" (at your option) any later version.
+.\"
+.\" This program is distributed in the hope that it will be useful,
+.\" but WITHOUT ANY WARRANTY; without even the implied warranty of
+.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+.\" GNU General Public License for more details.
+.\"
+.\" You should have received a copy of the GNU General Public License
+.\" along with this program; if not, write to the Free Software
+.\" Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+.\"
+.\"{{{}}}
+.\"{{{ Title
+.TH INIT 8 "29 Jul 2004" "" "Linux System Administrator's Manual"
+.\"}}}
+.\"{{{ Name
+.SH NAME
+init, telinit \- process control initialization
+.\"}}}
+.\"{{{ Synopsis
+.SH SYNOPSIS
+.B /sbin/init
+.RB [ " -a " ]
+.RB [ " -s " ]
+.RB [ " -b " ]
+[ \fB\-z\fP \fIxxx\fP ]
+.RB [ " 0123456Ss " ]
+.br
+.B /sbin/telinit
+[ \fB\-t\fP \fISECONDS\fP ]
+.RB [ " 0123456sSQqabcUu " ]
+.br
+.B /sbin/telinit
+[ \fB\-e\fP \fIVAR\fP[\fB=\fP\fIVAL\fP] ]
+.\"}}}
+.\"{{{ Description
+.SH DESCRIPTION
+.\"{{{ init
+.SS Init
+.B Init
+is the parent of all processes. Its primary role is to create processes
+from a script stored in the file \fB/etc/inittab\fP (see
+\fIinittab\fP(5)). This file usually has entries which cause \fBinit\fP
+to spawn \fBgetty\fPs on each line that users can log in. It also
+controls autonomous processes required by any particular system.
+.PP
+.\"{{{ Runlevels
+.SH RUNLEVELS
+A \fIrunlevel\fP is a software configuration of the system which allows
+only a selected group of processes to exist. The processes spawned by
+\fBinit\fP for each of these runlevels are defined in the
+\fB/etc/inittab\fP file. \fBInit\fP can be in one of eight runlevels:
+\fB0\(en6\fP and \fBS\fP or \fBs\fP. The runlevel is
+changed by having a privileged user run \fBtelinit\fP, which sends
+appropriate signals to \fBinit\fP, telling it which runlevel to change
+to.
+.PP
+Runlevels \fB0\fP, \fB1\fP, and \fB6\fP are reserved. Runlevel 0 is used to
+halt the system, runlevel 6 is used to reboot the system, and runlevel
+1 is used to get the system down into single user mode. Runlevel \fBS\fP
+is not really meant to be used directly, but more for the scripts that are
+executed when entering runlevel 1. For more information on this,
+see the manpages for \fBshutdown\fP(8) and \fBinittab\fP(5).
+.PP
+Runlevels 7-9 are also valid, though not really documented. This is
+because "traditional" Unix variants don't use them.
+In case you're curious, runlevels \fIS\fP and \fIs\fP are in fact the same.
+Internally they are aliases for the same runlevel.
+.\"}}}
+.PP
+.SH BOOTING
+After \fBinit\fP is invoked as the last step of the kernel boot sequence,
+it looks for the file \fB/etc/inittab\fP to see if there is an entry of the
+type \fBinitdefault\fP (see \fIinittab\fP(5)). The \fBinitdefault\fP entry
+determines the initial runlevel of the system. If there is no such
+entry (or no \fB/etc/inittab\fP at all), a runlevel must be
+entered at the system console.
+.PP
+Runlevel \fBS\fP or \fBs\fP bring the system to single user mode
+and do not require an \fB/etc/inittab\fP file. In single user mode,
+\fB/sbin/sulogin\fP is invoked on \fB/dev/console\fP.
+.PP
+When entering single user mode, \fBinit\fP initializes the consoles
+\fBstty\fP settings to sane values. Clocal mode is set. Hardware
+speed and handshaking are not changed.
+.PP
+When entering a multi-user mode for the first time, \fBinit\fP performs the
+\fBboot\fP and \fBbootwait\fP entries to allow file systems to be
+mounted before users can log in. Then all entries matching the runlevel
+are processed.
+.PP
+When starting a new process, \fBinit\fP first checks whether the file
+\fI/etc/initscript\fP exists. If it does, it uses this script to
+start the process.
+.PP
+Each time a child terminates, \fBinit\fP records the fact and the reason
+it died in \fB/var/run/utmp\fP and \fB/var/log/wtmp\fP,
+provided that these files exist.
+.SH CHANGING RUNLEVELS
+After it has spawned all of the processes specified, \fBinit\fP waits
+for one of its descendant processes to die, a powerfail signal, or until
+it is signaled by \fBtelinit\fP to change the system's runlevel.
+When one of the above three conditions occurs, it re-examines
+the \fB/etc/inittab\fP file. New entries can be added to this file at
+any time. However, \fBinit\fP still waits for one of the above three
+conditions to occur. To provide for an instantaneous response, the
+\fBtelinit Q\fP or \fBq\fP command can wake up \fBinit\fP to re-examine the
+\fB/etc/inittab\fP file.
+.PP
+If \fBinit\fP is not in single user mode and receives a powerfail
+signal (SIGPWR), it reads the file \fB/etc/powerstatus\fP. It then starts
+a command based on the contents of this file:
+.IP F(AIL)
+Power is failing, UPS is providing the power. Execute the \fBpowerwait\fP
+and \fBpowerfail\fP entries.
+.IP O(K)
+The power has been restored, execute the \fBpowerokwait\fP entries.
+.IP L(OW)
+The power is failing and the UPS has a low battery. Execute the
+\fBpowerfailnow\fP entries.
+.PP
+If /etc/powerstatus doesn't exist or contains anything else then the
+letters \fBF\fP, \fBO\fP or \fBL\fP, init will behave as if it has read
+the letter \fBF\fP.
+.PP
+Usage of \fBSIGPWR\fP and \fB/etc/powerstatus\fP is discouraged. Someone
+wanting to interact with \fBinit\fP should use the \fB/dev/initctl\fP
+control channel - see the source code of the \fBsysvinit\fP package
+for more documentation about this.
+.PP
+When \fBinit\fP is requested to change the runlevel, it sends the
+warning signal \s-1\fBSIGTERM\fP\s0 to all processes that are undefined
+in the new runlevel. It then waits 5 seconds before forcibly
+terminating these processes via the \s-1\fBSIGKILL\fP\s0 signal.
+Note that \fBinit\fP assumes that all these processes (and their
+descendants) remain in the same process group which \fBinit\fP
+originally created for them. If any process changes its process group
+affiliation it will not receive these signals. Such processes need to
+be terminated separately.
+.\"}}}
+.\"{{{ telinit
+.SH TELINIT
+\fB/sbin/telinit\fP is linked to \fB/sbin/init\fP. It takes a
+one-character argument and signals \fBinit\fP to perform the appropriate
+action. The following arguments serve as directives to
+\fBtelinit\fP:
+.IP "\fB0\fP,\fB1\fP,\fB2\fP,\fB3\fP,\fB4\fP,\fB5\fP or \fB6\fP"
+tell \fBinit\fP to switch to the specified run level.
+.IP \fBa\fP,\fBb\fP,\fBc\fP
+tell \fBinit\fP to process only those \fB/etc/inittab\fP file
+entries having runlevel \fBa\fP,\fBb\fP or \fBc\fP.
+.IP "\fBQ\fP or \fBq\fP"
+tell \fBinit\fP to re-examine the \fB/etc/inittab\fP file.
+.IP "\fBS\fP or \fBs\fP"
+tell \fBinit\fP to switch to single user mode.
+.IP "\fBU\fP or \fBu\fP"
+tell \fBinit\fP to re-execute itself (preserving the state). No re-examining of
+\fB/etc/inittab\fP file happens. Run level should be one of
+\fBSs0123456\fP
+otherwise request would be silently ignored.
+.PP
+\fBtelinit\fP can tell \fBinit\fP how long it should wait
+between sending processes the SIGTERM and SIGKILL signals. The default
+is 5 seconds, but this can be changed with the \fB-t\fP option.
+.PP
+\fBtelinit -e\fP tells \fBinit\fP to change the environment
+for processes it spawns.
+The argument of \fB-e\fP is either of the form \fIVAR\fP=\fIVAL\fP
+which sets variable \fIVAR\fP to value \fIVAL\fP,
+or of the form \fIVAR\fP
+(without an equality sign)
+which unsets variable \fIVAR\fP.
+.PP
+\fBtelinit\fP can be invoked only by users with appropriate
+privileges.
+.PP
+The \fBinit\fP binary checks if it is \fBinit\fP or \fBtelinit\fP by looking
+at its \fIprocess id\fP; the real \fBinit\fP's process id is always \fB1\fP.
+From this it follows that instead of calling \fBtelinit\fP one can also
+just use \fBinit\fP instead as a shortcut.
+.\"}}}
+.\"}}}
+.SH ENVIRONMENT
+\fBInit\fP sets the following environment variables for all its children:
+.IP \fBPATH\fP
+\fI/bin:/usr/bin:/sbin:/usr/sbin\fP
+.IP \fBINIT_VERSION\fP
+As the name says. Useful to determine if a script runs directly from \fBinit\fP.
+.IP \fBRUNLEVEL\fP
+The current system runlevel.
+.IP \fBPREVLEVEL\fP
+The previous runlevel (useful after a runlevel switch).
+.IP \fBCONSOLE\fP
+The system console. This is really inherited from the kernel; however
+if it is not set \fBinit\fP will set it to \fB/dev/console\fP by default.
+.SH BOOTFLAGS
+It is possible to pass a number of flags to \fBinit\fP from the
+boot monitor (eg. LILO). \fBInit\fP accepts the following flags:
+.TP 0.5i
+.B -s, S, single
+Single user mode boot. In this mode \fI/etc/inittab\fP is
+examined and the bootup rc scripts are usually run before
+the single user mode shell is started.
+.PP
+.TP 0.5i
+.B 1-5
+Runlevel to boot into.
+.PP
+.TP 0.5i
+.B -b, emergency
+Boot directly into a single user shell without running any
+other startup scripts.
+.PP
+.TP 0.5i
+.B -a, auto
+The LILO boot loader adds the word "auto" to the command line if it
+booted the kernel with the default command line (without user intervention).
+If this is found \fBinit\fP sets the "AUTOBOOT" environment
+variable to "yes". Note that you cannot use this for any security
+measures - of course the user could specify "auto" or \-a on the
+command line manually.
+.PP
+.TP 0.5i
+.BI "-z " xxx
+The argument to \fB-z\fP is ignored. You can use this to expand the command
+line a bit, so that it takes some more space on the stack. \fBInit\fP
+can then manipulate the command line so that \fBps\fP(1) shows
+the current runlevel.
+.PP
+.SH INTERFACE
+Init listens on a \fIfifo\fP in /dev, \fI/dev/initctl\fP, for messages.
+\fBTelinit\fP uses this to communicate with init. The interface is not
+very well documented or finished. Those interested should study the
+\fIinitreq.h\fP file in the \fIsrc/\fP subdirectory of the \fBinit\fP
+source code tar archive.
+.SH SIGNALS
+Init reacts to several signals:
+.TP 0.5i
+.B SIGHUP
+Has the same effect as \fBtelinit q\fP.
+.PP
+.TP 0.5i
+.B SIGUSR1
+On receipt of this signals, init closes and re-opens its control fifo,
+\fB/dev/initctl\fP. Useful for bootscripts when /dev is remounted.
+.TP 0.5i
+.B SIGINT
+Normally the kernel sends this signal to init when CTRL-ALT-DEL is
+pressed. It activates the \fIctrlaltdel\fP action.
+.TP 0.5i
+.B SIGWINCH
+The kernel sends this signal when the \fIKeyboardSignal\fP key is hit.
+It activates the \fIkbrequest\fP action.
+\"{{{ Conforming to
+.SH CONFORMING TO
+\fBInit\fP is compatible with the System V init. It works closely
+together with the scripts in the directories
+\fI/etc/init.d\fP and \fI/etc/rc{runlevel}.d\fP.
+If your system uses this convention, there should be a \fIREADME\fP
+file in the directory \fI/etc/init.d\fP explaining how these scripts work.
+.\"}}}
+.\"{{{ Files
+.SH FILES
+.nf
+/etc/inittab
+/etc/initscript
+/dev/console
+/var/run/utmp
+/var/log/wtmp
+/dev/initctl
+.fi
+.\"}}}
+.\"{{{ Warnings
+.SH WARNINGS
+\fBInit\fP assumes that processes and descendants of processes
+remain in the same process group which was originally created
+for them. If the processes change their group, \fBinit\fP can't
+kill them and you may end up with two processes reading from one
+terminal line.
+.\"}}}
+.\"{{{ Diagnostics
+.SH DIAGNOSTICS
+If \fBinit\fP finds that it is continuously respawning an entry
+more than 10 times in 2 minutes, it will assume that there is an error
+in the command string, generate an error message on the system console,
+and refuse to respawn this entry until either 5 minutes has elapsed or
+it receives a signal. This prevents it from eating up system resources
+when someone makes a typographical error in the \fB/etc/inittab\fP file
+or the program for the entry is removed.
+.\"}}}
+.\"{{{ Author
+.SH AUTHOR
+Miquel van Smoorenburg (miquels@cistron.nl), initial manual
+page by Michael Haardt (u31b3hs@pool.informatik.rwth-aachen.de).
+.\"}}}
+.\"{{{ See also
+.SH "SEE ALSO"
+.BR getty (1),
+.BR login (1),
+.BR sh (1),
+.BR runlevel (8),
+.BR shutdown(8),
+.BR kill (1),
+.BR inittab (5),
+.BR initscript (5),
+.BR utmp (5)
+.\"}}}
--- /dev/null
+'\" -*- coding: UTF-8 -*-
+.\" Copyright (C) 1998-2003 Miquel van Smoorenburg.
+.\"
+.\" This program is free software; you can redistribute it and/or modify
+.\" it under the terms of the GNU General Public License as published by
+.\" the Free Software Foundation; either version 2 of the License, or
+.\" (at your option) any later version.
+.\"
+.\" This program is distributed in the hope that it will be useful,
+.\" but WITHOUT ANY WARRANTY; without even the implied warranty of
+.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+.\" GNU General Public License for more details.
+.\"
+.\" You should have received a copy of the GNU General Public License
+.\" along with this program; if not, write to the Free Software
+.\" Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+.\"
+.TH INITSCRIPT 5 "July 10, 2003" "" "Linux System Administrator's Manual"
+.SH NAME
+initscript \- script that executes inittab commands.
+.SH SYNOPSIS
+/bin/sh /etc/initscript id runlevels action process
+.SH DESCRIPTION
+When the shell script \fI/etc/initscript\fP is present, \fBinit\fP
+will use it to execute the commands from \fIinittab\fP.
+This script can be used to set things like \fBulimit\fP and
+\fBumask\fP default values for every process.
+.SH EXAMPLES
+This is a sample initscript, which might be installed on your
+system as \fI/etc/initscript.sample\fP.
+.RS
+.sp
+.nf
+.ne 7
+
+#
+# initscript Executed by init(8) for every program it
+# wants to spawn like this:
+#
+# /bin/sh /etc/initscript <id> <level> <action> <process>
+#
+
+ # Set umask to safe level, and enable core dumps.
+ umask 022
+ ulimit -c 2097151
+ PATH=/bin:/sbin:/usr/bin:/usr/sbin
+ export PATH
+
+ # Increase the hard file descriptor limit for all processes
+ # to 8192. The soft limit is still 1024, but any unprivileged
+ # process can increase its soft limit up to the hard limit
+ # with "ulimit -Sn xxx" (needs a 2.2.13 or later Linux kernel).
+ ulimit -Hn 8192
+
+ # Execute the program.
+ eval exec "$4"
+
+.sp
+.RE
+.SH NOTES
+This script is not meant as startup script for daemons or somesuch.
+It has nothing to do with a \fIrc.local\fP style script. It's just
+a handler for things executed from \fB/etc/inittab\fP. Experimenting
+with this can make your system un(re)bootable.
+.RE
+.SH FILES
+/etc/inittab,
+/etc/initscript.
+.SH AUTHOR
+Miquel van Smoorenburg ,<miquels@cistron.nl>
+.SH "SEE ALSO"
+init(8), inittab(5).
--- /dev/null
+'\" -*- coding: UTF-8 -*-
+.\" Copyright (C) 1998-2001 Miquel van Smoorenburg.
+.\"
+.\" This program is free software; you can redistribute it and/or modify
+.\" it under the terms of the GNU General Public License as published by
+.\" the Free Software Foundation; either version 2 of the License, or
+.\" (at your option) any later version.
+.\"
+.\" This program is distributed in the hope that it will be useful,
+.\" but WITHOUT ANY WARRANTY; without even the implied warranty of
+.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+.\" GNU General Public License for more details.
+.\"
+.\" You should have received a copy of the GNU General Public License
+.\" along with this program; if not, write to the Free Software
+.\" Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+.\"
+.\"{{{}}}
+.\"{{{ Title
+.TH INITTAB 5 "Dec 4, 2001" "" "Linux System Administrator's Manual"
+.\"}}}
+.\"{{{ Name
+.SH NAME
+inittab \- format of the inittab file used by the sysv-compatible init
+process
+.\"}}}
+.\"{{{ Description
+.SH DESCRIPTION
+The \fBinittab\fP file describes which processes are started at bootup and
+during normal operation (e.g.\& /etc/init.d/boot, /etc/init.d/rc, gettys...).
+.BR Init (8)
+distinguishes multiple \fIrunlevels\fP, each of which can have its own set of
+processes that are started. Valid runlevels are \fB0\fP\-\fB6\fP plus
+\fBA\fP, \fBB\fP, and \fBC\fP for \fBondemand\fP entries. An entry in the
+\fBinittab\fP file has the following format:
+.RS
+.sp
+\fIid\fP:\fIrunlevels\fP:\fIaction\fP:\fIprocess\fP
+.sp
+.RE
+Lines beginning with `#' are ignored.
+.\"{{{ id
+.IP \fIid\fP
+is a unique sequence of 1-4 characters which identifies an entry in
+.B inittab
+(for versions of sysvinit compiled with the \fIold\fP libc5 (< 5.2.18) or
+a.out libraries the limit is 2 characters).
+.sp
+Note: traditionally, for getty and other login processes, the value of the
+\fIid\fP field is kept the same as the suffix of the corresponding tty, e.g.\&
+\fB1\fP for \fBtty1\fP. Some ancient login accounting programs might
+expect this, though I can't think of any.
+.\"}}}
+.\"{{{ runlevels
+.IP \fIrunlevels\fP
+lists the runlevels for which the specified action should be taken.
+.\"}}}
+.\"{{{ action
+.IP \fIaction\fP
+describes which action should be taken.
+.\"}}}
+.\"{{{ process
+.IP \fIprocess\fP
+specifies the process to be executed. If the process field starts with
+a `+' character,
+.B init
+will not do utmp and wtmp accounting for that process. This is needed for
+gettys that insist on doing their own utmp/wtmp housekeeping. This is also
+a historic bug.
+.\"}}}
+.PP
+The \fIrunlevels\fP field may contain multiple characters for different
+runlevels. For example, \fB123\fP specifies that the process should be
+started in runlevels 1, 2, and 3.
+The \fIrunlevels\fP for \fBondemand\fP entries may contain an \fBA\fP,
+\fBB\fP, or \fBC\fP. The \fIrunlevels\fP field of \fBsysinit\fP,
+\fBboot\fP, and \fBbootwait\fP entries are ignored.
+.PP
+When the system runlevel is changed, any running processes that are not
+specified for the new runlevel are killed, first with \s-2SIGTERM\s0,
+then with \s-2SIGKILL\s0.
+.PP
+Valid actions for the \fIaction\fP field are:
+.\"{{{ respawn
+.IP \fBrespawn\fP
+The process will be restarted whenever it terminates (e.g.\& getty).
+.\"}}}
+.\"{{{ wait
+.IP \fBwait\fP
+The process will be started once when the specified runlevel is entered and
+.B init
+will wait for its termination.
+.\"}}}
+.\"{{{ once
+.IP \fBonce\fP
+The process will be executed once when the specified runlevel is
+entered.
+.\"}}}
+.\"{{{ boot
+.IP \fBboot\fP
+The process will be executed during system boot. The \fIrunlevels\fP
+field is ignored.
+.\"}}}
+.\"{{{ bootwait
+.IP \fBbootwait\fP
+The process will be executed during system boot, while
+.B init
+waits for its termination (e.g.\& /etc/rc).
+The \fIrunlevels\fP field is ignored.
+.\"}}}
+.\"{{{ off
+.IP \fBoff\fP
+This does nothing.
+.\"}}}
+.\"{{{ ondemand
+.IP \fBondemand\fP
+A process marked with an \fBondemand\fP runlevel will be executed
+whenever the specified \fBondemand\fP runlevel is called. However, no
+runlevel change will occur (\fBondemand\fP runlevels are `a', `b',
+and `c').
+.\"}}}
+.\"{{{ initdefault
+.IP \fBinitdefault\fP
+An \fBinitdefault\fP entry specifies the runlevel which should be
+entered after system boot. If none exists,
+.B init
+will ask for a runlevel on the console. The \fIprocess\fP field is ignored.
+.\"}}}
+.\"{{{ sysinit
+.IP \fBsysinit\fP
+The process will be executed during system boot. It will be
+executed before any \fBboot\fP or \fB bootwait\fP entries.
+The \fIrunlevels\fP field is ignored.
+.\"}}}
+.\"{{{ powerwait
+.IP \fBpowerwait\fP
+The process will be executed when the power goes down. Init is usually
+informed about this by a process talking to a UPS connected to the computer.
+\fBInit\fP will wait for the process to finish before continuing.
+.\"}}}
+.\"{{{ powerfail
+.IP \fBpowerfail\fP
+As for \fBpowerwait\fP, except that \fBinit\fP does not wait for the process's
+completion.
+.\"}}}
+.\"{{{ powerokwait
+.IP \fBpowerokwait\fP
+This process will be executed as soon as \fBinit\fP is informed that the
+power has been restored.
+.\"}}}
+.\"{{{ powerfailnow
+.IP \fBpowerfailnow\fP
+This process will be executed when \fBinit\fP is told that the battery of
+the external UPS is almost empty and the power is failing (provided that the
+external UPS and the monitoring process are able to detect this condition).
+.\"}}}
+.\"{{{ ctrlaltdel
+.IP \fBctrlaltdel\fP
+The process will be executed when \fBinit\fP receives the SIGINT signal.
+This means that someone on the system console has pressed the
+\fBCTRL\-ALT\-DEL\fP key combination. Typically one wants to execute some
+sort of \fBshutdown\fP either to get into single\-user level or to
+reboot the machine.
+.\"}}}
+.\"{{{ kbrequest
+.IP \fBkbrequest\fP
+The process will be executed when \fBinit\fP receives a signal from the
+keyboard handler that a special key combination was pressed on the
+console keyboard.
+.sp
+The documentation for this function is not complete yet; more documentation
+can be found in the kbd-x.xx packages (most recent was kbd-0.94 at
+the time of this writing). Basically you want to map some keyboard
+combination to the "KeyboardSignal" action. For example, to map Alt-Uparrow
+for this purpose use the following in your keymaps file:
+.RS
+.sp
+alt keycode 103 = KeyboardSignal
+.sp
+.RE
+.\"}}}
+.\"}}}
+.\"{{{ Examples
+.SH EXAMPLES
+This is an example of a inittab which resembles the old Linux inittab:
+.RS
+.sp
+.nf
+.ne 7
+# inittab for linux
+id:1:initdefault:
+rc::bootwait:/etc/rc
+1:1:respawn:/etc/getty 9600 tty1
+2:1:respawn:/etc/getty 9600 tty2
+3:1:respawn:/etc/getty 9600 tty3
+4:1:respawn:/etc/getty 9600 tty4
+.fi
+.sp
+.RE
+This inittab file executes \fB/etc/rc\fP during boot and starts gettys
+on tty1\-tty4.
+.PP
+A more elaborate \fBinittab\fP with different runlevels (see the comments
+inside):
+.RS
+.sp
+.nf
+.ne 19
+# Level to run in
+id:2:initdefault:
+
+# Boot-time system configuration/initialization script.
+si::sysinit:/etc/init.d/rcS
+
+# What to do in single-user mode.
+~:S:wait:/sbin/sulogin
+
+# /etc/init.d executes the S and K scripts upon change
+# of runlevel.
+#
+# Runlevel 0 is halt.
+# Runlevel 1 is single-user.
+# Runlevels 2-5 are multi-user.
+# Runlevel 6 is reboot.
+
+l0:0:wait:/etc/init.d/rc 0
+l1:1:wait:/etc/init.d/rc 1
+l2:2:wait:/etc/init.d/rc 2
+l3:3:wait:/etc/init.d/rc 3
+l4:4:wait:/etc/init.d/rc 4
+l5:5:wait:/etc/init.d/rc 5
+l6:6:wait:/etc/init.d/rc 6
+
+# What to do at the "3 finger salute".
+ca::ctrlaltdel:/sbin/shutdown -t1 -h now
+
+# Runlevel 2,3: getty on virtual consoles
+# Runlevel 3: getty on terminal (ttyS0) and modem (ttyS1)
+1:23:respawn:/sbin/getty tty1 VC linux
+2:23:respawn:/sbin/getty tty2 VC linux
+3:23:respawn:/sbin/getty tty3 VC linux
+4:23:respawn:/sbin/getty tty4 VC linux
+S0:3:respawn:/sbin/getty -L 9600 ttyS0 vt320
+S1:3:respawn:/sbin/mgetty -x0 -D ttyS1
+
+.fi
+.sp
+.RE
+.\"}}}
+.\"{{{ Files
+.SH FILES
+/etc/inittab
+.\"}}}
+.\"{{{ Author
+.SH AUTHOR
+\fBInit\fP was written by Miquel van Smoorenburg
+(miquels@cistron.nl). This manual page was written by
+Sebastian Lederer (lederer@francium.informatik.uni-bonn.de) and modified
+by Michael Haardt (u31b3hs@pool.informatik.rwth-aachen.de).
+.\"}}}
+.\"{{{ See also
+.SH "SEE ALSO"
+.BR init (8),
+.BR telinit (8)
+.\"}}}
--- /dev/null
+'\" -*- coding: UTF-8 -*-
+.\" Copyright (C) 1998-2003 Miquel van Smoorenburg.
+.\"
+.\" This program is free software; you can redistribute it and/or modify
+.\" it under the terms of the GNU General Public License as published by
+.\" the Free Software Foundation; either version 2 of the License, or
+.\" (at your option) any later version.
+.\"
+.\" This program is distributed in the hope that it will be useful,
+.\" but WITHOUT ANY WARRANTY; without even the implied warranty of
+.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+.\" GNU General Public License for more details.
+.\"
+.\" You should have received a copy of the GNU General Public License
+.\" along with this program; if not, write to the Free Software
+.\" Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+.\"
+.TH KILLALL5 8 "04 Nov 2003" "" "Linux System Administrator's Manual"
+.SH NAME
+killall5 -- send a signal to all processes.
+.SH SYNOPSIS
+.B killall5
+.RB -signalnumber
+.RB [ \-o
+.IR omitpid ]
+.RB [ \-o
+.IR omitpid.. ]
+.SH DESCRIPTION
+.B killall5
+is the SystemV killall command. It sends a signal to all processes except
+kernel threads and the processes in its own session, so it won't kill
+the shell that is running the script it was called from. Its primary
+(only) use is in the \fBrc\fP scripts found in the /etc/init.d directory.
+.SH OPTIONS
+.IP "-o \fIomitpid\fP"
+Tells \fIkillall5\fP to omit processes with that process id.
+.SH NOTES
+\fIkillall5\fP can also be invoked as pidof, which is simply a
+(symbolic) link to the \fIkillall5\fP program.
+.SH EXIT STATUS
+The program return zero if it killed processes. It return 2 if no
+process were killed, and 1 if it was unable to find any processes
+(/proc/ is missing).
+.SH SEE ALSO
+.BR halt (8),
+.BR reboot (8),
+.BR pidof (8)
+.SH AUTHOR
+Miquel van Smoorenburg, miquels@cistron.nl
--- /dev/null
+'\" -*- coding: UTF-8 -*-
+.\" Copyright (C) 1998-2004 Miquel van Smoorenburg.
+.\"
+.\" This program is free software; you can redistribute it and/or modify
+.\" it under the terms of the GNU General Public License as published by
+.\" the Free Software Foundation; either version 2 of the License, or
+.\" (at your option) any later version.
+.\"
+.\" This program is distributed in the hope that it will be useful,
+.\" but WITHOUT ANY WARRANTY; without even the implied warranty of
+.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+.\" GNU General Public License for more details.
+.\"
+.\" You should have received a copy of the GNU General Public License
+.\" along with this program; if not, write to the Free Software
+.\" Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+.\"
+.\"{{{}}}
+.\"{{{ Title
+.TH LAST,LASTB 1 "Jul 31, 2004" "" "Linux System Administrator's Manual"
+.\"}}}
+.\"{{{ Name
+.SH NAME
+last, lastb \- show listing of last logged in users
+.\"}}}
+.\"{{{ Synopsis
+.SH SYNOPSIS
+.B last
+.RB [ \-R ]
+.RB [ \-\fInum\fP ]
+.RB "[ \-\fBn\fP \fInum\fP ]"
+.RB [ \-adFiox ]
+.RB "[ \-\fBf\fP \fIfile\fP ]"
+.RB "[ \-\fBt\fP \fIYYYYMMDDHHMMSS\fP ]"
+.RI [ name... ]
+.RI [ tty... ]
+.br
+.B lastb
+.RB [ \-R ]
+.RB [ \-\fInum\fP ]
+.RB "[ \-\fBn\fP \fInum\fP ]"
+.RB "[ \-\fBf\fP \fIfile\fP ]"
+.RB [ \-adFiox ]
+.RI [ name... ]
+.RI [ tty... ]
+.\"}}}
+.\"{{{ Description
+.SH DESCRIPTION
+.B Last
+searches back through the file \fB/var/log/wtmp\fP (or the file
+designated by the \fB\-f\fP flag) and displays a list of all
+users logged in (and out) since that file was created. Names of users
+and tty's can be given, in which case \fBlast\fP will show only those entries
+matching the arguments. Names of ttys can be abbreviated, thus \fBlast
+0\fP is the same as \fBlast tty0\fP.
+.PP
+When \fBlast\fP catches a \s-2SIGINT\s0 signal (generated by the interrupt key,
+usually control-C) or a \s-2SIGQUIT\s0 signal (generated by the quit key,
+usually control-\e), \fBlast\fP will show how far it has searched through the
+file; in the case of the \s-2SIGINT\s0 signal \fBlast\fP will then terminate.
+.PP
+The pseudo user \fBreboot\fP logs in each time the system is rebooted.
+Thus \fBlast reboot\fP will show a log of all reboots since the log file
+was created.
+.PP
+\fBLastb\fP is the same as \fBlast\fP, except that by default it shows a log
+of the file \fB/var/log/btmp\fP, which contains all the bad login attempts.
+.\"}}}
+.\"{{{ Options
+.SH OPTIONS
+.IP "\fB\-f\fP \fIfile\fP"
+Tells \fBlast\fP to use a specific file instead of \fB/var/log/wtmp\fP.
+.IP \fB\-\fP\fInum\fP
+This is a count telling \fBlast\fP how many lines to show.
+.IP "\fB\-n\fP \fInum\fP"
+The same.
+.IP "\fB\-t\fP \fIYYYYMMDDHHMMSS\fP"
+Display the state of logins as of the specified time. This is
+useful, e.g., to determine easily who was logged in at a particular
+time -- specify that time with \fB\-t\fP and look for "still logged
+in".
+.IP \fB\-R\fP
+Suppresses the display of the hostname field.
+.IP \fB\-a\fP
+Display the hostname in the last column. Useful in combination
+with the next flag.
+.IP \fB\-d\fP
+For non-local logins, Linux stores not only the host name of the remote
+host but its IP number as well. This option translates the IP number
+back into a hostname.
+.IP \fB\-F\fP
+Print full login and logout times and dates.
+.IP \fB\-i\fP
+This option is like \fB-d\fP in that it displays the IP number of the remote
+host, but it displays the IP number in numbers-and-dots notation.
+.IP \fB\-o\fP
+Read an old-type wtmp file (written by linux-libc5 applications).
+.IP \fB\-x\fP
+Display the system shutdown entries and run level changes.
+.\"}}}
+.SH NOTES
+The files \fIwtmp\fP and \fIbtmp\fP might not be found. The system only
+logs information in these files if they are present. This is a local
+configuration issue. If you want the files to be used, they can be
+created with a simple \fBtouch\fP(1) command (for example,
+\fItouch /var/log/wtmp\fP).
+.\"{{{ Files
+.SH FILES
+/var/log/wtmp
+.br
+/var/log/btmp
+.\"}}}
+.\"{{{ Author
+.SH AUTHOR
+Miquel van Smoorenburg, miquels@cistron.nl
+.\"}}}
+.\"{{{ See also
+.SH "SEE ALSO"
+.BR shutdown (8),
+.BR login (1),
+.BR init (8)
+.\"}}}
--- /dev/null
+.so man1/last.1
--- /dev/null
+'\" -*- coding: UTF-8 -*-
+.\" Copyright (C) 1998-2001 Miquel van Smoorenburg.
+.\"
+.\" This program is free software; you can redistribute it and/or modify
+.\" it under the terms of the GNU General Public License as published by
+.\" the Free Software Foundation; either version 2 of the License, or
+.\" (at your option) any later version.
+.\"
+.\" This program is distributed in the hope that it will be useful,
+.\" but WITHOUT ANY WARRANTY; without even the implied warranty of
+.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+.\" GNU General Public License for more details.
+.\"
+.\" You should have received a copy of the GNU General Public License
+.\" along with this program; if not, write to the Free Software
+.\" Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+.\"
+.\"{{{}}}
+.\"{{{ Title
+.TH MESG 1 "Feb 26, 2001" "" "Linux User's Manual"
+.\"}}}
+.\"{{{ Name
+.SH NAME
+mesg \- control write access to your terminal
+.\"}}}
+.\"{{{ Synopsis
+.SH SYNOPSIS
+.B mesg
+.RB [ y | n ]
+.\"}}}
+.\"{{{ Description
+.SH DESCRIPTION
+.B Mesg
+controls the access to your terminal by others. It's typically used to
+allow or disallow other users to write to your terminal (see \fBwrite\fP(1)).
+.\"}}}
+.\"{{{ Options
+.SH OPTIONS
+.IP \fBy\fP
+Allow write access to your terminal.
+.IP \fBn\fP
+Disallow write access to your terminal.
+.PP
+If no option is given, \fBmesg\fP prints out the current access state of your
+terminal.
+.\"}}}
+.\"{{{ Notes
+.SH NOTES
+\fBMesg\fP assumes that its standard input is connected to your
+terminal. That also means that if you are logged in multiple times,
+you can get/set the mesg status of other sessions by using redirection.
+For example "mesg n < /dev/pts/46".
+.SH AUTHOR
+Miquel van Smoorenburg (miquels@cistron.nl)
+.\"}}}
+.\"{{{ See also
+.SH "SEE ALSO"
+.BR talk (1),
+.BR write (1),
+.BR wall (1)
+.\"}}}
--- /dev/null
+'\" -*- coding: UTF-8 -*-
+.\" Copyright (C) 1998-2004 Miquel van Smoorenburg.
+.\"
+.\" This program is free software; you can redistribute it and/or modify
+.\" it under the terms of the GNU General Public License as published by
+.\" the Free Software Foundation; either version 2 of the License, or
+.\" (at your option) any later version.
+.\"
+.\" This program is distributed in the hope that it will be useful,
+.\" but WITHOUT ANY WARRANTY; without even the implied warranty of
+.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+.\" GNU General Public License for more details.
+.\"
+.\" You should have received a copy of the GNU General Public License
+.\" along with this program; if not, write to the Free Software
+.\" Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+.\"
+.TH MOUNTPOINT 1 "Mar 15, 2004" "" "Linux System Administrator's Manual"
+.SH NAME
+mountpoint \- see if a directory is a mountpoint
+.SH SYNOPSIS
+.B /bin/mountpoint
+.RB [ \-q ]
+.RB [ \-d ]
+.I /path/to/directory
+.br
+.B /bin/mountpoint
+.RB \-x
+.I /dev/device
+.SH DESCRIPTION
+\fBMountpoint\fP checks if the directory is a mountpoint.
+
+.SH OPTIONS
+.IP \fB\-q\fP
+Be quiet - don't print anything.
+.IP \fB\-d\fP
+Print major/minor device number of the filesystem on stdout.
+.IP \fB\-x\fP
+Print major/minor device number of the blockdevice on stdout.
+.SH EXIT STATUS
+Zero if the directory is a mountpoint, non-zero if not.
+.SH NOTES
+Symbolic links are not followed, except when the \fB-x\fP option is
+used. To force following symlinks, add a trailing slash to the
+path of the directory.
+.PP
+The name of the command is misleading when the -x option is used,
+but the option is useful for comparing if a directory and a device
+match up, and there is no other command that can print the info easily.
+.PP
+.SH AUTHOR
+Miquel van Smoorenburg, miquels@cistron.nl
+.SH "SEE ALSO"
+.BR stat (1)
--- /dev/null
+'\" -*- coding: UTF-8 -*-
+.\" Copyright (C) 1998 Miquel van Smoorenburg.
+.\"
+.\" This program is free software; you can redistribute it and/or modify
+.\" it under the terms of the GNU General Public License as published by
+.\" the Free Software Foundation; either version 2 of the License, or
+.\" (at your option) any later version.
+.\"
+.\" This program is distributed in the hope that it will be useful,
+.\" but WITHOUT ANY WARRANTY; without even the implied warranty of
+.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+.\" GNU General Public License for more details.
+.\"
+.\" You should have received a copy of the GNU General Public License
+.\" along with this program; if not, write to the Free Software
+.\" Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+.\"
+.TH PIDOF 8 "01 Sep 1998" "" "Linux System Administrator's Manual"
+.SH NAME
+pidof -- find the process ID of a running program.
+.SH SYNOPSIS
+.B pidof
+.RB [ \-s ]
+.RB [ \-c ]
+.RB [ \-x ]
+.RB [ \-o
+.IR omitpid ]
+.RB [ \-o
+.IR omitpid.. ]
+.B program
+.RB [ program.. ]
+.SH DESCRIPTION
+.B Pidof
+finds the process id's (pids) of the named programs. It prints those
+id's on the standard output. This program is on some systems used in
+run-level change scripts, especially when the system has a
+\fISystem-V\fP like \fIrc\fP structure. In that case these scripts are
+located in /etc/rc?.d, where ? is the runlevel. If the system has
+a
+.B start-stop-daemon
+(8) program that should be used instead.
+.SH OPTIONS
+.IP -s
+Single shot - this instructs the program to only return one \fIpid\fP.
+.IP -c
+Only return process ids that are running with the same root directory.
+This option is ignored for non-root users, as they will be unable to check
+the current root directory of processes they do not own.
+.IP -x
+Scripts too - this causes the program to also return process id's of
+shells running the named scripts.
+.IP "-o \fIomitpid\fP"
+Tells \fIpidof\fP to omit processes with that process id. The special
+pid \fB%PPID\fP can be used to name the parent process of the \fIpidof\fP
+program, in other words the calling shell or shell script.
+.SH "EXIT STATUS"
+.TP
+.B 0
+At least one program was found with the requested name.
+.TP
+.B 1
+No program was found with the requested name.
+.SH NOTES
+\fIpidof\fP is actually the same program as \fIkillall5\fP;
+the program behaves according to the name under which it is called.
+.PP
+When \fIpidof\fP is invoked with a full pathname to the program it
+should find the pid of, it is reasonably safe. Otherwise it is possible
+that it returns pids of running programs that happen to have the same name
+as the program you're after but are actually other programs.
+.SH SEE ALSO
+.BR shutdown (8),
+.BR init (8),
+.BR halt (8),
+.BR reboot (8),
+.BR killall5 (8)
+.SH AUTHOR
+Miquel van Smoorenburg, miquels@cistron.nl
--- /dev/null
+.so man8/halt.8
--- /dev/null
+.so man8/halt.8
--- /dev/null
+'\" -*- coding: UTF-8 -*-
+.\" Copyright (C) 1997 Miquel van Smoorenburg.
+.\"
+.\" This program is free software; you can redistribute it and/or modify
+.\" it under the terms of the GNU General Public License as published by
+.\" the Free Software Foundation; either version 2 of the License, or
+.\" (at your option) any later version.
+.\"
+.\" This program is distributed in the hope that it will be useful,
+.\" but WITHOUT ANY WARRANTY; without even the implied warranty of
+.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+.\" GNU General Public License for more details.
+.\"
+.\" You should have received a copy of the GNU General Public License
+.\" along with this program; if not, write to the Free Software
+.\" Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+.\"
+.TH RUNLEVEL 8 "May 27, 1997" "" "Linux System Administrator's Manual"
+.SH NAME
+runlevel -- find the previous and current system runlevel.
+.SH SYNOPSIS
+.B runlevel
+.RI [ utmp ]
+.SH DESCRIPTION
+.B Runlevel
+reads the system
+.I utmp
+file (typically
+.IR /var/run/utmp )
+to locate the runlevel record, and then
+prints the previous and current system runlevel on its standard output,
+separated by a single space. If there is no previous system
+runlevel, the letter \fBN\fP will be printed instead.
+.PP
+If no
+.I utmp
+file exists, or if no runlevel record can be found,
+.B runlevel
+prints the word \fBunknown\fP and exits with an error.
+.PP
+.B Runlevel
+can be used in \fIrc\fP scripts as a substitute for the System-V
+\fBwho -r\fP command.
+However, in newer versions of \fBinit\fP(8) this information
+is also available in the environment variables \fBRUNLEVEL\fP and
+\fBPREVLEVEL\fP.
+.SH OPTIONS
+.\"{{{ utmp
+.IP \fIutmp\fP
+The name of the \fIutmp\fP file to read.
+.\"}}}
+.SH SEE ALSO
+.BR init (8),
+.BR utmp (5)
+.SH AUTHOR
+Miquel van Smoorenburg, miquels@cistron.nl
--- /dev/null
+'\" -*- coding: UTF-8 -*-
+.\" Copyright (C) 1998-2003 Miquel van Smoorenburg.
+.\"
+.\" This program is free software; you can redistribute it and/or modify
+.\" it under the terms of the GNU General Public License as published by
+.\" the Free Software Foundation; either version 2 of the License, or
+.\" (at your option) any later version.
+.\"
+.\" This program is distributed in the hope that it will be useful,
+.\" but WITHOUT ANY WARRANTY; without even the implied warranty of
+.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+.\" GNU General Public License for more details.
+.\"
+.\" You should have received a copy of the GNU General Public License
+.\" along with this program; if not, write to the Free Software
+.\" Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+.\"
+.\"{{{}}}
+.\"{{{ Title
+.TH SHUTDOWN 8 "November 12, 2003" "" "Linux System Administrator's Manual"
+.\"}}}
+.\"{{{ Name
+.SH NAME
+shutdown \- bring the system down
+.\"}}}
+.\"{{{ Synopsis
+.SH SYNOPSIS
+.B /sbin/shutdown
+.RB [ \-t
+.IR sec ]
+.RB [ \-arkhncfFHP ]
+.I time
+.RI [ warning-message ]
+.\"}}}
+.\"{{{ Description
+.SH DESCRIPTION
+\fBshutdown\fP brings the system down in a secure way. All logged-in users are
+notified that the system is going down, and \fBlogin\fP(1) is blocked.
+It is possible to shut the system down immediately or after a specified delay.
+All processes are first notified that the system is going down by the
+signal \s-2SIGTERM\s0. This gives programs like \fBvi\fP(1)
+the time to save the file being edited,
+mail and news processing programs a chance to exit cleanly, etc.
+\fBshutdown\fP does its job by signalling the \fBinit\fP process,
+asking it to change the runlevel.
+Runlevel \fB0\fP is used to halt the system, runlevel \fB6\fP is used
+to reboot the system, and runlevel \fB1\fP is used to put to system into
+a state where administrative tasks can be performed; this is the default
+if neither the \fI-h\fP or \fI-r\fP flag is given to \fBshutdown\fP.
+To see which actions are taken on halt or reboot see the appropriate
+entries for these runlevels in the file \fI/etc/inittab\fP.
+.\"}}}
+.\"{{{ Options
+.SH OPTIONS
+.\"{{{ -a
+.IP "\fB\-a\fP
+Use \fB/etc/shutdown.allow\fP.
+.\"}}}
+.\"{{{ -t sec
+.IP "\fB\-t\fP \fIsec\fP"
+Tell \fBinit\fP(8) to wait \fIsec\fP seconds between sending processes the
+warning and the kill signal, before changing to another runlevel.
+.\"}}}
+.\"{{{ -k
+.IP \fB\-k\fP
+Don't really shutdown; only send the warning messages to everybody.
+.\"}}}
+.\"{{{ -r
+.IP \fB\-r\fP
+Reboot after shutdown.
+.\"}}}
+.\"{{{ -h
+.IP \fB\-h\fP
+Halt or power off after shutdown.
+.\"}}}
+.\"{{{ -H
+.IP \fB\-H\fP
+Modifier to the -h flag. Halt action is to halt or drop into boot
+monitor on systems that support it. Must be used with the -h flag.
+.\"}}}
+.\"{{{ -P
+.IP \fB\-P\fP
+Halt action is to turn off the power.
+.\"}}}
+.\"{{{ -n
+.IP \fB\-n\fP
+[DEPRECATED] Don't call \fBinit\fP(8) to do the shutdown but do it ourself.
+The use of this option is discouraged, and its results are not always what
+you'd expect.
+.\"}}}
+.\"{{{ -f
+.IP \fB\-f\fP
+Skip fsck on reboot.
+.\"}}}
+.\"{{{ -F
+.IP \fB\-F\fP
+Force fsck on reboot.
+.\"}}}
+.\"{{{ -c
+.IP \fB\-c\fP
+Cancel an already running shutdown. With this option it is of course
+not possible to give the \fBtime\fP argument, but you can enter a
+explanatory message on the command line that will be sent to all users.
+.\"}}}
+.\"{{{ time
+.IP \fItime\fP
+When to shutdown.
+.\"}}}
+.\"{{{ warning-message
+.IP \fIwarning-message\fP
+Message to send to all users.
+.\"}}}
+.PP
+The \fItime\fP argument can have different formats. First, it can be an
+absolute time in the format \fIhh:mm\fP, in which \fIhh\fP is the hour
+(1 or 2 digits) and \fImm\fP is the minute of the hour (in two digits).
+Second, it can be in the format \fB+\fP\fIm\fP, in which \fIm\fP is the
+number of minutes to wait. The word \fBnow\fP is an alias for \fB+0\fP.
+.PP
+If shutdown is called with a delay, it will create the advisory file
+.I /etc/nologin
+which causes programs such as \fIlogin(1)\fP to not allow new user
+logins. This file is created five minutes before the shutdown sequence
+starts. Shutdown removes this file if it is stopped before it
+can signal init (i.e. it is cancelled or something goes wrong).
+It also removes it before calling init to change the runlevel.
+.PP
+The \fB\-f\fP flag means `reboot fast'. This only creates an advisory
+file \fI/fastboot\fP which can be tested by the system when it comes
+up again. The boot rc file can test if this file is present, and decide not
+to run \fBfsck\fP(1) since the system has been shut down in the proper way.
+After that, the boot process should remove \fI/fastboot\fP.
+.PP
+The \fB\-F\fP flag means `force fsck'. This only creates an advisory
+file \fI/forcefsck\fP which can be tested by the system when it comes
+up again. The boot rc file can test if this file is present, and decide
+to run \fBfsck\fP(1) with a special `force' flag so that even properly
+unmounted file systems get checked.
+After that, the boot process should remove \fI/forcefsck\fP.
+.PP
+The \fB-n\fP flag causes \fBshutdown\fP not to call \fBinit\fP,
+but to kill all running processes itself.
+\fBshutdown\fP will then turn off quota, accounting, and swapping
+and unmount all file systems.
+.\"}}}
+.\"{{{ Files
+.SH ACCESS CONTROL
+\fBshutdown\fP can be called from \fBinit\fP(8) when the magic keys
+\fBCTRL-ALT-DEL\fP are pressed, by creating an appropriate entry in
+\fI/etc/inittab\fP. This means that everyone who has physical access
+to the console keyboard can shut the system down. To prevent this,
+\fBshutdown\fP can check to see if an authorized user is logged in on
+one of the virtual consoles. If \fBshutdown\fP is called with the \fB-a\fP
+argument (add this to the invocation of shutdown in /etc/inittab),
+it checks to see if the file \fI/etc/shutdown.allow\fP is present.
+It then compares the login names in that file with the list of people
+that are logged in on a virtual console (from \fI/var/run/utmp\fP). Only
+if one of those authorized users \fBor root\fP is logged in, it will
+proceed. Otherwise it will write the message
+.sp 1
+.nf
+\fBshutdown: no authorized users logged in\fP
+.fi
+.sp 1
+to the (physical) system console. The format of \fI/etc/shutdown.allow\fP
+is one user name per line. Empty lines and comment lines (prefixed by a
+\fB#\fP) are allowed. Currently there is a limit of 32 users in this file.
+.sp 1
+Note that if \fI/etc/shutdown.allow\fP is not present, the \fB-a\fP
+argument is ignored.
+.SH HALT OR POWEROFF
+The \fB-H\fP option just sets the \fIinit\fP environment variable
+\fIINIT_HALT\fP to \fIHALT\fP, and the \fB-P\fP option just sets
+that variable to \fIPOWEROFF\fP. The shutdown script that calls
+\fBhalt\fP(8) as the last thing in the shutdown sequence should
+check these environment variables and call \fBhalt\fP(8) with
+the right options for these options to actually have any effect.
+Debian 3.1 (sarge) supports this.
+.SH FILES
+.nf
+/fastboot
+/etc/inittab
+/etc/init.d/halt
+/etc/init.d/reboot
+/etc/shutdown.allow
+.fi
+.\"}}}
+.SH NOTES
+A lot of users forget to give the \fItime\fP argument
+and are then puzzled by the error message \fBshutdown\fP produces. The
+\fItime\fP argument is mandatory; in 90 percent of all cases this argument
+will be the word \fBnow\fP.
+.PP
+Init can only capture CTRL-ALT-DEL and start shutdown in console mode.
+If the system is running the X window System, the X server processes
+all key strokes. Some X11 environments make it possible to capture
+CTRL-ALT-DEL, but what exactly is done with that event depends on
+that environment.
+.PP
+Shutdown wasn't designed to be run setuid. /etc/shutdown.allow is
+not used to find out who is executing shutdown, it ONLY checks who
+is currently logged in on (one of the) console(s).
+.\"{{{ Author
+.SH AUTHOR
+Miquel van Smoorenburg, miquels@cistron.nl
+.\"}}}
+.\"{{{ See also
+.SH "SEE ALSO"
+.BR fsck (8),
+.BR init (8),
+.BR halt (8),
+.BR poweroff (8),
+.BR reboot (8)
+.\"}}}
--- /dev/null
+'\" -*- coding: UTF-8 -*-
+.\" Copyright (C) 1998-2006 Miquel van Smoorenburg.
+.\"
+.\" This program is free software; you can redistribute it and/or modify
+.\" it under the terms of the GNU General Public License as published by
+.\" the Free Software Foundation; either version 2 of the License, or
+.\" (at your option) any later version.
+.\"
+.\" This program is distributed in the hope that it will be useful,
+.\" but WITHOUT ANY WARRANTY; without even the implied warranty of
+.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+.\" GNU General Public License for more details.
+.\"
+.\" You should have received a copy of the GNU General Public License
+.\" along with this program; if not, write to the Free Software
+.\" Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+.\"
+.TH SULOGIN 8 "17 Jan 2006" "" "Linux System Administrator's Manual"
+.SH NAME
+sulogin \- Single-user login
+.SH SYNOPSIS
+.B sulogin
+[ \fB\-e\fP ]
+[ \fB\-p\fP ]
+[ \fB\-t\fP \fISECONDS\fP ]
+[ \fITTY\fP ]
+.SH DESCRIPTION
+.I sulogin
+is invoked by \fBinit(8)\fP when the system goes into single user mode.
+(This is done through an entry in \fIinittab(5)\fP.)
+\fBInit\fP also
+tries to execute \fIsulogin\fP when
+the boot loader (e.g., \fBgrub\fP(8))
+passes it the \fB\-b\fP option.
+.PP
+The user is prompted
+.IP "" .5i
+Give root password for system maintenance
+.br
+(or type Control\-D for normal startup):
+.PP
+\fIsulogin\fP will be connected to the current terminal, or to the
+optional device that can be specified on the command line
+(typically \fB/dev/console\fP).
+.PP
+If the \fB\-t\fP option is used then the program only waits
+the given number of seconds for user input.
+.PP
+If the \fB\-p\fP option is used then the single-user shell is invoked
+with a \fIdash\fP as the first character in \fIargv[0]\fP.
+This causes the shell process to behave as a login shell.
+The default is \fInot\fP to do this,
+so that the shell will \fInot\fP read \fB/etc/profile\fP
+or \fB$HOME/.profile\fP at startup.
+.PP
+After the user exits the single-user shell,
+or presses control\-D at the prompt,
+the system will (continue to) boot to the default runlevel.
+.SH ENVIRONMENT VARIABLES
+\fIsulogin\fP looks for the environment variable \fBSUSHELL\fP or
+\fBsushell\fP to determine what shell to start. If the environment variable
+is not set, it will try to execute root's shell from /etc/passwd. If that
+fails it will fall back to \fB/bin/sh\fP.
+.PP
+This is very valuable together with the \fB\-b\fP option to init. To boot
+the system into single user mode, with the root file system mounted read/write,
+using a special "fail safe" shell that is statically linked (this example
+is valid for the LILO bootprompt)
+.PP
+boot: linux \-b rw sushell=/sbin/sash
+.SH FALLBACK METHODS
+\fIsulogin\fP checks the root password using the standard method (getpwnam)
+first.
+Then, if the \fB\-e\fP option was specified,
+\fIsulogin\fP examines these files directly to find the root password:
+.PP
+/etc/passwd,
+.br
+/etc/shadow (if present)
+.PP
+If they are damaged or nonexistent, sulogin will start a root shell
+without asking for a password. Only use the \fB\-e\fP option if you
+are sure the console is physically protected against unauthorized access.
+.SH AUTHOR
+Miquel van Smoorenburg <miquels@cistron.nl>
+.SH SEE ALSO
+init(8), inittab(5).
--- /dev/null
+.so man8/init.8
--- /dev/null
+'\" -*- coding: UTF-8 -*-
+.\" Copyright (C) 1998-2003 Miquel van Smoorenburg.
+.\"
+.\" This program is free software; you can redistribute it and/or modify
+.\" it under the terms of the GNU General Public License as published by
+.\" the Free Software Foundation; either version 2 of the License, or
+.\" (at your option) any later version.
+.\"
+.\" This program is distributed in the hope that it will be useful,
+.\" but WITHOUT ANY WARRANTY; without even the implied warranty of
+.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+.\" GNU General Public License for more details.
+.\"
+.\" You should have received a copy of the GNU General Public License
+.\" along with this program; if not, write to the Free Software
+.\" Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+.\"
+.TH WALL 1 "15 April 2003" "" "Linux User's Manual"
+
+.SH NAME
+wall -- send a message to everybody's terminal.
+
+.SH SYNOPSIS
+.B wall
+.RB [ \-n ]
+.RB [ " message " ]
+
+.SH DESCRIPTION
+.B Wall
+sends a message to everybody logged in with their
+.IR mesg (1)
+permission
+set to
+.BR yes .
+The message can be given as an argument to
+.IR wall ,
+or it can be sent to
+.IR wall 's
+standard input. When using the standard input from a terminal,
+the message should be terminated with the
+.B EOF
+key (usually Control-D).
+.PP
+The length of the message is limited to 20 lines.
+For every invocation of
+.I wall
+a notification will be written to syslog, with facility
+.B LOG_USER
+and level
+.BR LOG_INFO .
+
+.SH OPTIONS
+.IP \fB\-n\fn
+Suppresses the normal banner printed by
+.IR wall ,
+changing it to "Remote broadcast message".
+This option is only available for root if
+.I wall
+is installed set-group-id, and is used by
+.IR rpc.walld (8).
+.PP
+
+.SH ENVIRONMENT
+.I Wall
+ignores the
+.B TZ
+variable - the time printed in the banner is based on the system's
+local time.
+
+.SH SEE ALSO
+.IR mesg (1),
+.IR rpc.rwalld (8).
+
+.SH AUTHOR
+Miquel van Smoorenburg, miquels@cistron.nl
--- /dev/null
+WARNING:
+
+ This version of sysvinit is really different from the 2.50 and
+ earlier version.
+
+ Shutdown now puts the system into runlevel 6 (reboot), 0 (halt)
+ or 1 (single user). This can cause unexpected results if you
+ install the binaries from this release into Slackware distributions
+ older than Slackware 3.0.
+
+SUPPORTED DISTRIBUTIONS:
+
+ The binaries from this package can be installed in:
+
+ o Debian 1.3 and later
+ o RedHat 3.x and later
+ o Slackware 3.0 (UNTESTED but it might work - no complaints yet).
+ Also read the INIT.README in the slackware/ directory.
+ o Slackware 2.x: see the slackware/ directory
+
+ Do not install any of the scripts from the debian/ directory unless
+ you know what you are doing.
+
+UNSUPPORTED DISTRIBUTIONS:
+
+ o The rest :)
+
+ If you have a non-supported system, please upgrade to the latest version
+ of your distribution that supports the Linux 2.0.x kernel (probably
+ the reason why you are installing this newer sysvinit).
+
+ You might get away by installing *just* the "init" binary, and nothing
+ else. Do _not_ replace your existing halt, reboot or shutdown programs.
+
+HOW TO NON DESTRUCTIVELY TEST THE NEW INIT:
+
+ Install *just* the init binary as /sbin/init.new. Now reboot the system,
+ and stop your bootloader so you can give arguments on the command line.
+ With LILO you can usually achieve this by keeping the SHIFT key
+ pressed during boot up. Enter the name of the kernel image (for LILO,
+ TAB shows a list) followed by the argument "init=/sbin/init.new".
+ The name "init.new" is special, do not use something like "init.test".
+
+ For example:
+
+ boot: linux init=/sbin/init.new
+
+ YOU CANNOT SHUTDOWN IN A CLEAN WAY AFTER THIS. Your best bet is to use
+ the "-n" flag to shutdown. This is because init is not running as process #1
+ if you use this method. Anyway, if this works, you can remove the old init
+ and copy the new init into place.
+
+DISCLAIMER:
+
+ If it breaks you get to keep both pieces. If you want to run the latest
+ Linux 2.0.x kernel and you can't get init to work just upgrade your entire
+ distribution to a newer version that supports the 2.0.x kernel properly.
+
--- /dev/null
+#! /bin/sh
+#
+# bootlogd One of the first scripts to be executed. Starts or stops
+# the bootlogd log program. If this script is called as
+# "stop-bootlogd", it will stop the daemon instead of
+# starting it even when called with the "start" argument.
+#
+# Version: @(#)bootlogd 2.77 24-Aug-1999 miquels@cistron.nl
+#
+
+PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
+DAEMON=/sbin/bootlogd
+NAME=bootlogd
+DESC="Bootlog daemon"
+PIDFILE=/var/run/$NAME.pid
+
+test -f $DAEMON || exit 0
+
+## set -e # not needed
+
+. /etc/default/rcS
+
+case "$0" in
+ *stop-bootlog*)
+ stopper=yes
+ ;;
+esac
+
+case "$1" in
+ start|stop)
+ if [ "$stopper" ] || [ "$1" = "stop" ]
+ then
+ echo -n "Stopping $DESC: "
+ start-stop-daemon --stop --quiet --exec $DAEMON
+ else
+ echo -n "Starting $DESC: "
+ start-stop-daemon --start --quiet --exec $DAEMON -- -r
+ fi
+ if [ "$stopper" ] && [ -f /var/log/boot.log ] && \
+ [ -f /var/log/boot.log~ ]
+ then
+ cd /var/log
+ savelog -p -c 5 boot.log > /dev/null 2>&1
+ mv boot.log.0 boot.log
+ mv boot.log~ boot.log.0
+ fi
+ echo "$NAME."
+ ;;
+ restart|force-reload)
+ echo -n "Restarting $DESC: "
+ start-stop-daemon --stop --quiet --pidfile \
+ $PIDFILE --exec $DAEMON -- -p $PIDFILE
+ sleep 1
+ start-stop-daemon --start --quiet --pidfile \
+ $PIDFILE --exec $DAEMON -- -p $PIDFILE
+ echo "$NAME."
+ ;;
+ *)
+ N=${0##*/}
+ N=${N#[SK]??}
+ echo "Usage: $N {start|stop|restart|force-reload}" >&2
+ exit 1
+ ;;
+esac
+
+exit 0
+
--- /dev/null
+'\" -*- coding: UTF-8 -*-
+.\" Copyright (C) 1994 Miquel van Smoorenburg.
+.\"
+.\" This program is free software; you can redistribute it and/or modify
+.\" it under the terms of the GNU General Public License as published by
+.\" the Free Software Foundation; either version 2 of the License, or
+.\" (at your option) any later version.
+.\"
+.\" This program is distributed in the hope that it will be useful,
+.\" but WITHOUT ANY WARRANTY; without even the implied warranty of
+.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+.\" GNU General Public License for more details.
+.\"
+.\" You should have received a copy of the GNU General Public License
+.\" along with this program; if not, write to the Free Software
+.\" Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+.\"
+.TH POWERD 8 "Feb 14, 1994" "" "Linux System Administrator's Manual"
+.SH NAME
+.\" powerd \(em monitor a serial line connected to an UPS.
+powerd -- monitor a serial line connected to an UPS.
+.SH SYNOPSIS
+.B /sbin/powerd
+.RB " serial-device "
+.SH DESCRIPTION
+.B Powerd
+is a daemon process that sits in the background and monitors the state
+of the DCD line of the serial device. This line is meant to be
+connected to a UPS (Uninterruptible Power Supply) so that \fBpowerd\fP knows
+about the state of the UPS. As soon as \fBpowerd\fP senses that the
+power is failing (it sees that DCD goes low) it notifies \fBinit\fP(8),
+and \fBinit\fP then executes the \fBpowerwait\fP and \fBpowerfail\fP entries.
+If \fBpowerd\fP senses that the power has been restored, it notifies \fBinit\fP
+again and \fBinit\fP will execute the \fBpowerokwait\fP entries.
+.SH ARGUMENTS
+.IP serial-device
+Some serial port that is not being used by some other device, and does not
+share an interrupt with any other serial port.
+.SH DIAGNOSTICS
+\fBPowerd\fP regularly checks the \fBDSR\fP line to see if it's high.
+\fBDSR\fP should be directly connected to \fBDTR\fP and \fBpowerd\fP
+keeps that line high, so if \fBDSR\fP is low then something is wrong
+with the connection. \fBPowerd\fP will notify you about this fact every
+two minutes. When it sees that the connection has been restored it
+will say so.
+.SH HOWTO
+It's pretty simple to connect your UPS to the Linux machine. The steps
+are easy:
+.TP 0.5i
+.B 1.
+Make sure you have an UPS with a simple relay output: it should
+close its connections (make) if the power is gone, and it should
+open its connections (break) if the power is good.
+.TP 0.5i
+.B 2.
+Buy a serial plug. Connect the DTR line to the DSR line directly.
+Connect the DTR line and the DCD line with a \fB10 kilo ohm\fP
+resistor. Now connect the relay output of the UPS to GROUND
+and the DCD line. If you don't know what pins DSR, DTR, DCD and
+GROUND are you can always ask at the store where you bought the plug.
+.TP 0.5i
+.B 3.
+You're all set.
+.SH BUGS
+Well, not a real bug but \fBpowerd\fP should be able to do a broadcast or
+something on the ethernet in case more Linux-boxes are connected to
+the same UPS and only one of them is connected to the UPS status line.
+.SH SEE ALSO
+.BR shutdown (8),
+.BR init (8),
+.BR inittab (5)
+.SH AUTHOR
+Miquel van Smoorenburg, miquels@cistron.nl
--- /dev/null
+There are 2 *much* better powerd's than the one that is included as
+an example with sysvinit. The powerd.c in this distribution is just ment
+as a programming example, not to be used in a real life situation.
+
+1. GENPOWERD.
+
+This is a powerd written by Tom Webster <webster@kaiwan.com>. It's a
+nice package, you can find info at http://www.kaiwan.com/~webster/genpower.html
+
+2. POWERD-2.0.
+
+This is another powerd, written by rubini@ipvvis.unipv.it (Alessandro Rubini).
+The main advantage over genpowerd is that it can signal other machines over
+the network.
+
+This LSM may be out of date. Please check if a newer version exists.
+
+Begin3
+Title: powerd
+Version: 2.0
+Entered-date: Sep 26 1995
+Description: A daemon to shut down and up computers connected to ups's.
+ Network-aware: server-mode and client-mode allowed.
+Keywords: ups, powerd, init
+Author: Alessandro Rubini (based on Miquel van Smoorenburg's work).
+Maintained-by: rubini@ipvvis.unipv.it (Alessandro Rubini)
+Primary-site: sunsite.unc.edu /pub/Linux/system/UPS/powerd-2.0.tar.gz
+ 25kB powerd-2.0.tar.gz
+ 1kB powerd-2.0.lsm
+Alternate-site:
+Original-site: iride.unipv.it /pub/linux
+ 25kB powerd-2.0.tar.gz
+ 1kB powerd-2.0.lsm
+Platform: Linux. Porting is foreseeable.
+Copying-policy: GPL
+End
--- /dev/null
+/*
+ * powerd Monitor the DCD line of a serial port connected to
+ * an UPS. If the power goes down, notify init.
+ * If the power comes up again, notify init again.
+ * As long as the power is OK, the DCD line should be
+ * "HIGH". When the power fails, DCD should go "LOW".
+ * Powerd keeps DTR high so that you can connect
+ * DCD and DTR with a resistor of 10 Kilo Ohm and let the
+ * UPS or some relais pull the DCD line to ground.
+ * You also need to connect DTR and DSR together. This
+ * way, powerd can check now and then if DSR is high
+ * so it knows the UPS is connected!!
+ *
+ * Usage: powerd /dev/cua4 (or any other serial device).
+ *
+ * Author: Miquel van Smoorenburg, <miquels@drinkel.cistron.nl>.
+ *
+ * Version: 1.31, 29-Feb-1996.
+ *
+ * This program was originally written for my employer,
+ * ** Cistron Electronics **
+ * who has given kind permission to release this program
+ * for general puppose.
+ *
+ * Copyright (C) 1991-1996 Cistron Electronics.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/* Use the new way of communicating with init. */
+#define NEWINIT
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <signal.h>
+#include <syslog.h>
+#include <string.h>
+#include "paths.h"
+#ifdef NEWINIT
+#include "initreq.h"
+#endif
+
+#ifndef SIGPWR
+# define SIGPWR SIGUSR1
+#endif
+
+#ifdef NEWINIT
+void alrm_handler()
+{
+}
+#endif
+
+/* Tell init the power has either gone or is back. */
+void powerfail(ok)
+int ok;
+{
+ int fd;
+#ifdef NEWINIT
+ struct init_request req;
+
+ /* Fill out the request struct. */
+ memset(&req, 0, sizeof(req));
+ req.magic = INIT_MAGIC;
+ req.cmd = ok ? INIT_CMD_POWEROK : INIT_CMD_POWERFAIL;
+
+ /* Open the fifo (with timeout) */
+ signal(SIGALRM, alrm_handler);
+ alarm(3);
+ if ((fd = open(INIT_FIFO, O_WRONLY)) >= 0
+ && write(fd, &req, sizeof(req)) == sizeof(req)) {
+ close(fd);
+ return;
+ }
+ /* Fall through to the old method.. */
+#endif
+
+ /* Create an info file for init. */
+ unlink(PWRSTAT);
+ if ((fd = open(PWRSTAT, O_CREAT|O_WRONLY, 0644)) >= 0) {
+ if (ok)
+ write(fd, "OK\n", 3);
+ else
+ write(fd, "FAIL\n", 5);
+ close(fd);
+ }
+ kill(1, SIGPWR);
+}
+
+/* Main program. */
+int main(int argc, char **argv)
+{
+ int fd;
+ int dtr_bit = TIOCM_DTR;
+ int flags;
+ int status, oldstat = -1;
+ int count = 0;
+ int tries = 0;
+
+ if (argc < 2) {
+ fprintf(stderr, "Usage: powerd <device>\n");
+ exit(1);
+ }
+
+ /* Start syslog. */
+ openlog("powerd", LOG_CONS|LOG_PERROR, LOG_DAEMON);
+
+ /* Open monitor device. */
+ if ((fd = open(argv[1], O_RDWR | O_NDELAY)) < 0) {
+ syslog(LOG_ERR, "%s: %s", argv[1], sys_errlist[errno]);
+ closelog();
+ exit(1);
+ }
+
+ /* Line is opened, so DTR is high. Force it anyway to be sure. */
+ ioctl(fd, TIOCMBIS, &dtr_bit);
+
+ /* Daemonize. */
+ switch(fork()) {
+ case 0: /* Child */
+ closelog();
+ setsid();
+ break;
+ case -1: /* Error */
+ syslog(LOG_ERR, "can't fork.");
+ closelog();
+ exit(1);
+ default: /* Parent */
+ closelog();
+ exit(0);
+ }
+
+ /* Restart syslog. */
+ openlog("powerd", LOG_CONS, LOG_DAEMON);
+
+ /* Now sample the DCD line. */
+ while(1) {
+ /* Get the status. */
+ ioctl(fd, TIOCMGET, &flags);
+
+ /* Check the connection: DSR should be high. */
+ tries = 0;
+ while((flags & TIOCM_DSR) == 0) {
+ /* Keep on trying, and warn every two minutes. */
+ if ((tries % 60) == 0)
+ syslog(LOG_ALERT, "UPS connection error");
+ sleep(2);
+ tries++;
+ ioctl(fd, TIOCMGET, &flags);
+ }
+ if (tries > 0)
+ syslog(LOG_ALERT, "UPS connection OK");
+
+ /* Calculate present status. */
+ status = (flags & TIOCM_CAR);
+
+ /* Did DCD drop to zero? Then the power has failed. */
+ if (oldstat != 0 && status == 0) {
+ count++;
+ if (count > 3)
+ powerfail(0);
+ else {
+ sleep(1);
+ continue;
+ }
+ }
+ /* Did DCD come up again? Then the power is back. */
+ if (oldstat == 0 && status > 0) {
+ count++;
+ if (count > 3)
+ powerfail(1);
+ else {
+ sleep(1);
+ continue;
+ }
+ }
+ /* Reset count, remember status and sleep 2 seconds. */
+ count = 0;
+ oldstat = status;
+ sleep(2);
+ }
+ /* Never happens */
+ return(0);
+}
--- /dev/null
+# Example configuration for power daemon.
+# NOTE: this is not implemented yet, just a design.
+#
+# @(#) powerd.cfg 1.01 01-Oct-1994 MvS
+#
+
+# This is the setup section. It sets up the default line
+# signals that your UPS likes to see.
+[ setup ]
+dtr = 1
+rts = 1
+baud = 2400
+send "AAAA"
+
+# Now: how to tell UPS to turn off the power.
+[ powerdown ]
+dtr = 0
+send "BYE"
+
+# How to monitor the UPS, or a remote UPS.
+# Possible line signals: dcd cts dsr ring
+#
+# Comment out the parts you don't want.
+#
+# All of this (1, 2, 3) can be combined.
+[ monitor ]
+
+# First, do we want to broadcast the UPS status
+# on ethernet when something happens?
+# Comment out to disable.
+# Syntax: address, portnumber
+# address: broadcast adress on ethernet
+# portnumber: unused priviliged port (under 1024)
+broadcast = 10.0.33.255,15
+
+# monitor type 1. This tells powerd to monitor line signals.
+ok = dcd
+fail = !dcd
+lowbat = rts
+
+# Monitor type 2. Tell powerd to look for data.
+ok = "OK"
+fail = "!"
+
+# Monitor type 3. Listen to the ethernet.
+#
+# Warn_host is the hostname of the system with the UPS
+# This is for security, so that someone on a DOS box
+# can't spoof the powerd broadcast. The number after it
+# is the portnumber to listen to (see above: broadcast).
+#
+# Note: if the broadcast address set above is enabled
+# and we receive a message from a remote powerd, we check
+# the received broadcast address. If this is the same
+# as from the broadcast we just received,
+# it will not be repeated (guess why).
+remote = warn_host,15
+
--- /dev/null
+/*
+ * utmpdump Simple program to dump UTMP and WTMP files in
+ * raw format, so they can be examined.
+ *
+ * Version: @(#)utmpdump.c 13-Aug-1996 1.00 miquels@cistron.nl
+ *
+ * This file is part of the sysvinit suite,
+ * Copyright (C) 1991-1996 Miquel van Smoorenburg.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <stdio.h>
+#include <utmp.h>
+#include <time.h>
+
+void dump(fp)
+FILE *fp;
+{
+ struct utmp ut;
+ int f;
+ time_t tm;
+
+ while (fread(&ut, sizeof(struct utmp), 1, fp) == 1) {
+ for(f = 0; f < 12; f++) if (ut.ut_line[f] == ' ') ut.ut_line[f] = '_';
+ for(f = 0; f < 8; f++) if (ut.ut_name[f] == ' ') ut.ut_name[f] = '_';
+ tm = ut.ut_time;
+ printf("[%d] [%05d] [%-4.4s] [%-8.8s] [%-12.12s] [%-15.15s]\n",
+ ut.ut_type, ut.ut_pid, ut.ut_id, ut.ut_user,
+ ut.ut_line, 4 + ctime(&tm));
+ }
+}
+
+int main(argc, argv)
+int argc;
+char **argv;
+{
+ int f;
+ FILE *fp;
+
+ if (argc < 2) {
+ argc = 2;
+ argv[1] = UTMP_FILE;
+ }
+
+ for(f = 1; f < argc; f++) {
+ if (strcmp(argv[f], "-") == 0) {
+ printf("Utmp dump of stdin\n");
+ dump(stdin);
+ } else if ((fp = fopen(argv[f], "r")) != NULL) {
+ printf("Utmp dump of %s\n", argv[f]);
+ dump(fp);
+ fclose(fp);
+ } else
+ perror(argv[f]);
+ }
+ return(0);
+}
--- /dev/null
+#
+# Makefile Makefile for the systemV init suite.
+# Targets: all compiles everything
+# install installs the binaries (not the scripts)
+# clean cleans up object files
+# clobber really cleans up
+#
+# Version: @(#)Makefile 2.85-13 23-Mar-2004 miquels@cistron.nl
+#
+
+CC = gcc
+CFLAGS = -ansi -W -Wall -O2 -fomit-frame-pointer -D_GNU_SOURCE
+LDFLAGS = -s
+STATIC =
+
+# For some known distributions we do not build all programs, otherwise we do.
+BIN =
+SBIN = init halt shutdown runlevel killall5
+USRBIN = last mesg
+
+MAN1 = last.1 lastb.1 mesg.1
+MAN5 = initscript.5 inittab.5
+MAN8 = halt.8 init.8 killall5.8 pidof.8 poweroff.8 reboot.8 runlevel.8
+MAN8 += shutdown.8 telinit.8
+
+ifeq ($(DISTRO),)
+BIN += mountpoint
+SBIN += sulogin bootlogd
+USRBIN += utmpdump wall
+MAN1 += mountpoint.1 wall.1
+MAN8 += sulogin.8 bootlogd.8
+endif
+
+ifeq ($(DISTRO),Debian)
+BIN += mountpoint
+SBIN += sulogin bootlogd
+MAN1 += mountpoint.1
+MAN8 += sulogin.8 bootlogd.8
+endif
+
+ifeq ($(DISTRO),Owl)
+USRBIN += wall
+MAN1 += wall.1
+endif
+
+BIN_OWNER = root
+BIN_GROUP = root
+BIN_COMBO = $(BIN_OWNER):$(BIN_GROUP)
+STRIP = strip -s -R .comment
+INSTALL_EXEC = install -o $(BIN_OWNER) -g $(BIN_GROUP) -m 755
+INSTALL_DATA = install -o $(BIN_OWNER) -g $(BIN_GROUP) -m 644
+MANDIR = /usr/share/man
+
+ifeq ($(WITH_SELINUX),yes)
+ SELINUX_DEF=-DWITH_SELINUX
+ INIT_SELIBS=-lsepol -lselinux
+ SULOGIN_SELIBS=-lselinux
+else
+ SELINUX_DEF=
+ INIT_SELIBS=
+ SULOGIN_SELIBS=
+endif
+
+
+
+# Additional libs for GNU libc.
+ifneq ($(wildcard /usr/lib/libcrypt.a),)
+LCRYPT = -lcrypt
+endif
+
+all: $(BIN) $(SBIN) $(USRBIN)
+
+init: init.o init_utmp.o
+ $(CC) $(LDFLAGS) $(STATIC) -o $@ init.o init_utmp.o $(INIT_SELIBS)
+
+halt: halt.o ifdown.o hddown.o utmp.o reboot.h
+ $(CC) $(LDFLAGS) -o $@ halt.o ifdown.o hddown.o utmp.o
+
+last: last.o oldutmp.h
+ $(CC) $(LDFLAGS) -o $@ last.o
+
+mesg: mesg.o
+ $(CC) $(LDFLAGS) -o $@ mesg.o
+
+mountpoint: mountpoint.o
+ $(CC) $(LDFLAGS) -o $@ mountpoint.o
+
+utmpdump: utmpdump.o
+ $(CC) $(LDFLAGS) -o $@ utmpdump.o
+
+runlevel: runlevel.o
+ $(CC) $(LDFLAGS) -o $@ runlevel.o
+
+sulogin: sulogin.o
+ $(CC) $(LDFLAGS) $(STATIC) $(SELINUX_DEF) -o $@ $^ $(LCRYPT) $(SULOGIN_SELIBS)
+
+wall: dowall.o wall.o
+ $(CC) $(LDFLAGS) -o $@ dowall.o wall.o
+
+shutdown: dowall.o shutdown.o utmp.o reboot.h
+ $(CC) $(LDFLAGS) -o $@ dowall.o shutdown.o utmp.o
+
+bootlogd: bootlogd.o
+ $(CC) $(LDFLAGS) -o $@ bootlogd.o -lutil
+
+sulogin.o: sulogin.c
+ $(CC) -c $(CFLAGS) $(SELINUX_DEF) sulogin.c
+
+init.o: init.c init.h set.h reboot.h initreq.h
+ $(CC) -c $(CFLAGS) $(SELINUX_DEF) init.c
+
+utmp.o: utmp.c init.h
+ $(CC) -c $(CFLAGS) utmp.c
+
+init_utmp.o: utmp.c init.h
+ $(CC) -c $(CFLAGS) -DINIT_MAIN utmp.c -o init_utmp.o
+
+cleanobjs:
+ rm -f *.o *.bak
+
+clean: cleanobjs
+ @echo Type \"make clobber\" to really clean up.
+
+clobber: cleanobjs
+ rm -f $(BIN) $(SBIN) $(USRBIN)
+
+distclean: clobber
+
+install:
+ for i in $(BIN); do \
+ $(STRIP) $$i ; \
+ $(INSTALL_EXEC) $$i $(ROOT)/bin/ ; \
+ done
+ for i in $(SBIN); do \
+ $(STRIP) $$i ; \
+ $(INSTALL_EXEC) $$i $(ROOT)/sbin/ ; \
+ done
+ for i in $(USRBIN); do \
+ $(STRIP) $$i ; \
+ $(INSTALL_EXEC) $$i $(ROOT)/usr/bin/ ; \
+ done
+ # $(INSTALL_EXEC) etc/initscript.sample $(ROOT)/etc/
+ ln -sf halt $(ROOT)/sbin/reboot
+ ln -sf halt $(ROOT)/sbin/poweroff
+ ln -sf init $(ROOT)/sbin/telinit
+ ln -sf /sbin/killall5 $(ROOT)/bin/pidof
+ if [ ! -f $(ROOT)/usr/bin/lastb ]; then \
+ ln -sf last $(ROOT)/usr/bin/lastb; \
+ fi
+ $(INSTALL_DATA) initreq.h $(ROOT)/usr/include/
+ for i in $(MAN1); do \
+ $(INSTALL_DATA) ../man/$$i $(ROOT)$(MANDIR)/man1/; \
+ done
+ for i in $(MAN5); do \
+ $(INSTALL_DATA) ../man/$$i $(ROOT)$(MANDIR)/man5/; \
+ done
+ for i in $(MAN8); do \
+ $(INSTALL_DATA) ../man/$$i $(ROOT)$(MANDIR)/man8/; \
+ done
+ifeq ($(ROOT),)
+ #
+ # This part is skipped on Debian systems, the
+ # debian.preinst script takes care of it.
+ @if [ ! -p /dev/initctl ]; then \
+ echo "Creating /dev/initctl"; \
+ rm -f /dev/initctl; \
+ mknod -m 600 /dev/initctl p; fi
+endif
--- /dev/null
+/*
+ * bootlogd.c Store output from the console during bootup into a file.
+ * The file is usually located on the /var partition, and
+ * gets written (and fsynced) as soon as possible.
+ *
+ * Version: @(#)bootlogd 2.86pre 12-Jan-2004 miquels@cistron.nl
+ *
+ * Bugs: Uses openpty(), only available in glibc. Sorry.
+ *
+ * This file is part of the sysvinit suite,
+ * Copyright (C) 1991-2004 Miquel van Smoorenburg.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * *NOTE* *NOTE* *NOTE*
+ * This is a PROOF OF CONCEPT IMPLEMENTATION
+ *
+ * I have bigger plans for Debian, but for now
+ * this has to do ;)
+ *
+ */
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <sys/utsname.h>
+#include <time.h>
+#include <stdio.h>
+#include <errno.h>
+#include <malloc.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <signal.h>
+#include <getopt.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <pty.h>
+#include <ctype.h>
+#ifdef __linux__
+#include <sys/mount.h>
+#endif
+
+char *Version = "@(#) bootlogd 2.86 03-Jun-2004 miquels@cistron.nl";
+
+#define LOGFILE "/var/log/boot"
+
+char ringbuf[32768];
+char *endptr = ringbuf + sizeof(ringbuf);
+char *inptr = ringbuf;
+char *outptr = ringbuf;
+
+int got_signal = 0;
+int didnl = 1;
+int createlogfile = 0;
+int syncalot = 0;
+
+struct line {
+ char buf[256];
+ int pos;
+} line;
+
+/*
+ * Console devices as listed on the kernel command line and
+ * the mapping to actual devices in /dev
+ */
+struct consdev {
+ char *cmdline;
+ char *dev1;
+ char *dev2;
+} consdev[] = {
+ { "ttyB", "/dev/ttyB%s", NULL },
+ { "ttySC", "/dev/ttySC%s", "/dev/ttsc/%s" },
+ { "ttyS", "/dev/ttyS%s", "/dev/tts/%s" },
+ { "tty", "/dev/tty%s", "/dev/vc/%s" },
+ { "hvc", "/dev/hvc%s", "/dev/hvc/%s" },
+ { NULL, NULL, NULL },
+};
+
+/*
+ * Devices to try as console if not found on kernel command line.
+ * Tried from left to right (as opposed to kernel cmdline).
+ */
+char *defcons[] = { "tty0", "hvc0", "ttyS0", "ttySC0", "ttyB0", NULL };
+
+/*
+ * Catch signals.
+ */
+void handler(int sig)
+{
+ got_signal = sig;
+}
+
+
+/*
+ * Scan /dev and find the device name.
+ * Side-effect: directory is changed to /dev
+ *
+ * FIXME: scan subdirectories for devfs support ?
+ */
+int findtty(char *res, int rlen, dev_t dev)
+{
+ DIR *dir;
+ struct dirent *ent;
+ struct stat st;
+ int r = 0;
+
+ if (chdir("/dev") < 0 || (dir = opendir(".")) == NULL) {
+ perror("bootlogd: /dev");
+ return -1;
+ }
+ while ((ent = readdir(dir)) != NULL) {
+ if (lstat(ent->d_name, &st) != 0)
+ continue;
+ if (!S_ISCHR(st.st_mode))
+ continue;
+ if (st.st_rdev == dev) {
+ break;
+ }
+ }
+ if (ent == NULL) {
+ fprintf(stderr, "bootlogd: cannot find console device "
+ "%d:%d in /dev\n", major(dev), minor(dev));
+ r = -1;
+ } else if (strlen(ent->d_name) + 5 >= rlen) {
+ fprintf(stderr, "bootlogd: console device name too long\n");
+ r = -1;
+ } else
+ snprintf(res, rlen, "/dev/%s", ent->d_name);
+ closedir(dir);
+
+ return r;
+}
+
+/*
+ * For some reason, openpty() in glibc sometimes doesn't
+ * work at boot-time. It must be a bug with old-style pty
+ * names, as new-style (/dev/pts) is not available at that
+ * point. So, we find a pty/tty pair ourself if openpty()
+ * fails for whatever reason.
+ */
+int findpty(int *master, int *slave, char *name)
+{
+ char pty[16];
+ char tty[16];
+ int i, j;
+ int found;
+
+ if (openpty(master, slave, name, NULL, NULL) >= 0)
+ return 0;
+
+ found = 0;
+
+ for (i = 'p'; i <= 'z'; i++) {
+ for (j = '0'; j <= 'f'; j++) {
+ if (j == '9' + 1) j = 'a';
+ sprintf(pty, "/dev/pty%c%c", i, j);
+ sprintf(tty, "/dev/tty%c%c", i, j);
+ if ((*master = open(pty, O_RDWR|O_NOCTTY)) >= 0) {
+ *slave = open(tty, O_RDWR|O_NOCTTY);
+ if (*slave >= 0) {
+ found = 1;
+ break;
+ }
+ }
+ }
+ if (found) break;
+ }
+ if (found < 0) return -1;
+
+ if (name) strcpy(name, tty);
+
+ return 0;
+}
+/*
+ * See if a console taken from the kernel command line maps
+ * to a character device we know about, and if we can open it.
+ */
+int isconsole(char *s, char *res, int rlen)
+{
+ struct consdev *c;
+ int l, sl, i, fd;
+ char *p, *q;
+
+ sl = strlen(s);
+
+ for (c = consdev; c->cmdline; c++) {
+ l = strlen(c->cmdline);
+ if (sl <= l) continue;
+ p = s + l;
+ if (strncmp(s, c->cmdline, l) != 0 || !isdigit(*p))
+ continue;
+ for (i = 0; i < 2; i++) {
+ snprintf(res, rlen, i ? c->dev1 : c->dev2, p);
+ if ((q = strchr(res, ',')) != NULL) *q = 0;
+ if ((fd = open(res, O_RDONLY|O_NONBLOCK)) >= 0) {
+ close(fd);
+ return 1;
+ }
+ }
+ }
+ return 0;
+}
+
+/*
+ * Find out the _real_ console. Assume that stdin is connected to
+ * the console device (/dev/console).
+ */
+int consolename(char *res, int rlen)
+{
+#ifdef TIOCGDEV
+ unsigned int kdev;
+#endif
+ struct stat st, st2;
+ char buf[256];
+ char *p;
+ int didmount = 0;
+ int n, r;
+ int fd;
+
+ fstat(0, &st);
+ if (major(st.st_rdev) != 5 || minor(st.st_rdev) != 1) {
+ /*
+ * Old kernel, can find real device easily.
+ */
+ return findtty(res, rlen, st.st_rdev);
+ }
+
+#ifdef TIOCGDEV
+ if (ioctl(0, TIOCGDEV, &kdev) == 0)
+ return findtty(res, rlen, (dev_t)kdev);
+ if (errno != ENOIOCTLCMD) return -1;
+#endif
+
+#ifdef __linux__
+ /*
+ * Read /proc/cmdline.
+ */
+ stat("/", &st);
+ if (stat("/proc", &st2) < 0) {
+ perror("bootlogd: /proc");
+ return -1;
+ }
+ if (st.st_dev == st2.st_dev) {
+ if (mount("proc", "/proc", "proc", 0, NULL) < 0) {
+ perror("bootlogd: mount /proc");
+ return -1;
+ }
+ didmount = 1;
+ }
+
+ n = 0;
+ r = -1;
+ if ((fd = open("/proc/cmdline", O_RDONLY)) < 0) {
+ perror("bootlogd: /proc/cmdline");
+ } else {
+ buf[0] = 0;
+ if ((n = read(fd, buf, sizeof(buf) - 1)) >= 0)
+ r = 0;
+ else
+ perror("bootlogd: /proc/cmdline");
+ close(fd);
+ }
+ if (didmount) umount("/proc");
+
+ if (r < 0) return r;
+
+ /*
+ * OK, so find console= in /proc/cmdline.
+ * Parse in reverse, opening as we go.
+ */
+ p = buf + n;
+ *p-- = 0;
+ r = -1;
+ while (p >= buf) {
+ if (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') {
+ *p-- = 0;
+ continue;
+ }
+ if (strncmp(p, "console=", 8) == 0 &&
+ isconsole(p + 8, res, rlen)) {
+ r = 0;
+ break;
+ }
+ p--;
+ }
+
+ if (r == 0) return r;
+#endif
+
+ /*
+ * Okay, no console on the command line -
+ * guess the default console.
+ */
+ for (n = 0; defcons[n]; n++)
+ if (isconsole(defcons[n], res, rlen))
+ return 0;
+
+ fprintf(stderr, "bootlogd: cannot deduce real console device\n");
+
+ return -1;
+}
+
+
+/*
+ * Write data and make sure it's on disk.
+ */
+void writelog(FILE *fp, unsigned char *ptr, int len)
+{
+ time_t t;
+ char *s;
+ char tmp[8];
+ int olen = len;
+ int dosync = 0;
+ int tlen;
+
+ while (len > 0) {
+ tmp[0] = 0;
+ if (didnl) {
+ time(&t);
+ s = ctime(&t);
+ fprintf(fp, "%.24s: ", s);
+ didnl = 0;
+ }
+ switch (*ptr) {
+ case 27: /* ESC */
+ strcpy(tmp, "^[");
+ break;
+ case '\r':
+ line.pos = 0;
+ break;
+ case 8: /* ^H */
+ if (line.pos > 0) line.pos--;
+ break;
+ case '\n':
+ didnl = 1;
+ dosync = syncalot;
+ break;
+ case '\t':
+ line.pos += (line.pos / 8 + 1) * 8;
+ if (line.pos >= sizeof(line.buf))
+ line.pos = sizeof(line.buf) - 1;
+ break;
+ case 32 ... 127:
+ case 161 ... 255:
+ tmp[0] = *ptr;
+ tmp[1] = 0;
+ break;
+ default:
+ sprintf(tmp, "\\%03o", *ptr);
+ break;
+ }
+ ptr++;
+ len--;
+
+ tlen = strlen(tmp);
+ if (tlen && (line.pos + tlen < sizeof(line.buf))) {
+ memcpy(line.buf + line.pos, tmp, tlen);
+ line.pos += tlen;
+ }
+ if (didnl) {
+ fprintf(fp, "%s\n", line.buf);
+ memset(&line, 0, sizeof(line));
+ }
+ }
+
+ if (dosync) {
+ fflush(fp);
+ fdatasync(fileno(fp));
+ }
+
+ outptr += olen;
+ if (outptr >= endptr)
+ outptr = ringbuf;
+
+}
+
+
+/*
+ * Print usage message and exit.
+ */
+void usage(void)
+{
+ fprintf(stderr, "Usage: bootlogd [-v] [-r] [-d] [-s] [-c] [-p pidfile] [-l logfile]\n");
+ exit(1);
+}
+
+int open_nb(char *buf)
+{
+ int fd, n;
+
+ if ((fd = open(buf, O_WRONLY|O_NONBLOCK|O_NOCTTY)) < 0)
+ return -1;
+ n = fcntl(fd, F_GETFL);
+ n &= ~(O_NONBLOCK);
+ fcntl(fd, F_SETFL, n);
+
+ return fd;
+}
+
+/*
+ * We got a write error on the real console. If its an EIO,
+ * somebody hung up our filedescriptor, so try to re-open it.
+ */
+int write_err(int pts, int realfd, char *realcons, int e)
+{
+ int fd;
+
+ if (e != EIO) {
+werr:
+ close(pts);
+ fprintf(stderr, "bootlogd: writing to console: %s\n",
+ strerror(e));
+ return -1;
+ }
+ close(realfd);
+ if ((fd = open_nb(realcons)) < 0)
+ goto werr;
+
+ return fd;
+}
+
+int main(int argc, char **argv)
+{
+ FILE *fp;
+ struct timeval tv;
+ fd_set fds;
+ char buf[1024];
+ char realcons[1024];
+ char *p;
+ char *logfile;
+ char *pidfile;
+ int rotate;
+ int dontfork;
+ int ptm, pts;
+ int realfd;
+ int n, m, i;
+ int todo;
+
+ fp = NULL;
+ logfile = LOGFILE;
+ pidfile = NULL;
+ rotate = 0;
+ dontfork = 0;
+
+ while ((i = getopt(argc, argv, "cdsl:p:rv")) != EOF) switch(i) {
+ case 'l':
+ logfile = optarg;
+ break;
+ case 'r':
+ rotate = 1;
+ break;
+ case 'v':
+ printf("%s\n", Version);
+ exit(0);
+ break;
+ case 'p':
+ pidfile = optarg;
+ break;
+ case 'c':
+ createlogfile = 1;
+ break;
+ case 'd':
+ dontfork = 1;
+ break;
+ case 's':
+ syncalot = 1;
+ break;
+ default:
+ usage();
+ break;
+ }
+ if (optind < argc) usage();
+
+ signal(SIGTERM, handler);
+ signal(SIGQUIT, handler);
+ signal(SIGINT, handler);
+ signal(SIGTTIN, SIG_IGN);
+ signal(SIGTTOU, SIG_IGN);
+ signal(SIGTSTP, SIG_IGN);
+
+ /*
+ * Open console device directly.
+ */
+ if (consolename(realcons, sizeof(realcons)) < 0)
+ return 1;
+
+ if (strcmp(realcons, "/dev/tty0") == 0)
+ strcpy(realcons, "/dev/tty1");
+ if (strcmp(realcons, "/dev/vc/0") == 0)
+ strcpy(realcons, "/dev/vc/1");
+
+ if ((realfd = open_nb(realcons)) < 0) {
+ fprintf(stderr, "bootlogd: %s: %s\n", buf, strerror(errno));
+ return 1;
+ }
+
+ /*
+ * Grab a pty, and redirect console messages to it.
+ */
+ ptm = -1;
+ pts = -1;
+ buf[0] = 0;
+ if (findpty(&ptm, &pts, buf) < 0) {
+ fprintf(stderr,
+ "bootlogd: cannot allocate pseudo tty: %s\n",
+ strerror(errno));
+ return 1;
+ }
+
+ (void)ioctl(0, TIOCCONS, NULL);
+#if 1
+ /* Work around bug in 2.1/2.2 kernels. Fixed in 2.2.13 and 2.3.18 */
+ if ((n = open("/dev/tty0", O_RDWR)) >= 0) {
+ (void)ioctl(n, TIOCCONS, NULL);
+ close(n);
+ }
+#endif
+ if (ioctl(pts, TIOCCONS, NULL) < 0) {
+ fprintf(stderr, "bootlogd: ioctl(%s, TIOCCONS): %s\n",
+ buf, strerror(errno));
+ return 1;
+ }
+
+ /*
+ * Fork and write pidfile if needed.
+ */
+ if (!dontfork) {
+ pid_t child_pid = fork();
+ switch (child_pid) {
+ case -1: /* I am parent and the attempt to create a child failed */
+ fprintf(stderr, "bootlogd: fork failed: %s\n",
+ strerror(errno));
+ exit(1);
+ break;
+ case 0: /* I am the child */
+ break;
+ default: /* I am parent and got child's pid */
+ exit(0);
+ break;
+ }
+ setsid();
+ }
+ if (pidfile) {
+ unlink(pidfile);
+ if ((fp = fopen(pidfile, "w")) != NULL) {
+ fprintf(fp, "%d\n", (int)getpid());
+ fclose(fp);
+ }
+ fp = NULL;
+ }
+
+ /*
+ * Read the console messages from the pty, and write
+ * to the real console and the logfile.
+ */
+ while (!got_signal) {
+
+ /*
+ * We timeout after 5 seconds if we still need to
+ * open the logfile. There might be buffered messages
+ * we want to write.
+ */
+ tv.tv_sec = 0;
+ tv.tv_usec = 500000;
+ FD_ZERO(&fds);
+ FD_SET(ptm, &fds);
+ if (select(ptm + 1, &fds, NULL, NULL, &tv) == 1) {
+ /*
+ * See how much space there is left, read.
+ */
+ if ((n = read(ptm, inptr, endptr - inptr)) >= 0) {
+ /*
+ * Write data (in chunks if needed)
+ * to the real output device.
+ */
+ m = n;
+ p = inptr;
+ while (m > 0) {
+ i = write(realfd, p, m);
+ if (i >= 0) {
+ m -= i;
+ p += i;
+ continue;
+ }
+ /*
+ * Handle EIO (somebody hung
+ * up our filedescriptor)
+ */
+ realfd = write_err(pts, realfd,
+ realcons, errno);
+ if (realfd >= 0) continue;
+ got_signal = 1; /* Not really */
+ break;
+ }
+
+ /*
+ * Increment buffer position. Handle
+ * wraps, and also drag output pointer
+ * along if we cross it.
+ */
+ inptr += n;
+ if (inptr - n < outptr && inptr > outptr)
+ outptr = inptr;
+ if (inptr >= endptr)
+ inptr = ringbuf;
+ if (outptr >= endptr)
+ outptr = ringbuf;
+ }
+ }
+
+ /*
+ * Perhaps we need to open the logfile.
+ */
+ if (fp == NULL && access(logfile, F_OK) == 0) {
+ if (rotate) {
+ snprintf(buf, sizeof(buf), "%s~", logfile);
+ rename(logfile, buf);
+ }
+ fp = fopen(logfile, "a");
+ }
+ if (fp == NULL && createlogfile)
+ fp = fopen(logfile, "a");
+
+ if (inptr >= outptr)
+ todo = inptr - outptr;
+ else
+ todo = endptr - outptr;
+ if (fp && todo)
+ writelog(fp, outptr, todo);
+ }
+
+ if (fp) {
+ if (!didnl) fputc('\n', fp);
+ fclose(fp);
+ }
+
+ close(pts);
+ close(ptm);
+ close(realfd);
+
+ return 0;
+}
+
--- /dev/null
+/*
+ * dowall.c Write to all users on the system.
+ *
+ * Author: Miquel van Smoorenburg, miquels@cistron.nl
+ *
+ * Version: @(#)dowall.c 2.85-5 02-Jul-2003 miquels@cistron.nl
+ *
+ * This file is part of the sysvinit suite,
+ * Copyright (C) 1991-2003 Miquel van Smoorenburg.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/sysmacros.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <utmp.h>
+#include <pwd.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <setjmp.h>
+
+static sigjmp_buf jbuf;
+
+/*
+ * Alarm handler
+ */
+/*ARGSUSED*/
+static void handler(int arg)
+{
+ siglongjmp(jbuf, 1);
+}
+
+
+/*
+ * Print a text, escape all characters not in Latin-1.
+ */
+static void feputs(char *line, FILE *fp)
+{
+ unsigned char *p;
+
+ for (p = (unsigned char *)line; *p; p++) {
+ if (strchr("\t\r\n", *p) ||
+ (*p >= 32 && *p <= 127) || (*p >= 160)) {
+ fputc(*p, fp);
+ } else {
+ fprintf(fp, "^%c", (*p & 0x1f) + 'A' - 1);
+ }
+ }
+ fflush(fp);
+}
+
+
+static void getuidtty(char **userp, char **ttyp)
+{
+ struct passwd *pwd;
+ uid_t uid;
+ char *tty;
+ static char uidbuf[32];
+ static char ttynm[UT_LINESIZE + 4];
+ static int init = 0;
+
+ if (!init) {
+
+ uid = getuid();
+ if ((pwd = getpwuid(uid)) != NULL) {
+ uidbuf[0] = 0;
+ strncat(uidbuf, pwd->pw_name, sizeof(uidbuf) - 1);
+ } else {
+ sprintf(uidbuf, uid ? "uid %d" : "root", (int)uid);
+ }
+
+ if ((tty = ttyname(0)) != NULL) {
+ if (strncmp(tty, "/dev/", 5) == 0)
+ tty += 5;
+ sprintf(ttynm, "(%.28s) ", tty);
+ } else
+ ttynm[0] = 0;
+ init++;
+ }
+
+ *userp = uidbuf;
+ *ttyp = ttynm;
+}
+
+/*
+ * Check whether given filename looks like tty device.
+ */
+static int file_isatty(const char *fname)
+{
+ struct stat st;
+ int major;
+
+ if (stat(fname, &st) < 0)
+ return 0;
+
+ if (st.st_nlink != 1 || !S_ISCHR(st.st_mode))
+ return 0;
+
+ /*
+ * It would be an impossible task to list all major/minors
+ * of tty devices here, so we just exclude the obvious
+ * majors of which just opening has side-effects:
+ * printers and tapes.
+ */
+ major = major(st.st_dev);
+ if (major == 1 || major == 2 || major == 6 || major == 9 ||
+ major == 12 || major == 16 || major == 21 || major == 27 ||
+ major == 37 || major == 96 || major == 97 || major == 206 ||
+ major == 230) return 0;
+
+ return 1;
+}
+
+/*
+ * Wall function.
+ */
+void wall(char *text, int fromshutdown, int remote)
+{
+ FILE *tp;
+ struct sigaction sa;
+ struct utmp *utmp;
+ time_t t;
+ char term[UT_LINESIZE+6];
+ char line[81];
+ char hostname[256]; /* HOST_NAME_MAX+1 */
+ char *date, *p;
+ char *user, *tty;
+ int fd, flags;
+
+ /*
+ * Make sure tp and fd aren't in a register. Some versions
+ * of gcc clobber those after longjmp (or so I understand).
+ */
+ (void) &tp;
+ (void) &fd;
+
+ getuidtty(&user, &tty);
+
+ /* Get and report current hostname, to make it easier to find
+ out which machine is being shut down. */
+ if (0 != gethostname(hostname, sizeof(hostname))) {
+ strncpy(hostname, "[unknown]", sizeof(hostname)-1);
+ }
+ /* If hostname is truncated, it is unspecified if the string
+ is null terminated or not. Make sure we know it is null
+ terminated. */
+ hostname[sizeof(hostname)-1] = 0;
+
+ /* Get the time */
+ time(&t);
+ date = ctime(&t);
+ for(p = date; *p && *p != '\n'; p++)
+ ;
+ *p = 0;
+
+ if (remote) {
+ snprintf(line, sizeof(line),
+ "\007\r\nRemote broadcast message (%s):\r\n\r\n",
+ date);
+ } else {
+ snprintf(line, sizeof(line),
+ "\007\r\nBroadcast message from %s@%s %s(%s):\r\n\r\n",
+ user, hostname, tty, date);
+ }
+
+ /*
+ * Fork to avoid us hanging in a write()
+ */
+ if (fork() != 0)
+ return;
+
+ memset(&sa, 0, sizeof(sa));
+ sa.sa_handler = handler;
+ sa.sa_flags = 0;
+ sigemptyset(&sa.sa_mask);
+ sigaction(SIGALRM, &sa, NULL);
+
+ setutent();
+
+ while ((utmp = getutent()) != NULL) {
+ if(utmp->ut_type != USER_PROCESS ||
+ utmp->ut_user[0] == 0) continue;
+ if (strncmp(utmp->ut_line, "/dev/", 5) == 0) {
+ term[0] = 0;
+ strncat(term, utmp->ut_line, sizeof(term)-1);
+ } else
+ snprintf(term, sizeof(term), "/dev/%.*s",
+ UT_LINESIZE, utmp->ut_line);
+ if (strstr(term, "/../")) continue;
+
+ fd = -1;
+ tp = NULL;
+
+ /*
+ * Open it non-delay
+ */
+ if (sigsetjmp(jbuf, 1) == 0) {
+ alarm(2);
+ flags = O_WRONLY|O_NDELAY|O_NOCTTY;
+ if (file_isatty(term) &&
+ (fd = open(term, flags)) >= 0) {
+ if (isatty(fd) &&
+ (tp = fdopen(fd, "w")) != NULL) {
+ fputs(line, tp);
+ feputs(text, tp);
+ fflush(tp);
+ }
+ }
+ }
+ alarm(0);
+ if (fd >= 0) close(fd);
+ if (tp != NULL) fclose(tp);
+ }
+ endutent();
+
+ exit(0);
+}
+
--- /dev/null
+/*
+ * Halt Stop the system running.
+ * It re-enables CTRL-ALT-DEL, so that a hard reboot can
+ * be done. If called as reboot, it will reboot the system.
+ *
+ * If the system is not in runlevel 0 or 6, halt will just
+ * execute a "shutdown -h" to halt the system, and reboot will
+ * execute an "shutdown -r". This is for compatibility with
+ * sysvinit 2.4.
+ *
+ * Usage: halt [-n] [-w] [-d] [-f] [-h] [-i] [-p]
+ * -n: don't sync before halting the system
+ * -w: only write a wtmp reboot record and exit.
+ * -d: don't write a wtmp record.
+ * -f: force halt/reboot, don't call shutdown.
+ * -h: put harddisks in standby mode
+ * -i: shut down all network interfaces.
+ * -p: power down the system (if possible, otherwise halt).
+ *
+ * Reboot and halt are both this program. Reboot
+ * is just a link to halt. Invoking the program
+ * as poweroff implies the -p option.
+ *
+ * Author: Miquel van Smoorenburg, miquels@cistron.nl
+ *
+ * Version: 2.86, 30-Jul-2004
+ *
+ * This file is part of the sysvinit suite,
+ * Copyright (C) 1991-2004 Miquel van Smoorenburg.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/param.h>
+#include <stdlib.h>
+#include <utmp.h>
+#include <fcntl.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/times.h>
+#include <time.h>
+#include <signal.h>
+#include <stdio.h>
+#include <getopt.h>
+#include "reboot.h"
+
+char *Version = "@(#)halt 2.86 31-Jul-2004 miquels@cistron.nl";
+char *progname;
+
+#define KERNEL_MONITOR 1 /* If halt() puts you into the kernel monitor. */
+#define RUNLVL_PICKY 0 /* Be picky about the runlevel */
+
+extern int ifdown(void);
+extern int hddown(void);
+extern void write_wtmp(char *user, char *id, int pid, int type, char *line);
+
+/*
+ * Send usage message.
+ */
+void usage(void)
+{
+ fprintf(stderr, "usage: %s [-n] [-w] [-d] [-f] [-h] [-i]%s\n",
+ progname, strcmp(progname, "halt") ? "" : " [-p]");
+ fprintf(stderr, "\t-n: don't sync before halting the system\n");
+ fprintf(stderr, "\t-w: only write a wtmp reboot record and exit.\n");
+ fprintf(stderr, "\t-d: don't write a wtmp record.\n");
+ fprintf(stderr, "\t-f: force halt/reboot, don't call shutdown.\n");
+ fprintf(stderr, "\t-h: put harddisks in standby mode.\n");
+ fprintf(stderr, "\t-i: shut down all network interfaces.\n");
+ if (!strcmp(progname, "halt"))
+ fprintf(stderr, "\t-p: power down the system (if possible, otherwise halt).\n");
+ exit(1);
+}
+
+/*
+ * See if we were started directly from init.
+ * Get the runlevel from /var/run/utmp or the environment.
+ */
+int get_runlevel(void)
+{
+ struct utmp *ut;
+ char *r;
+#if RUNLVL_PICKY
+ time_t boottime;
+#endif
+
+ /*
+ * First see if we were started directly from init.
+ */
+ if (getenv("INIT_VERSION") && (r = getenv("RUNLEVEL")) != NULL)
+ return *r;
+
+ /*
+ * Hmm, failed - read runlevel from /var/run/utmp..
+ */
+#if RUNLVL_PICKY
+ /*
+ * Get boottime from the kernel.
+ */
+ time(&boottime);
+ boottime -= (times(NULL) / HZ);
+#endif
+
+ /*
+ * Find runlevel in utmp.
+ */
+ setutent();
+ while ((ut = getutent()) != NULL) {
+#if RUNLVL_PICKY
+ /*
+ * Only accept value if it's from after boottime.
+ */
+ if (ut->ut_type == RUN_LVL && ut->ut_time > boottime)
+ return (ut->ut_pid & 255);
+#else
+ if (ut->ut_type == RUN_LVL)
+ return (ut->ut_pid & 255);
+#endif
+ }
+ endutent();
+
+ /* This should not happen but warn the user! */
+ fprintf(stderr, "WARNING: could not determine runlevel"
+ " - doing soft %s\n", progname);
+ fprintf(stderr, " (it's better to use shutdown instead of %s"
+ " from the command line)\n", progname);
+
+ return -1;
+}
+
+/*
+ * Switch to another runlevel.
+ */
+void do_shutdown(char *fl, char *tm)
+{
+ char *args[8];
+ int i = 0;
+
+ args[i++] = "shutdown";
+ args[i++] = fl;
+ if (tm) {
+ args[i++] = "-t";
+ args[i++] = tm;
+ }
+ args[i++] = "now";
+ args[i++] = NULL;
+
+ execv("/sbin/shutdown", args);
+ execv("/etc/shutdown", args);
+ execv("/bin/shutdown", args);
+
+ perror("shutdown");
+ exit(1);
+}
+
+/*
+ * Main program.
+ * Write a wtmp entry and reboot cq. halt.
+ */
+int main(int argc, char **argv)
+{
+ int do_reboot = 0;
+ int do_sync = 1;
+ int do_wtmp = 1;
+ int do_nothing = 0;
+ int do_hard = 0;
+ int do_ifdown = 0;
+ int do_hddown = 0;
+ int do_poweroff = 0;
+ int c;
+ char *tm = NULL;
+
+ /*
+ * Find out who we are
+ */
+ /* Remove dash passed on in argv[0] when used as login shell. */
+ if (argv[0][0] == '-') argv[0]++;
+ if ((progname = strrchr(argv[0], '/')) != NULL)
+ progname++;
+ else
+ progname = argv[0];
+
+ if (!strcmp(progname, "reboot")) do_reboot = 1;
+ if (!strcmp(progname, "poweroff")) do_poweroff = 1;
+
+ /*
+ * Get flags
+ */
+ while((c = getopt(argc, argv, ":ihdfnpwt:")) != EOF) {
+ switch(c) {
+ case 'n':
+ do_sync = 0;
+ do_wtmp = 0;
+ break;
+ case 'w':
+ do_nothing = 1;
+ break;
+ case 'd':
+ do_wtmp = 0;
+ break;
+ case 'f':
+ do_hard = 1;
+ break;
+ case 'i':
+ do_ifdown = 1;
+ break;
+ case 'h':
+ do_hddown = 1;
+ break;
+ case 'p':
+ do_poweroff = 1;
+ break;
+ case 't':
+ tm = optarg;
+ break;
+ default:
+ usage();
+ }
+ }
+ if (argc != optind) usage();
+
+ if (geteuid() != 0) {
+ fprintf(stderr, "%s: must be superuser.\n", progname);
+ exit(1);
+ }
+
+ (void)chdir("/");
+
+ if (!do_hard && !do_nothing) {
+ /*
+ * See if we are in runlevel 0 or 6.
+ */
+ c = get_runlevel();
+ if (c != '0' && c != '6')
+ do_shutdown(do_reboot ? "-r" : "-h", tm);
+ }
+
+ /*
+ * Record the fact that we're going down
+ */
+ if (do_wtmp)
+ write_wtmp("shutdown", "~~", 0, RUN_LVL, "~~");
+
+ /*
+ * Exit if all we wanted to do was write a wtmp record.
+ */
+ if (do_nothing && !do_hddown && !do_ifdown) exit(0);
+
+ if (do_sync) {
+ sync();
+ sleep(2);
+ }
+
+ if (do_ifdown)
+ (void)ifdown();
+
+ if (do_hddown)
+ (void)hddown();
+
+ if (do_nothing) exit(0);
+
+ if (do_reboot) {
+ init_reboot(BMAGIC_REBOOT);
+ } else {
+ /*
+ * Turn on hard reboot, CTRL-ALT-DEL will reboot now
+ */
+#ifdef BMAGIC_HARD
+ init_reboot(BMAGIC_HARD);
+#endif
+
+ /*
+ * Stop init; it is insensitive to the signals sent
+ * by the kernel.
+ */
+ kill(1, SIGTSTP);
+
+ /*
+ * Halt or poweroff.
+ */
+ if (do_poweroff)
+ init_reboot(BMAGIC_POWEROFF);
+ /*
+ * Fallthrough if failed.
+ */
+ init_reboot(BMAGIC_HALT);
+ }
+
+ /*
+ * If we return, we (c)ontinued from the kernel monitor.
+ */
+#ifdef BMAGIC_SOFT
+ init_reboot(BMAGIC_SOFT);
+#endif
+ kill(1, SIGCONT);
+
+ exit(0);
+}
--- /dev/null
+/*
+ * hddown.c Find all disks on the system and
+ * shut them down.
+ *
+ * Copyright (C) 2003 Miquel van Smoorenburg.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+char *v_hddown = "@(#)hddown.c 1.02 22-Apr-2003 miquels@cistron.nl";
+
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <time.h>
+#include <string.h>
+#include <fcntl.h>
+#include <dirent.h>
+
+#ifdef __linux__
+
+#include <sys/ioctl.h>
+#include <linux/hdreg.h>
+
+#define USE_SYSFS
+#ifdef USE_SYSFS
+/*
+ * sysfs part Find all disks on the system, list out IDE and unmanaged
+ * SATA disks, flush the cache of those and shut them down.
+ * Author: Werner Fink <werner@suse.de>, 2007/06/12
+ *
+ */
+#include <limits.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#ifdef WORDS_BIGENDIAN
+#include <byteswap.h>
+#endif
+
+#define SYS_BLK "/sys/block"
+#define SYS_CLASS "/sys/class/scsi_disk"
+#define DEV_BASE "/dev"
+#define ISSPACE(c) (((c)==' ')||((c)=='\n')||((c)=='\t')||((c)=='\v')||((c)=='\r')||((c)=='\f'))
+
+/* Used in flush_cache_ext(), compare with <linux/hdreg.h> */
+#define IDBYTES 512
+#define MASK_EXT 0xE000 /* Bit 15 shall be zero, bit 14 shall be one, bit 13 flush cache ext */
+#define TEST_EXT 0x6000
+
+/* Maybe set in list_disks() and used in do_standby_idedisk() */
+#define DISK_IS_IDE 0x00000001
+#define DISK_IS_SATA 0x00000002
+#define DISK_EXTFLUSH 0x00000004
+
+static char *strstrip(char *str);
+static FILE *hdopen(const char* const format, const char* const name);
+static int flush_cache_ext(const char *device);
+
+/*
+ * Find all disks through /sys/block.
+ */
+static char *list_disks(DIR* blk, unsigned int* flags)
+{
+ struct dirent *d;
+
+ while ((d = readdir(blk))) {
+ *flags = 0;
+ if (d->d_name[1] == 'd' && (d->d_name[0] == 'h' || d->d_name[0] == 's')) {
+ char buf[NAME_MAX+1], lnk[NAME_MAX+1], *ptr;
+ struct stat st;
+ FILE *fp;
+ int ret;
+
+ fp = hdopen(SYS_BLK "/%s/removable", d->d_name);
+ if ((long)fp <= 0) {
+ if ((long)fp < 0)
+ goto empty; /* error */
+ continue; /* no entry `removable' */
+ }
+
+ ret = getc(fp);
+ fclose(fp);
+
+ if (ret != '0')
+ continue; /* not a hard disk */
+
+ if (d->d_name[0] == 'h') {
+ (*flags) |= DISK_IS_IDE;
+ if ((ret = flush_cache_ext(d->d_name))) {
+ if (ret < 0)
+ goto empty;
+ (*flags) |= DISK_EXTFLUSH;
+ }
+ break; /* old IDE disk not managed by kernel, out here */
+ }
+
+ ret = snprintf(buf, sizeof(buf), SYS_BLK "/%s/device", d->d_name);
+ if ((ret >= sizeof(buf)) || (ret < 0))
+ goto empty; /* error */
+
+ ret = readlink(buf, lnk, sizeof(lnk));
+ if (ret >= sizeof(lnk))
+ goto empty; /* error */
+ if (ret < 0) {
+ if (errno != ENOENT)
+ goto empty; /* error */
+ continue; /* no entry `device' */
+ }
+ lnk[ret] = '\0';
+
+ ptr = basename(lnk);
+ if (!ptr || !*ptr)
+ continue; /* should not happen */
+
+ ret = snprintf(buf, sizeof(buf), SYS_CLASS "/%s/manage_start_stop", ptr);
+ if ((ret >= sizeof(buf)) || (ret < 0))
+ goto empty; /* error */
+
+ ret = stat(buf, &st);
+ if (ret == 0)
+ continue; /* disk found but managed by kernel */
+
+ if (errno != ENOENT)
+ goto empty; /* error */
+
+ fp = hdopen(SYS_BLK "/%s/device/vendor", d->d_name);
+ if ((long)fp <= 0) {
+ if ((long)fp < 0)
+ goto empty; /* error */
+ continue; /* no entry `device/vendor' */
+ }
+
+ ptr = fgets(buf, sizeof(buf), fp);
+ fclose(fp);
+ if (ptr == (char*)0)
+ continue; /* should not happen */
+
+ ptr = strstrip(buf);
+ if (*ptr == '\0')
+ continue; /* should not happen */
+
+ if (strncmp(buf, "ATA", sizeof(buf)))
+ continue; /* no SATA but a real SCSI disk */
+
+ (*flags) |= (DISK_IS_IDE|DISK_IS_SATA);
+ if ((ret = flush_cache_ext(d->d_name))) {
+ if (ret < 0)
+ goto empty;
+ (*flags) |= DISK_EXTFLUSH;
+ }
+ break; /* new SATA disk to shutdown, out here */
+ }
+ }
+ if (d == (struct dirent*)0)
+ goto empty;
+ return d->d_name;
+empty:
+ return (char*)0;
+}
+
+/*
+ * Put an disk in standby mode.
+ * Code stolen from hdparm.c
+ */
+static int do_standby_idedisk(char *device, unsigned int flags)
+{
+#ifndef WIN_STANDBYNOW1
+#define WIN_STANDBYNOW1 0xE0
+#endif
+#ifndef WIN_STANDBYNOW2
+#define WIN_STANDBYNOW2 0x94
+#endif
+#ifndef WIN_FLUSH_CACHE_EXT
+#define WIN_FLUSH_CACHE_EXT 0xEA
+#endif
+#ifndef WIN_FLUSH_CACHE
+#define WIN_FLUSH_CACHE 0xE7
+#endif
+ unsigned char flush1[4] = {WIN_FLUSH_CACHE_EXT,0,0,0};
+ unsigned char flush2[4] = {WIN_FLUSH_CACHE,0,0,0};
+ unsigned char stdby1[4] = {WIN_STANDBYNOW1,0,0,0};
+ unsigned char stdby2[4] = {WIN_STANDBYNOW2,0,0,0};
+ char buf[NAME_MAX+1];
+ int fd, ret;
+
+ ret = snprintf(buf, sizeof(buf), DEV_BASE "/%s", device);
+ if ((ret >= sizeof(buf)) || (ret < 0))
+ return -1;
+
+ if ((fd = open(buf, O_RDWR)) < 0)
+ return -1;
+
+ switch (flags & DISK_EXTFLUSH) {
+ case DISK_EXTFLUSH:
+ if (ioctl(fd, HDIO_DRIVE_CMD, &flush1) == 0)
+ break;
+ /* Extend flush rejected, try standard flush */
+ default:
+ ioctl(fd, HDIO_DRIVE_CMD, &flush2);
+ break;
+ }
+
+ ret = ioctl(fd, HDIO_DRIVE_CMD, &stdby1) &&
+ ioctl(fd, HDIO_DRIVE_CMD, &stdby2);
+ close(fd);
+
+ if (ret)
+ return -1;
+ return 0;
+}
+
+/*
+ * List all disks and put them in standby mode.
+ * This has the side-effect of flushing the writecache,
+ * which is exactly what we want on poweroff.
+ */
+int hddown(void)
+{
+ unsigned int flags;
+ char *disk;
+ DIR *blk;
+
+ if ((blk = opendir(SYS_BLK)) == (DIR*)0)
+ return -1;
+
+ while ((disk = list_disks(blk, &flags)))
+ do_standby_idedisk(disk, flags);
+
+ return closedir(blk);
+}
+
+/*
+ * Strip off trailing white spaces
+ */
+static char *strstrip(char *str)
+{
+ const size_t len = strlen(str);
+ if (len) {
+ char* end = str + len - 1;
+ while ((end != str) && ISSPACE(*end))
+ end--;
+ *(end + 1) = '\0'; /* remove trailing white spaces */
+ }
+ return str;
+}
+
+/*
+ * Open a sysfs file without getting a controlling tty
+ * and return FILE* pointer.
+ */
+static FILE *hdopen(const char* const format, const char* const name)
+{
+ char buf[NAME_MAX+1];
+ FILE *fp = (FILE*)-1;
+ int fd, ret;
+
+ ret = snprintf(buf, sizeof(buf), format, name);
+ if ((ret >= sizeof(buf)) || (ret < 0))
+ goto error; /* error */
+
+ fd = open(buf, O_RDONLY|O_NOCTTY);
+ if (fd < 0) {
+ if (errno != ENOENT)
+ goto error; /* error */
+ fp = (FILE*)0;
+ goto error; /* no entry `removable' */
+ }
+
+ fp = fdopen(fd, "r");
+ if (fp == (FILE*)0)
+ close(fd); /* should not happen */
+error:
+ return fp;
+}
+
+/*
+ * Check IDE/(S)ATA hard disk identity for
+ * the FLUSH CACHE EXT bit set.
+ */
+static int flush_cache_ext(const char *device)
+{
+#ifndef WIN_IDENTIFY
+#define WIN_IDENTIFY 0xEC
+#endif
+ unsigned char args[4+IDBYTES];
+ unsigned short *id = (unsigned short*)(&args[4]);
+ char buf[NAME_MAX+1], *ptr;
+ int fd = -1, ret = 0;
+ FILE *fp;
+
+ fp = hdopen(SYS_BLK "/%s/size", device);
+ if ((long)fp <= 0) {
+ if ((long)fp < 0)
+ return -1; /* error */
+ goto out; /* no entry `size' */
+ }
+
+ ptr = fgets(buf, sizeof(buf), fp);
+ fclose(fp);
+ if (ptr == (char*)0)
+ goto out; /* should not happen */
+
+ ptr = strstrip(buf);
+ if (*ptr == '\0')
+ goto out; /* should not happen */
+
+ if ((size_t)atoll(buf) < (1<<28))
+ goto out; /* small disk */
+
+ ret = snprintf(buf, sizeof(buf), DEV_BASE "/%s", device);
+ if ((ret >= sizeof(buf)) || (ret < 0))
+ return -1; /* error */
+
+ if ((fd = open(buf, O_RDONLY|O_NONBLOCK)) < 0)
+ goto out;
+
+ memset(&args[0], 0, sizeof(args));
+ args[0] = WIN_IDENTIFY;
+ args[3] = 1;
+ if (ioctl(fd, HDIO_DRIVE_CMD, &args))
+ goto out;
+#ifdef WORDS_BIGENDIAN
+# if 0
+ {
+ const unsigned short *end = id + IDBYTES/2;
+ const unsigned short *from = id;
+ unsigned short *to = id;
+
+ while (from < end)
+ *to++ = bswap_16(*from++);
+ }
+# else
+ id[83] = bswap_16(id[83]);
+# endif
+#endif
+ if ((id[83] & MASK_EXT) == TEST_EXT)
+ ret = 1;
+out:
+ if (fd >= 0)
+ close(fd);
+ return ret;
+}
+#else /* ! USE_SYSFS */
+#define MAX_DISKS 64
+#define PROC_IDE "/proc/ide"
+#define DEV_BASE "/dev"
+
+/*
+ * Find all IDE disks through /proc.
+ */
+static int find_idedisks(const char **dev, int maxdev, int *count)
+{
+ DIR *dd;
+ FILE *fp;
+ struct dirent *d;
+ char buf[256];
+
+ if ((dd = opendir(PROC_IDE)) == NULL)
+ return -1;
+
+ while (*count < maxdev && (d = readdir(dd)) != NULL) {
+ if (strncmp(d->d_name, "hd", 2) != 0)
+ continue;
+ buf[0] = 0;
+ snprintf(buf, sizeof(buf), PROC_IDE "/%s/media", d->d_name);
+ if ((fp = fopen(buf, "r")) == NULL)
+ continue;
+ if (fgets(buf, sizeof(buf), fp) == 0 ||
+ strcmp(buf, "disk\n") != 0) {
+ fclose(fp);
+ continue;
+ }
+ fclose(fp);
+ snprintf(buf, sizeof(buf), DEV_BASE "/%s", d->d_name);
+ dev[(*count)++] = strdup(buf);
+ }
+ closedir(dd);
+
+ return 0;
+}
+
+/*
+ * Find all SCSI/SATA disks.
+ */
+static int find_scsidisks(const char **dev, int maxdev, int *count)
+{
+ if (*count < maxdev) dev[(*count)++] = DEV_BASE "/sda";
+ if (*count < maxdev) dev[(*count)++] = DEV_BASE "/sdb";
+ if (*count < maxdev) dev[(*count)++] = DEV_BASE "/sdc";
+ if (*count < maxdev) dev[(*count)++] = DEV_BASE "/sdd";
+ if (*count < maxdev) dev[(*count)++] = DEV_BASE "/sde";
+ if (*count < maxdev) dev[(*count)++] = DEV_BASE "/sdf";
+ if (*count < maxdev) dev[(*count)++] = DEV_BASE "/sdg";
+ if (*count < maxdev) dev[(*count)++] = DEV_BASE "/sdh";
+
+ return 0;
+}
+
+/*
+ * Open the device node of a disk.
+ */
+static int open_disk(const char *device)
+{
+ return open(device, O_RDWR);
+}
+
+/*
+ * Open device nodes of all disks, and store the file descriptors in fds.
+ * This has to be done in advance because accessing the device nodes
+ * might cause a disk to spin back up.
+ */
+static int open_disks(const char **disks, int *fds, int count)
+{
+ int i;
+
+ for (i = 0; i < count; i++)
+ fds[i] = open_disk(disks[i]);
+
+ return 0;
+}
+
+/*
+ * Put an IDE/SCSI/SATA disk in standby mode.
+ * Code stolen from hdparm.c
+ */
+static int do_standby_disk(int fd)
+{
+#ifndef WIN_STANDBYNOW1
+#define WIN_STANDBYNOW1 0xE0
+#endif
+#ifndef WIN_STANDBYNOW2
+#define WIN_STANDBYNOW2 0x94
+#endif
+ unsigned char args1[4] = {WIN_STANDBYNOW1,0,0,0};
+ unsigned char args2[4] = {WIN_STANDBYNOW2,0,0,0};
+
+ if (fd < 0)
+ return -1;
+
+ if (ioctl(fd, HDIO_DRIVE_CMD, &args1) &&
+ ioctl(fd, HDIO_DRIVE_CMD, &args2))
+ return -1;
+
+ return 0;
+}
+
+/*
+ * Put all specified disks in standby mode.
+ */
+static int do_standby_disks(const int *fds, int count)
+{
+ int i;
+
+ for (i = 0; i < count; i++)
+ do_standby_disk(fds[i]);
+
+ return 0;
+}
+
+/*
+ * First find all IDE/SCSI/SATA disks, then put them in standby mode.
+ * This has the side-effect of flushing the writecache,
+ * which is exactly what we want on poweroff.
+ */
+int hddown(void)
+{
+ const char *disks[MAX_DISKS];
+ int fds[MAX_DISKS];
+ int count = 0;
+ int result1, result2;
+
+ result1 = find_idedisks(disks, MAX_DISKS, &count);
+ result2 = find_scsidisks(disks, MAX_DISKS, &count);
+
+ open_disks(disks, fds, count);
+ do_standby_disks(fds, count);
+
+ return (result1 ? result1 : result2);
+}
+#endif /* ! USE_SYSFS */
+#else /* __linux__ */
+
+int hddown(void)
+{
+ return 0;
+}
+
+#endif /* __linux__ */
+
+#ifdef STANDALONE
+int main(int argc, char **argv)
+{
+ return (hddown() == 0);
+}
+#endif
+
--- /dev/null
+/*
+ * ifdown.c Find all network interfaces on the system and
+ * shut them down.
+ *
+ * Copyright (C) 1998 Miquel van Smoorenburg.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+char *v_ifdown = "@(#)ifdown.c 1.11 02-Jun-1998 miquels@cistron.nl";
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <time.h>
+#include <string.h>
+
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <sys/errno.h>
+
+#include <net/if.h>
+#include <netinet/in.h>
+
+#define MAX_IFS 64
+
+/*
+ * First, we find all shaper devices and down them. Then we
+ * down all real interfaces. This is because the comment in the
+ * shaper driver says "if you down the shaper device before the
+ * attached inerface your computer will follow".
+ */
+int ifdown(void)
+{
+ struct ifreq ifr[MAX_IFS];
+ struct ifconf ifc;
+ int i, fd;
+ int numif;
+ int shaper;
+
+ if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
+ fprintf(stderr, "ifdown: ");
+ perror("socket");
+ return -1;
+ }
+ ifc.ifc_len = sizeof(ifr);
+ ifc.ifc_req = ifr;
+
+ if (ioctl(fd, SIOCGIFCONF, &ifc) < 0) {
+ fprintf(stderr, "ifdown: ");
+ perror("SIOCGIFCONF");
+ close(fd);
+ return -1;
+ }
+ numif = ifc.ifc_len / sizeof(struct ifreq);
+
+ for (shaper = 1; shaper >= 0; shaper--) {
+ for (i = 0; i < numif; i++) {
+
+ if ((strncmp(ifr[i].ifr_name, "shaper", 6) == 0)
+ != shaper) continue;
+
+ if (strcmp(ifr[i].ifr_name, "lo") == 0)
+ continue;
+ if (strchr(ifr[i].ifr_name, ':') != NULL)
+ continue;
+ ifr[i].ifr_flags &= ~(IFF_UP);
+ if (ioctl(fd, SIOCSIFFLAGS, &ifr[i]) < 0) {
+ fprintf(stderr, "ifdown: shutdown ");
+ perror(ifr[i].ifr_name);
+ }
+ }
+ }
+ close(fd);
+
+ return 0;
+}
+
--- /dev/null
+/*
+ * Init A System-V Init Clone.
+ *
+ * Usage: /sbin/init
+ * init [0123456SsQqAaBbCc]
+ * telinit [0123456SsQqAaBbCc]
+ *
+ * Version: @(#)init.c 2.86 30-Jul-2004 miquels@cistron.nl
+ */
+#define VERSION "2.86"
+#define DATE "31-Jul-2004"
+/*
+ * This file is part of the sysvinit suite,
+ * Copyright (C) 1991-2004 Miquel van Smoorenburg.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <sys/wait.h>
+#ifdef __linux__
+#include <sys/kd.h>
+#endif
+#include <sys/resource.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <stdio.h>
+#include <time.h>
+#include <fcntl.h>
+#include <string.h>
+#include <signal.h>
+#include <termios.h>
+#include <utmp.h>
+#include <ctype.h>
+#include <stdarg.h>
+#include <sys/syslog.h>
+#include <sys/time.h>
+
+#ifdef WITH_SELINUX
+#include <selinux/selinux.h>
+#endif
+
+
+#ifdef __i386__
+# if (__GLIBC__ >= 2)
+ /* GNU libc 2.x */
+# define STACK_DEBUG 1
+# if (__GLIBC__ == 2 && __GLIBC_MINOR__ == 0)
+ /* Only glibc 2.0 needs this */
+# include <sigcontext.h>
+# endif
+# endif
+#endif
+
+#include "init.h"
+#include "initreq.h"
+#include "paths.h"
+#include "reboot.h"
+#include "set.h"
+
+#ifndef SIGPWR
+# define SIGPWR SIGUSR2
+#endif
+
+#ifndef CBAUD
+# define CBAUD 0
+#endif
+#ifndef CBAUDEX
+# define CBAUDEX 0
+#endif
+
+/* Set a signal handler. */
+#define SETSIG(sa, sig, fun, flags) \
+ do { \
+ sa.sa_handler = fun; \
+ sa.sa_flags = flags; \
+ sigemptyset(&sa.sa_mask); \
+ sigaction(sig, &sa, NULL); \
+ } while(0)
+
+/* Version information */
+char *Version = "@(#) init " VERSION " " DATE " miquels@cistron.nl";
+char *bootmsg = "version " VERSION " %s";
+#define E_VERSION "INIT_VERSION=sysvinit-" VERSION
+
+CHILD *family = NULL; /* The linked list of all entries */
+CHILD *newFamily = NULL; /* The list after inittab re-read */
+
+CHILD ch_emerg = { /* Emergency shell */
+ WAITING, 0, 0, 0, 0,
+ "~~",
+ "S",
+ 3,
+ "/sbin/sulogin",
+ NULL,
+ NULL
+};
+
+char runlevel = 'S'; /* The current run level */
+char thislevel = 'S'; /* The current runlevel */
+char prevlevel = 'N'; /* Previous runlevel */
+int dfl_level = 0; /* Default runlevel */
+sig_atomic_t got_cont = 0; /* Set if we received the SIGCONT signal */
+sig_atomic_t got_signals; /* Set if we received a signal. */
+int emerg_shell = 0; /* Start emergency shell? */
+int wrote_wtmp_reboot = 1; /* Set when we wrote the reboot record */
+int wrote_utmp_reboot = 1; /* Set when we wrote the reboot record */
+int sltime = 5; /* Sleep time between TERM and KILL */
+char *argv0; /* First arguments; show up in ps listing */
+int maxproclen; /* Maximal length of argv[0] with \0 */
+struct utmp utproto; /* Only used for sizeof(utproto.ut_id) */
+char *user_console = NULL; /* User console device */
+char *console_dev; /* Console device. */
+int pipe_fd = -1; /* /dev/initctl */
+int did_boot = 0; /* Did we already do BOOT* stuff? */
+int main(int, char **);
+
+/* Used by re-exec part */
+int reload = 0; /* Should we do initialization stuff? */
+char *myname="/sbin/init"; /* What should we exec */
+int oops_error; /* Used by some of the re-exec code. */
+const char *Signature = "12567362"; /* Signature for re-exec fd */
+
+/* Macro to see if this is a special action */
+#define ISPOWER(i) ((i) == POWERWAIT || (i) == POWERFAIL || \
+ (i) == POWEROKWAIT || (i) == POWERFAILNOW || \
+ (i) == CTRLALTDEL)
+
+/* ascii values for the `action' field. */
+struct actions {
+ char *name;
+ int act;
+} actions[] = {
+ { "respawn", RESPAWN },
+ { "wait", WAIT },
+ { "once", ONCE },
+ { "boot", BOOT },
+ { "bootwait", BOOTWAIT },
+ { "powerfail", POWERFAIL },
+ { "powerfailnow",POWERFAILNOW },
+ { "powerwait", POWERWAIT },
+ { "powerokwait", POWEROKWAIT },
+ { "ctrlaltdel", CTRLALTDEL },
+ { "off", OFF },
+ { "ondemand", ONDEMAND },
+ { "initdefault", INITDEFAULT },
+ { "sysinit", SYSINIT },
+ { "kbrequest", KBREQUEST },
+ { NULL, 0 },
+};
+
+/*
+ * State parser token table (see receive_state)
+ */
+struct {
+ char name[4];
+ int cmd;
+} cmds[] = {
+ { "VER", C_VER },
+ { "END", C_END },
+ { "REC", C_REC },
+ { "EOR", C_EOR },
+ { "LEV", C_LEV },
+ { "FL ", C_FLAG },
+ { "AC ", C_ACTION },
+ { "CMD", C_PROCESS },
+ { "PID", C_PID },
+ { "EXS", C_EXS },
+ { "-RL", D_RUNLEVEL },
+ { "-TL", D_THISLEVEL },
+ { "-PL", D_PREVLEVEL },
+ { "-SI", D_GOTSIGN },
+ { "-WR", D_WROTE_WTMP_REBOOT},
+ { "-WU", D_WROTE_UTMP_REBOOT},
+ { "-ST", D_SLTIME },
+ { "-DB", D_DIDBOOT },
+ { "", 0 }
+};
+struct {
+ char *name;
+ int mask;
+} flags[]={
+ {"RU",RUNNING},
+ {"DE",DEMAND},
+ {"XD",XECUTED},
+ {"WT",WAITING},
+ {NULL,0}
+};
+
+#define NR_EXTRA_ENV 16
+char *extra_env[NR_EXTRA_ENV];
+
+
+/*
+ * Sleep a number of seconds.
+ *
+ * This only works correctly because the linux select updates
+ * the elapsed time in the struct timeval passed to select!
+ */
+void do_sleep(int sec)
+{
+ struct timeval tv;
+
+ tv.tv_sec = sec;
+ tv.tv_usec = 0;
+
+ while(select(0, NULL, NULL, NULL, &tv) < 0 && errno == EINTR)
+ ;
+}
+
+
+/*
+ * Non-failing allocation routines (init cannot fail).
+ */
+void *imalloc(size_t size)
+{
+ void *m;
+
+ while ((m = malloc(size)) == NULL) {
+ initlog(L_VB, "out of memory");
+ do_sleep(5);
+ }
+ memset(m, 0, size);
+ return m;
+}
+
+
+char *istrdup(char *s)
+{
+ char *m;
+ int l;
+
+ l = strlen(s) + 1;
+ m = imalloc(l);
+ memcpy(m, s, l);
+ return m;
+}
+
+
+/*
+ * Send the state info of the previous running init to
+ * the new one, in a version-independant way.
+ */
+void send_state(int fd)
+{
+ FILE *fp;
+ CHILD *p;
+ int i,val;
+
+ fp = fdopen(fd,"w");
+
+ fprintf(fp, "VER%s\n", Version);
+ fprintf(fp, "-RL%c\n", runlevel);
+ fprintf(fp, "-TL%c\n", thislevel);
+ fprintf(fp, "-PL%c\n", prevlevel);
+ fprintf(fp, "-SI%u\n", got_signals);
+ fprintf(fp, "-WR%d\n", wrote_wtmp_reboot);
+ fprintf(fp, "-WU%d\n", wrote_utmp_reboot);
+ fprintf(fp, "-ST%d\n", sltime);
+ fprintf(fp, "-DB%d\n", did_boot);
+
+ for (p = family; p; p = p->next) {
+ fprintf(fp, "REC%s\n", p->id);
+ fprintf(fp, "LEV%s\n", p->rlevel);
+ for (i = 0, val = p->flags; flags[i].mask; i++)
+ if (val & flags[i].mask) {
+ val &= ~flags[i].mask;
+ fprintf(fp, "FL %s\n",flags[i].name);
+ }
+ fprintf(fp, "PID%d\n",p->pid);
+ fprintf(fp, "EXS%u\n",p->exstat);
+ for(i = 0; actions[i].act; i++)
+ if (actions[i].act == p->action) {
+ fprintf(fp, "AC %s\n", actions[i].name);
+ break;
+ }
+ fprintf(fp, "CMD%s\n", p->process);
+ fprintf(fp, "EOR\n");
+ }
+ fprintf(fp, "END\n");
+ fclose(fp);
+}
+
+/*
+ * Read a string from a file descriptor.
+ * FIXME: why not use fgets() ?
+ */
+static int get_string(char *p, int size, FILE *f)
+{
+ int c;
+
+ while ((c = getc(f)) != EOF && c != '\n') {
+ if (--size > 0)
+ *p++ = c;
+ }
+ *p = '\0';
+ return (c != EOF) && (size > 0);
+}
+
+/*
+ * Read trailing data from the state pipe until we see a newline.
+ */
+static int get_void(FILE *f)
+{
+ int c;
+
+ while ((c = getc(f)) != EOF && c != '\n')
+ ;
+
+ return (c != EOF);
+}
+
+/*
+ * Read the next "command" from the state pipe.
+ */
+static int get_cmd(FILE *f)
+{
+ char cmd[4] = " ";
+ int i;
+
+ if (fread(cmd, 1, sizeof(cmd) - 1, f) != sizeof(cmd) - 1)
+ return C_EOF;
+
+ for(i = 0; cmds[i].cmd && strcmp(cmds[i].name, cmd) != 0; i++)
+ ;
+ return cmds[i].cmd;
+}
+
+/*
+ * Read a CHILD * from the state pipe.
+ */
+static CHILD *get_record(FILE *f)
+{
+ int cmd;
+ char s[32];
+ int i;
+ CHILD *p;
+
+ do {
+ switch (cmd = get_cmd(f)) {
+ case C_END:
+ get_void(f);
+ return NULL;
+ case 0:
+ get_void(f);
+ break;
+ case C_REC:
+ break;
+ case D_RUNLEVEL:
+ fscanf(f, "%c\n", &runlevel);
+ break;
+ case D_THISLEVEL:
+ fscanf(f, "%c\n", &thislevel);
+ break;
+ case D_PREVLEVEL:
+ fscanf(f, "%c\n", &prevlevel);
+ break;
+ case D_GOTSIGN:
+ fscanf(f, "%u\n", &got_signals);
+ break;
+ case D_WROTE_WTMP_REBOOT:
+ fscanf(f, "%d\n", &wrote_wtmp_reboot);
+ break;
+ case D_WROTE_UTMP_REBOOT:
+ fscanf(f, "%d\n", &wrote_utmp_reboot);
+ break;
+ case D_SLTIME:
+ fscanf(f, "%d\n", &sltime);
+ break;
+ case D_DIDBOOT:
+ fscanf(f, "%d\n", &did_boot);
+ break;
+ default:
+ if (cmd > 0 || cmd == C_EOF) {
+ oops_error = -1;
+ return NULL;
+ }
+ }
+ } while (cmd != C_REC);
+
+ p = imalloc(sizeof(CHILD));
+ get_string(p->id, sizeof(p->id), f);
+
+ do switch(cmd = get_cmd(f)) {
+ case 0:
+ case C_EOR:
+ get_void(f);
+ break;
+ case C_PID:
+ fscanf(f, "%d\n", &(p->pid));
+ break;
+ case C_EXS:
+ fscanf(f, "%u\n", &(p->exstat));
+ break;
+ case C_LEV:
+ get_string(p->rlevel, sizeof(p->rlevel), f);
+ break;
+ case C_PROCESS:
+ get_string(p->process, sizeof(p->process), f);
+ break;
+ case C_FLAG:
+ get_string(s, sizeof(s), f);
+ for(i = 0; flags[i].name; i++) {
+ if (strcmp(flags[i].name,s) == 0)
+ break;
+ }
+ p->flags |= flags[i].mask;
+ break;
+ case C_ACTION:
+ get_string(s, sizeof(s), f);
+ for(i = 0; actions[i].name; i++) {
+ if (strcmp(actions[i].name, s) == 0)
+ break;
+ }
+ p->action = actions[i].act ? actions[i].act : OFF;
+ break;
+ default:
+ free(p);
+ oops_error = -1;
+ return NULL;
+ } while( cmd != C_EOR);
+
+ return p;
+}
+
+/*
+ * Read the complete state info from the state pipe.
+ * Returns 0 on success
+ */
+int receive_state(int fd)
+{
+ FILE *f;
+ char old_version[256];
+ CHILD **pp;
+
+ f = fdopen(fd, "r");
+
+ if (get_cmd(f) != C_VER)
+ return -1;
+ get_string(old_version, sizeof(old_version), f);
+ oops_error = 0;
+ for (pp = &family; (*pp = get_record(f)) != NULL; pp = &((*pp)->next))
+ ;
+ fclose(f);
+ return oops_error;
+}
+
+/*
+ * Set the process title.
+ */
+#ifdef __GNUC__
+__attribute__ ((format (printf, 1, 2)))
+#endif
+static int setproctitle(char *fmt, ...)
+{
+ va_list ap;
+ int len;
+ char buf[256];
+
+ buf[0] = 0;
+
+ va_start(ap, fmt);
+ len = vsnprintf(buf, sizeof(buf), fmt, ap);
+ va_end(ap);
+
+ if (maxproclen > 1) {
+ memset(argv0, 0, maxproclen);
+ strncpy(argv0, buf, maxproclen - 1);
+ }
+
+ return len;
+}
+
+/*
+ * Set console_dev to a working console.
+ */
+void console_init(void)
+{
+ int fd;
+ int tried_devcons = 0;
+ int tried_vtmaster = 0;
+ char *s;
+
+ if (user_console) {
+ console_dev = user_console;
+ } else if ((s = getenv("CONSOLE")) != NULL)
+ console_dev = s;
+ else {
+ console_dev = CONSOLE;
+ tried_devcons++;
+ }
+
+ while ((fd = open(console_dev, O_RDONLY|O_NONBLOCK)) < 0) {
+ if (!tried_devcons) {
+ tried_devcons++;
+ console_dev = CONSOLE;
+ continue;
+ }
+ if (!tried_vtmaster) {
+ tried_vtmaster++;
+ console_dev = VT_MASTER;
+ continue;
+ }
+ break;
+ }
+ if (fd < 0)
+ console_dev = "/dev/null";
+ else
+ close(fd);
+}
+
+
+/*
+ * Open the console with retries.
+ */
+int console_open(int mode)
+{
+ int f, fd = -1;
+ int m;
+
+ /*
+ * Open device in nonblocking mode.
+ */
+ m = mode | O_NONBLOCK;
+
+ /*
+ * Retry the open five times.
+ */
+ for(f = 0; f < 5; f++) {
+ if ((fd = open(console_dev, m)) >= 0) break;
+ usleep(100);
+ }
+
+ if (fd < 0) return fd;
+
+ /*
+ * Set original flags.
+ */
+ if (m != mode)
+ fcntl(fd, F_SETFL, mode);
+ return fd;
+}
+
+/*
+ * We got a signal (HUP PWR WINCH ALRM INT)
+ */
+void signal_handler(int sig)
+{
+ ADDSET(got_signals, sig);
+}
+
+/*
+ * SIGCHLD: one of our children has died.
+ */
+void chld_handler()
+{
+ CHILD *ch;
+ int pid, st;
+ int saved_errno = errno;
+
+ /*
+ * Find out which process(es) this was (were)
+ */
+ while((pid = waitpid(-1, &st, WNOHANG)) != 0) {
+ if (errno == ECHILD) break;
+ for( ch = family; ch; ch = ch->next )
+ if ( ch->pid == pid && (ch->flags & RUNNING) ) {
+ INITDBG(L_VB,
+ "chld_handler: marked %d as zombie",
+ ch->pid);
+ ADDSET(got_signals, SIGCHLD);
+ ch->exstat = st;
+ ch->flags |= ZOMBIE;
+ if (ch->new) {
+ ch->new->exstat = st;
+ ch->new->flags |= ZOMBIE;
+ }
+ break;
+ }
+ if (ch == NULL) {
+ INITDBG(L_VB, "chld_handler: unknown child %d exited.",
+ pid);
+ }
+ }
+ errno = saved_errno;
+}
+
+/*
+ * Linux ignores all signals sent to init when the
+ * SIG_DFL handler is installed. Therefore we must catch SIGTSTP
+ * and SIGCONT, or else they won't work....
+ *
+ * The SIGCONT handler
+ */
+void cont_handler()
+{
+ got_cont = 1;
+}
+
+/*
+ * Fork and dump core in /.
+ */
+void coredump(void)
+{
+ static int dumped = 0;
+ struct rlimit rlim;
+ sigset_t mask;
+
+ if (dumped) return;
+ dumped = 1;
+
+ if (fork() != 0) return;
+
+ sigfillset(&mask);
+ sigprocmask(SIG_SETMASK, &mask, NULL);
+
+ rlim.rlim_cur = RLIM_INFINITY;
+ rlim.rlim_max = RLIM_INFINITY;
+ setrlimit(RLIMIT_CORE, &rlim);
+ chdir("/");
+
+ signal(SIGSEGV, SIG_DFL);
+ raise(SIGSEGV);
+ sigdelset(&mask, SIGSEGV);
+ sigprocmask(SIG_SETMASK, &mask, NULL);
+
+ do_sleep(5);
+ exit(0);
+}
+
+/*
+ * OOPS: segmentation violation!
+ * If we have the info, print where it occured.
+ * Then sleep 30 seconds and try to continue.
+ */
+#if defined(STACK_DEBUG) && defined(__linux__)
+void segv_handler(int sig, struct sigcontext ctx)
+{
+ char *p = "";
+ int saved_errno = errno;
+
+ if ((void *)ctx.eip >= (void *)do_sleep &&
+ (void *)ctx.eip < (void *)main)
+ p = " (code)";
+ initlog(L_VB, "PANIC: segmentation violation at %p%s! "
+ "sleeping for 30 seconds.", (void *)ctx.eip, p);
+ coredump();
+ do_sleep(30);
+ errno = saved_errno;
+}
+#else
+void segv_handler()
+{
+ int saved_errno = errno;
+
+ initlog(L_VB,
+ "PANIC: segmentation violation! sleeping for 30 seconds.");
+ coredump();
+ do_sleep(30);
+ errno = saved_errno;
+}
+#endif
+
+/*
+ * The SIGSTOP & SIGTSTP handler
+ */
+void stop_handler()
+{
+ int saved_errno = errno;
+
+ got_cont = 0;
+ while(!got_cont) pause();
+ got_cont = 0;
+ errno = saved_errno;
+}
+
+/*
+ * Set terminal settings to reasonable defaults
+ */
+void console_stty(void)
+{
+ struct termios tty;
+ int fd;
+
+ if ((fd = console_open(O_RDWR|O_NOCTTY)) < 0) {
+ initlog(L_VB, "can't open %s", console_dev);
+ return;
+ }
+
+ (void) tcgetattr(fd, &tty);
+
+ tty.c_cflag &= CBAUD|CBAUDEX|CSIZE|CSTOPB|PARENB|PARODD;
+ tty.c_cflag |= HUPCL|CLOCAL|CREAD;
+
+ tty.c_cc[VINTR] = 3; /* ctrl('c') */
+ tty.c_cc[VQUIT] = 28; /* ctrl('\\') */
+ tty.c_cc[VERASE] = 127;
+ tty.c_cc[VKILL] = 24; /* ctrl('x') */
+ tty.c_cc[VEOF] = 4; /* ctrl('d') */
+ tty.c_cc[VTIME] = 0;
+ tty.c_cc[VMIN] = 1;
+ tty.c_cc[VSTART] = 17; /* ctrl('q') */
+ tty.c_cc[VSTOP] = 19; /* ctrl('s') */
+ tty.c_cc[VSUSP] = 26; /* ctrl('z') */
+
+ /*
+ * Set pre and post processing
+ */
+ tty.c_iflag = IGNPAR|ICRNL|IXON|IXANY;
+ tty.c_oflag = OPOST|ONLCR;
+ tty.c_lflag = ISIG|ICANON|ECHO|ECHOCTL|ECHOPRT|ECHOKE;
+
+ /*
+ * Now set the terminal line.
+ * We don't care about non-transmitted output data
+ * and non-read input data.
+ */
+ (void) tcsetattr(fd, TCSANOW, &tty);
+ (void) tcflush(fd, TCIOFLUSH);
+ (void) close(fd);
+}
+
+/*
+ * Print to the system console
+ */
+void print(char *s)
+{
+ int fd;
+
+ if ((fd = console_open(O_WRONLY|O_NOCTTY|O_NDELAY)) >= 0) {
+ write(fd, s, strlen(s));
+ close(fd);
+ }
+}
+
+/*
+ * Log something to a logfile and the console.
+ */
+#ifdef __GNUC__
+__attribute__ ((format (printf, 2, 3)))
+#endif
+void initlog(int loglevel, char *s, ...)
+{
+ va_list va_alist;
+ char buf[256];
+ sigset_t nmask, omask;
+
+ va_start(va_alist, s);
+ vsnprintf(buf, sizeof(buf), s, va_alist);
+ va_end(va_alist);
+
+ if (loglevel & L_SY) {
+ /*
+ * Re-establish connection with syslogd every time.
+ * Block signals while talking to syslog.
+ */
+ sigfillset(&nmask);
+ sigprocmask(SIG_BLOCK, &nmask, &omask);
+ openlog("init", 0, LOG_DAEMON);
+ syslog(LOG_INFO, "%s", buf);
+ closelog();
+ sigprocmask(SIG_SETMASK, &omask, NULL);
+ }
+
+ /*
+ * And log to the console.
+ */
+ if (loglevel & L_CO) {
+ print("\rINIT: ");
+ print(buf);
+ print("\r\n");
+ }
+}
+
+
+/*
+ * Build a new environment for execve().
+ */
+char **init_buildenv(int child)
+{
+ char i_lvl[] = "RUNLEVEL=x";
+ char i_prev[] = "PREVLEVEL=x";
+ char i_cons[32];
+ char **e;
+ int n, i;
+
+ for (n = 0; environ[n]; n++)
+ ;
+ n += NR_EXTRA_ENV + 8;
+ e = calloc(n, sizeof(char *));
+
+ for (n = 0; environ[n]; n++)
+ e[n] = istrdup(environ[n]);
+
+ for (i = 0; i < NR_EXTRA_ENV; i++)
+ if (extra_env[i])
+ e[n++] = istrdup(extra_env[i]);
+
+ if (child) {
+ snprintf(i_cons, sizeof(i_cons), "CONSOLE=%s", console_dev);
+ i_lvl[9] = thislevel;
+ i_prev[10] = prevlevel;
+ e[n++] = istrdup(i_lvl);
+ e[n++] = istrdup(i_prev);
+ e[n++] = istrdup(i_cons);
+ e[n++] = istrdup(E_VERSION);
+ }
+
+ e[n++] = NULL;
+
+ return e;
+}
+
+
+void init_freeenv(char **e)
+{
+ int n;
+
+ for (n = 0; e[n]; n++)
+ free(e[n]);
+ free(e);
+}
+
+
+/*
+ * Fork and execute.
+ *
+ * This function is too long and indents too deep.
+ *
+ */
+int spawn(CHILD *ch, int *res)
+{
+ char *args[16]; /* Argv array */
+ char buf[136]; /* Line buffer */
+ int f, st, rc; /* Scratch variables */
+ char *ptr; /* Ditto */
+ time_t t; /* System time */
+ int oldAlarm; /* Previous alarm value */
+ char *proc = ch->process; /* Command line */
+ pid_t pid, pgrp; /* child, console process group. */
+ sigset_t nmask, omask; /* For blocking SIGCHLD */
+ struct sigaction sa;
+
+ *res = -1;
+ buf[sizeof(buf) - 1] = 0;
+
+ /* Skip '+' if it's there */
+ if (proc[0] == '+') proc++;
+
+ ch->flags |= XECUTED;
+
+ if (ch->action == RESPAWN || ch->action == ONDEMAND) {
+ /* Is the date stamp from less than 2 minutes ago? */
+ time(&t);
+ if (ch->tm + TESTTIME > t) {
+ ch->count++;
+ } else {
+ ch->count = 0;
+ ch->tm = t;
+ }
+
+ /* Do we try to respawn too fast? */
+ if (ch->count >= MAXSPAWN) {
+
+ initlog(L_VB,
+ "Id \"%s\" respawning too fast: disabled for %d minutes",
+ ch->id, SLEEPTIME / 60);
+ ch->flags &= ~RUNNING;
+ ch->flags |= FAILING;
+
+ /* Remember the time we stopped */
+ ch->tm = t;
+
+ /* Try again in 5 minutes */
+ oldAlarm = alarm(0);
+ if (oldAlarm > SLEEPTIME || oldAlarm <= 0) oldAlarm = SLEEPTIME;
+ alarm(oldAlarm);
+ return(-1);
+ }
+ }
+
+ /* See if there is an "initscript" (except in single user mode). */
+ if (access(INITSCRIPT, R_OK) == 0 && runlevel != 'S') {
+ /* Build command line using "initscript" */
+ args[1] = SHELL;
+ args[2] = INITSCRIPT;
+ args[3] = ch->id;
+ args[4] = ch->rlevel;
+ args[5] = "unknown";
+ for(f = 0; actions[f].name; f++) {
+ if (ch->action == actions[f].act) {
+ args[5] = actions[f].name;
+ break;
+ }
+ }
+ args[6] = proc;
+ args[7] = NULL;
+ } else if (strpbrk(proc, "~`!$^&*()=|\\{}[];\"'<>?")) {
+ /* See if we need to fire off a shell for this command */
+ /* Give command line to shell */
+ args[1] = SHELL;
+ args[2] = "-c";
+ strcpy(buf, "exec ");
+ strncat(buf, proc, sizeof(buf) - strlen(buf) - 1);
+ args[3] = buf;
+ args[4] = NULL;
+ } else {
+ /* Split up command line arguments */
+ buf[0] = 0;
+ strncat(buf, proc, sizeof(buf) - 1);
+ ptr = buf;
+ for(f = 1; f < 15; f++) {
+ /* Skip white space */
+ while(*ptr == ' ' || *ptr == '\t') ptr++;
+ args[f] = ptr;
+
+ /* May be trailing space.. */
+ if (*ptr == 0) break;
+
+ /* Skip this `word' */
+ while(*ptr && *ptr != ' ' && *ptr != '\t' && *ptr != '#')
+ ptr++;
+
+ /* If end-of-line, break */
+ if (*ptr == '#' || *ptr == 0) {
+ f++;
+ *ptr = 0;
+ break;
+ }
+ /* End word with \0 and continue */
+ *ptr++ = 0;
+ }
+ args[f] = NULL;
+ }
+ args[0] = args[1];
+ while(1) {
+ /*
+ * Block sigchild while forking.
+ */
+ sigemptyset(&nmask);
+ sigaddset(&nmask, SIGCHLD);
+ sigprocmask(SIG_BLOCK, &nmask, &omask);
+
+ if ((pid = fork()) == 0) {
+
+ close(0);
+ close(1);
+ close(2);
+ if (pipe_fd >= 0) close(pipe_fd);
+
+ sigprocmask(SIG_SETMASK, &omask, NULL);
+
+ /*
+ * In sysinit, boot, bootwait or single user mode:
+ * for any wait-type subprocess we _force_ the console
+ * to be its controlling tty.
+ */
+ if (strchr("*#sS", runlevel) && ch->flags & WAITING) {
+ /*
+ * We fork once extra. This is so that we can
+ * wait and change the process group and session
+ * of the console after exit of the leader.
+ */
+ setsid();
+ if ((f = console_open(O_RDWR|O_NOCTTY)) >= 0) {
+ /* Take over controlling tty by force */
+ (void)ioctl(f, TIOCSCTTY, 1);
+ dup(f);
+ dup(f);
+ }
+ SETSIG(sa, SIGCHLD, SIG_DFL, SA_RESTART);
+ if ((pid = fork()) < 0) {
+ initlog(L_VB, "cannot fork: %s",
+ strerror(errno));
+ exit(1);
+ }
+ if (pid > 0) {
+ /*
+ * Ignore keyboard signals etc.
+ * Then wait for child to exit.
+ */
+ SETSIG(sa, SIGINT, SIG_IGN, SA_RESTART);
+ SETSIG(sa, SIGTSTP, SIG_IGN, SA_RESTART);
+ SETSIG(sa, SIGQUIT, SIG_IGN, SA_RESTART);
+
+ while ((rc = waitpid(pid, &st, 0)) != pid)
+ if (rc < 0 && errno == ECHILD)
+ break;
+
+ /*
+ * Small optimization. See if stealing
+ * controlling tty back is needed.
+ */
+ pgrp = tcgetpgrp(f);
+ if (pgrp != getpid())
+ exit(0);
+
+ /*
+ * Steal controlling tty away. We do
+ * this with a temporary process.
+ */
+ if ((pid = fork()) < 0) {
+ initlog(L_VB, "cannot fork: %s",
+ strerror(errno));
+ exit(1);
+ }
+ if (pid == 0) {
+ setsid();
+ (void)ioctl(f, TIOCSCTTY, 1);
+ exit(0);
+ }
+ while((rc = waitpid(pid, &st, 0)) != pid)
+ if (rc < 0 && errno == ECHILD)
+ break;
+ exit(0);
+ }
+
+ /* Set ioctl settings to default ones */
+ console_stty();
+
+ } else {
+ setsid();
+ if ((f = console_open(O_RDWR|O_NOCTTY)) < 0) {
+ initlog(L_VB, "open(%s): %s", console_dev,
+ strerror(errno));
+ f = open("/dev/null", O_RDWR);
+ }
+ dup(f);
+ dup(f);
+ }
+
+ /* Reset all the signals, set up environment */
+ for(f = 1; f < NSIG; f++) SETSIG(sa, f, SIG_DFL, SA_RESTART);
+ environ = init_buildenv(1);
+
+ /*
+ * Execute prog. In case of ENOEXEC try again
+ * as a shell script.
+ */
+ execvp(args[1], args + 1);
+ if (errno == ENOEXEC) {
+ args[1] = SHELL;
+ args[2] = "-c";
+ strcpy(buf, "exec ");
+ strncat(buf, proc, sizeof(buf) - strlen(buf) - 1);
+ args[3] = buf;
+ args[4] = NULL;
+ execvp(args[1], args + 1);
+ }
+ initlog(L_VB, "cannot execute \"%s\"", args[1]);
+ exit(1);
+ }
+ *res = pid;
+ sigprocmask(SIG_SETMASK, &omask, NULL);
+
+ INITDBG(L_VB, "Started id %s (pid %d)", ch->id, pid);
+
+ if (pid == -1) {
+ initlog(L_VB, "cannot fork, retry..");
+ do_sleep(5);
+ continue;
+ }
+ return(pid);
+ }
+}
+
+/*
+ * Start a child running!
+ */
+void startup(CHILD *ch)
+{
+ /*
+ * See if it's disabled
+ */
+ if (ch->flags & FAILING) return;
+
+ switch(ch->action) {
+
+ case SYSINIT:
+ case BOOTWAIT:
+ case WAIT:
+ case POWERWAIT:
+ case POWERFAILNOW:
+ case POWEROKWAIT:
+ case CTRLALTDEL:
+ if (!(ch->flags & XECUTED)) ch->flags |= WAITING;
+ case KBREQUEST:
+ case BOOT:
+ case POWERFAIL:
+ case ONCE:
+ if (ch->flags & XECUTED) break;
+ case ONDEMAND:
+ case RESPAWN:
+ ch->flags |= RUNNING;
+ if (spawn(ch, &(ch->pid)) < 0) break;
+ /*
+ * Do NOT log if process field starts with '+'
+ * FIXME: that's for compatibility with *very*
+ * old getties - probably it can be taken out.
+ */
+ if (ch->process[0] != '+')
+ write_utmp_wtmp("", ch->id, ch->pid,
+ INIT_PROCESS, "");
+ break;
+ }
+}
+
+
+/*
+ * Read the inittab file.
+ */
+void read_inittab(void)
+{
+ FILE *fp; /* The INITTAB file */
+ CHILD *ch, *old, *i; /* Pointers to CHILD structure */
+ CHILD *head = NULL; /* Head of linked list */
+#ifdef INITLVL
+ struct stat st; /* To stat INITLVL */
+#endif
+ sigset_t nmask, omask; /* For blocking SIGCHLD. */
+ char buf[256]; /* Line buffer */
+ char err[64]; /* Error message. */
+ char *id, *rlevel,
+ *action, *process; /* Fields of a line */
+ char *p;
+ int lineNo = 0; /* Line number in INITTAB file */
+ int actionNo; /* Decoded action field */
+ int f; /* Counter */
+ int round; /* round 0 for SIGTERM, 1 for SIGKILL */
+ int foundOne = 0; /* No killing no sleep */
+ int talk; /* Talk to the user */
+ int done = 0; /* Ready yet? */
+
+#if DEBUG
+ if (newFamily != NULL) {
+ INITDBG(L_VB, "PANIC newFamily != NULL");
+ exit(1);
+ }
+ INITDBG(L_VB, "Reading inittab");
+#endif
+
+ /*
+ * Open INITTAB and real line by line.
+ */
+ if ((fp = fopen(INITTAB, "r")) == NULL)
+ initlog(L_VB, "No inittab file found");
+
+ while(!done) {
+ /*
+ * Add single user shell entry at the end.
+ */
+ if (fp == NULL || fgets(buf, sizeof(buf), fp) == NULL) {
+ done = 1;
+ /*
+ * See if we have a single user entry.
+ */
+ for(old = newFamily; old; old = old->next)
+ if (strpbrk(old->rlevel, "S")) break;
+ if (old == NULL)
+ snprintf(buf, sizeof(buf), "~~:S:wait:%s\n", SULOGIN);
+ else
+ continue;
+ }
+ lineNo++;
+ /*
+ * Skip comments and empty lines
+ */
+ for(p = buf; *p == ' ' || *p == '\t'; p++)
+ ;
+ if (*p == '#' || *p == '\n') continue;
+
+ /*
+ * Decode the fields
+ */
+ id = strsep(&p, ":");
+ rlevel = strsep(&p, ":");
+ action = strsep(&p, ":");
+ process = strsep(&p, "\n");
+
+ /*
+ * Check if syntax is OK. Be very verbose here, to
+ * avoid newbie postings on comp.os.linux.setup :)
+ */
+ err[0] = 0;
+ if (!id || !*id) strcpy(err, "missing id field");
+ if (!rlevel) strcpy(err, "missing runlevel field");
+ if (!process) strcpy(err, "missing process field");
+ if (!action || !*action)
+ strcpy(err, "missing action field");
+ if (id && strlen(id) > sizeof(utproto.ut_id))
+ sprintf(err, "id field too long (max %d characters)",
+ (int)sizeof(utproto.ut_id));
+ if (rlevel && strlen(rlevel) > 11)
+ strcpy(err, "rlevel field too long (max 11 characters)");
+ if (process && strlen(process) > 127)
+ strcpy(err, "process field too long");
+ if (action && strlen(action) > 32)
+ strcpy(err, "action field too long");
+ if (err[0] != 0) {
+ initlog(L_VB, "%s[%d]: %s", INITTAB, lineNo, err);
+ INITDBG(L_VB, "%s:%s:%s:%s", id, rlevel, action, process);
+ continue;
+ }
+
+ /*
+ * Decode the "action" field
+ */
+ actionNo = -1;
+ for(f = 0; actions[f].name; f++)
+ if (strcasecmp(action, actions[f].name) == 0) {
+ actionNo = actions[f].act;
+ break;
+ }
+ if (actionNo == -1) {
+ initlog(L_VB, "%s[%d]: %s: unknown action field",
+ INITTAB, lineNo, action);
+ continue;
+ }
+
+ /*
+ * See if the id field is unique
+ */
+ for(old = newFamily; old; old = old->next) {
+ if(strcmp(old->id, id) == 0 && strcmp(id, "~~")) {
+ initlog(L_VB, "%s[%d]: duplicate ID field \"%s\"",
+ INITTAB, lineNo, id);
+ break;
+ }
+ }
+ if (old) continue;
+
+ /*
+ * Allocate a CHILD structure
+ */
+ ch = imalloc(sizeof(CHILD));
+
+ /*
+ * And fill it in.
+ */
+ ch->action = actionNo;
+ strncpy(ch->id, id, sizeof(utproto.ut_id) + 1); /* Hack for different libs. */
+ strncpy(ch->process, process, sizeof(ch->process) - 1);
+ if (rlevel[0]) {
+ for(f = 0; f < sizeof(rlevel) - 1 && rlevel[f]; f++) {
+ ch->rlevel[f] = rlevel[f];
+ if (ch->rlevel[f] == 's') ch->rlevel[f] = 'S';
+ }
+ strncpy(ch->rlevel, rlevel, sizeof(ch->rlevel) - 1);
+ } else {
+ strcpy(ch->rlevel, "0123456789");
+ if (ISPOWER(ch->action))
+ strcpy(ch->rlevel, "S0123456789");
+ }
+ /*
+ * We have the fake runlevel '#' for SYSINIT and
+ * '*' for BOOT and BOOTWAIT.
+ */
+ if (ch->action == SYSINIT) strcpy(ch->rlevel, "#");
+ if (ch->action == BOOT || ch->action == BOOTWAIT)
+ strcpy(ch->rlevel, "*");
+
+ /*
+ * Now add it to the linked list. Special for powerfail.
+ */
+ if (ISPOWER(ch->action)) {
+
+ /*
+ * Disable by default
+ */
+ ch->flags |= XECUTED;
+
+ /*
+ * Tricky: insert at the front of the list..
+ */
+ old = NULL;
+ for(i = newFamily; i; i = i->next) {
+ if (!ISPOWER(i->action)) break;
+ old = i;
+ }
+ /*
+ * Now add after entry "old"
+ */
+ if (old) {
+ ch->next = i;
+ old->next = ch;
+ if (i == NULL) head = ch;
+ } else {
+ ch->next = newFamily;
+ newFamily = ch;
+ if (ch->next == NULL) head = ch;
+ }
+ } else {
+ /*
+ * Just add at end of the list
+ */
+ if (ch->action == KBREQUEST) ch->flags |= XECUTED;
+ ch->next = NULL;
+ if (head)
+ head->next = ch;
+ else
+ newFamily = ch;
+ head = ch;
+ }
+
+ /*
+ * Walk through the old list comparing id fields
+ */
+ for(old = family; old; old = old->next)
+ if (strcmp(old->id, ch->id) == 0) {
+ old->new = ch;
+ break;
+ }
+ }
+ /*
+ * We're done.
+ */
+ if (fp) fclose(fp);
+
+ /*
+ * Loop through the list of children, and see if they need to
+ * be killed.
+ */
+
+ INITDBG(L_VB, "Checking for children to kill");
+ for(round = 0; round < 2; round++) {
+ talk = 1;
+ for(ch = family; ch; ch = ch->next) {
+ ch->flags &= ~KILLME;
+
+ /*
+ * Is this line deleted?
+ */
+ if (ch->new == NULL) ch->flags |= KILLME;
+
+ /*
+ * If the entry has changed, kill it anyway. Note that
+ * we do not check ch->process, only the "action" field.
+ * This way, you can turn an entry "off" immediately, but
+ * changes in the command line will only become effective
+ * after the running version has exited.
+ */
+ if (ch->new && ch->action != ch->new->action) ch->flags |= KILLME;
+
+ /*
+ * Only BOOT processes may live in all levels
+ */
+ if (ch->action != BOOT &&
+ strchr(ch->rlevel, runlevel) == NULL) {
+ /*
+ * Ondemand procedures live always,
+ * except in single user
+ */
+ if (runlevel == 'S' || !(ch->flags & DEMAND))
+ ch->flags |= KILLME;
+ }
+
+ /*
+ * Now, if this process may live note so in the new list
+ */
+ if ((ch->flags & KILLME) == 0) {
+ ch->new->flags = ch->flags;
+ ch->new->pid = ch->pid;
+ ch->new->exstat = ch->exstat;
+ continue;
+ }
+
+
+ /*
+ * Is this process still around?
+ */
+ if ((ch->flags & RUNNING) == 0) {
+ ch->flags &= ~KILLME;
+ continue;
+ }
+ INITDBG(L_VB, "Killing \"%s\"", ch->process);
+ switch(round) {
+ case 0: /* Send TERM signal */
+ if (talk)
+ initlog(L_CO,
+ "Sending processes the TERM signal");
+ kill(-(ch->pid), SIGTERM);
+ foundOne = 1;
+ break;
+ case 1: /* Send KILL signal and collect status */
+ if (talk)
+ initlog(L_CO,
+ "Sending processes the KILL signal");
+ kill(-(ch->pid), SIGKILL);
+ break;
+ }
+ talk = 0;
+
+ }
+ /*
+ * See if we have to wait 5 seconds
+ */
+ if (foundOne && round == 0) {
+ /*
+ * Yup, but check every second if we still have children.
+ */
+ for(f = 0; f < sltime; f++) {
+ for(ch = family; ch; ch = ch->next) {
+ if (!(ch->flags & KILLME)) continue;
+ if ((ch->flags & RUNNING) && !(ch->flags & ZOMBIE))
+ break;
+ }
+ if (ch == NULL) {
+ /*
+ * No running children, skip SIGKILL
+ */
+ round = 1;
+ foundOne = 0; /* Skip the sleep below. */
+ break;
+ }
+ do_sleep(1);
+ }
+ }
+ }
+
+ /*
+ * Now give all processes the chance to die and collect exit statuses.
+ */
+ if (foundOne) do_sleep(1);
+ for(ch = family; ch; ch = ch->next)
+ if (ch->flags & KILLME) {
+ if (!(ch->flags & ZOMBIE))
+ initlog(L_CO, "Pid %d [id %s] seems to hang", ch->pid,
+ ch->id);
+ else {
+ INITDBG(L_VB, "Updating utmp for pid %d [id %s]",
+ ch->pid, ch->id);
+ ch->flags &= ~RUNNING;
+ if (ch->process[0] != '+')
+ write_utmp_wtmp("", ch->id, ch->pid, DEAD_PROCESS, NULL);
+ }
+ }
+
+ /*
+ * Both rounds done; clean up the list.
+ */
+ sigemptyset(&nmask);
+ sigaddset(&nmask, SIGCHLD);
+ sigprocmask(SIG_BLOCK, &nmask, &omask);
+ for(ch = family; ch; ch = old) {
+ old = ch->next;
+ free(ch);
+ }
+ family = newFamily;
+ for(ch = family; ch; ch = ch->next) ch->new = NULL;
+ newFamily = NULL;
+ sigprocmask(SIG_SETMASK, &omask, NULL);
+
+#ifdef INITLVL
+ /*
+ * Dispose of INITLVL file.
+ */
+ if (lstat(INITLVL, &st) >= 0 && S_ISLNK(st.st_mode)) {
+ /*
+ * INITLVL is a symbolic link, so just truncate the file.
+ */
+ close(open(INITLVL, O_WRONLY|O_TRUNC));
+ } else {
+ /*
+ * Delete INITLVL file.
+ */
+ unlink(INITLVL);
+ }
+#endif
+#ifdef INITLVL2
+ /*
+ * Dispose of INITLVL2 file.
+ */
+ if (lstat(INITLVL2, &st) >= 0 && S_ISLNK(st.st_mode)) {
+ /*
+ * INITLVL2 is a symbolic link, so just truncate the file.
+ */
+ close(open(INITLVL2, O_WRONLY|O_TRUNC));
+ } else {
+ /*
+ * Delete INITLVL2 file.
+ */
+ unlink(INITLVL2);
+ }
+#endif
+}
+
+/*
+ * Walk through the family list and start up children.
+ * The entries that do not belong here at all are removed
+ * from the list.
+ */
+void start_if_needed(void)
+{
+ CHILD *ch; /* Pointer to child */
+ int delete; /* Delete this entry from list? */
+
+ INITDBG(L_VB, "Checking for children to start");
+
+ for(ch = family; ch; ch = ch->next) {
+
+#if DEBUG
+ if (ch->rlevel[0] == 'C') {
+ INITDBG(L_VB, "%s: flags %d", ch->process, ch->flags);
+ }
+#endif
+
+ /* Are we waiting for this process? Then quit here. */
+ if (ch->flags & WAITING) break;
+
+ /* Already running? OK, don't touch it */
+ if (ch->flags & RUNNING) continue;
+
+ /* See if we have to start it up */
+ delete = 1;
+ if (strchr(ch->rlevel, runlevel) ||
+ ((ch->flags & DEMAND) && !strchr("#*Ss", runlevel))) {
+ startup(ch);
+ delete = 0;
+ }
+
+ if (delete) {
+ /* FIXME: is this OK? */
+ ch->flags &= ~(RUNNING|WAITING);
+ if (!ISPOWER(ch->action) && ch->action != KBREQUEST)
+ ch->flags &= ~XECUTED;
+ ch->pid = 0;
+ } else
+ /* Do we have to wait for this process? */
+ if (ch->flags & WAITING) break;
+ }
+ /* Done. */
+}
+
+/*
+ * Ask the user on the console for a runlevel
+ */
+int ask_runlevel(void)
+{
+ const char prompt[] = "\nEnter runlevel: ";
+ char buf[8];
+ int lvl = -1;
+ int fd;
+
+ console_stty();
+ fd = console_open(O_RDWR|O_NOCTTY);
+
+ if (fd < 0) return('S');
+
+ while(!strchr("0123456789S", lvl)) {
+ write(fd, prompt, sizeof(prompt) - 1);
+ buf[0] = 0;
+ read(fd, buf, sizeof(buf));
+ if (buf[0] != 0 && (buf[1] == '\r' || buf[1] == '\n'))
+ lvl = buf[0];
+ if (islower(lvl)) lvl = toupper(lvl);
+ }
+ close(fd);
+ return lvl;
+}
+
+/*
+ * Search the INITTAB file for the 'initdefault' field, with the default
+ * runlevel. If this fails, ask the user to supply a runlevel.
+ */
+int get_init_default(void)
+{
+ CHILD *ch;
+ int lvl = -1;
+ char *p;
+
+ /*
+ * Look for initdefault.
+ */
+ for(ch = family; ch; ch = ch->next)
+ if (ch->action == INITDEFAULT) {
+ p = ch->rlevel;
+ while(*p) {
+ if (*p > lvl) lvl = *p;
+ p++;
+ }
+ break;
+ }
+ /*
+ * See if level is valid
+ */
+ if (lvl > 0) {
+ if (islower(lvl)) lvl = toupper(lvl);
+ if (strchr("0123456789S", lvl) == NULL) {
+ initlog(L_VB,
+ "Initdefault level '%c' is invalid", lvl);
+ lvl = 0;
+ }
+ }
+ /*
+ * Ask for runlevel on console if needed.
+ */
+ if (lvl <= 0) lvl = ask_runlevel();
+
+ /*
+ * Log the fact that we have a runlevel now.
+ */
+ return lvl;
+}
+
+
+/*
+ * We got signaled.
+ *
+ * Do actions for the new level. If we are compatible with
+ * the "old" INITLVL and arg == 0, try to read the new
+ * runlevel from that file first.
+ */
+int read_level(int arg)
+{
+ CHILD *ch; /* Walk through list */
+ unsigned char foo = 'X'; /* Contents of INITLVL */
+ int ok = 1;
+#ifdef INITLVL
+ FILE *fp;
+ struct stat stt;
+ int st;
+#endif
+
+ if (arg) foo = arg;
+
+#ifdef INITLVL
+ ok = 0;
+
+ if (arg == 0) {
+ fp = NULL;
+ if (stat(INITLVL, &stt) != 0 || stt.st_size != 0L)
+ fp = fopen(INITLVL, "r");
+#ifdef INITLVL2
+ if (fp == NULL &&
+ (stat(INITLVL2, &stt) != 0 || stt.st_size != 0L))
+ fp = fopen(INITLVL2, "r");
+#endif
+ if (fp == NULL) {
+ /* INITLVL file empty or not there - act as 'init q' */
+ initlog(L_SY, "Re-reading inittab");
+ return(runlevel);
+ }
+ ok = fscanf(fp, "%c %d", &foo, &st);
+ fclose(fp);
+ } else {
+ /* We go to the new runlevel passed as an argument. */
+ foo = arg;
+ ok = 1;
+ }
+ if (ok == 2) sltime = st;
+
+#endif /* INITLVL */
+
+ if (islower(foo)) foo = toupper(foo);
+ if (ok < 1 || ok > 2 || strchr("QS0123456789ABCU", foo) == NULL) {
+ initlog(L_VB, "bad runlevel: %c", foo);
+ return runlevel;
+ }
+
+ /* Log this action */
+ switch(foo) {
+ case 'S':
+ initlog(L_VB, "Going single user");
+ break;
+ case 'Q':
+ initlog(L_SY, "Re-reading inittab");
+ break;
+ case 'A':
+ case 'B':
+ case 'C':
+ initlog(L_SY,
+ "Activating demand-procedures for '%c'", foo);
+ break;
+ case 'U':
+ initlog(L_SY, "Trying to re-exec init");
+ return 'U';
+ default:
+ initlog(L_VB, "Switching to runlevel: %c", foo);
+ }
+
+ if (foo == 'Q') return runlevel;
+
+ /* Check if this is a runlevel a, b or c */
+ if (strchr("ABC", foo)) {
+ if (runlevel == 'S') return(runlevel);
+
+ /* Read inittab again first! */
+ read_inittab();
+
+ /* Mark those special tasks */
+ for(ch = family; ch; ch = ch->next)
+ if (strchr(ch->rlevel, foo) != NULL ||
+ strchr(ch->rlevel, tolower(foo)) != NULL) {
+ ch->flags |= DEMAND;
+ ch->flags &= ~XECUTED;
+ INITDBG(L_VB,
+ "Marking (%s) as ondemand, flags %d",
+ ch->id, ch->flags);
+ }
+ return runlevel;
+ }
+
+ /* Store both the old and the new runlevel. */
+ write_utmp_wtmp("runlevel", "~~", foo + 256*runlevel, RUN_LVL, "~");
+ thislevel = foo;
+ prevlevel = runlevel;
+ return foo;
+}
+
+
+/*
+ * This procedure is called after every signal (SIGHUP, SIGALRM..)
+ *
+ * Only clear the 'failing' flag if the process is sleeping
+ * longer than 5 minutes, or inittab was read again due
+ * to user interaction.
+ */
+void fail_check(void)
+{
+ CHILD *ch; /* Pointer to child structure */
+ time_t t; /* System time */
+ time_t next_alarm = 0; /* When to set next alarm */
+
+ time(&t);
+
+ for(ch = family; ch; ch = ch->next) {
+
+ if (ch->flags & FAILING) {
+ /* Can we free this sucker? */
+ if (ch->tm + SLEEPTIME < t) {
+ ch->flags &= ~FAILING;
+ ch->count = 0;
+ ch->tm = 0;
+ } else {
+ /* No, we'll look again later */
+ if (next_alarm == 0 ||
+ ch->tm + SLEEPTIME > next_alarm)
+ next_alarm = ch->tm + SLEEPTIME;
+ }
+ }
+ }
+ if (next_alarm) {
+ next_alarm -= t;
+ if (next_alarm < 1) next_alarm = 1;
+ alarm(next_alarm);
+ }
+}
+
+/* Set all 'Fail' timers to 0 */
+void fail_cancel(void)
+{
+ CHILD *ch;
+
+ for(ch = family; ch; ch = ch->next) {
+ ch->count = 0;
+ ch->tm = 0;
+ ch->flags &= ~FAILING;
+ }
+}
+
+/*
+ * Start up powerfail entries.
+ */
+void do_power_fail(int pwrstat)
+{
+ CHILD *ch;
+
+ /*
+ * Tell powerwait & powerfail entries to start up
+ */
+ for (ch = family; ch; ch = ch->next) {
+ if (pwrstat == 'O') {
+ /*
+ * The power is OK again.
+ */
+ if (ch->action == POWEROKWAIT)
+ ch->flags &= ~XECUTED;
+ } else if (pwrstat == 'L') {
+ /*
+ * Low battery, shut down now.
+ */
+ if (ch->action == POWERFAILNOW)
+ ch->flags &= ~XECUTED;
+ } else {
+ /*
+ * Power is failing, shutdown imminent
+ */
+ if (ch->action == POWERFAIL || ch->action == POWERWAIT)
+ ch->flags &= ~XECUTED;
+ }
+ }
+}
+
+/*
+ * Check for state-pipe presence
+ */
+int check_pipe(int fd)
+{
+ struct timeval t;
+ fd_set s;
+ char signature[8];
+
+ FD_ZERO(&s);
+ FD_SET(fd, &s);
+ t.tv_sec = t.tv_usec = 0;
+
+ if (select(fd+1, &s, NULL, NULL, &t) != 1)
+ return 0;
+ if (read(fd, signature, 8) != 8)
+ return 0;
+ return strncmp(Signature, signature, 8) == 0;
+}
+
+/*
+ * Make a state-pipe.
+ */
+int make_pipe(int fd)
+{
+ int fds[2];
+
+ pipe(fds);
+ dup2(fds[0], fd);
+ close(fds[0]);
+ fcntl(fds[1], F_SETFD, 1);
+ fcntl(fd, F_SETFD, 0);
+ write(fds[1], Signature, 8);
+
+ return fds[1];
+}
+
+/*
+ * Attempt to re-exec.
+ */
+void re_exec(void)
+{
+ CHILD *ch;
+ sigset_t mask, oldset;
+ pid_t pid;
+ char **env;
+ int fd;
+
+ if (strchr("S0123456",runlevel) == NULL)
+ return;
+
+ /*
+ * Reset the alarm, and block all signals.
+ */
+ alarm(0);
+ sigfillset(&mask);
+ sigprocmask(SIG_BLOCK, &mask, &oldset);
+
+ /*
+ * construct a pipe fd --> STATE_PIPE and write a signature
+ */
+ fd = make_pipe(STATE_PIPE);
+
+ /*
+ * It's a backup day today, so I'm pissed off. Being a BOFH, however,
+ * does have it's advantages...
+ */
+ fail_cancel();
+ close(pipe_fd);
+ pipe_fd = -1;
+ DELSET(got_signals, SIGCHLD);
+ DELSET(got_signals, SIGHUP);
+ DELSET(got_signals, SIGUSR1);
+
+ /*
+ * That should be cleaned.
+ */
+ for(ch = family; ch; ch = ch->next)
+ if (ch->flags & ZOMBIE) {
+ INITDBG(L_VB, "Child died, PID= %d", ch->pid);
+ ch->flags &= ~(RUNNING|ZOMBIE|WAITING);
+ if (ch->process[0] != '+')
+ write_utmp_wtmp("", ch->id, ch->pid, DEAD_PROCESS, NULL);
+ }
+
+ if ((pid = fork()) == 0) {
+ /*
+ * Child sends state information to the parent.
+ */
+ send_state(fd);
+ exit(0);
+ }
+
+ /*
+ * The existing init process execs a new init binary.
+ */
+ env = init_buildenv(0);
+ execle(myname, myname, "--init", NULL, env);
+
+ /*
+ * We shouldn't be here, something failed.
+ * Bitch, close the state pipe, unblock signals and return.
+ */
+ close(fd);
+ close(STATE_PIPE);
+ sigprocmask(SIG_SETMASK, &oldset, NULL);
+ init_freeenv(env);
+ initlog(L_CO, "Attempt to re-exec failed");
+}
+
+
+/*
+ * We got a change runlevel request through the
+ * init.fifo. Process it.
+ */
+void fifo_new_level(int level)
+{
+#if CHANGE_WAIT
+ CHILD *ch;
+#endif
+ int oldlevel;
+
+ if (level == runlevel) return;
+
+#if CHANGE_WAIT
+ /* Are we waiting for a child? */
+ for(ch = family; ch; ch = ch->next)
+ if (ch->flags & WAITING) break;
+ if (ch == NULL)
+#endif
+ {
+ /* We need to go into a new runlevel */
+ oldlevel = runlevel;
+ runlevel = read_level(level);
+ if (runlevel == 'U') {
+ runlevel = oldlevel;
+ re_exec();
+ } else {
+ if (oldlevel != 'S' && runlevel == 'S') console_stty();
+ if (runlevel == '6' || runlevel == '0' ||
+ runlevel == '1') console_stty();
+ read_inittab();
+ fail_cancel();
+ setproctitle("init [%c]", runlevel);
+ }
+ }
+}
+
+
+/*
+ * Set/unset environment variables. The variables are
+ * encoded as KEY=VAL\0KEY=VAL\0\0. With "=VAL" it means
+ * setenv, without it means unsetenv.
+ */
+void initcmd_setenv(char *data, int size)
+{
+ char *env, *p, *e, *eq;
+ int i, sz;
+
+ e = data + size;
+
+ while (*data && data < e) {
+ eq = NULL;
+ for (p = data; *p && p < e; p++)
+ if (*p == '=') eq = p;
+ if (*p) break;
+ env = data;
+ data = ++p;
+
+ sz = eq ? (eq - env) : (p - env);
+
+ /*initlog(L_SY, "init_setenv: %s, %s, %d", env, eq, sz);*/
+
+ /*
+ * We only allow INIT_* to be set.
+ */
+ if (strncmp(env, "INIT_", 5) != 0)
+ continue;
+
+ /* Free existing vars. */
+ for (i = 0; i < NR_EXTRA_ENV; i++) {
+ if (extra_env[i] == NULL) continue;
+ if (!strncmp(extra_env[i], env, sz) &&
+ extra_env[i][sz] == '=') {
+ free(extra_env[i]);
+ extra_env[i] = NULL;
+ }
+ }
+
+ /* Set new vars if needed. */
+ if (eq == NULL) continue;
+ for (i = 0; i < NR_EXTRA_ENV; i++) {
+ if (extra_env[i] == NULL) {
+ extra_env[i] = istrdup(env);
+ break;
+ }
+ }
+ }
+}
+
+
+/*
+ * Read from the init FIFO. Processes like telnetd and rlogind can
+ * ask us to create login processes on their behalf.
+ *
+ * FIXME: this needs to be finished. NOT that it is buggy, but we need
+ * to add the telnetd/rlogind stuff so people can start using it.
+ * Maybe move to using an AF_UNIX socket so we can use
+ * the 2.2 kernel credential stuff to see who we're talking to.
+ *
+ */
+void check_init_fifo(void)
+{
+ struct init_request request;
+ struct timeval tv;
+ struct stat st, st2;
+ fd_set fds;
+ int n;
+ int quit = 0;
+
+ /*
+ * First, try to create /dev/initctl if not present.
+ */
+ if (stat(INIT_FIFO, &st2) < 0 && errno == ENOENT)
+ (void)mkfifo(INIT_FIFO, 0600);
+
+ /*
+ * If /dev/initctl is open, stat the file to see if it
+ * is still the _same_ inode.
+ */
+ if (pipe_fd >= 0) {
+ fstat(pipe_fd, &st);
+ if (stat(INIT_FIFO, &st2) < 0 ||
+ st.st_dev != st2.st_dev ||
+ st.st_ino != st2.st_ino) {
+ close(pipe_fd);
+ pipe_fd = -1;
+ }
+ }
+
+ /*
+ * Now finally try to open /dev/initctl
+ */
+ if (pipe_fd < 0) {
+ if ((pipe_fd = open(INIT_FIFO, O_RDWR|O_NONBLOCK)) >= 0) {
+ fstat(pipe_fd, &st);
+ if (!S_ISFIFO(st.st_mode)) {
+ initlog(L_VB, "%s is not a fifo", INIT_FIFO);
+ close(pipe_fd);
+ pipe_fd = -1;
+ }
+ }
+ if (pipe_fd >= 0) {
+ /*
+ * Don't use fd's 0, 1 or 2.
+ */
+ (void) dup2(pipe_fd, PIPE_FD);
+ close(pipe_fd);
+ pipe_fd = PIPE_FD;
+
+ /*
+ * Return to caller - we'll be back later.
+ */
+ }
+ }
+
+ /* Wait for data to appear, _if_ the pipe was opened. */
+ if (pipe_fd >= 0) while(!quit) {
+
+ /* Do select, return on EINTR. */
+ FD_ZERO(&fds);
+ FD_SET(pipe_fd, &fds);
+ tv.tv_sec = 5;
+ tv.tv_usec = 0;
+ n = select(pipe_fd + 1, &fds, NULL, NULL, &tv);
+ if (n <= 0) {
+ if (n == 0 || errno == EINTR) return;
+ continue;
+ }
+
+ /* Read the data, return on EINTR. */
+ n = read(pipe_fd, &request, sizeof(request));
+ if (n == 0) {
+ /*
+ * End of file. This can't happen under Linux (because
+ * the pipe is opened O_RDWR - see select() in the
+ * kernel) but you never know...
+ */
+ close(pipe_fd);
+ pipe_fd = -1;
+ return;
+ }
+ if (n <= 0) {
+ if (errno == EINTR) return;
+ initlog(L_VB, "error reading initrequest");
+ continue;
+ }
+
+ /*
+ * This is a convenient point to also try to
+ * find the console device or check if it changed.
+ */
+ console_init();
+
+ /*
+ * Process request.
+ */
+ if (request.magic != INIT_MAGIC || n != sizeof(request)) {
+ initlog(L_VB, "got bogus initrequest");
+ continue;
+ }
+ switch(request.cmd) {
+ case INIT_CMD_RUNLVL:
+ sltime = request.sleeptime;
+ fifo_new_level(request.runlevel);
+ quit = 1;
+ break;
+ case INIT_CMD_POWERFAIL:
+ sltime = request.sleeptime;
+ do_power_fail('F');
+ quit = 1;
+ break;
+ case INIT_CMD_POWERFAILNOW:
+ sltime = request.sleeptime;
+ do_power_fail('L');
+ quit = 1;
+ break;
+ case INIT_CMD_POWEROK:
+ sltime = request.sleeptime;
+ do_power_fail('O');
+ quit = 1;
+ break;
+ case INIT_CMD_SETENV:
+ initcmd_setenv(request.i.data, sizeof(request.i.data));
+ break;
+ case INIT_CMD_CHANGECONS:
+ if (user_console) {
+ free(user_console);
+ user_console = NULL;
+ }
+ if (!request.i.bsd.reserved[0])
+ user_console = NULL;
+ else
+ user_console = strdup(request.i.bsd.reserved);
+ console_init();
+ quit = 1;
+ break;
+ default:
+ initlog(L_VB, "got unimplemented initrequest.");
+ break;
+ }
+ }
+
+ /*
+ * We come here if the pipe couldn't be opened.
+ */
+ if (pipe_fd < 0) pause();
+
+}
+
+
+/*
+ * This function is used in the transition
+ * sysinit (-> single user) boot -> multi-user.
+ */
+void boot_transitions()
+{
+ CHILD *ch;
+ static int newlevel = 0;
+ static int warn = 1;
+ int loglevel;
+ int oldlevel;
+
+ /* Check if there is something to wait for! */
+ for( ch = family; ch; ch = ch->next )
+ if ((ch->flags & RUNNING) && ch->action != BOOT) break;
+
+ if (ch == NULL) {
+ /* No processes left in this level, proceed to next level. */
+ loglevel = -1;
+ oldlevel = 'N';
+ switch(runlevel) {
+ case '#': /* SYSINIT -> BOOT */
+ INITDBG(L_VB, "SYSINIT -> BOOT");
+
+ /* Write a boot record. */
+ wrote_utmp_reboot = 0;
+ wrote_wtmp_reboot = 0;
+ write_utmp_wtmp("reboot", "~~", 0, BOOT_TIME, "~");
+
+ /* Get our run level */
+ newlevel = dfl_level ? dfl_level : get_init_default();
+ if (newlevel == 'S') {
+ runlevel = newlevel;
+ /* Not really 'S' but show anyway. */
+ setproctitle("init [S]");
+ } else
+ runlevel = '*';
+ break;
+ case '*': /* BOOT -> NORMAL */
+ INITDBG(L_VB, "BOOT -> NORMAL");
+ if (runlevel != newlevel)
+ loglevel = newlevel;
+ runlevel = newlevel;
+ did_boot = 1;
+ warn = 1;
+ break;
+ case 'S': /* Ended SU mode */
+ case 's':
+ INITDBG(L_VB, "END SU MODE");
+ newlevel = get_init_default();
+ if (!did_boot && newlevel != 'S')
+ runlevel = '*';
+ else {
+ if (runlevel != newlevel)
+ loglevel = newlevel;
+ runlevel = newlevel;
+ oldlevel = 'S';
+ }
+ warn = 1;
+ for(ch = family; ch; ch = ch->next)
+ if (strcmp(ch->rlevel, "S") == 0)
+ ch->flags &= ~(FAILING|WAITING|XECUTED);
+ break;
+ default:
+ if (warn)
+ initlog(L_VB,
+ "no more processes left in this runlevel");
+ warn = 0;
+ loglevel = -1;
+ if (got_signals == 0)
+ check_init_fifo();
+ break;
+ }
+ if (loglevel > 0) {
+ initlog(L_VB, "Entering runlevel: %c", runlevel);
+ write_utmp_wtmp("runlevel", "~~", runlevel + 256 * oldlevel, RUN_LVL, "~");
+ thislevel = runlevel;
+ prevlevel = oldlevel;
+ setproctitle("init [%c]", runlevel);
+ }
+ }
+}
+
+/*
+ * Init got hit by a signal. See which signal it is,
+ * and act accordingly.
+ */
+void process_signals()
+{
+ CHILD *ch;
+ int pwrstat;
+ int oldlevel;
+ int fd;
+ char c;
+
+ if (ISMEMBER(got_signals, SIGPWR)) {
+ INITDBG(L_VB, "got SIGPWR");
+ /* See _what_ kind of SIGPWR this is. */
+ pwrstat = 0;
+ if ((fd = open(PWRSTAT, O_RDONLY)) >= 0) {
+ c = 0;
+ read(fd, &c, 1);
+ pwrstat = c;
+ close(fd);
+ unlink(PWRSTAT);
+ }
+ do_power_fail(pwrstat);
+ DELSET(got_signals, SIGPWR);
+ }
+
+ if (ISMEMBER(got_signals, SIGINT)) {
+ INITDBG(L_VB, "got SIGINT");
+ /* Tell ctrlaltdel entry to start up */
+ for(ch = family; ch; ch = ch->next)
+ if (ch->action == CTRLALTDEL)
+ ch->flags &= ~XECUTED;
+ DELSET(got_signals, SIGINT);
+ }
+
+ if (ISMEMBER(got_signals, SIGWINCH)) {
+ INITDBG(L_VB, "got SIGWINCH");
+ /* Tell kbrequest entry to start up */
+ for(ch = family; ch; ch = ch->next)
+ if (ch->action == KBREQUEST)
+ ch->flags &= ~XECUTED;
+ DELSET(got_signals, SIGWINCH);
+ }
+
+ if (ISMEMBER(got_signals, SIGALRM)) {
+ INITDBG(L_VB, "got SIGALRM");
+ /* The timer went off: check it out */
+ DELSET(got_signals, SIGALRM);
+ }
+
+ if (ISMEMBER(got_signals, SIGCHLD)) {
+ INITDBG(L_VB, "got SIGCHLD");
+ /* First set flag to 0 */
+ DELSET(got_signals, SIGCHLD);
+
+ /* See which child this was */
+ for(ch = family; ch; ch = ch->next)
+ if (ch->flags & ZOMBIE) {
+ INITDBG(L_VB, "Child died, PID= %d", ch->pid);
+ ch->flags &= ~(RUNNING|ZOMBIE|WAITING);
+ if (ch->process[0] != '+')
+ write_utmp_wtmp("", ch->id, ch->pid, DEAD_PROCESS, NULL);
+ }
+
+ }
+
+ if (ISMEMBER(got_signals, SIGHUP)) {
+ INITDBG(L_VB, "got SIGHUP");
+#if CHANGE_WAIT
+ /* Are we waiting for a child? */
+ for(ch = family; ch; ch = ch->next)
+ if (ch->flags & WAITING) break;
+ if (ch == NULL)
+#endif
+ {
+ /* We need to go into a new runlevel */
+ oldlevel = runlevel;
+#ifdef INITLVL
+ runlevel = read_level(0);
+#endif
+ if (runlevel == 'U') {
+ runlevel = oldlevel;
+ re_exec();
+ } else {
+ if (oldlevel != 'S' && runlevel == 'S') console_stty();
+ if (runlevel == '6' || runlevel == '0' ||
+ runlevel == '1') console_stty();
+ read_inittab();
+ fail_cancel();
+ setproctitle("init [%c]", runlevel);
+ DELSET(got_signals, SIGHUP);
+ }
+ }
+ }
+ if (ISMEMBER(got_signals, SIGUSR1)) {
+ /*
+ * SIGUSR1 means close and reopen /dev/initctl
+ */
+ INITDBG(L_VB, "got SIGUSR1");
+ close(pipe_fd);
+ pipe_fd = -1;
+ DELSET(got_signals, SIGUSR1);
+ }
+}
+
+/*
+ * The main loop
+ */
+int init_main()
+{
+ CHILD *ch;
+ struct sigaction sa;
+ sigset_t sgt;
+ pid_t rc;
+ int f, st;
+
+ if (!reload) {
+
+#if INITDEBUG
+ /*
+ * Fork so we can debug the init process.
+ */
+ if ((f = fork()) > 0) {
+ static const char killmsg[] = "PRNT: init killed.\r\n";
+ pid_t rc;
+
+ while((rc = wait(&st)) != f)
+ if (rc < 0 && errno == ECHILD)
+ break;
+ write(1, killmsg, sizeof(killmsg) - 1);
+ while(1) pause();
+ }
+#endif
+
+#ifdef __linux__
+ /*
+ * Tell the kernel to send us SIGINT when CTRL-ALT-DEL
+ * is pressed, and that we want to handle keyboard signals.
+ */
+ init_reboot(BMAGIC_SOFT);
+ if ((f = open(VT_MASTER, O_RDWR | O_NOCTTY)) >= 0) {
+ (void) ioctl(f, KDSIGACCEPT, SIGWINCH);
+ close(f);
+ } else