Import Upstream version 2.96

This commit is contained in:
openKylinBot 2022-05-14 02:57:07 +08:00
commit c6e5b365de
70 changed files with 16767 additions and 0 deletions

339
COPYING Normal file
View File

@ -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.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
<signature of Ty Coon>, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License.

22
COPYRIGHT Normal file
View File

@ -0,0 +1,22 @@
Sysvinit is Copyright (C) 1991-2004 Miquel van Smoorenburg
Updated Copyright (C) 2018 Jesse Smith
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
On Debian GNU/Linux systems, the complete text of the GNU General
Public License can be found in `/usr/share/common-licenses/GPL-2'.
Send patches to sysvinit-devel@nongnu.org

44
Makefile Normal file
View File

@ -0,0 +1,44 @@
PACKAGE=sysvinit
VERSION=$(shell sed -rn '1s/.*[[:blank:]]\((.*)\)[[:blank:]].*/\1/p' doc/Changelog)
all install clean distclean:
@rm -f $(PACKAGE)-$(VERSION).tar.xz $(PACKAGE)-$(VERSION).tar.xz.sig
$(MAKE) VERSION=$(VERSION) -C src $@
GITLOGIN=$(shell git remote -v | head -n 1 | cut -f 1 -d '@' | sed 's/origin\t//g')
override TMP:=$(shell mktemp -du $(VERSION).XXXXXXXX)
override TARBALL:=$(TMP)/$(PACKAGE)-$(VERSION).tar.xz
override SFTPBATCH:=$(TMP)/$(VERSION)-sftpbatch
SOURCES=contrib COPYING COPYRIGHT doc Makefile man README src
dist: $(TARBALL).sig
@cp $(TARBALL) .
@cp $(TARBALL).sig .
@echo "tarball $(PACKAGE)-$(VERSION).tar.xz ready"
rm -rf $(TMP)
upload: $(SFTPBATCH)
echo @sftp -b $< $(GITLOGIN)@dl.sv.nongnu.org:/releases/$(PACKAGE)
rm -rf $(TMP)
$(SFTPBATCH): $(TARBALL).sig
@echo progress > $@
@echo put $(TARBALL) >> $@
@echo chmod 664 $(notdir $(TARBALL)) >> $@
@echo put $(TARBALL).sig >> $@
@echo chmod 664 $(notdir $(TARBALL)).sig >> $@
@echo rm $(PACKAGE)-latest.tar.bz2 >> $@
@echo symlink $(notdir $(TARBALL)) $(PACKAGE)-latest.tar.bz2 >> $@
@echo quit >> $@
$(TARBALL).sig: $(TARBALL)
@gpg -q -ba --use-agent -o $@ $<
$(TARBALL): $(TMP)/$(PACKAGE)-$(VERSION)
@tar --exclude=.git --owner=nobody --group=nogroup -cJf $@ -C $(TMP) $(PACKAGE)-$(VERSION)
$(TMP)/$(PACKAGE)-$(VERSION): distclean
@mkdir -p $(TMP)/$(PACKAGE)-$(VERSION)
@cp -R $(SOURCES) $(TMP)/$(PACKAGE)-$(VERSION)/
@chmod -R a+r,u+w,og-w $@
@find $@ -type d | xargs -r chmod a+rx,u+w,og-w

33
README Normal file
View File

@ -0,0 +1,33 @@
README for SysV init
====================
SysV init is a classic initilization program (PID 1) for GNU/Linux and
other UNIX/POSIX systems. It is designed to be small, simple and to
stay out of the way.
Init is the parent (or grandparent) of all other processes on the
system. It kicks off the starting of other system services and
can act as a parent process to services which no longer have an
active parent process.
SysV init uses the concept of runlevels. A runlevel is a configuration of the
system which allows only a selected group of processes to exist.
The processes spawned by init for each of these runlevels are defined in the
/etc/inittab file. Init can be in one of eight runlevels. The runlevel is
changed by the administrator running the telinit command which selects
which runlevel we want to use.
More information on init, runlevels and switching between them can be found
in the init manual page. (See "man init".)
contrib Unofficial stuff, add-on programs
doc Documentation
man Manual pages
src Source code
For instructions on building and installing SysV init, please
see the "doc/Install" file.
The project home is on https://savannah.nongnu.org/projects/sysvinit
Send patches to sysvinit-devel@nongnu.org .

16
contrib/TODO Normal file
View File

@ -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.

29
contrib/alexander.viro Normal file
View File

@ -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 <viro@math.psu.edu>:
> As for the code separation - I think it's nice. Actually, read_inittab()
> with get_part() and newFamily are also pretty separatable. Another good
> set is any(), spawn(), startup(), spawn_emerg() and start_if_needed().
> BTW, fail_check();process_signals(); is equivalent to process_signal();
> fail_check();. I think that swapping them (in main loop) would be a good
> idea - then we can move fail_check() into start_if_needed(). And one more
> - I'ld propose to move start_if_needed to the beginning of the main loop,
> as in
> foo();
> while(1) { bar();foo();
> #if 0
> baz();
> #endif
> }
> to
> while(1) { foo();bar();
> #if 0
> baz();
> #endif
> }
>
>
> What do you think about it?

34
contrib/migrate-svn-git Executable file
View File

@ -0,0 +1,34 @@
#!/bin/sh
#
# Script to migrate sysvinit project source code from subversion to git.
# Used february 2018.
authorsmap=$(tempfile)
cat > $authorsmap <<EOF
pere = Petter Reinholdtsen <pere@hungry.com>
wfink = Werner Fink <werner@suse.de>
EOF
for p in sysvinit startpar insserv; do
git svn clone http://svn.savannah.nongnu.org/svn/sysvinit/$p \
--authors-file=$authorsmap \
--no-metadata \
--tags=tags \
--trunk=trunk \
--prefix=$p/ \
$p-git
(
cd $p-git
for tag in `git branch -r | grep "tags/" | sed "s/ $p\/tags\///"`; do
git tag -a -m"Converting SVN tags" $tag refs/remotes/$p/tags/$tag
done
if [ "sysvinit" = "$p" ]; then
remote=$p.git
else
remote=sysvinit/$p.git
fi
git remote add origin ssh://git.savannah.gnu.org:/srv/git/$remote
)
done
rm $authorsmap

View File

@ -0,0 +1,220 @@
Index: src/init.sample
===================================================================
--- src/init.sample (revision 0)
+++ src/init.sample (revision 0)
@@ -0,0 +1,9 @@
+#%PAM-1.0
+#
+# The PAM configuration file for /sbin/init
+# Used for updating the lastlog logging file
+#
+auth sufficient pam_rootok.so
+account include common-account
+session include common-session
+session requisite pam_lastlog.so silent
Index: src/init.c
===================================================================
--- src/init.c (revision 56)
+++ src/init.c (working copy)
@@ -76,6 +76,10 @@
#include "reboot.h"
#include "set.h"
+#ifdef USE_PAM
+extern void notify_pam_dead_session(const char *id);
+#endif
+
#ifndef SIGPWR
# define SIGPWR SIGUSR2
#endif
@@ -1129,6 +1133,9 @@
}
dup(f);
dup(f);
+#ifdef USE_PAM
+ notify_pam_dead_session(ch->id);
+#endif
}
/*
@@ -1548,6 +1555,9 @@
INITDBG(L_VB, "Updating utmp for pid %d [id %s]",
ch->pid, ch->id);
ch->flags &= ~RUNNING;
+#ifdef USE_PAM
+ notify_pam_dead_session(ch->id);
+#endif
if (ch->process[0] != '+')
write_utmp_wtmp("", ch->id, ch->pid, DEAD_PROCESS, NULL);
}
@@ -2009,6 +2019,9 @@
if (ch->flags & ZOMBIE) {
INITDBG(L_VB, "Child died, PID= %d", ch->pid);
ch->flags &= ~(RUNNING|ZOMBIE|WAITING);
+#ifdef USE_PAM
+ notify_pam_dead_session(ch->id);
+#endif
if (ch->process[0] != '+')
write_utmp_wtmp("", ch->id, ch->pid, DEAD_PROCESS, NULL);
}
@@ -2453,6 +2466,9 @@
if (ch->flags & ZOMBIE) {
INITDBG(L_VB, "Child died, PID= %d", ch->pid);
ch->flags &= ~(RUNNING|ZOMBIE|WAITING);
+#ifdef USE_PAM
+ notify_pam_dead_session(ch->id);
+#endif
if (ch->process[0] != '+')
write_utmp_wtmp("", ch->id, ch->pid, DEAD_PROCESS, NULL);
}
Index: src/utmp.c
===================================================================
--- src/utmp.c (revision 51)
+++ src/utmp.c (working copy)
@@ -34,10 +34,18 @@
#include <string.h>
#include <utmp.h>
+#if defined(USE_PAM) && defined(INIT_MAIN)
+# include <security/pam_appl.h>
+# include <security/pam_misc.h>
+#endif
+
#include "init.h"
#include "initreq.h"
#include "paths.h"
+#ifndef _PATH_DEV
+# define _PATH_DEV "/dev/"
+#endif
#if defined(__GLIBC__)
# if (__GLIBC__ == 2) && (__GLIBC_MINOR__ == 0) && defined(__powerpc__)
@@ -127,9 +135,9 @@
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)
+
+ /* 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
@@ -262,3 +270,75 @@
write_wtmp(user, id, pid, type, line && line[0] ? line : oldline);
}
+#if defined(USE_PAM) && defined(INIT_MAIN)
+static pam_handle_t *pamh = NULL;
+# ifdef __GNUC__
+static int
+null_conv(int num_msg, const struct pam_message **msgm,
+ struct pam_response **response __attribute__((unused)),
+ void *appdata_ptr __attribute__((unused)))
+# else
+static int
+null_conv(int num_msg, const struct pam_message **msgm,
+ struct pam_response **response, void *appdata_ptr)
+# endif
+{
+ int i;
+ for (i = 0; i < num_msg; i++) {
+ const struct pam_message *msg = msgm[i];
+ if (msg == (const struct pam_message*)0)
+ continue;
+ if (msg->msg == (char*)0)
+ continue;
+ switch (msg->msg_style) {
+ case PAM_ERROR_MSG:
+ case PAM_TEXT_INFO:
+ initlog(L_VB, "pam_message %s", msg->msg);
+ default:
+ break;
+ }
+ }
+ return 0;
+}
+static const struct pam_conv conv = { null_conv, NULL };
+# define PAM_FAIL_CHECK(func, args...) \
+ { \
+ if ((pam_ret = (func)(args)) != PAM_SUCCESS) { \
+ initlog(L_VB, "%s", pam_strerror(pamh, pam_ret)); \
+ goto pam_error; \
+ } \
+ }
+
+void notify_pam_dead_session(const char *id)
+{
+ struct utmp *oldut, ut;
+
+ setutent();
+
+ memset(&ut, 0, sizeof(ut));
+ ut.ut_type = DEAD_PROCESS;
+ strncpy(ut.ut_id, id, sizeof(ut.ut_id));
+
+ if ((oldut = getutid(&ut)) && (oldut->ut_type == USER_PROCESS)) {
+ int pam_ret;
+ char tty[UT_LINESIZE+ strlen(_PATH_DEV) + 1];
+
+ if (strncmp(oldut->ut_line, _PATH_DEV, strlen(_PATH_DEV)))
+ snprintf(tty, sizeof(tty), _PATH_DEV "%.*s",
+ UT_LINESIZE, oldut->ut_line);
+ else
+ snprintf(tty, sizeof(tty), "%.*s",
+ UT_LINESIZE, oldut->ut_line);
+
+ PAM_FAIL_CHECK(pam_start, "init", oldut->ut_user, &conv, &pamh);
+ PAM_FAIL_CHECK(pam_set_item, pamh, PAM_TTY, tty);
+ PAM_FAIL_CHECK(pam_set_item, pamh, PAM_RHOST, oldut->ut_host);
+ PAM_FAIL_CHECK(pam_close_session, pamh, PAM_SILENT);
+ pam_error:
+ pam_end(pamh, pam_ret);
+ }
+
+ endutent();
+}
+#endif /* USE_PAM && INIT_MAIN */
+
Index: src/Makefile
===================================================================
--- src/Makefile (revision 58)
+++ src/Makefile (working copy)
@@ -8,7 +8,7 @@
# Version: @(#)Makefile 2.85-13 23-Mar-2004 miquels@cistron.nl
#
-CPPFLAGS =
+CPPFLAGS = -DUSE_PAM
CFLAGS ?= -ansi -O2 -fomit-frame-pointer
override CFLAGS += -W -Wall -D_GNU_SOURCE
STATIC =
@@ -79,6 +79,13 @@
endif
# Additional libs for GNU libc.
+ifneq ($(findstring -DUSE_PAM,$(CPPFLAGS)),)
+ INITLIBS += -lpam
+ PAMDOTD = /etc/pam.d
+ PAMINIT = $(PAMDOTD)/init
+endif
+
+# Additional libs for GNU libc.
ifneq ($(wildcard /usr/lib*/libcrypt.a),)
SULOGINLIBS += -lcrypt
endif
@@ -153,6 +160,11 @@
$(STRIP) $$i ; \
$(INSTALL_EXEC) $$i $(ROOT)/usr/bin/ ; \
done
+ifneq ($(findstring -DUSE_PAM,$(CPPFLAGS)),)
+ $(INSTALL_DIR) $(ROOT)$(PAMDOTD)
+ test -s $(ROOT)$(PAMINIT) || \
+ $(INSTALL_DATA) init.sample $(ROOT)$(PAMINIT)
+endif
# $(INSTALL_DIR) $(ROOT)/etc/
# $(INSTALL_EXEC) initscript.sample $(ROOT)/etc/
ln -sf halt $(ROOT)/sbin/reboot

120
contrib/zefram-patches Normal file
View File

@ -0,0 +1,120 @@
Date: Mon, 14 Apr 1997 15:39:08 +0100 (BST)
From: Zefram <zefram@dcs.warwick.ac.uk>
Message-Id: <25042.199704141439@stone.dcs.warwick.ac.uk>
Subject: SysVinit patch
To: miquels@drinkel.cistron.nl
Hi. Enclosed is a patch for SysVinit that I am finding tiresome to
keep updating; you might like to fold it into your official releases.
It does the following:
* shutdown: use /etc/shutdown.allow if an option -s is given instead
of testing for a controlling tty. The tty check was inflexible and
unreliable.
* sulogin(8): refer to inittab(5) instead of inittab(4).
* sulogin: use libc passwd/shadow functions instead of home-grown ones
(there's just no point in using the special version).
* shutdown/sulogin: don't rely on the username "root" -- it's the UID
that counts.
-zefram
begin 644 initdiffs.gz
M'XL(``IG43,"`^T[:U/;R+*?E5_1>&L3&\G&#Y[FD%H")/%9)U"8G-0](44)
M>62KD"6M1K+7)YO_?KM[1K)DFP"[V:T]=2^5`FFFIZ??W=.C##W7A;H3@YS+
MJ1=X2;W=V#W8DN,T&8:SH+%?GC`+,\\V-S?O76:\CCWX9QI`:QO_=3N[W68;
M6@<'N\_J]?K].(T!+CF.8FAUH-WI-G>Z.QU:MD>;%7]X\U;+:NT!OP(T7L&6
MO/6"'!N-7;Z"3W!=3^BY=PE2./#YV48^'M^-`\?%(9J&Q)L(7M3#R9D=!UXP
MJD^$E/9(*)CKRM>O7YD#M34^U?_0UO*I>Z\3P^Z!M;>MQ$"DX>M^*R--_!KY
M-DHZC.>0H0L#2,8"G'`RL8,A^%X@<,!.8.;Y/MP*I#5((`G!]GU$D4H1R\:"
M!),>OGSY`E"7]-*[@&OWU75=7KL7^'X6R#36"&U>#'::C,/8DV)(6$E(0%)B
M*M`4$C$!3X(?CD8(X04-Q'(UQA'\%X3Q!,F8$Z(AS,8BH,TR.>..!.0@!,[>
MSFF.[`K'J_LU"_'PBI,D]NO'?E(_%3[!1S%*0@P;``,A%!7"2;R0</>.'0=G
MX20,DCCT%4^WP@]GX(8Q3$+DS0M<(HM6-)0X2"Z0RR53)DNF1V^$!N`C$:,E
MP/:^WJJ;.U9K8=?(TLG59;]^W+^JGY[UB64;:=`\6,2U$PLD)AB!'8`=17$8
MQ9Z="$`UHMJ]@+'TMD3B;)%T$OL6L324C"?"#J32EIB*>!ZB,<S&(8QME--X
M+CT4+M@L$T2#Q"O3"63H"[@3\]O0CH>H@4#IM:!2YA"N0B)URA:%^UGH`DL*
MI+7.6#AW+!I4B.<R(\IH_H.*92,J6@B:,.(A4D.7MYQZ<9(BH9HPV8">>[^A
MN'$X*9@*T>0EB@99(((0NQZRF0DOUQNB"6<:)RD"N2,'Z3'_`7E6A#J2C`&)
M1H(#&[V/*&=),]:9EXP5A"<38B028>23X2C?B46)872F)2ZAJAGI;4WM>"M.
M@ZTTF41(5ZT!YX$_)[[<A9A"*9:E*DD,:-9Q&"::GWQ/BX1"(0'1H$TY@CSF
M'`F.9^C*V23,8B]13J3C"UF^C*!%?P-767`FN2XZ]"H-^9;D)BJZMJU6>SL+
M8O_O`FM=@#1+;O![7"`W/H10D1L11131K/^[OE"2+QLWBF+5]%6X^%.,?_A`
M.>;<6SHYWRK''.,J%7"<CJBN:C6[.[O=UL'#Y9CSM'+LP&H5RA!\V\D<&#8-
M`\L%E,`P1"$D,"(GBL-T-`;:&&ZY)""QAFDLA3\57''H=4Z7/,3!Y$U.X:.K
M#^>`*J8BJ9!,<_B$\KG$S81OSS%Y)S.!ECCHO;DZNWP'5//@\\^]?I]3.A%`
M:5POQG43^PY=F*J8A1/*=4ZH]N1?*%V"ZQKOO%]2I'2*2P>3$,N%X#:-1Q9,
M>%S^-(R]X$[X#0<M/<8"(O`5DG42W6M:>[M9(3`-/2(!C:Q:P]<OM,S%,!<D
M;E4F0Q''%%F,R@<"Z5XGD(L&/M7OXK$;.)_Q20GG,Q<I\$F7FIGY?KX.*HC7
MJ%PGA*!^UP7^&8;!BP10\%2*96@M="=\)12-I76Q7A>+6\PL8+L)2B[7U!+P
M6`./;7\M*%G37LO:R^OM[R`)^=\IBK5&LF_M=Q9^MX?E?^YX.>(@IW[9_ZXK
M7`95V`=QG,5$[(%KRV290$?CR?QQU0V7%JSWQ4S6Y(MW%$BE-PILGQ>;B\52
M[_9XARSOCM+A'%?A&KR">6243B@/4];'O6TZ%VU`E6J$ZTJ`>:Q2PT6(I'9(
M1H7')R^IMOAE_=FKO;MOM?<*XF^CS[;1:;/(AT1A:/.&08A$PA$T#_-1+</2
M6(26S0.F'D#I$=\YT.M>_PPVW4B].6,[ADV2.R>33YWVY\+$;>I^:K7W<6@M
MZ?O;5J=SD(47XI!^8U1RHGF5<-[X6+7X%E1:2A[/ZCB_M<FG)DRF,X%V.D6=
M4!*FPY)/*D6;G7BD2]C<X@4(64V<D4BB41Q5FS78.(+LK0;/G_,+\HTO1T?0
MPA%>1C]5-T+.W3`2075P>MSOGW]$:N)*C9&\_]#OU]#[ZT28@72]#]=28A&E
M,X%2+-;_'/85C8:GY*NP7&)Z@34U3@8\&V/%4G61:EE%"5N`(K8P_M04+89!
M#)/HFY^)H1<_O(#??H/"P'7PHL:$>D$J#O,E'KR$3FMY!C-459(4$,$A;,KH
M$&1DFC46ZR;-9!CY1;-A+$S",TW<EM0Z3".BJ\807YEOWO8?T&G7H+#B<X['
M=7RLE%`-O"@7\PP=V+_+0TBIY**BT?6HKY"74JJ#4!+?!HW=A'>D_RH&'C:)
M-$'?K*Y3KK&PND1W!G1O@<LS/*]S4?BODVP79BU-ZB_3Y":91X)0?AB<7=Y<
M7)Z?G`T&RV*6.GVP0BM;0S'=^E%6+-`HJ$=26ZA*)G:B0)_+I(8";*Y3*((U
M9'(3(S)X#LU?7[\^4=;?_+6YW5Q:DG%Y24F"V/-G]ES"^<\ECL@Y)U'&&`F`
M_`&75-AYFID)&EJ\IJG(,6XQHM^IYZ_?D"F55VCJ.K3F6Y,1*A\!92ZDMJ+!
MX#C:I-J*Z-Q`0@.BM`B42U/1_>'JYOWQN[-![]]GN>NL$EZD7!DM_Q+!4%M+
MP3`U-VP7,UMFO.1>3H1I_$5?+4:9D_/W@_/^&4IUMA)EJ#S,ZPPW0ICK^)&U
M?>,Z5DDEPU+R*\V8D6<;S26%9&0.5-2]T)9.&4T=U91/T;1RJJJC_`AGJYCJ
M'(L2WA3I='[!JB=PYTEWU/6ZFK&S\]<U53N!(?$`YHRK#C-*66Q_SVH?M%46
M6Y<\.GMMJ[/?7N2]3GL'!_(C.RN+_RP2"<<_(AVIPN=#-:_UBUG?<&P\E;V0
M+[HJR7#BFX1#05S2O)$GPY9:D"UF'HRA<.W43[KZU=`58C:M@0WU^E6G/!,X
MU>*&IV'6"DSQ5#<GIPA&#;6WRF)Z>PI9CTI,)EOE1_&">X99*B>.+)#A@U7-
M)B'`DT7IY*O/NPU0D6(8"LD5':X/J27I<%VJ?C($O^!!V7/G%J%@&7LJQ#AT
M@D9HW#X-T*ZG:$2TD^TX81HDFO<L.YH/9T?S@>QH/CT[FO=F1_/W94?SP>QH
MDG68#V5'LYP=V8[^8'8TGYP=U:Y/RX[FT[*C^>CL:#XJ.V90&X.;WN#D[:7.
MDN05M75P3\BB]TMC35XSGY#7S#^2U\SEO&86\YJI0F5F<\6\EIO4M_*:N9K7
MS"?E-?.[Y#5S-:^9H!E;Y#7-I8Z[.J]Q1*%S3^BNG"95J6ZK)J'.=<NL#4XO
M>J=KHB\&>5?B&4OS].,0#?8Y[H-TW-=F2[E!N>;2,YM8UV3+YOC*<R`B@%WJ
ML37;W>W._3VV?-636FS[>8L-H#%X"Z=G@Y/+WL55[_R]ND?4>/&%+#^8AG>E
MB['J?HU:M7PK5NA3CT)NR%*[%Z6/HF93(Y=\1J=C[47#D"\*551#E\IZZ]1U
MU4WUZG9-M5QQOYZZ74%KE2$U<F-/<$=9_(IY,.%.LJ8V)TGEI<B6?%6HN]/4
MJ0?7MT>(A,V!)JB7,@GY8A.J8F1!O]<_K_$MY86Z0V!191>T?T=1[?PM1/6`
M*SCW&:[S#5=PC(](U#]3']K;T-KIMMK=SNZ#KN`8[T+M"MO01`=J=EO?<(7V
M-E:HA;[+MM5I9OK^P0L</\7"\1^N$R1^8_RR-*B;34NCT6Q(0V81<&QC%%H&
MY+Y"*%>PSN66%SJ+[5RXN7GS_L--O_?J\OCR?VYNX.41[*XOISO6=C-S[!^P
MD*4[^9.W9R<_WZ#=&JV5T7>G.SR*YX-LXN)X,/AX:E2X."/#F`TKA>G!V^/3
M\X]Z6C%6*:!]U7L_>(NSZC,&FGF6M9?^A2&?+L>/H/)3]8>:UA>T&GO0VJ^C
MFNND6G@W'30JA^HP@,HXN/_P0"5A>]%Y^JHVV]JD(\^F,1#TX0`639Y]Z^>7
M3U/;3X7Z(B#A$-]@:#K8<3M8BJ2JR-W$I98F'5?1J?1+=ORC=QJ-D1E\II.6
M/@YOJ3:^\4:H&SPZ5+-[S<)XJ/Q7=?LI"6$5D#IZ>@B;6)P1>#3CQ,T=Z0T\
M4R54CB_!HI$=TN2BC;>`9)*IEOK4WMG]O#(CRU.*0>X#4JIC^K'8-UY3.S7$
MHE&&$P'Z3,37;?9P2AW'A@+<(B1D\]'LAF[D2+W<23@L3&BJ<:HT/!).*&ET
MD$88_3Y@""S-#ST2<&6K-"C'PO=74*59P[.`?3%$H\OY7MEYEO"/%@D?80TD
M)\:B3L'4&*L18^T<!YC[2?;ZZ$<3*+(-+3(LR%GAGHKUN=ZI[F@H*):7/F<S
M,7S$(9U8@$K19YR-,CFZ9\,EHP)E&7>Q%-E116R-X8Q%$<M<@'D$.^J9#/MY
M21T6V904$0XC/CK.*\@J.4)M:>[!J2)^UNM]Z(N`J.#'@+'*RX!4+M8R+H_8
MW-6;KH<W2#\;Y;/51EE=/1411.#$\RC!3).K"\,#>K4WU("8\"A:N'B81:$I
M,RAJD[03+5O0TD42%HZ2BV"5PMWL8XD`57B_E65<Z)CC#:LE#:*A+!EER86O
MBB:HBA/"P3?Z=`G-<=#6P"J4Y_`6<*61%+U<M_27C*BB+6"EGN9$<8]_K95.
M.59J:A\IH16/DD]P*?FG^]23K;*H-YGP-P1*'I:&(UY0/Q;??7U_FU3:RWSQ
MFXK/+71CO8G^+HT_<G_@S9><8%$(<"H^EG?,6U&L#7@?)OKKPMP5@E`MT,F.
M[W,I`^)1>286GQ#*.R]2+8%A&M,IDRKC+*EOZ(J!+J.8SOQJV=`97)=]$/KD
M9,G\4,TM$C2M^]3:58U-A0T91,.F"1ZD"SWO4*<?+=?*&V^Z7&[PEX8V3HN`
M,G9V'YDOJ9)4J&^COU*LG_(2Q2F:G1TG:53K@E[FNGXJQZ1!E(J^R:.OK-I[
M5FL_OZI<DOZ?7`BAKK(XL(1D:5A&-"C7Q<DGE3IJP_75SF)NN>!9S-Q;\RQ`
MRF7/8GRI\EE,%(N?PDYKZI^(B2+CG.&B:O,1D?G$I@\5N-EHPX?>*30S$;,2
ME5$5XW*FDE*)!`]GVSRU%D)8'E'J+]=DO;\BZ97W5G</S47"D[E$980&D9-*
MQE$0[GK17A0=]4=9"H`E1+R?\"4?8[(X*QE$1C?X%Q/<NBU4`JL,RFP^?CN5
M\4H2H"YW<>/#0@#^6\9?]<Y\_961^$?YK3AL@1;T=XW&:_L;!WM6IYEW^PP5
M<`V4CX/"&6/$LV!W6Z'$>D8$TVKE[?F[,R113;:4.61S_?,WU):N9!?&*P!T
M`;`T6T`]>'O6[^.P3'51K^:U"ZL.S+;5WLG_Y\%W)+AHX_>1O0KS:.+7-F3V
M]ZW.P4XN_3Q,K76*0MA1]6RK6*UR<5NH*PJA.[^_5)!K8A8&+?CM-X;(OL[!
MPI=C<35:L*TC[,J`^BI`W;1J[FF7FN[3=':LSO9"94_D<HFQ<@C5+?B_G*__
)!9>7)7O2-```
`
end

1165
doc/Changelog Normal file

File diff suppressed because it is too large Load Diff

73
doc/Install Normal file
View File

@ -0,0 +1,73 @@
Install instructions for the System V style init
init, shutdown, halt, reboot, wall, last, mesg, runlevel,
killall5, pidof, sulogin.
All programs, files and scripts in this package are covered by
the GNU General Public License version 2, and copyrighted by
Miquel van Smoorenburg (1991-2004) and, Jesse Smith (2018).
If you are not using Debian and the debianized package,
you may have to install the new init by hand if Debian is
using an init system other than SysV (eg systemd). You should
be able to drop the binaries into a Slackware or Devuan system, I think.
The SysV init software, core programs and manual pages can be
installed by running the following two commands from the top-level
source directory.
make
sudo make install
If sudo is not installed, the "make install" command may be run as
the root user.
Other than the GNU make utility, SysV init has few dependencies.
SysV can be built on virtually any Linux system featuring
the GNU C library or musl libc. A C compiler, such as the GNU Compiler
Collection (GCC) or Clang is also required.
Here is a list of preferred directories to install the progs & manpages,
this should be done for you automatically when you run "make install"
as the root user, or via sudo, ie "sudo make install".
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 version of 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?):
* All communication to init goes through the FIFO /run/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.

75
doc/Propaganda Normal file
View File

@ -0,0 +1,75 @@
Propaganda for version 2.89 of sysvinit & utilities
==================================================
NOTE: If you use a standard distribution like Slackware, Devuan
or Gentoo 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.
SysV init was probably the most widely used init package for Linux.
Most distributions once used it. sysvinit 2.4 was really a good package,
and it was not the need for bug fixes but the need for more features
that made me work on sysvinit again.
SysV init 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:
http://download.savannah.nongnu.org/releases/sysvinit/

19
doc/bootlogd.README Normal file
View File

@ -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.

94
doc/initctl Normal file
View File

@ -0,0 +1,94 @@
This document describes the communiction pipe set up by SysV init
at /run/initctl. This named pipe allows programs with the proper
permissions (typically programs run by root have read+write access to
the pipe) to send signals to the init program (PID 1).
The init manual page has, up until recently, simply stated
that people wishing to understand how to send messages to init
should read the init program's source code, but that is not usually practical.
Messages sent to the pipe to talk to init must have a special format.
This format is defined as a C structure and the technical break-down
is presented here:
/*
* 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;
};
Let's go through the init_request structure one line at a time. The
first variable, the "magic" number must be of the value 0x03091969.
The init program then knows that only programs with root access which send
this magic number are authorized to communicate with init.
The cmd variable is a value in the range of 0-8 (currently). This cmd
variable tells init what we want it to do. Here are the possible options:
1 - Set the current runlevel, specified by the runlevel variable.
2 - The power will fail soon (probably low battery) prepare to shutdown.
3 - The power is failing, do shutdown immediately.
4 - The power is okay, cancel shutdown.
6 - Set an environment variable to a value to be specified in
the data variable of this structure.
Other cmd options may be added to init later. For example, command values
0, 5 and 7 are defined but currently not implemented.
The runlevel variable will specify the runlevel to switch to (0-6).
The sleeptime variable is to be used when we want to tell init to change
the time spent waiting between sending SIGTERM and SIGKILL during the
shutdown process. Changing this at run time is not yet implemented.
The data variable (in the union) can be used to pass misc data which init
might need to process our request. For example, when setting environment
variables.
When setting an environment variable through init's /run/initctl pipe,
the data variable should have the format VARIABLE=VALUE. The string
should be terminated with a NULL '\0' character.
The following C code example shows how to send a set environment variable
request to the init process using the /run/initctl pipe. This example
is simplified and skips the error checking. A more comlpete example can be
found in the shutdown.c program's init_setnv() function.
struct init_request request; /* this is the structure defined above */
int fd; /* file descriptor for the pipe */
memset(&request, 0, sizeof(request)); /* initialize structure */
request.magic = 0x03091969; /* this magic number must be included */
request.cmd = 6; /* 6 is the command to set a variable */
sprintf(request.data, "VARIABLE=VALUE"); /* set VARIABLE to VALUE in init */
if ((fd = open(INIT_FIFO, O_WRONLY)) >= 0) /* open pipe for writing */
{
size_t s = sizeof(request); /* size of the structure to write */
void *ptr = &request; /* temporary pointer */
write(fd, ptr, s); /* send the structure to the pipe */
close(fd); /* close the pipe when done */
}
Usually the /run/initctl pipe would only be used by low-level programs to
request a power-related shutdown or change the runlevel, like telinit
would do. Most of the time there is no need to talk to init directly, but
this gives us an extenable approach so init can be taught how to learn
more commands.

25
doc/initscript.sample Executable file
View File

@ -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 <id> <level> <action> <process>
#
# 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, <miquels@cistron.nl>
#
# 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"

82
man/bootlogd.8 Normal file
View File

@ -0,0 +1,82 @@
'\" -*- 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 [ \-e ]
.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\-e\fP
Print escape characters to the boot log file. This turns off filtering of
escape characters and allows tools like GNU Less to see and use colour control
characters (show the log in colour).
.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 NOTES
bootlogd saves log data which includes control characters. The log is
technically a text file, but not very easy for humans to read. To address
this the readbootlog(1) command can be used to display the boot log
without the control characters.
.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), readbootlog(1).

71
man/fstab-decode.8 Normal file
View File

@ -0,0 +1,71 @@
'\" -*- coding: UTF-8 -*-
.\" A man page for fstab-decode(8).
.\"
.\" Copyright (C) 2006 Red Hat, Inc. All rights reserved.
.\"
.\" This copyrighted material is made available to anyone wishing to use,
.\" modify, copy, or redistribute it subject to the terms and conditions of the
.\" GNU General Public License v.2.
.\"
.\" 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.
.\"
.\" Author: Miloslav Trmac <mitr@redhat.com>
.TH fstab-decode 8 "May 2006"
.SH NAME
fstab-decode \- run a command with fstab-encoded arguments
.SH SYNOPSIS
\fBfstab-decode\fR \fICOMMAND\fR [\fIARGUMENT\fR]...
.SH DESCRIPTION
.B fstab-decode
decodes escapes (such as newline characters and other whitespace)
in the specified \fIARGUMENT\fRs and uses them to run \fICOMMAND\fR.
The argument escaping uses the same rules as path escaping in
\fB/etc/fstab\fR,
.B /etc/mtab
and \fB/proc/mtab\fR.
In essence fstab-decode can be used anytime we want to pass multiple
parameters to a command as a list of command line argments. It turns output
like this:
.nf
/root
/mnt/remote-disk
/home
Into one long list of parameters, "/root /mnt/remote-disk /home". This
can be useful when trying to work with multiple filesystems at once. For
instance, we can use it to unmount multiple NFS shares. This program also
removes whitespace and other characters which might cause programs such
as mount or umount to fail.
.SH EXIT STATUS
.B fstab-decode
exits with status 127 if
.I COMMAND
can't be run.
Otherwise it exits with the status returned by \fICOMMAND\fR.
.SH EXAMPLES
.nf
The following example reads fstab, finds all instances of VFAT filesystems and
prints their mount points (argument 2 in the fstab file). fstab-decode then runs
the specified program, umount, and passes it the list of VFAT mountpoints.
This unmounts all VFAT partitions.
.B fstab-decode umount $(awk \[aq]$3 == \[dq]vfat\[dq] { print $2 }\[aq] /etc/fstab)
.fi
.SH SEE ALSO
.BR fstab (5)

123
man/halt.8 Normal file
View File

@ -0,0 +1,123 @@
'\" -*- 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. This implies \fB\-d\fP.
.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.
.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.
Warning: This may not work on interfaces which do not have an IP address
and should ideally be handled by a network manager service.
.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 and /var/run/runlevel does not exist) \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)
.\"}}}

353
man/init.8 Normal file
View File

@ -0,0 +1,353 @@
'\" -*- 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/init
.RB [ " --version " ]
.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 (a.k.a. \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 \fBS\fP, \fB0\fP, \fB1\fP, and \fB6\fP are reserved.
Runlevel S is used to initialize the system on boot.
When starting runlevel S (on boot)
or runlevel 1 (switching from a multi-user runlevel)
the system is entering ``single-user mode'', after which the
current runlevel is S.
Runlevel 0 is used to halt the system;
runlevel 6 is used to reboot the system.
.PP
After booting through S the system automatically enters one of
the multi-user runlevels 2 through 5, unless there was some
problem that needs to be fixed by the administrator in
single-user mode.
Normally after entering single-user mode
the administrator performs maintenance and then reboots the system.
.PP
For more information,
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.
.PP
Runlevels \fIS\fP and \fIs\fP are 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 initialize the system
and do not require an \fB/etc/inittab\fP file.
.PP
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 (reload) 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/run/initctl\fP
control channel - see the initctl manual page 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 3 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. Runlevel 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 3 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 or GRUB). \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
.TP 0.5i
.B \-\-version
This argument, when used on its own, displays the current version of init
to the console/stdout. It is a quick way to determine which init software and
version is being used. After the version information is displayed, init
immediately exits with a return code of zero.
.PP
.SH INTERFACE
Init listens on a \fIfifo\fP in /dev, \fI/run/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/run/initctl\fP. Useful for bootscripts when /dev is remounted.
.TP 0.5i
.B SIGUSR2
When init receives SIGUSR2, init closes and leaves the control fifo,
\fB/run/initctl\fP, closed. This may be used to make sure init is not
holding open any files. However, it also prevents init from switching
runlevels. Which means commands like shutdown no longer work.
The fifo can be re-opened by sending init the SIGUSR1 signal.
.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
/run/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.
.PP
On a Debian system, entering runlevel 1 causes all processes
to be killed except for kernel threads and the script that does
the killing and other processes in its session.
As a consequence of this, it isn't safe to return from runlevel 1
to a multi-user runlevel: daemons that were started in runlevel S
and are needed for normal operation are no longer running.
The system should be rebooted.
.\"}}}
.\"{{{ 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 initctl (5),
.BR inittab (5),
.BR initscript (5),
.BR utmp (5)
.\"}}}

148
man/initctl.5 Normal file
View File

@ -0,0 +1,148 @@
'\" -*- coding: UTF-8 -*-
.\" Copyright (C) 2018 Jesse Smith
.\"
.\" 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.
.\"
.\" 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 INITCTL 5 "April 13, 2018" "" "Linux System Administrator's Manual"
.SH NAME
initctl \- /run/initctl is a named pipe which passes commands to SysV init.
.SH SYNOPSIS
/run/initctl
.SH DESCRIPTION
This document describes the communiction pipe set up by SysV init
at /run/initctl. This named pipe allows programs with the proper
permissions (typically programs run by root have read+write access to
the pipe) to send signals to the init program (PID 1).
The init manual page has, up until recently, simply stated
that people wishing to understand how to send messages to init
should read the init program's source code, but that is not usually practical.
Messages sent to the pipe to talk to init must have a special format.
This format is defined as a C structure and the technical break-down
is presented here:
/*
* 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;
};
Let's go through the init_request structure one line at a time. The
first variable, the "magic" number must be of the value 0x03091969.
The init program then knows that only programs with root access which send
this magic number are authorized to communicate with init.
The cmd variable is a value in the range of 0-8 (currently). This cmd
variable tells init what we want it to do. Here are the possible options:
1 - Set the current runlevel, specified by the runlevel variable.
2 - The power will fail soon (probably low battery) prepare to shutdown.
3 - The power is failing, do shutdown immediately.
4 - The power is okay, cancel shutdown.
6 - Set an environment variable to a value to be specified in
the data variable of this structure.
Other cmd options may be added to init later. For example, command values
0, 5 and 7 are defined but currently not implemented.
The runlevel variable will specify the runlevel to switch to (0-6).
The sleeptime variable is to be used when we want to tell init to change
the time spent waiting between sending SIGTERM and SIGKILL during the
shutdown process. Changing this at run time is not yet implemented.
The data variable (in the union) can be used to pass misc data which init
might need to process our request. For example, when setting environment
variables.
When setting an environment variable through init's /run/initctl pipe,
the data variable should have the format VARIABLE=VALUE. The string
should be terminated with a NULL character.
.SH EXAMPLES
The following C code example shows how to send a set environment variable
request to the init process using the /run/initctl pipe. This example
is simplified and skips the error checking. A more comlpete example can be
found in the shutdown.c program's init_setnv() function.
.nf
struct init_request request; /* structure defined above */
int fd; /* file descriptor for pipe */
memset(&request, 0, sizeof(request)); /* initialize structure */
request.magic = 0x03091969; /* magic number required */
request.cmd = 6; /* 6 is to set a variable */
sprintf(request.data, "VARIABLE=VALUE"); /* set VAR to VALUE in init */
if ((fd = open(INIT_FIFO, O_WRONLY)) >= 0) /* open pipe for writing */
{
size_t s = sizeof(request); /* size of structure to write */
void *ptr = &request; /* temporary pointer */
write(fd, ptr, s); /* send structure to the pipe */
close(fd); /* close the pipe when done */
}
.fi
.sp
.SH NOTES
Usually the /run/initctl pipe would only be used by low-level programs to
request a power-related shutdown or change the runlevel, like telinit
would do. Most of the time there is no need to talk to init directly, but
this gives us an extenable approach so init can be taught how to learn
more commands.
.PP
The commands passed through the /run/initctl pipe must be sent in a specific
binary format and be of a specific length. Larger data structures or ones
not using the proper format will be ignored. Typically, only root has the
ability to write to the initctl pipe for security reasons.
.PP
The /run/initctl pipe can be closed by sending init (PID 1) the SIGUSR2
signal. This closes the pipe and leaves it closed. This may be useful
for making sure init is not keeping any files open. However, when the
pipe is closed, init no longer receives signals, such as those sent by
shutdown or telinit. In other words if we close the pipe, init cannot
change its runlevel directly. The pipe may be re-opened by sending init (PID 1)
the SIGUSR1 signal.
.PP
If the /run/initctl pipe is closed then it may still be possible to bring
down the system using the shutdown command's -n flag, but this is not
always clean and not recommended.
.SH FILES
/run/initctl
/sbin/init
.SH AUTHOR
Jesse Smith <jsmith@resonatingmedia.com>
.SH "SEE ALSO"
init(8)

71
man/initscript.5 Normal file
View File

@ -0,0 +1,71 @@
'\" -*- coding: UTF-8 -*-
.\" Copyright (C) 1998-2003 Miquel van Smoorenburg.
.\"
.\" This program is free software; you can redistribute it and/or modify
.\" it under the terms of the GNU General Public License as published by
.\" the Free Software Foundation; either version 2 of the License, or
.\" (at your option) any later version.
.\"
.\" This program is distributed in the hope that it will be useful,
.\" but WITHOUT ANY WARRANTY; without even the implied warranty of
.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
.\" GNU General Public License for more details.
.\"
.\" You should have received a copy of the GNU General Public License
.\" along with this program; if not, write to the Free Software
.\" Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
.\"
.TH INITSCRIPT 5 "July 10, 2003" "" "Linux System Administrator's Manual"
.SH NAME
initscript \- script that executes inittab commands.
.SH SYNOPSIS
/bin/sh /etc/initscript id runlevels action process
.SH DESCRIPTION
When the shell script \fI/etc/initscript\fP is present, \fBinit\fP
will use it to execute the commands from \fIinittab\fP.
This script can be used to set things like \fBulimit\fP and
\fBumask\fP default values for every process.
.SH EXAMPLES
This is a sample initscript, which might be installed on your
system as \fI/etc/initscript.sample\fP.
.RS
.sp
.nf
.ne 7
#
# initscript Executed by init(8) for every program it
# wants to spawn like this:
#
# /bin/sh /etc/initscript <id> <level> <action> <process>
#
# Set umask to safe level, and enable core dumps.
umask 022
ulimit -c 2097151
PATH=/bin:/sbin:/usr/bin:/usr/sbin
export PATH
# Increase the hard file descriptor limit for all processes
# to 8192. The soft limit is still 1024, but any unprivileged
# process can increase its soft limit up to the hard limit
# with "ulimit -Sn xxx" (needs a 2.2.13 or later Linux kernel).
ulimit -Hn 8192
# Execute the program.
eval exec "$4"
.sp
.RE
.SH NOTES
This script is not meant as startup script for daemons or somesuch.
It has nothing to do with a \fIrc.local\fP style script. It's just
a handler for things executed from \fB/etc/inittab\fP. Experimenting
with this can make your system un(re)bootable.
.SH FILES
/etc/inittab,
/etc/initscript.
.SH AUTHOR
Miquel van Smoorenburg ,<miquels@cistron.nl>
.SH "SEE ALSO"
init(8), inittab(5).

265
man/inittab.5 Normal file
View File

@ -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. The length of this field is limited to 127 characters.
.\"}}}
.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)
.\"}}}

49
man/killall5.8 Normal file
View File

@ -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
.B \-signalnumber
.RB [ \-o
.IR omitpid [, omitpid ...]]
.RB [ \-o
.IR omitpid [, 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

128
man/last.1 Normal file
View File

@ -0,0 +1,128 @@
'\" -*- 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 ]
[\-\fBn\fP \fInum\/\fP]
.RB [ \-adFiowx ]
[\-\fBf\fP \fIfile\/\fP]
[\-\fBt\fP \fIYYYYMMDDHHMMSS\/\fP]
.RI [ name... ]
.RI [ tty... ]
.br
.B lastb
.RB [ \-R ]
.RB [ \-\fInum\fP ]
[\-\fBn\fP \fInum\/\fP]
[\-\fBf\fP \fIfile\/\fP]
.RB [ \-adFiowx ]
.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\-l\fP
This option allows the display of usernames longer than 8 characters.
This may mess up formatting in some programs and make the output wider than
the standard 80 characters.
.IP \fB\-o\fP
Read an old-type wtmp file (written by linux-libc5 applications).
.IP \fB\-w\fP
Display full user and domain names in the output.
.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)
.\"}}}

1
man/lastb.1 Normal file
View File

@ -0,0 +1 @@
.so man1/last.1

61
man/logsave.8 Normal file
View File

@ -0,0 +1,61 @@
.\" -*- nroff -*-
.\" Copyright 2003 by Theodore Ts'o. All Rights Reserved.
.\" This file may be copied under the terms of the GNU Public License.
.\"
.TH LOGSAVE 8 "@E2FSPROGS_MONTH@ @E2FSPROGS_YEAR@" "E2fsprogs version @E2FSPROGS_VERSION@"
.SH NAME
logsave \- save the output of a command in a logfile
.SH SYNOPSIS
.B logsave
[
.B \-asv
]
.I logfile cmd_prog [ ... ]
.SH DESCRIPTION
The
.B logsave
program will execute
.I cmd_prog
with the specified argument(s), and save a copy of its output to
.IR logfile .
If the containing directory for
.I logfile
does not exist,
.B logsave
will accumulate the output in memory until it can be written out.
A copy of the output will also be written to standard output.
.PP
If
.I cmd_prog
is a single hyphen ('-'), then instead of executing a program,
.B logsave
will take its input from standard input and save it in
.I logfile
.PP
.B logsave
is useful for saving the output of initial boot scripts
until the /var partition is mounted, so the output can be written to
/var/log.
.SH OPTIONS
.TP
.B \-a
This option will cause the output to be appended to
.IR logfile ,
instead of replacing its current contents.
.TP
.B \-s
This option will cause
.B logsave
to skip writing to the log file text which is bracketed with a control-A
(ASCII 001 or Start of Header) and control-B (ASCII 002 or Start of
Text). This allows progress bar information to be visible to the user
on the console, while not being written to the log file.
.TP
.B \-v
This option will make
.B logsave
to be more verbose in its output to the user.
.SH AUTHOR
Theodore Ts'o (tytso@mit.edu)
.SH SEE ALSO
.BR fsck (8)

61
man/mesg.1 Normal file
View File

@ -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)
.\"}}}

69
man/mountpoint.1 Normal file
View File

@ -0,0 +1,69 @@
'\" -*- 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
.B \-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\-p\fP
Check Linux's /proc/mounts file to try to detect circular mount points.
.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
The mountpoint command fails when a directory is binded to one of its grandparents.
For example, if /a/b/c/d is a mount point for /a/b then mountpoint will report
/a/b/c/d is not a valid mount point. This is because both the original directory and
its new mount point share the same inode and device number.
.PP
The circular mount problem can be worked around on Linux systems by using
the -p flag to check the /proc/mounts file for references to the circular mount bind.
When using the -p flag, make sure to specify the full path (ie /home/user/mp and
not just mp). Also, mountpoint may still fail if there are spaces in
the mount point's path, even when using the -p flag because of the way
/proc/mounts mangles the spaces in the path name. Of course, if the
admin is using circular mount points with spaces in the name, there
are bigger concerns.
.SH AUTHOR
Miquel van Smoorenburg, miquels@cistron.nl
.SH "SEE ALSO"
.BR stat (1)

110
man/pidof.8 Normal file
View File

@ -0,0 +1,110 @@
'\" -*- 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 [ \-n ]
.RB [ \-x ]
.RB [ \-z ]
.RB [ \-o
.IR omitpid[,omitpid...] ]
.RB [ \-o
.IR omitpid[,omitpid...]... ]
.RB [ \-d
.IR sep ]
.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 PIDs 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 \-n
Avoid
.BR stat (2)
system function call on all binaries which are located on network
based file systems like
.BR NFS .
Instead of using this option the variable
.B PIDOF_NETFS
may be set and exported.
.IP \-q
Do not display matched PIDs to standard out. Simply exit with
a status of true or false to indicate whether a matching PID was found.
.IP \-x
Scripts too - this causes the program to also return process id's of
shells running the named scripts.
.IP \-z
Try to detect processes which are stuck in uninterruptible (D) or zombie (Z)
status. Usually these processes are skipped as trying to deal with them can cause
pidof to hang.
.IP "-d \fIsep\fP"
Tells \fIpidof\fP to use \fIsep\fP as an output separator if more than one PID
is shown. The default separator is a space.
.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. Note
that the executable name of running processes is calculated with
.BR readlink (2),
so symbolic links to executables will also match.
.PP
Zombie processes or processes in disk sleep (states Z and D, respectively)
are ignored, as attempts to access the stats of these will sometimes fail.
The \-z flag (see above) tells pidof to try to detect these sleeping and zombie
processes, at the risk of failing or hanging.
.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

1
man/poweroff.8 Normal file
View File

@ -0,0 +1 @@
.so man8/halt.8

58
man/readbootlog.1 Normal file
View File

@ -0,0 +1,58 @@
'\" -*- 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 READBOOTLOG 1 "NOV 12, 2018" "" "Linux System Administrator's Manual"
.\"}}}
.\"{{{ Name
.SH NAME
readbootlog \- show contents of the boot log, stripping away control characters
.\"}}}
.\"{{{ Synopsis
.SH SYNOPSIS
.B readbootlog
.RB [ \-h ]
[\-\fBf\fP \fIfile\/\fP]
.br
.\"}}}
.\"{{{ Description
.SH DESCRIPTION
.B readbootlog
is a tool for reading the boot log (by default /var/log/boot). The program
strips away control characters and non-human readable contents from the log
file. Output is dumped to the terminal where it can be piped or redirected
to a file.
.\"}}}
.\"{{{ Options
.SH OPTIONS
.IP "\fB\-f\fP \fIfile\fP"
Tells \fBreadbootlog\fP to use a specific file instead of \fB/var/log/boot\fP.
.IP \fB\-h\fP
Displays a brief help message.
.\"{{{ Files
.SH FILES
/var/log/boot
.\"}}}
.\"{{{ Author
.SH AUTHOR
Jesse Smith <jsmith@resonatingmedia.com>
.\"}}}
.\"{{{ See also
.SH "SEE ALSO"
.BR bootlogd (8)
.\"}}}

1
man/reboot.8 Normal file
View File

@ -0,0 +1 @@
.so man8/halt.8

58
man/runlevel.8 Normal file
View File

@ -0,0 +1,58 @@
'\" -*- 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, and if no runlevel record can be found in the
.I /var/run/runlevel
file,
.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

240
man/shutdown.8 Normal file
View File

@ -0,0 +1,240 @@
'\" -*- 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 [ \-akrhPHfFnc ]
.RB [ \-t
.IR sec ]
.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.
.\"}}}
.\"{{{ -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. Usually used with the -P or -H flags,
depending on whether we want to poweroff or simply stop the operating system.
.\"}}}
.\"{{{ -P
.IP \fB\-P\fP
Modifier to the -h flag. Halt action is to turn off the power.
Must be used with the -h flag.
.\"}}}
.\"{{{ -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.
Halting is often used to run through the shutdown process and leave
output on the screen for debugging purposes. Or when the user wants the OS to
stop, but leave the power on. To power off at the end of the shutdown sequence
use the -P modifier instead.
.\"}}}
.\"{{{ -f
.IP \fB\-f\fP
Skip fsck on reboot.
.\"}}}
.\"{{{ -F
.IP \fB\-F\fP
Force fsck on reboot.
.\"}}}
.\"{{{ -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.
.\"}}}
.\"{{{ -c
.IP \fB\-c\fP
Cancel a waiting shutdown. ("shutdown now" is no longer waiting.) With
this option it is of course not possible to give the time argument, but
you can enter explanatory message arguments on the command line that
will be sent to all users.
.\"{{{ -q
.IP \fB\-q
Reduce the number of warnings shutdown displays. Usually shutdown displays
warnings every 15 minutes and then every minute in the last 10 minutes of the
countdown until \fItime\fP is reached. When \-q is specified
shutdown only warns at 60 minute intervals, at the 10 minute mark,
at the 5 minue mark, and when the shutdown process actually happens.
.\"{{{ -Q
.IP \fB\-Q
Silence warnings prior to shutting down. Usually shutdown displays
warnings every 15 minutes and then every minute in the last 10 minutes of the
countdown until \fItime\fP is reached. When \-Q is specified
shutdown only warns when the shutdown process actually happens. All
other warning intervals are suppressed.
.\"}}}
.\"{{{ -t sec
.IP "\fB\-t\fP \fIsec\fP"
Tell \fBinit\fP(8) to wait \fIsec\fP seconds between sending all processes the
warning (SIGTERM) and the kill signal (SIGKILL), before changing to another runlevel.
The default time, if no value is specified, between these two signals is
three seconds. Warning: when shutdown calls init to perform the shutdown (the
default behaviour), init checks to see if all processes have terminated
and will stop waiting early once its children have all terminated.
When shutdown is called with the -n flag, it waits the full time specified
(or three seconds) even if all other processes have terminated.
.\"}}}
.\"{{{ 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 script (usually /etc/init.d/halt) that calls
the \fBhalt\fP(8) program as the last thing in the shutting down sequence should
check this environment variable and call the \fBhalt\fP(8) program with
the right options for these options to actually have any effect.
.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)
.\"}}}

87
man/sulogin.8 Normal file
View File

@ -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 <miquels@cistron.nl>
.SH SEE ALSO
init(8), inittab(5).

1
man/telinit.8 Normal file
View File

@ -0,0 +1 @@
.so man8/init.8

66
man/utmpdump.1 Normal file
View File

@ -0,0 +1,66 @@
'\" -*- coding: UTF-8 -*-
.\" Copyright (C) 2010 Michael Krapp
.\"
.\" 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 UTMPDUMP 1 "Februar 8, 2010" "" "Linux System Administrator's Manual"
.SH NAME
utmpdump \- dump UTMP and WTMP files in raw format
.SH SYNOPSIS
.B utmpdump
.RB [ \-froh ]
.I filename
.SH DESCRIPTION
\fButmpdump\fP is a simple program to dump UTMP and WTMP files
in raw format, so they can be examined.
.SH OPTIONS
.IP \fB\-f\fP
output appended data as the file grows.
.IP "\fB\-r\fP"
reverse. Write back edited login information into utmp or wtmp files.
.IP \fB\-o\fP
use old libc5 format.
.IP \fB\-h\fP
usage information.
.PP
utmpdump can be useful in cases of corrupted utmp or wtmp entries.
It can dump out utmp/wtmp to an ASCII file, then that file can
be edited to remove bogus entries and reintegrated, using
.PP
.sp 1
.in +1c
.nf
\fButmpdump -r < ascii file > wtmp\fP
.fi
.in -1c
.sp 1
but be warned as
.B utmpdump
was written for debugging purpose only.
.SH BUGS
You may
.B not
use the option \fB\-r\fP as the format for the
utmp/wtmp files strongly depends on the
input format. This tool was
.B not
written for normal use but for debugging.
.SH AUTHOR
Michael Krapp
.SH "SEE ALSO"
.BR last (1),
.BR w (1),
.BR who (1),
.BR utmp (5),

75
man/wall.1 Normal file
View File

@ -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\fP
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

12
src/.gitignore vendored Normal file
View File

@ -0,0 +1,12 @@
bootlogd
fstab-decode
halt
init
killall5
last
mesg
runlevel
shutdown
sulogin
utmpdump
wall

237
src/Makefile Normal file
View File

@ -0,0 +1,237 @@
#
# 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
#
CPPFLAGS =
CFLAGS ?= -O2
override CFLAGS += -ansi -fomit-frame-pointer -fstack-protector-strong -W -Wall -Wunreachable-code -Wformat -Werror=format-security -D_FORTIFY_SOURCE=2 -D_XOPEN_SOURCE -D_GNU_SOURCE -DVERSION=\"$(VERSION)\"
override CFLAGS += $(shell getconf LFS_CFLAGS)
STATIC =
MANDB := s@^\('\\\\\"\)[^\*-]*-\*- coding: [^[:blank:]]\+ -\*-@\1@
#
# Leave empty if the mountpoint(1) command from util-linux 2.20
# and above should be used, otherwise set it to yes.
#
MNTPOINT=
# For some known distributions we do not build all programs, otherwise we do.
BIN =
SBIN = init halt shutdown runlevel killall5 fstab-decode logsave
USRBIN = last mesg readbootlog
MAN1 = last.1 lastb.1 mesg.1 readbootlog.1
MAN5 = initscript.5 inittab.5 initctl.5
MAN8 = halt.8 init.8 killall5.8 pidof.8 poweroff.8 reboot.8 runlevel.8
MAN8 += shutdown.8 telinit.8 fstab-decode.8 logsave.8
ifeq ($(DISTRO),)
SBIN += sulogin bootlogd
USRBIN += utmpdump wall
MAN1 += utmpdump.1 wall.1
MAN8 += sulogin.8 bootlogd.8
endif
ifeq ($(DISTRO),Debian)
CPPFLAGS+= -DACCTON_OFF
SBIN += sulogin bootlogd
MAN8 += sulogin.8 bootlogd.8
MANDB :=
endif
ifeq ($(DISTRO),Owl)
USRBIN += wall
MAN1 += wall.1
MANDB :=
endif
ifeq ($(DISTRO),SuSE)
CPPFLAGS+= -DUSE_SYSFS -DSANE_TIO -DSIGINT_ONLYONCE -DUSE_ONELINE
SBIN += sulogin
USRBIN += utmpdump
MAN1 += utmpdump.1
MAN8 += sulogin.8
MANDB :=
endif
ifeq ($(MNTPOINT),yes)
BIN += mountpoint
MAN1 += mountpoint.1
endif
ID = $(shell id -u)
BIN_OWNER = root
BIN_GROUP = root
BIN_COMBO = $(BIN_OWNER):$(BIN_GROUP)
ifeq ($(ID),0)
INSTALL_EXEC = install -o $(BIN_OWNER) -g $(BIN_GROUP) -m 755
INSTALL_DATA = install -o $(BIN_OWNER) -g $(BIN_GROUP) -m 644
else
INSTALL_EXEC = install -m 755
INSTALL_DATA = install -m 644
endif
INSTALL_DIR = install -m 755 -d
MANDIR = /usr/share/man
ifeq ($(WITH_SELINUX),yes)
SELINUX_DEF = -DWITH_SELINUX
INITLIBS += -lselinux
SULOGINLIBS = -lselinux
else
SELINUX_DEF =
INITLIBS =
SULOGINLIBS =
endif
# Additional libs for GNU libc.
ifneq ($(wildcard /usr/lib*/libcrypt.*),)
SULOGINLIBS += -lcrypt
endif
# Additional libs for GNU libc / multiarch on Debian based systems.
ifneq ($(wildcard /usr/lib/*/libcrypt.*),)
SULOGINLIBS += -lcrypt
endif
all: $(BIN) $(SBIN) $(USRBIN)
#%: %.o
# $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $^ $(LDLIBS)
#%.o: %.c
# $(CC) $(CFLAGS) $(CPPFLAGS) -c $^ -o $@
init: LDLIBS += $(INITLIBS) $(STATIC)
init: init.o init_utmp.o runlevellog.o
halt: LDLIBS += $(STATIC)
halt: halt.o ifdown.o hddown.o utmp.o runlevellog.o
last: LDLIBS += $(STATIC)
last: last.o
logsave: LDLIBS += $(STATIC)
logsave: logsave.o
mesg: LDLIBS += $(STATIC)
mesg: mesg.o
mountpoint: LDLIBS += $(STATIC)
mountpoint: mountpoint.o
utmpdump: LDLIBS += $(STATIC)
utmpdump: utmpdump.o
runlevel: LDLIBS += $(STATIC)
runlevel: runlevel.o runlevellog.o
sulogin: LDLIBS += $(SULOGINLIBS) $(STATIC)
sulogin: sulogin.o consoles.o
wall: LDLIBS += $(STATIC)
wall: dowall.o wall.o
shutdown: LDLIBS += $(STATIC)
shutdown: dowall.o shutdown.o utmp.o
bootlogd: LDLIBS += -lutil $(STATIC)
bootlogd: bootlogd.o
readbootlog: LDLIBS += $(STATIC)
readbootlog: readbootlog.o
fstab-decode: LDLIBS += $(STATIC)
fstab-decode: fstab-decode.o
sulogin.o: CPPFLAGS += $(SELINUX_DEF)
sulogin.o: sulogin.c
runlevellog.o: runlevellog.h runlevellog.c paths.h
init.o: CPPFLAGS += $(SELINUX_DEF)
init.o: init.c init.h initreq.h paths.h reboot.h runlevellog.h runlevellog.c set.h
utmp.o:
init_utmp.o: CPPFLAGS += -DINIT_MAIN
init_utmp.o: utmp.c init.h initreq.h paths.h
$(CC) $(CFLAGS) $(CPPFLAGS) -c -o $@ $<
bootlogd.o: bootlogd.c bootlogd.h
readbootlog.o: readbootlog.c
utmpdump.o: utmpdump.c oldutmp.h
shutdown.o: shutdown.c paths.h reboot.h initreq.h init.h
halt.o: halt.c reboot.h paths.h runlevellog.c runlevellog.h
last.o: last.c oldutmp.h
logsave.o: logsave.c
consoles.o: consoles.c consoles.h
cleanobjs:
rm -f *.o *.bak
clean: cleanobjs clobber
clobber: cleanobjs
rm -f $(BIN) $(SBIN) $(USRBIN)
distclean: clobber
install: all
$(INSTALL_DIR) $(ROOT)/bin/ $(ROOT)/sbin/
$(INSTALL_DIR) $(ROOT)/usr/bin/
for i in $(BIN); do \
$(INSTALL_EXEC) $$i $(ROOT)/bin/ ; \
done
for i in $(SBIN); do \
$(INSTALL_EXEC) $$i $(ROOT)/sbin/ ; \
done
for i in $(USRBIN); do \
$(INSTALL_EXEC) $$i $(ROOT)/usr/bin/ ; \
done
# $(INSTALL_DIR) $(ROOT)/etc/
# $(INSTALL_EXEC) ../doc/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_DIR) $(ROOT)/usr/include/
$(INSTALL_DATA) initreq.h $(ROOT)/usr/include/
$(INSTALL_DIR) $(ROOT)$(MANDIR)/man1/
$(INSTALL_DIR) $(ROOT)$(MANDIR)/man5/
$(INSTALL_DIR) $(ROOT)$(MANDIR)/man8/
for man in $(MAN1); do \
$(INSTALL_DATA) ../man/$$man $(ROOT)$(MANDIR)/man1/; \
sed -i "1{ $(MANDB); }" $(ROOT)$(MANDIR)/man1/$$man ; \
done
for man in $(MAN5); do \
$(INSTALL_DATA) ../man/$$man $(ROOT)$(MANDIR)/man5/; \
sed -i "1{ $(MANDB); }" $(ROOT)$(MANDIR)/man5/$$man ; \
done
for man in $(MAN8); do \
$(INSTALL_DATA) ../man/$$man $(ROOT)$(MANDIR)/man8/; \
sed -i "1{ $(MANDB); }" $(ROOT)$(MANDIR)/man8/$$man ; \
done
ifeq ($(ROOT),)
#
# This part is skipped on Debian systems, the
# debian.preinst script takes care of it.
@if [ ! -p /run/initctl ]; then \
echo "Creating /run/initctl"; \
rm -f /run/initctl; \
mknod -m 600 /run/initctl p; fi
endif

738
src/bootlogd.c Normal file
View File

@ -0,0 +1,738 @@
/*
* 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
*
*/
#include <sys/types.h>
#include <sys/time.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <sys/utsname.h>
#include <time.h>
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>
#include <getopt.h>
#include <dirent.h>
#include <fcntl.h>
#ifdef __linux__
#include <pty.h>
#endif
#ifdef __FreeBSD__
#include <termios.h>
#include <libutil.h>
#endif
#include <ctype.h>
#ifdef __linux__
#include <sys/mount.h>
#endif
#include "bootlogd.h"
#define MAX_CONSOLES 16
#define KERNEL_COMMAND_LENGTH 4096
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 real_cons {
char name[1024];
int fd;
};
/*
* 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.
*/
/*
This function does not appear to be called anymore. Commenting it
out for now, can probably be removed entirely in the future.
static int findtty(char *res, const char *startdir, int rlen, dev_t dev)
{
DIR *dir;
struct dirent *ent;
struct stat st;
int r = -1;
char *olddir = getcwd(NULL, 0);
if (chdir(startdir) < 0 || (dir = opendir(".")) == NULL) {
int msglen = strlen(startdir) + 11;
char *msg = malloc(msglen);
snprintf(msg, msglen, "bootlogd: %s", startdir);
perror(msg);
free(msg);
chdir(olddir);
return -1;
}
while ((ent = readdir(dir)) != NULL) {
if (lstat(ent->d_name, &st) != 0)
continue;
if (S_ISDIR(st.st_mode)
&& 0 != strcmp(".", ent->d_name)
&& 0 != strcmp("..", ent->d_name)) {
char *path = malloc(rlen);
snprintf(path, rlen, "%s/%s", startdir, ent->d_name);
r = findtty(res, path, rlen, dev);
free(path);
if (0 == r) {
closedir(dir);
chdir(olddir);
return 0;
}
continue;
}
if (!S_ISCHR(st.st_mode))
continue;
if (st.st_rdev == dev) {
if ( (int) (strlen(ent->d_name) + strlen(startdir) + 1) >= rlen) {
fprintf(stderr, "bootlogd: console device name too long\n");
closedir(dir);
chdir(olddir);
return -1;
} else {
snprintf(res, rlen, "%s/%s", startdir, ent->d_name);
closedir(dir);
chdir(olddir);
return 0;
}
}
}
closedir(dir);
chdir(olddir);
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) 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(s). Assume that stdin is connected to
* the console device (/dev/console).
*/
int consolenames(struct real_cons *cons, int max_consoles)
{
#ifdef TIOCGDEV
/* This appears to be unused. unsigned int kdev; */
#endif
struct stat st, st2;
char buf[KERNEL_COMMAND_LENGTH];
char *p;
int didmount = 0;
int n;
int fd;
int considx, num_consoles = 0;
#ifdef __linux__
/*
* Read /proc/cmdline.
*/
stat("/", &st);
if (stat("/proc", &st2) < 0) {
perror("bootlogd: /proc");
return 0;
}
if (st.st_dev == st2.st_dev) {
if (mount("proc", "/proc", "proc", 0, NULL) < 0) {
perror("bootlogd: mount /proc");
return -1;
}
didmount = 1;
}
n = -1;
if ((fd = open("/proc/cmdline", O_RDONLY)) < 0) {
perror("bootlogd: /proc/cmdline");
} else {
buf[0] = 0;
if ((n = read(fd, buf, KERNEL_COMMAND_LENGTH - 1)) < 0)
perror("bootlogd: /proc/cmdline");
close(fd);
}
if (didmount) umount("/proc");
if (n < 0) return 0;
/*
* OK, so find console= in /proc/cmdline.
* Parse in reverse, opening as we go.
*/
p = buf + n;
*p-- = 0;
while (p >= buf) {
if (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') {
*p-- = 0;
continue;
}
if (strncmp(p, "console=", 8) == 0 &&
isconsole(p + 8, cons[num_consoles].name, sizeof(cons[num_consoles].name))) {
/*
* Suppress duplicates
*/
for (considx = 0; considx < num_consoles; considx++) {
if (!strcmp(cons[num_consoles].name, cons[considx].name)) {
goto dontuse;
}
}
num_consoles++;
if (num_consoles >= max_consoles) {
break;
}
}
dontuse:
p--;
}
if (num_consoles > 0) return num_consoles;
#endif
/*
* Okay, no console on the command line -
* guess the default console.
*/
for (n = 0; defcons[n]; n++)
if (isconsole(defcons[n], cons[0].name, sizeof(cons[0].name)))
return 1;
fprintf(stderr, "bootlogd: cannot deduce real console device\n");
return 0;
}
/*
* Write data and make sure it's on disk.
*/
void writelog(FILE *fp, unsigned char *ptr, int len, int print_escape_characters)
{
int dosync = 0;
int i;
static int first_run = 1;
static int inside_esc = 0;
for (i = 0; i < len; i++) {
int ignore = 0;
/* prepend date to every line */
if (*(ptr-1) == '\n' || first_run) {
time_t t;
char *s;
time(&t);
s = ctime(&t);
fprintf(fp, "%.24s: ", s);
dosync = 1;
first_run = 0;
}
/* remove escape sequences, but do it in a way that allows us to stop
* in the middle in case the string was cut off */
if (! print_escape_characters)
{
if (inside_esc == 1) {
/* first '[' is special because if we encounter it again, it should be considered the final byte */
if (*ptr == '[') {
/* multi char sequence */
ignore = 1;
inside_esc = 2;
} else {
/* single char sequence */
if (*ptr >= 64 && *ptr <= 95) {
ignore = 1;
}
inside_esc = 0;
}
} else if (inside_esc == 2) {
switch (*ptr) {
case '0' ... '9': /* intermediate chars of escape sequence */
case ';':
case 32 ... 47:
if (inside_esc) {
ignore = 1;
}
break;
case 64 ... 126: /* final char of escape sequence */
if (inside_esc) {
ignore = 1;
inside_esc = 0;
}
break;
}
} else {
switch (*ptr) {
case '\r':
ignore = 1;
break;
case 27: /* ESC */
ignore = 1;
inside_esc = 1;
break;
}
}
} /* end of if we should filter escape characters */
if (!ignore) {
fwrite(ptr, sizeof(char), 1, fp);
}
ptr++;
}
if (dosync) {
fflush(fp);
if (syncalot) {
fdatasync(fileno(fp));
}
}
outptr += len;
if (outptr >= endptr)
outptr = ringbuf;
}
/*
* Print usage message and exit.
*/
void usage(void)
{
fprintf(stderr, "Usage: bootlogd [-v] [-r] [-d] [-e] [-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 *p;
char *logfile;
char *pidfile;
int rotate;
int dontfork;
int ptm, pts;
/* int realfd; -- this is now unused */
int n, m, i;
int todo;
#ifndef __linux__ /* BSD-style ioctl needs an argument. */
int on = 1;
#endif
int considx;
struct real_cons cons[MAX_CONSOLES];
int num_consoles, consoles_left;
int print_escape_sequence = 0;
fp = NULL;
logfile = LOGFILE;
pidfile = NULL;
rotate = 0;
dontfork = 0;
while ((i = getopt(argc, argv, "cdesl:p:rv")) != EOF) switch(i) {
case 'l':
logfile = optarg;
break;
case 'r':
rotate = 1;
break;
case 'v':
printf("bootlogd - %s\n", VERSION);
exit(0);
break;
case 'p':
pidfile = optarg;
break;
case 'c':
createlogfile = 1;
break;
case 'd':
dontfork = 1;
break;
case 'e':
print_escape_sequence = 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", realcons, strerror(errno));
return 1;
}
*/
if ((num_consoles = consolenames(cons, MAX_CONSOLES)) <= 0)
return 1;
consoles_left = num_consoles;
for (considx = 0; considx < num_consoles; considx++) {
if (strcmp(cons[considx].name, "/dev/tty0") == 0)
strcpy(cons[considx].name, "/dev/tty1");
if (strcmp(cons[considx].name, "/dev/vc/0") == 0)
strcpy(cons[considx].name, "/dev/vc/1");
if ((cons[considx].fd = open_nb(cons[considx].name)) < 0) {
fprintf(stderr, "bootlogd: %s: %s\n",
cons[considx].name, strerror(errno));
consoles_left--;
}
}
if (!consoles_left)
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;
}
#ifdef __linux__
(void)ioctl(0, TIOCCONS, NULL);
/* 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
#ifdef __linux__
if (ioctl(pts, TIOCCONS, NULL) < 0)
#else /* BSD usage of ioctl TIOCCONS. */
if (ioctl(pts, TIOCCONS, &on) < 0)
#endif
{
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 devices.
*/
for (considx = 0; considx < num_consoles; considx++) {
if (cons[considx].fd < 0) continue;
m = n;
p = inptr;
while (m > 0) {
i = write(cons[considx].fd, p, m);
if (i >= 0) {
m -= i;
p += i;
continue;
}
/*
* Handle EIO (somebody hung
* up our filedescriptor)
*/
cons[considx].fd = write_err(pts,
cons[considx].fd,
cons[considx].name, errno);
if (cons[considx].fd >= 0) continue;
/*
* If this was the last console,
* generate a fake signal
*/
if (--consoles_left <= 0) got_signal = 1;
break;
} /* end of while */
} /* end of going through all consoles */
/*
* 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;
} /* end of got data from read */
} /* end of checking select for new data */
/*
* 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, (unsigned char *)outptr, todo, print_escape_sequence);
} /* end of while waiting for signal */
if (fp) {
if (!didnl) fputc('\n', fp);
fclose(fp);
}
close(pts);
close(ptm);
for (considx = 0; considx < num_consoles; considx++) {
close(cons[considx].fd);
}
return 0;
}

12
src/bootlogd.h Normal file
View File

@ -0,0 +1,12 @@
#ifndef LOGFILE
#define LOGFILE "/var/log/boot"
#endif
#ifndef TRUE
#define TRUE 1
#endif
#ifndef FALSE
#define FALSE 0
#endif

510
src/consoles.c Normal file
View File

@ -0,0 +1,510 @@
/*
* consoles.c Routines to detect the system consoles
*
* Copyright (c) 2011 SuSE LINUX Products GmbH, All rights reserved.
*
* 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, 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 (see the file COPYING); if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
* MA 02110-1301, USA.
*
* Author: Werner Fink <werner@suse.de>
*/
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <sys/ttydefaults.h>
#ifdef __linux__
# include <sys/vt.h>
# include <sys/kd.h>
# include <linux/serial.h>
#include <sys/sysmacros.h>
#endif
#include <fcntl.h>
#include <dirent.h>
#include <unistd.h>
#include "consoles.h"
#ifdef __linux__
# include <linux/major.h>
#endif
#if !defined(__STDC_VERSION__) || (__STDC_VERSION__ < 199901L)
# ifndef typeof
# define typeof __typeof__
# endif
# ifndef restrict
# define restrict __restrict__
# endif
#endif
#define alignof(type) ((sizeof(type)+(sizeof(void*)-1)) & ~(sizeof(void*)-1))
struct console *consoles;
/*
* Read and allocate one line from file,
* the caller has to free the result
*/
static
#ifdef __GNUC__
__attribute__((__nonnull__))
#endif
char *oneline(const char *file)
{
FILE *fp;
char *ret = (char*)0, *nl;
size_t len = 0;
if ((fp = fopen(file, "re")) == (FILE*)0)
goto err;
if (getline(&ret, &len, fp) < 0)
goto out;
if (len)
ret[len-1] = '\0';
if ((nl = strchr(ret, '\n')))
*nl = '\0';
out:
fclose(fp);
err:
return ret;
}
#ifdef __linux__
/*
* Read and determine active attribute for tty below
* /sys/class/tty, the caller has to free the result.
*/
static
__attribute__((__malloc__))
char *actattr(const char *tty)
{
char *ret = (char*)0;
char *path;
if (!tty || *tty == '\0')
goto err;
if (asprintf(&path, "/sys/class/tty/%s/active", tty) < 0)
goto err;
if ((ret = oneline(path)) == (char*)0)
goto out;
out:
free(path);
err:
return ret;
}
/*
* Read and determine device attribute for tty below
* /sys/class/tty.
*/
static
dev_t devattr(const char *tty)
{
unsigned int maj, min;
dev_t dev = 0;
char *path, *value;
if (!tty || *tty == '\0')
goto err;
if (asprintf(&path, "/sys/class/tty/%s/dev", tty) < 0)
goto err;
if ((value = oneline(path)) == (char*)0)
goto out;
if (sscanf(value, "%u:%u", &maj, &min) == 2)
dev = makedev(maj, min);
free(value);
out:
free(path);
err:
return dev;
}
#endif /* __linux__ */
/*
* Search below /dev for the characer device in
* the local `dev_t comparedev' variable.
*/
static dev_t comparedev;
static
#ifdef __GNUC__
__attribute__((__nonnull__,__malloc__,__hot__))
#endif
char* scandev(DIR *dir)
{
char *name = (char*)0;
struct dirent *dent;
int fd;
fd = dirfd(dir);
rewinddir(dir);
while ((dent = readdir(dir))) {
char *path;
struct stat st;
if (fstatat(fd, dent->d_name, &st, 0) < 0)
continue;
if (!S_ISCHR(st.st_mode))
continue;
if (comparedev != st.st_rdev)
continue;
if (asprintf(&path, "/dev/%s", dent->d_name) < 0)
continue;
name = realpath(path, NULL);
free(path);
break;
}
return name;
}
/*
* Default control characters for an unknown terminal line.
*/
static
struct chardata initcp = {
CERASE,
CKILL,
CTRL('r'),
0
};
/*
* Allocate an aligned `struct console' memory area,
* initialize its default values, and append it to
* the global linked list.
*/
static int concount; /* Counter for console IDs */
static
#ifdef __GNUC__
__attribute__((__nonnull__,__hot__))
#endif
void consalloc(char * name)
{
struct console *restrict tail;
if (posix_memalign((void*)&tail, sizeof(void*), alignof(typeof(struct console))) != 0)
perror("memory allocation");
tail->next = (struct console*)0;
tail->tty = name;
tail->file = (FILE*)0;
tail->flags = 0;
tail->fd = -1;
tail->id = concount++;
tail->pid = 0;
memset(&tail->tio, 0, sizeof(tail->tio));
memcpy(&tail->cp, &initcp, sizeof(struct chardata));
if (!consoles)
consoles = tail;
else
consoles->next = tail;
}
/*
* Try to detect the real device(s) used for the system console
* /dev/console if but only if /dev/console is used. On Linux
* this can be more than one device, e.g. a serial line as well
* as a virtual console as well as a simple printer.
*
* Returns 1 if stdout and stderr should be reconnected and 0
* otherwise.
*/
int detect_consoles(const char *device, int fallback)
{
int fd, ret = 0;
#ifdef __linux__
char *attrib, *cmdline;
FILE *fc;
#endif
if (!device || *device == '\0')
fd = dup(fallback);
else {
fd = open(device, O_RDWR|O_NONBLOCK|O_NOCTTY|O_CLOEXEC);
ret = 1;
}
if (fd >= 0) {
DIR *dir;
char *name;
struct stat st;
#ifdef TIOCGDEV
unsigned int devnum;
#endif
if (fstat(fd, &st) < 0) {
close(fd);
goto fallback;
}
comparedev = st.st_rdev;
if (ret && (fstat(fallback, &st) < 0 || comparedev != st.st_rdev))
dup2(fd, fallback);
#ifdef __linux__
/*
* Check if the device detection for Linux system console should be used.
*/
if (comparedev == makedev(TTYAUX_MAJOR, 0)) { /* /dev/tty */
close(fd);
device = "/dev/tty";
goto fallback;
}
if (comparedev == makedev(TTYAUX_MAJOR, 1)) { /* /dev/console */
close(fd);
goto console;
}
if (comparedev == makedev(TTYAUX_MAJOR, 2)) { /* /dev/ptmx */
close(fd);
device = "/dev/tty";
goto fallback;
}
if (comparedev == makedev(TTY_MAJOR, 0)) { /* /dev/tty0 */
struct vt_stat vt;
if (ioctl(fd, VT_GETSTATE, &vt) < 0) {
close(fd);
goto fallback;
}
comparedev = makedev(TTY_MAJOR, (int)vt.v_active);
}
#endif
#ifdef TIOCGDEV
if (ioctl (fd, TIOCGDEV, &devnum) < 0) {
close(fd);
goto fallback;
}
comparedev = (dev_t)devnum;
#endif
close(fd);
dir = opendir("/dev");
if (!dir)
goto fallback;
name = scandev(dir);
if (name)
consalloc(name);
closedir(dir);
if (!consoles)
goto fallback;
return ret;
}
#ifdef __linux__
console:
/*
* Detection of devices used for Linux system consolei using
* the /proc/consoles API with kernel 2.6.38 and higher.
*/
if ((fc = fopen("/proc/consoles", "re"))) {
char fbuf[16];
int maj, min;
DIR *dir;
dir = opendir("/dev");
if (!dir) {
fclose(fc);
goto fallback;
}
while ((fscanf(fc, "%*s %*s (%[^)]) %d:%d", &fbuf[0], &maj, &min) == 3)) {
char * name;
if (!strchr(fbuf, 'E'))
continue;
comparedev = makedev(maj, min);
name = scandev(dir);
if (!name)
continue;
consalloc(name);
}
closedir(dir);
fclose(fc);
return ret;
}
/*
* Detection of devices used for Linux system console using
* the sysfs /sys/class/tty/ API with kernel 2.6.37 and higher.
*/
if ((attrib = actattr("console"))) {
char *words = attrib, *token;
DIR *dir;
dir = opendir("/dev");
if (!dir) {
free(attrib);
goto fallback;
}
while ((token = strsep(&words, " \t\r\n"))) {
char * name;
if (*token == '\0')
continue;
comparedev = devattr(token);
if (comparedev == makedev(TTY_MAJOR, 0)) {
char *tmp = actattr(token);
if (!tmp)
continue;
comparedev = devattr(tmp);
free(tmp);
}
name = scandev(dir);
if (!name)
continue;
consalloc(name);
}
closedir(dir);
free(attrib);
if (!consoles)
goto fallback;
return ret;
}
/*
* Detection of devices used for Linux system console using
* kernel parameter on the kernels command line.
*/
if ((cmdline = oneline("/proc/cmdline"))) {
char *words= cmdline, *token;
DIR *dir;
dir = opendir("/dev");
if (!dir) {
free(cmdline);
goto fallback;
}
while ((token = strsep(&words, " \t\r\n"))) {
#ifdef TIOCGDEV
unsigned int devnum;
#else
struct vt_stat vt;
struct stat st;
#endif
char *colon, *name;
if (*token != 'c')
continue;
if (strncmp(token, "console=", 8) != 0)
continue;
token += 8;
if (strcmp(token, "brl") == 0)
token += 4;
if ((colon = strchr(token, ',')))
*colon = '\0';
if (asprintf(&name, "/dev/%s", token) < 0)
continue;
if ((fd = open(name, O_RDWR|O_NONBLOCK|O_NOCTTY|O_CLOEXEC)) < 0) {
free(name);
continue;
}
free(name);
#ifdef TIOCGDEV
if (ioctl (fd, TIOCGDEV, &devnum) < 0) {
close(fd);
continue;
}
comparedev = (dev_t)devnum;
#else
if (fstat(fd, &st) < 0) {
close(fd);
continue;
}
comparedev = st.st_rdev;
if (comparedev == makedev(TTY_MAJOR, 0)) {
if (ioctl(fd, VT_GETSTATE, &vt) < 0) {
close(fd);
continue;
}
comparedev = makedev(TTY_MAJOR, (int)vt.v_active);
}
#endif
close(fd);
name = scandev(dir);
if (!name)
continue;
consalloc(name);
}
closedir(dir);
free(cmdline);
/*
* Detection of the device used for Linux system console using
* the ioctl TIOCGDEV if available (e.g. official 2.6.38).
*/
if (!consoles) {
#ifdef TIOCGDEV
unsigned int devnum;
const char *name;
if (!device || *device == '\0')
fd = dup(fallback);
else fd = open(device, O_RDWR|O_NONBLOCK|O_NOCTTY|O_CLOEXEC);
if (fd < 0)
goto fallback;
if (ioctl (fd, TIOCGDEV, &devnum) < 0) {
close(fd);
goto fallback;
}
comparedev = (dev_t)devnum;
close(fd);
if (device && *device != '\0')
name = device;
else name = ttyname(fallback);
if (!name)
name = "/dev/tty1";
consalloc(strdup(name));
if (consoles) {
if (!device || *device == '\0')
consoles->fd = fallback;
return ret;
}
#endif
goto fallback;
}
return ret;
}
#endif /* __linux __ */
fallback:
if (fallback >= 0) {
const char *name;
if (device && *device != '\0')
name = device;
else name = ttyname(fallback);
if (!name)
name = "/dev/tty";
consalloc(strdup(name));
if (consoles)
consoles->fd = fallback;
}
return ret;
}

48
src/consoles.h Normal file
View File

@ -0,0 +1,48 @@
/*
* consoles.h Header file for routines to detect the system consoles
*
* Copyright (c) 2011 SuSE LINUX Products GmbH, All rights reserved.
*
* 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, 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 (see the file COPYING); if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
* MA 02110-1301, USA.
*
* Author: Werner Fink <werner@suse.de>
*/
#include <sys/types.h>
#include <stdint.h>
#include <stdio.h>
#include <termios.h>
struct chardata {
uint8_t erase;
uint8_t kill;
uint8_t eol;
uint8_t parity;
};
struct console {
char *tty;
FILE *file;
uint32_t flags;
int fd, id;
#define CON_SERIAL 0x0001
#define CON_NOTTY 0x0002
pid_t pid;
struct chardata cp;
struct termios tio;
struct console *next;
};
extern struct console *consoles;
extern int detect_consoles(const char *, int);

260
src/dowall.c Normal file
View File

@ -0,0 +1,260 @@
/*
* dowall.c Write to all users on the system.
*
* Author: Miquel van Smoorenburg, miquels@cistron.nl
*
* Version: @(#)dowall.c 2.85-5 02-Jul-2003 miquels@cistron.nl
*
* This file is part of the sysvinit suite,
* Copyright (C) 1991-2003 Miquel van Smoorenburg.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/sysmacros.h>
#include <limits.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <stdio.h>
#include <utmp.h>
#include <pwd.h>
#include <fcntl.h>
#include <signal.h>
#include <setjmp.h>
#include <paths.h>
#ifndef _PATH_DEV
# define _PATH_DEV "/dev/"
#endif
#ifndef HOST_NAME_MAX
# define HOST_NAME_MAX 255
#endif
static sigjmp_buf jbuf;
/*
* Alarm handler
*/
/*ARGSUSED*/
# ifdef __GNUC__
static void handler(int arg __attribute__((unused)))
# else
static void handler(int arg)
# endif
{
siglongjmp(jbuf, 1);
}
/*
* Print a text, escape all characters not in Latin-1.
*/
static void feputs(const 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 {
/* Using variable number of data parameters in one
function makes the Clang compiler cry. -- Jesse
sprintf(uidbuf, uid ? "uid %d" : "root", (int)uid);
*/
if (uid)
sprintf(uidbuf, "uid %d", (int) uid);
else
sprintf(uidbuf, "root");
}
if ((tty = ttyname(0)) != NULL) {
const size_t plen = strlen(_PATH_DEV);
if (strncmp(tty, _PATH_DEV, plen) == 0) {
tty += plen;
if (tty[0] == '/')
tty++;
}
snprintf(ttynm, sizeof(ttynm), "(%.*s) ",
UT_LINESIZE, 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(const char *text, int remote)
{
FILE *tp;
struct sigaction sa;
struct utmp *utmp;
time_t t;
char term[UT_LINESIZE+ strlen(_PATH_DEV) + 1];
char line[81];
char hostname[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),
"\r\nRemote broadcast message (%s):\r\n\r\n",
date);
} else {
snprintf(line, sizeof(line),
"\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, _PATH_DEV, strlen(_PATH_DEV)) == 0) {
term[0] = 0;
strncat(term, utmp->ut_line, sizeof(term)-1);
} else
snprintf(term, sizeof(term), _PATH_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);
}

86
src/fstab-decode.c Normal file
View File

@ -0,0 +1,86 @@
/* fstab-decode(8).
Copyright (c) 2006 Red Hat, Inc. All rights reserved.
This copyrighted material is made available to anyone wishing to use, modify,
copy, or redistribute it subject to the terms and conditions of the GNU General
Public License v.2.
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.
Author: Miloslav Trmac <mitr@redhat.com> */
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
/* Decode the fstab-encoded string in place. */
static void
decode(char *s)
{
const char *src;
char *dest;
src = s;
dest = s;
while (*src != '\0') {
if (*src != '\\')
*dest = *src++;
else {
static const struct repl {
char orig[4];
size_t len;
char new;
} repls[] = {
#define R(X, Y) { X, sizeof(X) - 1, Y }
R("\\", '\\'),
R("011", '\t'),
R("012", '\n'),
R("040", ' '),
R("134", '\\')
#undef R
};
size_t i;
for (i = 0; i < sizeof (repls) / sizeof (repls[0]);
i++) {
if (memcmp(src + 1, repls[i].orig,
repls[i].len) == 0) {
*dest = repls[i].new;
src += 1 + repls[i].len;
goto found;
}
}
*dest = *src++;
found:
;
}
dest++;
}
*dest = '\0';
}
int
main (int argc, char *argv[])
{
size_t i;
if (argc < 2) {
fprintf(stderr, "Usage: fstab-decode command [arguments]\n");
return EXIT_FAILURE;
}
for (i = 2; i < (size_t)argc; i++)
decode(argv[i]);
execvp(argv[1], argv + 1);
fprintf(stderr, "fstab-decode: %s: %s\n", argv[1], strerror(errno));
return 127;
}

333
src/halt.c Normal file
View File

@ -0,0 +1,333 @@
/*
* Halt Stop the system running.
* It re-enables CTRL-ALT-DEL, so that a hard reboot can
* be done. If called as reboot, it will reboot the system.
*
* If the system is not in runlevel 0 or 6, halt will just
* execute a "shutdown -h" to halt the system, and reboot will
* execute an "shutdown -r". This is for compatibility with
* sysvinit 2.4.
*
* Usage: halt [-n] [-w] [-d] [-f] [-h] [-i] [-p]
* -n: don't sync before halting the system
* -w: only write a wtmp reboot record and exit.
* -d: don't write a wtmp record.
* -f: force halt/reboot, don't call shutdown.
* -h: put harddisks in standby mode
* -i: shut down all network interfaces.
* -p: power down the system (if possible, otherwise halt).
*
* Reboot and halt are both this program. Reboot
* is just a link to halt. Invoking the program
* as poweroff implies the -p option.
*
* Author: Miquel van Smoorenburg, miquels@cistron.nl
*
* Version: 2.86, 30-Jul-2004
*
* This file is part of the sysvinit suite,
* Copyright (C) 1991-2004 Miquel van Smoorenburg.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/param.h>
#include <stdlib.h>
#include <utmp.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
#include <sys/times.h>
#include <time.h>
#include <signal.h>
#include <stdio.h>
#include <getopt.h>
#include "reboot.h"
#include "runlevellog.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 int hdflush(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.
* Or the /var/run/runlevel log.
*/
int get_runlevel(void)
{
struct utmp *ut;
char *r;
int runlevel, status;
#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();
/* Did not find utmp entry, try to read from log file */
status = Read_Runlevel_Log(&runlevel);
if (status)
return runlevel;
/* 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, int should_poweroff, char *tm)
{
char *args[9];
int i = 0;
args[i++] = "shutdown";
args[i++] = fl;
if ( (! strcmp(fl, "-h") ) && (should_poweroff) )
args[i++] = "-P";
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);
}
if (chdir("/")) {
fprintf(stderr, "%s: chdir(/): %m\n", progname);
exit(1);
}
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", do_poweroff, 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();
/* Sync should be fine on its own for making sure data is written.
We probably call shutdown after this anyway to clean up.
-- Jesse
sleep(2);
*/
}
if (do_ifdown)
(void)ifdown();
if (do_hddown)
(void)hddown();
else
(void)hdflush();
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);
}

570
src/hddown.c Normal file
View File

@ -0,0 +1,570 @@
/*
* hddown.c Find all disks on the system and
* shut them down.
*
* Copyright (C) 2003 Miquel van Smoorenburg.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
char *v_hddown = "@(#)hddown.c 1.02 22-Apr-2003 miquels@cistron.nl";
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <time.h>
#include <string.h>
#include <fcntl.h>
#include <dirent.h>
#ifdef __linux__
#include <sys/ioctl.h>
#include <linux/hdreg.h>
#include <linux/fs.h>
#ifndef USE_SYSFS
# define USE_SYSFS 1
#endif
#if defined(USE_SYSFS) && (USE_SYSFS == 1)
/*
* sysfs part Find all disks on the system, list out IDE and unmanaged
* SATA disks, flush the cache of those and shut them down.
* Author: Werner Fink <werner@suse.de>, 2007/06/12
*
*/
#include <limits.h>
#include <errno.h>
#include <sys/stat.h>
#include <sys/types.h>
#ifdef WORDS_BIGENDIAN
#include <byteswap.h>
#endif
#define SYS_BLK "/sys/block"
#define SYS_CLASS "/sys/class/scsi_disk"
#define DEV_BASE "/dev"
#define ISSPACE(c) (((c)==' ')||((c)=='\n')||((c)=='\t')||((c)=='\v')||((c)=='\r')||((c)=='\f'))
/* Used in flush_cache_ext(), compare with <linux/hdreg.h> */
#define IDBYTES 512
#define MASK_EXT 0xE000 /* Bit 15 shall be zero, bit 14 shall be one, bit 13 flush cache ext */
#define TEST_EXT 0x6000
/* Maybe set in list_disks() and used in do_standby_disk() */
#define DISK_IS_IDE 0x00000001
#define DISK_IS_SATA 0x00000002
#define DISK_EXTFLUSH 0x00000004
#define DISK_REMOVABLE 0x00000008
#define DISK_MANAGED 0x00000010
#define DISK_FLUSHONLY 0x00000020
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;
FILE *fp;
int ret;
fp = hdopen(SYS_BLK "/%s/removable", d->d_name);
if (0 == (long)fp || -1 == (long)fp) {
if (-1 == (long)fp)
goto empty; /* error */
continue; /* no entry `removable' */
}
ret = getc(fp);
fclose(fp);
if (ret != '0')
(*flags) |= DISK_REMOVABLE;
if (d->d_name[0] == 'h') {
if ((*flags) & DISK_REMOVABLE)
continue; /* not a hard disk */
(*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 >= (int)sizeof(buf)) || (ret < 0))
goto empty; /* error */
ret = readlink(buf, lnk, sizeof(lnk));
if (ret >= (int)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 */
fp = hdopen(SYS_CLASS "/%s/manage_start_stop", ptr);
if (0 == (long)fp || -1 == (long)fp) {
if (-1 == (long)fp)
goto empty; /* error */
} else {
ret = getc(fp);
fclose(fp);
if (ret != '0') {
(*flags) |= DISK_MANAGED;
continue;
}
}
fp = hdopen(SYS_BLK "/%s/device/vendor", d->d_name);
if (0 == (long)fp || -1 == (long)fp) {
if (-1 == (long)fp)
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)) == 0) {
if ((*flags) & DISK_REMOVABLE)
continue; /* not a hard 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 (((*flags) & DISK_REMOVABLE) == 0)
continue; /* Seems to be a real SCSI disk */
if ((ret = flush_cache_ext(d->d_name))) {
if (ret < 0)
goto empty;
(*flags) |= DISK_EXTFLUSH;
}
break; /* Removable disk like USB stick to shutdown */
}
}
if (d == (struct dirent*)0)
goto empty;
return d->d_name;
empty:
return (char*)0;
}
/*
* Put an IDE/SCSI/SATA disk in standby mode.
* Code stolen from hdparm.c
*/
static int do_standby_disk(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 >= (int)sizeof(buf)) || (ret < 0))
return -1;
if ((fd = open(buf, O_RDWR|O_NONBLOCK)) < 0)
return -1;
switch (flags & DISK_EXTFLUSH) {
case DISK_EXTFLUSH:
if ((ret = ioctl(fd, HDIO_DRIVE_CMD, &flush1)) == 0)
break;
/* Else Fall through */
/* Extend flush rejected, try standard flush */
default:
ret = ioctl(fd, HDIO_DRIVE_CMD, &flush2) &&
ioctl(fd, BLKFLSBUF);
break;
}
if ((flags & DISK_FLUSHONLY) == 0x0) {
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_disk(disk, flags);
return closedir(blk);
}
/*
* List all disks and cause them to flush their buffers.
*/
int hdflush(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_disk(disk, (flags|DISK_FLUSHONLY));
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. Return 0 if the file didn't exist, or (FILE*)-1 if
* something else went wrong.
*/
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 >= (int)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 (0 == (long)fp || -1 == (long)fp) {
if (-1 == (long)fp)
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 >= (int)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);
}
int hdflush(void)
{
return 0;
}
#endif /* ! USE_SYSFS */
#else /* __linux__ */
int hddown(void)
{
return 0;
}
int hdflush(void)
{
return 0;
}
#endif /* __linux__ */
#ifdef STANDALONE
int main(int argc, char **argv)
{
return (hddown() == 0);
}
#endif

139
src/ifdown.c Normal file
View File

@ -0,0 +1,139 @@
/*
* ifdown.c Find all network interfaces on the system and
* shut them down.
*
* Copyright (C) 1998 Miquel van Smoorenburg.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
char *v_ifdown = "@(#)ifdown.c 1.11 02-Jun-1998 miquels@cistron.nl";
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <time.h>
#include <string.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <net/if.h>
#include <netinet/in.h>
#define MAX_IFS 64
/* XXX: Ideally this would get detected at configure time... */
#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || \
defined(__NetBSD__) || defined(__OpenBSD__)
#define HAVE_SOCKADDR_SA_LEN 1
#endif
#ifndef _SIZEOF_ADDR_IFREQ
#ifdef HAVE_SOCKADDR_SA_LEN
#define _SIZEOF_ADDR_IFREQ(ifr) \
((ifr).ifr_addr.sa_len > sizeof(struct sockaddr) ? \
(sizeof((ifr).ifr_name) + (ifr).ifr_addr.sa_len) : \
sizeof(struct ifreq))
#else
#define _SIZEOF_ADDR_IFREQ(ifr) sizeof(struct ifreq)
#endif
#endif
/*
* 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)
{
char ifr_buf[sizeof(struct ifreq) * MAX_IFS];
char *ifr_end;
struct ifconf ifc;
int fd;
int shaper;
if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
fprintf(stderr, "ifdown: ");
perror("socket");
return -1;
}
ifc.ifc_len = sizeof(ifr_buf);
ifc.ifc_buf = ifr_buf;
if (ioctl(fd, SIOCGIFCONF, &ifc) < 0) {
fprintf(stderr, "ifdown: ");
perror("SIOCGIFCONF");
close(fd);
return -1;
}
ifr_end = ifr_buf + ifc.ifc_len;
for (shaper = 1; shaper >= 0; shaper--) {
char *ifr_next = ifr_buf;
while (ifr_next < ifr_end) {
struct ifreq *ifr;
int flags;
ifr = (struct ifreq *)ifr_next;
ifr_next += _SIZEOF_ADDR_IFREQ(*ifr);
if ((strncmp(ifr->ifr_name, "shaper", 6) == 0)
!= shaper) continue;
if (strncmp(ifr->ifr_name, "lo", 2) == 0)
continue;
if (strchr(ifr->ifr_name, ':') != NULL)
continue;
/* Read interface flags */
if (ioctl(fd, SIOCGIFFLAGS, ifr) < 0) {
fprintf(stderr, "ifdown: shutdown ");
perror(ifr->ifr_name);
continue;
}
/*
* Expected in <net/if.h> according to
* "UNIX Network Programming".
*/
#ifdef ifr_flagshigh
flags = (ifr->ifr_flags & 0xffff) |
(ifr->ifr_flagshigh << 16);
#else
flags = ifr->ifr_flags;
#endif
if (flags & IFF_UP) {
flags &= ~(IFF_UP);
#ifdef ifr_flagshigh
ifr->ifr_flags = flags & 0xffff;
ifr->ifr_flagshigh = flags >> 16;
#else
ifr->ifr_flags = flags;
#endif
if (ioctl(fd, SIOCSIFFLAGS, ifr) < 0) {
fprintf(stderr, "ifdown: shutdown ");
perror(ifr->ifr_name);
}
}
}
}
close(fd);
return 0;
}

3128
src/init.c Normal file

File diff suppressed because it is too large Load Diff

165
src/init.h Normal file
View File

@ -0,0 +1,165 @@
/*
* 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
*
*/
#ifndef TRUE
#define TRUE 1
#endif
#ifndef FALSE
#define FALSE 0
#endif
/* 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 */
#define WAIT_BETWEEN_SIGNALS 3 /* default time to wait between TERM and KILL */
/* 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);
/* from dowall.c */
void wall(const char *text, int remote);
#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;
extern int wrote_wtmp_rlevel;
extern int wrote_utmp_rlevel;
extern char thislevel;
extern char prevlevel;
/* 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
#define D_WROTE_WTMP_RLEVEL -16
#define D_WROTE_UTMP_RLEVEL -17
#ifdef __FreeBSD__
#define UTMP_FILE "/var/run/utmp"
#define RUN_LVL 1
struct utmp
{
char ut_id[4];
};
#endif

82
src/initreq.h Normal file
View File

@ -0,0 +1,82 @@
/*
* initreq.h Interface to talk to init through /run/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 <sys/param.h>
#ifndef INIT_FIFO
#define INIT_FIFO "/run/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
#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

1272
src/killall5.c Normal file

File diff suppressed because it is too large Load Diff

970
src/last.c Normal file
View File

@ -0,0 +1,970 @@
/*
* 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 <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <time.h>
#include <stdio.h>
#include <ctype.h>
#include <utmp.h>
#include <errno.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>
#include <getopt.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>
#include "oldutmp.h"
#ifndef SHUTDOWN_TIME
# define SHUTDOWN_TIME 254
#endif
#ifndef PATH_MAX
#define PATH_MAX 2048
#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 allow_long_username = 0; /* Show usernames longer than 8 characters */
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 name_len = 8; /* Default print 8 characters of name */
int domain_len = 16; /* Default print 16 characters of domain */
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[PATH_MAX + 5]; /* max path + btmp + null terminator */
char *p;
memset(btmp, '\0', PATH_MAX + 5);
strncpy(btmp, WTMP_FILE, PATH_MAX);
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;
int mapped = 0;
flags = useip ? NI_NUMERICHOST : 0;
/*
* IPv4 or IPv6 ?
* 1. If last 3 4bytes are 0, must be IPv4
* 2. If IPv6 in IPv4, handle as IPv4
* 3. Anything else is IPv6
*
* Ugly.
*/
if (a[0] == 0 && a[1] == 0 && a[2] == (int32_t)htonl (0xffff))
mapped = 1;
if (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[512];
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;
strncpy(logintime, ctime(&tmp), sizeof(logintime));
logintime[sizeof(logintime)-1] = 0; /* enforce null termination */
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 >= (int)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
#define str(s) # s
#define xstr(s) str(s)
if (!altlist) {
if (allow_long_username)
{
len = snprintf(final, sizeof(final),
oldfmt ? "%-" xstr(OLD_NAMESIZE) "." xstr(OLD_NAMESIZE) "s %-12.12s "
"%-16.16s %-16.16s %-7.7s %-12.12s\n"
: "%-" xstr(UT_NAMESIZE) "." xstr(UT_NAMESIZE) "s %-12.12s "
"%-16.16s %-16.16s %-7.7s %-12.12s\n",
p->ut_name, utline,
domain, logintime, logouttime, length);
}
else /* show short username */
{
len = snprintf(final, sizeof(final),
fulltime ?
"%-8.*s %-12.12s %-16.*s %-24.24s %-26.26s %-12.12s\n" :
"%-8.*s %-12.12s %-16.*s %-16.16s %-7.7s %-12.12s\n",
name_len, p->ut_name, utline,
domain_len, domain, logintime, logouttime, length);
} /* show short username */
} else {
if (allow_long_username)
{
len = snprintf(final, sizeof(final),
oldfmt ? "%-" xstr(OLD_NAMESIZE) "." xstr(OLD_NAMESIZE) "s %-12.12s "
"%-16.16s %-7.7s %-16.16s %s\n"
: "%-" xstr(UT_NAMESIZE) "." xstr(UT_NAMESIZE) "s %-12.12s "
"%-16.16s %-7.7s %-16.16s %s\n",
p->ut_name, utline,
logintime, logouttime, length, domain);
}
else /* show short username */
{
len = snprintf(final, sizeof(final),
fulltime ?
"%-8.*s %-12.12s %-24.24s %-26.26s %-12.12s %s\n" :
"%-8.*s %-12.12s %-16.16s %-7.7s %-12.12s %s\n",
name_len, p->ut_name, utline,
logintime, logouttime, length, domain);
} /* done showing short username */
}
} else
if (allow_long_username)
{
len = snprintf(final, sizeof(final),
oldfmt ? "%-" xstr(OLD_NAMESIZE) "." xstr(OLD_NAMESIZE) "s %-12.12s "
"%-16.16s %-7.7s %-12.12s\n"
: "%-" xstr(UT_NAMESIZE) "." xstr(UT_NAMESIZE) "s %-12.12s "
"%-16.16s %-7.7s %-12.12s\n",
p->ut_name, utline,
logintime, logouttime, length);
}
else /* show short username */
{
len = snprintf(final, sizeof(final),
fulltime ?
"%-8.*s %-12.12s %-24.24s %-26.26s %-12.12s\n" :
"%-8.*s %-12.12s %-16.16s %-7.7s %-12.12s\n",
name_len, p->ut_name, utline,
logintime, logouttime, length);
} /* end of showing short username */
#if defined(__GLIBC__)
# if (__GLIBC__ == 2) && (__GLIBC_MINOR__ == 0)
final[sizeof(final)-1] = '\0';
# endif
#endif
/*
* Print out "final" string safely.
*/
for (s = final; *s; s++) {
if (*s == '\n' || (*s >= 32 && (unsigned char)*s <= 126))
putchar(*s);
else
putchar('*');
}
if (len < 0 || (size_t)len >= sizeof(final))
putchar('\n');
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] [-adioxFw] [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:RxadFliot:0123456789w")) != 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 'l':
allow_long_username = 1;
break;
case 't':
if ((until = parsetm(optarg)) == (time_t)-1) {
fprintf(stderr, "%s: Invalid time value \"%s\"\n",
progname, optarg);
usage(progname);
}
break;
case 'w':
if (UT_NAMESIZE > name_len)
name_len = UT_NAMESIZE;
if (UT_HOSTSIZE > domain_len)
domain_len = UT_HOSTSIZE;
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) {
strncpy(ut.ut_line, "system down", OLD_LINESIZE - 1);
quit = list(&ut, lastboot, R_NORMAL);
}
lastdown = lastrch = ut.ut_time;
down = 1;
break;
case OLD_TIME:
case NEW_TIME:
if (extended) {
strncpy(ut.ut_line,
ut.ut_type == NEW_TIME ? "new time" :
"old time", OLD_LINESIZE - 1);
quit = list(&ut, lastdown, R_TIMECHANGE);
}
break;
case BOOT_TIME:
strncpy(ut.ut_line, "system boot", OLD_LINESIZE - 1);
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;
}

338
src/logsave.c Normal file
View File

@ -0,0 +1,338 @@
/*
* logsave.c --- A program which saves the output of a program until
* /var/log is mounted.
*
* Copyright (C) 2003 Theodore Ts'o.
*
* %Begin-Header%
* This file may be redistributed under the terms of the GNU Public
* License.
* %End-Header%
*/
#ifndef _XOPEN_SOURCE
#define _XOPEN_SOURCE 600 /* for inclusion of sa_handler in Solaris */
#endif
#ifndef HAVE_SIGNAL_H
#define HAVE_SIGNAL_H
#endif
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <time.h>
#include <errno.h>
#ifdef HAVE_SIGNAL_H
#include <signal.h>
#endif
#ifdef HAVE_GETOPT_H
#include <getopt.h>
#else
extern char *optarg;
extern int optind;
#endif
static int outfd = -1;
static int outbufsize = 0;
static void *outbuf = 0;
static int verbose = 0;
static int do_skip = 0;
static int skip_mode = 0;
static pid_t child_pid = -1;
static void usage(char *progname)
{
printf("Usage: %s [-asv] logfile program\n", progname);
exit(1);
}
#define SEND_LOG 0x01
#define SEND_CONSOLE 0x02
#define SEND_BOTH 0x03
/*
* Helper function that does the right thing if write returns a
* partial write, or an EAGAIN/EINTR error.
*/
static int write_all(int fd, const char *buf, size_t count)
{
ssize_t ret;
int c = 0;
while (count > 0) {
ret = write(fd, buf, count);
if (ret < 0) {
if ((errno == EAGAIN) || (errno == EINTR))
continue;
return -1;
}
count -= ret;
buf += ret;
c += ret;
}
return c;
}
static void send_output(const char *buffer, int c, int flag)
{
const char *cp;
char *n;
int cnt, d, del;
if (c == 0)
c = strlen(buffer);
if (flag & SEND_CONSOLE) {
cnt = c;
cp = buffer;
while (cnt) {
del = 0;
for (d=0; d < cnt; d++) {
if (skip_mode &&
(cp[d] == '\001' || cp[d] == '\002')) {
del = 1;
break;
}
}
write_all(1, cp, d);
if (del)
d++;
cnt -= d;
cp += d;
}
}
if (!(flag & SEND_LOG))
return;
if (outfd > 0)
write_all(outfd, buffer, c);
else {
n = realloc(outbuf, outbufsize + c);
if (n) {
outbuf = n;
memcpy(((char *)outbuf)+outbufsize, buffer, c);
outbufsize += c;
}
}
}
static int do_read(int fd)
{
int c;
char buffer[4096], *cp, *sep;
c = read(fd, buffer, sizeof(buffer)-1);
if (c <= 0)
return c;
if (do_skip) {
send_output(buffer, c, SEND_CONSOLE);
buffer[c] = 0;
cp = buffer;
while (*cp) {
if (skip_mode) {
cp = strchr(cp, '\002');
if (!cp)
return 0;
cp++;
skip_mode = 0;
continue;
}
sep = strchr(cp, '\001');
if (sep)
*sep = 0;
send_output(cp, 0, SEND_LOG);
if (sep) {
cp = sep + 1;
skip_mode = 1;
} else
break;
}
} else
send_output(buffer, c, SEND_BOTH);
return c;
}
static void signal_term(int sig)
{
if (child_pid > 0)
kill(child_pid, sig);
}
static int run_program(char **argv)
{
int fds[2];
int status, rc, pid;
char buffer[80];
#ifdef HAVE_SIGNAL_H
struct sigaction sa;
#endif
if (pipe(fds) < 0) {
perror("pipe");
exit(1);
}
#ifdef HAVE_SIGNAL_H
memset(&sa, 0, sizeof(struct sigaction));
sa.sa_handler = signal_term;
sigaction(SIGINT, &sa, 0);
sigaction(SIGTERM, &sa, 0);
#ifdef SA_RESTART
sa.sa_flags = SA_RESTART;
#endif
#endif
pid = fork();
if (pid < 0) {
perror("vfork");
exit(1);
}
if (pid == 0) {
dup2(fds[1],1); /* fds[1] replaces stdout */
dup2(fds[1],2); /* fds[1] replaces stderr */
close(fds[0]); /* don't need this here */
close(fds[1]);
execvp(argv[0], argv);
perror(argv[0]);
exit(1);
}
child_pid = pid;
close(fds[1]);
while (!(waitpid(pid, &status, WNOHANG ))) {
do_read(fds[0]);
}
child_pid = -1;
do_read(fds[0]);
close(fds[0]);
if ( WIFEXITED(status) ) {
rc = WEXITSTATUS(status);
if (rc) {
send_output(argv[0], 0, SEND_BOTH);
sprintf(buffer, " exited with status code %d\n", rc);
send_output(buffer, 0, SEND_BOTH);
}
} else {
if (WIFSIGNALED(status)) {
send_output(argv[0], 0, SEND_BOTH);
sprintf(buffer, "died with signal %d\n",
WTERMSIG(status));
send_output(buffer, 0, SEND_BOTH);
return 1;
}
rc = 0;
}
return rc;
}
static int copy_from_stdin(void)
{
int c, bad_read = 0;
while (1) {
c = do_read(0);
if ((c == 0 ) ||
((c < 0) && ((errno == EAGAIN) || (errno == EINTR)))) {
if (bad_read++ > 3)
break;
continue;
}
if (c < 0) {
perror("read");
exit(1);
}
bad_read = 0;
}
return 0;
}
int main(int argc, char **argv)
{
int c, pid, rc;
char *outfn, **cpp;
int openflags = O_CREAT|O_WRONLY|O_TRUNC;
int send_flag = SEND_LOG;
int do_stdin;
time_t t;
while ((c = getopt(argc, argv, "+asv")) != EOF) {
switch (c) {
case 'a':
openflags &= ~O_TRUNC;
openflags |= O_APPEND;
break;
case 's':
do_skip = 1;
break;
case 'v':
verbose++;
send_flag |= SEND_CONSOLE;
break;
}
}
if (optind == argc || optind+1 == argc)
usage(argv[0]);
outfn = argv[optind];
optind++;
argv += optind;
/* argc -= optind; - this is not used */
outfd = open(outfn, openflags, 0644);
do_stdin = !strcmp(argv[0], "-");
send_output("Log of ", 0, send_flag);
if (do_stdin)
send_output("stdin", 0, send_flag);
else {
for (cpp = argv; *cpp; cpp++) {
send_output(*cpp, 0, send_flag);
send_output(" ", 0, send_flag);
}
}
send_output("\n", 0, send_flag);
t = time(0);
send_output(ctime(&t), 0, send_flag);
send_output("\n", 0, send_flag);
if (do_stdin)
rc = copy_from_stdin();
else
rc = run_program(argv);
send_output("\n", 0, send_flag);
t = time(0);
send_output(ctime(&t), 0, send_flag);
send_output("----------------\n", 0, send_flag);
if (outbuf) {
pid = fork();
if (pid < 0) {
perror("fork");
exit(1);
}
if (pid) {
if (verbose)
printf("Backgrounding to save %s later\n",
outfn);
exit(rc);
}
setsid(); /* To avoid getting killed by init */
while (outfd < 0) {
outfd = open(outfn, openflags, 0644);
sleep(1);
}
write_all(outfd, outbuf, outbufsize);
free(outbuf);
}
if (outfd >= 0)
close(outfd);
exit(rc);
}

124
src/mesg.c Normal file
View File

@ -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 <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <grp.h>
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;
}

196
src/mountpoint.c Normal file
View File

@ -0,0 +1,196 @@
/*
* 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 <sys/stat.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <stdarg.h>
#include <getopt.h>
#include <stdio.h>
#ifndef PATH_MAX
#define PATH_MAX 2048
#endif
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;
}
/*
This function checks to see if the passed path is listed in the
/proc/mounts file. If /proc/mounts does not exist or cannot
be read, we return false. If the path is not found, we return false.
If the path is found we return true.
*/
int do_proc_check(char *path)
{
FILE *mounts;
char *found = NULL, *status;
char *target_string;
char line[512];
int last_character;
target_string = (char *) calloc( strlen(path) + 3, sizeof(char));
if (! target_string)
return 0;
mounts = fopen("/proc/mounts", "r");
if (! mounts)
{
free(target_string);
return 0;
}
/* copy path so we can adjust it without harming the original */
sprintf(target_string, "%s", path);
/* trim trailing slash */
last_character = strlen(target_string) - 1;
if ( (last_character >= 1) && (target_string[last_character] == '/') )
target_string[last_character] = '\0';
/* Search for path name in /proc/mounts file */
status = fgets(line, 512, mounts);
while ( (status) && (! found) )
{
found = strstr(line, target_string);
if (! found)
status = fgets(line, 512, mounts);
}
fclose(mounts);
free(target_string);
return found ? 1 : 0;
}
void usage(void) {
fprintf(stderr, "Usage: mountpoint [-p] [-q] [-d] [-x] path\n");
exit(1);
}
int main(int argc, char **argv)
{
struct stat st, st2;
char buf[PATH_MAX + 1];
char *path;
int quiet = 0;
int showdev = 0;
int xdev = 0;
int c, r;
int check_proc = 0;
while ((c = getopt(argc, argv, "dpqx")) != EOF) switch(c) {
case 'd':
showdev = 1;
break;
case 'p':
check_proc = 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);
strncat(buf, "/..", 3);
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) ) ;
(A || (!A && B)) is the same as (A || B) so simplifying this below.
Thanks to David Binderman for pointing this out. -- Jesse
*/
r = ( (st.st_dev != st2.st_dev) || (st.st_ino == st2.st_ino) );
/* Mount point was not found yet. If we have access
to /proc we can check there too. */
if ( (!r) && (check_proc) )
{
if ( do_proc_check(path) )
r = 1;
}
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;
}

41
src/oldutmp.h Normal file
View File

@ -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

51
src/paths.h Normal file
View File

@ -0,0 +1,51 @@
/*
* paths.h Paths of files that init and related utilities need.
*
* Version: @(#) paths.h 2.85-8 05-Nov-2003
*
* Author: Miquel van Smoorenburg, <miquels@cistron.nl>
*
* 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_OLD "/etc/powerstatus" /* COMPAT: SIGPWR reason (OK/BAD) */
#define PWRSTAT "/var/run/powerstatus" /* COMPAT: SIGPWR reason (OK/BAD) */
#define RUNLEVEL_LOG "/var/run/runlevel" /* neutral place to store run level */
#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

141
src/readbootlog.c Normal file
View File

@ -0,0 +1,141 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "bootlogd.h"
#ifndef MAX_LINE
#define MAX_LINE 256
#endif
void print_usage()
{
printf("readbootlog reads the system's boot log, stripping away\n");
printf("control characters to make the log human readable.\n\n");
printf("Usage for readbootlog: readbootlog [-h] [-f logfile]\n");
printf("\t\t-h display this help message\n");
printf("\t\t-f <logfile> display a specific boot log file\n");
printf("\t\t default is to use %s\n", LOGFILE);
printf("\n");
}
/*
Clean up the unwanted characters from a line of input.
Cleaned line is passed back in output_line.
Returns TRUE on success or FALSE if we encounter an error.
*/
int Clean_Line(char *source_line, char *output_line)
{
int source_index = 0, target_index = 0;
int source_max_index;
char a_letter;
int done;
char *garbage;
if (! source_line) return FALSE;
if (! output_line) return FALSE;
source_max_index = strlen(source_line);
while (source_index < source_max_index)
{
a_letter = source_line[source_index];
if (a_letter == '^')
{
/* skip ahead until we find a valid place to stop */
done = FALSE;
while (! done)
{
source_index++;
if (source_index >= source_max_index)
done = TRUE;
else
{
a_letter = source_line[source_index];
if ( (a_letter == '.') || (a_letter == ' ') ||
(a_letter == '(') || (a_letter == 'd') ||
(a_letter == '\n') )
done = TRUE;
}
}
} /* done found character to scrub */
else if ( (a_letter == '?') && (source_line[source_index + 1] == '?') &&
(source_line[source_index + 2] == '7') )
{
source_index += 3;
output_line[target_index] = ' ';
target_index++;
}
else if ( (a_letter == '8') && (source_line[source_index + 1] == '?') &&
(source_line[source_index + 2] == '?') )
{
source_index += 3;
output_line[target_index] = ']';
target_index++;
}
else
{
output_line[target_index] = a_letter;
target_index++;
source_index++;
} /* found valid character */
} /* done processing line */
garbage = strstr(output_line, " .\n");
if (garbage)
{
garbage[0] = '\n';
garbage[1] = '\0';
}
return TRUE;
}
int main(int argc, char *argv[])
{
FILE *log_file = NULL;
char *log_filename = LOGFILE;
char line[MAX_LINE];
char output[MAX_LINE];
char *status;
int c;
/* check provided options */
while ( (c = getopt(argc, argv, "hf:") ) != EOF)
{
switch (c)
{
case 'h':
print_usage();
exit(0);
case 'f':
log_filename = optarg;
break;
default:
print_usage();
exit(1);
}
} /* done processing arguments */
log_file = fopen(log_filename, "r");
if (log_file)
{
status = fgets(line, MAX_LINE, log_file);
while (status)
{
memset(output, '\0', MAX_LINE);
if ( Clean_Line(line, output) )
{
printf("%s", output);
}
status = fgets(line, MAX_LINE, log_file);
} /* done reading file lines */
fclose(log_file);
} /* end of successfully opened log file */
else
{
fprintf(stderr, "Unable to open file %s\n", log_filename);
return 1;
}
return 0;
}

51
src/reboot.h Normal file
View File

@ -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 <sys/reboot.h>
#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)

61
src/runlevel.c Normal file
View File

@ -0,0 +1,61 @@
/*
* 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 <stdio.h>
#include <utmp.h>
#include <time.h>
#include <stdlib.h>
#include "runlevellog.h"
int main(argc, argv)
int argc;
char **argv;
{
struct utmp *ut;
char prev;
int status, runlevel;
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);
}
}
endutent();
status = Read_Runlevel_Log(&runlevel);
if (status)
{
printf("N %c\n", runlevel);
return 0;
}
printf("unknown\n");
return(1);
}

70
src/runlevellog.c Normal file
View File

@ -0,0 +1,70 @@
/*
* runlevellog - Saves and restores runlevel from distor-neutral location.
*
*
* This file is part of the sysvinit suite,
* Copyright (C) 2018 Jesse Smith
*
* 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; version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <stdio.h>
#include "paths.h"
#include "runlevellog.h"
/*
Write the current runlevel to its log file.
The function returns TRUE on success and FALSE
on failure.
*/
int Write_Runlevel_Log(int new_runlevel)
{
FILE *log_file;
int status;
log_file = fopen(RUNLEVEL_LOG, "w");
if (! log_file)
return FALSE;
status = fprintf(log_file, "%c", new_runlevel);
fclose(log_file);
if (status < 1)
return FALSE;
return TRUE;
}
/*
This function reads the last runlevel from the log file.
The function stores the read value at the addressed passed
into the function (aka runlevel). The function returns
TRUE on success and FALSE on failure.
*/
int Read_Runlevel_Log(int *runlevel)
{
FILE *log_file;
int status;
log_file = fopen(RUNLEVEL_LOG, "r");
if (! log_file)
return FALSE;
status = fscanf(log_file, "%c", (char *) runlevel);
fclose(log_file);
if (status < 1)
return FALSE;
return TRUE;
}

36
src/runlevellog.h Normal file
View File

@ -0,0 +1,36 @@
/*
* runlevellog - Saves and restores runlevel from distor-neutral location.
*
*
* This file is part of the sysvinit suite,
* Copyright (C) 2018 Jesse Smith
*
* 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; version 2 of the License.
*
* 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 RUNLEVEL_LOG_HEADER__
#define RUNLEVEL_LOG_HEADER__
#ifndef TRUE
#define TRUE 1
#endif
#ifndef FALSE
#define FALSE 0
#endif
int Write_Runlevel_Log(int new_runlevel);
int Read_Runlevel_Log(int *runlevel);
#endif

28
src/set.h Normal file
View File

@ -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)

836
src/shutdown.c Normal file
View File

@ -0,0 +1,836 @@
/*
* 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
*/
#ifndef _GNU_SOURCE
# define _GNU_SOURCE /* otherwise `extern char **environ' is missed */
#endif
#ifndef ACCTON_OFF
# define ACCTON_OFF 0
#endif
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#ifdef __linux__
#include <sys/sysmacros.h> /* brought in my LFS patch */
#endif
#include <time.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <errno.h>
#include <stdlib.h>
#include <stdio.h>
#include <signal.h>
#include <fcntl.h>
#include <stdarg.h>
#ifdef __FreeBSD__
#include <utmpx.h>
#else
#include <utmp.h>
#endif
#include <syslog.h>
#include "paths.h"
#include "reboot.h"
#include "initreq.h"
#include "init.h"
#ifdef __FreeBSD__
extern char **environ;
#endif
#define MESSAGELEN 256
#define STATELEN 64
#define WHEN_SIZE 64
/* Whether we should warn system is shutting down */
#define QUIET_FULL 2
#define QUIET_PARTIAL 1
#define QUIET_NONE 0
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[STATELEN]; /* What are we gonna do */
int doself = 0; /* Don't use init */
int got_alrm = 0;
char *clean_env[] = {
"HOME=/",
"PATH=" PATH_DEFAULT,
"TERM=dumb",
"SHELL=/bin/sh",
NULL,
};
/* 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.
*/
# ifdef __GNUC__
void stopit(int sig __attribute__((unused)))
# else
void stopit(int sig)
# endif
{
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 [-akrhPHfFnc] [-t sec] 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 can only be used along with -h flag.\n"
"\t\t -H: halt action is to just halt.\n"
"\t\t can only be used along with -h flag.\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 -q: quiet mode - display fewer shutdown warnings.\n"
"\t\t -Q: full quiet mode - display only final shutdown warning.\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;
size_t 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 >= (int)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 /run/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) {
ssize_t p = 0;
size_t s = sizeof(request);
void *ptr = &request;
while (s > 0) {
p = write(fd, ptr, s);
if (p < 0) {
if (errno == EINTR || errno == EAGAIN)
continue;
break;
}
ptr += p;
s -= p;
}
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 issue_warn(int mins)
{
char buf[MESSAGELEN + sizeof(newstate) + 1];
int len;
buf[0] = 0;
strncpy(buf, message, MESSAGELEN);
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, 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);
if (chdir("/"))
exit(1);
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: %s.\r\n", strerror(errno));
exit(1);
}
/* Kill all processes. */
fprintf(stderr, "shutdown: sending all processes the TERM signal...\r\n");
kill(-1, SIGTERM);
sleep(sltime ? atoi(sltime) : WAIT_BETWEEN_SIGNALS);
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. */
/* Give init the chance to collect zombies. */
/* sleep(1); */
/* Record the fact that we're going down */
write_wtmp("shutdown", "~~", 0, RUN_LVL, "~~");
/* This is for those who have quota installed. */
#if defined(ACCTON_OFF)
# if (ACCTON_OFF > 1) && (_BSD_SOURCE || (_XOPEN_SOURCE && _XOPEN_SOURCE < 500))
/* This is an alternative way to disable accounting, saving a fork() */
if (acct(NULL))
fprintf(stderr, "shutdown: can not stop process accounting: %s.\r\n", strerror(errno));
# elif (ACCTON_OFF > 0)
spawn(1, "accton", "off", NULL);
# else
spawn(1, "accton", NULL);
# endif
#endif
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 issue_shutdown(char *halttype)
{
char *args[8];
int argp = 0;
int do_halt = (down_level[0] == '0');
/* Warn for the last time */
issue_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 quiet_mode)
{
int ret;
if (quiet_mode == QUIET_FULL) return FALSE;
else if (quiet_mode == QUIET_PARTIAL)
{
if (wt == 10)
return TRUE;
else if (wt == 5)
return TRUE;
else if ( (wt % 60) == 0)
return TRUE;
else
return FALSE;
}
/* no silence setting, print lots of warnings */
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, target_time;
char *halttype;
char *downusers[32];
char buf[128];
char term[UT_LINESIZE + 6];
char *sp;
char when[WHEN_SIZE];
int c, i, wt;
int hours, mins;
int didnolog = 0;
int cancel = 0;
int useacl = 0;
int pid = 0;
int user_ok = 0;
int quiet_level = QUIET_NONE; /* Whether to display shutdown warning */
/* We can be installed setuid root (executable for a special group) */
/*
This way is risky, do error check on setuid call.
setuid(geteuid());
*/
errno = 0;
if (setuid(geteuid()) == -1) {
fprintf(stderr, "%s (%d): %s\n", __FILE__, __LINE__, strerror(errno));
abort();
}
if (getuid() != 0) {
fprintf(stderr, "shutdown: you must be root to do that!\n");
usage();
exit(1);
}
strcpy(down_level, "1");
halttype = NULL;
memset(when, '\0', WHEN_SIZE);
/* Process the options. */
while((c = getopt(argc, argv, "HPacqQkrhnfFyt:g:i:")) != EOF) {
switch(c) {
case 'H':
halttype = "HALT";
break;
case 'P':
halttype = "POWEROFF";
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 'q': /* put into somewhat quiet mode */
quiet_level = QUIET_PARTIAL;
break;
case 'Q': /* put into full quiet mode */
quiet_level = QUIET_FULL;
break;
case 'y': /* Ignored for sysV compatibility */
break;
case 'g': /* sysv style to specify time. */
strncpy(when, optarg, WHEN_SIZE - 1);
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) {
if (fscanf(fp, "%d", &pid) != 1)
pid = 0;
fclose(fp);
}
/* Read remaining words, skip time if needed. */
message[0] = 0;
for(c = optind + (!cancel && !when[0]); 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, 0);
exit(0);
}
/* Check syntax. */
if (when[0] == '\0') {
if (optind == argc) usage();
strncpy(when, argv[optind++], WHEN_SIZE - 1);
}
/* 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':
strncpy(newstate, "for system halt", STATELEN);
break;
case '6':
strncpy(newstate, "for reboot", STATELEN);
break;
case '1':
strncpy(newstate, "to maintenance mode", STATELEN);
break;
default:
snprintf(newstate, STATELEN, "to runlevel %s", down_level);
break;
}
/* Go to the root directory */
if (chdir("/")) {
fprintf(stderr, "shutdown: chdir(/): %m\n");
exit(1);
}
/* 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);
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");
sp = when;
if (when[0] == '+') sp++;
/* Decode shutdown time. */
for ( ; *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) issue_shutdown(halttype);
/* Rather than loop and reduce wt (wait time) once per minute,
we shall check the current time against the target time.
Then calculate the remaining wating time based on the difference
between current time and target time.
This avoids missing shutdown time (target time) after the
computer has been asleep. -- Jesse
*/
/* target time, in seconds = current time + wait time */
time(&t);
target_time = t + (60 * wt);
/* Give warnings on regular intervals and finally shutdown. */
if (wt < 15 && !needwarning(wt, quiet_level)) issue_warn(wt);
while(wt) {
if (wt <= 5 && !didnolog) {
donologin(wt);
didnolog++;
}
if (needwarning(wt, quiet_level)) issue_warn(wt);
hardsleep(60);
time(&t); /* get current time once per minute */
if (t >= target_time) /* past the target */
wt = 0;
else if ( (target_time - t) <= 60 ) /* less 1 min remains */
{
hardsleep(target_time - t);
wt = 0;
}
else /* more than 1 min remains */
wt = (int) (target_time - t) / 60;
}
issue_shutdown(halttype);
return 0; /* Never happens */
}

1043
src/sulogin.c Normal file

File diff suppressed because it is too large Load Diff

268
src/utmp.c Normal file
View File

@ -0,0 +1,268 @@
/*
* 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 <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <sys/utsname.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <stdio.h>
#include <time.h>
#include <fcntl.h>
#include <string.h>
#ifdef __FreeBSD__
#include <utmpx.h>
#else
#include <utmp.h>
#endif
#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;
/*
* Can't do much if WTMP_FILE is not present or not writable.
*/
if (access(WTMP_FILE, W_OK) < 0)
return;
/*
* 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, "~");
/*
* Note if we are going to write a runlevel record.
*/
if (type == RUN_LVL) wrote_wtmp_rlevel++;
/*
* See if we need to write a runlevel 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 runlevel record.
*/
if (wrote_wtmp_rlevel == 0 && type != RUN_LVL) {
int runlevel = thislevel;
int oldlevel = prevlevel;
write_wtmp("runlevel", "~~", runlevel + 256 * oldlevel, RUN_LVL, "~");
}
#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 or not writable.
*/
if (access(UTMP_FILE, W_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 UTMP we write a delayed boot record.
*/
if (wrote_utmp_reboot == 0 && type != BOOT_TIME)
write_utmp("reboot", "~~", 0, BOOT_TIME, "~", NULL);
/*
* Note if we are going to write a runlevel record.
*/
if (type == RUN_LVL) wrote_utmp_rlevel++;
/*
* See if we need to write a runlevel 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 UTMP we write a delayed runlevel record.
*/
if (wrote_utmp_rlevel == 0 && type != RUN_LVL) {
int runlevel = thislevel;
int oldlevel = prevlevel;
write_utmp("runlevel", "~~", runlevel + 256 * oldlevel, RUN_LVL, "~", 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);
}

311
src/utmpdump.c Normal file
View File

@ -0,0 +1,311 @@
/*
* utmpdump Simple program to dump UTMP and WTMP files in
* raw format, so they can be examined.
*
* Author: Miquel van Smoorenburg, <miquels@cistron.nl>
* Danek Duvall <duvall@alumni.princeton.edu>
*
* 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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <utmp.h>
#include <time.h>
#include <ctype.h>
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#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_buf[INET6_ADDRSTRLEN+1];
const char *addr_string, *time_string;
void *in_addr = &ut.ut_addr_v6;
size_t addr_length = INET6_ADDRSTRLEN;
int addr_family = AF_INET6;
if (!ut.ut_addr_v6[1] && !ut.ut_addr_v6[2] && !ut.ut_addr_v6[3]) {
addr_family = AF_INET;
addr_length = INET_ADDRSTRLEN;
in_addr = &ut.ut_addr;
}
if ((addr_string = inet_ntop(addr_family, in_addr, addr_buf, addr_length)) == 0) {
addr_buf[0] = '\0';
addr_string = &addr_buf[0];
}
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
# ifdef __GNUC__
undump(FILE *fp, int forever __attribute__((unused)), int oldfmt)
#else
undump(FILE *fp, int forever, int oldfmt)
#endif
{
struct utmp ut;
struct oldutmp uto;
char s_addr[16], s_time[29], *linestart;
linestart = malloc(1024 * sizeof *linestart);
s_addr[15] = 0;
s_time[28] = 0;
while(fgets(linestart, 1023, fp))
{
char *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);
(void)line; /* Quiet down static source analyzers */
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);
}
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;
}

123
src/wall.c Normal file
View File

@ -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 <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <pwd.h>
#include <syslog.h>
#include <sys/types.h>
#include "init.h"
#define MAXLEN 4096
#define LINE_SIZE 80
#define MAXLINES 20
int main(int argc, char **argv)
{
char buf[MAXLEN];
char line[LINE_SIZE + 3]; /* leave room for \r\n\0 */
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-4) 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 (%zu chars)",
whoami, i, strlen(buf));
closelog();
unsetenv("TZ");
wall(buf, remote);
/*NOTREACHED*/
return 0;
}