Index: Makefile
===================================================================
--- Makefile (nonexistent)
+++ Makefile (revision 5)
@@ -0,0 +1,60 @@
+
+COMPONENT_TARGETS = $(HARDWARE_NOARCH)
+
+
+include ../../../../build-system/constants.mk
+
+
+url = $(DOWNLOAD_SERVER)/sources/packages/a/sysvinit
+
+versions = 2.99
+pkgname = sysvinit
+suffix = tar.xz
+
+tarballs = $(addsuffix .$(suffix), $(addprefix $(pkgname)-, $(versions)))
+sha1s = $(addsuffix .sha1sum, $(tarballs))
+
+patches = $(CURDIR)/patches/sysvinit-2.99-initctl.patch
+patches += $(CURDIR)/patches/sysvinit-2.99-paths.patch
+patches += $(CURDIR)/patches/sysvinit-2.99-version.patch
+
+.NOTPARALLEL: $(patches)
+
+
+BUILD_TARGETS = $(tarballs) $(sha1s) $(patches)
+
+
+include ../../../../build-system/core.mk
+
+
+.PHONY: download_clean
+
+
+$(tarballs):
+ @echo -e "\n======= Downloading source tarballs =======" ; \
+ for tarball in $(tarballs) ; do \
+ echo "$(url)/$$tarball" | xargs -n 1 -P 100 wget $(WGET_OPTIONS) - & \
+ done ; wait
+
+$(sha1s): $(tarballs)
+ @for sha in $@ ; do \
+ echo -e "\n======= Downloading '$$sha' signature =======\n" ; \
+ echo "$(url)/$$sha" | xargs -n 1 -P 100 wget $(WGET_OPTIONS) - & wait %1 ; \
+ touch $$sha ; \
+ echo -e "\n======= Check the '$$sha' sha1sum =======\n" ; \
+ sha1sum --check $$sha ; ret="$$?" ; \
+ if [ "$$ret" == "1" ]; then \
+ echo -e "\n======= ERROR: Bad '$$sha' sha1sum =======\n" ; \
+ exit 1 ; \
+ fi ; \
+ done
+
+$(patches): $(sha1s)
+ @echo -e "\n======= Create Patches =======\n" ; \
+ ( cd create-2.99-initctl-patch ; ./create.patch.sh ) ; \
+ ( cd create-2.99-paths-patch ; ./create.patch.sh ) ; \
+ ( cd create-2.99-version-patch ; ./create.patch.sh ) ; \
+ echo -e "\n"
+
+download_clean:
+ @rm -f $(tarballs) $(sha1s) $(patches)
Index: create-2.99-initctl-patch/create.patch.sh
===================================================================
--- create-2.99-initctl-patch/create.patch.sh (nonexistent)
+++ create-2.99-initctl-patch/create.patch.sh (revision 5)
@@ -0,0 +1,15 @@
+#!/bin/sh
+
+VERSION=2.99
+
+tar --files-from=file.list -xJvf ../sysvinit-$VERSION.tar.xz
+mv sysvinit-$VERSION sysvinit-$VERSION-orig
+
+cp -rf ./sysvinit-$VERSION-new ./sysvinit-$VERSION
+
+diff --unified -Nr sysvinit-$VERSION-orig sysvinit-$VERSION > sysvinit-$VERSION-initctl.patch
+
+mv sysvinit-$VERSION-initctl.patch ../patches
+
+rm -rf ./sysvinit-$VERSION
+rm -rf ./sysvinit-$VERSION-orig
Property changes on: create-2.99-initctl-patch/create.patch.sh
___________________________________________________________________
Added: svn:executable
## -0,0 +1 ##
+*
\ No newline at end of property
Index: create-2.99-initctl-patch/file.list
===================================================================
--- create-2.99-initctl-patch/file.list (nonexistent)
+++ create-2.99-initctl-patch/file.list (revision 5)
@@ -0,0 +1,8 @@
+sysvinit-2.99/doc/Install
+sysvinit-2.99/doc/initctl
+sysvinit-2.99/man/init.8
+sysvinit-2.99/man/initctl.5
+sysvinit-2.99/src/Makefile
+sysvinit-2.99/src/init.c
+sysvinit-2.99/src/initreq.h
+sysvinit-2.99/src/shutdown.c
Index: create-2.99-initctl-patch/sysvinit-2.99-new/doc/Install
===================================================================
--- create-2.99-initctl-patch/sysvinit-2.99-new/doc/Install (nonexistent)
+++ create-2.99-initctl-patch/sysvinit-2.99-new/doc/Install (revision 5)
@@ -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 /dev/initctl.
+ There should be no problem using a read-only root file system
+ If you use a Linux kernel > 1.3.66. Older kernels don't allow
+ writing to a FIFO on a read-only file system.
+
Index: create-2.99-initctl-patch/sysvinit-2.99-new/doc/initctl
===================================================================
--- create-2.99-initctl-patch/sysvinit-2.99-new/doc/initctl (nonexistent)
+++ create-2.99-initctl-patch/sysvinit-2.99-new/doc/initctl (revision 5)
@@ -0,0 +1,94 @@
+This document describes the communiction pipe set up by SysV init
+at /dev/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 separate 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 /dev/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 /dev/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 /dev/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.
+
Index: create-2.99-initctl-patch/sysvinit-2.99-new/doc
===================================================================
--- create-2.99-initctl-patch/sysvinit-2.99-new/doc (nonexistent)
+++ create-2.99-initctl-patch/sysvinit-2.99-new/doc (revision 5)
Property changes on: create-2.99-initctl-patch/sysvinit-2.99-new/doc
___________________________________________________________________
Added: svn:ignore
## -0,0 +1,73 ##
+
+# install dir
+dist
+
+# Target build dirs
+.a1x-newlib
+.a2x-newlib
+.at91sam7s-newlib
+
+.build-machine
+
+.a1x-glibc
+.a2x-glibc
+.h3-glibc
+.h5-glibc
+.i586-glibc
+.i686-glibc
+.imx6-glibc
+.jz47xx-glibc
+.makefile
+.am335x-glibc
+.omap543x-glibc
+.p5600-glibc
+.power8-glibc
+.power8le-glibc
+.power9-glibc
+.power9le-glibc
+.m1000-glibc
+.riscv64-glibc
+.rk328x-glibc
+.rk33xx-glibc
+.rk339x-glibc
+.s8xx-glibc
+.s9xx-glibc
+.x86_64-glibc
+
+# Hidden files (each file)
+.makefile
+.dist
+.rootfs
+
+# src & hw requires
+.src_requires
+.src_requires_depend
+.requires
+.requires_depend
+
+# Tarballs
+*.gz
+*.bz2
+*.lz
+*.xz
+*.tgz
+*.txz
+
+# Signatures
+*.asc
+*.sig
+*.sign
+*.sha1sum
+
+# Patches
+*.patch
+
+# Descriptions
+*.dsc
+*.txt
+
+# Default linux config files
+*.defconfig
+
+# backup copies
+*~
Index: create-2.99-initctl-patch/sysvinit-2.99-new/man/init.8
===================================================================
--- create-2.99-initctl-patch/sysvinit-2.99-new/man/init.8 (nonexistent)
+++ create-2.99-initctl-patch/sysvinit-2.99-new/man/init.8 (revision 5)
@@ -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/dev/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/dev/initctl\fP, for messages.
+\fBTelinit\fP uses this to communicate with init. The interface is not
+very well documented or finished. Those interested should study the
+\fIinitreq.h\fP file in the \fIsrc/\fP subdirectory of the \fBinit\fP
+source code tar archive.
+.SH SIGNALS
+Init reacts to several signals:
+.TP 0.5i
+.B SIGHUP
+Has the same effect as \fBtelinit q\fP.
+.PP
+.TP 0.5i
+.B SIGUSR1
+On receipt of this signals, init closes and re-opens its control fifo,
+\fB/dev/initctl\fP. Useful for bootscripts when /dev is remounted.
+.TP 0.5i
+.B SIGUSR2
+When init receives SIGUSR2, init closes and leaves the control fifo,
+\fB/dev/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
+/dev/initctl
+.fi
+.\"}}}
+.\"{{{ Warnings
+.SH WARNINGS
+\fBInit\fP assumes that processes and descendants of processes
+remain in the same process group which was originally created
+for them. If the processes change their group, \fBinit\fP can't
+kill them and you may end up with two processes reading from one
+terminal line.
+.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)
+.\"}}}
Index: create-2.99-initctl-patch/sysvinit-2.99-new/man/initctl.5
===================================================================
--- create-2.99-initctl-patch/sysvinit-2.99-new/man/initctl.5 (nonexistent)
+++ create-2.99-initctl-patch/sysvinit-2.99-new/man/initctl.5 (revision 5)
@@ -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 \- /dev/initctl is a named pipe which passes commands to SysV init.
+.SH SYNOPSIS
+/dev/initctl
+.SH DESCRIPTION
+
+This document describes the communiction pipe set up by SysV init
+at /dev/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 separate 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 /dev/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 /dev/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 /dev/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 /dev/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 /dev/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 /dev/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
+/dev/initctl
+/sbin/init
+.SH AUTHOR
+Jesse Smith <jsmith@resonatingmedia.com>
+.SH "SEE ALSO"
+init(8)
Index: create-2.99-initctl-patch/sysvinit-2.99-new/man
===================================================================
--- create-2.99-initctl-patch/sysvinit-2.99-new/man (nonexistent)
+++ create-2.99-initctl-patch/sysvinit-2.99-new/man (revision 5)
Property changes on: create-2.99-initctl-patch/sysvinit-2.99-new/man
___________________________________________________________________
Added: svn:ignore
## -0,0 +1,73 ##
+
+# install dir
+dist
+
+# Target build dirs
+.a1x-newlib
+.a2x-newlib
+.at91sam7s-newlib
+
+.build-machine
+
+.a1x-glibc
+.a2x-glibc
+.h3-glibc
+.h5-glibc
+.i586-glibc
+.i686-glibc
+.imx6-glibc
+.jz47xx-glibc
+.makefile
+.am335x-glibc
+.omap543x-glibc
+.p5600-glibc
+.power8-glibc
+.power8le-glibc
+.power9-glibc
+.power9le-glibc
+.m1000-glibc
+.riscv64-glibc
+.rk328x-glibc
+.rk33xx-glibc
+.rk339x-glibc
+.s8xx-glibc
+.s9xx-glibc
+.x86_64-glibc
+
+# Hidden files (each file)
+.makefile
+.dist
+.rootfs
+
+# src & hw requires
+.src_requires
+.src_requires_depend
+.requires
+.requires_depend
+
+# Tarballs
+*.gz
+*.bz2
+*.lz
+*.xz
+*.tgz
+*.txz
+
+# Signatures
+*.asc
+*.sig
+*.sign
+*.sha1sum
+
+# Patches
+*.patch
+
+# Descriptions
+*.dsc
+*.txt
+
+# Default linux config files
+*.defconfig
+
+# backup copies
+*~
Index: create-2.99-initctl-patch/sysvinit-2.99-new/src/Makefile
===================================================================
--- create-2.99-initctl-patch/sysvinit-2.99-new/src/Makefile (nonexistent)
+++ create-2.99-initctl-patch/sysvinit-2.99-new/src/Makefile (revision 5)
@@ -0,0 +1,240 @@
+#
+# 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 $(ROOT)/usr/lib*/libcrypt.*),)
+ SULOGINLIBS += -lcrypt
+endif
+
+# Additional libs for GNU libc / multiarch on Debian based systems.
+ifneq ($(wildcard $(ROOT)/usr/lib/*/libcrypt.*),)
+ifneq ($(findstring -lcrypt, $(SULOGINLIBS)), -lcrypt)
+ SULOGINLIBS += -lcrypt
+endif
+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_DIR) $(ROOT)/etc/inittab.d
+ # $(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 /dev/initctl ]; then \
+ echo "Creating /dev/initctl"; \
+ rm -f /dev/initctl; \
+ mknod -m 600 /dev/initctl p; fi
+endif
Index: create-2.99-initctl-patch/sysvinit-2.99-new/src/init.c
===================================================================
--- create-2.99-initctl-patch/sysvinit-2.99-new/src/init.c (nonexistent)
+++ create-2.99-initctl-patch/sysvinit-2.99-new/src/init.c (revision 5)
@@ -0,0 +1,3190 @@
+/*
+ * Init A System-V Init Clone.
+ *
+ * Usage: /sbin/init
+ * init [0123456SsQqAaBbCc]
+ * telinit [0123456SsQqAaBbCc]
+ *
+ * Version: @(#)init.c 2.86 30-Jul-2004 miquels@cistron.nl
+ * Version: init.c 2.90 18-Jun-2018 jsmith@resonatingmedia.com
+ */
+
+/*
+Version information is not placed in the top-level Makefile by default
+*/
+#ifndef VERSION
+#define VERSION "2.94"
+#endif
+/*
+ * This file is part of the sysvinit suite,
+ * Copyright (C) 1991-2004 Miquel van Smoorenburg.
+ * Copyright (C) 2017-2019 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 Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <sys/wait.h>
+#ifdef __linux__
+#include <sys/kd.h>
+#endif
+#include <sys/resource.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <stdio.h>
+#include <time.h>
+#include <fcntl.h>
+#include <string.h>
+#include <signal.h>
+#include <termios.h>
+#ifdef __FreeBSD__
+#include <utmpx.h>
+#else
+#include <utmp.h>
+#endif
+#include <ctype.h>
+#include <stdarg.h>
+#include <sys/ttydefaults.h>
+#include <sys/syslog.h>
+#include <sys/time.h>
+/*
+ * inittab.d
+ */
+#include <sys/types.h>
+#include <dirent.h>
+
+#ifdef WITH_SELINUX
+# include <selinux/selinux.h>
+#endif
+#ifdef __FreeBSD__
+extern char **environ;
+#endif
+
+#ifdef __i386__
+# ifdef __GLIBC__
+ /* GNU libc 2.x */
+# define STACK_DEBUG 1
+# if (__GLIBC__ == 2 && __GLIBC_MINOR__ == 0)
+ /* Only glibc 2.0 needs this */
+# include <sigcontext.h>
+# elif ( __GLIBC__ > 2) && ((__GLIBC__ == 2) && (__GLIBC_MINOR__ >= 1))
+# include <bits/sigcontext.h>
+# endif
+# endif
+#endif
+
+#include "init.h"
+#include "initreq.h"
+#include "paths.h"
+#include "reboot.h"
+#include "runlevellog.h"
+#include "set.h"
+
+#ifndef SIGPWR
+# define SIGPWR SIGUSR2
+#endif
+
+#ifndef CBAUD
+# define CBAUD 0
+#endif
+#ifndef CBAUDEX
+# define CBAUDEX 0
+#endif
+
+/* Set a signal handler. */
+#define SETSIG(sa, sig, fun, flags) \
+ do { \
+ memset(&sa, 0, sizeof(sa)); \
+ sa.sa_handler = fun; \
+ sa.sa_flags = flags; \
+ sigemptyset(&sa.sa_mask); \
+ sigaction(sig, &sa, NULL); \
+ } while(0)
+
+/* Version information */
+char *Version = "@(#) init " VERSION " miquels@cistron.nl";
+char *bootmsg = "version " VERSION " %s";
+#define E_VERSION "INIT_VERSION=sysvinit-" VERSION
+
+CHILD *family = NULL; /* The linked list of all entries */
+CHILD *newFamily = NULL; /* The list after inittab re-read */
+
+CHILD ch_emerg = { /* Emergency shell */
+ WAITING, 0, 0, 0, 0,
+ "~~",
+ "S",
+ 3,
+ "/sbin/sulogin",
+ NULL,
+ NULL
+};
+
+char runlevel = 'S'; /* The current run level */
+char thislevel = 'S'; /* The current runlevel */
+char prevlevel = 'N'; /* Previous runlevel */
+int dfl_level = 0; /* Default runlevel */
+sig_atomic_t got_cont = 0; /* Set if we received the SIGCONT signal */
+sig_atomic_t got_signals; /* Set if we received a signal. */
+int emerg_shell = 0; /* Start emergency shell? */
+int wrote_wtmp_reboot = 1; /* Set when we wrote the reboot record */
+int wrote_utmp_reboot = 1; /* Set when we wrote the reboot record */
+int wrote_wtmp_rlevel = 1; /* Set when we wrote the runlevel record */
+int wrote_utmp_rlevel = 1; /* Set when we wrote the runlevel record */
+int sleep_time = WAIT_BETWEEN_SIGNALS; /* Sleep time between TERM and KILL */
+char *argv0; /* First arguments; show up in ps listing */
+int maxproclen; /* Maximal length of argv[0] with \0 */
+struct utmp utproto; /* Only used for sizeof(utproto.ut_id) */
+char *console_dev; /* Console device. */
+int pipe_fd = -1; /* /dev/initctl */
+int did_boot = 0; /* Did we already do BOOT* stuff? */
+int main(int, char **);
+
+/* Used by re-exec part */
+int reload = 0; /* Should we do initialization stuff? */
+char *myname=INIT; /* What should we exec */
+int oops_error; /* Used by some of the re-exec code. */
+const char *Signature = "12567362"; /* Signature for re-exec fd */
+
+/* Macro to see if this is a special action */
+#define ISPOWER(i) ((i) == POWERWAIT || (i) == POWERFAIL || \
+ (i) == POWEROKWAIT || (i) == POWERFAILNOW || \
+ (i) == CTRLALTDEL)
+
+/* ascii values for the `action' field. */
+struct actions {
+ char *name;
+ int act;
+} actions[] = {
+ { "respawn", RESPAWN },
+ { "wait", WAIT },
+ { "once", ONCE },
+ { "boot", BOOT },
+ { "bootwait", BOOTWAIT },
+ { "powerfail", POWERFAIL },
+ { "powerfailnow",POWERFAILNOW },
+ { "powerwait", POWERWAIT },
+ { "powerokwait", POWEROKWAIT },
+ { "ctrlaltdel", CTRLALTDEL },
+ { "off", OFF },
+ { "ondemand", ONDEMAND },
+ { "initdefault", INITDEFAULT },
+ { "sysinit", SYSINIT },
+ { "kbrequest", KBREQUEST },
+ { NULL, 0 },
+};
+
+/*
+ * State parser token table (see receive_state)
+ */
+struct {
+ char name[4];
+ int cmd;
+} cmds[] = {
+ { "VER", C_VER },
+ { "END", C_END },
+ { "REC", C_REC },
+ { "EOR", C_EOR },
+ { "LEV", C_LEV },
+ { "FL ", C_FLAG },
+ { "AC ", C_ACTION },
+ { "CMD", C_PROCESS },
+ { "PID", C_PID },
+ { "EXS", C_EXS },
+ { "-RL", D_RUNLEVEL },
+ { "-TL", D_THISLEVEL },
+ { "-PL", D_PREVLEVEL },
+ { "-SI", D_GOTSIGN },
+ { "-WR", D_WROTE_WTMP_REBOOT},
+ { "-WU", D_WROTE_UTMP_REBOOT},
+ { "-ST", D_SLTIME },
+ { "-DB", D_DIDBOOT },
+ { "-LW", D_WROTE_WTMP_RLEVEL},
+ { "-LU", D_WROTE_UTMP_RLEVEL},
+ { "", 0 }
+};
+struct {
+ char *name;
+ int mask;
+} flags[]={
+ {"RU",RUNNING},
+ {"DE",DEMAND},
+ {"XD",XECUTED},
+ {"WT",WAITING},
+ {NULL,0}
+};
+
+#define NR_EXTRA_ENV 16
+char *extra_env[NR_EXTRA_ENV];
+
+#define MINI_SLEEP 10 /* ten milliseconds */
+#define SHORT_SLEEP 5000 /* five seconds */
+#define LONG_SLEEP 30000 /* 30 seconds sleep to deal with memory issues*/
+
+/*
+ * Sleep a number of milliseconds.
+ *
+ * This only works correctly because Linux select updates
+ * the elapsed time in the struct timeval passed to select!
+ */
+static
+void do_msleep(int msec)
+{
+ struct timeval tv;
+
+ tv.tv_sec = msec / 1000;
+ tv.tv_usec = (msec % 1000) * 1000;
+
+ while(select(0, NULL, NULL, NULL, &tv) < 0 && errno == EINTR)
+ ;
+}
+
+
+/*
+ * Non-failing allocation routines (init cannot fail).
+ */
+static
+void *imalloc(size_t size)
+{
+ void *m;
+
+ while ((m = malloc(size)) == NULL) {
+ initlog(L_VB, "out of memory");
+ do_msleep(SHORT_SLEEP);
+ }
+ memset(m, 0, size);
+ return m;
+}
+
+static
+char *istrdup(const char *s)
+{
+ char *m;
+ int l;
+
+ l = strlen(s) + 1;
+ m = imalloc(l);
+ memcpy(m, s, l);
+ return m;
+}
+
+
+/*
+ * Send the state info of the previous running init to
+ * the new one, in a version-independant way.
+ */
+static
+void send_state(int fd)
+{
+ FILE *fp;
+ CHILD *p;
+ int i,val;
+
+ fp = fdopen(fd,"w");
+
+ fprintf(fp, "VER%s\n", Version);
+ fprintf(fp, "-RL%c\n", runlevel);
+ fprintf(fp, "-TL%c\n", thislevel);
+ fprintf(fp, "-PL%c\n", prevlevel);
+ fprintf(fp, "-SI%u\n", got_signals);
+ fprintf(fp, "-WR%d\n", wrote_wtmp_reboot);
+ fprintf(fp, "-WU%d\n", wrote_utmp_reboot);
+ fprintf(fp, "-ST%d\n", sleep_time);
+ fprintf(fp, "-DB%d\n", did_boot);
+
+ for (p = family; p; p = p->next) {
+ fprintf(fp, "REC%s\n", p->id);
+ fprintf(fp, "LEV%s\n", p->rlevel);
+ for (i = 0, val = p->flags; flags[i].mask; i++)
+ if (val & flags[i].mask) {
+ val &= ~flags[i].mask;
+ fprintf(fp, "FL %s\n",flags[i].name);
+ }
+ fprintf(fp, "PID%d\n",p->pid);
+ fprintf(fp, "EXS%u\n",p->exstat);
+ for(i = 0; actions[i].act; i++)
+ if (actions[i].act == p->action) {
+ fprintf(fp, "AC %s\n", actions[i].name);
+ break;
+ }
+ fprintf(fp, "CMD%s\n", p->process);
+ fprintf(fp, "EOR\n");
+ }
+ fprintf(fp, "END\n");
+ fclose(fp);
+}
+
+/*
+ * Read a string from a file descriptor.
+ * Q: why not use fgets() ?
+ * A: Answer: looked into this. Turns out after all the checks
+ * required in the fgets() approach (removing newline, read errors, etc)
+ * the function is longer and takes approximately the same amount of
+ * time to do one big fgets and checks as it does to do a pile of getcs.
+ * We don't benefit from switching.
+ * - Jesse
+ */
+static int get_string(char *p, int size, FILE *f)
+{
+ int c;
+
+ while ((c = getc(f)) != EOF && c != '\n') {
+ if (--size > 0)
+ *p++ = c;
+ }
+ *p = '\0';
+ return (c != EOF) && (size > 0);
+}
+
+/*
+ * Read trailing data from the state pipe until we see a newline.
+ */
+static int get_void(FILE *f)
+{
+ int c;
+
+ while ((c = getc(f)) != EOF && c != '\n')
+ ;
+
+ return (c != EOF);
+}
+
+/*
+ * Read the next "command" from the state pipe.
+ */
+static int get_cmd(FILE *f)
+{
+ char cmd[4] = " ";
+ int i;
+
+ if (fread(cmd, 1, sizeof(cmd) - 1, f) != sizeof(cmd) - 1)
+ return C_EOF;
+
+ for(i = 0; cmds[i].cmd && strcmp(cmds[i].name, cmd) != 0; i++)
+ ;
+ return cmds[i].cmd;
+}
+
+/*
+ * Read a CHILD * from the state pipe.
+ */
+static CHILD *get_record(FILE *f)
+{
+ int cmd;
+ char s[32];
+ int i;
+ CHILD *p;
+
+ do {
+ errno = 0;
+ switch (cmd = get_cmd(f)) {
+ case C_END:
+ get_void(f);
+ return NULL;
+ case 0:
+ get_void(f);
+ break;
+ case C_REC:
+ break;
+ case D_RUNLEVEL:
+ if (fscanf(f, "%c\n", &runlevel) == EOF && errno != 0) {
+ fprintf(stderr, "%s (%d): %s\n", __FILE__, __LINE__, strerror(errno));
+ }
+ break;
+ case D_THISLEVEL:
+ if (fscanf(f, "%c\n", &thislevel) == EOF && errno != 0) {
+ fprintf(stderr, "%s (%d): %s\n", __FILE__, __LINE__, strerror(errno));
+ }
+ break;
+ case D_PREVLEVEL:
+ if (fscanf(f, "%c\n", &prevlevel) == EOF && errno != 0) {
+ fprintf(stderr, "%s (%d): %s\n", __FILE__, __LINE__, strerror(errno));
+ }
+ break;
+ case D_GOTSIGN:
+ if (fscanf(f, "%u\n", &got_signals) == EOF && errno != 0) {
+ fprintf(stderr, "%s (%d): %s\n", __FILE__, __LINE__, strerror(errno));
+ }
+ break;
+ case D_WROTE_WTMP_REBOOT:
+ if (fscanf(f, "%d\n", &wrote_wtmp_reboot) == EOF && errno != 0) {
+ fprintf(stderr, "%s (%d): %s\n", __FILE__, __LINE__, strerror(errno));
+ }
+ break;
+ case D_WROTE_UTMP_REBOOT:
+ if (fscanf(f, "%d\n", &wrote_utmp_reboot) == EOF && errno != 0) {
+ fprintf(stderr, "%s (%d): %s\n", __FILE__, __LINE__, strerror(errno));
+ }
+ break;
+ case D_SLTIME:
+ if (fscanf(f, "%d\n", &sleep_time) == EOF && errno != 0) {
+ fprintf(stderr, "%s (%d): %s\n", __FILE__, __LINE__, strerror(errno));
+ }
+ break;
+ case D_DIDBOOT:
+ if (fscanf(f, "%d\n", &did_boot) == EOF && errno != 0) {
+ fprintf(stderr, "%s (%d): %s\n", __FILE__, __LINE__, strerror(errno));
+ }
+ break;
+ case D_WROTE_WTMP_RLEVEL:
+ if (fscanf(f, "%d\n", &wrote_wtmp_rlevel) == EOF && errno != 0) {
+ fprintf(stderr, "%s (%d): %s\n", __FILE__, __LINE__, strerror(errno));
+ }
+ break;
+ case D_WROTE_UTMP_RLEVEL:
+ if (fscanf(f, "%d\n", &wrote_utmp_rlevel) == EOF && errno != 0) {
+ fprintf(stderr, "%s (%d): %s\n", __FILE__, __LINE__, strerror(errno));
+ }
+ break;
+ default:
+ if (cmd > 0 || cmd == C_EOF) {
+ oops_error = -1;
+ return NULL;
+ }
+ }
+ } while (cmd != C_REC);
+
+ p = imalloc(sizeof(CHILD));
+ get_string(p->id, sizeof(p->id), f);
+
+ do switch(cmd = get_cmd(f)) {
+ case 0:
+ case C_EOR:
+ get_void(f);
+ break;
+ case C_PID:
+ if (fscanf(f, "%d\n", &(p->pid)) == EOF && errno != 0) {
+ fprintf(stderr, "%s (%d): %s\n", __FILE__, __LINE__, strerror(errno));
+ }
+ break;
+ case C_EXS:
+ if (fscanf(f, "%u\n", &(p->exstat)) == EOF && errno != 0) {
+ fprintf(stderr, "%s (%d): %s\n", __FILE__, __LINE__, strerror(errno));
+ }
+ break;
+ case C_LEV:
+ get_string(p->rlevel, sizeof(p->rlevel), f);
+ break;
+ case C_PROCESS:
+ get_string(p->process, sizeof(p->process), f);
+ break;
+ case C_FLAG:
+ get_string(s, sizeof(s), f);
+ for(i = 0; flags[i].name; i++) {
+ if (strcmp(flags[i].name,s) == 0)
+ break;
+ }
+ p->flags |= flags[i].mask;
+ break;
+ case C_ACTION:
+ get_string(s, sizeof(s), f);
+ for(i = 0; actions[i].name; i++) {
+ if (strcmp(actions[i].name, s) == 0)
+ break;
+ }
+ p->action = actions[i].act ? actions[i].act : OFF;
+ break;
+ default:
+ free(p);
+ oops_error = -1;
+ return NULL;
+ } while( cmd != C_EOR);
+
+ return p;
+}
+
+/*
+ * Read the complete state info from the state pipe.
+ * Returns 0 on success
+ */
+static
+int receive_state(int fd)
+{
+ FILE *f;
+ char old_version[256];
+ CHILD **pp;
+
+ f = fdopen(fd, "r");
+
+ if (get_cmd(f) != C_VER) {
+ fclose(f);
+ return -1;
+ }
+ get_string(old_version, sizeof(old_version), f);
+ oops_error = 0;
+ for (pp = &family; (*pp = get_record(f)) != NULL; pp = &((*pp)->next))
+ ;
+ fclose(f);
+ return oops_error;
+}
+
+/*
+ * Set the process title.
+ */
+#ifdef __GNUC__
+#ifndef __FreeBSD__
+__attribute__ ((format (printf, 1, 2)))
+#endif
+#endif
+/* This function already exists on FreeBSD. No need to declare it. */
+#ifndef __FreeBSD__
+static int setproctitle(char *fmt, ...)
+{
+ va_list ap;
+ int len;
+ char buf[256];
+
+ buf[0] = 0;
+
+ va_start(ap, fmt);
+ len = vsnprintf(buf, sizeof(buf), fmt, ap);
+ va_end(ap);
+
+ if (maxproclen > 1) {
+ memset(argv0, 0, maxproclen);
+ strncpy(argv0, buf, maxproclen - 1);
+ }
+
+ return len;
+}
+#endif
+
+/*
+ * Set console_dev to a working console.
+ */
+static
+void console_init(void)
+{
+ int fd;
+ int tried_devcons = 0;
+ int tried_vtmaster = 0;
+ char *s;
+
+ if ((s = getenv("CONSOLE")) != NULL)
+ console_dev = s;
+ else {
+ console_dev = CONSOLE;
+ tried_devcons++;
+ }
+
+ while ((fd = open(console_dev, O_RDONLY|O_NONBLOCK)) < 0) {
+ if (!tried_devcons) {
+ tried_devcons++;
+ console_dev = CONSOLE;
+ continue;
+ }
+ if (!tried_vtmaster) {
+ tried_vtmaster++;
+ console_dev = VT_MASTER;
+ continue;
+ }
+ break;
+ }
+ if (fd < 0)
+ console_dev = "/dev/null";
+ else
+ close(fd);
+}
+
+
+/*
+ * Open the console with retries.
+ */
+static
+int console_open(int mode)
+{
+ int f, fd = -1;
+ int m;
+
+ /*
+ * Open device in nonblocking mode.
+ */
+ m = mode | O_NONBLOCK;
+
+ /*
+ * Retry the open five times.
+ */
+ for(f = 0; f < 5; f++) {
+ if ((fd = open(console_dev, m)) >= 0) break;
+ usleep(10000);
+ }
+
+ if (fd < 0) return fd;
+
+ /*
+ * Set original flags.
+ */
+ if (m != mode)
+ fcntl(fd, F_SETFL, mode);
+ return fd;
+}
+
+/*
+ * We got a signal (HUP PWR WINCH ALRM INT)
+ */
+static
+void signal_handler(int sig)
+{
+ ADDSET(got_signals, sig);
+}
+
+/*
+ * SIGCHLD: one of our children has died.
+ */
+static
+# ifdef __GNUC__
+void chld_handler(int sig __attribute__((unused)))
+# else
+void chld_handler(int sig)
+# endif
+{
+ CHILD *ch;
+ int pid, st;
+ int saved_errno = errno;
+
+ /*
+ * Find out which process(es) this was (were)
+ */
+ while((pid = waitpid(-1, &st, WNOHANG)) != 0) {
+ if (errno == ECHILD) break;
+ for( ch = family; ch; ch = ch->next )
+ if ( ch->pid == pid && (ch->flags & RUNNING) ) {
+ INITDBG(L_VB,
+ "chld_handler: marked %d as zombie",
+ ch->pid);
+ ADDSET(got_signals, SIGCHLD);
+ ch->exstat = st;
+ ch->flags |= ZOMBIE;
+ if (ch->new) {
+ ch->new->exstat = st;
+ ch->new->flags |= ZOMBIE;
+ }
+ break;
+ }
+ if (ch == NULL) {
+ INITDBG(L_VB, "chld_handler: unknown child %d exited.",
+ pid);
+ }
+ }
+ errno = saved_errno;
+}
+
+/*
+ * Linux ignores all signals sent to init when the
+ * SIG_DFL handler is installed. Therefore we must catch SIGTSTP
+ * and SIGCONT, or else they won't work....
+ *
+ * The SIGCONT handler
+ */
+static
+# ifdef __GNUC__
+void cont_handler(int sig __attribute__((unused)))
+# else
+void cont_handler(int sig)
+# endif
+{
+ got_cont = 1;
+}
+
+/*
+ * Fork and dump core in /.
+ */
+static
+void coredump(void)
+{
+ static int dumped = 0;
+ struct rlimit rlim;
+ sigset_t mask;
+
+ if (dumped) return;
+ dumped = 1;
+
+ if (fork() != 0) return;
+
+ sigfillset(&mask);
+ sigprocmask(SIG_SETMASK, &mask, NULL);
+
+ rlim.rlim_cur = RLIM_INFINITY;
+ rlim.rlim_max = RLIM_INFINITY;
+ setrlimit(RLIMIT_CORE, &rlim);
+ if (0 != chdir("/"))
+ initlog(L_VB, "unable to chdir to /: %s",
+ strerror(errno));
+
+ signal(SIGSEGV, SIG_DFL);
+ raise(SIGSEGV);
+ sigdelset(&mask, SIGSEGV);
+ sigprocmask(SIG_SETMASK, &mask, NULL);
+
+ do_msleep(SHORT_SLEEP);
+ exit(0);
+}
+
+/*
+ * OOPS: segmentation violation!
+ * If we have the info, print where it occurred.
+ * Then sleep 30 seconds and try to continue.
+ */
+static
+#if defined(STACK_DEBUG) && defined(__linux__)
+# ifdef __GNUC__
+void segv_handler(int sig __attribute__((unused)), struct sigcontext ctx)
+# else
+void segv_handler(int sig, struct sigcontext ctx)
+# endif
+{
+ char *p = "";
+ int saved_errno = errno;
+
+ if ((void *)ctx.eip >= (void *)do_msleep &&
+ (void *)ctx.eip < (void *)main)
+ p = " (code)";
+ initlog(L_VB, "PANIC: segmentation violation at %p%s! "
+ "sleeping for 30 seconds.", (void *)ctx.eip, p);
+ coredump();
+ do_msleep(LONG_SLEEP);
+ errno = saved_errno;
+}
+#else
+# ifdef __GNUC__
+void segv_handler(int sig __attribute__((unused)))
+# else
+void segv_handler(int sig)
+# endif
+{
+ int saved_errno = errno;
+
+ initlog(L_VB,
+ "PANIC: segmentation violation! sleeping for 30 seconds.");
+ coredump();
+ do_msleep(LONG_SLEEP);
+ errno = saved_errno;
+}
+#endif
+
+/*
+ * The SIGSTOP & SIGTSTP handler
+ */
+static
+# ifdef __GNUC__
+void stop_handler(int sig __attribute__((unused)))
+# else
+void stop_handler(int sig)
+# endif
+{
+ int saved_errno = errno;
+
+ got_cont = 0;
+ while(!got_cont) pause();
+ got_cont = 0;
+ errno = saved_errno;
+}
+
+/*
+ * Set terminal settings to reasonable defaults
+ */
+static
+void console_stty(void)
+{
+ struct termios tty;
+ int fd;
+
+ if ((fd = console_open(O_RDWR|O_NOCTTY)) < 0) {
+ initlog(L_VB, "can't open %s", console_dev);
+ return;
+ }
+
+#ifdef __FreeBSD_kernel__
+ /*
+ * The kernel of FreeBSD expects userland to set TERM. Usually, we want
+ * "xterm". Later, gettys might disagree on this (i.e. we're not using
+ * syscons) but some boot scripts, like /etc/init.d/xserver-xorg, still
+ * need a non-dumb terminal.
+ */
+ putenv ("TERM=xterm");
+#endif
+
+ (void) tcgetattr(fd, &tty);
+
+ tty.c_cflag &= CBAUD|CBAUDEX|CSIZE|CSTOPB|PARENB|PARODD;
+ tty.c_cflag |= HUPCL|CLOCAL|CREAD;
+
+ tty.c_cc[VINTR] = CINTR;
+ tty.c_cc[VQUIT] = CQUIT;
+ tty.c_cc[VERASE] = CERASE; /* ASCII DEL (0177) */
+ tty.c_cc[VKILL] = CKILL;
+ tty.c_cc[VEOF] = CEOF;
+ tty.c_cc[VTIME] = 0;
+ tty.c_cc[VMIN] = 1;
+#ifdef VSWTC /* not defined on FreeBSD */
+ tty.c_cc[VSWTC] = _POSIX_VDISABLE;
+#endif /* VSWTC */
+ tty.c_cc[VSTART] = CSTART;
+ tty.c_cc[VSTOP] = CSTOP;
+ tty.c_cc[VSUSP] = CSUSP;
+ tty.c_cc[VEOL] = _POSIX_VDISABLE;
+ tty.c_cc[VREPRINT] = CREPRINT;
+ tty.c_cc[VDISCARD] = CDISCARD;
+ tty.c_cc[VWERASE] = CWERASE;
+ tty.c_cc[VLNEXT] = CLNEXT;
+ tty.c_cc[VEOL2] = _POSIX_VDISABLE;
+
+ /*
+ * Set pre and post processing
+ */
+ tty.c_iflag = IGNPAR|ICRNL|IXON|IXANY
+#ifdef IUTF8 /* Not defined on FreeBSD */
+ | (tty.c_iflag & IUTF8)
+#endif /* IUTF8 */
+ ;
+ tty.c_oflag = OPOST|ONLCR;
+ tty.c_lflag = ISIG|ICANON|ECHO|ECHOCTL|ECHOE|ECHOKE;
+
+#if defined(SANE_TIO) && (SANE_TIO == 1)
+ /*
+ * Disable flow control (-ixon), ignore break (ignbrk),
+ * and make nl/cr more usable (sane).
+ */
+ tty.c_iflag |= IGNBRK;
+ tty.c_iflag &= ~(BRKINT|INLCR|IGNCR|IXON);
+ tty.c_oflag &= ~(OCRNL|ONLRET);
+#endif
+ /*
+ * Now set the terminal line.
+ * We don't care about non-transmitted output data
+ * and non-read input data.
+ */
+ (void) tcsetattr(fd, TCSANOW, &tty);
+ (void) tcflush(fd, TCIOFLUSH);
+ (void) close(fd);
+}
+
+static ssize_t
+safe_write(int fd, const char *buffer, size_t count)
+{
+ ssize_t offset = 0;
+
+ while (count > 0) {
+ ssize_t block = write(fd, &buffer[offset], count);
+
+ if (block < 0 && errno == EINTR)
+ continue;
+ if (block <= 0)
+ return offset ? offset : block;
+ offset += block;
+ count -= block;
+ }
+ return offset;
+}
+
+/*
+ * Print to the system console
+ */
+void print(char *s)
+{
+ int fd;
+
+ if ((fd = console_open(O_WRONLY|O_NOCTTY|O_NDELAY)) >= 0) {
+ safe_write(fd, s, strlen(s));
+ close(fd);
+ }
+}
+
+/*
+ * Log something to a logfile and the console.
+ */
+#ifdef __GNUC__
+__attribute__ ((format (printf, 2, 3)))
+#endif
+void initlog(int loglevel, char *s, ...)
+{
+ va_list va_alist;
+ char buf[256];
+ sigset_t nmask, omask;
+
+ va_start(va_alist, s);
+ vsnprintf(buf, sizeof(buf), s, va_alist);
+ va_end(va_alist);
+
+ if (loglevel & L_SY) {
+ /*
+ * Re-establish connection with syslogd every time.
+ * Block signals while talking to syslog.
+ */
+ sigfillset(&nmask);
+ sigprocmask(SIG_BLOCK, &nmask, &omask);
+ openlog("init", 0, LOG_DAEMON);
+ syslog(LOG_INFO, "%s", buf);
+ closelog();
+ sigprocmask(SIG_SETMASK, &omask, NULL);
+ }
+
+ /*
+ * And log to the console.
+ */
+ if (loglevel & L_CO) {
+ print("\rINIT: ");
+ print(buf);
+ print("\r\n");
+ }
+}
+
+/*
+ * Add or replace specific environment value
+ */
+int addnewenv(const char *new, char **curr, int n)
+{
+ size_t nlen = strcspn(new, "=");
+ int i;
+ for (i = 0; i < n; i++) {
+ if (nlen != strcspn(curr[i], "="))
+ continue;
+ if (strncmp (new, curr[i], nlen) == 0)
+ break;
+ }
+ if (i >= n)
+ curr[n++] = istrdup(new);
+ else {
+ free(curr[i]);
+ curr[i] = istrdup(new);
+ }
+ return n;
+}
+
+/*
+ * Build a new environment for execve().
+ */
+char **init_buildenv(int child)
+{
+ char i_lvl[] = "RUNLEVEL=x";
+ char i_prev[] = "PREVLEVEL=x";
+ char i_cons[128];
+ char i_shell[] = "SHELL=" SHELL;
+ char **e;
+ int n, i;
+
+ for (n = 0; environ[n]; n++)
+ ;
+ n += NR_EXTRA_ENV + 1; /* Also room for last NULL */
+ if (child)
+ n += 8;
+
+ while ((e = (char**)calloc(n, sizeof(char *))) == NULL) {
+ initlog(L_VB, "out of memory");
+ do_msleep(SHORT_SLEEP);
+ }
+
+ for (n = 0; environ[n]; n++)
+ e[n] = istrdup(environ[n]);
+
+ for (i = 0; i < NR_EXTRA_ENV; i++) {
+ if (extra_env[i] == NULL || *extra_env[i] == '\0')
+ continue;
+ n = addnewenv(extra_env[i], e, n);
+ }
+
+ if (child) {
+ snprintf(i_cons, sizeof(i_cons), "CONSOLE=%s", console_dev);
+ i_lvl[9] = thislevel;
+ i_prev[10] = prevlevel;
+ n = addnewenv(i_shell, e, n);
+ n = addnewenv(i_lvl, e, n);
+ n = addnewenv(i_prev, e, n);
+ n = addnewenv(i_cons, e, n);
+ n = addnewenv(E_VERSION, e, n);
+ }
+
+ e[n++] = NULL;
+
+ return e;
+}
+
+
+void init_freeenv(char **e)
+{
+ int n;
+
+ for (n = 0; e[n]; n++)
+ free(e[n]);
+ free(e);
+}
+
+
+/*
+ * Fork and execute.
+ *
+ * This function is too long and indents too deep.
+ *
+ */
+static
+pid_t spawn(CHILD *ch, int *res)
+{
+ char *args[16]; /* Argv array */
+ char buf[136]; /* Line buffer */
+ int f, st; /* Scratch variables */
+ char *ptr; /* Ditto */
+ time_t t; /* System time */
+ int oldAlarm; /* Previous alarm value */
+ char *proc = ch->process; /* Command line */
+ pid_t pid, pgrp; /* child, console process group. */
+ sigset_t nmask, omask; /* For blocking SIGCHLD */
+ struct sigaction sa;
+
+ *res = -1;
+ buf[sizeof(buf) - 1] = 0;
+
+ /* Skip '+' if it's there */
+ if (proc[0] == '+') proc++;
+
+ ch->flags |= XECUTED;
+
+ if (ch->action == RESPAWN || ch->action == ONDEMAND) {
+ /* Is the date stamp from less than 2 minutes ago? */
+ time(&t);
+ if (ch->tm + TESTTIME > t) {
+ ch->count++;
+ } else {
+ ch->count = 0;
+ ch->tm = t;
+ }
+
+ /* Do we try to respawn too fast? */
+ if (ch->count >= MAXSPAWN) {
+
+ initlog(L_VB,
+ "Id \"%s\" respawning too fast: disabled for %d minutes",
+ ch->id, SLEEPTIME / 60);
+ ch->flags &= ~RUNNING;
+ ch->flags |= FAILING;
+
+ /* Remember the time we stopped */
+ ch->tm = t;
+
+ /* Try again in 5 minutes */
+ oldAlarm = alarm(0);
+ if (oldAlarm > SLEEPTIME || oldAlarm <= 0) oldAlarm = SLEEPTIME;
+ alarm(oldAlarm);
+ return(-1);
+ }
+ }
+
+ /* See if there is an "initscript" (except in single user mode). */
+ if (access(INITSCRIPT, R_OK) == 0 && runlevel != 'S') {
+ /* Build command line using "initscript" */
+ args[1] = SHELL;
+ args[2] = INITSCRIPT;
+ args[3] = ch->id;
+ args[4] = ch->rlevel;
+ args[5] = "unknown";
+ for(f = 0; actions[f].name; f++) {
+ if (ch->action == actions[f].act) {
+ args[5] = actions[f].name;
+ break;
+ }
+ }
+ args[6] = proc;
+ args[7] = NULL;
+ } else if (strpbrk(proc, "~`!$^&*()=|\\{}[];\"'<>?")) {
+ /* See if we need to fire off a shell for this command */
+ /* Give command line to shell */
+ args[1] = SHELL;
+ args[2] = "-c";
+ strcpy(buf, "exec ");
+ strncat(buf, proc, sizeof(buf) - strlen(buf) - 1);
+ args[3] = buf;
+ args[4] = NULL;
+ } else {
+ /* Split up command line arguments */
+ buf[0] = 0;
+ strncat(buf, proc, sizeof(buf) - 1);
+ ptr = buf;
+ for(f = 1; f < 15; f++) {
+ /* Skip white space */
+ while(*ptr == ' ' || *ptr == '\t') ptr++;
+ args[f] = ptr;
+
+ /* May be trailing space.. */
+ if (*ptr == 0) break;
+
+ /* Skip this `word' */
+ while(*ptr && *ptr != ' ' && *ptr != '\t' && *ptr != '#')
+ ptr++;
+
+ /* If end-of-line, break */
+ if (*ptr == '#' || *ptr == 0) {
+ f++;
+ *ptr = 0;
+ break;
+ }
+ /* End word with \0 and continue */
+ *ptr++ = 0;
+ }
+ args[f] = NULL;
+ }
+ args[0] = args[1];
+ while(1) {
+ /*
+ * Block sigchild while forking.
+ */
+ sigemptyset(&nmask);
+ sigaddset(&nmask, SIGCHLD);
+ sigprocmask(SIG_BLOCK, &nmask, &omask);
+
+ if ((pid = fork()) == 0) {
+
+ close(0);
+ close(1);
+ close(2);
+ if (pipe_fd >= 0)
+ {
+ close(pipe_fd);
+ pipe_fd = -1;
+ }
+
+ sigprocmask(SIG_SETMASK, &omask, NULL);
+
+ /*
+ * In sysinit, boot, bootwait or single user mode:
+ * for any wait-type subprocess we _force_ the console
+ * to be its controlling tty.
+ */
+ if (strchr("*#sS", runlevel) && ch->flags & WAITING) {
+ int ftty; /* Handler for tty controlling */
+ /*
+ * We fork once extra. This is so that we can
+ * wait and change the process group and session
+ * of the console after exit of the leader.
+ */
+ setsid();
+ if ((ftty = console_open(O_RDWR|O_NOCTTY)) >= 0) {
+ /* Take over controlling tty by force */
+ (void)ioctl(ftty, TIOCSCTTY, 1);
+
+ if(dup(ftty) < 0){
+ initlog(L_VB, "cannot duplicate console fd");
+ }
+
+ if(dup(ftty) < 0){
+ initlog(L_VB, "cannot duplicate console fd");
+ }
+
+ }
+
+ /*
+ * 4 Sep 2001, Andrea Arcangeli:
+ * Fix a race in spawn() that is used to deadlock init in a
+ * waitpid() loop: must set the childhandler as default before forking
+ * off the child or the chld_handler could run before the waitpid loop
+ * has a chance to find its zombie-child.
+ */
+ SETSIG(sa, SIGCHLD, SIG_DFL, SA_RESTART);
+ if ((pid = fork()) < 0) {
+ initlog(L_VB, "cannot fork: %s",
+ strerror(errno));
+ exit(1);
+ }
+ if (pid > 0) {
+ pid_t rc;
+ /*
+ * Ignore keyboard signals etc.
+ * Then wait for child to exit.
+ */
+ SETSIG(sa, SIGINT, SIG_IGN, SA_RESTART);
+ SETSIG(sa, SIGTSTP, SIG_IGN, SA_RESTART);
+ SETSIG(sa, SIGQUIT, SIG_IGN, SA_RESTART);
+
+ while ((rc = waitpid(pid, &st, 0)) != pid)
+ if (rc < 0 && errno == ECHILD)
+ break;
+
+ /*
+ * Small optimization. See if stealing
+ * controlling tty back is needed.
+ */
+ pgrp = tcgetpgrp(ftty);
+ if (pgrp != getpid())
+ exit(0);
+
+ /*
+ * Steal controlling tty away. We do
+ * this with a temporary process.
+ */
+ if ((pid = fork()) < 0) {
+ initlog(L_VB, "cannot fork: %s",
+ strerror(errno));
+ exit(1);
+ }
+ if (pid == 0) {
+ setsid();
+ (void)ioctl(ftty, TIOCSCTTY, 1);
+ exit(0);
+ }
+ while((rc = waitpid(pid, &st, 0)) != pid)
+ if (rc < 0 && errno == ECHILD)
+ break;
+ exit(0);
+ }
+
+ /* Set ioctl settings to default ones */
+ console_stty();
+
+ } else { /* parent */
+ int fd;
+ setsid();
+ if ((fd = console_open(O_RDWR|O_NOCTTY)) < 0) {
+ initlog(L_VB, "open(%s): %s", console_dev,
+ strerror(errno));
+ fd = open("/dev/null", O_RDWR);
+ }
+
+ if(dup(fd) < 0) {
+ initlog(L_VB, "cannot duplicate /dev/null fd");
+ }
+
+ if(dup(fd) < 0) {
+ initlog(L_VB, "cannot duplicate /dev/null fd");
+ }
+
+ }
+
+ /*
+ * Update utmp/wtmp file prior to starting
+ * any child. This MUST be done right here in
+ * the child process in order to prevent a race
+ * condition that occurs when the child
+ * process' time slice executes before the
+ * parent (can and does happen in a uniprocessor
+ * environment). If the child is a getty and
+ * the race condition happens, then init's utmp
+ * update will happen AFTER the getty runs
+ * and expects utmp to be updated already!
+ *
+ * Do NOT log if process field starts with '+'
+ * This is for compatibility with *very*
+ * old getties - probably it can be taken out.
+ */
+ if (ch->process[0] != '+')
+ write_utmp_wtmp("", ch->id, getpid(), INIT_PROCESS, "");
+
+ /* Reset all the signals, set up environment */
+ for(f = 1; f < NSIG; f++) SETSIG(sa, f, SIG_DFL, SA_RESTART);
+ environ = init_buildenv(1);
+
+ /*
+ * Execute prog. In case of ENOEXEC try again
+ * as a shell script.
+ */
+ execvp(args[1], args + 1);
+ if (errno == ENOEXEC) {
+ args[1] = SHELL;
+ args[2] = "-c";
+ strcpy(buf, "exec ");
+ strncat(buf, proc, sizeof(buf) - strlen(buf) - 1);
+ args[3] = buf;
+ args[4] = NULL;
+ execvp(args[1], args + 1);
+ }
+ initlog(L_VB, "cannot execute \"%s\"", args[1]);
+
+ if (ch->process[0] != '+')
+ write_utmp_wtmp("", ch->id, getpid(), DEAD_PROCESS, NULL);
+ exit(1);
+ }
+ *res = pid;
+ sigprocmask(SIG_SETMASK, &omask, NULL);
+
+ INITDBG(L_VB, "Started id %s (pid %d)", ch->id, pid);
+
+ if (pid == -1) {
+ initlog(L_VB, "cannot fork, retry..");
+ do_msleep(SHORT_SLEEP);
+ continue;
+ }
+ return(pid);
+ }
+}
+
+/*
+ * Start a child running!
+ */
+static
+void startup(CHILD *ch)
+{
+ /*
+ * See if it's disabled
+ */
+ if (ch->flags & FAILING) return;
+
+ switch(ch->action) {
+
+ case SYSINIT:
+ case BOOTWAIT:
+ case WAIT:
+ case POWERWAIT:
+ case POWERFAILNOW:
+ case POWEROKWAIT:
+ case CTRLALTDEL:
+ if (!(ch->flags & XECUTED)) ch->flags |= WAITING;
+ /* Fall through */
+ case KBREQUEST:
+ case BOOT:
+ case POWERFAIL:
+ case ONCE:
+ if (ch->flags & XECUTED) break;
+ /* Fall through */
+ case ONDEMAND:
+ case RESPAWN:
+ ch->flags |= RUNNING;
+ (void)spawn(ch, &(ch->pid));
+ break;
+ }
+}
+
+#ifdef __linux__
+static
+void check_kernel_console()
+{
+ FILE* fp;
+ char buf[4096];
+ if ((fp = fopen("/proc/cmdline", "r")) == 0) {
+ return;
+ }
+ if (fgets(buf, sizeof(buf), fp)) {
+ char* p = buf;
+ if ( strstr(p, "init.autocon=1") )
+ {
+ while ((p = strstr(p, "console="))) {
+ char* e;
+ p += strlen("console=");
+ for (e = p; *e; ++e) {
+ switch (*e) {
+ case '-' ... '9':
+ case 'A' ... 'Z':
+ case '_':
+ case 'a' ... 'z':
+ continue;
+ }
+ break;
+ }
+ if (p != e) {
+ CHILD* old;
+ int dup = 0;
+ char id[8] = {0};
+ char dev[32] = {0};
+ strncpy(dev, p, MIN(sizeof(dev), (unsigned)(e-p)));
+ if (!strncmp(dev, "tty", 3))
+ strncpy(id, dev+3, sizeof(id));
+ else
+ strncpy(id, dev, sizeof(id));
+
+ for(old = newFamily; old; old = old->next) {
+ if (!strcmp(old->id, id)) {
+ dup = 1;
+ }
+ }
+ if (!dup) {
+ CHILD* ch = imalloc(sizeof(CHILD));
+ ch->action = RESPAWN;
+ strcpy(ch->id, id);
+ strcpy(ch->rlevel, "2345");
+ sprintf(ch->process, "/sbin/agetty -L -s 115200,38400,9600 %s vt102", dev);
+ ch->next = NULL;
+ for(old = family; old; old = old->next) {
+ if (strcmp(old->id, ch->id) == 0) {
+ old->new = ch;
+ break;
+ }
+ }
+ /* add to end */
+ for(old = newFamily; old; old = old->next) {
+ if (!old->next) {
+ old->next = ch;
+ break;
+ }
+ }
+
+ initlog(L_VB, "added agetty on %s with id %s", dev, id);
+ }
+ }
+ }
+ }
+ }
+ fclose(fp);
+ return;
+}
+#endif
+
+/*
+ * Read the inittab file.
+ */
+static
+void read_inittab(void)
+{
+ FILE *fp; /* The INITTAB file */
+ FILE *fp_tab; /* The INITTABD files */
+ CHILD *ch, *old, *i; /* Pointers to CHILD structure */
+ CHILD *head = NULL; /* Head of linked list */
+#ifdef INITLVL
+ struct stat st; /* To stat INITLVL */
+#endif
+ sigset_t nmask, omask; /* For blocking SIGCHLD. */
+ char buf[256]; /* Line buffer */
+ char err[64]; /* Error message. */
+ char *id, *rlevel,
+ *action, *process; /* Fields of a line */
+ char *p;
+ int lineNo = 0; /* Line number in INITTAB file */
+ int actionNo; /* Decoded action field */
+ int f; /* Counter */
+ int round; /* round 0 for SIGTERM, 1 for SIGKILL */
+ int foundOne = 0; /* No killing no sleep */
+ int talk; /* Talk to the user */
+ int done = -1; /* Ready yet? , 2 level : -1 nothing done, 0 inittab done, 1 inittab and inittab.d done */
+ DIR *tabdir=NULL; /* the INITTAB.D dir */
+ struct dirent *file_entry; /* inittab.d entry */
+ char f_name[272]; /* size d_name + strlen /etc/inittad.d/ */
+
+#if DEBUG
+ if (newFamily != NULL) {
+ INITDBG(L_VB, "PANIC newFamily != NULL");
+ exit(1);
+ }
+ INITDBG(L_VB, "Reading inittab");
+#endif
+
+ /*
+ * Open INITTAB and read line by line.
+ */
+ if ((fp = fopen(INITTAB, "r")) == NULL)
+ initlog(L_VB, "No inittab file found");
+
+ /*
+ * Open INITTAB.D directory
+ */
+ if( (tabdir = opendir(INITTABD))==NULL)
+ initlog(L_VB, "No inittab.d directory found");
+
+ while(done!=1) {
+ /*
+ * Add single user shell entry at the end.
+ */
+ if(done == -1) {
+ if (fp == NULL || fgets(buf, sizeof(buf), fp) == NULL) {
+ done = 0;
+ /*
+ * See if we have a single user entry.
+ */
+ for(old = newFamily; old; old = old->next)
+ if (strpbrk(old->rlevel, "S")) break;
+ if (old == NULL)
+ snprintf(buf, sizeof(buf), "~~:S:wait:%s\n", SULOGIN);
+ else
+ continue;
+ }
+ } /* end if( done==-1) */
+ else if ( done == 0 ){
+ /* parse /etc/inittab.d and read all .tab files */
+ if(tabdir!=NULL){
+ if( (file_entry = readdir(tabdir))!=NULL){
+ /* ignore files not like *.tab */
+ if (!strcmp(file_entry->d_name, ".") || !strcmp(file_entry->d_name, ".."))
+ continue;
+ if (strlen(file_entry->d_name) < 5 || strcmp(file_entry->d_name + strlen(file_entry->d_name) - 4, ".tab"))
+ continue;
+ /*
+ * initialize filename
+ */
+ memset(f_name,0,sizeof(char)*272);
+ snprintf(f_name,272,"/etc/inittab.d/%s",file_entry->d_name);
+ initlog(L_VB, "Reading: %s",f_name);
+ /*
+ * read file in inittab.d only one entry per file
+ */
+ if ((fp_tab = fopen(f_name, "r")) == NULL)
+ continue;
+ /* read the file while the line contain comment */
+ while( fgets(buf, sizeof(buf), fp_tab) != NULL) {
+ for(p = buf; *p == ' ' || *p == '\t'; p++);
+ if (*p != '#' && *p != '\n')
+ break;
+ }
+ fclose(fp_tab);
+ /* do some checks */
+ if( buf == NULL )
+ continue;
+ if( strlen( p ) == 0 )
+ continue;
+ } /* end of readdir, all is done */
+ else {
+ done = 1;
+ continue;
+ }
+ } /* end of if(tabdir!=NULL) */
+ else {
+ done = 1;
+ continue;
+ }
+ } /* end of if ( done == 0 ) */
+ lineNo++;
+ /*
+ * Skip comments and empty lines
+ */
+ for(p = buf; *p == ' ' || *p == '\t'; p++)
+ ;
+ if (*p == '#' || *p == '\n') continue;
+
+ /*
+ * Decode the fields
+ */
+ id = strsep(&p, ":");
+ rlevel = strsep(&p, ":");
+ action = strsep(&p, ":");
+ process = strsep(&p, "\n");
+
+ /*
+ * Check if syntax is OK. Be very verbose here, to
+ * avoid newbie postings on comp.os.linux.setup :)
+ */
+ err[0] = 0;
+ if (!id || !*id) strcpy(err, "missing id field");
+ if (!rlevel) strcpy(err, "missing runlevel field");
+ if (!process) strcpy(err, "missing process field");
+ if (!action || !*action)
+ strcpy(err, "missing action field");
+ if (id && strlen(id) > sizeof(utproto.ut_id))
+ sprintf(err, "id field too long (max %d characters)",
+ (int)sizeof(utproto.ut_id));
+ if (rlevel && strlen(rlevel) > 11)
+ strcpy(err, "rlevel field too long (max 11 characters)");
+ if (process && strlen(process) > 127)
+ strcpy(err, "process field too long (max 127 characters)");
+ if (action && strlen(action) > 32)
+ strcpy(err, "action field too long");
+ if (err[0] != 0) {
+ initlog(L_VB, "%s[%d]: %s", INITTAB, lineNo, err);
+ INITDBG(L_VB, "%s:%s:%s:%s", id, rlevel, action, process);
+ continue;
+ }
+
+ /*
+ * Decode the "action" field
+ */
+ actionNo = -1;
+ for(f = 0; actions[f].name; f++)
+ if (strcasecmp(action, actions[f].name) == 0) {
+ actionNo = actions[f].act;
+ break;
+ }
+ if (actionNo == -1) {
+ initlog(L_VB, "%s[%d]: %s: unknown action field",
+ INITTAB, lineNo, action);
+ continue;
+ }
+
+ /*
+ * See if the id field is unique
+ */
+ for(old = newFamily; old; old = old->next) {
+ if(strcmp(old->id, id) == 0 && strcmp(id, "~~")) {
+ initlog(L_VB, "%s[%d]: duplicate ID field \"%s\"",
+ INITTAB, lineNo, id);
+ break;
+ }
+ }
+ if (old) continue;
+
+ /*
+ * Allocate a CHILD structure
+ */
+ ch = imalloc(sizeof(CHILD));
+
+ /*
+ * And fill it in.
+ */
+ ch->action = actionNo;
+ strncpy(ch->id, id, sizeof(utproto.ut_id) + 1); /* Hack for different libs. */
+ strncpy(ch->process, process, sizeof(ch->process) - 1);
+ if (rlevel[0]) {
+ for(f = 0; f < (int)sizeof(rlevel) - 1 && rlevel[f]; f++) {
+ ch->rlevel[f] = rlevel[f];
+ if (ch->rlevel[f] == 's') ch->rlevel[f] = 'S';
+ }
+ strncpy(ch->rlevel, rlevel, sizeof(ch->rlevel) - 1);
+ } else {
+ strcpy(ch->rlevel, "0123456789");
+ if (ISPOWER(ch->action))
+ strcpy(ch->rlevel, "S0123456789");
+ }
+ /*
+ * We have the fake runlevel '#' for SYSINIT and
+ * '*' for BOOT and BOOTWAIT.
+ */
+ if (ch->action == SYSINIT) strcpy(ch->rlevel, "#");
+ if (ch->action == BOOT || ch->action == BOOTWAIT)
+ strcpy(ch->rlevel, "*");
+
+ /*
+ * Now add it to the linked list. Special for powerfail.
+ */
+ if (ISPOWER(ch->action)) {
+
+ /*
+ * Disable by default
+ */
+ ch->flags |= XECUTED;
+
+ /*
+ * Tricky: insert at the front of the list..
+ */
+ old = NULL;
+ for(i = newFamily; i; i = i->next) {
+ if (!ISPOWER(i->action)) break;
+ old = i;
+ }
+ /*
+ * Now add after entry "old"
+ */
+ if (old) {
+ ch->next = i;
+ old->next = ch;
+ if (i == NULL) head = ch;
+ } else {
+ ch->next = newFamily;
+ newFamily = ch;
+ if (ch->next == NULL) head = ch;
+ }
+ } else {
+ /*
+ * Just add at end of the list
+ */
+ if (ch->action == KBREQUEST) ch->flags |= XECUTED;
+ ch->next = NULL;
+ if (head)
+ head->next = ch;
+ else
+ newFamily = ch;
+ head = ch;
+ }
+
+ /*
+ * Walk through the old list comparing id fields
+ */
+ for(old = family; old; old = old->next)
+ if (strcmp(old->id, ch->id) == 0) {
+ old->new = ch;
+ break;
+ }
+ }
+
+ /*
+ * We're done.
+ */
+ if (fp) fclose(fp);
+ if(tabdir) closedir(tabdir);
+
+#ifdef __linux__
+ check_kernel_console();
+#endif
+
+ /*
+ * Loop through the list of children, and see if they need to
+ * be killed.
+ */
+
+ INITDBG(L_VB, "Checking for children to kill");
+ for(round = 0; round < 2; round++) {
+ talk = 1;
+ for(ch = family; ch; ch = ch->next) {
+ ch->flags &= ~KILLME;
+
+ /*
+ * Is this line deleted?
+ */
+ if (ch->new == NULL) ch->flags |= KILLME;
+
+ /*
+ * If the entry has changed, kill it anyway. Note that
+ * we do not check ch->process, only the "action" field.
+ * This way, you can turn an entry "off" immediately, but
+ * changes in the command line will only become effective
+ * after the running version has exited.
+ */
+ if (ch->new && ch->action != ch->new->action) ch->flags |= KILLME;
+
+ /*
+ * Only BOOT processes may live in all levels
+ */
+ if (ch->action != BOOT &&
+ strchr(ch->rlevel, runlevel) == NULL) {
+ /*
+ * Ondemand procedures live always,
+ * except in single user
+ */
+ if (runlevel == 'S' || !(ch->flags & DEMAND))
+ ch->flags |= KILLME;
+ }
+
+ /*
+ * Now, if this process may live note so in the new list
+ */
+ if ((ch->flags & KILLME) == 0) {
+ ch->new->flags = ch->flags;
+ ch->new->pid = ch->pid;
+ ch->new->exstat = ch->exstat;
+ continue;
+ }
+
+
+ /*
+ * Is this process still around?
+ */
+ if ((ch->flags & RUNNING) == 0) {
+ ch->flags &= ~KILLME;
+ continue;
+ }
+ INITDBG(L_VB, "Killing \"%s\"", ch->process);
+ switch(round) {
+ case 0: /* Send TERM signal */
+ if (talk)
+ initlog(L_CO,
+ "Sending processes configured via /etc/inittab the TERM signal");
+ kill(-(ch->pid), SIGTERM);
+ foundOne = 1;
+ break;
+ case 1: /* Send KILL signal and collect status */
+ if (talk)
+ initlog(L_CO,
+ "Sending processes configured via /etc/inittab the KILL signal");
+ kill(-(ch->pid), SIGKILL);
+ break;
+ }
+ talk = 0;
+
+ }
+ /*
+ * See if we have to wait sleep_time seconds
+ */
+ if (foundOne && round == 0) {
+ /*
+ * Yup, but check every 10 milliseconds if we still have children.
+ * The f < 100 * sleep_time refers to sleep time in 10 millisecond chunks.
+ */
+ for(f = 0; f < 100 * sleep_time; f++) {
+ for(ch = family; ch; ch = ch->next) {
+ if (!(ch->flags & KILLME)) continue;
+ if ((ch->flags & RUNNING) && !(ch->flags & ZOMBIE))
+ break;
+ }
+ if (ch == NULL) {
+ /*
+ * No running children, skip SIGKILL
+ */
+ round = 1;
+ foundOne = 0; /* Skip the sleep below. */
+ break;
+ }
+ do_msleep(MINI_SLEEP);
+ }
+ }
+ }
+
+ /*
+ * Now give all processes the chance to die and collect exit statuses.
+ */
+ if (foundOne) do_msleep(MINI_SLEEP);
+ for(ch = family; ch; ch = ch->next)
+ if (ch->flags & KILLME) {
+ if (!(ch->flags & ZOMBIE))
+ initlog(L_CO, "Pid %d [id %s] seems to hang", ch->pid,
+ ch->id);
+ else {
+ INITDBG(L_VB, "Updating utmp for pid %d [id %s]",
+ ch->pid, ch->id);
+ ch->flags &= ~RUNNING;
+ if (ch->process[0] != '+')
+ write_utmp_wtmp("", ch->id, ch->pid, DEAD_PROCESS, NULL);
+ }
+ }
+
+ /*
+ * Both rounds done; clean up the list.
+ */
+ sigemptyset(&nmask);
+ sigaddset(&nmask, SIGCHLD);
+ sigprocmask(SIG_BLOCK, &nmask, &omask);
+ for(ch = family; ch; ch = old) {
+ old = ch->next;
+ free(ch);
+ }
+ family = newFamily;
+ for(ch = family; ch; ch = ch->next) ch->new = NULL;
+ newFamily = NULL;
+ sigprocmask(SIG_SETMASK, &omask, NULL);
+
+#ifdef INITLVL
+ /*
+ * Dispose of INITLVL file.
+ */
+ if (lstat(INITLVL, &st) >= 0 && S_ISLNK(st.st_mode)) {
+ /*
+ * INITLVL is a symbolic link, so just truncate the file.
+ */
+ close(open(INITLVL, O_WRONLY|O_TRUNC));
+ } else {
+ /*
+ * Delete INITLVL file.
+ */
+ unlink(INITLVL);
+ }
+#endif
+#ifdef INITLVL2
+ /*
+ * Dispose of INITLVL2 file.
+ */
+ if (lstat(INITLVL2, &st) >= 0 && S_ISLNK(st.st_mode)) {
+ /*
+ * INITLVL2 is a symbolic link, so just truncate the file.
+ */
+ close(open(INITLVL2, O_WRONLY|O_TRUNC));
+ } else {
+ /*
+ * Delete INITLVL2 file.
+ */
+ unlink(INITLVL2);
+ }
+#endif
+}
+
+/*
+ * Walk through the family list and start up children.
+ * The entries that do not belong here at all are removed
+ * from the list.
+ */
+static
+void start_if_needed(void)
+{
+ CHILD *ch; /* Pointer to child */
+ int delete; /* Delete this entry from list? */
+
+ INITDBG(L_VB, "Checking for children to start");
+
+ for(ch = family; ch; ch = ch->next) {
+
+#if DEBUG
+ if (ch->rlevel[0] == 'C') {
+ INITDBG(L_VB, "%s: flags %d", ch->process, ch->flags);
+ }
+#endif
+
+ /* Are we waiting for this process? Then quit here. */
+ if (ch->flags & WAITING) break;
+
+ /* Already running? OK, don't touch it */
+ if (ch->flags & RUNNING) continue;
+
+ /* See if we have to start it up */
+ delete = 1;
+ if (strchr(ch->rlevel, runlevel) ||
+ ((ch->flags & DEMAND) && !strchr("#*Ss", runlevel))) {
+ startup(ch);
+ delete = 0;
+ }
+
+ if (delete) {
+ /* is this OK? */
+ ch->flags &= ~(RUNNING|WAITING);
+ if (!ISPOWER(ch->action) && ch->action != KBREQUEST)
+ ch->flags &= ~XECUTED;
+ ch->pid = 0;
+ } else
+ /* Do we have to wait for this process? */
+ if (ch->flags & WAITING) break;
+ }
+ /* Done. */
+}
+
+/*
+ * Ask the user on the console for a runlevel
+ */
+static
+int ask_runlevel(void)
+{
+ const char prompt[] = "\nEnter runlevel: ";
+ char buf[8];
+ int lvl = -1;
+ int fd;
+
+ console_stty();
+ fd = console_open(O_RDWR|O_NOCTTY);
+
+ if (fd < 0) return('S');
+
+ while(!strchr("0123456789S", lvl)) {
+ safe_write(fd, prompt, sizeof(prompt) - 1);
+ if (read(fd, buf, sizeof(buf)) <= 0)
+ buf[0] = 0;
+ if (buf[0] != 0 && (buf[1] == '\r' || buf[1] == '\n'))
+ lvl = buf[0];
+ if (islower(lvl)) lvl = toupper(lvl);
+ }
+ close(fd);
+ return lvl;
+}
+
+/*
+ * Search the INITTAB file for the 'initdefault' field, with the default
+ * runlevel. If this fails, ask the user to supply a runlevel.
+ */
+static
+int get_init_default(void)
+{
+ CHILD *ch;
+ int lvl = -1;
+ char *p;
+
+ /*
+ * Look for initdefault.
+ */
+ for(ch = family; ch; ch = ch->next)
+ if (ch->action == INITDEFAULT) {
+ p = ch->rlevel;
+ while(*p) {
+ if (*p > lvl) lvl = *p;
+ p++;
+ }
+ break;
+ }
+ /*
+ * See if level is valid
+ */
+ if (lvl > 0) {
+ if (islower(lvl)) lvl = toupper(lvl);
+ if (strchr("0123456789S", lvl) == NULL) {
+ initlog(L_VB,
+ "Initdefault level '%c' is invalid", lvl);
+ lvl = 0;
+ }
+ }
+ /*
+ * Ask for runlevel on console if needed.
+ */
+ if (lvl <= 0) lvl = ask_runlevel();
+
+ /*
+ * Log the fact that we have a runlevel now.
+ */
+ Write_Runlevel_Log(lvl);
+ return lvl;
+}
+
+
+/*
+ * We got signaled.
+ *
+ * Do actions for the new level. If we are compatible with
+ * the "old" INITLVL and arg == 0, try to read the new
+ * runlevel from that file first.
+ */
+static
+int read_level(int arg)
+{
+ CHILD *ch; /* Walk through list */
+ unsigned char foo = 'X'; /* Contents of INITLVL */
+ int ok = 1;
+#ifdef INITLVL
+ FILE *fp;
+ struct stat stt;
+ int st;
+#endif
+
+ if (arg) foo = arg;
+
+#ifdef INITLVL
+ ok = 0;
+
+ if (arg == 0) {
+ fp = NULL;
+ if (stat(INITLVL, &stt) != 0 || stt.st_size != 0L)
+ fp = fopen(INITLVL, "r");
+#ifdef INITLVL2
+ if (fp == NULL &&
+ (stat(INITLVL2, &stt) != 0 || stt.st_size != 0L))
+ fp = fopen(INITLVL2, "r");
+#endif
+ if (fp == NULL) {
+ /* INITLVL file empty or not there - act as 'init q' */
+ initlog(L_SY, "Re-reading inittab");
+ return(runlevel);
+ }
+ ok = fscanf(fp, "%c %d", &foo, &st);
+ fclose(fp);
+ } else {
+ /* We go to the new runlevel passed as an argument. */
+ foo = arg;
+ ok = 1;
+ }
+ if (ok == 2) sleep_time = st;
+
+#endif /* INITLVL */
+
+ if (islower(foo)) foo = toupper(foo);
+ if (ok < 1 || ok > 2 || strchr("QS0123456789ABCU", foo) == NULL) {
+ initlog(L_VB, "bad runlevel: %c", foo);
+ return runlevel;
+ }
+
+ /* Log this action */
+ switch(foo) {
+ case 'S':
+ initlog(L_VB, "Going single user");
+ break;
+ case 'Q':
+ initlog(L_SY, "Re-reading inittab");
+ break;
+ case 'A':
+ case 'B':
+ case 'C':
+ initlog(L_SY,
+ "Activating demand-procedures for '%c'", foo);
+ break;
+ case 'U':
+ initlog(L_SY, "Trying to re-exec init");
+ return 'U';
+ default:
+ initlog(L_VB, "Switching to runlevel: %c", foo);
+ }
+
+ if (foo == 'Q') {
+#if defined(SIGINT_ONLYONCE) && (SIGINT_ONLYONCE == 1)
+ /* Re-enable signal from keyboard */
+ struct sigaction sa;
+ SETSIG(sa, SIGINT, signal_handler, 0);
+#endif
+ return runlevel;
+ }
+
+ /* Check if this is a runlevel a, b or c */
+ if (strchr("ABC", foo)) {
+ if (runlevel == 'S') return(runlevel);
+
+ /* Read inittab again first! */
+ read_inittab();
+
+ /* Mark those special tasks */
+ for(ch = family; ch; ch = ch->next)
+ if (strchr(ch->rlevel, foo) != NULL ||
+ strchr(ch->rlevel, tolower(foo)) != NULL) {
+ ch->flags |= DEMAND;
+ ch->flags &= ~XECUTED;
+ INITDBG(L_VB,
+ "Marking (%s) as ondemand, flags %d",
+ ch->id, ch->flags);
+ }
+ return runlevel;
+ }
+
+ /* Store both the old and the new runlevel. */
+ wrote_utmp_rlevel = 0;
+ wrote_wtmp_rlevel = 0;
+ write_utmp_wtmp("runlevel", "~~", foo + 256*runlevel, RUN_LVL, "~");
+ thislevel = foo;
+ prevlevel = runlevel;
+ Write_Runlevel_Log(runlevel);
+ return foo;
+}
+
+
+/*
+ * This procedure is called after every signal (SIGHUP, SIGALRM..)
+ *
+ * Only clear the 'failing' flag if the process is sleeping
+ * longer than 5 minutes, or inittab was read again due
+ * to user interaction.
+ */
+static
+void fail_check(void)
+{
+ CHILD *ch; /* Pointer to child structure */
+ time_t t; /* System time */
+ time_t next_alarm = 0; /* When to set next alarm */
+
+ time(&t);
+
+ for(ch = family; ch; ch = ch->next) {
+
+ if (ch->flags & FAILING) {
+ /* Can we free this sucker? */
+ if (ch->tm + SLEEPTIME < t) {
+ ch->flags &= ~FAILING;
+ ch->count = 0;
+ ch->tm = 0;
+ } else {
+ /* No, we'll look again later */
+ if (next_alarm == 0 ||
+ ch->tm + SLEEPTIME > next_alarm)
+ next_alarm = ch->tm + SLEEPTIME;
+ }
+ }
+ }
+ if (next_alarm) {
+ next_alarm -= t;
+ if (next_alarm < 1) next_alarm = 1;
+ alarm(next_alarm);
+ }
+}
+
+/* Set all 'Fail' timers to 0 */
+static
+void fail_cancel(void)
+{
+ CHILD *ch;
+
+ for(ch = family; ch; ch = ch->next) {
+ ch->count = 0;
+ ch->tm = 0;
+ ch->flags &= ~FAILING;
+ }
+}
+
+/*
+ * Start up powerfail entries.
+ */
+static
+void do_power_fail(int pwrstat)
+{
+ CHILD *ch;
+
+ /*
+ * Tell powerwait & powerfail entries to start up
+ */
+ for (ch = family; ch; ch = ch->next) {
+ if (pwrstat == 'O') {
+ /*
+ * The power is OK again.
+ */
+ if (ch->action == POWEROKWAIT)
+ ch->flags &= ~XECUTED;
+ } else if (pwrstat == 'L') {
+ /*
+ * Low battery, shut down now.
+ */
+ if (ch->action == POWERFAILNOW)
+ ch->flags &= ~XECUTED;
+ } else {
+ /*
+ * Power is failing, shutdown imminent
+ */
+ if (ch->action == POWERFAIL || ch->action == POWERWAIT)
+ ch->flags &= ~XECUTED;
+ }
+ }
+}
+
+/*
+ * Check for state-pipe presence
+ */
+static
+int check_pipe(int fd)
+{
+ struct timeval t;
+ fd_set s;
+ char signature[8];
+
+ FD_ZERO(&s);
+ FD_SET(fd, &s);
+ t.tv_sec = t.tv_usec = 0;
+
+ if (select(fd+1, &s, NULL, NULL, &t) != 1)
+ return 0;
+ if (read(fd, signature, 8) != 8)
+ return 0;
+ return strncmp(Signature, signature, 8) == 0;
+}
+
+/*
+ * Make a state-pipe.
+ */
+static
+int make_pipe(int fd)
+{
+ int fds[2];
+
+ if (pipe(fds)) {
+ initlog(L_VB, "pipe: %m");
+ return -1;
+ }
+ dup2(fds[0], fd);
+ close(fds[0]);
+ fcntl(fds[1], F_SETFD, 1);
+ fcntl(fd, F_SETFD, 0);
+ safe_write(fds[1], Signature, 8);
+
+ return fds[1];
+}
+
+/*
+ * Attempt to re-exec.
+ * Renaming to my_re_exec since re_exec is now a common function name
+ * which conflicts.
+ */
+static
+void my_re_exec(void)
+{
+ CHILD *ch;
+ sigset_t mask, oldset;
+ pid_t pid;
+ char **env;
+ int fd;
+
+ if (strchr("S0123456",runlevel) == NULL)
+ return;
+
+ /*
+ * Reset the alarm, and block all signals.
+ */
+ alarm(0);
+ sigfillset(&mask);
+ sigprocmask(SIG_BLOCK, &mask, &oldset);
+
+ /*
+ * construct a pipe fd --> STATE_PIPE and write a signature
+ */
+ if ((fd = make_pipe(STATE_PIPE)) < 0) {
+ sigprocmask(SIG_SETMASK, &oldset, NULL);
+ initlog(L_CO, "Attempt to re-exec failed");
+ }
+
+ fail_cancel();
+ if (pipe_fd >= 0)
+ close(pipe_fd);
+ pipe_fd = -1;
+ DELSET(got_signals, SIGCHLD);
+ DELSET(got_signals, SIGHUP);
+ DELSET(got_signals, SIGUSR1);
+ DELSET(got_signals, SIGUSR2);
+
+ /*
+ * That should be cleaned.
+ */
+ for(ch = family; ch; ch = ch->next)
+ if (ch->flags & ZOMBIE) {
+ INITDBG(L_VB, "Child died, PID= %d", ch->pid);
+ ch->flags &= ~(RUNNING|ZOMBIE|WAITING);
+ if (ch->process[0] != '+')
+ write_utmp_wtmp("", ch->id, ch->pid, DEAD_PROCESS, NULL);
+ }
+
+ if ((pid = fork()) == 0) {
+ /*
+ * Child sends state information to the parent.
+ */
+ send_state(fd);
+ exit(0);
+ }
+
+ /*
+ * The existing init process execs a new init binary.
+ */
+ env = init_buildenv(0);
+ execle(myname, myname, "--init", NULL, env);
+
+ /*
+ * We shouldn't be here, something failed.
+ * Close the state pipe, unblock signals and return.
+ */
+ init_freeenv(env);
+ close(fd);
+ close(STATE_PIPE);
+ sigprocmask(SIG_SETMASK, &oldset, NULL);
+ initlog(L_CO, "Attempt to re-exec failed");
+}
+
+/*
+ * Redo utmp/wtmp entries if required or requested
+ * Check for written records and size of utmp
+ */
+static
+void redo_utmp_wtmp(void)
+{
+ struct stat ustat;
+ const int ret = stat(UTMP_FILE, &ustat);
+
+ if ((ret < 0) || (ustat.st_size == 0))
+ wrote_utmp_rlevel = wrote_utmp_reboot = 0;
+
+ if ((wrote_wtmp_reboot == 0) || (wrote_utmp_reboot == 0))
+ write_utmp_wtmp("reboot", "~~", 0, BOOT_TIME, "~");
+
+ if ((wrote_wtmp_rlevel == 0) || (wrote_utmp_rlevel == 0))
+ write_utmp_wtmp("runlevel", "~~", thislevel + 256 * prevlevel, RUN_LVL, "~");
+}
+
+/*
+ * We got a change runlevel request through the
+ * init.fifo. Process it.
+ */
+static
+void fifo_new_level(int level)
+{
+#if CHANGE_WAIT
+ CHILD *ch;
+#endif
+ int oldlevel;
+
+ if (level == runlevel) return;
+
+#if CHANGE_WAIT
+ /* Are we waiting for a child? */
+ for(ch = family; ch; ch = ch->next)
+ if (ch->flags & WAITING) break;
+ if (ch == NULL)
+#endif
+ {
+ /* We need to go into a new runlevel */
+ oldlevel = runlevel;
+ runlevel = read_level(level);
+ if (runlevel == 'U') {
+ runlevel = oldlevel;
+ my_re_exec();
+ } else {
+ if (oldlevel != 'S' && runlevel == 'S') console_stty();
+ if (runlevel == '6' || runlevel == '0' ||
+ runlevel == '1') console_stty();
+ if (runlevel > '1' && runlevel < '6') redo_utmp_wtmp();
+ read_inittab();
+ fail_cancel();
+ setproctitle("init [%c]", (int)runlevel);
+ }
+ }
+ Write_Runlevel_Log(runlevel);
+}
+
+
+/*
+ * Set/unset environment variables. The variables are
+ * encoded as KEY=VAL\0KEY=VAL\0\0. With "=VAL" it means
+ * setenv, without it means unsetenv.
+ */
+static
+void initcmd_setenv(char *data, int size)
+{
+ char *env, *p, *e;
+ size_t sz;
+ int i, eq;
+
+ e = data + size;
+
+ while (*data && data < e) {
+ for (p = data; *p && p < e; p++)
+ ;
+ if (*p) break;
+ env = data;
+ data = ++p;
+
+ /*
+ * We only allow INIT_* to be set.
+ */
+ if (strncmp(env, "INIT_", 5) != 0)
+ continue;
+
+ sz = strcspn(env, "=");
+ eq = (env[sz] == '=');
+
+ /*initlog(L_SY, "init_setenv: %s, %d, %d", env, eq, sz);*/
+
+ /* Free existing vars. */
+ for (i = 0; i < NR_EXTRA_ENV; i++) {
+ if (extra_env[i] == NULL)
+ continue;
+ if (sz != strcspn(extra_env[i], "="))
+ continue;
+ if (strncmp(extra_env[i], env, sz) == 0) {
+ free(extra_env[i]);
+ extra_env[i] = NULL;
+ }
+ }
+
+ if (eq == 0)
+ continue;
+
+ /* Set new vars if needed. */
+ for (i = 0; i < NR_EXTRA_ENV; i++) {
+ if (extra_env[i] == NULL) {
+ extra_env[i] = istrdup(env);
+ break;
+ }
+ }
+ }
+}
+
+
+/*
+ * Read from the init FIFO. Processes like telnetd and rlogind can
+ * ask us to create login processes on their behalf.
+ */
+static
+void check_init_fifo(void)
+{
+ struct init_request request;
+ struct timeval tv;
+ struct stat st, st2;
+ fd_set fds;
+ int n;
+ int quit = 0;
+
+ /*
+ * First, try to create /dev/initctl if not present.
+ */
+ if (stat(INIT_FIFO, &st2) < 0 && errno == ENOENT)
+ (void)mkfifo(INIT_FIFO, 0600);
+
+ /*
+ * If /dev/initctl is open, stat the file to see if it
+ * is still the _same_ inode.
+ */
+ if (pipe_fd >= 0) {
+ fstat(pipe_fd, &st);
+ if (stat(INIT_FIFO, &st2) < 0 ||
+ st.st_dev != st2.st_dev ||
+ st.st_ino != st2.st_ino) {
+ close(pipe_fd);
+ pipe_fd = -1;
+ }
+ }
+
+ /*
+ * Now finally try to open /dev/initctl if pipe_fd is -1
+ * if it is -2, then we leave it closed
+ */
+ if (pipe_fd == -1) {
+ if ((pipe_fd = open(INIT_FIFO, O_RDWR|O_NONBLOCK)) >= 0) {
+ fstat(pipe_fd, &st);
+ if (!S_ISFIFO(st.st_mode)) {
+ initlog(L_VB, "%s is not a fifo", INIT_FIFO);
+ close(pipe_fd);
+ pipe_fd = -1;
+ }
+ }
+ if (pipe_fd >= 0) {
+ /*
+ * Don't use fd's 0, 1 or 2.
+ */
+ (void) dup2(pipe_fd, PIPE_FD);
+ close(pipe_fd);
+ pipe_fd = PIPE_FD;
+
+ /*
+ * Return to caller - we'll be back later.
+ */
+ }
+ }
+
+ /* Wait for data to appear, _if_ the pipe was opened. */
+ if (pipe_fd >= 0) {
+ while(!quit) {
+
+ /* Do select, return on EINTR. */
+ FD_ZERO(&fds);
+ FD_SET(pipe_fd, &fds);
+ tv.tv_sec = 5;
+ tv.tv_usec = 0;
+ n = select(pipe_fd + 1, &fds, NULL, NULL, &tv);
+ if (n <= 0) {
+ if (n == 0 || errno == EINTR) return;
+ continue;
+ }
+
+ /* Read the data, return on EINTR. */
+ n = read(pipe_fd, &request, sizeof(request));
+ if (n == 0) {
+ /*
+ * End of file. This can't happen under Linux (because
+ * the pipe is opened O_RDWR - see select() in the
+ * kernel) but you never know...
+ */
+ close(pipe_fd);
+ pipe_fd = -1;
+ return;
+ }
+ if (n <= 0) {
+ if (errno == EINTR) return;
+ initlog(L_VB, "error reading initrequest");
+ continue;
+ }
+
+ /*
+ * This is a convenient point to also try to
+ * find the console device or check if it changed.
+ */
+ console_init();
+
+ /*
+ * Process request.
+ */
+ if (request.magic != INIT_MAGIC || n != sizeof(request)) {
+ initlog(L_VB, "got bogus initrequest");
+ continue;
+ }
+ switch(request.cmd) {
+ case INIT_CMD_RUNLVL:
+ sleep_time = request.sleeptime;
+ fifo_new_level(request.runlevel);
+ quit = 1;
+ break;
+ case INIT_CMD_POWERFAIL:
+ sleep_time = request.sleeptime;
+ do_power_fail('F');
+ quit = 1;
+ break;
+ case INIT_CMD_POWERFAILNOW:
+ sleep_time = request.sleeptime;
+ do_power_fail('L');
+ quit = 1;
+ break;
+ case INIT_CMD_POWEROK:
+ sleep_time = request.sleeptime;
+ do_power_fail('O');
+ quit = 1;
+ break;
+ case INIT_CMD_SETENV:
+ initcmd_setenv(request.i.data, sizeof(request.i.data));
+ break;
+ default:
+ initlog(L_VB, "got unimplemented initrequest.");
+ break;
+ } /* end of switch */
+ } /* end of while loop not quitting */
+ } /* end of if the pipe is open */
+ /*
+ * We come here if the pipe couldn't be opened.
+ */
+ if (pipe_fd == -1) pause();
+
+}
+
+
+/*
+ * This function is used in the transition
+ * sysinit (-> single user) boot -> multi-user.
+ */
+static
+void boot_transitions()
+{
+ CHILD *ch;
+ static int newlevel = 0;
+ static int warn = 1;
+ int loglevel;
+ int oldlevel;
+
+ /* Check if there is something to wait for! */
+ for( ch = family; ch; ch = ch->next )
+ if ((ch->flags & RUNNING) && ch->action != BOOT) break;
+
+ if (ch == NULL) {
+ /* No processes left in this level, proceed to next level. */
+ loglevel = -1;
+ oldlevel = 'N';
+ switch(runlevel) {
+ case '#': /* SYSINIT -> BOOT */
+ INITDBG(L_VB, "SYSINIT -> BOOT");
+
+ /* Write a boot record. */
+ wrote_utmp_reboot = 0;
+ wrote_wtmp_reboot = 0;
+ write_utmp_wtmp("reboot", "~~", 0, BOOT_TIME, "~");
+
+ /* Get our run level */
+ newlevel = dfl_level ? dfl_level : get_init_default();
+ if (newlevel == 'S') {
+ runlevel = newlevel;
+ /* Not really 'S' but show anyway. */
+ setproctitle("init [S]");
+ } else
+ runlevel = '*';
+ break;
+ case '*': /* BOOT -> NORMAL */
+ INITDBG(L_VB, "BOOT -> NORMAL");
+ if (runlevel != newlevel)
+ loglevel = newlevel;
+ runlevel = newlevel;
+ did_boot = 1;
+ warn = 1;
+ break;
+ case 'S': /* Ended SU mode */
+ case 's':
+ INITDBG(L_VB, "END SU MODE");
+ newlevel = get_init_default();
+ if (!did_boot && newlevel != 'S')
+ runlevel = '*';
+ else {
+ if (runlevel != newlevel)
+ loglevel = newlevel;
+ runlevel = newlevel;
+ oldlevel = 'S';
+ }
+ warn = 1;
+ for(ch = family; ch; ch = ch->next)
+ if (strcmp(ch->rlevel, "S") == 0)
+ ch->flags &= ~(FAILING|WAITING|XECUTED);
+ break;
+ default:
+ if (warn)
+ initlog(L_VB,
+ "no more processes left in this runlevel");
+ warn = 0;
+ loglevel = -1;
+ if (got_signals == 0)
+ check_init_fifo();
+ break;
+ }
+ if (loglevel > 0) {
+ initlog(L_VB, "Entering runlevel: %c", runlevel);
+ wrote_utmp_rlevel = 0;
+ wrote_wtmp_rlevel = 0;
+ write_utmp_wtmp("runlevel", "~~", runlevel + 256 * oldlevel, RUN_LVL, "~");
+ thislevel = runlevel;
+ prevlevel = oldlevel;
+ setproctitle("init [%c]", (int)runlevel);
+ }
+ Write_Runlevel_Log(runlevel);
+ }
+}
+
+/*
+ * Init got hit by a signal. See which signal it is,
+ * and act accordingly.
+ */
+static
+void process_signals()
+{
+ CHILD *ch;
+ int pwrstat;
+ int oldlevel;
+ int fd;
+ char c;
+
+ if (ISMEMBER(got_signals, SIGPWR)) {
+ INITDBG(L_VB, "got SIGPWR");
+ /* See _what_ kind of SIGPWR this is. */
+ pwrstat = 0;
+ if ((fd = open(PWRSTAT, O_RDONLY)) >= 0) {
+ if (read(fd, &c, 1) != 1)
+ c = 0;
+ pwrstat = c;
+ close(fd);
+ unlink(PWRSTAT);
+ } else if ((fd = open(PWRSTAT_OLD, O_RDONLY)) >= 0) {
+ /* Path changed 2010-03-20. Look for the old path for a while. */
+ initlog(L_VB, "warning: found obsolete path %s, use %s instead",
+ PWRSTAT_OLD, PWRSTAT);
+ if (read(fd, &c, 1) != 1)
+ c = 0;
+ pwrstat = c;
+ close(fd);
+ unlink(PWRSTAT_OLD);
+ }
+ do_power_fail(pwrstat);
+ DELSET(got_signals, SIGPWR);
+ }
+
+ if (ISMEMBER(got_signals, SIGINT)) {
+#if defined(SIGINT_ONLYONCE) && (SIGINT_ONLYONCE == 1)
+ /* Ignore any further signal from keyboard */
+ struct sigaction sa;
+ SETSIG(sa, SIGINT, SIG_IGN, SA_RESTART);
+#endif
+ INITDBG(L_VB, "got SIGINT");
+ /* Tell ctrlaltdel entry to start up */
+ for(ch = family; ch; ch = ch->next)
+ if (ch->action == CTRLALTDEL)
+ ch->flags &= ~XECUTED;
+ DELSET(got_signals, SIGINT);
+ }
+
+ if (ISMEMBER(got_signals, SIGWINCH)) {
+ INITDBG(L_VB, "got SIGWINCH");
+ /* Tell kbrequest entry to start up */
+ for(ch = family; ch; ch = ch->next)
+ if (ch->action == KBREQUEST)
+ ch->flags &= ~XECUTED;
+ DELSET(got_signals, SIGWINCH);
+ }
+
+ if (ISMEMBER(got_signals, SIGALRM)) {
+ INITDBG(L_VB, "got SIGALRM");
+ /* The timer went off: check it out */
+ DELSET(got_signals, SIGALRM);
+ }
+
+ if (ISMEMBER(got_signals, SIGCHLD)) {
+ INITDBG(L_VB, "got SIGCHLD");
+ /* First set flag to 0 */
+ DELSET(got_signals, SIGCHLD);
+
+ /* See which child this was */
+ for(ch = family; ch; ch = ch->next)
+ if (ch->flags & ZOMBIE) {
+ INITDBG(L_VB, "Child died, PID= %d", ch->pid);
+ ch->flags &= ~(RUNNING|ZOMBIE|WAITING);
+ if (ch->process[0] != '+')
+ write_utmp_wtmp("", ch->id, ch->pid, DEAD_PROCESS, NULL);
+ }
+
+ }
+
+ if (ISMEMBER(got_signals, SIGHUP)) {
+ INITDBG(L_VB, "got SIGHUP");
+#if CHANGE_WAIT
+ /* Are we waiting for a child? */
+ for(ch = family; ch; ch = ch->next)
+ if (ch->flags & WAITING) break;
+ if (ch == NULL)
+#endif
+ {
+ /* We need to go into a new runlevel */
+ oldlevel = runlevel;
+#ifdef INITLVL
+ runlevel = read_level(0);
+#endif
+ if (runlevel == 'U') {
+ runlevel = oldlevel;
+ my_re_exec();
+ } else {
+ if (oldlevel != 'S' && runlevel == 'S') console_stty();
+ if (runlevel == '6' || runlevel == '0' ||
+ runlevel == '1') console_stty();
+ read_inittab();
+ fail_cancel();
+ setproctitle("init [%c]", (int)runlevel);
+ DELSET(got_signals, SIGHUP);
+ }
+ Write_Runlevel_Log(runlevel);
+ }
+ }
+ if (ISMEMBER(got_signals, SIGUSR1)) {
+ /*
+ * SIGUSR1 means close and reopen /dev/initctl
+ */
+ INITDBG(L_VB, "got SIGUSR1");
+ if (pipe_fd)
+ close(pipe_fd);
+ pipe_fd = -1;
+ DELSET(got_signals, SIGUSR1);
+ }
+ else if (ISMEMBER(got_signals, SIGUSR2)) {
+ /* SIGUSR1 mean close the pipe and leave it closed */
+ INITDBG(L_VB, "got SIGUSR2");
+ if (pipe_fd)
+ close(pipe_fd);
+ pipe_fd = -2;
+ DELSET(got_signals, SIGUSR2);
+ }
+}
+
+/*
+ * The main loop
+ */
+static
+void init_main(void)
+{
+ CHILD *ch;
+ struct sigaction sa;
+ sigset_t sgt;
+ int f, st;
+
+ if (!reload) {
+
+#if INITDEBUG
+ /*
+ * Fork so we can debug the init process.
+ */
+ if ((f = fork()) > 0) {
+ static const char killmsg[] = "PRNT: init killed.\r\n";
+ pid_t rc;
+
+ while((rc = wait(&st)) != f)
+ if (rc < 0 && errno == ECHILD)
+ break;
+ safe_write(1, killmsg, sizeof(killmsg) - 1);
+ while(1) pause();
+ }
+#endif
+
+#ifdef __linux__
+ /*
+ * Tell the kernel to send us SIGINT when CTRL-ALT-DEL
+ * is pressed, and that we want to handle keyboard signals.
+ */
+ init_reboot(BMAGIC_SOFT);
+ if ((f = open(VT_MASTER, O_RDWR | O_NOCTTY)) >= 0) {
+ (void) ioctl(f, KDSIGACCEPT, SIGWINCH);
+ close(f);
+ } else
+ (void) ioctl(0, KDSIGACCEPT, SIGWINCH);
+#endif
+
+ /*
+ * Ignore all signals.
+ */
+ for(f = 1; f <= NSIG; f++)
+ SETSIG(sa, f, SIG_IGN, SA_RESTART);
+ }
+
+ SETSIG(sa, SIGALRM, signal_handler, 0);
+ SETSIG(sa, SIGHUP, signal_handler, 0);
+ SETSIG(sa, SIGINT, signal_handler, 0);
+ SETSIG(sa, SIGCHLD, chld_handler, SA_RESTART);
+ SETSIG(sa, SIGPWR, signal_handler, 0);
+ SETSIG(sa, SIGWINCH, signal_handler, 0);
+ SETSIG(sa, SIGUSR1, signal_handler, 0);
+ SETSIG(sa, SIGUSR2, signal_handler, 0);
+ SETSIG(sa, SIGSTOP, stop_handler, SA_RESTART);
+ SETSIG(sa, SIGTSTP, stop_handler, SA_RESTART);
+ SETSIG(sa, SIGCONT, cont_handler, SA_RESTART);
+ SETSIG(sa, SIGSEGV, (void (*)(int))segv_handler, SA_RESTART);
+
+ console_init();
+
+ if (!reload) {
+ int fd;
+
+ /* Close whatever files are open, and reset the console. */
+ close(0);
+ close(1);
+ close(2);
+ console_stty();
+ setsid();
+
+ /*
+ * Set default PATH variable.
+ */
+ setenv("PATH", PATH_DEFAULT, 1 /* Overwrite */);
+
+ /*
+ * Initialize /var/run/utmp (only works if /var is on
+ * root and mounted rw)
+ */
+ if ((fd = open(UTMP_FILE, O_WRONLY|O_CREAT|O_TRUNC, 0644)) >= 0)
+ close(fd);
+
+ /*
+ * Say hello to the world
+ */
+ initlog(L_CO, bootmsg, "booting");
+
+ /*
+ * See if we have to start an emergency shell.
+ */
+ if (emerg_shell) {
+ pid_t rc;
+ SETSIG(sa, SIGCHLD, SIG_DFL, SA_RESTART);
+ if (spawn(&ch_emerg, &f) > 0) {
+ while((rc = wait(&st)) != f)
+ if (rc < 0 && errno == ECHILD)
+ break;
+ }
+ SETSIG(sa, SIGCHLD, chld_handler, SA_RESTART);
+ }
+
+ /*
+ * Start normal boot procedure.
+ */
+ runlevel = '#';
+ read_inittab();
+
+ } else {
+ /*
+ * Restart: unblock signals and let the show go on
+ */
+ initlog(L_CO, bootmsg, "reloading");
+ sigfillset(&sgt);
+ sigprocmask(SIG_UNBLOCK, &sgt, NULL);
+
+ /*
+ * Set default PATH variable.
+ */
+ setenv("PATH", PATH_DEFAULT, 0 /* Don't overwrite */);
+ }
+ start_if_needed();
+
+ while(1) {
+
+ /* See if we need to make the boot transitions. */
+ boot_transitions();
+ INITDBG(L_VB, "init_main: waiting..");
+
+ /* Check if there are processes to be waited on. */
+ for(ch = family; ch; ch = ch->next)
+ if ((ch->flags & RUNNING) && ch->action != BOOT) break;
+
+#if CHANGE_WAIT
+ /* Wait until we get hit by some signal. */
+ while (ch != NULL && got_signals == 0) {
+ if (ISMEMBER(got_signals, SIGHUP)) {
+ /* See if there are processes to be waited on. */
+ for(ch = family; ch; ch = ch->next)
+ if (ch->flags & WAITING) break;
+ }
+ if (ch != NULL) check_init_fifo();
+ }
+#else /* CHANGE_WAIT */
+ if (ch != NULL && got_signals == 0) check_init_fifo();
+#endif /* CHANGE_WAIT */
+
+ /* Check the 'failing' flags */
+ fail_check();
+
+ /* Process any signals. */
+ process_signals();
+
+ /* See what we need to start up (again) */
+ start_if_needed();
+ }
+ /*NOTREACHED*/
+}
+
+/*
+ * Tell the user about the syntax we expect.
+ */
+static
+void usage(char *s)
+{
+ fprintf(stderr, "Usage: %s {-e VAR[=VAL] | [-t SECONDS] {0|1|2|3|4|5|6|S|s|Q|q|A|a|B|b|C|c|U|u}}\n", s);
+ exit(1);
+}
+
+static
+int telinit(char *progname, int argc, char **argv)
+{
+#ifdef TELINIT_USES_INITLVL
+ FILE *fp;
+#endif
+ struct init_request request;
+ struct sigaction sa;
+ int f, fd, l;
+ char *env = NULL;
+
+ memset(&request, 0, sizeof(request));
+ request.magic = INIT_MAGIC;
+
+ while ((f = getopt(argc, argv, "t:e:")) != EOF) switch(f) {
+ case 't':
+ sleep_time = atoi(optarg);
+ break;
+ case 'e':
+ if (env == NULL)
+ env = request.i.data;
+ l = strlen(optarg);
+ if (env + l + 2 > request.i.data + sizeof(request.i.data)) {
+ fprintf(stderr, "%s: -e option data "
+ "too large\n", progname);
+ exit(1);
+ }
+ memcpy(env, optarg, l);
+ env += l;
+ *env++ = 0;
+ break;
+ default:
+ usage(progname);
+ break;
+ }
+
+ if (env) *env++ = 0;
+
+ if (env) {
+ if (argc != optind)
+ usage(progname);
+ request.cmd = INIT_CMD_SETENV;
+ } else {
+ if (argc - optind != 1 || strlen(argv[optind]) != 1)
+ usage(progname);
+ if (!strchr("0123456789SsQqAaBbCcUu", argv[optind][0]))
+ usage(progname);
+ request.cmd = INIT_CMD_RUNLVL;
+ request.runlevel = argv[optind][0];
+ request.sleeptime = sleep_time;
+ }
+
+ /* Change to the root directory. */
+ if (0 != chdir("/"))
+ initlog(L_VB, "unable to chdir to /: %s",
+ strerror(errno));
+
+ /* Open the fifo and write a command. */
+ /* Make sure we don't hang on opening /dev/initctl */
+ SETSIG(sa, SIGALRM, signal_handler, 0);
+ alarm(3);
+ if ((fd = open(INIT_FIFO, O_WRONLY)) >= 0) {
+ 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;
+ }
+
+#ifdef TELINIT_USES_INITLVL
+ if (request.cmd == INIT_CMD_RUNLVL) {
+ /* Fallthrough to the old method. */
+
+ /* Now write the new runlevel. */
+ if ((fp = fopen(INITLVL, "w")) == NULL) {
+ fprintf(stderr, "%s: cannot create %s\n",
+ progname, INITLVL);
+ exit(1);
+ }
+ fprintf(fp, "%s %d", argv[optind], sleep_time);
+ fclose(fp);
+
+ /* And tell init about the pending runlevel change. */
+ if (kill(INITPID, SIGHUP) < 0) perror(progname);
+
+ return 0;
+ }
+#endif
+
+ fprintf(stderr, "%s: ", progname);
+ if (ISMEMBER(got_signals, SIGALRM)) {
+ fprintf(stderr, "timeout opening/writing control channel %s\n",
+ INIT_FIFO);
+ } else {
+ perror(INIT_FIFO);
+ }
+ return 1;
+}
+
+/*
+ * Main entry for init and telinit.
+ */
+int main(int argc, char **argv)
+{
+ char *p;
+ int f;
+ int isinit;
+#ifdef WITH_SELINUX
+ int enforce = 0;
+#endif
+
+ /* Get my own name */
+ if ((p = strrchr(argv[0], '/')) != NULL)
+ p++;
+ else
+ p = argv[0];
+
+ if ( (argc == 2) && (! strcmp(argv[1], "--version") ) )
+ {
+ printf("SysV init version: %s\n\n", VERSION);
+ exit(0);
+ }
+
+ /* Common umask */
+ umask(umask(077) | 022);
+
+ /* Quick check */
+ if (geteuid() != 0) {
+ fprintf(stderr, "%s: must be superuser.\n", p);
+ exit(1);
+ }
+
+ /*
+ * Is this telinit or init ?
+ */
+ isinit = (getpid() == 1);
+ for (f = 1; f < argc; f++) {
+ if (!strcmp(argv[f], "-i") || !strcmp(argv[f], "--init")) {
+ isinit = 1;
+ break;
+ }
+ }
+ if (!isinit) exit(telinit(p, argc, argv));
+
+ /*
+ * Check for re-exec
+ */
+ if (check_pipe(STATE_PIPE)) {
+
+ receive_state(STATE_PIPE);
+
+ myname = istrdup(argv[0]);
+ argv0 = argv[0];
+ maxproclen = 0;
+ for (f = 0; f < argc; f++)
+ maxproclen += strlen(argv[f]) + 1;
+ reload = 1;
+ setproctitle("init [%c]", (int)runlevel);
+
+ init_main();
+ }
+
+ /* Check command line arguments */
+ maxproclen = strlen(argv[0]) + 1;
+ for(f = 1; f < argc; f++) {
+ if (!strcmp(argv[f], "single") || !strcmp(argv[f], "-s"))
+ dfl_level = 'S';
+ else if (!strcmp(argv[f], "-a") || !strcmp(argv[f], "auto"))
+ putenv("AUTOBOOT=YES");
+ else if (!strcmp(argv[f], "-b") || !strcmp(argv[f],"emergency"))
+ emerg_shell = 1;
+ else if (!strcmp(argv[f], "-z")) {
+ /* Ignore -z xxx */
+ if (argv[f + 1]) f++;
+ } else if (strchr("0123456789sS", argv[f][0])
+ && strlen(argv[f]) == 1)
+ dfl_level = argv[f][0];
+ /* "init u" in the very beginning makes no sense */
+ if (dfl_level == 's') dfl_level = 'S';
+ maxproclen += strlen(argv[f]) + 1;
+ }
+
+#ifdef WITH_SELINUX
+ if (getenv("SELINUX_INIT") == NULL) {
+ if (is_selinux_enabled() != 1) {
+ if (selinux_init_load_policy(&enforce) == 0) {
+ putenv("SELINUX_INIT=YES");
+ execv(myname, argv);
+ } else {
+ if (enforce > 0) {
+ /* SELinux in enforcing mode but load_policy failed */
+ /* At this point, we probably can't open /dev/console, so log() won't work */
+ fprintf(stderr,"Unable to load SELinux Policy. Machine is in enforcing mode. Halting now.\n");
+ exit(1);
+ }
+ }
+ }
+ }
+#endif
+ /* Start booting. */
+ argv0 = argv[0];
+ argv[1] = NULL;
+ setproctitle("init boot");
+ init_main();
+
+ /*NOTREACHED*/
+ return 0;
+}
Index: create-2.99-initctl-patch/sysvinit-2.99-new/src/initreq.h
===================================================================
--- create-2.99-initctl-patch/sysvinit-2.99-new/src/initreq.h (nonexistent)
+++ create-2.99-initctl-patch/sysvinit-2.99-new/src/initreq.h (revision 5)
@@ -0,0 +1,82 @@
+/*
+ * initreq.h Interface to talk to init through /dev/initctl.
+ *
+ * Copyright (C) 1995-2004 Miquel van Smoorenburg
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Version: @(#)initreq.h 1.28 31-Mar-2004 MvS
+ *
+ */
+#ifndef _INITREQ_H
+#define _INITREQ_H
+
+#include <sys/param.h>
+
+#ifndef INIT_FIFO
+#define INIT_FIFO "/dev/initctl"
+#endif
+
+#define INIT_MAGIC 0x03091969
+#define INIT_CMD_START 0
+#define INIT_CMD_RUNLVL 1
+#define INIT_CMD_POWERFAIL 2
+#define INIT_CMD_POWERFAILNOW 3
+#define INIT_CMD_POWEROK 4
+#define INIT_CMD_BSD 5
+#define INIT_CMD_SETENV 6
+#define INIT_CMD_UNSETENV 7
+
+#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 separate 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
Index: create-2.99-initctl-patch/sysvinit-2.99-new/src/shutdown.c
===================================================================
--- create-2.99-initctl-patch/sysvinit-2.99-new/src/shutdown.c (nonexistent)
+++ create-2.99-initctl-patch/sysvinit-2.99-new/src/shutdown.c (revision 5)
@@ -0,0 +1,846 @@
+/*
+ * 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 /dev/initctl
+ */
+ memset(&sa, 0, sizeof(sa));
+ sa.sa_handler = alrm_handler;
+ sigaction(SIGALRM, &sa, NULL);
+ got_alrm = 0;
+ alarm(3);
+ if ((fd = open(INIT_FIFO, O_WRONLY)) >= 0) {
+ 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;
+ /* Validate time argument. */
+ for ( ; *sp; sp++) {
+ if (*sp != '+' && *sp != ':' && (*sp < '0' || *sp > '9'))
+ usage();
+ }
+ sp = when;
+ /* Decode shutdown time. */
+ if (when[0] == '+') sp++;
+ if (strchr(when, ':') == NULL) {
+ /* Time in minutes. */
+ wt = atoi(sp);
+ if (wt == 0 && sp[0] != '0') usage();
+ } else {
+ if (sscanf(when, "%d:%2d", &hours, &mins) != 2) usage();
+ /* Time in hh:mm format. */
+ if (when[0] == '+') {
+ /* Hours and minutes from now */
+ if (hours > 99999 || mins > 59) usage();
+ wt = (60*hours + mins);
+ if (wt < 0) usage();
+ } else {
+ /* Time of day */
+ 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 waiting 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 */
+}
Index: create-2.99-initctl-patch/sysvinit-2.99-new/src
===================================================================
--- create-2.99-initctl-patch/sysvinit-2.99-new/src (nonexistent)
+++ create-2.99-initctl-patch/sysvinit-2.99-new/src (revision 5)
Property changes on: create-2.99-initctl-patch/sysvinit-2.99-new/src
___________________________________________________________________
Added: svn:ignore
## -0,0 +1,73 ##
+
+# install dir
+dist
+
+# Target build dirs
+.a1x-newlib
+.a2x-newlib
+.at91sam7s-newlib
+
+.build-machine
+
+.a1x-glibc
+.a2x-glibc
+.h3-glibc
+.h5-glibc
+.i586-glibc
+.i686-glibc
+.imx6-glibc
+.jz47xx-glibc
+.makefile
+.am335x-glibc
+.omap543x-glibc
+.p5600-glibc
+.power8-glibc
+.power8le-glibc
+.power9-glibc
+.power9le-glibc
+.m1000-glibc
+.riscv64-glibc
+.rk328x-glibc
+.rk33xx-glibc
+.rk339x-glibc
+.s8xx-glibc
+.s9xx-glibc
+.x86_64-glibc
+
+# Hidden files (each file)
+.makefile
+.dist
+.rootfs
+
+# src & hw requires
+.src_requires
+.src_requires_depend
+.requires
+.requires_depend
+
+# Tarballs
+*.gz
+*.bz2
+*.lz
+*.xz
+*.tgz
+*.txz
+
+# Signatures
+*.asc
+*.sig
+*.sign
+*.sha1sum
+
+# Patches
+*.patch
+
+# Descriptions
+*.dsc
+*.txt
+
+# Default linux config files
+*.defconfig
+
+# backup copies
+*~
Index: create-2.99-initctl-patch/sysvinit-2.99-new
===================================================================
--- create-2.99-initctl-patch/sysvinit-2.99-new (nonexistent)
+++ create-2.99-initctl-patch/sysvinit-2.99-new (revision 5)
Property changes on: create-2.99-initctl-patch/sysvinit-2.99-new
___________________________________________________________________
Added: svn:ignore
## -0,0 +1,73 ##
+
+# install dir
+dist
+
+# Target build dirs
+.a1x-newlib
+.a2x-newlib
+.at91sam7s-newlib
+
+.build-machine
+
+.a1x-glibc
+.a2x-glibc
+.h3-glibc
+.h5-glibc
+.i586-glibc
+.i686-glibc
+.imx6-glibc
+.jz47xx-glibc
+.makefile
+.am335x-glibc
+.omap543x-glibc
+.p5600-glibc
+.power8-glibc
+.power8le-glibc
+.power9-glibc
+.power9le-glibc
+.m1000-glibc
+.riscv64-glibc
+.rk328x-glibc
+.rk33xx-glibc
+.rk339x-glibc
+.s8xx-glibc
+.s9xx-glibc
+.x86_64-glibc
+
+# Hidden files (each file)
+.makefile
+.dist
+.rootfs
+
+# src & hw requires
+.src_requires
+.src_requires_depend
+.requires
+.requires_depend
+
+# Tarballs
+*.gz
+*.bz2
+*.lz
+*.xz
+*.tgz
+*.txz
+
+# Signatures
+*.asc
+*.sig
+*.sign
+*.sha1sum
+
+# Patches
+*.patch
+
+# Descriptions
+*.dsc
+*.txt
+
+# Default linux config files
+*.defconfig
+
+# backup copies
+*~
Index: create-2.99-initctl-patch
===================================================================
--- create-2.99-initctl-patch (nonexistent)
+++ create-2.99-initctl-patch (revision 5)
Property changes on: create-2.99-initctl-patch
___________________________________________________________________
Added: svn:ignore
## -0,0 +1,73 ##
+
+# install dir
+dist
+
+# Target build dirs
+.a1x-newlib
+.a2x-newlib
+.at91sam7s-newlib
+
+.build-machine
+
+.a1x-glibc
+.a2x-glibc
+.h3-glibc
+.h5-glibc
+.i586-glibc
+.i686-glibc
+.imx6-glibc
+.jz47xx-glibc
+.makefile
+.am335x-glibc
+.omap543x-glibc
+.p5600-glibc
+.power8-glibc
+.power8le-glibc
+.power9-glibc
+.power9le-glibc
+.m1000-glibc
+.riscv64-glibc
+.rk328x-glibc
+.rk33xx-glibc
+.rk339x-glibc
+.s8xx-glibc
+.s9xx-glibc
+.x86_64-glibc
+
+# Hidden files (each file)
+.makefile
+.dist
+.rootfs
+
+# src & hw requires
+.src_requires
+.src_requires_depend
+.requires
+.requires_depend
+
+# Tarballs
+*.gz
+*.bz2
+*.lz
+*.xz
+*.tgz
+*.txz
+
+# Signatures
+*.asc
+*.sig
+*.sign
+*.sha1sum
+
+# Patches
+*.patch
+
+# Descriptions
+*.dsc
+*.txt
+
+# Default linux config files
+*.defconfig
+
+# backup copies
+*~
Index: create-2.99-paths-patch/create.patch.sh
===================================================================
--- create-2.99-paths-patch/create.patch.sh (nonexistent)
+++ create-2.99-paths-patch/create.patch.sh (revision 5)
@@ -0,0 +1,15 @@
+#!/bin/sh
+
+VERSION=2.99
+
+tar --files-from=file.list -xJvf ../sysvinit-$VERSION.tar.xz
+mv sysvinit-$VERSION sysvinit-$VERSION-orig
+
+cp -rf ./sysvinit-$VERSION-new ./sysvinit-$VERSION
+
+diff --unified -Nr sysvinit-$VERSION-orig sysvinit-$VERSION > sysvinit-$VERSION-paths.patch
+
+mv sysvinit-$VERSION-paths.patch ../patches
+
+rm -rf ./sysvinit-$VERSION
+rm -rf ./sysvinit-$VERSION-orig
Property changes on: create-2.99-paths-patch/create.patch.sh
___________________________________________________________________
Added: svn:executable
## -0,0 +1 ##
+*
\ No newline at end of property
Index: create-2.99-paths-patch/file.list
===================================================================
--- create-2.99-paths-patch/file.list (nonexistent)
+++ create-2.99-paths-patch/file.list (revision 5)
@@ -0,0 +1 @@
+sysvinit-2.99/src/paths.h
Index: create-2.99-paths-patch/sysvinit-2.99-new/src/paths.h
===================================================================
--- create-2.99-paths-patch/sysvinit-2.99-new/src/paths.h (nonexistent)
+++ create-2.99-paths-patch/sysvinit-2.99-new/src/paths.h (revision 5)
@@ -0,0 +1,52 @@
+/*
+ * 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 INITTABD "/etc/inittab.d" /* Location of inittab.d directory */
+#define INIT "/sbin/init" /* Location of init itself. */
+#define NOLOGIN "/etc/nologin" /* Stop user logging in. */
+#define FASTBOOT "/etc/fastboot" /* Enable fast boot. */
+#define FORCEFSCK "/etc/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
+
Index: create-2.99-paths-patch/sysvinit-2.99-new/src
===================================================================
--- create-2.99-paths-patch/sysvinit-2.99-new/src (nonexistent)
+++ create-2.99-paths-patch/sysvinit-2.99-new/src (revision 5)
Property changes on: create-2.99-paths-patch/sysvinit-2.99-new/src
___________________________________________________________________
Added: svn:ignore
## -0,0 +1,73 ##
+
+# install dir
+dist
+
+# Target build dirs
+.a1x-newlib
+.a2x-newlib
+.at91sam7s-newlib
+
+.build-machine
+
+.a1x-glibc
+.a2x-glibc
+.h3-glibc
+.h5-glibc
+.i586-glibc
+.i686-glibc
+.imx6-glibc
+.jz47xx-glibc
+.makefile
+.am335x-glibc
+.omap543x-glibc
+.p5600-glibc
+.power8-glibc
+.power8le-glibc
+.power9-glibc
+.power9le-glibc
+.m1000-glibc
+.riscv64-glibc
+.rk328x-glibc
+.rk33xx-glibc
+.rk339x-glibc
+.s8xx-glibc
+.s9xx-glibc
+.x86_64-glibc
+
+# Hidden files (each file)
+.makefile
+.dist
+.rootfs
+
+# src & hw requires
+.src_requires
+.src_requires_depend
+.requires
+.requires_depend
+
+# Tarballs
+*.gz
+*.bz2
+*.lz
+*.xz
+*.tgz
+*.txz
+
+# Signatures
+*.asc
+*.sig
+*.sign
+*.sha1sum
+
+# Patches
+*.patch
+
+# Descriptions
+*.dsc
+*.txt
+
+# Default linux config files
+*.defconfig
+
+# backup copies
+*~
Index: create-2.99-paths-patch/sysvinit-2.99-new
===================================================================
--- create-2.99-paths-patch/sysvinit-2.99-new (nonexistent)
+++ create-2.99-paths-patch/sysvinit-2.99-new (revision 5)
Property changes on: create-2.99-paths-patch/sysvinit-2.99-new
___________________________________________________________________
Added: svn:ignore
## -0,0 +1,73 ##
+
+# install dir
+dist
+
+# Target build dirs
+.a1x-newlib
+.a2x-newlib
+.at91sam7s-newlib
+
+.build-machine
+
+.a1x-glibc
+.a2x-glibc
+.h3-glibc
+.h5-glibc
+.i586-glibc
+.i686-glibc
+.imx6-glibc
+.jz47xx-glibc
+.makefile
+.am335x-glibc
+.omap543x-glibc
+.p5600-glibc
+.power8-glibc
+.power8le-glibc
+.power9-glibc
+.power9le-glibc
+.m1000-glibc
+.riscv64-glibc
+.rk328x-glibc
+.rk33xx-glibc
+.rk339x-glibc
+.s8xx-glibc
+.s9xx-glibc
+.x86_64-glibc
+
+# Hidden files (each file)
+.makefile
+.dist
+.rootfs
+
+# src & hw requires
+.src_requires
+.src_requires_depend
+.requires
+.requires_depend
+
+# Tarballs
+*.gz
+*.bz2
+*.lz
+*.xz
+*.tgz
+*.txz
+
+# Signatures
+*.asc
+*.sig
+*.sign
+*.sha1sum
+
+# Patches
+*.patch
+
+# Descriptions
+*.dsc
+*.txt
+
+# Default linux config files
+*.defconfig
+
+# backup copies
+*~
Index: create-2.99-paths-patch
===================================================================
--- create-2.99-paths-patch (nonexistent)
+++ create-2.99-paths-patch (revision 5)
Property changes on: create-2.99-paths-patch
___________________________________________________________________
Added: svn:ignore
## -0,0 +1,73 ##
+
+# install dir
+dist
+
+# Target build dirs
+.a1x-newlib
+.a2x-newlib
+.at91sam7s-newlib
+
+.build-machine
+
+.a1x-glibc
+.a2x-glibc
+.h3-glibc
+.h5-glibc
+.i586-glibc
+.i686-glibc
+.imx6-glibc
+.jz47xx-glibc
+.makefile
+.am335x-glibc
+.omap543x-glibc
+.p5600-glibc
+.power8-glibc
+.power8le-glibc
+.power9-glibc
+.power9le-glibc
+.m1000-glibc
+.riscv64-glibc
+.rk328x-glibc
+.rk33xx-glibc
+.rk339x-glibc
+.s8xx-glibc
+.s9xx-glibc
+.x86_64-glibc
+
+# Hidden files (each file)
+.makefile
+.dist
+.rootfs
+
+# src & hw requires
+.src_requires
+.src_requires_depend
+.requires
+.requires_depend
+
+# Tarballs
+*.gz
+*.bz2
+*.lz
+*.xz
+*.tgz
+*.txz
+
+# Signatures
+*.asc
+*.sig
+*.sign
+*.sha1sum
+
+# Patches
+*.patch
+
+# Descriptions
+*.dsc
+*.txt
+
+# Default linux config files
+*.defconfig
+
+# backup copies
+*~
Index: create-2.99-version-patch/create.patch.sh
===================================================================
--- create-2.99-version-patch/create.patch.sh (nonexistent)
+++ create-2.99-version-patch/create.patch.sh (revision 5)
@@ -0,0 +1,15 @@
+#!/bin/sh
+
+VERSION=2.99
+
+tar --files-from=file.list -xJvf ../sysvinit-$VERSION.tar.xz
+mv sysvinit-$VERSION sysvinit-$VERSION-orig
+
+cp -rf ./sysvinit-$VERSION-new ./sysvinit-$VERSION
+
+diff --unified -Nr sysvinit-$VERSION-orig sysvinit-$VERSION > sysvinit-$VERSION-version.patch
+
+mv sysvinit-$VERSION-version.patch ../patches
+
+rm -rf ./sysvinit-$VERSION
+rm -rf ./sysvinit-$VERSION-orig
Property changes on: create-2.99-version-patch/create.patch.sh
___________________________________________________________________
Added: svn:executable
## -0,0 +1 ##
+*
\ No newline at end of property
Index: create-2.99-version-patch/file.list
===================================================================
--- create-2.99-version-patch/file.list (nonexistent)
+++ create-2.99-version-patch/file.list (revision 5)
@@ -0,0 +1 @@
+sysvinit-2.99/src/init.c
Index: create-2.99-version-patch/sysvinit-2.99-new/src/init.c
===================================================================
--- create-2.99-version-patch/sysvinit-2.99-new/src/init.c (nonexistent)
+++ create-2.99-version-patch/sysvinit-2.99-new/src/init.c (revision 5)
@@ -0,0 +1,3188 @@
+/*
+ * Init A System-V Init Clone.
+ *
+ * Usage: /sbin/init
+ * init [0123456SsQqAaBbCc]
+ * telinit [0123456SsQqAaBbCc]
+ *
+ * Version: @(#)init.c 2.86 30-Jul-2004 miquels@cistron.nl
+ * Version: init.c 2.90 18-Jun-2018 jsmith@resonatingmedia.com
+ */
+
+/*
+Version information is not placed in the top-level Makefile by default
+*/
+#define VERSION "2.99"
+/*
+ * This file is part of the sysvinit suite,
+ * Copyright (C) 1991-2004 Miquel van Smoorenburg.
+ * Copyright (C) 2017-2019 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 Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <sys/wait.h>
+#ifdef __linux__
+#include <sys/kd.h>
+#endif
+#include <sys/resource.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <stdio.h>
+#include <time.h>
+#include <fcntl.h>
+#include <string.h>
+#include <signal.h>
+#include <termios.h>
+#ifdef __FreeBSD__
+#include <utmpx.h>
+#else
+#include <utmp.h>
+#endif
+#include <ctype.h>
+#include <stdarg.h>
+#include <sys/ttydefaults.h>
+#include <sys/syslog.h>
+#include <sys/time.h>
+/*
+ * inittab.d
+ */
+#include <sys/types.h>
+#include <dirent.h>
+
+#ifdef WITH_SELINUX
+# include <selinux/selinux.h>
+#endif
+#ifdef __FreeBSD__
+extern char **environ;
+#endif
+
+#ifdef __i386__
+# ifdef __GLIBC__
+ /* GNU libc 2.x */
+# define STACK_DEBUG 1
+# if (__GLIBC__ == 2 && __GLIBC_MINOR__ == 0)
+ /* Only glibc 2.0 needs this */
+# include <sigcontext.h>
+# elif ( __GLIBC__ > 2) && ((__GLIBC__ == 2) && (__GLIBC_MINOR__ >= 1))
+# include <bits/sigcontext.h>
+# endif
+# endif
+#endif
+
+#include "init.h"
+#include "initreq.h"
+#include "paths.h"
+#include "reboot.h"
+#include "runlevellog.h"
+#include "set.h"
+
+#ifndef SIGPWR
+# define SIGPWR SIGUSR2
+#endif
+
+#ifndef CBAUD
+# define CBAUD 0
+#endif
+#ifndef CBAUDEX
+# define CBAUDEX 0
+#endif
+
+/* Set a signal handler. */
+#define SETSIG(sa, sig, fun, flags) \
+ do { \
+ memset(&sa, 0, sizeof(sa)); \
+ sa.sa_handler = fun; \
+ sa.sa_flags = flags; \
+ sigemptyset(&sa.sa_mask); \
+ sigaction(sig, &sa, NULL); \
+ } while(0)
+
+/* Version information */
+char *Version = "@(#) init " VERSION " miquels@cistron.nl";
+char *bootmsg = "version " VERSION " %s";
+#define E_VERSION "INIT_VERSION=sysvinit-" VERSION
+
+CHILD *family = NULL; /* The linked list of all entries */
+CHILD *newFamily = NULL; /* The list after inittab re-read */
+
+CHILD ch_emerg = { /* Emergency shell */
+ WAITING, 0, 0, 0, 0,
+ "~~",
+ "S",
+ 3,
+ "/sbin/sulogin",
+ NULL,
+ NULL
+};
+
+char runlevel = 'S'; /* The current run level */
+char thislevel = 'S'; /* The current runlevel */
+char prevlevel = 'N'; /* Previous runlevel */
+int dfl_level = 0; /* Default runlevel */
+sig_atomic_t got_cont = 0; /* Set if we received the SIGCONT signal */
+sig_atomic_t got_signals; /* Set if we received a signal. */
+int emerg_shell = 0; /* Start emergency shell? */
+int wrote_wtmp_reboot = 1; /* Set when we wrote the reboot record */
+int wrote_utmp_reboot = 1; /* Set when we wrote the reboot record */
+int wrote_wtmp_rlevel = 1; /* Set when we wrote the runlevel record */
+int wrote_utmp_rlevel = 1; /* Set when we wrote the runlevel record */
+int sleep_time = WAIT_BETWEEN_SIGNALS; /* Sleep time between TERM and KILL */
+char *argv0; /* First arguments; show up in ps listing */
+int maxproclen; /* Maximal length of argv[0] with \0 */
+struct utmp utproto; /* Only used for sizeof(utproto.ut_id) */
+char *console_dev; /* Console device. */
+int pipe_fd = -1; /* /run/initctl */
+int did_boot = 0; /* Did we already do BOOT* stuff? */
+int main(int, char **);
+
+/* Used by re-exec part */
+int reload = 0; /* Should we do initialization stuff? */
+char *myname=INIT; /* What should we exec */
+int oops_error; /* Used by some of the re-exec code. */
+const char *Signature = "12567362"; /* Signature for re-exec fd */
+
+/* Macro to see if this is a special action */
+#define ISPOWER(i) ((i) == POWERWAIT || (i) == POWERFAIL || \
+ (i) == POWEROKWAIT || (i) == POWERFAILNOW || \
+ (i) == CTRLALTDEL)
+
+/* ascii values for the `action' field. */
+struct actions {
+ char *name;
+ int act;
+} actions[] = {
+ { "respawn", RESPAWN },
+ { "wait", WAIT },
+ { "once", ONCE },
+ { "boot", BOOT },
+ { "bootwait", BOOTWAIT },
+ { "powerfail", POWERFAIL },
+ { "powerfailnow",POWERFAILNOW },
+ { "powerwait", POWERWAIT },
+ { "powerokwait", POWEROKWAIT },
+ { "ctrlaltdel", CTRLALTDEL },
+ { "off", OFF },
+ { "ondemand", ONDEMAND },
+ { "initdefault", INITDEFAULT },
+ { "sysinit", SYSINIT },
+ { "kbrequest", KBREQUEST },
+ { NULL, 0 },
+};
+
+/*
+ * State parser token table (see receive_state)
+ */
+struct {
+ char name[4];
+ int cmd;
+} cmds[] = {
+ { "VER", C_VER },
+ { "END", C_END },
+ { "REC", C_REC },
+ { "EOR", C_EOR },
+ { "LEV", C_LEV },
+ { "FL ", C_FLAG },
+ { "AC ", C_ACTION },
+ { "CMD", C_PROCESS },
+ { "PID", C_PID },
+ { "EXS", C_EXS },
+ { "-RL", D_RUNLEVEL },
+ { "-TL", D_THISLEVEL },
+ { "-PL", D_PREVLEVEL },
+ { "-SI", D_GOTSIGN },
+ { "-WR", D_WROTE_WTMP_REBOOT},
+ { "-WU", D_WROTE_UTMP_REBOOT},
+ { "-ST", D_SLTIME },
+ { "-DB", D_DIDBOOT },
+ { "-LW", D_WROTE_WTMP_RLEVEL},
+ { "-LU", D_WROTE_UTMP_RLEVEL},
+ { "", 0 }
+};
+struct {
+ char *name;
+ int mask;
+} flags[]={
+ {"RU",RUNNING},
+ {"DE",DEMAND},
+ {"XD",XECUTED},
+ {"WT",WAITING},
+ {NULL,0}
+};
+
+#define NR_EXTRA_ENV 16
+char *extra_env[NR_EXTRA_ENV];
+
+#define MINI_SLEEP 10 /* ten milliseconds */
+#define SHORT_SLEEP 5000 /* five seconds */
+#define LONG_SLEEP 30000 /* 30 seconds sleep to deal with memory issues*/
+
+/*
+ * Sleep a number of milliseconds.
+ *
+ * This only works correctly because Linux select updates
+ * the elapsed time in the struct timeval passed to select!
+ */
+static
+void do_msleep(int msec)
+{
+ struct timeval tv;
+
+ tv.tv_sec = msec / 1000;
+ tv.tv_usec = (msec % 1000) * 1000;
+
+ while(select(0, NULL, NULL, NULL, &tv) < 0 && errno == EINTR)
+ ;
+}
+
+
+/*
+ * Non-failing allocation routines (init cannot fail).
+ */
+static
+void *imalloc(size_t size)
+{
+ void *m;
+
+ while ((m = malloc(size)) == NULL) {
+ initlog(L_VB, "out of memory");
+ do_msleep(SHORT_SLEEP);
+ }
+ memset(m, 0, size);
+ return m;
+}
+
+static
+char *istrdup(const char *s)
+{
+ char *m;
+ int l;
+
+ l = strlen(s) + 1;
+ m = imalloc(l);
+ memcpy(m, s, l);
+ return m;
+}
+
+
+/*
+ * Send the state info of the previous running init to
+ * the new one, in a version-independant way.
+ */
+static
+void send_state(int fd)
+{
+ FILE *fp;
+ CHILD *p;
+ int i,val;
+
+ fp = fdopen(fd,"w");
+
+ fprintf(fp, "VER%s\n", Version);
+ fprintf(fp, "-RL%c\n", runlevel);
+ fprintf(fp, "-TL%c\n", thislevel);
+ fprintf(fp, "-PL%c\n", prevlevel);
+ fprintf(fp, "-SI%u\n", got_signals);
+ fprintf(fp, "-WR%d\n", wrote_wtmp_reboot);
+ fprintf(fp, "-WU%d\n", wrote_utmp_reboot);
+ fprintf(fp, "-ST%d\n", sleep_time);
+ fprintf(fp, "-DB%d\n", did_boot);
+
+ for (p = family; p; p = p->next) {
+ fprintf(fp, "REC%s\n", p->id);
+ fprintf(fp, "LEV%s\n", p->rlevel);
+ for (i = 0, val = p->flags; flags[i].mask; i++)
+ if (val & flags[i].mask) {
+ val &= ~flags[i].mask;
+ fprintf(fp, "FL %s\n",flags[i].name);
+ }
+ fprintf(fp, "PID%d\n",p->pid);
+ fprintf(fp, "EXS%u\n",p->exstat);
+ for(i = 0; actions[i].act; i++)
+ if (actions[i].act == p->action) {
+ fprintf(fp, "AC %s\n", actions[i].name);
+ break;
+ }
+ fprintf(fp, "CMD%s\n", p->process);
+ fprintf(fp, "EOR\n");
+ }
+ fprintf(fp, "END\n");
+ fclose(fp);
+}
+
+/*
+ * Read a string from a file descriptor.
+ * Q: why not use fgets() ?
+ * A: Answer: looked into this. Turns out after all the checks
+ * required in the fgets() approach (removing newline, read errors, etc)
+ * the function is longer and takes approximately the same amount of
+ * time to do one big fgets and checks as it does to do a pile of getcs.
+ * We don't benefit from switching.
+ * - Jesse
+ */
+static int get_string(char *p, int size, FILE *f)
+{
+ int c;
+
+ while ((c = getc(f)) != EOF && c != '\n') {
+ if (--size > 0)
+ *p++ = c;
+ }
+ *p = '\0';
+ return (c != EOF) && (size > 0);
+}
+
+/*
+ * Read trailing data from the state pipe until we see a newline.
+ */
+static int get_void(FILE *f)
+{
+ int c;
+
+ while ((c = getc(f)) != EOF && c != '\n')
+ ;
+
+ return (c != EOF);
+}
+
+/*
+ * Read the next "command" from the state pipe.
+ */
+static int get_cmd(FILE *f)
+{
+ char cmd[4] = " ";
+ int i;
+
+ if (fread(cmd, 1, sizeof(cmd) - 1, f) != sizeof(cmd) - 1)
+ return C_EOF;
+
+ for(i = 0; cmds[i].cmd && strcmp(cmds[i].name, cmd) != 0; i++)
+ ;
+ return cmds[i].cmd;
+}
+
+/*
+ * Read a CHILD * from the state pipe.
+ */
+static CHILD *get_record(FILE *f)
+{
+ int cmd;
+ char s[32];
+ int i;
+ CHILD *p;
+
+ do {
+ errno = 0;
+ switch (cmd = get_cmd(f)) {
+ case C_END:
+ get_void(f);
+ return NULL;
+ case 0:
+ get_void(f);
+ break;
+ case C_REC:
+ break;
+ case D_RUNLEVEL:
+ if (fscanf(f, "%c\n", &runlevel) == EOF && errno != 0) {
+ fprintf(stderr, "%s (%d): %s\n", __FILE__, __LINE__, strerror(errno));
+ }
+ break;
+ case D_THISLEVEL:
+ if (fscanf(f, "%c\n", &thislevel) == EOF && errno != 0) {
+ fprintf(stderr, "%s (%d): %s\n", __FILE__, __LINE__, strerror(errno));
+ }
+ break;
+ case D_PREVLEVEL:
+ if (fscanf(f, "%c\n", &prevlevel) == EOF && errno != 0) {
+ fprintf(stderr, "%s (%d): %s\n", __FILE__, __LINE__, strerror(errno));
+ }
+ break;
+ case D_GOTSIGN:
+ if (fscanf(f, "%u\n", &got_signals) == EOF && errno != 0) {
+ fprintf(stderr, "%s (%d): %s\n", __FILE__, __LINE__, strerror(errno));
+ }
+ break;
+ case D_WROTE_WTMP_REBOOT:
+ if (fscanf(f, "%d\n", &wrote_wtmp_reboot) == EOF && errno != 0) {
+ fprintf(stderr, "%s (%d): %s\n", __FILE__, __LINE__, strerror(errno));
+ }
+ break;
+ case D_WROTE_UTMP_REBOOT:
+ if (fscanf(f, "%d\n", &wrote_utmp_reboot) == EOF && errno != 0) {
+ fprintf(stderr, "%s (%d): %s\n", __FILE__, __LINE__, strerror(errno));
+ }
+ break;
+ case D_SLTIME:
+ if (fscanf(f, "%d\n", &sleep_time) == EOF && errno != 0) {
+ fprintf(stderr, "%s (%d): %s\n", __FILE__, __LINE__, strerror(errno));
+ }
+ break;
+ case D_DIDBOOT:
+ if (fscanf(f, "%d\n", &did_boot) == EOF && errno != 0) {
+ fprintf(stderr, "%s (%d): %s\n", __FILE__, __LINE__, strerror(errno));
+ }
+ break;
+ case D_WROTE_WTMP_RLEVEL:
+ if (fscanf(f, "%d\n", &wrote_wtmp_rlevel) == EOF && errno != 0) {
+ fprintf(stderr, "%s (%d): %s\n", __FILE__, __LINE__, strerror(errno));
+ }
+ break;
+ case D_WROTE_UTMP_RLEVEL:
+ if (fscanf(f, "%d\n", &wrote_utmp_rlevel) == EOF && errno != 0) {
+ fprintf(stderr, "%s (%d): %s\n", __FILE__, __LINE__, strerror(errno));
+ }
+ break;
+ default:
+ if (cmd > 0 || cmd == C_EOF) {
+ oops_error = -1;
+ return NULL;
+ }
+ }
+ } while (cmd != C_REC);
+
+ p = imalloc(sizeof(CHILD));
+ get_string(p->id, sizeof(p->id), f);
+
+ do switch(cmd = get_cmd(f)) {
+ case 0:
+ case C_EOR:
+ get_void(f);
+ break;
+ case C_PID:
+ if (fscanf(f, "%d\n", &(p->pid)) == EOF && errno != 0) {
+ fprintf(stderr, "%s (%d): %s\n", __FILE__, __LINE__, strerror(errno));
+ }
+ break;
+ case C_EXS:
+ if (fscanf(f, "%u\n", &(p->exstat)) == EOF && errno != 0) {
+ fprintf(stderr, "%s (%d): %s\n", __FILE__, __LINE__, strerror(errno));
+ }
+ break;
+ case C_LEV:
+ get_string(p->rlevel, sizeof(p->rlevel), f);
+ break;
+ case C_PROCESS:
+ get_string(p->process, sizeof(p->process), f);
+ break;
+ case C_FLAG:
+ get_string(s, sizeof(s), f);
+ for(i = 0; flags[i].name; i++) {
+ if (strcmp(flags[i].name,s) == 0)
+ break;
+ }
+ p->flags |= flags[i].mask;
+ break;
+ case C_ACTION:
+ get_string(s, sizeof(s), f);
+ for(i = 0; actions[i].name; i++) {
+ if (strcmp(actions[i].name, s) == 0)
+ break;
+ }
+ p->action = actions[i].act ? actions[i].act : OFF;
+ break;
+ default:
+ free(p);
+ oops_error = -1;
+ return NULL;
+ } while( cmd != C_EOR);
+
+ return p;
+}
+
+/*
+ * Read the complete state info from the state pipe.
+ * Returns 0 on success
+ */
+static
+int receive_state(int fd)
+{
+ FILE *f;
+ char old_version[256];
+ CHILD **pp;
+
+ f = fdopen(fd, "r");
+
+ if (get_cmd(f) != C_VER) {
+ fclose(f);
+ return -1;
+ }
+ get_string(old_version, sizeof(old_version), f);
+ oops_error = 0;
+ for (pp = &family; (*pp = get_record(f)) != NULL; pp = &((*pp)->next))
+ ;
+ fclose(f);
+ return oops_error;
+}
+
+/*
+ * Set the process title.
+ */
+#ifdef __GNUC__
+#ifndef __FreeBSD__
+__attribute__ ((format (printf, 1, 2)))
+#endif
+#endif
+/* This function already exists on FreeBSD. No need to declare it. */
+#ifndef __FreeBSD__
+static int setproctitle(char *fmt, ...)
+{
+ va_list ap;
+ int len;
+ char buf[256];
+
+ buf[0] = 0;
+
+ va_start(ap, fmt);
+ len = vsnprintf(buf, sizeof(buf), fmt, ap);
+ va_end(ap);
+
+ if (maxproclen > 1) {
+ memset(argv0, 0, maxproclen);
+ strncpy(argv0, buf, maxproclen - 1);
+ }
+
+ return len;
+}
+#endif
+
+/*
+ * Set console_dev to a working console.
+ */
+static
+void console_init(void)
+{
+ int fd;
+ int tried_devcons = 0;
+ int tried_vtmaster = 0;
+ char *s;
+
+ if ((s = getenv("CONSOLE")) != NULL)
+ console_dev = s;
+ else {
+ console_dev = CONSOLE;
+ tried_devcons++;
+ }
+
+ while ((fd = open(console_dev, O_RDONLY|O_NONBLOCK)) < 0) {
+ if (!tried_devcons) {
+ tried_devcons++;
+ console_dev = CONSOLE;
+ continue;
+ }
+ if (!tried_vtmaster) {
+ tried_vtmaster++;
+ console_dev = VT_MASTER;
+ continue;
+ }
+ break;
+ }
+ if (fd < 0)
+ console_dev = "/dev/null";
+ else
+ close(fd);
+}
+
+
+/*
+ * Open the console with retries.
+ */
+static
+int console_open(int mode)
+{
+ int f, fd = -1;
+ int m;
+
+ /*
+ * Open device in nonblocking mode.
+ */
+ m = mode | O_NONBLOCK;
+
+ /*
+ * Retry the open five times.
+ */
+ for(f = 0; f < 5; f++) {
+ if ((fd = open(console_dev, m)) >= 0) break;
+ usleep(10000);
+ }
+
+ if (fd < 0) return fd;
+
+ /*
+ * Set original flags.
+ */
+ if (m != mode)
+ fcntl(fd, F_SETFL, mode);
+ return fd;
+}
+
+/*
+ * We got a signal (HUP PWR WINCH ALRM INT)
+ */
+static
+void signal_handler(int sig)
+{
+ ADDSET(got_signals, sig);
+}
+
+/*
+ * SIGCHLD: one of our children has died.
+ */
+static
+# ifdef __GNUC__
+void chld_handler(int sig __attribute__((unused)))
+# else
+void chld_handler(int sig)
+# endif
+{
+ CHILD *ch;
+ int pid, st;
+ int saved_errno = errno;
+
+ /*
+ * Find out which process(es) this was (were)
+ */
+ while((pid = waitpid(-1, &st, WNOHANG)) != 0) {
+ if (errno == ECHILD) break;
+ for( ch = family; ch; ch = ch->next )
+ if ( ch->pid == pid && (ch->flags & RUNNING) ) {
+ INITDBG(L_VB,
+ "chld_handler: marked %d as zombie",
+ ch->pid);
+ ADDSET(got_signals, SIGCHLD);
+ ch->exstat = st;
+ ch->flags |= ZOMBIE;
+ if (ch->new) {
+ ch->new->exstat = st;
+ ch->new->flags |= ZOMBIE;
+ }
+ break;
+ }
+ if (ch == NULL) {
+ INITDBG(L_VB, "chld_handler: unknown child %d exited.",
+ pid);
+ }
+ }
+ errno = saved_errno;
+}
+
+/*
+ * Linux ignores all signals sent to init when the
+ * SIG_DFL handler is installed. Therefore we must catch SIGTSTP
+ * and SIGCONT, or else they won't work....
+ *
+ * The SIGCONT handler
+ */
+static
+# ifdef __GNUC__
+void cont_handler(int sig __attribute__((unused)))
+# else
+void cont_handler(int sig)
+# endif
+{
+ got_cont = 1;
+}
+
+/*
+ * Fork and dump core in /.
+ */
+static
+void coredump(void)
+{
+ static int dumped = 0;
+ struct rlimit rlim;
+ sigset_t mask;
+
+ if (dumped) return;
+ dumped = 1;
+
+ if (fork() != 0) return;
+
+ sigfillset(&mask);
+ sigprocmask(SIG_SETMASK, &mask, NULL);
+
+ rlim.rlim_cur = RLIM_INFINITY;
+ rlim.rlim_max = RLIM_INFINITY;
+ setrlimit(RLIMIT_CORE, &rlim);
+ if (0 != chdir("/"))
+ initlog(L_VB, "unable to chdir to /: %s",
+ strerror(errno));
+
+ signal(SIGSEGV, SIG_DFL);
+ raise(SIGSEGV);
+ sigdelset(&mask, SIGSEGV);
+ sigprocmask(SIG_SETMASK, &mask, NULL);
+
+ do_msleep(SHORT_SLEEP);
+ exit(0);
+}
+
+/*
+ * OOPS: segmentation violation!
+ * If we have the info, print where it occurred.
+ * Then sleep 30 seconds and try to continue.
+ */
+static
+#if defined(STACK_DEBUG) && defined(__linux__)
+# ifdef __GNUC__
+void segv_handler(int sig __attribute__((unused)), struct sigcontext ctx)
+# else
+void segv_handler(int sig, struct sigcontext ctx)
+# endif
+{
+ char *p = "";
+ int saved_errno = errno;
+
+ if ((void *)ctx.eip >= (void *)do_msleep &&
+ (void *)ctx.eip < (void *)main)
+ p = " (code)";
+ initlog(L_VB, "PANIC: segmentation violation at %p%s! "
+ "sleeping for 30 seconds.", (void *)ctx.eip, p);
+ coredump();
+ do_msleep(LONG_SLEEP);
+ errno = saved_errno;
+}
+#else
+# ifdef __GNUC__
+void segv_handler(int sig __attribute__((unused)))
+# else
+void segv_handler(int sig)
+# endif
+{
+ int saved_errno = errno;
+
+ initlog(L_VB,
+ "PANIC: segmentation violation! sleeping for 30 seconds.");
+ coredump();
+ do_msleep(LONG_SLEEP);
+ errno = saved_errno;
+}
+#endif
+
+/*
+ * The SIGSTOP & SIGTSTP handler
+ */
+static
+# ifdef __GNUC__
+void stop_handler(int sig __attribute__((unused)))
+# else
+void stop_handler(int sig)
+# endif
+{
+ int saved_errno = errno;
+
+ got_cont = 0;
+ while(!got_cont) pause();
+ got_cont = 0;
+ errno = saved_errno;
+}
+
+/*
+ * Set terminal settings to reasonable defaults
+ */
+static
+void console_stty(void)
+{
+ struct termios tty;
+ int fd;
+
+ if ((fd = console_open(O_RDWR|O_NOCTTY)) < 0) {
+ initlog(L_VB, "can't open %s", console_dev);
+ return;
+ }
+
+#ifdef __FreeBSD_kernel__
+ /*
+ * The kernel of FreeBSD expects userland to set TERM. Usually, we want
+ * "xterm". Later, gettys might disagree on this (i.e. we're not using
+ * syscons) but some boot scripts, like /etc/init.d/xserver-xorg, still
+ * need a non-dumb terminal.
+ */
+ putenv ("TERM=xterm");
+#endif
+
+ (void) tcgetattr(fd, &tty);
+
+ tty.c_cflag &= CBAUD|CBAUDEX|CSIZE|CSTOPB|PARENB|PARODD;
+ tty.c_cflag |= HUPCL|CLOCAL|CREAD;
+
+ tty.c_cc[VINTR] = CINTR;
+ tty.c_cc[VQUIT] = CQUIT;
+ tty.c_cc[VERASE] = CERASE; /* ASCII DEL (0177) */
+ tty.c_cc[VKILL] = CKILL;
+ tty.c_cc[VEOF] = CEOF;
+ tty.c_cc[VTIME] = 0;
+ tty.c_cc[VMIN] = 1;
+#ifdef VSWTC /* not defined on FreeBSD */
+ tty.c_cc[VSWTC] = _POSIX_VDISABLE;
+#endif /* VSWTC */
+ tty.c_cc[VSTART] = CSTART;
+ tty.c_cc[VSTOP] = CSTOP;
+ tty.c_cc[VSUSP] = CSUSP;
+ tty.c_cc[VEOL] = _POSIX_VDISABLE;
+ tty.c_cc[VREPRINT] = CREPRINT;
+ tty.c_cc[VDISCARD] = CDISCARD;
+ tty.c_cc[VWERASE] = CWERASE;
+ tty.c_cc[VLNEXT] = CLNEXT;
+ tty.c_cc[VEOL2] = _POSIX_VDISABLE;
+
+ /*
+ * Set pre and post processing
+ */
+ tty.c_iflag = IGNPAR|ICRNL|IXON|IXANY
+#ifdef IUTF8 /* Not defined on FreeBSD */
+ | (tty.c_iflag & IUTF8)
+#endif /* IUTF8 */
+ ;
+ tty.c_oflag = OPOST|ONLCR;
+ tty.c_lflag = ISIG|ICANON|ECHO|ECHOCTL|ECHOE|ECHOKE;
+
+#if defined(SANE_TIO) && (SANE_TIO == 1)
+ /*
+ * Disable flow control (-ixon), ignore break (ignbrk),
+ * and make nl/cr more usable (sane).
+ */
+ tty.c_iflag |= IGNBRK;
+ tty.c_iflag &= ~(BRKINT|INLCR|IGNCR|IXON);
+ tty.c_oflag &= ~(OCRNL|ONLRET);
+#endif
+ /*
+ * Now set the terminal line.
+ * We don't care about non-transmitted output data
+ * and non-read input data.
+ */
+ (void) tcsetattr(fd, TCSANOW, &tty);
+ (void) tcflush(fd, TCIOFLUSH);
+ (void) close(fd);
+}
+
+static ssize_t
+safe_write(int fd, const char *buffer, size_t count)
+{
+ ssize_t offset = 0;
+
+ while (count > 0) {
+ ssize_t block = write(fd, &buffer[offset], count);
+
+ if (block < 0 && errno == EINTR)
+ continue;
+ if (block <= 0)
+ return offset ? offset : block;
+ offset += block;
+ count -= block;
+ }
+ return offset;
+}
+
+/*
+ * Print to the system console
+ */
+void print(char *s)
+{
+ int fd;
+
+ if ((fd = console_open(O_WRONLY|O_NOCTTY|O_NDELAY)) >= 0) {
+ safe_write(fd, s, strlen(s));
+ close(fd);
+ }
+}
+
+/*
+ * Log something to a logfile and the console.
+ */
+#ifdef __GNUC__
+__attribute__ ((format (printf, 2, 3)))
+#endif
+void initlog(int loglevel, char *s, ...)
+{
+ va_list va_alist;
+ char buf[256];
+ sigset_t nmask, omask;
+
+ va_start(va_alist, s);
+ vsnprintf(buf, sizeof(buf), s, va_alist);
+ va_end(va_alist);
+
+ if (loglevel & L_SY) {
+ /*
+ * Re-establish connection with syslogd every time.
+ * Block signals while talking to syslog.
+ */
+ sigfillset(&nmask);
+ sigprocmask(SIG_BLOCK, &nmask, &omask);
+ openlog("init", 0, LOG_DAEMON);
+ syslog(LOG_INFO, "%s", buf);
+ closelog();
+ sigprocmask(SIG_SETMASK, &omask, NULL);
+ }
+
+ /*
+ * And log to the console.
+ */
+ if (loglevel & L_CO) {
+ print("\rINIT: ");
+ print(buf);
+ print("\r\n");
+ }
+}
+
+/*
+ * Add or replace specific environment value
+ */
+int addnewenv(const char *new, char **curr, int n)
+{
+ size_t nlen = strcspn(new, "=");
+ int i;
+ for (i = 0; i < n; i++) {
+ if (nlen != strcspn(curr[i], "="))
+ continue;
+ if (strncmp (new, curr[i], nlen) == 0)
+ break;
+ }
+ if (i >= n)
+ curr[n++] = istrdup(new);
+ else {
+ free(curr[i]);
+ curr[i] = istrdup(new);
+ }
+ return n;
+}
+
+/*
+ * Build a new environment for execve().
+ */
+char **init_buildenv(int child)
+{
+ char i_lvl[] = "RUNLEVEL=x";
+ char i_prev[] = "PREVLEVEL=x";
+ char i_cons[128];
+ char i_shell[] = "SHELL=" SHELL;
+ char **e;
+ int n, i;
+
+ for (n = 0; environ[n]; n++)
+ ;
+ n += NR_EXTRA_ENV + 1; /* Also room for last NULL */
+ if (child)
+ n += 8;
+
+ while ((e = (char**)calloc(n, sizeof(char *))) == NULL) {
+ initlog(L_VB, "out of memory");
+ do_msleep(SHORT_SLEEP);
+ }
+
+ for (n = 0; environ[n]; n++)
+ e[n] = istrdup(environ[n]);
+
+ for (i = 0; i < NR_EXTRA_ENV; i++) {
+ if (extra_env[i] == NULL || *extra_env[i] == '\0')
+ continue;
+ n = addnewenv(extra_env[i], e, n);
+ }
+
+ if (child) {
+ snprintf(i_cons, sizeof(i_cons), "CONSOLE=%s", console_dev);
+ i_lvl[9] = thislevel;
+ i_prev[10] = prevlevel;
+ n = addnewenv(i_shell, e, n);
+ n = addnewenv(i_lvl, e, n);
+ n = addnewenv(i_prev, e, n);
+ n = addnewenv(i_cons, e, n);
+ n = addnewenv(E_VERSION, e, n);
+ }
+
+ e[n++] = NULL;
+
+ return e;
+}
+
+
+void init_freeenv(char **e)
+{
+ int n;
+
+ for (n = 0; e[n]; n++)
+ free(e[n]);
+ free(e);
+}
+
+
+/*
+ * Fork and execute.
+ *
+ * This function is too long and indents too deep.
+ *
+ */
+static
+pid_t spawn(CHILD *ch, int *res)
+{
+ char *args[16]; /* Argv array */
+ char buf[136]; /* Line buffer */
+ int f, st; /* Scratch variables */
+ char *ptr; /* Ditto */
+ time_t t; /* System time */
+ int oldAlarm; /* Previous alarm value */
+ char *proc = ch->process; /* Command line */
+ pid_t pid, pgrp; /* child, console process group. */
+ sigset_t nmask, omask; /* For blocking SIGCHLD */
+ struct sigaction sa;
+
+ *res = -1;
+ buf[sizeof(buf) - 1] = 0;
+
+ /* Skip '+' if it's there */
+ if (proc[0] == '+') proc++;
+
+ ch->flags |= XECUTED;
+
+ if (ch->action == RESPAWN || ch->action == ONDEMAND) {
+ /* Is the date stamp from less than 2 minutes ago? */
+ time(&t);
+ if (ch->tm + TESTTIME > t) {
+ ch->count++;
+ } else {
+ ch->count = 0;
+ ch->tm = t;
+ }
+
+ /* Do we try to respawn too fast? */
+ if (ch->count >= MAXSPAWN) {
+
+ initlog(L_VB,
+ "Id \"%s\" respawning too fast: disabled for %d minutes",
+ ch->id, SLEEPTIME / 60);
+ ch->flags &= ~RUNNING;
+ ch->flags |= FAILING;
+
+ /* Remember the time we stopped */
+ ch->tm = t;
+
+ /* Try again in 5 minutes */
+ oldAlarm = alarm(0);
+ if (oldAlarm > SLEEPTIME || oldAlarm <= 0) oldAlarm = SLEEPTIME;
+ alarm(oldAlarm);
+ return(-1);
+ }
+ }
+
+ /* See if there is an "initscript" (except in single user mode). */
+ if (access(INITSCRIPT, R_OK) == 0 && runlevel != 'S') {
+ /* Build command line using "initscript" */
+ args[1] = SHELL;
+ args[2] = INITSCRIPT;
+ args[3] = ch->id;
+ args[4] = ch->rlevel;
+ args[5] = "unknown";
+ for(f = 0; actions[f].name; f++) {
+ if (ch->action == actions[f].act) {
+ args[5] = actions[f].name;
+ break;
+ }
+ }
+ args[6] = proc;
+ args[7] = NULL;
+ } else if (strpbrk(proc, "~`!$^&*()=|\\{}[];\"'<>?")) {
+ /* See if we need to fire off a shell for this command */
+ /* Give command line to shell */
+ args[1] = SHELL;
+ args[2] = "-c";
+ strcpy(buf, "exec ");
+ strncat(buf, proc, sizeof(buf) - strlen(buf) - 1);
+ args[3] = buf;
+ args[4] = NULL;
+ } else {
+ /* Split up command line arguments */
+ buf[0] = 0;
+ strncat(buf, proc, sizeof(buf) - 1);
+ ptr = buf;
+ for(f = 1; f < 15; f++) {
+ /* Skip white space */
+ while(*ptr == ' ' || *ptr == '\t') ptr++;
+ args[f] = ptr;
+
+ /* May be trailing space.. */
+ if (*ptr == 0) break;
+
+ /* Skip this `word' */
+ while(*ptr && *ptr != ' ' && *ptr != '\t' && *ptr != '#')
+ ptr++;
+
+ /* If end-of-line, break */
+ if (*ptr == '#' || *ptr == 0) {
+ f++;
+ *ptr = 0;
+ break;
+ }
+ /* End word with \0 and continue */
+ *ptr++ = 0;
+ }
+ args[f] = NULL;
+ }
+ args[0] = args[1];
+ while(1) {
+ /*
+ * Block sigchild while forking.
+ */
+ sigemptyset(&nmask);
+ sigaddset(&nmask, SIGCHLD);
+ sigprocmask(SIG_BLOCK, &nmask, &omask);
+
+ if ((pid = fork()) == 0) {
+
+ close(0);
+ close(1);
+ close(2);
+ if (pipe_fd >= 0)
+ {
+ close(pipe_fd);
+ pipe_fd = -1;
+ }
+
+ sigprocmask(SIG_SETMASK, &omask, NULL);
+
+ /*
+ * In sysinit, boot, bootwait or single user mode:
+ * for any wait-type subprocess we _force_ the console
+ * to be its controlling tty.
+ */
+ if (strchr("*#sS", runlevel) && ch->flags & WAITING) {
+ int ftty; /* Handler for tty controlling */
+ /*
+ * We fork once extra. This is so that we can
+ * wait and change the process group and session
+ * of the console after exit of the leader.
+ */
+ setsid();
+ if ((ftty = console_open(O_RDWR|O_NOCTTY)) >= 0) {
+ /* Take over controlling tty by force */
+ (void)ioctl(ftty, TIOCSCTTY, 1);
+
+ if(dup(ftty) < 0){
+ initlog(L_VB, "cannot duplicate console fd");
+ }
+
+ if(dup(ftty) < 0){
+ initlog(L_VB, "cannot duplicate console fd");
+ }
+
+ }
+
+ /*
+ * 4 Sep 2001, Andrea Arcangeli:
+ * Fix a race in spawn() that is used to deadlock init in a
+ * waitpid() loop: must set the childhandler as default before forking
+ * off the child or the chld_handler could run before the waitpid loop
+ * has a chance to find its zombie-child.
+ */
+ SETSIG(sa, SIGCHLD, SIG_DFL, SA_RESTART);
+ if ((pid = fork()) < 0) {
+ initlog(L_VB, "cannot fork: %s",
+ strerror(errno));
+ exit(1);
+ }
+ if (pid > 0) {
+ pid_t rc;
+ /*
+ * Ignore keyboard signals etc.
+ * Then wait for child to exit.
+ */
+ SETSIG(sa, SIGINT, SIG_IGN, SA_RESTART);
+ SETSIG(sa, SIGTSTP, SIG_IGN, SA_RESTART);
+ SETSIG(sa, SIGQUIT, SIG_IGN, SA_RESTART);
+
+ while ((rc = waitpid(pid, &st, 0)) != pid)
+ if (rc < 0 && errno == ECHILD)
+ break;
+
+ /*
+ * Small optimization. See if stealing
+ * controlling tty back is needed.
+ */
+ pgrp = tcgetpgrp(ftty);
+ if (pgrp != getpid())
+ exit(0);
+
+ /*
+ * Steal controlling tty away. We do
+ * this with a temporary process.
+ */
+ if ((pid = fork()) < 0) {
+ initlog(L_VB, "cannot fork: %s",
+ strerror(errno));
+ exit(1);
+ }
+ if (pid == 0) {
+ setsid();
+ (void)ioctl(ftty, TIOCSCTTY, 1);
+ exit(0);
+ }
+ while((rc = waitpid(pid, &st, 0)) != pid)
+ if (rc < 0 && errno == ECHILD)
+ break;
+ exit(0);
+ }
+
+ /* Set ioctl settings to default ones */
+ console_stty();
+
+ } else { /* parent */
+ int fd;
+ setsid();
+ if ((fd = console_open(O_RDWR|O_NOCTTY)) < 0) {
+ initlog(L_VB, "open(%s): %s", console_dev,
+ strerror(errno));
+ fd = open("/dev/null", O_RDWR);
+ }
+
+ if(dup(fd) < 0) {
+ initlog(L_VB, "cannot duplicate /dev/null fd");
+ }
+
+ if(dup(fd) < 0) {
+ initlog(L_VB, "cannot duplicate /dev/null fd");
+ }
+
+ }
+
+ /*
+ * Update utmp/wtmp file prior to starting
+ * any child. This MUST be done right here in
+ * the child process in order to prevent a race
+ * condition that occurs when the child
+ * process' time slice executes before the
+ * parent (can and does happen in a uniprocessor
+ * environment). If the child is a getty and
+ * the race condition happens, then init's utmp
+ * update will happen AFTER the getty runs
+ * and expects utmp to be updated already!
+ *
+ * Do NOT log if process field starts with '+'
+ * This is for compatibility with *very*
+ * old getties - probably it can be taken out.
+ */
+ if (ch->process[0] != '+')
+ write_utmp_wtmp("", ch->id, getpid(), INIT_PROCESS, "");
+
+ /* Reset all the signals, set up environment */
+ for(f = 1; f < NSIG; f++) SETSIG(sa, f, SIG_DFL, SA_RESTART);
+ environ = init_buildenv(1);
+
+ /*
+ * Execute prog. In case of ENOEXEC try again
+ * as a shell script.
+ */
+ execvp(args[1], args + 1);
+ if (errno == ENOEXEC) {
+ args[1] = SHELL;
+ args[2] = "-c";
+ strcpy(buf, "exec ");
+ strncat(buf, proc, sizeof(buf) - strlen(buf) - 1);
+ args[3] = buf;
+ args[4] = NULL;
+ execvp(args[1], args + 1);
+ }
+ initlog(L_VB, "cannot execute \"%s\"", args[1]);
+
+ if (ch->process[0] != '+')
+ write_utmp_wtmp("", ch->id, getpid(), DEAD_PROCESS, NULL);
+ exit(1);
+ }
+ *res = pid;
+ sigprocmask(SIG_SETMASK, &omask, NULL);
+
+ INITDBG(L_VB, "Started id %s (pid %d)", ch->id, pid);
+
+ if (pid == -1) {
+ initlog(L_VB, "cannot fork, retry..");
+ do_msleep(SHORT_SLEEP);
+ continue;
+ }
+ return(pid);
+ }
+}
+
+/*
+ * Start a child running!
+ */
+static
+void startup(CHILD *ch)
+{
+ /*
+ * See if it's disabled
+ */
+ if (ch->flags & FAILING) return;
+
+ switch(ch->action) {
+
+ case SYSINIT:
+ case BOOTWAIT:
+ case WAIT:
+ case POWERWAIT:
+ case POWERFAILNOW:
+ case POWEROKWAIT:
+ case CTRLALTDEL:
+ if (!(ch->flags & XECUTED)) ch->flags |= WAITING;
+ /* Fall through */
+ case KBREQUEST:
+ case BOOT:
+ case POWERFAIL:
+ case ONCE:
+ if (ch->flags & XECUTED) break;
+ /* Fall through */
+ case ONDEMAND:
+ case RESPAWN:
+ ch->flags |= RUNNING;
+ (void)spawn(ch, &(ch->pid));
+ break;
+ }
+}
+
+#ifdef __linux__
+static
+void check_kernel_console()
+{
+ FILE* fp;
+ char buf[4096];
+ if ((fp = fopen("/proc/cmdline", "r")) == 0) {
+ return;
+ }
+ if (fgets(buf, sizeof(buf), fp)) {
+ char* p = buf;
+ if ( strstr(p, "init.autocon=1") )
+ {
+ while ((p = strstr(p, "console="))) {
+ char* e;
+ p += strlen("console=");
+ for (e = p; *e; ++e) {
+ switch (*e) {
+ case '-' ... '9':
+ case 'A' ... 'Z':
+ case '_':
+ case 'a' ... 'z':
+ continue;
+ }
+ break;
+ }
+ if (p != e) {
+ CHILD* old;
+ int dup = 0;
+ char id[8] = {0};
+ char dev[32] = {0};
+ strncpy(dev, p, MIN(sizeof(dev), (unsigned)(e-p)));
+ if (!strncmp(dev, "tty", 3))
+ strncpy(id, dev+3, sizeof(id));
+ else
+ strncpy(id, dev, sizeof(id));
+
+ for(old = newFamily; old; old = old->next) {
+ if (!strcmp(old->id, id)) {
+ dup = 1;
+ }
+ }
+ if (!dup) {
+ CHILD* ch = imalloc(sizeof(CHILD));
+ ch->action = RESPAWN;
+ strcpy(ch->id, id);
+ strcpy(ch->rlevel, "2345");
+ sprintf(ch->process, "/sbin/agetty -L -s 115200,38400,9600 %s vt102", dev);
+ ch->next = NULL;
+ for(old = family; old; old = old->next) {
+ if (strcmp(old->id, ch->id) == 0) {
+ old->new = ch;
+ break;
+ }
+ }
+ /* add to end */
+ for(old = newFamily; old; old = old->next) {
+ if (!old->next) {
+ old->next = ch;
+ break;
+ }
+ }
+
+ initlog(L_VB, "added agetty on %s with id %s", dev, id);
+ }
+ }
+ }
+ }
+ }
+ fclose(fp);
+ return;
+}
+#endif
+
+/*
+ * Read the inittab file.
+ */
+static
+void read_inittab(void)
+{
+ FILE *fp; /* The INITTAB file */
+ FILE *fp_tab; /* The INITTABD files */
+ CHILD *ch, *old, *i; /* Pointers to CHILD structure */
+ CHILD *head = NULL; /* Head of linked list */
+#ifdef INITLVL
+ struct stat st; /* To stat INITLVL */
+#endif
+ sigset_t nmask, omask; /* For blocking SIGCHLD. */
+ char buf[256]; /* Line buffer */
+ char err[64]; /* Error message. */
+ char *id, *rlevel,
+ *action, *process; /* Fields of a line */
+ char *p;
+ int lineNo = 0; /* Line number in INITTAB file */
+ int actionNo; /* Decoded action field */
+ int f; /* Counter */
+ int round; /* round 0 for SIGTERM, 1 for SIGKILL */
+ int foundOne = 0; /* No killing no sleep */
+ int talk; /* Talk to the user */
+ int done = -1; /* Ready yet? , 2 level : -1 nothing done, 0 inittab done, 1 inittab and inittab.d done */
+ DIR *tabdir=NULL; /* the INITTAB.D dir */
+ struct dirent *file_entry; /* inittab.d entry */
+ char f_name[272]; /* size d_name + strlen /etc/inittad.d/ */
+
+#if DEBUG
+ if (newFamily != NULL) {
+ INITDBG(L_VB, "PANIC newFamily != NULL");
+ exit(1);
+ }
+ INITDBG(L_VB, "Reading inittab");
+#endif
+
+ /*
+ * Open INITTAB and read line by line.
+ */
+ if ((fp = fopen(INITTAB, "r")) == NULL)
+ initlog(L_VB, "No inittab file found");
+
+ /*
+ * Open INITTAB.D directory
+ */
+ if( (tabdir = opendir(INITTABD))==NULL)
+ initlog(L_VB, "No inittab.d directory found");
+
+ while(done!=1) {
+ /*
+ * Add single user shell entry at the end.
+ */
+ if(done == -1) {
+ if (fp == NULL || fgets(buf, sizeof(buf), fp) == NULL) {
+ done = 0;
+ /*
+ * See if we have a single user entry.
+ */
+ for(old = newFamily; old; old = old->next)
+ if (strpbrk(old->rlevel, "S")) break;
+ if (old == NULL)
+ snprintf(buf, sizeof(buf), "~~:S:wait:%s\n", SULOGIN);
+ else
+ continue;
+ }
+ } /* end if( done==-1) */
+ else if ( done == 0 ){
+ /* parse /etc/inittab.d and read all .tab files */
+ if(tabdir!=NULL){
+ if( (file_entry = readdir(tabdir))!=NULL){
+ /* ignore files not like *.tab */
+ if (!strcmp(file_entry->d_name, ".") || !strcmp(file_entry->d_name, ".."))
+ continue;
+ if (strlen(file_entry->d_name) < 5 || strcmp(file_entry->d_name + strlen(file_entry->d_name) - 4, ".tab"))
+ continue;
+ /*
+ * initialize filename
+ */
+ memset(f_name,0,sizeof(char)*272);
+ snprintf(f_name,272,"/etc/inittab.d/%s",file_entry->d_name);
+ initlog(L_VB, "Reading: %s",f_name);
+ /*
+ * read file in inittab.d only one entry per file
+ */
+ if ((fp_tab = fopen(f_name, "r")) == NULL)
+ continue;
+ /* read the file while the line contain comment */
+ while( fgets(buf, sizeof(buf), fp_tab) != NULL) {
+ for(p = buf; *p == ' ' || *p == '\t'; p++);
+ if (*p != '#' && *p != '\n')
+ break;
+ }
+ fclose(fp_tab);
+ /* do some checks */
+ if( buf == NULL )
+ continue;
+ if( strlen( p ) == 0 )
+ continue;
+ } /* end of readdir, all is done */
+ else {
+ done = 1;
+ continue;
+ }
+ } /* end of if(tabdir!=NULL) */
+ else {
+ done = 1;
+ continue;
+ }
+ } /* end of if ( done == 0 ) */
+ lineNo++;
+ /*
+ * Skip comments and empty lines
+ */
+ for(p = buf; *p == ' ' || *p == '\t'; p++)
+ ;
+ if (*p == '#' || *p == '\n') continue;
+
+ /*
+ * Decode the fields
+ */
+ id = strsep(&p, ":");
+ rlevel = strsep(&p, ":");
+ action = strsep(&p, ":");
+ process = strsep(&p, "\n");
+
+ /*
+ * Check if syntax is OK. Be very verbose here, to
+ * avoid newbie postings on comp.os.linux.setup :)
+ */
+ err[0] = 0;
+ if (!id || !*id) strcpy(err, "missing id field");
+ if (!rlevel) strcpy(err, "missing runlevel field");
+ if (!process) strcpy(err, "missing process field");
+ if (!action || !*action)
+ strcpy(err, "missing action field");
+ if (id && strlen(id) > sizeof(utproto.ut_id))
+ sprintf(err, "id field too long (max %d characters)",
+ (int)sizeof(utproto.ut_id));
+ if (rlevel && strlen(rlevel) > 11)
+ strcpy(err, "rlevel field too long (max 11 characters)");
+ if (process && strlen(process) > 127)
+ strcpy(err, "process field too long (max 127 characters)");
+ if (action && strlen(action) > 32)
+ strcpy(err, "action field too long");
+ if (err[0] != 0) {
+ initlog(L_VB, "%s[%d]: %s", INITTAB, lineNo, err);
+ INITDBG(L_VB, "%s:%s:%s:%s", id, rlevel, action, process);
+ continue;
+ }
+
+ /*
+ * Decode the "action" field
+ */
+ actionNo = -1;
+ for(f = 0; actions[f].name; f++)
+ if (strcasecmp(action, actions[f].name) == 0) {
+ actionNo = actions[f].act;
+ break;
+ }
+ if (actionNo == -1) {
+ initlog(L_VB, "%s[%d]: %s: unknown action field",
+ INITTAB, lineNo, action);
+ continue;
+ }
+
+ /*
+ * See if the id field is unique
+ */
+ for(old = newFamily; old; old = old->next) {
+ if(strcmp(old->id, id) == 0 && strcmp(id, "~~")) {
+ initlog(L_VB, "%s[%d]: duplicate ID field \"%s\"",
+ INITTAB, lineNo, id);
+ break;
+ }
+ }
+ if (old) continue;
+
+ /*
+ * Allocate a CHILD structure
+ */
+ ch = imalloc(sizeof(CHILD));
+
+ /*
+ * And fill it in.
+ */
+ ch->action = actionNo;
+ strncpy(ch->id, id, sizeof(utproto.ut_id) + 1); /* Hack for different libs. */
+ strncpy(ch->process, process, sizeof(ch->process) - 1);
+ if (rlevel[0]) {
+ for(f = 0; f < (int)sizeof(rlevel) - 1 && rlevel[f]; f++) {
+ ch->rlevel[f] = rlevel[f];
+ if (ch->rlevel[f] == 's') ch->rlevel[f] = 'S';
+ }
+ strncpy(ch->rlevel, rlevel, sizeof(ch->rlevel) - 1);
+ } else {
+ strcpy(ch->rlevel, "0123456789");
+ if (ISPOWER(ch->action))
+ strcpy(ch->rlevel, "S0123456789");
+ }
+ /*
+ * We have the fake runlevel '#' for SYSINIT and
+ * '*' for BOOT and BOOTWAIT.
+ */
+ if (ch->action == SYSINIT) strcpy(ch->rlevel, "#");
+ if (ch->action == BOOT || ch->action == BOOTWAIT)
+ strcpy(ch->rlevel, "*");
+
+ /*
+ * Now add it to the linked list. Special for powerfail.
+ */
+ if (ISPOWER(ch->action)) {
+
+ /*
+ * Disable by default
+ */
+ ch->flags |= XECUTED;
+
+ /*
+ * Tricky: insert at the front of the list..
+ */
+ old = NULL;
+ for(i = newFamily; i; i = i->next) {
+ if (!ISPOWER(i->action)) break;
+ old = i;
+ }
+ /*
+ * Now add after entry "old"
+ */
+ if (old) {
+ ch->next = i;
+ old->next = ch;
+ if (i == NULL) head = ch;
+ } else {
+ ch->next = newFamily;
+ newFamily = ch;
+ if (ch->next == NULL) head = ch;
+ }
+ } else {
+ /*
+ * Just add at end of the list
+ */
+ if (ch->action == KBREQUEST) ch->flags |= XECUTED;
+ ch->next = NULL;
+ if (head)
+ head->next = ch;
+ else
+ newFamily = ch;
+ head = ch;
+ }
+
+ /*
+ * Walk through the old list comparing id fields
+ */
+ for(old = family; old; old = old->next)
+ if (strcmp(old->id, ch->id) == 0) {
+ old->new = ch;
+ break;
+ }
+ }
+
+ /*
+ * We're done.
+ */
+ if (fp) fclose(fp);
+ if(tabdir) closedir(tabdir);
+
+#ifdef __linux__
+ check_kernel_console();
+#endif
+
+ /*
+ * Loop through the list of children, and see if they need to
+ * be killed.
+ */
+
+ INITDBG(L_VB, "Checking for children to kill");
+ for(round = 0; round < 2; round++) {
+ talk = 1;
+ for(ch = family; ch; ch = ch->next) {
+ ch->flags &= ~KILLME;
+
+ /*
+ * Is this line deleted?
+ */
+ if (ch->new == NULL) ch->flags |= KILLME;
+
+ /*
+ * If the entry has changed, kill it anyway. Note that
+ * we do not check ch->process, only the "action" field.
+ * This way, you can turn an entry "off" immediately, but
+ * changes in the command line will only become effective
+ * after the running version has exited.
+ */
+ if (ch->new && ch->action != ch->new->action) ch->flags |= KILLME;
+
+ /*
+ * Only BOOT processes may live in all levels
+ */
+ if (ch->action != BOOT &&
+ strchr(ch->rlevel, runlevel) == NULL) {
+ /*
+ * Ondemand procedures live always,
+ * except in single user
+ */
+ if (runlevel == 'S' || !(ch->flags & DEMAND))
+ ch->flags |= KILLME;
+ }
+
+ /*
+ * Now, if this process may live note so in the new list
+ */
+ if ((ch->flags & KILLME) == 0) {
+ ch->new->flags = ch->flags;
+ ch->new->pid = ch->pid;
+ ch->new->exstat = ch->exstat;
+ continue;
+ }
+
+
+ /*
+ * Is this process still around?
+ */
+ if ((ch->flags & RUNNING) == 0) {
+ ch->flags &= ~KILLME;
+ continue;
+ }
+ INITDBG(L_VB, "Killing \"%s\"", ch->process);
+ switch(round) {
+ case 0: /* Send TERM signal */
+ if (talk)
+ initlog(L_CO,
+ "Sending processes configured via /etc/inittab the TERM signal");
+ kill(-(ch->pid), SIGTERM);
+ foundOne = 1;
+ break;
+ case 1: /* Send KILL signal and collect status */
+ if (talk)
+ initlog(L_CO,
+ "Sending processes configured via /etc/inittab the KILL signal");
+ kill(-(ch->pid), SIGKILL);
+ break;
+ }
+ talk = 0;
+
+ }
+ /*
+ * See if we have to wait sleep_time seconds
+ */
+ if (foundOne && round == 0) {
+ /*
+ * Yup, but check every 10 milliseconds if we still have children.
+ * The f < 100 * sleep_time refers to sleep time in 10 millisecond chunks.
+ */
+ for(f = 0; f < 100 * sleep_time; f++) {
+ for(ch = family; ch; ch = ch->next) {
+ if (!(ch->flags & KILLME)) continue;
+ if ((ch->flags & RUNNING) && !(ch->flags & ZOMBIE))
+ break;
+ }
+ if (ch == NULL) {
+ /*
+ * No running children, skip SIGKILL
+ */
+ round = 1;
+ foundOne = 0; /* Skip the sleep below. */
+ break;
+ }
+ do_msleep(MINI_SLEEP);
+ }
+ }
+ }
+
+ /*
+ * Now give all processes the chance to die and collect exit statuses.
+ */
+ if (foundOne) do_msleep(MINI_SLEEP);
+ for(ch = family; ch; ch = ch->next)
+ if (ch->flags & KILLME) {
+ if (!(ch->flags & ZOMBIE))
+ initlog(L_CO, "Pid %d [id %s] seems to hang", ch->pid,
+ ch->id);
+ else {
+ INITDBG(L_VB, "Updating utmp for pid %d [id %s]",
+ ch->pid, ch->id);
+ ch->flags &= ~RUNNING;
+ if (ch->process[0] != '+')
+ write_utmp_wtmp("", ch->id, ch->pid, DEAD_PROCESS, NULL);
+ }
+ }
+
+ /*
+ * Both rounds done; clean up the list.
+ */
+ sigemptyset(&nmask);
+ sigaddset(&nmask, SIGCHLD);
+ sigprocmask(SIG_BLOCK, &nmask, &omask);
+ for(ch = family; ch; ch = old) {
+ old = ch->next;
+ free(ch);
+ }
+ family = newFamily;
+ for(ch = family; ch; ch = ch->next) ch->new = NULL;
+ newFamily = NULL;
+ sigprocmask(SIG_SETMASK, &omask, NULL);
+
+#ifdef INITLVL
+ /*
+ * Dispose of INITLVL file.
+ */
+ if (lstat(INITLVL, &st) >= 0 && S_ISLNK(st.st_mode)) {
+ /*
+ * INITLVL is a symbolic link, so just truncate the file.
+ */
+ close(open(INITLVL, O_WRONLY|O_TRUNC));
+ } else {
+ /*
+ * Delete INITLVL file.
+ */
+ unlink(INITLVL);
+ }
+#endif
+#ifdef INITLVL2
+ /*
+ * Dispose of INITLVL2 file.
+ */
+ if (lstat(INITLVL2, &st) >= 0 && S_ISLNK(st.st_mode)) {
+ /*
+ * INITLVL2 is a symbolic link, so just truncate the file.
+ */
+ close(open(INITLVL2, O_WRONLY|O_TRUNC));
+ } else {
+ /*
+ * Delete INITLVL2 file.
+ */
+ unlink(INITLVL2);
+ }
+#endif
+}
+
+/*
+ * Walk through the family list and start up children.
+ * The entries that do not belong here at all are removed
+ * from the list.
+ */
+static
+void start_if_needed(void)
+{
+ CHILD *ch; /* Pointer to child */
+ int delete; /* Delete this entry from list? */
+
+ INITDBG(L_VB, "Checking for children to start");
+
+ for(ch = family; ch; ch = ch->next) {
+
+#if DEBUG
+ if (ch->rlevel[0] == 'C') {
+ INITDBG(L_VB, "%s: flags %d", ch->process, ch->flags);
+ }
+#endif
+
+ /* Are we waiting for this process? Then quit here. */
+ if (ch->flags & WAITING) break;
+
+ /* Already running? OK, don't touch it */
+ if (ch->flags & RUNNING) continue;
+
+ /* See if we have to start it up */
+ delete = 1;
+ if (strchr(ch->rlevel, runlevel) ||
+ ((ch->flags & DEMAND) && !strchr("#*Ss", runlevel))) {
+ startup(ch);
+ delete = 0;
+ }
+
+ if (delete) {
+ /* is this OK? */
+ ch->flags &= ~(RUNNING|WAITING);
+ if (!ISPOWER(ch->action) && ch->action != KBREQUEST)
+ ch->flags &= ~XECUTED;
+ ch->pid = 0;
+ } else
+ /* Do we have to wait for this process? */
+ if (ch->flags & WAITING) break;
+ }
+ /* Done. */
+}
+
+/*
+ * Ask the user on the console for a runlevel
+ */
+static
+int ask_runlevel(void)
+{
+ const char prompt[] = "\nEnter runlevel: ";
+ char buf[8];
+ int lvl = -1;
+ int fd;
+
+ console_stty();
+ fd = console_open(O_RDWR|O_NOCTTY);
+
+ if (fd < 0) return('S');
+
+ while(!strchr("0123456789S", lvl)) {
+ safe_write(fd, prompt, sizeof(prompt) - 1);
+ if (read(fd, buf, sizeof(buf)) <= 0)
+ buf[0] = 0;
+ if (buf[0] != 0 && (buf[1] == '\r' || buf[1] == '\n'))
+ lvl = buf[0];
+ if (islower(lvl)) lvl = toupper(lvl);
+ }
+ close(fd);
+ return lvl;
+}
+
+/*
+ * Search the INITTAB file for the 'initdefault' field, with the default
+ * runlevel. If this fails, ask the user to supply a runlevel.
+ */
+static
+int get_init_default(void)
+{
+ CHILD *ch;
+ int lvl = -1;
+ char *p;
+
+ /*
+ * Look for initdefault.
+ */
+ for(ch = family; ch; ch = ch->next)
+ if (ch->action == INITDEFAULT) {
+ p = ch->rlevel;
+ while(*p) {
+ if (*p > lvl) lvl = *p;
+ p++;
+ }
+ break;
+ }
+ /*
+ * See if level is valid
+ */
+ if (lvl > 0) {
+ if (islower(lvl)) lvl = toupper(lvl);
+ if (strchr("0123456789S", lvl) == NULL) {
+ initlog(L_VB,
+ "Initdefault level '%c' is invalid", lvl);
+ lvl = 0;
+ }
+ }
+ /*
+ * Ask for runlevel on console if needed.
+ */
+ if (lvl <= 0) lvl = ask_runlevel();
+
+ /*
+ * Log the fact that we have a runlevel now.
+ */
+ Write_Runlevel_Log(lvl);
+ return lvl;
+}
+
+
+/*
+ * We got signaled.
+ *
+ * Do actions for the new level. If we are compatible with
+ * the "old" INITLVL and arg == 0, try to read the new
+ * runlevel from that file first.
+ */
+static
+int read_level(int arg)
+{
+ CHILD *ch; /* Walk through list */
+ unsigned char foo = 'X'; /* Contents of INITLVL */
+ int ok = 1;
+#ifdef INITLVL
+ FILE *fp;
+ struct stat stt;
+ int st;
+#endif
+
+ if (arg) foo = arg;
+
+#ifdef INITLVL
+ ok = 0;
+
+ if (arg == 0) {
+ fp = NULL;
+ if (stat(INITLVL, &stt) != 0 || stt.st_size != 0L)
+ fp = fopen(INITLVL, "r");
+#ifdef INITLVL2
+ if (fp == NULL &&
+ (stat(INITLVL2, &stt) != 0 || stt.st_size != 0L))
+ fp = fopen(INITLVL2, "r");
+#endif
+ if (fp == NULL) {
+ /* INITLVL file empty or not there - act as 'init q' */
+ initlog(L_SY, "Re-reading inittab");
+ return(runlevel);
+ }
+ ok = fscanf(fp, "%c %d", &foo, &st);
+ fclose(fp);
+ } else {
+ /* We go to the new runlevel passed as an argument. */
+ foo = arg;
+ ok = 1;
+ }
+ if (ok == 2) sleep_time = st;
+
+#endif /* INITLVL */
+
+ if (islower(foo)) foo = toupper(foo);
+ if (ok < 1 || ok > 2 || strchr("QS0123456789ABCU", foo) == NULL) {
+ initlog(L_VB, "bad runlevel: %c", foo);
+ return runlevel;
+ }
+
+ /* Log this action */
+ switch(foo) {
+ case 'S':
+ initlog(L_VB, "Going single user");
+ break;
+ case 'Q':
+ initlog(L_SY, "Re-reading inittab");
+ break;
+ case 'A':
+ case 'B':
+ case 'C':
+ initlog(L_SY,
+ "Activating demand-procedures for '%c'", foo);
+ break;
+ case 'U':
+ initlog(L_SY, "Trying to re-exec init");
+ return 'U';
+ default:
+ initlog(L_VB, "Switching to runlevel: %c", foo);
+ }
+
+ if (foo == 'Q') {
+#if defined(SIGINT_ONLYONCE) && (SIGINT_ONLYONCE == 1)
+ /* Re-enable signal from keyboard */
+ struct sigaction sa;
+ SETSIG(sa, SIGINT, signal_handler, 0);
+#endif
+ return runlevel;
+ }
+
+ /* Check if this is a runlevel a, b or c */
+ if (strchr("ABC", foo)) {
+ if (runlevel == 'S') return(runlevel);
+
+ /* Read inittab again first! */
+ read_inittab();
+
+ /* Mark those special tasks */
+ for(ch = family; ch; ch = ch->next)
+ if (strchr(ch->rlevel, foo) != NULL ||
+ strchr(ch->rlevel, tolower(foo)) != NULL) {
+ ch->flags |= DEMAND;
+ ch->flags &= ~XECUTED;
+ INITDBG(L_VB,
+ "Marking (%s) as ondemand, flags %d",
+ ch->id, ch->flags);
+ }
+ return runlevel;
+ }
+
+ /* Store both the old and the new runlevel. */
+ wrote_utmp_rlevel = 0;
+ wrote_wtmp_rlevel = 0;
+ write_utmp_wtmp("runlevel", "~~", foo + 256*runlevel, RUN_LVL, "~");
+ thislevel = foo;
+ prevlevel = runlevel;
+ Write_Runlevel_Log(runlevel);
+ return foo;
+}
+
+
+/*
+ * This procedure is called after every signal (SIGHUP, SIGALRM..)
+ *
+ * Only clear the 'failing' flag if the process is sleeping
+ * longer than 5 minutes, or inittab was read again due
+ * to user interaction.
+ */
+static
+void fail_check(void)
+{
+ CHILD *ch; /* Pointer to child structure */
+ time_t t; /* System time */
+ time_t next_alarm = 0; /* When to set next alarm */
+
+ time(&t);
+
+ for(ch = family; ch; ch = ch->next) {
+
+ if (ch->flags & FAILING) {
+ /* Can we free this sucker? */
+ if (ch->tm + SLEEPTIME < t) {
+ ch->flags &= ~FAILING;
+ ch->count = 0;
+ ch->tm = 0;
+ } else {
+ /* No, we'll look again later */
+ if (next_alarm == 0 ||
+ ch->tm + SLEEPTIME > next_alarm)
+ next_alarm = ch->tm + SLEEPTIME;
+ }
+ }
+ }
+ if (next_alarm) {
+ next_alarm -= t;
+ if (next_alarm < 1) next_alarm = 1;
+ alarm(next_alarm);
+ }
+}
+
+/* Set all 'Fail' timers to 0 */
+static
+void fail_cancel(void)
+{
+ CHILD *ch;
+
+ for(ch = family; ch; ch = ch->next) {
+ ch->count = 0;
+ ch->tm = 0;
+ ch->flags &= ~FAILING;
+ }
+}
+
+/*
+ * Start up powerfail entries.
+ */
+static
+void do_power_fail(int pwrstat)
+{
+ CHILD *ch;
+
+ /*
+ * Tell powerwait & powerfail entries to start up
+ */
+ for (ch = family; ch; ch = ch->next) {
+ if (pwrstat == 'O') {
+ /*
+ * The power is OK again.
+ */
+ if (ch->action == POWEROKWAIT)
+ ch->flags &= ~XECUTED;
+ } else if (pwrstat == 'L') {
+ /*
+ * Low battery, shut down now.
+ */
+ if (ch->action == POWERFAILNOW)
+ ch->flags &= ~XECUTED;
+ } else {
+ /*
+ * Power is failing, shutdown imminent
+ */
+ if (ch->action == POWERFAIL || ch->action == POWERWAIT)
+ ch->flags &= ~XECUTED;
+ }
+ }
+}
+
+/*
+ * Check for state-pipe presence
+ */
+static
+int check_pipe(int fd)
+{
+ struct timeval t;
+ fd_set s;
+ char signature[8];
+
+ FD_ZERO(&s);
+ FD_SET(fd, &s);
+ t.tv_sec = t.tv_usec = 0;
+
+ if (select(fd+1, &s, NULL, NULL, &t) != 1)
+ return 0;
+ if (read(fd, signature, 8) != 8)
+ return 0;
+ return strncmp(Signature, signature, 8) == 0;
+}
+
+/*
+ * Make a state-pipe.
+ */
+static
+int make_pipe(int fd)
+{
+ int fds[2];
+
+ if (pipe(fds)) {
+ initlog(L_VB, "pipe: %m");
+ return -1;
+ }
+ dup2(fds[0], fd);
+ close(fds[0]);
+ fcntl(fds[1], F_SETFD, 1);
+ fcntl(fd, F_SETFD, 0);
+ safe_write(fds[1], Signature, 8);
+
+ return fds[1];
+}
+
+/*
+ * Attempt to re-exec.
+ * Renaming to my_re_exec since re_exec is now a common function name
+ * which conflicts.
+ */
+static
+void my_re_exec(void)
+{
+ CHILD *ch;
+ sigset_t mask, oldset;
+ pid_t pid;
+ char **env;
+ int fd;
+
+ if (strchr("S0123456",runlevel) == NULL)
+ return;
+
+ /*
+ * Reset the alarm, and block all signals.
+ */
+ alarm(0);
+ sigfillset(&mask);
+ sigprocmask(SIG_BLOCK, &mask, &oldset);
+
+ /*
+ * construct a pipe fd --> STATE_PIPE and write a signature
+ */
+ if ((fd = make_pipe(STATE_PIPE)) < 0) {
+ sigprocmask(SIG_SETMASK, &oldset, NULL);
+ initlog(L_CO, "Attempt to re-exec failed");
+ }
+
+ fail_cancel();
+ if (pipe_fd >= 0)
+ close(pipe_fd);
+ pipe_fd = -1;
+ DELSET(got_signals, SIGCHLD);
+ DELSET(got_signals, SIGHUP);
+ DELSET(got_signals, SIGUSR1);
+ DELSET(got_signals, SIGUSR2);
+
+ /*
+ * That should be cleaned.
+ */
+ for(ch = family; ch; ch = ch->next)
+ if (ch->flags & ZOMBIE) {
+ INITDBG(L_VB, "Child died, PID= %d", ch->pid);
+ ch->flags &= ~(RUNNING|ZOMBIE|WAITING);
+ if (ch->process[0] != '+')
+ write_utmp_wtmp("", ch->id, ch->pid, DEAD_PROCESS, NULL);
+ }
+
+ if ((pid = fork()) == 0) {
+ /*
+ * Child sends state information to the parent.
+ */
+ send_state(fd);
+ exit(0);
+ }
+
+ /*
+ * The existing init process execs a new init binary.
+ */
+ env = init_buildenv(0);
+ execle(myname, myname, "--init", NULL, env);
+
+ /*
+ * We shouldn't be here, something failed.
+ * Close the state pipe, unblock signals and return.
+ */
+ init_freeenv(env);
+ close(fd);
+ close(STATE_PIPE);
+ sigprocmask(SIG_SETMASK, &oldset, NULL);
+ initlog(L_CO, "Attempt to re-exec failed");
+}
+
+/*
+ * Redo utmp/wtmp entries if required or requested
+ * Check for written records and size of utmp
+ */
+static
+void redo_utmp_wtmp(void)
+{
+ struct stat ustat;
+ const int ret = stat(UTMP_FILE, &ustat);
+
+ if ((ret < 0) || (ustat.st_size == 0))
+ wrote_utmp_rlevel = wrote_utmp_reboot = 0;
+
+ if ((wrote_wtmp_reboot == 0) || (wrote_utmp_reboot == 0))
+ write_utmp_wtmp("reboot", "~~", 0, BOOT_TIME, "~");
+
+ if ((wrote_wtmp_rlevel == 0) || (wrote_utmp_rlevel == 0))
+ write_utmp_wtmp("runlevel", "~~", thislevel + 256 * prevlevel, RUN_LVL, "~");
+}
+
+/*
+ * We got a change runlevel request through the
+ * init.fifo. Process it.
+ */
+static
+void fifo_new_level(int level)
+{
+#if CHANGE_WAIT
+ CHILD *ch;
+#endif
+ int oldlevel;
+
+ if (level == runlevel) return;
+
+#if CHANGE_WAIT
+ /* Are we waiting for a child? */
+ for(ch = family; ch; ch = ch->next)
+ if (ch->flags & WAITING) break;
+ if (ch == NULL)
+#endif
+ {
+ /* We need to go into a new runlevel */
+ oldlevel = runlevel;
+ runlevel = read_level(level);
+ if (runlevel == 'U') {
+ runlevel = oldlevel;
+ my_re_exec();
+ } else {
+ if (oldlevel != 'S' && runlevel == 'S') console_stty();
+ if (runlevel == '6' || runlevel == '0' ||
+ runlevel == '1') console_stty();
+ if (runlevel > '1' && runlevel < '6') redo_utmp_wtmp();
+ read_inittab();
+ fail_cancel();
+ setproctitle("init [%c]", (int)runlevel);
+ }
+ }
+ Write_Runlevel_Log(runlevel);
+}
+
+
+/*
+ * Set/unset environment variables. The variables are
+ * encoded as KEY=VAL\0KEY=VAL\0\0. With "=VAL" it means
+ * setenv, without it means unsetenv.
+ */
+static
+void initcmd_setenv(char *data, int size)
+{
+ char *env, *p, *e;
+ size_t sz;
+ int i, eq;
+
+ e = data + size;
+
+ while (*data && data < e) {
+ for (p = data; *p && p < e; p++)
+ ;
+ if (*p) break;
+ env = data;
+ data = ++p;
+
+ /*
+ * We only allow INIT_* to be set.
+ */
+ if (strncmp(env, "INIT_", 5) != 0)
+ continue;
+
+ sz = strcspn(env, "=");
+ eq = (env[sz] == '=');
+
+ /*initlog(L_SY, "init_setenv: %s, %d, %d", env, eq, sz);*/
+
+ /* Free existing vars. */
+ for (i = 0; i < NR_EXTRA_ENV; i++) {
+ if (extra_env[i] == NULL)
+ continue;
+ if (sz != strcspn(extra_env[i], "="))
+ continue;
+ if (strncmp(extra_env[i], env, sz) == 0) {
+ free(extra_env[i]);
+ extra_env[i] = NULL;
+ }
+ }
+
+ if (eq == 0)
+ continue;
+
+ /* Set new vars if needed. */
+ for (i = 0; i < NR_EXTRA_ENV; i++) {
+ if (extra_env[i] == NULL) {
+ extra_env[i] = istrdup(env);
+ break;
+ }
+ }
+ }
+}
+
+
+/*
+ * Read from the init FIFO. Processes like telnetd and rlogind can
+ * ask us to create login processes on their behalf.
+ */
+static
+void check_init_fifo(void)
+{
+ struct init_request request;
+ struct timeval tv;
+ struct stat st, st2;
+ fd_set fds;
+ int n;
+ int quit = 0;
+
+ /*
+ * First, try to create /run/initctl if not present.
+ */
+ if (stat(INIT_FIFO, &st2) < 0 && errno == ENOENT)
+ (void)mkfifo(INIT_FIFO, 0600);
+
+ /*
+ * If /run/initctl is open, stat the file to see if it
+ * is still the _same_ inode.
+ */
+ if (pipe_fd >= 0) {
+ fstat(pipe_fd, &st);
+ if (stat(INIT_FIFO, &st2) < 0 ||
+ st.st_dev != st2.st_dev ||
+ st.st_ino != st2.st_ino) {
+ close(pipe_fd);
+ pipe_fd = -1;
+ }
+ }
+
+ /*
+ * Now finally try to open /run/initctl if pipe_fd is -1
+ * if it is -2, then we leave it closed
+ */
+ if (pipe_fd == -1) {
+ if ((pipe_fd = open(INIT_FIFO, O_RDWR|O_NONBLOCK)) >= 0) {
+ fstat(pipe_fd, &st);
+ if (!S_ISFIFO(st.st_mode)) {
+ initlog(L_VB, "%s is not a fifo", INIT_FIFO);
+ close(pipe_fd);
+ pipe_fd = -1;
+ }
+ }
+ if (pipe_fd >= 0) {
+ /*
+ * Don't use fd's 0, 1 or 2.
+ */
+ (void) dup2(pipe_fd, PIPE_FD);
+ close(pipe_fd);
+ pipe_fd = PIPE_FD;
+
+ /*
+ * Return to caller - we'll be back later.
+ */
+ }
+ }
+
+ /* Wait for data to appear, _if_ the pipe was opened. */
+ if (pipe_fd >= 0) {
+ while(!quit) {
+
+ /* Do select, return on EINTR. */
+ FD_ZERO(&fds);
+ FD_SET(pipe_fd, &fds);
+ tv.tv_sec = 5;
+ tv.tv_usec = 0;
+ n = select(pipe_fd + 1, &fds, NULL, NULL, &tv);
+ if (n <= 0) {
+ if (n == 0 || errno == EINTR) return;
+ continue;
+ }
+
+ /* Read the data, return on EINTR. */
+ n = read(pipe_fd, &request, sizeof(request));
+ if (n == 0) {
+ /*
+ * End of file. This can't happen under Linux (because
+ * the pipe is opened O_RDWR - see select() in the
+ * kernel) but you never know...
+ */
+ close(pipe_fd);
+ pipe_fd = -1;
+ return;
+ }
+ if (n <= 0) {
+ if (errno == EINTR) return;
+ initlog(L_VB, "error reading initrequest");
+ continue;
+ }
+
+ /*
+ * This is a convenient point to also try to
+ * find the console device or check if it changed.
+ */
+ console_init();
+
+ /*
+ * Process request.
+ */
+ if (request.magic != INIT_MAGIC || n != sizeof(request)) {
+ initlog(L_VB, "got bogus initrequest");
+ continue;
+ }
+ switch(request.cmd) {
+ case INIT_CMD_RUNLVL:
+ sleep_time = request.sleeptime;
+ fifo_new_level(request.runlevel);
+ quit = 1;
+ break;
+ case INIT_CMD_POWERFAIL:
+ sleep_time = request.sleeptime;
+ do_power_fail('F');
+ quit = 1;
+ break;
+ case INIT_CMD_POWERFAILNOW:
+ sleep_time = request.sleeptime;
+ do_power_fail('L');
+ quit = 1;
+ break;
+ case INIT_CMD_POWEROK:
+ sleep_time = request.sleeptime;
+ do_power_fail('O');
+ quit = 1;
+ break;
+ case INIT_CMD_SETENV:
+ initcmd_setenv(request.i.data, sizeof(request.i.data));
+ break;
+ default:
+ initlog(L_VB, "got unimplemented initrequest.");
+ break;
+ } /* end of switch */
+ } /* end of while loop not quitting */
+ } /* end of if the pipe is open */
+ /*
+ * We come here if the pipe couldn't be opened.
+ */
+ if (pipe_fd == -1) pause();
+
+}
+
+
+/*
+ * This function is used in the transition
+ * sysinit (-> single user) boot -> multi-user.
+ */
+static
+void boot_transitions()
+{
+ CHILD *ch;
+ static int newlevel = 0;
+ static int warn = 1;
+ int loglevel;
+ int oldlevel;
+
+ /* Check if there is something to wait for! */
+ for( ch = family; ch; ch = ch->next )
+ if ((ch->flags & RUNNING) && ch->action != BOOT) break;
+
+ if (ch == NULL) {
+ /* No processes left in this level, proceed to next level. */
+ loglevel = -1;
+ oldlevel = 'N';
+ switch(runlevel) {
+ case '#': /* SYSINIT -> BOOT */
+ INITDBG(L_VB, "SYSINIT -> BOOT");
+
+ /* Write a boot record. */
+ wrote_utmp_reboot = 0;
+ wrote_wtmp_reboot = 0;
+ write_utmp_wtmp("reboot", "~~", 0, BOOT_TIME, "~");
+
+ /* Get our run level */
+ newlevel = dfl_level ? dfl_level : get_init_default();
+ if (newlevel == 'S') {
+ runlevel = newlevel;
+ /* Not really 'S' but show anyway. */
+ setproctitle("init [S]");
+ } else
+ runlevel = '*';
+ break;
+ case '*': /* BOOT -> NORMAL */
+ INITDBG(L_VB, "BOOT -> NORMAL");
+ if (runlevel != newlevel)
+ loglevel = newlevel;
+ runlevel = newlevel;
+ did_boot = 1;
+ warn = 1;
+ break;
+ case 'S': /* Ended SU mode */
+ case 's':
+ INITDBG(L_VB, "END SU MODE");
+ newlevel = get_init_default();
+ if (!did_boot && newlevel != 'S')
+ runlevel = '*';
+ else {
+ if (runlevel != newlevel)
+ loglevel = newlevel;
+ runlevel = newlevel;
+ oldlevel = 'S';
+ }
+ warn = 1;
+ for(ch = family; ch; ch = ch->next)
+ if (strcmp(ch->rlevel, "S") == 0)
+ ch->flags &= ~(FAILING|WAITING|XECUTED);
+ break;
+ default:
+ if (warn)
+ initlog(L_VB,
+ "no more processes left in this runlevel");
+ warn = 0;
+ loglevel = -1;
+ if (got_signals == 0)
+ check_init_fifo();
+ break;
+ }
+ if (loglevel > 0) {
+ initlog(L_VB, "Entering runlevel: %c", runlevel);
+ wrote_utmp_rlevel = 0;
+ wrote_wtmp_rlevel = 0;
+ write_utmp_wtmp("runlevel", "~~", runlevel + 256 * oldlevel, RUN_LVL, "~");
+ thislevel = runlevel;
+ prevlevel = oldlevel;
+ setproctitle("init [%c]", (int)runlevel);
+ }
+ Write_Runlevel_Log(runlevel);
+ }
+}
+
+/*
+ * Init got hit by a signal. See which signal it is,
+ * and act accordingly.
+ */
+static
+void process_signals()
+{
+ CHILD *ch;
+ int pwrstat;
+ int oldlevel;
+ int fd;
+ char c;
+
+ if (ISMEMBER(got_signals, SIGPWR)) {
+ INITDBG(L_VB, "got SIGPWR");
+ /* See _what_ kind of SIGPWR this is. */
+ pwrstat = 0;
+ if ((fd = open(PWRSTAT, O_RDONLY)) >= 0) {
+ if (read(fd, &c, 1) != 1)
+ c = 0;
+ pwrstat = c;
+ close(fd);
+ unlink(PWRSTAT);
+ } else if ((fd = open(PWRSTAT_OLD, O_RDONLY)) >= 0) {
+ /* Path changed 2010-03-20. Look for the old path for a while. */
+ initlog(L_VB, "warning: found obsolete path %s, use %s instead",
+ PWRSTAT_OLD, PWRSTAT);
+ if (read(fd, &c, 1) != 1)
+ c = 0;
+ pwrstat = c;
+ close(fd);
+ unlink(PWRSTAT_OLD);
+ }
+ do_power_fail(pwrstat);
+ DELSET(got_signals, SIGPWR);
+ }
+
+ if (ISMEMBER(got_signals, SIGINT)) {
+#if defined(SIGINT_ONLYONCE) && (SIGINT_ONLYONCE == 1)
+ /* Ignore any further signal from keyboard */
+ struct sigaction sa;
+ SETSIG(sa, SIGINT, SIG_IGN, SA_RESTART);
+#endif
+ INITDBG(L_VB, "got SIGINT");
+ /* Tell ctrlaltdel entry to start up */
+ for(ch = family; ch; ch = ch->next)
+ if (ch->action == CTRLALTDEL)
+ ch->flags &= ~XECUTED;
+ DELSET(got_signals, SIGINT);
+ }
+
+ if (ISMEMBER(got_signals, SIGWINCH)) {
+ INITDBG(L_VB, "got SIGWINCH");
+ /* Tell kbrequest entry to start up */
+ for(ch = family; ch; ch = ch->next)
+ if (ch->action == KBREQUEST)
+ ch->flags &= ~XECUTED;
+ DELSET(got_signals, SIGWINCH);
+ }
+
+ if (ISMEMBER(got_signals, SIGALRM)) {
+ INITDBG(L_VB, "got SIGALRM");
+ /* The timer went off: check it out */
+ DELSET(got_signals, SIGALRM);
+ }
+
+ if (ISMEMBER(got_signals, SIGCHLD)) {
+ INITDBG(L_VB, "got SIGCHLD");
+ /* First set flag to 0 */
+ DELSET(got_signals, SIGCHLD);
+
+ /* See which child this was */
+ for(ch = family; ch; ch = ch->next)
+ if (ch->flags & ZOMBIE) {
+ INITDBG(L_VB, "Child died, PID= %d", ch->pid);
+ ch->flags &= ~(RUNNING|ZOMBIE|WAITING);
+ if (ch->process[0] != '+')
+ write_utmp_wtmp("", ch->id, ch->pid, DEAD_PROCESS, NULL);
+ }
+
+ }
+
+ if (ISMEMBER(got_signals, SIGHUP)) {
+ INITDBG(L_VB, "got SIGHUP");
+#if CHANGE_WAIT
+ /* Are we waiting for a child? */
+ for(ch = family; ch; ch = ch->next)
+ if (ch->flags & WAITING) break;
+ if (ch == NULL)
+#endif
+ {
+ /* We need to go into a new runlevel */
+ oldlevel = runlevel;
+#ifdef INITLVL
+ runlevel = read_level(0);
+#endif
+ if (runlevel == 'U') {
+ runlevel = oldlevel;
+ my_re_exec();
+ } else {
+ if (oldlevel != 'S' && runlevel == 'S') console_stty();
+ if (runlevel == '6' || runlevel == '0' ||
+ runlevel == '1') console_stty();
+ read_inittab();
+ fail_cancel();
+ setproctitle("init [%c]", (int)runlevel);
+ DELSET(got_signals, SIGHUP);
+ }
+ Write_Runlevel_Log(runlevel);
+ }
+ }
+ if (ISMEMBER(got_signals, SIGUSR1)) {
+ /*
+ * SIGUSR1 means close and reopen /run/initctl
+ */
+ INITDBG(L_VB, "got SIGUSR1");
+ if (pipe_fd)
+ close(pipe_fd);
+ pipe_fd = -1;
+ DELSET(got_signals, SIGUSR1);
+ }
+ else if (ISMEMBER(got_signals, SIGUSR2)) {
+ /* SIGUSR1 mean close the pipe and leave it closed */
+ INITDBG(L_VB, "got SIGUSR2");
+ if (pipe_fd)
+ close(pipe_fd);
+ pipe_fd = -2;
+ DELSET(got_signals, SIGUSR2);
+ }
+}
+
+/*
+ * The main loop
+ */
+static
+void init_main(void)
+{
+ CHILD *ch;
+ struct sigaction sa;
+ sigset_t sgt;
+ int f, st;
+
+ if (!reload) {
+
+#if INITDEBUG
+ /*
+ * Fork so we can debug the init process.
+ */
+ if ((f = fork()) > 0) {
+ static const char killmsg[] = "PRNT: init killed.\r\n";
+ pid_t rc;
+
+ while((rc = wait(&st)) != f)
+ if (rc < 0 && errno == ECHILD)
+ break;
+ safe_write(1, killmsg, sizeof(killmsg) - 1);
+ while(1) pause();
+ }
+#endif
+
+#ifdef __linux__
+ /*
+ * Tell the kernel to send us SIGINT when CTRL-ALT-DEL
+ * is pressed, and that we want to handle keyboard signals.
+ */
+ init_reboot(BMAGIC_SOFT);
+ if ((f = open(VT_MASTER, O_RDWR | O_NOCTTY)) >= 0) {
+ (void) ioctl(f, KDSIGACCEPT, SIGWINCH);
+ close(f);
+ } else
+ (void) ioctl(0, KDSIGACCEPT, SIGWINCH);
+#endif
+
+ /*
+ * Ignore all signals.
+ */
+ for(f = 1; f <= NSIG; f++)
+ SETSIG(sa, f, SIG_IGN, SA_RESTART);
+ }
+
+ SETSIG(sa, SIGALRM, signal_handler, 0);
+ SETSIG(sa, SIGHUP, signal_handler, 0);
+ SETSIG(sa, SIGINT, signal_handler, 0);
+ SETSIG(sa, SIGCHLD, chld_handler, SA_RESTART);
+ SETSIG(sa, SIGPWR, signal_handler, 0);
+ SETSIG(sa, SIGWINCH, signal_handler, 0);
+ SETSIG(sa, SIGUSR1, signal_handler, 0);
+ SETSIG(sa, SIGUSR2, signal_handler, 0);
+ SETSIG(sa, SIGSTOP, stop_handler, SA_RESTART);
+ SETSIG(sa, SIGTSTP, stop_handler, SA_RESTART);
+ SETSIG(sa, SIGCONT, cont_handler, SA_RESTART);
+ SETSIG(sa, SIGSEGV, (void (*)(int))segv_handler, SA_RESTART);
+
+ console_init();
+
+ if (!reload) {
+ int fd;
+
+ /* Close whatever files are open, and reset the console. */
+ close(0);
+ close(1);
+ close(2);
+ console_stty();
+ setsid();
+
+ /*
+ * Set default PATH variable.
+ */
+ setenv("PATH", PATH_DEFAULT, 1 /* Overwrite */);
+
+ /*
+ * Initialize /var/run/utmp (only works if /var is on
+ * root and mounted rw)
+ */
+ if ((fd = open(UTMP_FILE, O_WRONLY|O_CREAT|O_TRUNC, 0644)) >= 0)
+ close(fd);
+
+ /*
+ * Say hello to the world
+ */
+ initlog(L_CO, bootmsg, "booting");
+
+ /*
+ * See if we have to start an emergency shell.
+ */
+ if (emerg_shell) {
+ pid_t rc;
+ SETSIG(sa, SIGCHLD, SIG_DFL, SA_RESTART);
+ if (spawn(&ch_emerg, &f) > 0) {
+ while((rc = wait(&st)) != f)
+ if (rc < 0 && errno == ECHILD)
+ break;
+ }
+ SETSIG(sa, SIGCHLD, chld_handler, SA_RESTART);
+ }
+
+ /*
+ * Start normal boot procedure.
+ */
+ runlevel = '#';
+ read_inittab();
+
+ } else {
+ /*
+ * Restart: unblock signals and let the show go on
+ */
+ initlog(L_CO, bootmsg, "reloading");
+ sigfillset(&sgt);
+ sigprocmask(SIG_UNBLOCK, &sgt, NULL);
+
+ /*
+ * Set default PATH variable.
+ */
+ setenv("PATH", PATH_DEFAULT, 0 /* Don't overwrite */);
+ }
+ start_if_needed();
+
+ while(1) {
+
+ /* See if we need to make the boot transitions. */
+ boot_transitions();
+ INITDBG(L_VB, "init_main: waiting..");
+
+ /* Check if there are processes to be waited on. */
+ for(ch = family; ch; ch = ch->next)
+ if ((ch->flags & RUNNING) && ch->action != BOOT) break;
+
+#if CHANGE_WAIT
+ /* Wait until we get hit by some signal. */
+ while (ch != NULL && got_signals == 0) {
+ if (ISMEMBER(got_signals, SIGHUP)) {
+ /* See if there are processes to be waited on. */
+ for(ch = family; ch; ch = ch->next)
+ if (ch->flags & WAITING) break;
+ }
+ if (ch != NULL) check_init_fifo();
+ }
+#else /* CHANGE_WAIT */
+ if (ch != NULL && got_signals == 0) check_init_fifo();
+#endif /* CHANGE_WAIT */
+
+ /* Check the 'failing' flags */
+ fail_check();
+
+ /* Process any signals. */
+ process_signals();
+
+ /* See what we need to start up (again) */
+ start_if_needed();
+ }
+ /*NOTREACHED*/
+}
+
+/*
+ * Tell the user about the syntax we expect.
+ */
+static
+void usage(char *s)
+{
+ fprintf(stderr, "Usage: %s {-e VAR[=VAL] | [-t SECONDS] {0|1|2|3|4|5|6|S|s|Q|q|A|a|B|b|C|c|U|u}}\n", s);
+ exit(1);
+}
+
+static
+int telinit(char *progname, int argc, char **argv)
+{
+#ifdef TELINIT_USES_INITLVL
+ FILE *fp;
+#endif
+ struct init_request request;
+ struct sigaction sa;
+ int f, fd, l;
+ char *env = NULL;
+
+ memset(&request, 0, sizeof(request));
+ request.magic = INIT_MAGIC;
+
+ while ((f = getopt(argc, argv, "t:e:")) != EOF) switch(f) {
+ case 't':
+ sleep_time = atoi(optarg);
+ break;
+ case 'e':
+ if (env == NULL)
+ env = request.i.data;
+ l = strlen(optarg);
+ if (env + l + 2 > request.i.data + sizeof(request.i.data)) {
+ fprintf(stderr, "%s: -e option data "
+ "too large\n", progname);
+ exit(1);
+ }
+ memcpy(env, optarg, l);
+ env += l;
+ *env++ = 0;
+ break;
+ default:
+ usage(progname);
+ break;
+ }
+
+ if (env) *env++ = 0;
+
+ if (env) {
+ if (argc != optind)
+ usage(progname);
+ request.cmd = INIT_CMD_SETENV;
+ } else {
+ if (argc - optind != 1 || strlen(argv[optind]) != 1)
+ usage(progname);
+ if (!strchr("0123456789SsQqAaBbCcUu", argv[optind][0]))
+ usage(progname);
+ request.cmd = INIT_CMD_RUNLVL;
+ request.runlevel = argv[optind][0];
+ request.sleeptime = sleep_time;
+ }
+
+ /* Change to the root directory. */
+ if (0 != chdir("/"))
+ initlog(L_VB, "unable to chdir to /: %s",
+ strerror(errno));
+
+ /* Open the fifo and write a command. */
+ /* Make sure we don't hang on opening /run/initctl */
+ SETSIG(sa, SIGALRM, signal_handler, 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;
+ }
+
+#ifdef TELINIT_USES_INITLVL
+ if (request.cmd == INIT_CMD_RUNLVL) {
+ /* Fallthrough to the old method. */
+
+ /* Now write the new runlevel. */
+ if ((fp = fopen(INITLVL, "w")) == NULL) {
+ fprintf(stderr, "%s: cannot create %s\n",
+ progname, INITLVL);
+ exit(1);
+ }
+ fprintf(fp, "%s %d", argv[optind], sleep_time);
+ fclose(fp);
+
+ /* And tell init about the pending runlevel change. */
+ if (kill(INITPID, SIGHUP) < 0) perror(progname);
+
+ return 0;
+ }
+#endif
+
+ fprintf(stderr, "%s: ", progname);
+ if (ISMEMBER(got_signals, SIGALRM)) {
+ fprintf(stderr, "timeout opening/writing control channel %s\n",
+ INIT_FIFO);
+ } else {
+ perror(INIT_FIFO);
+ }
+ return 1;
+}
+
+/*
+ * Main entry for init and telinit.
+ */
+int main(int argc, char **argv)
+{
+ char *p;
+ int f;
+ int isinit;
+#ifdef WITH_SELINUX
+ int enforce = 0;
+#endif
+
+ /* Get my own name */
+ if ((p = strrchr(argv[0], '/')) != NULL)
+ p++;
+ else
+ p = argv[0];
+
+ if ( (argc == 2) && (! strcmp(argv[1], "--version") ) )
+ {
+ printf("SysV init version: %s\n\n", VERSION);
+ exit(0);
+ }
+
+ /* Common umask */
+ umask(umask(077) | 022);
+
+ /* Quick check */
+ if (geteuid() != 0) {
+ fprintf(stderr, "%s: must be superuser.\n", p);
+ exit(1);
+ }
+
+ /*
+ * Is this telinit or init ?
+ */
+ isinit = (getpid() == 1);
+ for (f = 1; f < argc; f++) {
+ if (!strcmp(argv[f], "-i") || !strcmp(argv[f], "--init")) {
+ isinit = 1;
+ break;
+ }
+ }
+ if (!isinit) exit(telinit(p, argc, argv));
+
+ /*
+ * Check for re-exec
+ */
+ if (check_pipe(STATE_PIPE)) {
+
+ receive_state(STATE_PIPE);
+
+ myname = istrdup(argv[0]);
+ argv0 = argv[0];
+ maxproclen = 0;
+ for (f = 0; f < argc; f++)
+ maxproclen += strlen(argv[f]) + 1;
+ reload = 1;
+ setproctitle("init [%c]", (int)runlevel);
+
+ init_main();
+ }
+
+ /* Check command line arguments */
+ maxproclen = strlen(argv[0]) + 1;
+ for(f = 1; f < argc; f++) {
+ if (!strcmp(argv[f], "single") || !strcmp(argv[f], "-s"))
+ dfl_level = 'S';
+ else if (!strcmp(argv[f], "-a") || !strcmp(argv[f], "auto"))
+ putenv("AUTOBOOT=YES");
+ else if (!strcmp(argv[f], "-b") || !strcmp(argv[f],"emergency"))
+ emerg_shell = 1;
+ else if (!strcmp(argv[f], "-z")) {
+ /* Ignore -z xxx */
+ if (argv[f + 1]) f++;
+ } else if (strchr("0123456789sS", argv[f][0])
+ && strlen(argv[f]) == 1)
+ dfl_level = argv[f][0];
+ /* "init u" in the very beginning makes no sense */
+ if (dfl_level == 's') dfl_level = 'S';
+ maxproclen += strlen(argv[f]) + 1;
+ }
+
+#ifdef WITH_SELINUX
+ if (getenv("SELINUX_INIT") == NULL) {
+ if (is_selinux_enabled() != 1) {
+ if (selinux_init_load_policy(&enforce) == 0) {
+ putenv("SELINUX_INIT=YES");
+ execv(myname, argv);
+ } else {
+ if (enforce > 0) {
+ /* SELinux in enforcing mode but load_policy failed */
+ /* At this point, we probably can't open /dev/console, so log() won't work */
+ fprintf(stderr,"Unable to load SELinux Policy. Machine is in enforcing mode. Halting now.\n");
+ exit(1);
+ }
+ }
+ }
+ }
+#endif
+ /* Start booting. */
+ argv0 = argv[0];
+ argv[1] = NULL;
+ setproctitle("init boot");
+ init_main();
+
+ /*NOTREACHED*/
+ return 0;
+}
Index: create-2.99-version-patch/sysvinit-2.99-new/src
===================================================================
--- create-2.99-version-patch/sysvinit-2.99-new/src (nonexistent)
+++ create-2.99-version-patch/sysvinit-2.99-new/src (revision 5)
Property changes on: create-2.99-version-patch/sysvinit-2.99-new/src
___________________________________________________________________
Added: svn:ignore
## -0,0 +1,73 ##
+
+# install dir
+dist
+
+# Target build dirs
+.a1x-newlib
+.a2x-newlib
+.at91sam7s-newlib
+
+.build-machine
+
+.a1x-glibc
+.a2x-glibc
+.h3-glibc
+.h5-glibc
+.i586-glibc
+.i686-glibc
+.imx6-glibc
+.jz47xx-glibc
+.makefile
+.am335x-glibc
+.omap543x-glibc
+.p5600-glibc
+.power8-glibc
+.power8le-glibc
+.power9-glibc
+.power9le-glibc
+.m1000-glibc
+.riscv64-glibc
+.rk328x-glibc
+.rk33xx-glibc
+.rk339x-glibc
+.s8xx-glibc
+.s9xx-glibc
+.x86_64-glibc
+
+# Hidden files (each file)
+.makefile
+.dist
+.rootfs
+
+# src & hw requires
+.src_requires
+.src_requires_depend
+.requires
+.requires_depend
+
+# Tarballs
+*.gz
+*.bz2
+*.lz
+*.xz
+*.tgz
+*.txz
+
+# Signatures
+*.asc
+*.sig
+*.sign
+*.sha1sum
+
+# Patches
+*.patch
+
+# Descriptions
+*.dsc
+*.txt
+
+# Default linux config files
+*.defconfig
+
+# backup copies
+*~
Index: create-2.99-version-patch/sysvinit-2.99-new
===================================================================
--- create-2.99-version-patch/sysvinit-2.99-new (nonexistent)
+++ create-2.99-version-patch/sysvinit-2.99-new (revision 5)
Property changes on: create-2.99-version-patch/sysvinit-2.99-new
___________________________________________________________________
Added: svn:ignore
## -0,0 +1,73 ##
+
+# install dir
+dist
+
+# Target build dirs
+.a1x-newlib
+.a2x-newlib
+.at91sam7s-newlib
+
+.build-machine
+
+.a1x-glibc
+.a2x-glibc
+.h3-glibc
+.h5-glibc
+.i586-glibc
+.i686-glibc
+.imx6-glibc
+.jz47xx-glibc
+.makefile
+.am335x-glibc
+.omap543x-glibc
+.p5600-glibc
+.power8-glibc
+.power8le-glibc
+.power9-glibc
+.power9le-glibc
+.m1000-glibc
+.riscv64-glibc
+.rk328x-glibc
+.rk33xx-glibc
+.rk339x-glibc
+.s8xx-glibc
+.s9xx-glibc
+.x86_64-glibc
+
+# Hidden files (each file)
+.makefile
+.dist
+.rootfs
+
+# src & hw requires
+.src_requires
+.src_requires_depend
+.requires
+.requires_depend
+
+# Tarballs
+*.gz
+*.bz2
+*.lz
+*.xz
+*.tgz
+*.txz
+
+# Signatures
+*.asc
+*.sig
+*.sign
+*.sha1sum
+
+# Patches
+*.patch
+
+# Descriptions
+*.dsc
+*.txt
+
+# Default linux config files
+*.defconfig
+
+# backup copies
+*~
Index: create-2.99-version-patch
===================================================================
--- create-2.99-version-patch (nonexistent)
+++ create-2.99-version-patch (revision 5)
Property changes on: create-2.99-version-patch
___________________________________________________________________
Added: svn:ignore
## -0,0 +1,73 ##
+
+# install dir
+dist
+
+# Target build dirs
+.a1x-newlib
+.a2x-newlib
+.at91sam7s-newlib
+
+.build-machine
+
+.a1x-glibc
+.a2x-glibc
+.h3-glibc
+.h5-glibc
+.i586-glibc
+.i686-glibc
+.imx6-glibc
+.jz47xx-glibc
+.makefile
+.am335x-glibc
+.omap543x-glibc
+.p5600-glibc
+.power8-glibc
+.power8le-glibc
+.power9-glibc
+.power9le-glibc
+.m1000-glibc
+.riscv64-glibc
+.rk328x-glibc
+.rk33xx-glibc
+.rk339x-glibc
+.s8xx-glibc
+.s9xx-glibc
+.x86_64-glibc
+
+# Hidden files (each file)
+.makefile
+.dist
+.rootfs
+
+# src & hw requires
+.src_requires
+.src_requires_depend
+.requires
+.requires_depend
+
+# Tarballs
+*.gz
+*.bz2
+*.lz
+*.xz
+*.tgz
+*.txz
+
+# Signatures
+*.asc
+*.sig
+*.sign
+*.sha1sum
+
+# Patches
+*.patch
+
+# Descriptions
+*.dsc
+*.txt
+
+# Default linux config files
+*.defconfig
+
+# backup copies
+*~
Index: patches/README
===================================================================
--- patches/README (nonexistent)
+++ patches/README (revision 5)
@@ -0,0 +1,6 @@
+
+/* begin *
+
+ sysvinit-2.99-initctl.patch - should be applied before sysvinit-2.99-version.patch
+
+ * end */
Index: patches
===================================================================
--- patches (nonexistent)
+++ patches (revision 5)
Property changes on: patches
___________________________________________________________________
Added: svn:ignore
## -0,0 +1,73 ##
+
+# install dir
+dist
+
+# Target build dirs
+.a1x-newlib
+.a2x-newlib
+.at91sam7s-newlib
+
+.build-machine
+
+.a1x-glibc
+.a2x-glibc
+.h3-glibc
+.h5-glibc
+.i586-glibc
+.i686-glibc
+.imx6-glibc
+.jz47xx-glibc
+.makefile
+.am335x-glibc
+.omap543x-glibc
+.p5600-glibc
+.power8-glibc
+.power8le-glibc
+.power9-glibc
+.power9le-glibc
+.m1000-glibc
+.riscv64-glibc
+.rk328x-glibc
+.rk33xx-glibc
+.rk339x-glibc
+.s8xx-glibc
+.s9xx-glibc
+.x86_64-glibc
+
+# Hidden files (each file)
+.makefile
+.dist
+.rootfs
+
+# src & hw requires
+.src_requires
+.src_requires_depend
+.requires
+.requires_depend
+
+# Tarballs
+*.gz
+*.bz2
+*.lz
+*.xz
+*.tgz
+*.txz
+
+# Signatures
+*.asc
+*.sig
+*.sign
+*.sha1sum
+
+# Patches
+*.patch
+
+# Descriptions
+*.dsc
+*.txt
+
+# Default linux config files
+*.defconfig
+
+# backup copies
+*~
Index: .
===================================================================
--- . (nonexistent)
+++ . (revision 5)
Property changes on: .
___________________________________________________________________
Added: svn:ignore
## -0,0 +1,73 ##
+
+# install dir
+dist
+
+# Target build dirs
+.a1x-newlib
+.a2x-newlib
+.at91sam7s-newlib
+
+.build-machine
+
+.a1x-glibc
+.a2x-glibc
+.h3-glibc
+.h5-glibc
+.i586-glibc
+.i686-glibc
+.imx6-glibc
+.jz47xx-glibc
+.makefile
+.am335x-glibc
+.omap543x-glibc
+.p5600-glibc
+.power8-glibc
+.power8le-glibc
+.power9-glibc
+.power9le-glibc
+.m1000-glibc
+.riscv64-glibc
+.rk328x-glibc
+.rk33xx-glibc
+.rk339x-glibc
+.s8xx-glibc
+.s9xx-glibc
+.x86_64-glibc
+
+# Hidden files (each file)
+.makefile
+.dist
+.rootfs
+
+# src & hw requires
+.src_requires
+.src_requires_depend
+.requires
+.requires_depend
+
+# Tarballs
+*.gz
+*.bz2
+*.lz
+*.xz
+*.tgz
+*.txz
+
+# Signatures
+*.asc
+*.sig
+*.sign
+*.sha1sum
+
+# Patches
+*.patch
+
+# Descriptions
+*.dsc
+*.txt
+
+# Default linux config files
+*.defconfig
+
+# backup copies
+*~