From a74aeac6c89551df48c57e41a8fcb962032363ed Mon Sep 17 00:00:00 2001 From: Petter Reinholdtsen Date: Thu, 10 Sep 2009 08:28:49 +0000 Subject: [PATCH] Drop hurd specific dependency on libc0.3 (>= 2.3.2.ds1-12). It is no longer needed according to Michael Bunk. Patch from Michael Biebl. git-svn-id: svn://svn.sv.gnu.org/sysvinit/sysvinit/trunk@1 456724a4-4300-0410-8514-c89748c515a2 --- COPYING | 339 ++++ COPYRIGHT | 26 + README | 7 + contrib/TODO | 16 + contrib/alexander.viro | 29 + contrib/start-stop-daemon.README | 45 + contrib/start-stop-daemon.c | 430 +++++ contrib/zefram-patches | 120 ++ doc/Changelog | 635 +++++++ doc/Install | 56 + doc/Propaganda | 81 + doc/bootlogd.README | 19 + doc/sysvinit-2.86.lsm | 14 + man/bootlogd.8 | 72 + man/bootlogd.8.todo | 52 + man/halt.8 | 120 ++ man/init.8 | 313 ++++ man/initscript.5 | 72 + man/inittab.5 | 265 +++ man/killall5.8 | 49 + man/last.1 | 122 ++ man/lastb.1 | 1 + man/mesg.1 | 61 + man/mountpoint.1 | 54 + man/pidof.8 | 78 + man/poweroff.8 | 1 + man/reboot.8 | 1 + man/runlevel.8 | 56 + man/shutdown.8 | 214 +++ man/sulogin.8 | 87 + man/telinit.8 | 1 + man/wall.1 | 75 + obsolete/README.RIGHT.NOW | 58 + obsolete/bootlogd.init | 67 + obsolete/powerd.8 | 73 + obsolete/powerd.README | 36 + obsolete/powerd.c | 201 +++ obsolete/powerd.cfg | 58 + obsolete/utmpdump.c.OLD | 70 + src/Makefile | 168 ++ src/bootlogd.c | 657 ++++++++ src/dowall.c | 236 +++ src/halt.c | 312 ++++ src/hddown.c | 512 ++++++ src/ifdown.c | 91 + src/init.c | 2714 ++++++++++++++++++++++++++++++ src/init.h | 139 ++ src/initreq.h | 86 + src/initscript.sample | 25 + src/killall5.c | 741 ++++++++ src/last.c | 911 ++++++++++ src/mesg.c | 124 ++ src/mountpoint.c | 128 ++ src/oldutmp.h | 41 + src/paths.h | 49 + src/reboot.h | 51 + src/runlevel.c | 53 + src/set.h | 28 + src/shutdown.c | 727 ++++++++ src/sulogin.c | 505 ++++++ src/utmp.c | 224 +++ src/utmpdump.c | 298 ++++ src/wall.c | 123 ++ 63 files changed, 13017 insertions(+) create mode 100644 COPYING create mode 100644 COPYRIGHT create mode 100644 README create mode 100644 contrib/TODO create mode 100644 contrib/alexander.viro create mode 100644 contrib/start-stop-daemon.README create mode 100644 contrib/start-stop-daemon.c create mode 100644 contrib/zefram-patches create mode 100644 doc/Changelog create mode 100644 doc/Install create mode 100644 doc/Propaganda create mode 100644 doc/bootlogd.README create mode 100644 doc/sysvinit-2.86.lsm create mode 100644 man/bootlogd.8 create mode 100644 man/bootlogd.8.todo create mode 100644 man/halt.8 create mode 100644 man/init.8 create mode 100644 man/initscript.5 create mode 100644 man/inittab.5 create mode 100644 man/killall5.8 create mode 100644 man/last.1 create mode 100644 man/lastb.1 create mode 100644 man/mesg.1 create mode 100644 man/mountpoint.1 create mode 100644 man/pidof.8 create mode 100644 man/poweroff.8 create mode 100644 man/reboot.8 create mode 100644 man/runlevel.8 create mode 100644 man/shutdown.8 create mode 100644 man/sulogin.8 create mode 100644 man/telinit.8 create mode 100644 man/wall.1 create mode 100644 obsolete/README.RIGHT.NOW create mode 100755 obsolete/bootlogd.init create mode 100644 obsolete/powerd.8 create mode 100644 obsolete/powerd.README create mode 100644 obsolete/powerd.c create mode 100644 obsolete/powerd.cfg create mode 100644 obsolete/utmpdump.c.OLD create mode 100644 src/Makefile create mode 100644 src/bootlogd.c create mode 100644 src/dowall.c create mode 100644 src/halt.c create mode 100644 src/hddown.c create mode 100644 src/ifdown.c create mode 100644 src/init.c create mode 100644 src/init.h create mode 100644 src/initreq.h create mode 100755 src/initscript.sample create mode 100644 src/killall5.c create mode 100644 src/last.c create mode 100644 src/mesg.c create mode 100644 src/mountpoint.c create mode 100644 src/oldutmp.h create mode 100644 src/paths.h create mode 100644 src/reboot.h create mode 100644 src/runlevel.c create mode 100644 src/set.h create mode 100644 src/shutdown.c create mode 100644 src/sulogin.c create mode 100644 src/utmp.c create mode 100644 src/utmpdump.c create mode 100644 src/wall.c diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..d511905 --- /dev/null +++ b/COPYING @@ -0,0 +1,339 @@ + 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. + + + Copyright (C) + + 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. + + , 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. diff --git a/COPYRIGHT b/COPYRIGHT new file mode 100644 index 0000000..536d712 --- /dev/null +++ b/COPYRIGHT @@ -0,0 +1,26 @@ +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 , + * public domain. + diff --git a/README b/README new file mode 100644 index 0000000..2bb8bff --- /dev/null +++ b/README @@ -0,0 +1,7 @@ + +contrib Unofficial stuff +doc Documentation, mostly obsolete +man Manual pages, not obsolete +obsolete Really obsolete stuff ;) +src Source code + diff --git a/contrib/TODO b/contrib/TODO new file mode 100644 index 0000000..6ec1312 --- /dev/null +++ b/contrib/TODO @@ -0,0 +1,16 @@ + +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. + diff --git a/contrib/alexander.viro b/contrib/alexander.viro new file mode 100644 index 0000000..976d09b --- /dev/null +++ b/contrib/alexander.viro @@ -0,0 +1,29 @@ +I proposed moving some stuff to a seperate file, such as the +re-exec routines. Alexander wrote: + + +According to Alexander Viro : +> 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? + diff --git a/contrib/start-stop-daemon.README b/contrib/start-stop-daemon.README new file mode 100644 index 0000000..fb69c08 --- /dev/null +++ b/contrib/start-stop-daemon.README @@ -0,0 +1,45 @@ +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 +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 + diff --git a/contrib/start-stop-daemon.c b/contrib/start-stop-daemon.c new file mode 100644 index 0000000..ce286a2 --- /dev/null +++ b/contrib/start-stop-daemon.c @@ -0,0 +1,430 @@ +/* + * 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 , + * public domain. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 , 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 program to start/check if it is running\n\ + -p|--pidfile pid file to check\n\ + -u|--user | stop this user's processes\n\ + -n|--name stop processes with this name\n\ + -s|--signal signal to send (default 15)\n\ + -a|--startas program to start (default )\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 */ + startas = optarg; + break; + case 'n': /* --name */ + cmdname = optarg; + break; + case 'o': /* --oknodo */ + exitnodo = 0; + break; + case 'p': /* --pidfile */ + pidfile = optarg; + break; + case 'q': /* --quiet */ + quietmode = 1; + break; + case 's': /* --signal */ + if (sscanf(optarg, "%d", &signal_nr) != 1) + badusage("--signal takes a numeric argument"); + break; + case 't': /* --test */ + testmode = 1; + break; + case 'u': /* --user | */ + userspec = optarg; + break; + case 'v': /* --verbose */ + quietmode = -1; + break; + case 'x': /* --exec */ + 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)); +} + diff --git a/contrib/zefram-patches b/contrib/zefram-patches new file mode 100644 index 0000000..9bdf98a --- /dev/null +++ b/contrib/zefram-patches @@ -0,0 +1,120 @@ +Date: Mon, 14 Apr 1997 15:39:08 +0100 (BST) +From: Zefram +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,H@4#IM:!2YA"N0B)URA:%^UGH`DL* +MI+7.6#AW+!I4B.>[^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$_)[[SL+ +M8O_O`FM=@#1+;O![7"`W/H10D1L11131K/^[OE"2+QLWBF+5]%6X^%.,?_A` +M.>;<6SHYWRK''.,J%7"`*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[M9(3`-/2(!C:Q:P]6243B@/4];'O6TZ%VU`E6J$ZTJ`>:Q2PT6(I'9( +M1H7')R^IMOAE_=FKO;MOM?<*XF^CS[;1:;/(AT1A:/.&08A$PA$T#_-1+_PPVW4B].6,[ADV2.R>33YWVY\+$;>I^:K7W<6@M +MZ?O;5J=SD(47XI!^8U1RHGF5<-[X6+7X%E1:2A[/ZCB_M1C]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",TWD_RH&'C:) +M-$'?K*Y3KK&PND1W!G1O@'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\]*&IV'6"DSQ5#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^TX81HDFOQH/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'.=J=KHB\&>5?B&4OS].,0#?8Y[H-TW-=F2[E!N>;2,YM8UV3+YOC*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(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^P0LPSN66%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^TQ%-E116R-X8Q%$[!J2)^UNM]Z(N`J.#'@+'*RX!4+M8R+H_8 +MW-6;KHK4WU("8\"A:N'B81:$I +M,RAJD[03+5O0TD42%HZ2BV"5PMWL8XD`57B_E65<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*!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/#0@#^6\9?]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_D7)7O2-``` +` +end + diff --git a/doc/Changelog b/doc/Changelog new file mode 100644 index 0000000..0b4864a --- /dev/null +++ b/doc/Changelog @@ -0,0 +1,635 @@ +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 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 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 , 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 + * 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 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" ) + * 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 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 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 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 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 Thu, 26 Jul 2001 13:26:56 +0200 + +sysvinit (2.79) cistron; urgency=low + + * New upstream version + * several fixes to wall by Tim Robbins + * 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 ) + + -- Miquel van Smoorenburg 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 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 Thu, 30 Dec 1999 20:40:23 +0100 + +sysvinit (2.77-2) unstable; urgency=low + + * Fix last -i option + + -- Miquel van Smoorenburg 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 for "last" -i option + + -- Miquel van Smoorenburg 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 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 + + -- Miquel van Smoorenburg 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 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 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 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 and + David Parrish + * 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 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 + + -- Miquel van Smoorenburg 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 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 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 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 Wed, 22 Oct 1997 14:44:00 +0200 + +sysvinit (2.72) unstable; urgency=low + + * Applied manual page patches by Bill Hawes . Thanks Bill! + * Applied patches to the sample Slackware scripts by + "Jonathan I. Kamens" + * 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 ) + * Compress all manpages (missed 2) + * Compiled for libc6 + * Added poweroff patch by Roderich Schupp + + -- Miquel van Smoorenburg 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 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 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 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 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 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 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 . +- 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. + diff --git a/doc/Install b/doc/Install new file mode 100644 index 0000000..05f23a2 --- /dev/null +++ b/doc/Install @@ -0,0 +1,56 @@ + + 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 diff --git a/doc/Propaganda b/doc/Propaganda new file mode 100644 index 0000000..072aa13 --- /dev/null +++ b/doc/Propaganda @@ -0,0 +1,81 @@ + + + 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) + diff --git a/doc/bootlogd.README b/doc/bootlogd.README new file mode 100644 index 0000000..52f9638 --- /dev/null +++ b/doc/bootlogd.README @@ -0,0 +1,19 @@ + +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. + diff --git a/doc/sysvinit-2.86.lsm b/doc/sysvinit-2.86.lsm new file mode 100644 index 0000000..3b985a5 --- /dev/null +++ b/doc/sysvinit-2.86.lsm @@ -0,0 +1,14 @@ +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 diff --git a/man/bootlogd.8 b/man/bootlogd.8 new file mode 100644 index 0000000..cab345b --- /dev/null +++ b/man/bootlogd.8 @@ -0,0 +1,72 @@ +'\" -*- 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). diff --git a/man/bootlogd.8.todo b/man/bootlogd.8.todo new file mode 100644 index 0000000..4f24cfc --- /dev/null +++ b/man/bootlogd.8.todo @@ -0,0 +1,52 @@ +'\" -*- 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) diff --git a/man/halt.8 b/man/halt.8 new file mode 100644 index 0000000..8ae3c63 --- /dev/null +++ b/man/halt.8 @@ -0,0 +1,120 @@ +'\" -*- 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) +.\"}}} diff --git a/man/init.8 b/man/init.8 new file mode 100644 index 0000000..d106a16 --- /dev/null +++ b/man/init.8 @@ -0,0 +1,313 @@ +'\" -*- 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) +.\"}}} diff --git a/man/initscript.5 b/man/initscript.5 new file mode 100644 index 0000000..875879c --- /dev/null +++ b/man/initscript.5 @@ -0,0 +1,72 @@ +'\" -*- 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 +# + + # 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 , +.SH "SEE ALSO" +init(8), inittab(5). diff --git a/man/inittab.5 b/man/inittab.5 new file mode 100644 index 0000000..e1d739d --- /dev/null +++ b/man/inittab.5 @@ -0,0 +1,265 @@ +'\" -*- 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) +.\"}}} diff --git a/man/killall5.8 b/man/killall5.8 new file mode 100644 index 0000000..5a3009f --- /dev/null +++ b/man/killall5.8 @@ -0,0 +1,49 @@ +'\" -*- 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 diff --git a/man/last.1 b/man/last.1 new file mode 100644 index 0000000..01b79b2 --- /dev/null +++ b/man/last.1 @@ -0,0 +1,122 @@ +'\" -*- 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) +.\"}}} diff --git a/man/lastb.1 b/man/lastb.1 new file mode 100644 index 0000000..f57c02a --- /dev/null +++ b/man/lastb.1 @@ -0,0 +1 @@ +.so man1/last.1 diff --git a/man/mesg.1 b/man/mesg.1 new file mode 100644 index 0000000..e33c348 --- /dev/null +++ b/man/mesg.1 @@ -0,0 +1,61 @@ +'\" -*- 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) +.\"}}} diff --git a/man/mountpoint.1 b/man/mountpoint.1 new file mode 100644 index 0000000..e873e78 --- /dev/null +++ b/man/mountpoint.1 @@ -0,0 +1,54 @@ +'\" -*- 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) diff --git a/man/pidof.8 b/man/pidof.8 new file mode 100644 index 0000000..0385514 --- /dev/null +++ b/man/pidof.8 @@ -0,0 +1,78 @@ +'\" -*- 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 diff --git a/man/poweroff.8 b/man/poweroff.8 new file mode 100644 index 0000000..41c02c1 --- /dev/null +++ b/man/poweroff.8 @@ -0,0 +1 @@ +.so man8/halt.8 diff --git a/man/reboot.8 b/man/reboot.8 new file mode 100644 index 0000000..41c02c1 --- /dev/null +++ b/man/reboot.8 @@ -0,0 +1 @@ +.so man8/halt.8 diff --git a/man/runlevel.8 b/man/runlevel.8 new file mode 100644 index 0000000..1e4ea77 --- /dev/null +++ b/man/runlevel.8 @@ -0,0 +1,56 @@ +'\" -*- 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 diff --git a/man/shutdown.8 b/man/shutdown.8 new file mode 100644 index 0000000..d313df5 --- /dev/null +++ b/man/shutdown.8 @@ -0,0 +1,214 @@ +'\" -*- 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) +.\"}}} diff --git a/man/sulogin.8 b/man/sulogin.8 new file mode 100644 index 0000000..4b5d153 --- /dev/null +++ b/man/sulogin.8 @@ -0,0 +1,87 @@ +'\" -*- 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 +.SH SEE ALSO +init(8), inittab(5). diff --git a/man/telinit.8 b/man/telinit.8 new file mode 100644 index 0000000..a12cb4f --- /dev/null +++ b/man/telinit.8 @@ -0,0 +1 @@ +.so man8/init.8 diff --git a/man/wall.1 b/man/wall.1 new file mode 100644 index 0000000..2fe8301 --- /dev/null +++ b/man/wall.1 @@ -0,0 +1,75 @@ +'\" -*- 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 diff --git a/obsolete/README.RIGHT.NOW b/obsolete/README.RIGHT.NOW new file mode 100644 index 0000000..7a6fb1d --- /dev/null +++ b/obsolete/README.RIGHT.NOW @@ -0,0 +1,58 @@ +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. + diff --git a/obsolete/bootlogd.init b/obsolete/bootlogd.init new file mode 100755 index 0000000..cf7b9eb --- /dev/null +++ b/obsolete/bootlogd.init @@ -0,0 +1,67 @@ +#! /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 + diff --git a/obsolete/powerd.8 b/obsolete/powerd.8 new file mode 100644 index 0000000..c1c27e3 --- /dev/null +++ b/obsolete/powerd.8 @@ -0,0 +1,73 @@ +'\" -*- 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 diff --git a/obsolete/powerd.README b/obsolete/powerd.README new file mode 100644 index 0000000..dd0563c --- /dev/null +++ b/obsolete/powerd.README @@ -0,0 +1,36 @@ +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 . 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 diff --git a/obsolete/powerd.c b/obsolete/powerd.c new file mode 100644 index 0000000..fdd31d8 --- /dev/null +++ b/obsolete/powerd.c @@ -0,0 +1,201 @@ +/* + * 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, . + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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 \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); +} diff --git a/obsolete/powerd.cfg b/obsolete/powerd.cfg new file mode 100644 index 0000000..65a76f0 --- /dev/null +++ b/obsolete/powerd.cfg @@ -0,0 +1,58 @@ +# 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 + diff --git a/obsolete/utmpdump.c.OLD b/obsolete/utmpdump.c.OLD new file mode 100644 index 0000000..9161396 --- /dev/null +++ b/obsolete/utmpdump.c.OLD @@ -0,0 +1,70 @@ +/* + * 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 +#include +#include + +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); +} diff --git a/src/Makefile b/src/Makefile new file mode 100644 index 0000000..c466bf8 --- /dev/null +++ b/src/Makefile @@ -0,0 +1,168 @@ +# +# 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 diff --git a/src/bootlogd.c b/src/bootlogd.c new file mode 100644 index 0000000..d9be60b --- /dev/null +++ b/src/bootlogd.c @@ -0,0 +1,657 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef __linux__ +#include +#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; +} + diff --git a/src/dowall.c b/src/dowall.c new file mode 100644 index 0000000..821324f --- /dev/null +++ b/src/dowall.c @@ -0,0 +1,236 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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); +} + diff --git a/src/halt.c b/src/halt.c new file mode 100644 index 0000000..60d79a1 --- /dev/null +++ b/src/halt.c @@ -0,0 +1,312 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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); +} diff --git a/src/hddown.c b/src/hddown.c new file mode 100644 index 0000000..89e9f47 --- /dev/null +++ b/src/hddown.c @@ -0,0 +1,512 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include + +#ifdef __linux__ + +#include +#include + +#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 , 2007/06/12 + * + */ +#include +#include +#include +#include +#ifdef WORDS_BIGENDIAN +#include +#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 */ +#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 + diff --git a/src/ifdown.c b/src/ifdown.c new file mode 100644 index 0000000..42c0d6f --- /dev/null +++ b/src/ifdown.c @@ -0,0 +1,91 @@ +/* + * 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 +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#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; +} + diff --git a/src/init.c b/src/init.c new file mode 100644 index 0000000..1d7371c --- /dev/null +++ b/src/init.c @@ -0,0 +1,2714 @@ +/* + * 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 +#include +#include +#include +#ifdef __linux__ +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef WITH_SELINUX +#include +#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 +# 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 + (void) ioctl(0, KDSIGACCEPT, SIGWINCH); +#endif + + /* + * Ignore all signals. + */ + for(f = 1; f <= NSIG; f++) + SETSIG(sa, f, SIG_IGN, SA_RESTART); + } + + SETSIG(sa, SIGALRM, signal_handler, 0); + SETSIG(sa, SIGHUP, signal_handler, 0); + SETSIG(sa, SIGINT, signal_handler, 0); + SETSIG(sa, SIGCHLD, chld_handler, SA_RESTART); + SETSIG(sa, SIGPWR, signal_handler, 0); + SETSIG(sa, SIGWINCH, signal_handler, 0); + SETSIG(sa, SIGUSR1, signal_handler, 0); + SETSIG(sa, SIGSTOP, stop_handler, SA_RESTART); + SETSIG(sa, SIGTSTP, stop_handler, SA_RESTART); + SETSIG(sa, SIGCONT, cont_handler, SA_RESTART); + SETSIG(sa, SIGSEGV, (void (*)(int))segv_handler, SA_RESTART); + + console_init(); + + if (!reload) { + + /* Close whatever files are open, and reset the console. */ + close(0); + close(1); + close(2); + console_stty(); + setsid(); + + /* + * Set default PATH variable. + */ + setenv("PATH", PATH_DEFAULT, 1 /* Overwrite */); + + /* + * Initialize /var/run/utmp (only works if /var is on + * root and mounted rw) + */ + (void) close(open(UTMP_FILE, O_WRONLY|O_CREAT|O_TRUNC, 0644)); + + /* + * Say hello to the world + */ + initlog(L_CO, bootmsg, "booting"); + + /* + * See if we have to start an emergency shell. + */ + if (emerg_shell) { + SETSIG(sa, SIGCHLD, SIG_DFL, SA_RESTART); + if (spawn(&ch_emerg, &f) > 0) { + while((rc = wait(&st)) != f) + if (rc < 0 && errno == ECHILD) + break; + } + SETSIG(sa, SIGCHLD, chld_handler, SA_RESTART); + } + + /* + * Start normal boot procedure. + */ + runlevel = '#'; + read_inittab(); + + } else { + /* + * Restart: unblock signals and let the show go on + */ + initlog(L_CO, bootmsg, "reloading"); + sigfillset(&sgt); + sigprocmask(SIG_UNBLOCK, &sgt, NULL); + + /* + * Set default PATH variable. + */ + setenv("PATH", PATH_DEFAULT, 0 /* Don't overwrite */); + } + start_if_needed(); + + while(1) { + + /* See if we need to make the boot transitions. */ + boot_transitions(); + INITDBG(L_VB, "init_main: waiting.."); + + /* Check if there are processes to be waited on. */ + for(ch = family; ch; ch = ch->next) + if ((ch->flags & RUNNING) && ch->action != BOOT) break; + +#if CHANGE_WAIT + /* Wait until we get hit by some signal. */ + while (ch != NULL && got_signals == 0) { + if (ISMEMBER(got_signals, SIGHUP)) { + /* See if there are processes to be waited on. */ + for(ch = family; ch; ch = ch->next) + if (ch->flags & WAITING) break; + } + if (ch != NULL) check_init_fifo(); + } +#else /* CHANGE_WAIT */ + if (ch != NULL && got_signals == 0) check_init_fifo(); +#endif /* CHANGE_WAIT */ + + /* Check the 'failing' flags */ + fail_check(); + + /* Process any signals. */ + process_signals(); + + /* See what we need to start up (again) */ + start_if_needed(); + } + /*NOTREACHED*/ +} + +/* + * Tell the user about the syntax we expect. + */ +void usage(char *s) +{ + fprintf(stderr, "Usage: %s {-e VAR[=VAL] | [-t SECONDS] {0|1|2|3|4|5|6|S|s|Q|q|A|a|B|b|C|c|U|u}}\n", s); + exit(1); +} + +int telinit(char *progname, int argc, char **argv) +{ +#ifdef TELINIT_USES_INITLVL + FILE *fp; +#endif + struct init_request request; + struct sigaction sa; + int f, fd, l; + char *env = NULL; + + memset(&request, 0, sizeof(request)); + request.magic = INIT_MAGIC; + + while ((f = getopt(argc, argv, "t:e:")) != EOF) switch(f) { + case 't': + sltime = atoi(optarg); + break; + case 'e': + if (env == NULL) + env = request.i.data; + l = strlen(optarg); + if (env + l + 2 > request.i.data + sizeof(request.i.data)) { + fprintf(stderr, "%s: -e option data " + "too large\n", progname); + exit(1); + } + memcpy(env, optarg, l); + env += l; + *env++ = 0; + break; + default: + usage(progname); + break; + } + + if (env) *env++ = 0; + + if (env) { + if (argc != optind) + usage(progname); + request.cmd = INIT_CMD_SETENV; + } else { + if (argc - optind != 1 || strlen(argv[optind]) != 1) + usage(progname); + if (!strchr("0123456789SsQqAaBbCcUu", argv[optind][0])) + usage(progname); + request.cmd = INIT_CMD_RUNLVL; + request.runlevel = env ? 0 : argv[optind][0]; + request.sleeptime = sltime; + } + + /* Open the fifo and write a command. */ + /* Make sure we don't hang on opening /dev/initctl */ + SETSIG(sa, SIGALRM, signal_handler, 0); + alarm(3); + if ((fd = open(INIT_FIFO, O_WRONLY)) >= 0 && + write(fd, &request, sizeof(request)) == sizeof(request)) { + close(fd); + alarm(0); + return 0; + } + +#ifdef TELINIT_USES_INITLVL + if (request.cmd == INIT_CMD_RUNLVL) { + /* Fallthrough to the old method. */ + + /* Now write the new runlevel. */ + if ((fp = fopen(INITLVL, "w")) == NULL) { + fprintf(stderr, "%s: cannot create %s\n", + progname, INITLVL); + exit(1); + } + fprintf(fp, "%s %d", argv[optind], sltime); + fclose(fp); + + /* And tell init about the pending runlevel change. */ + if (kill(INITPID, SIGHUP) < 0) perror(progname); + + return 0; + } +#endif + + fprintf(stderr, "%s: ", progname); + if (ISMEMBER(got_signals, SIGALRM)) { + fprintf(stderr, "timeout opening/writing control channel %s\n", + INIT_FIFO); + } else { + perror(INIT_FIFO); + } + return 1; +} + +/* + * Main entry for init and telinit. + */ +int main(int argc, char **argv) +{ + char *p; + int f; + int isinit; + int enforce = 0; + + /* Get my own name */ + if ((p = strrchr(argv[0], '/')) != NULL) + p++; + else + p = argv[0]; + umask(022); + + /* Quick check */ + if (geteuid() != 0) { + fprintf(stderr, "%s: must be superuser.\n", p); + exit(1); + } + + /* + * Is this telinit or init ? + */ + isinit = (getpid() == 1); + for (f = 1; f < argc; f++) { + if (!strcmp(argv[f], "-i") || !strcmp(argv[f], "--init")) + isinit = 1; + break; + } + if (!isinit) exit(telinit(p, argc, argv)); + + /* + * Check for re-exec + */ + if (check_pipe(STATE_PIPE)) { + + receive_state(STATE_PIPE); + + myname = istrdup(argv[0]); + argv0 = argv[0]; + maxproclen = 0; + for (f = 0; f < argc; f++) + maxproclen += strlen(argv[f]) + 1; + reload = 1; + setproctitle("init [%c]",runlevel); + + init_main(); + } + + /* Check command line arguments */ + maxproclen = strlen(argv[0]) + 1; + for(f = 1; f < argc; f++) { + if (!strcmp(argv[f], "single") || !strcmp(argv[f], "-s")) + dfl_level = 'S'; + else if (!strcmp(argv[f], "-a") || !strcmp(argv[f], "auto")) + putenv("AUTOBOOT=YES"); + else if (!strcmp(argv[f], "-b") || !strcmp(argv[f],"emergency")) + emerg_shell = 1; + else if (!strcmp(argv[f], "-z")) { + /* Ignore -z xxx */ + if (argv[f + 1]) f++; + } else if (strchr("0123456789sS", argv[f][0]) + && strlen(argv[f]) == 1) + dfl_level = argv[f][0]; + /* "init u" in the very beginning makes no sense */ + if (dfl_level == 's') dfl_level = 'S'; + maxproclen += strlen(argv[f]) + 1; + } + +#ifdef WITH_SELINUX + if (getenv("SELINUX_INIT") == NULL && !is_selinux_enabled()) { + putenv("SELINUX_INIT=YES"); + if (selinux_init_load_policy(&enforce) == 0 ) { + execv(myname, argv); + } else { + if (enforce > 0) { + /* SELinux in enforcing mode but load_policy failed */ + /* At this point, we probably can't open /dev/console, so log() won't work */ + fprintf(stderr,"Unable to load SELinux Policy. Machine is in enforcing mode. Halting now.\n"); + exit(1); + } + } + } +#endif + /* Start booting. */ + argv0 = argv[0]; + argv[1] = NULL; + setproctitle("init boot"); + init_main(dfl_level); + + /*NOTREACHED*/ + return 0; +} diff --git a/src/init.h b/src/init.h new file mode 100644 index 0000000..fea706b --- /dev/null +++ b/src/init.h @@ -0,0 +1,139 @@ +/* + * init.h Several defines and declarations to be + * included by all modules of the init program. + * + * Version: @(#)init.h 2.85-5 02-Jul-2003 miquels@cistron.nl + * + * 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 + * + */ + +/* Standard configuration */ +#define CHANGE_WAIT 0 /* Change runlevel while + waiting for a process to exit? */ +/* Debug and test modes */ +#define DEBUG 0 /* Debug code off */ +#define INITDEBUG 0 /* Fork at startup to debug init. */ + +/* Some constants */ +#define INITPID 1 /* pid of first process */ +#define PIPE_FD 10 /* Fileno of initfifo. */ +#define STATE_PIPE 11 /* used to pass state through exec */ + +/* Failsafe configuration */ +#define MAXSPAWN 10 /* Max times respawned in.. */ +#define TESTTIME 120 /* this much seconds */ +#define SLEEPTIME 300 /* Disable time */ + +/* Default path inherited by every child. */ +#define PATH_DEFAULT "/sbin:/usr/sbin:/bin:/usr/bin" + + +/* Prototypes. */ +void write_utmp_wtmp(char *user, char *id, int pid, int type, char *line); +void write_wtmp(char *user, char *id, int pid, int type, char *line); +#ifdef __GNUC__ +__attribute__ ((format (printf, 2, 3))) +#endif +void initlog(int loglevel, char *fmt, ...); +void set_term(int how); +void print(char *fmt); + +#if DEBUG +# define INITDBG(level, fmt, args...) initlog(level, fmt, ##args) +#else +# define INITDBG(level, fmt, args...) +#endif + +/* Actions to be taken by init */ +#define RESPAWN 1 +#define WAIT 2 +#define ONCE 3 +#define BOOT 4 +#define BOOTWAIT 5 +#define POWERFAIL 6 +#define POWERWAIT 7 +#define POWEROKWAIT 8 +#define CTRLALTDEL 9 +#define OFF 10 +#define ONDEMAND 11 +#define INITDEFAULT 12 +#define SYSINIT 13 +#define POWERFAILNOW 14 +#define KBREQUEST 15 + +/* Information about a process in the in-core inittab */ +typedef struct _child_ { + int flags; /* Status of this entry */ + int exstat; /* Exit status of process */ + int pid; /* Pid of this process */ + time_t tm; /* When respawned last */ + int count; /* Times respawned in the last 2 minutes */ + char id[8]; /* Inittab id (must be unique) */ + char rlevel[12]; /* run levels */ + int action; /* what to do (see list below) */ + char process[128]; /* The command line */ + struct _child_ *new; /* New entry (after inittab re-read) */ + struct _child_ *next; /* For the linked list */ +} CHILD; + +/* Values for the 'flags' field */ +#define RUNNING 2 /* Process is still running */ +#define KILLME 4 /* Kill this process */ +#define DEMAND 8 /* "runlevels" a b c */ +#define FAILING 16 /* process respawns rapidly */ +#define WAITING 32 /* We're waiting for this process */ +#define ZOMBIE 64 /* This process is already dead */ +#define XECUTED 128 /* Set if spawned once or more times */ + +/* Log levels. */ +#define L_CO 1 /* Log on the console. */ +#define L_SY 2 /* Log with syslog() */ +#define L_VB (L_CO|L_SY) /* Log with both. */ + +#ifndef NO_PROCESS +# define NO_PROCESS 0 +#endif + +/* + * Global variables. + */ +extern CHILD *family; +extern int wrote_wtmp_reboot; +extern int wrote_utmp_reboot; + +/* Tokens in state parser */ +#define C_VER 1 +#define C_END 2 +#define C_REC 3 +#define C_EOR 4 +#define C_LEV 5 +#define C_FLAG 6 +#define C_ACTION 7 +#define C_PROCESS 8 +#define C_PID 9 +#define C_EXS 10 +#define C_EOF -1 +#define D_RUNLEVEL -2 +#define D_THISLEVEL -3 +#define D_PREVLEVEL -4 +#define D_GOTSIGN -5 +#define D_WROTE_WTMP_REBOOT -6 +#define D_WROTE_UTMP_REBOOT -7 +#define D_SLTIME -8 +#define D_DIDBOOT -9 + diff --git a/src/initreq.h b/src/initreq.h new file mode 100644 index 0000000..f533594 --- /dev/null +++ b/src/initreq.h @@ -0,0 +1,86 @@ +/* + * initreq.h Interface to talk to init through /dev/initctl. + * + * Copyright (C) 1995-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 + * + * Version: @(#)initreq.h 1.28 31-Mar-2004 MvS + * + */ +#ifndef _INITREQ_H +#define _INITREQ_H + +#include + +#if defined(__FreeBSD_kernel__) +# define INIT_FIFO "/etc/.initctl" +#else +# define INIT_FIFO "/dev/initctl" +#endif + +#define INIT_MAGIC 0x03091969 +#define INIT_CMD_START 0 +#define INIT_CMD_RUNLVL 1 +#define INIT_CMD_POWERFAIL 2 +#define INIT_CMD_POWERFAILNOW 3 +#define INIT_CMD_POWEROK 4 +#define INIT_CMD_BSD 5 +#define INIT_CMD_SETENV 6 +#define INIT_CMD_UNSETENV 7 + +#define INIT_CMD_CHANGECONS 12345 + +#ifdef MAXHOSTNAMELEN +# define INITRQ_HLEN MAXHOSTNAMELEN +#else +# define INITRQ_HLEN 64 +#endif + +/* + * This is what BSD 4.4 uses when talking to init. + * Linux doesn't use this right now. + */ +struct init_request_bsd { + char gen_id[8]; /* Beats me.. telnetd uses "fe" */ + char tty_id[16]; /* Tty name minus /dev/tty */ + char host[INITRQ_HLEN]; /* Hostname */ + char term_type[16]; /* Terminal type */ + int signal; /* Signal to send */ + int pid; /* Process to send to */ + char exec_name[128]; /* Program to execute */ + char reserved[128]; /* For future expansion. */ +}; + + +/* + * Because of legacy interfaces, "runlevel" and "sleeptime" + * aren't in a seperate struct in the union. + * + * The weird sizes are because init expects the whole + * struct to be 384 bytes. + */ +struct init_request { + int magic; /* Magic number */ + int cmd; /* What kind of request */ + int runlevel; /* Runlevel to change to */ + int sleeptime; /* Time between TERM and KILL */ + union { + struct init_request_bsd bsd; + char data[368]; + } i; +}; + +#endif diff --git a/src/initscript.sample b/src/initscript.sample new file mode 100755 index 0000000..64126d0 --- /dev/null +++ b/src/initscript.sample @@ -0,0 +1,25 @@ +# +# initscript If this script is intalled as /etc/initscript, +# it is executed by init(8) for every program it +# wants to spawn like this: +# +# /bin/sh /etc/initscript +# +# It can be used to set the default umask and ulimit +# of all processes. By default this script is installed +# as /etc/initscript.sample, so to enable it you must +# rename this script first to /etc/initscript. +# +# Version: @(#)initscript 1.10 10-Dec-1995 MvS. +# +# Author: Miquel van Smoorenburg, +# + + # Set umask to safe level, and enable core dumps. + umask 022 + ulimit -c 2097151 + PATH=/bin:/sbin:/usr/bin:/usr/sbin + export PATH + + # Execute the program. + eval exec "$4" diff --git a/src/killall5.c b/src/killall5.c new file mode 100644 index 0000000..1f9dbb0 --- /dev/null +++ b/src/killall5.c @@ -0,0 +1,741 @@ +/* + * kilall5.c Kill all processes except processes that have the + * same session id, so that the shell that called us + * won't be killed. Typically used in shutdown scripts. + * + * pidof.c Tries to get the pid of the process[es] named. + * + * Version: 2.86 30-Jul-2004 MvS + * + * Usage: killall5 [-][signal] + * pidof [-s] [-o omitpid [-o omitpid]] program [program..] + * + * Authors: Miquel van Smoorenburg, miquels@cistron.nl + * + * Riku Meskanen, + * - return all running pids of given program name + * - single shot '-s' option for backwards combatibility + * - omit pid '-o' option and %PPID (parent pid metavariable) + * - syslog() only if not a connected to controlling terminal + * - swapped out programs pids are caught now + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +char *Version = "@(#)killall5 2.86 31-Jul-2004 miquels@cistron.nl"; + +#define STATNAMELEN 15 +#define DO_STAT 1 +#define NO_STAT 0 + +/* Info about a process. */ +typedef struct proc { + char *argv0; /* Name as found out from argv[0] */ + char *argv0base; /* `basename argv[1]` */ + char *argv1; /* Name as found out from argv[1] */ + char *argv1base; /* `basename argv[1]` */ + char *statname; /* the statname without braces */ + ino_t ino; /* Inode number */ + dev_t dev; /* Device it is on */ + pid_t pid; /* Process ID. */ + int sid; /* Session ID. */ + int kernel; /* Kernel thread or zombie. */ + struct proc *next; /* Pointer to next struct. */ +} PROC; + +/* pid queue */ + +typedef struct pidq { + PROC *proc; + struct pidq *next; +} PIDQ; + +typedef struct { + PIDQ *head; + PIDQ *tail; + PIDQ *next; +} PIDQ_HEAD; + +/* List of processes. */ +PROC *plist; + +/* Did we stop all processes ? */ +int sent_sigstop; + +int scripts_too = 0; + +char *progname; /* the name of the running program */ +#ifdef __GNUC__ +__attribute__ ((format (printf, 2, 3))) +#endif +void nsyslog(int pri, char *fmt, ...); + +/* + * Malloc space, barf if out of memory. + */ +void *xmalloc(int bytes) +{ + void *p; + + if ((p = malloc(bytes)) == NULL) { + if (sent_sigstop) kill(-1, SIGCONT); + nsyslog(LOG_ERR, "out of memory"); + exit(1); + } + return p; +} + +/* + * See if the proc filesystem is there. Mount if needed. + */ +int mount_proc(void) +{ + struct stat st; + char *args[] = { "mount", "-t", "proc", "proc", "/proc", 0 }; + pid_t pid, rc; + int wst; + int did_mount = 0; + + /* Stat /proc/version to see if /proc is mounted. */ + if (stat("/proc/version", &st) < 0 && errno == ENOENT) { + + /* It's not there, so mount it. */ + if ((pid = fork()) < 0) { + nsyslog(LOG_ERR, "cannot fork"); + exit(1); + } + if (pid == 0) { + /* Try a few mount binaries. */ + execv("/sbin/mount", args); + execv("/bin/mount", args); + + /* Okay, I give up. */ + nsyslog(LOG_ERR, "cannot execute mount"); + exit(1); + } + /* Wait for child. */ + while ((rc = wait(&wst)) != pid) + if (rc < 0 && errno == ECHILD) + break; + if (rc != pid || WEXITSTATUS(wst) != 0) + nsyslog(LOG_ERR, "mount returned non-zero exit status"); + + did_mount = 1; + } + + /* See if mount succeeded. */ + if (stat("/proc/version", &st) < 0) { + if (errno == ENOENT) + nsyslog(LOG_ERR, "/proc not mounted, failed to mount."); + else + nsyslog(LOG_ERR, "/proc unavailable."); + exit(1); + } + + return did_mount; +} + +int readarg(FILE *fp, char *buf, int sz) +{ + int c = 0, f = 0; + + while (f < (sz-1) && (c = fgetc(fp)) != EOF && c) + buf[f++] = c; + buf[f] = 0; + + return (c == EOF && f == 0) ? c : f; +} + +/* + * Read the proc filesystem. + * CWD must be /proc to avoid problems if / is affected by the killing (ie depend on fuse). + */ +int readproc(int do_stat) +{ + DIR *dir; + FILE *fp; + PROC *p, *n; + struct dirent *d; + struct stat st; + char path[256]; + char buf[256]; + char *s, *q; + unsigned long startcode, endcode; + int pid, f; + + /* Open the /proc directory. */ + if (chdir("/proc") == -1) { + nsyslog(LOG_ERR, "chdir /proc failed"); + return -1; + } + if ((dir = opendir(".")) == NULL) { + nsyslog(LOG_ERR, "cannot opendir(/proc)"); + return -1; + } + + /* Free the already existing process list. */ + n = plist; + for (p = plist; n; p = n) { + n = p->next; + if (p->argv0) free(p->argv0); + if (p->argv1) free(p->argv1); + free(p); + } + plist = NULL; + + /* Walk through the directory. */ + while ((d = readdir(dir)) != NULL) { + + /* See if this is a process */ + if ((pid = atoi(d->d_name)) == 0) continue; + + /* Get a PROC struct . */ + p = (PROC *)xmalloc(sizeof(PROC)); + memset(p, 0, sizeof(PROC)); + + /* Open the status file. */ + snprintf(path, sizeof(path), "%s/stat", d->d_name); + + /* Read SID & statname from it. */ + if ((fp = fopen(path, "r")) != NULL) { + buf[0] = 0; + fgets(buf, sizeof(buf), fp); + + /* See if name starts with '(' */ + s = buf; + while (*s != ' ') s++; + s++; + if (*s == '(') { + /* Read program name. */ + q = strrchr(buf, ')'); + if (q == NULL) { + p->sid = 0; + nsyslog(LOG_ERR, + "can't get program name from /proc/%s\n", + path); + free(p); + continue; + } + s++; + } else { + q = s; + while (*q != ' ') q++; + } + *q++ = 0; + while (*q == ' ') q++; + p->statname = (char *)xmalloc(strlen(s)+1); + strcpy(p->statname, s); + + /* Get session, startcode, endcode. */ + startcode = endcode = 0; + if (sscanf(q, "%*c %*d %*d %d %*d %*d %*u %*u " + "%*u %*u %*u %*u %*u %*d %*d " + "%*d %*d %*d %*d %*u %*u %*d " + "%*u %lu %lu", + &p->sid, &startcode, &endcode) != 3) { + p->sid = 0; + nsyslog(LOG_ERR, "can't read sid from %s\n", + path); + free(p); + continue; + } + if (startcode == 0 && endcode == 0) + p->kernel = 1; + fclose(fp); + } else { + /* Process disappeared.. */ + free(p); + continue; + } + + snprintf(path, sizeof(path), "%s/cmdline", d->d_name); + if ((fp = fopen(path, "r")) != NULL) { + + /* Now read argv[0] */ + f = readarg(fp, buf, sizeof(buf)); + + if (buf[0]) { + /* Store the name into malloced memory. */ + p->argv0 = (char *)xmalloc(f + 1); + strcpy(p->argv0, buf); + + /* Get a pointer to the basename. */ + p->argv0base = strrchr(p->argv0, '/'); + if (p->argv0base != NULL) + p->argv0base++; + else + p->argv0base = p->argv0; + } + + /* And read argv[1] */ + while ((f = readarg(fp, buf, sizeof(buf))) != EOF) + if (buf[0] != '-') break; + + if (buf[0]) { + /* Store the name into malloced memory. */ + p->argv1 = (char *)xmalloc(f + 1); + strcpy(p->argv1, buf); + + /* Get a pointer to the basename. */ + p->argv1base = strrchr(p->argv1, '/'); + if (p->argv1base != NULL) + p->argv1base++; + else + p->argv1base = p->argv1; + } + + fclose(fp); + + } else { + /* Process disappeared.. */ + free(p); + continue; + } + + /* Try to stat the executable. */ + snprintf(path, sizeof(path), "/proc/%s/exe", d->d_name); + if (do_stat && stat(path, &st) == 0) { + p->dev = st.st_dev; + p->ino = st.st_ino; + } + + /* Link it into the list. */ + p->next = plist; + plist = p; + p->pid = pid; + } + closedir(dir); + + /* Done. */ + return 0; +} + +PIDQ_HEAD *init_pid_q(PIDQ_HEAD *q) +{ + q->head = q->next = q->tail = NULL; + return q; +} + +int empty_q(PIDQ_HEAD *q) +{ + return (q->head == NULL); +} + +int add_pid_to_q(PIDQ_HEAD *q, PROC *p) +{ + PIDQ *tmp; + + tmp = (PIDQ *)xmalloc(sizeof(PIDQ)); + + tmp->proc = p; + tmp->next = NULL; + + if (empty_q(q)) { + q->head = tmp; + q->tail = tmp; + } else { + q->tail->next = tmp; + q->tail = tmp; + } + return 0; +} + +PROC *get_next_from_pid_q(PIDQ_HEAD *q) +{ + PROC *p; + PIDQ *tmp = q->head; + + if (!empty_q(q)) { + p = q->head->proc; + q->head = tmp->next; + free(tmp); + return p; + } + + return NULL; +} + +/* Try to get the process ID of a given process. */ +PIDQ_HEAD *pidof(char *prog) +{ + PROC *p; + PIDQ_HEAD *q; + struct stat st; + char *s; + int dostat = 0; + int foundone = 0; + int ok = 0; + + if (! prog) + return NULL; + + /* Get basename of program. */ + if ((s = strrchr(prog, '/')) == NULL) + s = prog; + else + s++; + + if (! *s) + return NULL; + + q = (PIDQ_HEAD *)xmalloc(sizeof(PIDQ_HEAD)); + q = init_pid_q(q); + + /* Try to stat the executable. */ + if (prog[0] == '/' && stat(prog, &st) == 0) + dostat++; + + /* First try to find a match based on dev/ino pair. */ + if (dostat) { + for (p = plist; p; p = p->next) { + if (p->dev == st.st_dev && p->ino == st.st_ino) { + add_pid_to_q(q, p); + foundone++; + } + } + } + + /* If we didn't find a match based on dev/ino, try the name. */ + if (!foundone) for (p = plist; p; p = p->next) { + ok = 0; + + /* matching nonmatching + * proc name prog name prog name + * --- ----------- ------------ + * b b, p/b, q/b + * p/b b, p/b q/b + * + * Algorithm: Match if: + * cmd = arg + * or cmd = base(arg) + * or base(cmd) = arg + * + * Specifically, do not match just because base(cmd) = base(arg) + * as was done in earlier versions of this program, since this + * allows /aaa/foo to match /bbb/foo . + */ + ok |= + (p->argv0 && strcmp(p->argv0, prog) == 0) + || (p->argv0 && s != prog && strcmp(p->argv0, s) == 0) + || (p->argv0base && strcmp(p->argv0base, prog) == 0); + + /* For scripts, compare argv[1] as well. */ + if ( + scripts_too && p->statname && p->argv1base + && !strncmp(p->statname, p->argv1base, STATNAMELEN) + ) { + ok |= + (p->argv1 && strcmp(p->argv1, prog) == 0) + || (p->argv1 && s != prog && strcmp(p->argv1, s) == 0) + || (p->argv1base && strcmp(p->argv1base, prog) == 0); + } + + /* + * if we have a space in argv0, process probably + * used setproctitle so try statname. + */ + if (strlen(s) <= STATNAMELEN && + (p->argv0 == NULL || + p->argv0[0] == 0 || + strchr(p->argv0, ' '))) { + ok |= (strcmp(p->statname, s) == 0); + } + if (ok) add_pid_to_q(q, p); + } + + return q; +} + +/* Give usage message and exit. */ +void usage(void) +{ + nsyslog(LOG_ERR, "only one argument, a signal number, allowed"); + closelog(); + exit(1); +} + +/* write to syslog file if not open terminal */ +#ifdef __GNUC__ +__attribute__ ((format (printf, 2, 3))) +#endif +void nsyslog(int pri, char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + + if (ttyname(0) == NULL) { + vsyslog(pri, fmt, args); + } else { + fprintf(stderr, "%s: ",progname); + vfprintf(stderr, fmt, args); + fprintf(stderr, "\n"); + } + + va_end(args); +} + +#define PIDOF_SINGLE 0x01 +#define PIDOF_OMIT 0x02 + +#define PIDOF_OMITSZ 5 + +/* + * Pidof functionality. + */ +int main_pidof(int argc, char **argv) +{ + PIDQ_HEAD *q; + PROC *p; + pid_t opid[PIDOF_OMITSZ], spid; + int f; + int first = 1; + int i, oind, opt, flags = 0; + int chroot_check = 0; + struct stat st; + char tmp[512]; + + for (oind = PIDOF_OMITSZ-1; oind > 0; oind--) + opid[oind] = 0; + opterr = 0; + + while ((opt = getopt(argc,argv,"hco:sx")) != EOF) switch (opt) { + case '?': + nsyslog(LOG_ERR,"invalid options on command line!\n"); + closelog(); + exit(1); + case 'c': + if (geteuid() == 0) chroot_check = 1; + break; + case 'o': + if (oind >= PIDOF_OMITSZ -1) { + nsyslog(LOG_ERR,"omit pid buffer size %d " + "exceeded!\n", PIDOF_OMITSZ); + closelog(); + exit(1); + } + if (strcmp("%PPID",optarg) == 0) + opid[oind] = getppid(); + else if ((opid[oind] = atoi(optarg)) < 1) { + nsyslog(LOG_ERR, + "illegal omit pid value (%s)!\n", + optarg); + closelog(); + exit(1); + } + oind++; + flags |= PIDOF_OMIT; + break; + case 's': + flags |= PIDOF_SINGLE; + break; + case 'x': + scripts_too++; + break; + default: + /* Nothing */ + break; + } + argc -= optind; + argv += optind; + + /* Check if we are in a chroot */ + if (chroot_check) { + snprintf(tmp, 512, "/proc/%d/root", getpid()); + if (stat(tmp, &st) < 0) { + nsyslog(LOG_ERR, "stat failed for %s!\n", tmp); + closelog(); + exit(1); + } + } + + /* Print out process-ID's one by one. */ + readproc(DO_STAT); + for(f = 0; f < argc; f++) { + if ((q = pidof(argv[f])) != NULL) { + spid = 0; + while ((p = get_next_from_pid_q(q))) { + if (flags & PIDOF_OMIT) { + for (i = 0; i < oind; i++) + if (opid[i] == p->pid) + break; + /* + * On a match, continue with + * the for loop above. + */ + if (i < oind) + continue; + } + if (flags & PIDOF_SINGLE) { + if (spid) + continue; + else + spid = 1; + } + if (chroot_check) { + struct stat st2; + snprintf(tmp, 512, "/proc/%d/root", + p->pid); + if (stat(tmp, &st2) < 0 || + st.st_dev != st2.st_dev || + st.st_ino != st2.st_ino) { + continue; + } + } + if (!first) + printf(" "); + printf("%d", p->pid); + first = 0; + } + } + } + if (!first) + printf("\n"); + closelog(); + return(first ? 1 : 0); +} + + + +#define KILLALL_OMITSZ 16 + +/* Main for either killall or pidof. */ +int main(int argc, char **argv) +{ + PROC *p; + int pid, sid = -1; + pid_t opid[KILLALL_OMITSZ]; + int i, oind, omit = 0; + int sig = SIGKILL; + + /* return non-zero if no process was killed */ + int retval = 2; + + /* Get program name. */ + if ((progname = strrchr(argv[0], '/')) == NULL) + progname = argv[0]; + else + progname++; + + /* Now connect to syslog. */ + openlog(progname, LOG_CONS|LOG_PID, LOG_DAEMON); + + /* Were we called as 'pidof' ? */ + if (strcmp(progname, "pidof") == 0) + return main_pidof(argc, argv); + + /* Right, so we are "killall". */ + for (oind = KILLALL_OMITSZ-1; oind > 0; oind--) + opid[oind] = 0; + + if (argc > 1) { + for (i = 1; i < argc; i++) { + if (argv[i][0] == '-') (argv[i])++; + if (argv[i][0] == 'o') { + if (++i >= argc) usage(); + if (oind >= KILLALL_OMITSZ -1) { + nsyslog(LOG_ERR,"omit pid buffer size " + "%d exceeded!\n", + KILLALL_OMITSZ); + closelog(); + exit(1); + } + if ((opid[oind] = atoi(argv[i])) < 1) { + nsyslog(LOG_ERR, + "illegal omit pid value " + "(%s)!\n", argv[i]); + closelog(); + exit(1); + } + oind++; + omit = 1; + } + else if ((sig = atoi(argv[1])) <= 0 || sig > 31) + usage(); + } + } + + /* First get the /proc filesystem online. */ + mount_proc(); + + /* + * Ignoring SIGKILL and SIGSTOP do not make sense, but + * someday kill(-1, sig) might kill ourself if we don't + * do this. This certainly is a valid concern for SIGTERM- + * Linux 2.1 might send the calling process the signal too. + */ + signal(SIGTERM, SIG_IGN); + signal(SIGSTOP, SIG_IGN); + signal(SIGKILL, SIG_IGN); + + /* lock us into memory */ + mlockall(MCL_CURRENT | MCL_FUTURE); + + /* Now stop all processes. */ + kill(-1, SIGSTOP); + sent_sigstop = 1; + + /* Read /proc filesystem */ + if (readproc(NO_STAT) < 0) { + kill(-1, SIGCONT); + return(1); + } + + /* Now kill all processes except init (pid 1) and our session. */ + sid = (int)getsid(0); + pid = (int)getpid(); + for (p = plist; p; p = p->next) { + if (p->pid == 1 || p->pid == pid || p->sid == sid || p->kernel) + continue; + if (omit) { + for (i = 0; i < oind; i++) + if (opid[i] == p->pid) + break; + /* On a match, continue with the for loop above. */ + if (i < oind) + continue; + } + kill(p->pid, sig); + retval = 0; + } + + /* And let them continue. */ + kill(-1, SIGCONT); + + /* Done. */ + closelog(); + + /* Force the kernel to run the scheduler */ + usleep(1); + + return retval; +} diff --git a/src/last.c b/src/last.c new file mode 100644 index 0000000..5bf48ec --- /dev/null +++ b/src/last.c @@ -0,0 +1,911 @@ +/* + * last.c Re-implementation of the 'last' command, this time + * for Linux. Yes I know there is BSD last, but I + * just felt like writing this. No thanks :-). + * Also, this version gives lots more info (especially with -x) + * + * Author: Miquel van Smoorenburg, miquels@cistron.nl + * + * Version: @(#)last 2.85 30-Jul-2004 miquels@cistron.nl + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "oldutmp.h" + +#ifndef SHUTDOWN_TIME +# define SHUTDOWN_TIME 254 +#endif + +char *Version = "@(#) last 2.85 31-Apr-2004 miquels"; + +#define CHOP_DOMAIN 0 /* Define to chop off local domainname. */ +#define NEW_UTMP 1 /* Fancy & fast utmp read code. */ +#define UCHUNKSIZE 16384 /* How much we read at once. */ + +/* Double linked list of struct utmp's */ +struct utmplist { + struct utmp ut; + struct utmplist *next; + struct utmplist *prev; +}; +struct utmplist *utmplist = NULL; + +/* Types of listing */ +#define R_CRASH 1 /* No logout record, system boot in between */ +#define R_DOWN 2 /* System brought down in decent way */ +#define R_NORMAL 3 /* Normal */ +#define R_NOW 4 /* Still logged in */ +#define R_REBOOT 5 /* Reboot record. */ +#define R_PHANTOM 6 /* No logout record but session is stale. */ +#define R_TIMECHANGE 7 /* NEW_TIME or OLD_TIME */ + +/* Global variables */ +int maxrecs = 0; /* Maximum number of records to list. */ +int recsdone = 0; /* Number of records listed */ +int showhost = 1; /* Show hostname too? */ +int altlist = 0; /* Show hostname at the end. */ +int usedns = 0; /* Use DNS to lookup the hostname. */ +int useip = 0; /* Print IP address in number format */ +int fulltime = 0; /* Print full dates and times */ +int oldfmt = 0; /* Use old libc5 format? */ +char **show = NULL; /* What do they want us to show */ +char *ufile; /* Filename of this file */ +time_t lastdate; /* Last date we've seen */ +char *progname; /* Name of this program */ +#if CHOP_DOMAIN +char hostname[256]; /* For gethostbyname() */ +char *domainname; /* Our domainname. */ +#endif + +/* + * Convert old utmp format to new. + */ +void uconv(struct oldutmp *oldut, struct utmp *utn) +{ + memset(utn, 0, sizeof(struct utmp)); + utn->ut_type = oldut->ut_type; + utn->ut_pid = oldut->ut_pid; + utn->ut_time = oldut->ut_oldtime; + utn->ut_addr = oldut->ut_oldaddr; + strncpy(utn->ut_line, oldut->ut_line, OLD_LINESIZE); + strncpy(utn->ut_user, oldut->ut_user, OLD_NAMESIZE); + strncpy(utn->ut_host, oldut->ut_host, OLD_HOSTSIZE); +} + +#if NEW_UTMP +/* + * Read one utmp entry, return in new format. + * Automatically reposition file pointer. + */ +int uread(FILE *fp, struct utmp *u, int *quit) +{ + static int utsize; + static char buf[UCHUNKSIZE]; + char tmp[1024]; + static off_t fpos; + static int bpos; + struct oldutmp uto; + int r; + off_t o; + + if (quit == NULL && u != NULL) { + /* + * Normal read. + */ + if (oldfmt) { + r = fread(&uto, sizeof(uto), 1, fp); + uconv(&uto, u); + } else + r = fread(u, sizeof(struct utmp), 1, fp); + return r; + } + + if (u == NULL) { + /* + * Initialize and position. + */ + utsize = oldfmt ? sizeof(uto) : sizeof(struct utmp); + fseeko(fp, 0, SEEK_END); + fpos = ftello(fp); + if (fpos == 0) + return 0; + o = ((fpos - 1) / UCHUNKSIZE) * UCHUNKSIZE; + if (fseeko(fp, o, SEEK_SET) < 0) { + fprintf(stderr, "%s: seek failed!\n", progname); + return 0; + } + bpos = (int)(fpos - o); + if (fread(buf, bpos, 1, fp) != 1) { + fprintf(stderr, "%s: read failed!\n", progname); + return 0; + } + fpos = o; + return 1; + } + + /* + * Read one struct. From the buffer if possible. + */ + bpos -= utsize; + if (bpos >= 0) { + if (oldfmt) + uconv((struct oldutmp *)(buf + bpos), u); + else + memcpy(u, buf + bpos, sizeof(struct utmp)); + return 1; + } + + /* + * Oops we went "below" the buffer. We should be able to + * seek back UCHUNKSIZE bytes. + */ + fpos -= UCHUNKSIZE; + if (fpos < 0) + return 0; + + /* + * Copy whatever is left in the buffer. + */ + memcpy(tmp + (-bpos), buf, utsize + bpos); + if (fseeko(fp, fpos, SEEK_SET) < 0) { + perror("fseek"); + return 0; + } + + /* + * Read another UCHUNKSIZE bytes. + */ + if (fread(buf, UCHUNKSIZE, 1, fp) != 1) { + perror("fread"); + return 0; + } + + /* + * The end of the UCHUNKSIZE byte buffer should be the first + * few bytes of the current struct utmp. + */ + memcpy(tmp, buf + UCHUNKSIZE + bpos, -bpos); + bpos += UCHUNKSIZE; + + if (oldfmt) + uconv((struct oldutmp *)tmp, u); + else + memcpy(u, tmp, sizeof(struct utmp)); + + return 1; +} + +#else /* NEW_UTMP */ + +/* + * Read one utmp entry, return in new format. + * Automatically reposition file pointer. + */ +int uread(FILE *fp, struct utmp *u, int *quit) +{ + struct oldutmp uto; + off_t r; + + if (u == NULL) { + r = oldfmt ? sizeof(struct oldutmp) : sizeof(struct utmp); + fseek(fp, -1 * r, SEEK_END); + return 1; + } + + if (!oldfmt) { + r = fread(u, sizeof(struct utmp), 1, fp); + if (r == 1) { + if (fseeko(fp, -2 * sizeof(struct utmp), SEEK_CUR) < 0) + if (quit) *quit = 1; + } + return r; + } + r = fread(&uto, sizeof(struct oldutmp), 1, fp); + if (r == 1) { + if (fseeko(fp, -2 * sizeof(struct oldutmp), SEEK_CUR) < 0) + if (quit) *quit = 1; + uconv(&uto, u); + } + + return r; +} +#endif + +/* + * Try to be smart about the location of the BTMP file + */ +#ifndef BTMP_FILE +#define BTMP_FILE getbtmp() +char *getbtmp() +{ + static char btmp[128]; + char *p; + + strcpy(btmp, WTMP_FILE); + if ((p = strrchr(btmp, '/')) == NULL) + p = btmp; + else + p++; + *p = 0; + strcat(btmp, "btmp"); + return btmp; +} +#endif + +/* + * Print a short date. + */ +char *showdate() +{ + char *s = ctime(&lastdate); + s[16] = 0; + return s; +} + +/* + * SIGINT handler + */ +void int_handler() +{ + printf("Interrupted %s\n", showdate()); + exit(1); +} + +/* + * SIGQUIT handler + */ +void quit_handler() +{ + printf("Interrupted %s\n", showdate()); + signal(SIGQUIT, quit_handler); +} + +/* + * Get the basename of a filename + */ +char *mybasename(char *s) +{ + char *p; + + if ((p = strrchr(s, '/')) != NULL) + p++; + else + p = s; + return p; +} + +/* + * Lookup a host with DNS. + */ +int dns_lookup(char *result, int size, int useip, int32_t *a) +{ + struct sockaddr_in sin; + struct sockaddr_in6 sin6; + struct sockaddr *sa; + int salen, flags; + unsigned int topnibble; + unsigned int azero = 0, sitelocal = 0; + int mapped = 0; + + flags = useip ? NI_NUMERICHOST : 0; + + /* + * IPv4 or IPv6 ? We use 2 heuristics: + * 1. Current IPv6 range uses 2000-3fff or fec0-feff. + * Outside of that is illegal and must be IPv4. + * 2. If last 3 bytes are 0, must be IPv4 + * 3. If IPv6 in IPv4, handle as IPv4 + * + * Ugly. + */ + if (a[0] == 0 && a[1] == 0 && a[2] == htonl (0xffff)) + mapped = 1; + topnibble = ntohl((unsigned int)a[0]) >> 28; + + azero = ntohl((unsigned int)a[0]) >> 16; + sitelocal = (azero >= 0xfec0 && azero <= 0xfeff) ? 1 : 0; + + if (((topnibble < 2 || topnibble > 3) && (!sitelocal)) || mapped || + (a[1] == 0 && a[2] == 0 && a[3] == 0)) { + /* IPv4 */ + sin.sin_family = AF_INET; + sin.sin_port = 0; + sin.sin_addr.s_addr = mapped ? a[3] : a[0]; + sa = (struct sockaddr *)&sin; + salen = sizeof(sin); + } else { + /* IPv6 */ + memset(&sin6, 0, sizeof(sin6)); + sin6.sin6_family = AF_INET6; + sin6.sin6_port = 0; + memcpy(sin6.sin6_addr.s6_addr, a, 16); + sa = (struct sockaddr *)&sin6; + salen = sizeof(sin6); + } + + return getnameinfo(sa, salen, result, size, NULL, 0, flags); +} + +/* + * Show one line of information on screen + */ +int list(struct utmp *p, time_t t, int what) +{ + time_t secs, tmp; + char logintime[32]; + char logouttime[32]; + char length[32]; + char final[128]; + char utline[UT_LINESIZE+1]; + char domain[256]; + char *s, **walk; + int mins, hours, days; + int r, len; + + /* + * uucp and ftp have special-type entries + */ + utline[0] = 0; + strncat(utline, p->ut_line, UT_LINESIZE); + if (strncmp(utline, "ftp", 3) == 0 && isdigit(utline[3])) + utline[3] = 0; + if (strncmp(utline, "uucp", 4) == 0 && isdigit(utline[4])) + utline[4] = 0; + + /* + * Is this something we wanna show? + */ + if (show) { + for (walk = show; *walk; walk++) { + if (strncmp(p->ut_name, *walk, UT_NAMESIZE) == 0 || + strcmp(utline, *walk) == 0 || + (strncmp(utline, "tty", 3) == 0 && + strcmp(utline + 3, *walk) == 0)) break; + } + if (*walk == NULL) return 0; + } + + /* + * Calculate times + */ + tmp = (time_t)p->ut_time; + strcpy(logintime, ctime(&tmp)); + if (fulltime) + sprintf(logouttime, "- %s", ctime(&t)); + else { + logintime[16] = 0; + sprintf(logouttime, "- %s", ctime(&t) + 11); + logouttime[7] = 0; + } + secs = t - p->ut_time; + mins = (secs / 60) % 60; + hours = (secs / 3600) % 24; + days = secs / 86400; + if (days) + sprintf(length, "(%d+%02d:%02d)", days, hours, mins); + else + sprintf(length, " (%02d:%02d)", hours, mins); + + switch(what) { + case R_CRASH: + sprintf(logouttime, "- crash"); + break; + case R_DOWN: + sprintf(logouttime, "- down "); + break; + case R_NOW: + length[0] = 0; + if (fulltime) + sprintf(logouttime, " still logged in"); + else { + sprintf(logouttime, " still"); + sprintf(length, "logged in"); + } + break; + case R_PHANTOM: + length[0] = 0; + if (fulltime) + sprintf(logouttime, " gone - no logout"); + else { + sprintf(logouttime, " gone"); + sprintf(length, "- no logout"); + } + break; + case R_REBOOT: + break; + case R_TIMECHANGE: + logouttime[0] = 0; + length[0] = 0; + break; + case R_NORMAL: + break; + } + + /* + * Look up host with DNS if needed. + */ + r = -1; + if (usedns || useip) + r = dns_lookup(domain, sizeof(domain), useip, p->ut_addr_v6); + if (r < 0) { + len = UT_HOSTSIZE; + if (len >= sizeof(domain)) len = sizeof(domain) - 1; + domain[0] = 0; + strncat(domain, p->ut_host, len); + } + + if (showhost) { +#if CHOP_DOMAIN + /* + * See if this is in our domain. + */ + if (!usedns && (s = strchr(p->ut_host, '.')) != NULL && + strcmp(s + 1, domainname) == 0) *s = 0; +#endif + if (!altlist) { + snprintf(final, sizeof(final), + fulltime ? + "%-8.8s %-12.12s %-16.16s %-24.24s %-26.26s %-12.12s\n" : + "%-8.8s %-12.12s %-16.16s %-16.16s %-7.7s %-12.12s\n", + p->ut_name, utline, + domain, logintime, logouttime, length); + } else { + snprintf(final, sizeof(final), + fulltime ? + "%-8.8s %-12.12s %-24.24s %-26.26s %-12.12s %s\n" : + "%-8.8s %-12.12s %-16.16s %-7.7s %-12.12s %s\n", + p->ut_name, utline, + logintime, logouttime, length, domain); + } + } else + snprintf(final, sizeof(final), + fulltime ? + "%-8.8s %-12.12s %-24.24s %-26.26s %-12.12s\n" : + "%-8.8s %-12.12s %-16.16s %-7.7s %-12.12s\n", + p->ut_name, utline, + logintime, logouttime, length); + + /* + * Print out "final" string safely. + */ + for (s = final; *s; s++) { + if (*s == '\n' || (*s >= 32 && (unsigned char)*s <= 126)) + putchar(*s); + else + putchar('*'); + } + + recsdone++; + if (maxrecs && recsdone >= maxrecs) + return 1; + + return 0; +} + + +/* + * show usage + */ +void usage(char *s) +{ + fprintf(stderr, "Usage: %s [-num | -n num] [-f file] " + "[-t YYYYMMDDHHMMSS] " + "[-R] [-adioxF] [username..] [tty..]\n", s); + exit(1); +} + +time_t parsetm(char *ts) +{ + struct tm u, origu; + time_t tm; + + memset(&tm, 0, sizeof(tm)); + + if (sscanf(ts, "%4d%2d%2d%2d%2d%2d", &u.tm_year, + &u.tm_mon, &u.tm_mday, &u.tm_hour, &u.tm_min, + &u.tm_sec) != 6) + return (time_t)-1; + + u.tm_year -= 1900; + u.tm_mon -= 1; + u.tm_isdst = -1; + + origu = u; + + if ((tm = mktime(&u)) == (time_t)-1) + return tm; + + /* + * Unfortunately mktime() is much more forgiving than + * it should be. For example, it'll gladly accept + * "30" as a valid month number. This behavior is by + * design, but we don't like it, so we want to detect + * it and complain. + */ + if (u.tm_year != origu.tm_year || + u.tm_mon != origu.tm_mon || + u.tm_mday != origu.tm_mday || + u.tm_hour != origu.tm_hour || + u.tm_min != origu.tm_min || + u.tm_sec != origu.tm_sec) + return (time_t)-1; + + return tm; +} + +int main(int argc, char **argv) +{ + FILE *fp; /* Filepointer of wtmp file */ + + struct utmp ut; /* Current utmp entry */ + struct utmp oldut; /* Old utmp entry to check for duplicates */ + struct utmplist *p; /* Pointer into utmplist */ + struct utmplist *next;/* Pointer into utmplist */ + + time_t lastboot = 0; /* Last boottime */ + time_t lastrch = 0; /* Last run level change */ + time_t lastdown; /* Last downtime */ + time_t begintime; /* When wtmp begins */ + int whydown = 0; /* Why we went down: crash or shutdown */ + + int c, x; /* Scratch */ + struct stat st; /* To stat the [uw]tmp file */ + int quit = 0; /* Flag */ + int down = 0; /* Down flag */ + int lastb = 0; /* Is this 'lastb' ? */ + int extended = 0; /* Lots of info. */ + char *altufile = NULL;/* Alternate wtmp */ + + time_t until = 0; /* at what time to stop parsing the file */ + + progname = mybasename(argv[0]); + + /* Process the arguments. */ + while((c = getopt(argc, argv, "f:n:RxadFiot:0123456789")) != EOF) + switch(c) { + case 'R': + showhost = 0; + break; + case 'x': + extended = 1; + break; + case 'n': + maxrecs = atoi(optarg); + break; + case 'o': + oldfmt = 1; + break; + case 'f': + if((altufile = malloc(strlen(optarg)+1)) == NULL) { + fprintf(stderr, "%s: out of memory\n", + progname); + exit(1); + } + strcpy(altufile, optarg); + break; + case 'd': + usedns++; + break; + case 'i': + useip++; + break; + case 'a': + altlist++; + break; + case 'F': + fulltime++; + break; + case 't': + if ((until = parsetm(optarg)) == (time_t)-1) { + fprintf(stderr, "%s: Invalid time value \"%s\"\n", + progname, optarg); + usage(progname); + } + break; + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + maxrecs = 10*maxrecs + c - '0'; + break; + default: + usage(progname); + break; + } + if (optind < argc) show = argv + optind; + + /* + * Which file do we want to read? + */ + if (strcmp(progname, "lastb") == 0) { + ufile = BTMP_FILE; + lastb = 1; + } else + ufile = WTMP_FILE; + if (altufile) + ufile = altufile; + time(&lastdown); + lastrch = lastdown; + + /* + * Fill in 'lastdate' + */ + lastdate = lastdown; + +#if CHOP_DOMAIN + /* + * Find out domainname. + * + * This doesn't work on modern systems, where only a DNS + * lookup of the result from hostname() will get you the domainname. + * Remember that domainname() is the NIS domainname, not DNS. + * So basically this whole piece of code is bullshit. + */ + hostname[0] = 0; + (void) gethostname(hostname, sizeof(hostname)); + if ((domainname = strchr(hostname, '.')) != NULL) domainname++; + if (domainname == NULL || domainname[0] == 0) { + hostname[0] = 0; + (void) getdomainname(hostname, sizeof(hostname)); + hostname[sizeof(hostname) - 1] = 0; + domainname = hostname; + if (strcmp(domainname, "(none)") == 0 || domainname[0] == 0) + domainname = NULL; + } +#endif + + /* + * Install signal handlers + */ + signal(SIGINT, int_handler); + signal(SIGQUIT, quit_handler); + + /* + * Open the utmp file + */ + if ((fp = fopen(ufile, "r")) == NULL) { + x = errno; + fprintf(stderr, "%s: %s: %s\n", progname, ufile, strerror(errno)); + if (altufile == NULL && x == ENOENT) + fprintf(stderr, "Perhaps this file was removed by the " + "operator to prevent logging %s info.\n", progname); + exit(1); + } + + /* + * Optimize the buffer size. + */ + setvbuf(fp, NULL, _IOFBF, UCHUNKSIZE); + + /* + * Read first structure to capture the time field + */ + if (uread(fp, &ut, NULL) == 1) + begintime = ut.ut_time; + else { + fstat(fileno(fp), &st); + begintime = st.st_ctime; + quit = 1; + } + + /* + * Go to end of file minus one structure + * and/or initialize utmp reading code. + */ + uread(fp, NULL, NULL); + + /* + * Read struct after struct backwards from the file. + */ + while(!quit) { + + if (uread(fp, &ut, &quit) != 1) + break; + + if (until && until < ut.ut_time) + continue; + + if (memcmp(&ut, &oldut, sizeof(struct utmp)) == 0) continue; + memcpy(&oldut, &ut, sizeof(struct utmp)); + lastdate = ut.ut_time; + + if (lastb) { + quit = list(&ut, ut.ut_time, R_NORMAL); + continue; + } + + /* + * Set ut_type to the correct type. + */ + if (strncmp(ut.ut_line, "~", 1) == 0) { + if (strncmp(ut.ut_user, "shutdown", 8) == 0) + ut.ut_type = SHUTDOWN_TIME; + else if (strncmp(ut.ut_user, "reboot", 6) == 0) + ut.ut_type = BOOT_TIME; + else if (strncmp(ut.ut_user, "runlevel", 8) == 0) + ut.ut_type = RUN_LVL; + } +#if 1 /*def COMPAT*/ + /* + * For stupid old applications that don't fill in + * ut_type correctly. + */ + else { + if (ut.ut_type != DEAD_PROCESS && + ut.ut_name[0] && ut.ut_line[0] && + strcmp(ut.ut_name, "LOGIN") != 0) + ut.ut_type = USER_PROCESS; + /* + * Even worse, applications that write ghost + * entries: ut_type set to USER_PROCESS but + * empty ut_name... + */ + if (ut.ut_name[0] == 0) + ut.ut_type = DEAD_PROCESS; + + /* + * Clock changes. + */ + if (strcmp(ut.ut_name, "date") == 0) { + if (ut.ut_line[0] == '|') ut.ut_type = OLD_TIME; + if (ut.ut_line[0] == '{') ut.ut_type = NEW_TIME; + } + } +#endif + + switch (ut.ut_type) { + case SHUTDOWN_TIME: + if (extended) { + strcpy(ut.ut_line, "system down"); + quit = list(&ut, lastboot, R_NORMAL); + } + lastdown = lastrch = ut.ut_time; + down = 1; + break; + case OLD_TIME: + case NEW_TIME: + if (extended) { + strcpy(ut.ut_line, + ut.ut_type == NEW_TIME ? "new time" : + "old time"); + quit = list(&ut, lastdown, R_TIMECHANGE); + } + break; + case BOOT_TIME: + strcpy(ut.ut_line, "system boot"); + quit = list(&ut, lastdown, R_REBOOT); + lastboot = ut.ut_time; + down = 1; + break; + case RUN_LVL: + x = ut.ut_pid & 255; + if (extended) { + sprintf(ut.ut_line, "(to lvl %c)", x); + quit = list(&ut, lastrch, R_NORMAL); + } + if (x == '0' || x == '6') { + lastdown = ut.ut_time; + down = 1; + ut.ut_type = SHUTDOWN_TIME; + } + lastrch = ut.ut_time; + break; + + case USER_PROCESS: + /* + * This was a login - show the first matching + * logout record and delete all records with + * the same ut_line. + */ + c = 0; + for (p = utmplist; p; p = next) { + next = p->next; + if (strncmp(p->ut.ut_line, ut.ut_line, + UT_LINESIZE) == 0) { + /* Show it */ + if (c == 0) { + quit = list(&ut, p->ut.ut_time, + R_NORMAL); + c = 1; + } + if (p->next) p->next->prev = p->prev; + if (p->prev) + p->prev->next = p->next; + else + utmplist = p->next; + free(p); + } + } + /* + * Not found? Then crashed, down, still + * logged in, or missing logout record. + */ + if (c == 0) { + if (lastboot == 0) { + c = R_NOW; + /* Is process still alive? */ + if (ut.ut_pid > 0 && + kill(ut.ut_pid, 0) != 0 && + errno == ESRCH) + c = R_PHANTOM; + } else + c = whydown; + quit = list(&ut, lastboot, c); + } + /* FALLTHRU */ + + case DEAD_PROCESS: + /* + * Just store the data if it is + * interesting enough. + */ + if (ut.ut_line[0] == 0) + break; + if ((p = malloc(sizeof(struct utmplist))) == NULL) { + fprintf(stderr, "%s: out of memory\n", + progname); + exit(1); + } + memcpy(&p->ut, &ut, sizeof(struct utmp)); + p->next = utmplist; + p->prev = NULL; + if (utmplist) utmplist->prev = p; + utmplist = p; + break; + + } + /* + * If we saw a shutdown/reboot record we can remove + * the entire current utmplist. + */ + if (down) { + lastboot = ut.ut_time; + whydown = (ut.ut_type == SHUTDOWN_TIME) ? R_DOWN : R_CRASH; + for (p = utmplist; p; p = next) { + next = p->next; + free(p); + } + utmplist = NULL; + down = 0; + } + } + printf("\n%s begins %s", mybasename(ufile), ctime(&begintime)); + + fclose(fp); + + /* + * Should we free memory here? Nah. This is not NT :) + */ + return 0; +} diff --git a/src/mesg.c b/src/mesg.c new file mode 100644 index 0000000..5bd6c0b --- /dev/null +++ b/src/mesg.c @@ -0,0 +1,124 @@ +/* + * mesg.c The "mesg" utility. Gives / restrict access to + * your terminal by others. + * + * Usage: mesg [y|n]. + * Without arguments prints out the current settings. + * + * This file is part of the sysvinit suite, + * Copyright (C) 1991-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 + */ +#include +#include +#include +#include +#include +#include +#include +#include + +char *Version = "@(#) mesg 2.81 31-Jul-2001 miquels@cistron.nl"; + +#define TTYGRP "tty" + +/* + * See if the system has a special 'tty' group. + * If it does, and the tty device is in that group, + * we set the modes to -rw--w--- instead if -rw--w--w. + */ +int hasttygrp(void) +{ + struct group *grp; + + if ((grp = getgrnam(TTYGRP)) != NULL) + return 1; + return 0; +} + + +/* + * See if the tty devices group is indeed 'tty' + */ +int tty_in_ttygrp(struct stat *st) +{ + struct group *gr; + + if ((gr = getgrgid(st->st_gid)) == NULL) + return 0; + if (strcmp(gr->gr_name, TTYGRP) != 0) + return 0; + + return 1; +} + +int main(int argc, char **argv) +{ + struct stat st; + unsigned int ttymode, st_mode_old; + int ht; + int it; + int e; + + if (!isatty(0)) { + /* Or should we look in /var/run/utmp? */ + fprintf(stderr, "stdin: is not a tty\n"); + return(1); + } + + if (fstat(0, &st) < 0) { + perror("fstat"); + return(1); + } + + ht = hasttygrp(); + it = tty_in_ttygrp(&st); + + if (argc < 2) { + ttymode = (ht && it) ? 020 : 002; + printf("is %s\n", (st.st_mode & ttymode) ? "y" : "n"); + return 0; + } + if (argc > 2 || (argv[1][0] != 'y' && argv[1][0] != 'n')) { + fprintf(stderr, "Usage: mesg [y|n]\n"); + return 1; + } + + /* + * Security check: allow mesg n when group is + * weird, but don't allow mesg y. + */ + ttymode = ht ? 020 : 022; + if (ht && !it && argv[1][0] == 'y') { + fprintf(stderr, "mesg: error: tty device is not owned " + "by group `%s'\n", TTYGRP); + exit(1); + } + + st_mode_old = st.st_mode; + if (argv[1][0] == 'y') + st.st_mode |= ttymode; + else + st.st_mode &= ~(ttymode); + if (st_mode_old != st.st_mode && fchmod(0, st.st_mode) != 0) { + e = errno; + fprintf(stderr, "mesg: %s: %s\n", + ttyname(0), strerror(e)); + exit(1); + } + + return 0; +} diff --git a/src/mountpoint.c b/src/mountpoint.c new file mode 100644 index 0000000..aca6c04 --- /dev/null +++ b/src/mountpoint.c @@ -0,0 +1,128 @@ +/* + * mountpoint See if a directory is a mountpoint. + * + * Author: Miquel van Smoorenburg. + * + * Version: @(#)mountpoint 2.85-12 17-Mar-2004 miquels@cistron.nl + * + * 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 +#include +#include +#include +#include +#include +#include +#include + +int dostat(char *path, struct stat *st, int do_lstat, int quiet) +{ + int n; + + if (do_lstat) + n = lstat(path, st); + else + n = stat(path, st); + + if (n != 0) { + if (!quiet) + fprintf(stderr, "mountpoint: %s: %s\n", path, + strerror(errno)); + return -1; + } + return 0; +} + +void usage(void) { + fprintf(stderr, "Usage: mountpoint [-q] [-d] [-x] path\n"); + exit(1); +} + +int main(int argc, char **argv) +{ + struct stat st, st2; + char buf[256]; + char *path; + int quiet = 0; + int showdev = 0; + int xdev = 0; + int c, r; + + while ((c = getopt(argc, argv, "dqx")) != EOF) switch(c) { + case 'd': + showdev = 1; + break; + case 'q': + quiet = 1; + break; + case 'x': + xdev = 1; + break; + default: + usage(); + break; + } + if (optind != argc - 1) usage(); + path = argv[optind]; + + if (dostat(path, &st, !xdev, quiet) < 0) + return 1; + + if (xdev) { +#ifdef __linux__ + if (!S_ISBLK(st.st_mode)) +#else + if (!S_ISBLK(st.st_mode) && !S_ISCHR(st.st_mode)) +#endif + { + if (quiet) + printf("\n"); + else + fprintf(stderr, "mountpoint: %s: not a block device\n", + path); + return 1; + } + printf("%u:%u\n", major(st.st_rdev), minor(st.st_rdev)); + return 0; + } + + if (!S_ISDIR(st.st_mode)) { + if (!quiet) + fprintf(stderr, "mountpoint: %s: not a directory\n", + path); + return 1; + } + + memset(buf, 0, sizeof(buf)); + strncpy(buf, path, sizeof(buf) - 4); + strcat(buf, "/.."); + if (dostat(buf, &st2, 0, quiet) < 0) + return 1; + + r = (st.st_dev != st2.st_dev) || + (st.st_dev == st2.st_dev && st.st_ino == st2.st_ino); + + if (!quiet && !showdev) + printf("%s is %sa mountpoint\n", path, r ? "" : "not "); + if (showdev) + printf("%u:%u\n", major(st.st_dev), minor(st.st_dev)); + + return r ? 0 : 1; +} diff --git a/src/oldutmp.h b/src/oldutmp.h new file mode 100644 index 0000000..b94029c --- /dev/null +++ b/src/oldutmp.h @@ -0,0 +1,41 @@ +/* + * oldutmp.h Definition of the old libc5 utmp structure. + * + * Version: @(#)oldutmp.h 1.00 29-Mar-1998 miquels@cistron.nl + * + * Copyright (C) 1991-2000 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 + * + */ +#ifndef OLD_UTMP_H +#define OLD_UTMP_H + +#define OLD_LINESIZE 12 +#define OLD_NAMESIZE 8 +#define OLD_HOSTSIZE 16 + +struct oldutmp { + short ut_type; + int ut_pid; + char ut_line[OLD_LINESIZE]; + char ut_id[4]; + long ut_oldtime; + char ut_user[OLD_NAMESIZE]; + char ut_host[OLD_HOSTSIZE]; + long ut_oldaddr; +}; + +#endif diff --git a/src/paths.h b/src/paths.h new file mode 100644 index 0000000..884df35 --- /dev/null +++ b/src/paths.h @@ -0,0 +1,49 @@ +/* + * paths.h Paths of files that init and related utilities need. + * + * Version: @(#) paths.h 2.85-8 05-Nov-2003 + * + * Author: Miquel van Smoorenburg, + * + * This file is part of the sysvinit suite, + * Copyright (C) 1991-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 + */ +#define VT_MASTER "/dev/tty0" /* Virtual console master */ +#define CONSOLE "/dev/console" /* Logical system console */ +#define SECURETTY "/etc/securetty" /* List of root terminals */ +#define SDALLOW "/etc/shutdown.allow" /* Users allowed to shutdown */ +#define INITTAB "/etc/inittab" /* Location of inittab */ +#define INIT "/sbin/init" /* Location of init itself. */ +#define NOLOGIN "/etc/nologin" /* Stop user logging in. */ +#define FASTBOOT "/fastboot" /* Enable fast boot. */ +#define FORCEFSCK "/forcefsck" /* Force fsck on boot */ +#define SDPID "/var/run/shutdown.pid" /* PID of shutdown program */ +#define SHELL "/bin/sh" /* Default shell */ +#define SULOGIN "/sbin/sulogin" /* Sulogin */ +#define INITSCRIPT "/etc/initscript" /* Initscript. */ +#define PWRSTAT "/etc/powerstatus" /* COMPAT: SIGPWR reason (OK/BAD) */ + +#if 0 +#define INITLVL "/etc/initrunlvl" /* COMPAT: New runlevel */ +#define INITLVL2 "/var/log/initrunlvl" /* COMPAT: New runlevel */ + /* Note: INITLVL2 definition needs INITLVL */ +#define HALTSCRIPT1 "/etc/init.d/halt" /* Called by "fast" shutdown */ +#define HALTSCRIPT2 "/etc/rc.d/rc.0" /* Called by "fast" shutdown */ +#define REBOOTSCRIPT1 "/etc/init.d/reboot" /* Ditto. */ +#define REBOOTSCRIPT2 "/etc/rc.d/rc.6" /* Ditto. */ +#endif + diff --git a/src/reboot.h b/src/reboot.h new file mode 100644 index 0000000..c7807ca --- /dev/null +++ b/src/reboot.h @@ -0,0 +1,51 @@ +/* + * reboot.h Headerfile that defines how to handle + * the reboot() system call. + * + * Version: @(#)reboot.h 2.85-17 04-Jun-2004 miquels@cistron.nl + * + * Copyright (C) (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 + +#ifdef RB_ENABLE_CAD +# define BMAGIC_HARD RB_ENABLE_CAD +#endif + +#ifdef RB_DISABLE_CAD +# define BMAGIC_SOFT RB_DISABLE_CAD +#endif + +#ifdef RB_HALT_SYSTEM +# define BMAGIC_HALT RB_HALT_SYSTEM +#else +# define BMAGIC_HALT RB_HALT +#endif + +#define BMAGIC_REBOOT RB_AUTOBOOT + +#ifdef RB_POWER_OFF +# define BMAGIC_POWEROFF RB_POWER_OFF +#elif defined(RB_POWEROFF) +# define BMAGIC_POWEROFF RB_POWEROFF +#else +# define BMAGIC_POWEROFF BMAGIC_HALT +#endif + +#define init_reboot(magic) reboot(magic) + diff --git a/src/runlevel.c b/src/runlevel.c new file mode 100644 index 0000000..65e4b31 --- /dev/null +++ b/src/runlevel.c @@ -0,0 +1,53 @@ +/* + * runlevel Prints out the previous and the current runlevel. + * + * Version: @(#)runlevel 1.20 16-Apr-1997 MvS + * + * This file is part of the sysvinit suite, + * Copyright (C) 1991-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 + */ + +#include +#include +#include +#include + +int main(argc, argv) +int argc; +char **argv; +{ + struct utmp *ut; + char prev; + + if (argc > 1) utmpname(argv[1]); + + setutent(); + while ((ut = getutent()) != NULL) { + if (ut->ut_type == RUN_LVL) { + prev = ut->ut_pid / 256; + if (prev == 0) prev = 'N'; + printf("%c %c\n", prev, ut->ut_pid % 256); + endutent(); + exit(0); + } + } + + printf("unknown\n"); + endutent(); + return(1); +} + diff --git a/src/set.h b/src/set.h new file mode 100644 index 0000000..724c35f --- /dev/null +++ b/src/set.h @@ -0,0 +1,28 @@ +/* + * set.h Macros that look like sigaddset et al. but + * aren't. They are used to manipulate bits in + * an integer, to do our signal bookeeping. + * + * Copyright (C) 2005 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 + * + */ + +#define ISMEMBER(set, val) ((set) & (1 << (val))) +#define DELSET(set, val) ((set) &= ~(1 << (val))) +#define ADDSET(set, val) ((set) |= (1 << (val))) +#define EMPTYSET(set) ((set) = 0) + diff --git a/src/shutdown.c b/src/shutdown.c new file mode 100644 index 0000000..e96b3a6 --- /dev/null +++ b/src/shutdown.c @@ -0,0 +1,727 @@ +/* + * shutdown.c Shut the system down. + * + * Usage: shutdown [-krhfnc] time [warning message] + * -k: don't really shutdown, only warn. + * -r: reboot after shutdown. + * -h: halt after shutdown. + * -f: do a 'fast' reboot (skip fsck). + * -F: Force fsck on reboot. + * -n: do not go through init but do it ourselves. + * -c: cancel an already running shutdown. + * -t secs: delay between SIGTERM and SIGKILL for init. + * + * Author: Miquel van Smoorenburg, miquels@cistron.nl + * + * Version: @(#)shutdown 2.86-1 31-Jul-2004 miquels@cistron.nl + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "paths.h" +#include "reboot.h" +#include "initreq.h" + +char *Version = "@(#) shutdown 2.86-1 31-Jul-2004 miquels@cistron.nl"; + +#define MESSAGELEN 256 + +int dontshut = 0; /* Don't shutdown, only warn */ +char down_level[2]; /* What runlevel to go to. */ +int dosync = 1; /* Sync before reboot or halt */ +int fastboot = 0; /* Do a 'fast' reboot */ +int forcefsck = 0; /* Force fsck on reboot */ +char message[MESSAGELEN]; /* Warning message */ +char *sltime = 0; /* Sleep time */ +char newstate[64]; /* What are we gonna do */ +int doself = 0; /* Don't use init */ +int got_alrm = 0; + +char *clean_env[] = { + "HOME=/", + "PATH=/bin:/usr/bin:/sbin:/usr/sbin", + "TERM=dumb", + NULL, +}; + +/* From "wall.c" */ +extern void wall(char *, int, int); + +/* From "utmp.c" */ +extern void write_wtmp(char *user, char *id, int pid, int type, char *line); + +/* + * Sleep without being interrupted. + */ +void hardsleep(int secs) +{ + struct timespec ts, rem; + + ts.tv_sec = secs; + ts.tv_nsec = 0; + + while(nanosleep(&ts, &rem) < 0 && errno == EINTR) + ts = rem; +} + +/* + * Break off an already running shutdown. + */ +void stopit(int sig) +{ + unlink(NOLOGIN); + unlink(FASTBOOT); + unlink(FORCEFSCK); + unlink(SDPID); + printf("\r\nShutdown cancelled.\r\n"); + exit(0); +} + +/* + * Show usage message. + */ +void usage(void) +{ + fprintf(stderr, + "Usage:\t shutdown [-akrhHPfnc] [-t secs] time [warning message]\n" + "\t\t -a: use /etc/shutdown.allow\n" + "\t\t -k: don't really shutdown, only warn.\n" + "\t\t -r: reboot after shutdown.\n" + "\t\t -h: halt after shutdown.\n" + "\t\t -P: halt action is to turn off power.\n" + "\t\t -H: halt action is to just halt.\n" + "\t\t -f: do a 'fast' reboot (skip fsck).\n" + "\t\t -F: Force fsck on reboot.\n" + "\t\t -n: do not go through \"init\" but go down real fast.\n" + "\t\t -c: cancel a running shutdown.\n" + "\t\t -t secs: delay between warning and kill signal.\n" + "\t\t ** the \"time\" argument is mandatory! (try \"now\") **\n"); + exit(1); +} + + +void alrm_handler(int sig) +{ + got_alrm = sig; +} + + +/* + * Set environment variables in the init process. + */ +int init_setenv(char *name, char *value) +{ + struct init_request request; + struct sigaction sa; + int fd; + int nl, vl; + + memset(&request, 0, sizeof(request)); + request.magic = INIT_MAGIC; + request.cmd = INIT_CMD_SETENV; + nl = strlen(name); + vl = value ? strlen(value) : 0; + + if (nl + vl + 3 >= sizeof(request.i.data)) + return -1; + + memcpy(request.i.data, name, nl); + if (value) { + request.i.data[nl] = '='; + memcpy(request.i.data + nl + 1, value, vl); + } + + /* + * Open the fifo and write the command. + * Make sure we don't hang on opening /dev/initctl + */ + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = alrm_handler; + sigaction(SIGALRM, &sa, NULL); + got_alrm = 0; + alarm(3); + if ((fd = open(INIT_FIFO, O_WRONLY)) >= 0 && + write(fd, &request, sizeof(request)) == sizeof(request)) { + close(fd); + alarm(0); + return 0; + } + + fprintf(stderr, "shutdown: "); + if (got_alrm) { + fprintf(stderr, "timeout opening/writing control channel %s\n", + INIT_FIFO); + } else { + perror(INIT_FIFO); + } + return -1; +} + + +/* + * Tell everyone the system is going down in 'mins' minutes. + */ +void warn(int mins) +{ + char buf[MESSAGELEN + sizeof(newstate)]; + int len; + + buf[0] = 0; + strncat(buf, message, sizeof(buf) - 1); + len = strlen(buf); + + if (mins == 0) + snprintf(buf + len, sizeof(buf) - len, + "\rThe system is going down %s NOW!\r\n", + newstate); + else + snprintf(buf + len, sizeof(buf) - len, + "\rThe system is going DOWN %s in %d minute%s!\r\n", + newstate, mins, mins == 1 ? "" : "s"); + wall(buf, 1, 0); +} + +/* + * Create the /etc/nologin file. + */ +void donologin(int min) +{ + FILE *fp; + time_t t; + + time(&t); + t += 60 * min; + + if ((fp = fopen(NOLOGIN, "w")) != NULL) { + fprintf(fp, "\rThe system is going down on %s\r\n", ctime(&t)); + if (message[0]) fputs(message, fp); + fclose(fp); + } +} + +/* + * Spawn an external program. + */ +int spawn(int noerr, char *prog, ...) +{ + va_list ap; + pid_t pid, rc; + int i; + char *argv[8]; + + i = 0; + while ((pid = fork()) < 0 && i < 10) { + perror("fork"); + sleep(5); + i++; + } + + if (pid < 0) return -1; + + if (pid > 0) { + while((rc = wait(&i)) != pid) + if (rc < 0 && errno == ECHILD) + break; + return (rc == pid) ? WEXITSTATUS(i) : -1; + } + + if (noerr) fclose(stderr); + + argv[0] = prog; + va_start(ap, prog); + for (i = 1; i < 7 && (argv[i] = va_arg(ap, char *)) != NULL; i++) + ; + argv[i] = NULL; + va_end(ap); + + chdir("/"); + environ = clean_env; + + execvp(argv[0], argv); + perror(argv[0]); + exit(1); + + /*NOTREACHED*/ + return 0; +} + +/* + * Kill all processes, call /etc/init.d/halt (if present) + */ +void fastdown() +{ + int do_halt = (down_level[0] == '0'); + int i; +#if 0 + char cmd[128]; + char *script; + + /* + * Currently, the halt script is either init.d/halt OR rc.d/rc.0, + * likewise for the reboot script. Test for the presence + * of either. + */ + if (do_halt) { + if (access(HALTSCRIPT1, X_OK) == 0) + script = HALTSCRIPT1; + else + script = HALTSCRIPT2; + } else { + if (access(REBOOTSCRIPT1, X_OK) == 0) + script = REBOOTSCRIPT1; + else + script = REBOOTSCRIPT2; + } +#endif + + /* First close all files. */ + for(i = 0; i < 3; i++) + if (!isatty(i)) { + close(i); + open("/dev/null", O_RDWR); + } + for(i = 3; i < 20; i++) close(i); + close(255); + + /* First idle init. */ + if (kill(1, SIGTSTP) < 0) { + fprintf(stderr, "shutdown: can't idle init.\r\n"); + exit(1); + } + + /* Kill all processes. */ + fprintf(stderr, "shutdown: sending all processes the TERM signal...\r\n"); + kill(-1, SIGTERM); + sleep(sltime ? atoi(sltime) : 3); + fprintf(stderr, "shutdown: sending all processes the KILL signal.\r\n"); + (void) kill(-1, SIGKILL); + +#if 0 + /* See if we can run /etc/init.d/halt */ + if (access(script, X_OK) == 0) { + spawn(1, cmd, "fast", NULL); + fprintf(stderr, "shutdown: %s returned - falling back " + "on default routines\r\n", script); + } +#endif + + /* script failed or not present: do it ourself. */ + sleep(1); /* Give init the chance to collect zombies. */ + + /* Record the fact that we're going down */ + write_wtmp("shutdown", "~~", 0, RUN_LVL, "~~"); + + /* This is for those who have quota installed. */ + spawn(1, "accton", NULL); + spawn(1, "quotaoff", "-a", NULL); + + sync(); + fprintf(stderr, "shutdown: turning off swap\r\n"); + spawn(0, "swapoff", "-a", NULL); + fprintf(stderr, "shutdown: unmounting all file systems\r\n"); + spawn(0, "umount", "-a", NULL); + + /* We're done, halt or reboot now. */ + if (do_halt) { + fprintf(stderr, "The system is halted. Press CTRL-ALT-DEL " + "or turn off power\r\n"); + init_reboot(BMAGIC_HALT); + exit(0); + } + + fprintf(stderr, "Please stand by while rebooting the system.\r\n"); + init_reboot(BMAGIC_REBOOT); + exit(0); +} + +/* + * Go to runlevel 0, 1 or 6. + */ +void shutdown(char *halttype) +{ + char *args[8]; + int argp = 0; + int do_halt = (down_level[0] == '0'); + + /* Warn for the last time */ + warn(0); + if (dontshut) { + hardsleep(1); + stopit(0); + } + openlog("shutdown", LOG_PID, LOG_USER); + if (do_halt) + syslog(LOG_NOTICE, "shutting down for system halt"); + else + syslog(LOG_NOTICE, "shutting down for system reboot"); + closelog(); + + /* See if we have to do it ourself. */ + if (doself) fastdown(); + + /* Create the arguments for init. */ + args[argp++] = INIT; + if (sltime) { + args[argp++] = "-t"; + args[argp++] = sltime; + } + args[argp++] = down_level; + args[argp] = (char *)NULL; + + unlink(SDPID); + unlink(NOLOGIN); + + /* Now execute init to change runlevel. */ + sync(); + init_setenv("INIT_HALT", halttype); + execv(INIT, args); + + /* Oops - failed. */ + fprintf(stderr, "\rshutdown: cannot execute %s\r\n", INIT); + unlink(FASTBOOT); + unlink(FORCEFSCK); + init_setenv("INIT_HALT", NULL); + openlog("shutdown", LOG_PID, LOG_USER); + syslog(LOG_NOTICE, "shutdown failed"); + closelog(); + exit(1); +} + +/* + * returns if a warning is to be sent for wt + */ +static int needwarning(int wt) +{ + int ret; + + if (wt < 10) + ret = 1; + else if (wt < 60) + ret = (wt % 15 == 0); + else if (wt < 180) + ret = (wt % 30 == 0); + else + ret = (wt % 60 == 0); + + return ret; +} + +/* + * Main program. + * Process the options and do the final countdown. + */ +int main(int argc, char **argv) +{ + FILE *fp; + extern int getopt(); + extern int optind; + struct sigaction sa; + struct tm *lt; + struct stat st; + struct utmp *ut; + time_t t; + uid_t realuid; + char *halttype; + char *downusers[32]; + char buf[128]; + char term[UT_LINESIZE + 6]; + char *sp; + char *when = NULL; + int c, i, wt; + int hours, mins; + int didnolog = 0; + int cancel = 0; + int useacl = 0; + int pid = 0; + int user_ok = 0; + + /* We can be installed setuid root (executable for a special group) */ + realuid = getuid(); + setuid(geteuid()); + + if (getuid() != 0) { + fprintf(stderr, "shutdown: you must be root to do that!\n"); + exit(1); + } + strcpy(down_level, "1"); + halttype = NULL; + + /* Process the options. */ + while((c = getopt(argc, argv, "HPacqkrhnfFyt:g:i:")) != EOF) { + switch(c) { + case 'H': + halttype = "HALT"; + break; + case 'P': + halttype = "POWERDOWN"; + break; + case 'a': /* Access control. */ + useacl = 1; + break; + case 'c': /* Cancel an already running shutdown. */ + cancel = 1; + break; + case 'k': /* Don't really shutdown, only warn.*/ + dontshut = 1; + break; + case 'r': /* Automatic reboot */ + down_level[0] = '6'; + break; + case 'h': /* Halt after shutdown */ + down_level[0] = '0'; + break; + case 'f': /* Don't perform fsck after next boot */ + fastboot = 1; + break; + case 'F': /* Force fsck after next boot */ + forcefsck = 1; + break; + case 'n': /* Don't switch runlevels. */ + doself = 1; + break; + case 't': /* Delay between TERM and KILL */ + sltime = optarg; + break; + case 'y': /* Ignored for sysV compatibility */ + break; + case 'g': /* sysv style to specify time. */ + when = optarg; + break; + case 'i': /* Level to go to. */ + if (!strchr("0156aAbBcCsS", optarg[0])) { + fprintf(stderr, + "shutdown: `%s': bad runlevel\n", + optarg); + exit(1); + } + down_level[0] = optarg[0]; + break; + default: + usage(); + break; + } + } + + if (NULL != halttype && down_level[0] != '0') { + fprintf(stderr, "shutdown: -H and -P flags can only be used along with -h flag.\n"); + usage(); + exit(1); + } + + /* Do we need to use the shutdown.allow file ? */ + if (useacl && (fp = fopen(SDALLOW, "r")) != NULL) { + + /* Read /etc/shutdown.allow. */ + i = 0; + while(fgets(buf, 128, fp)) { + if (buf[0] == '#' || buf[0] == '\n') continue; + if (i > 31) continue; + for(sp = buf; *sp; sp++) if (*sp == '\n') *sp = 0; + downusers[i++] = strdup(buf); + } + if (i < 32) downusers[i] = 0; + fclose(fp); + + /* Now walk through /var/run/utmp to find logged in users. */ + while(!user_ok && (ut = getutent()) != NULL) { + + /* See if this is a user process on a VC. */ + if (ut->ut_type != USER_PROCESS) continue; + sprintf(term, "/dev/%.*s", UT_LINESIZE, ut->ut_line); + if (stat(term, &st) < 0) continue; +#ifdef major /* glibc */ + if (major(st.st_rdev) != 4 || + minor(st.st_rdev) > 63) continue; +#else + if ((st.st_rdev & 0xFFC0) != 0x0400) continue; +#endif + /* Root is always OK. */ + if (strcmp(ut->ut_user, "root") == 0) { + user_ok++; + break; + } + + /* See if this is an allowed user. */ + for(i = 0; i < 32 && downusers[i]; i++) + if (!strncmp(downusers[i], ut->ut_user, + UT_NAMESIZE)) { + user_ok++; + break; + } + } + endutent(); + + /* See if user was allowed. */ + if (!user_ok) { + if ((fp = fopen(CONSOLE, "w")) != NULL) { + fprintf(fp, "\rshutdown: no authorized users " + "logged in.\r\n"); + fclose(fp); + } + exit(1); + } + } + + /* Read pid of running shutdown from a file */ + if ((fp = fopen(SDPID, "r")) != NULL) { + fscanf(fp, "%d", &pid); + fclose(fp); + } + + /* Read remaining words, skip time if needed. */ + message[0] = 0; + for(c = optind + (!cancel && !when); c < argc; c++) { + if (strlen(message) + strlen(argv[c]) + 4 > MESSAGELEN) + break; + strcat(message, argv[c]); + strcat(message, " "); + } + if (message[0]) strcat(message, "\r\n"); + + /* See if we want to run or cancel. */ + if (cancel) { + if (pid <= 0) { + fprintf(stderr, "shutdown: cannot find pid " + "of running shutdown.\n"); + exit(1); + } + init_setenv("INIT_HALT", NULL); + if (kill(pid, SIGINT) < 0) { + fprintf(stderr, "shutdown: not running.\n"); + exit(1); + } + if (message[0]) wall(message, 1, 0); + exit(0); + } + + /* Check syntax. */ + if (when == NULL) { + if (optind == argc) usage(); + when = argv[optind++]; + } + + /* See if we are already running. */ + if (pid > 0 && kill(pid, 0) == 0) { + fprintf(stderr, "\rshutdown: already running.\r\n"); + exit(1); + } + + /* Extra check. */ + if (doself && down_level[0] != '0' && down_level[0] != '6') { + fprintf(stderr, + "shutdown: can use \"-n\" for halt or reboot only.\r\n"); + exit(1); + } + + /* Tell users what we're gonna do. */ + switch(down_level[0]) { + case '0': + strcpy(newstate, "for system halt"); + break; + case '6': + strcpy(newstate, "for reboot"); + break; + case '1': + strcpy(newstate, "to maintenance mode"); + break; + default: + sprintf(newstate, "to runlevel %s", down_level); + break; + } + + /* Create a new PID file. */ + unlink(SDPID); + umask(022); + if ((fp = fopen(SDPID, "w")) != NULL) { + fprintf(fp, "%d\n", getpid()); + fclose(fp); + } else if (errno != EROFS) + fprintf(stderr, "shutdown: warning: cannot open %s\n", SDPID); + + /* + * Catch some common signals. + */ + signal(SIGQUIT, SIG_IGN); + signal(SIGCHLD, SIG_IGN); + signal(SIGHUP, SIG_IGN); + signal(SIGTSTP, SIG_IGN); + signal(SIGTTIN, SIG_IGN); + signal(SIGTTOU, SIG_IGN); + + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = stopit; + sigaction(SIGINT, &sa, NULL); + + /* Go to the root directory */ + chdir("/"); + if (fastboot) close(open(FASTBOOT, O_CREAT | O_RDWR, 0644)); + if (forcefsck) close(open(FORCEFSCK, O_CREAT | O_RDWR, 0644)); + + /* Alias now and take care of old '+mins' notation. */ + if (!strcmp(when, "now")) strcpy(when, "0"); + if (when[0] == '+') when++; + + /* Decode shutdown time. */ + for (sp = when; *sp; sp++) { + if (*sp != ':' && (*sp < '0' || *sp > '9')) + usage(); + } + if (strchr(when, ':') == NULL) { + /* Time in minutes. */ + wt = atoi(when); + if (wt == 0 && when[0] != '0') usage(); + } else { + /* Time in hh:mm format. */ + if (sscanf(when, "%d:%2d", &hours, &mins) != 2) usage(); + if (hours > 23 || mins > 59) usage(); + time(&t); + lt = localtime(&t); + wt = (60*hours + mins) - (60*lt->tm_hour + lt->tm_min); + if (wt < 0) wt += 1440; + } + /* Shutdown NOW if time == 0 */ + if (wt == 0) shutdown(halttype); + + /* Give warnings on regular intervals and finally shutdown. */ + if (wt < 15 && !needwarning(wt)) warn(wt); + while(wt) { + if (wt <= 5 && !didnolog) { + donologin(wt); + didnolog++; + } + if (needwarning(wt)) warn(wt); + hardsleep(60); + wt--; + } + shutdown(halttype); + + return 0; /* Never happens */ +} diff --git a/src/sulogin.c b/src/sulogin.c new file mode 100644 index 0000000..dcc049b --- /dev/null +++ b/src/sulogin.c @@ -0,0 +1,505 @@ +/* + * sulogin This program gives Linux machines a reasonable + * secure way to boot single user. It forces the + * user to supply the root password before a + * shell is started. + * + * If there is a shadow password file and the + * encrypted root password is "x" the shadow + * password will be used. + * + * Version: @(#)sulogin 2.85-3 23-Apr-2003 miquels@cistron.nl + * + * 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 + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if defined(__GLIBC__) +# include +#endif + +#ifdef WITH_SELINUX +# include +# include +#endif + +#define CHECK_DES 1 +#define CHECK_MD5 1 + +#define F_PASSWD "/etc/passwd" +#define F_SHADOW "/etc/shadow" +#define BINSH "/bin/sh" +#define STATICSH "/bin/sash" + +char *Version = "@(#)sulogin 2.85-3 23-Apr-2003 miquels@cistron.nl"; + +int timeout = 0; +int profile = 0; + +#ifndef IUCLC +# define IUCLC 0 +#endif + +#if 0 +/* + * Fix the tty modes and set reasonable defaults. + * (I'm not sure if this is needed under Linux, but..) + */ +void fixtty(void) +{ + struct termios tty; + + tcgetattr(0, &tty); + + /* + * Set or adjust tty modes. + */ + tty.c_iflag &= ~(INLCR|IGNCR|IUCLC); + tty.c_iflag |= ICRNL; + tty.c_oflag &= ~(OCRNL|OLCUC|ONOCR|ONLRET|OFILL); + tty.c_oflag |= OPOST|ONLCR; + tty.c_cflag |= CLOCAL; + tty.c_lflag = ISIG|ICANON|ECHO|ECHOE|ECHOK|ECHOCTL|ECHOKE; + + /* + * Set the most important characters */ + */ + tty.c_cc[VINTR] = 3; + tty.c_cc[VQUIT] = 28; + tty.c_cc[VERASE] = 127; + tty.c_cc[VKILL] = 24; + tty.c_cc[VEOF] = 4; + tty.c_cc[VTIME] = 0; + tty.c_cc[VMIN] = 1; + tty.c_cc[VSTART] = 17; + tty.c_cc[VSTOP] = 19; + tty.c_cc[VSUSP] = 26; + + tcsetattr(0, TCSANOW, &tty); +} +#endif + + +/* + * Called at timeout. + */ +void alrm_handler() +{ +} + +/* + * See if an encrypted password is valid. The encrypted + * password is checked for traditional-style DES and + * FreeBSD-style MD5 encryption. + */ +int valid(char *pass) +{ + char *s; + int len; + + if (pass[0] == 0) return 1; +#if CHECK_MD5 + /* + * 3 bytes for the signature $1$ + * up to 8 bytes for the salt + * $ + * the MD5 hash (128 bits or 16 bytes) encoded in base64 = 22 bytes + */ + if (strncmp(pass, "$1$", 3) == 0) { + for(s = pass + 3; *s && *s != '$'; s++) + ; + if (*s++ != '$') return 0; + len = strlen(s); + if (len < 22 || len > 24) return 0; + + return 1; + } +#endif +#if CHECK_DES + if (strlen(pass) != 13) return 0; + for (s = pass; *s; s++) { + if ((*s < '0' || *s > '9') && + (*s < 'a' || *s > 'z') && + (*s < 'A' || *s > 'Z') && + *s != '.' && *s != '/') return 0; + } +#endif + return 1; +} + +/* + * Set a variable if the value is not NULL. + */ +void set(char **var, char *val) +{ + if (val) *var = val; +} + +/* + * Get the root password entry. + */ +struct passwd *getrootpwent(int try_manually) +{ + static struct passwd pwd; + struct passwd *pw; + struct spwd *spw; + FILE *fp; + static char line[256]; + static char sline[256]; + char *p; + + /* + * First, we try to get the password the standard + * way using normal library calls. + */ + if ((pw = getpwnam("root")) && + !strcmp(pw->pw_passwd, "x") && + (spw = getspnam("root"))) + pw->pw_passwd = spw->sp_pwdp; + if (pw || !try_manually) return pw; + + /* + * If we come here, we could not retrieve the root + * password through library calls and we try to + * read the password and shadow files manually. + */ + pwd.pw_name = "root"; + pwd.pw_passwd = ""; + pwd.pw_gecos = "Super User"; + pwd.pw_dir = "/"; + pwd.pw_shell = ""; + pwd.pw_uid = 0; + pwd.pw_gid = 0; + + if ((fp = fopen(F_PASSWD, "r")) == NULL) { + perror(F_PASSWD); + return &pwd; + } + + /* + * Find root in the password file. + */ + while((p = fgets(line, 256, fp)) != NULL) { + if (strncmp(line, "root:", 5) != 0) + continue; + p += 5; + set(&pwd.pw_passwd, strsep(&p, ":")); + (void)strsep(&p, ":"); + (void)strsep(&p, ":"); + set(&pwd.pw_gecos, strsep(&p, ":")); + set(&pwd.pw_dir, strsep(&p, ":")); + set(&pwd.pw_shell, strsep(&p, "\n")); + p = line; + break; + } + fclose(fp); + + /* + * If the encrypted password is valid + * or not found, return. + */ + if (p == NULL) { + fprintf(stderr, "%s: no entry for root\n", F_PASSWD); + return &pwd; + } + if (valid(pwd.pw_passwd)) return &pwd; + + /* + * The password is invalid. If there is a + * shadow password, try it. + */ + strcpy(pwd.pw_passwd, ""); + if ((fp = fopen(F_SHADOW, "r")) == NULL) { + fprintf(stderr, "%s: root password garbled\n", F_PASSWD); + return &pwd; + } + while((p = fgets(sline, 256, fp)) != NULL) { + if (strncmp(sline, "root:", 5) != 0) + continue; + p += 5; + set(&pwd.pw_passwd, strsep(&p, ":")); + break; + } + fclose(fp); + + /* + * If the password is still invalid, + * NULL it, and return. + */ + if (p == NULL) { + fprintf(stderr, "%s: no entry for root\n", F_SHADOW); + strcpy(pwd.pw_passwd, ""); + } + if (!valid(pwd.pw_passwd)) { + fprintf(stderr, "%s: root password garbled\n", F_SHADOW); + strcpy(pwd.pw_passwd, ""); } + return &pwd; +} + +/* + * Ask for the password. Note that there is no + * default timeout as we normally skip this during boot. + */ +char *getpasswd(char *crypted) +{ + struct sigaction sa; + struct termios old, tty; + static char pass[128]; + char *ret = pass; + int i; + + if (crypted[0]) + printf("Give root password for maintenance\n"); + else + printf("Press enter for maintenance\n"); + printf("(or type Control-D to continue): "); + fflush(stdout); + + tcgetattr(0, &old); + tcgetattr(0, &tty); + tty.c_iflag &= ~(IUCLC|IXON|IXOFF|IXANY); + tty.c_lflag &= ~(ECHO|ECHOE|ECHOK|ECHONL|TOSTOP); + tcsetattr(0, TCSANOW, &tty); + + pass[sizeof(pass) - 1] = 0; + + sa.sa_handler = alrm_handler; + sa.sa_flags = 0; + sigaction(SIGALRM, &sa, NULL); + if (timeout) alarm(timeout); + + if (read(0, pass, sizeof(pass) - 1) <= 0) + ret = NULL; + else { + for(i = 0; i < sizeof(pass) && pass[i]; i++) + if (pass[i] == '\r' || pass[i] == '\n') { + pass[i] = 0; + break; + } + } + alarm(0); + tcsetattr(0, TCSANOW, &old); + printf("\n"); + + return ret; +} + +/* + * Password was OK, execute a shell. + */ +void sushell(struct passwd *pwd) +{ + char shell[128]; + char home[128]; + char *p; + char *sushell; + + /* + * Set directory and shell. + */ + (void)chdir(pwd->pw_dir); + if ((p = getenv("SUSHELL")) != NULL) + sushell = p; + else if ((p = getenv("sushell")) != NULL) + sushell = p; + else { + if (pwd->pw_shell[0]) + sushell = pwd->pw_shell; + else + sushell = BINSH; + } + if ((p = strrchr(sushell, '/')) == NULL) + p = sushell; + else + p++; + snprintf(shell, sizeof(shell), profile ? "-%s" : "%s", p); + + /* + * Set some important environment variables. + */ + getcwd(home, sizeof(home)); + setenv("HOME", home, 1); + setenv("LOGNAME", "root", 1); + setenv("USER", "root", 1); + if (!profile) + setenv("SHLVL","0",1); + + /* + * Try to execute a shell. + */ + setenv("SHELL", sushell, 1); + signal(SIGINT, SIG_DFL); + signal(SIGTSTP, SIG_DFL); + signal(SIGQUIT, SIG_DFL); +#ifdef WITH_SELINUX + if (is_selinux_enabled > 0) { + security_context_t scon=NULL; + char *seuser=NULL; + char *level=NULL; + if (getseuserbyname("root", &seuser, &level) == 0) + if (get_default_context_with_level(seuser, level, 0, &scon) > 0) { + if (setexeccon(scon) != 0) + fprintf(stderr, "setexeccon faile\n"); + freecon(scon); + } + free(seuser); + free(level); + } +#endif + execl(sushell, shell, NULL); + perror(sushell); + + setenv("SHELL", BINSH, 1); + execl(BINSH, profile ? "-sh" : "sh", NULL); + perror(BINSH); + + /* Fall back to staticly linked shell if both the users shell + and /bin/sh failed to execute. */ + setenv("SHELL", STATICSH, 1); + execl(STATICSH, STATICSH, NULL); + perror(STATICSH); +} + +void usage(void) +{ + fprintf(stderr, "Usage: sulogin [-e] [-p] [-t timeout] [tty device]\n"); +} + +int main(int argc, char **argv) +{ + char *tty = NULL; + char *p; + struct passwd *pwd; + int c, fd = -1; + int opt_e = 0; + pid_t pid, pgrp, ppgrp, ttypgrp; + + /* + * See if we have a timeout flag. + */ + opterr = 0; + while((c = getopt(argc, argv, "ept:")) != EOF) switch(c) { + case 't': + timeout = atoi(optarg); + break; + case 'p': + profile = 1; + break; + case 'e': + opt_e = 1; + break; + default: + usage(); + /* Do not exit! */ + break; + } + + if (geteuid() != 0) { + fprintf(stderr, "sulogin: only root can run sulogin.\n"); + exit(1); + } + + /* + * See if we need to open an other tty device. + */ + signal(SIGINT, SIG_IGN); + signal(SIGQUIT, SIG_IGN); + signal(SIGTSTP, SIG_IGN); + if (optind < argc) tty = argv[optind]; + if (tty) { + if ((fd = open(tty, O_RDWR)) < 0) { + perror(tty); + } else if (!isatty(fd)) { + fprintf(stderr, "%s: not a tty\n", tty); + close(fd); + } else { + + /* + * Only go through this trouble if the new + * tty doesn't fall in this process group. + */ + pid = getpid(); + pgrp = getpgid(0); + ppgrp = getpgid(getppid()); + ioctl(fd, TIOCGPGRP, &ttypgrp); + + if (pgrp != ttypgrp && ppgrp != ttypgrp) { + if (pid != getsid(0)) { + if (pid == getpgid(0)) + setpgid(0, getpgid(getppid())); + setsid(); + } + + signal(SIGHUP, SIG_IGN); + ioctl(0, TIOCNOTTY, (char *)1); + signal(SIGHUP, SIG_DFL); + close(0); + close(1); + close(2); + close(fd); + fd = open(tty, O_RDWR); + ioctl(0, TIOCSCTTY, (char *)1); + dup(fd); + dup(fd); + } else + close(fd); + } + } else if (getpid() == 1) { + /* We are init. We hence need to set a session anyway */ + setsid(); + if (ioctl(0, TIOCSCTTY, (char *)1)) + perror("ioctl(TIOCSCTTY)"); + } + + /* + * Get the root password. + */ + if ((pwd = getrootpwent(opt_e)) == NULL) { + fprintf(stderr, "sulogin: cannot open password database!\n"); + sleep(2); + } + + /* + * Ask for the password. + */ + while(pwd) { + if ((p = getpasswd(pwd->pw_passwd)) == NULL) break; + if (pwd->pw_passwd[0] == 0 || + strcmp(crypt(p, pwd->pw_passwd), pwd->pw_passwd) == 0) + sushell(pwd); + printf("Login incorrect.\n"); + } + + /* + * User pressed Control-D. + */ + return 0; +} + diff --git a/src/utmp.c b/src/utmp.c new file mode 100644 index 0000000..5a0c101 --- /dev/null +++ b/src/utmp.c @@ -0,0 +1,224 @@ +/* + * utmp.c Routines to read/write the utmp and wtmp files. + * Basically just wrappers around the library routines. + * + * Version: @(#)utmp.c 2.77 09-Jun-1999 miquels@cistron.nl + * + * Copyright (C) 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 + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "init.h" +#include "initreq.h" +#include "paths.h" + + +#if defined(__GLIBC__) +# if (__GLIBC__ == 2) && (__GLIBC_MINOR__ == 0) && defined(__powerpc__) +# define HAVE_UPDWTMP 0 +# else +# define HAVE_UPDWTMP 1 +# endif +#else +# define HAVE_UPDWTMP 0 +#endif + + +/* + * Log an event in the wtmp file (reboot, runlevel) + */ +void write_wtmp( +char *user, /* name of user */ +char *id, /* inittab ID */ +int pid, /* PID of process */ +int type, /* TYPE of entry */ +char *line) /* Which line is this */ +{ + int fd; + struct utmp utmp; + struct utsname uname_buf; + struct timeval tv; + + /* + * Try to open the wtmp file. Note that we even try + * this if we have updwtmp() so we can see if the + * wtmp file is accessible. + */ + if ((fd = open(WTMP_FILE, O_WRONLY|O_APPEND)) < 0) return; + +#ifdef INIT_MAIN + /* + * Note if we are going to write a boot record. + */ + if (type == BOOT_TIME) wrote_wtmp_reboot++; + + /* + * See if we need to write a reboot record. The reason that + * we are being so paranoid is that when we first tried to + * write the reboot record, /var was possibly not mounted + * yet. As soon as we can open WTMP we write a delayed boot record. + */ + if (wrote_wtmp_reboot == 0 && type != BOOT_TIME) + write_wtmp("reboot", "~~", 0, BOOT_TIME, "~"); +#endif + + /* + * Zero the fields and enter new fields. + */ + memset(&utmp, 0, sizeof(utmp)); +#if defined(__GLIBC__) + gettimeofday(&tv, NULL); + utmp.ut_tv.tv_sec = tv.tv_sec; + utmp.ut_tv.tv_usec = tv.tv_usec; +#else + time(&utmp.ut_time); +#endif + utmp.ut_pid = pid; + utmp.ut_type = type; + strncpy(utmp.ut_name, user, sizeof(utmp.ut_name)); + strncpy(utmp.ut_id , id , sizeof(utmp.ut_id )); + strncpy(utmp.ut_line, line, sizeof(utmp.ut_line)); + + /* Put the OS version in place of the hostname */ + if (uname(&uname_buf) == 0) + strncpy(utmp.ut_host, uname_buf.release, sizeof(utmp.ut_host)); + +#if HAVE_UPDWTMP + updwtmp(WTMP_FILE, &utmp); +#else + write(fd, (char *)&utmp, sizeof(utmp)); +#endif + close(fd); +} + +/* + * Write an entry to the UTMP file. For DEAD_PROCESS, put + * the previous ut_line into oldline if oldline != NULL. + */ +static void write_utmp( +char *user, /* name of user */ +char *id, /* inittab ID */ +int pid, /* PID of process */ +int type, /* TYPE of entry */ +char *line, /* LINE if used. */ +char *oldline) /* Line of old utmp entry. */ +{ + struct utmp utmp; + struct utmp tmp; + struct utmp *utmptr; + struct timeval tv; + + /* + * Can't do much if UTMP_FILE is not present. + */ + if (access(UTMP_FILE, F_OK) < 0) + return; + +#ifdef INIT_MAIN + /* + * Note if we are going to write a boot record. + */ + if (type == BOOT_TIME) wrote_utmp_reboot++; + + /* + * See if we need to write a reboot record. The reason that + * we are being so paranoid is that when we first tried to + * write the reboot record, /var was possibly not mounted + * yet. As soon as we can open WTMP we write a delayed boot record. + */ + if (wrote_utmp_reboot == 0 && type != BOOT_TIME) + write_utmp("reboot", "~~", 0, BOOT_TIME, "~", NULL); +#endif + + /* + * Fill out an utmp struct. + */ + memset(&utmp, 0, sizeof(utmp)); + utmp.ut_type = type; + utmp.ut_pid = pid; + strncpy(utmp.ut_id, id, sizeof(utmp.ut_id)); +#if defined(__GLIBC__) + gettimeofday(&tv, NULL); + utmp.ut_tv.tv_sec = tv.tv_sec; + utmp.ut_tv.tv_usec = tv.tv_usec; +#else + time(&utmp.ut_time); +#endif + strncpy(utmp.ut_user, user, UT_NAMESIZE); + if (line) strncpy(utmp.ut_line, line, UT_LINESIZE); + + /* + * We might need to find the existing entry first, to + * find the tty of the process (for wtmp accounting). + */ + if (type == DEAD_PROCESS) { + /* + * Find existing entry for the tty line. + */ + setutent(); + tmp = utmp; + if ((utmptr = getutid(&tmp)) != NULL) { + strncpy(utmp.ut_line, utmptr->ut_line, UT_LINESIZE); + if (oldline) + strncpy(oldline, utmptr->ut_line, UT_LINESIZE); + } + } + + /* + * Update existing utmp file. + */ + setutent(); + pututline(&utmp); + endutent(); +} + +/* + * Write a record to both utmp and wtmp. + */ +void write_utmp_wtmp( +char *user, /* name of user */ +char *id, /* inittab ID */ +int pid, /* PID of process */ +int type, /* TYPE of entry */ +char *line) /* LINE if used. */ +{ + char oldline[UT_LINESIZE]; + + /* + * For backwards compatibility we just return + * if user == NULL (means : clean up utmp file). + */ + if (user == NULL) + return; + + oldline[0] = 0; + write_utmp(user, id, pid, type, line, oldline); + write_wtmp(user, id, pid, type, line && line[0] ? line : oldline); +} + diff --git a/src/utmpdump.c b/src/utmpdump.c new file mode 100644 index 0000000..d9174c6 --- /dev/null +++ b/src/utmpdump.c @@ -0,0 +1,298 @@ +/* + * utmpdump Simple program to dump UTMP and WTMP files in + * raw format, so they can be examined. + * + * Author: Miquel van Smoorenburg, + * Danek Duvall + * + * Version: @(#)utmpdump 2.79 12-Sep-2000 + * + * This file is part of the sysvinit suite, + * Copyright (C) 1991-2000 Miquel van Smoorenburg. + * + * Additional Copyright on this file 1998 Danek Duvall. + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include "oldutmp.h" + +struct utmp +oldtonew(struct oldutmp src) +{ + struct utmp dest; + + memset(&dest, 0, sizeof dest); + dest.ut_type = src.ut_type; + dest.ut_pid = src.ut_pid; + dest.ut_time = src.ut_oldtime; + dest.ut_addr = src.ut_oldaddr; + strncpy(dest.ut_id, src.ut_id, 4); + strncpy(dest.ut_line, src.ut_line, OLD_LINESIZE); + strncpy(dest.ut_user, src.ut_user, OLD_NAMESIZE); + strncpy(dest.ut_host, src.ut_host, OLD_HOSTSIZE); + + return dest; +} + +struct oldutmp +newtoold(struct utmp src) +{ + struct oldutmp dest; + + memset(&dest, 0, sizeof dest); + dest.ut_type = src.ut_type; + dest.ut_pid = src.ut_pid; + dest.ut_oldtime = src.ut_time; + dest.ut_oldaddr = src.ut_addr; + strncpy(dest.ut_id, src.ut_id, 4); + strncpy(dest.ut_line, src.ut_line, OLD_LINESIZE); + strncpy(dest.ut_user, src.ut_user, OLD_NAMESIZE); + strncpy(dest.ut_host, src.ut_host, OLD_HOSTSIZE); + + return dest; +} + +char * +timetostr(const time_t time) +{ + static char s[29]; /* [Sun Sep 01 00:00:00 1998 PST] */ + + if (time != 0) + strftime(s, 29, "%a %b %d %T %Y %Z", localtime(&time)); + else + s[0] = '\0'; + + return s; +} + +time_t +strtotime(const char *s_time) +{ + struct tm tm; + + memset(&tm, '\0', sizeof(struct tm)); + + if (s_time[0] == ' ' || s_time[0] == '\0') + return (time_t)0; + + strptime(s_time, "%a %b %d %T %Y", &tm); + + /* Cheesy way of checking for DST */ + if (s_time[26] == 'D') + tm.tm_isdst = 1; + + return mktime(&tm); +} + +#define cleanse(x) xcleanse(x, sizeof(x)) +void +xcleanse(char *s, int len) +{ + for ( ; *s && len-- > 0; s++) + if (!isprint(*s) || *s == '[' || *s == ']') + *s = '?'; +} + +void +unspace(char *s, int len) +{ + while (*s && *s != ' ' && len--) + ++s; + + if (len > 0) + *s = '\0'; +} + +void +print_utline(struct utmp ut) +{ + char *addr_string, *time_string; + struct in_addr in; + + in.s_addr = ut.ut_addr; + addr_string = inet_ntoa(in); + time_string = timetostr(ut.ut_time); + cleanse(ut.ut_id); + cleanse(ut.ut_user); + cleanse(ut.ut_line); + cleanse(ut.ut_host); + + /* pid id user line host addr time */ + printf("[%d] [%05d] [%-4.4s] [%-*.*s] [%-*.*s] [%-*.*s] [%-15.15s] [%-28.28s]\n", + ut.ut_type, ut.ut_pid, ut.ut_id, 8, UT_NAMESIZE, ut.ut_user, + 12, UT_LINESIZE, ut.ut_line, 20, UT_HOSTSIZE, ut.ut_host, + addr_string, time_string); +} + +void +dump(FILE *fp, int forever, int oldfmt) +{ + struct utmp ut; + struct oldutmp uto; + + if (forever) + fseek(fp, -10 * (oldfmt ? sizeof uto : sizeof ut), SEEK_END); + + do { + if (oldfmt) + while (fread(&uto, sizeof uto, 1, fp) == 1) + print_utline(oldtonew(uto)); + else + while (fread(&ut, sizeof ut, 1, fp) == 1) + print_utline(ut); + if (forever) sleep(1); + } while (forever); +} + +/* This function won't work properly if there's a ']' or a ' ' in the real + * token. Thankfully, this should never happen. */ +int +gettok(char *line, char *dest, int size, int eatspace) +{ + int bpos, epos, eaten; + char *t; + + bpos = strchr(line, '[') - line; + if (bpos < 0) { + fprintf(stderr, "Extraneous newline in file. Exiting."); + exit(1); + } + line += 1 + bpos; + + epos = strchr(line, ']') - line; + if (epos < 0) { + fprintf(stderr, "Extraneous newline in file. Exiting."); + exit(1); + } + line[epos] = '\0'; + + eaten = bpos + epos + 1; + + if (eatspace) + if ((t = strchr(line, ' '))) + *t = 0; + + strncpy(dest, line, size); + + return eaten + 1; +} + +void +undump(FILE *fp, int forever, int oldfmt) +{ + struct utmp ut; + struct oldutmp uto; + char s_addr[16], s_time[29], *linestart, *line; + int count = 0; + + line = linestart = malloc(1024 * sizeof *linestart); + s_addr[15] = 0; + s_time[28] = 0; + + while(fgets(linestart, 1023, fp)) + { + line = linestart; + memset(&ut, '\0', sizeof(ut)); + sscanf(line, "[%hd] [%d] [%4c] ", &ut.ut_type, &ut.ut_pid, ut.ut_id); + + line += 19; + line += gettok(line, ut.ut_user, sizeof(ut.ut_user), 1); + line += gettok(line, ut.ut_line, sizeof(ut.ut_line), 1); + line += gettok(line, ut.ut_host, sizeof(ut.ut_host), 1); + line += gettok(line, s_addr, sizeof(s_addr)-1, 1); + line += gettok(line, s_time, sizeof(s_time)-1, 0); + + ut.ut_addr = inet_addr(s_addr); + ut.ut_time = strtotime(s_time); + + if (oldfmt) { + uto = newtoold(ut); + fwrite(&uto, sizeof(uto), 1, stdout); + } else + fwrite(&ut, sizeof(ut), 1, stdout); + + ++count; + } + + free(linestart); +} + +void +usage(int result) +{ + printf("Usage: utmpdump [ -froh ] [ filename ]\n"); + exit(result); +} + +int main(int argc, char **argv) +{ + int c; + FILE *fp; + int reverse = 0, forever = 0, oldfmt = 0; + + while ((c = getopt(argc, argv, "froh")) != EOF) { + switch (c) { + case 'r': + reverse = 1; + break; + + case 'f': + forever = 1; + break; + + case 'o': + oldfmt = 1; + break; + + case 'h': + usage(0); + break; + + default: + usage(1); + } + } + + if (optind < argc) { + fprintf(stderr, "Utmp %sdump of %s\n", reverse ? "un" : "", argv[optind]); + if ((fp = fopen(argv[optind], "r")) == NULL) { + perror("Unable to open file"); + exit(1); + } + } + else { + fprintf(stderr, "Utmp %sdump of stdin\n", reverse ? "un" : ""); + fp = stdin; + } + + if (reverse) + undump(fp, forever, oldfmt); + else + dump(fp, forever, oldfmt); + + fclose(fp); + + return 0; +} diff --git a/src/wall.c b/src/wall.c new file mode 100644 index 0000000..12176dc --- /dev/null +++ b/src/wall.c @@ -0,0 +1,123 @@ +/* + * wall.c Write to all users logged in. + * + * Usage: wall [text] + * + * Version: @(#)wall 2.79 12-Sep-2000 miquels@cistron.nl + * + * This file is part of the sysvinit suite, + * Copyright (C) 1991-2000 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 +#include +#include +#include +#include +#include + + +char *Version = "@(#) wall 2.79 12-Sep-2000 miquels@cistron.nl"; +#define MAXLEN 4096 +#define MAXLINES 20 + +extern void wall(char *, int, int); + +int main(int argc, char **argv) +{ + char buf[MAXLEN]; + char line[83]; + int i, f, ch; + int len = 0; + int remote = 0; + char *p; + char *whoami; + struct passwd *pwd; + + buf[0] = 0; + if ((pwd = getpwuid(getuid())) == NULL) { + if (getuid() == 0) + whoami = "root"; + else { + fprintf(stderr, "You don't exist. Go away.\n"); + exit(1); + } + } else + whoami = pwd->pw_name; + + while((ch = getopt(argc, argv, "n")) != EOF) + switch(ch) { + case 'n': + /* + * Undocumented option for suppressing + * banner from rpc.rwalld. Only works if + * we are root or if we're NOT setgid. + */ + if (geteuid() != 0 && getgid() != getegid()) { + fprintf(stderr, "wall -n: not priviliged\n"); + exit(1); + } + remote = 1; + break; + default: + fprintf(stderr, "usage: wall [message]\n"); + return 1; + break; + } + + if ((argc - optind) > 0) { + for(f = optind; f < argc; f++) { + len += strlen(argv[f]) + 1; + if (len >= MAXLEN-2) break; + strcat(buf, argv[f]); + if (f < argc-1) strcat(buf, " "); + } + strcat(buf, "\r\n"); + } else { + while(fgets(line, 80, stdin)) { + /* + * Make sure that line ends in \r\n + */ + for(p = line; *p && *p != '\r' && *p != '\n'; p++) + ; + strcpy(p, "\r\n"); + len += strlen(line); + if (len >= MAXLEN) break; + strcat(buf, line); + } + } + + i = 0; + for (p = buf; *p; p++) { + if (*p == '\n' && i++ > MAXLINES) { + *++p = 0; + break; + } + } + + openlog("wall", LOG_PID, LOG_USER); + syslog(LOG_INFO, "wall: user %s broadcasted %d lines (%d chars)", + whoami, i, strlen(buf)); + closelog(); + + unsetenv("TZ"); + wall(buf, 0, remote); + + /*NOTREACHED*/ + return 0; +} + -- 2.39.2