sysvinit-2.90/0000755017777601777760000000000013303017401013145 5ustar nobodynogroupsysvinit-2.90/contrib/0000755017777601777760000000000013303017401014605 5ustar nobodynogroupsysvinit-2.90/contrib/zefram-patches0000644017777601777760000001452013303017401017443 0ustar nobodynogroupDate: Mon, 14 Apr 1997 15:39:08 +0100 (BST) From: Zefram Message-Id: <25042.199704141439@stone.dcs.warwick.ac.uk> Subject: SysVinit patch To: miquels@drinkel.cistron.nl Hi. Enclosed is a patch for SysVinit that I am finding tiresome to keep updating; you might like to fold it into your official releases. It does the following: * shutdown: use /etc/shutdown.allow if an option -s is given instead of testing for a controlling tty. The tty check was inflexible and unreliable. * sulogin(8): refer to inittab(5) instead of inittab(4). * sulogin: use libc passwd/shadow functions instead of home-grown ones (there's just no point in using the special version). * shutdown/sulogin: don't rely on the username "root" -- it's the UID that counts. -zefram begin 644 initdiffs.gz M'XL(``IG43,"`^T[:U/;R+*?E5_1>&L3&\G&#Y[FD%H")/%9)U"8G-0](44) M>62KD"6M1K+7)YO_?KM[1K)DFP"[V:T]=2^5`FFFIZ??W=.C##W7A;H3@YS+ MJ1=X2;W=V#W8DN,T&8:SH+%?GC`+,\\V-S?O76:\CCWX9QI`:QO_=3N[W68; M6@<'N\_J]?K].(T!+CF.8FAUH-WI-G>Z.QU:MD>;%7]X\U;+:NT!OP(T7L&6 MO/6"'!N-7;Z"3W!=3^BY=PE2./#YV48^'M^-`\?%(9J&Q)L(7M3#R9D=!UXP MJD^$E/9(*)CKRM>O7YD#M34^U?_0UO*I>Z\3P^Z!M;>MQ$"DX>M^*R--_!KY M-DHZC.>0H0L#2,8"G'`RL8,A^%X@<,!.8.;Y/MP*I#5((`G!]GU$D4H1R\:" M!),>OGSY`E"7]-*[@&OWU75=7KL7^'X6R#36"&U>#'::C,/8DV)(6$E(0%)B M*M`4$C$!3X(?CD8(X04-Q'(UQA'\%X3Q!,F8$Z(AS,8BH,TR.>..!.0@!,[> MSFF.[`K'J_LU"_'PBI,D]NO'?E(_%3[!1S%*0@P;``,A%!7"2;R0.'0=G MX20,DCCT%4^WP@]GX(8Q3$+DS0M<(HM6-)0X2"Z0RR53)DNF1V^$!N`C$:,E MP/:^WJJ;.U9K8=?(TLG59;]^W+^JGY[UB64;:=`\6,2U$PLD)AB!'8`=17$8 MQ9Z="$`UHMJ]@+'TMD3B;)%T$OL6L324C"?"#J32EIB*>!ZB,H@4#IM:!2YA"N0B)URA:%^UGH`DL* MI+7.6#AW+!I4B.>[^A MN'$X*9@*T>0EB@99(((0NQZRF0DOUQNB"6<:)RD"N2,'Z3'_`7E6A#J2C`&) M1H(#&[V/*&=),]:9EXP5A"<38B028>23X2C?B46)872F)2ZAJAGI;4WM>"M. M@ZTTF41(5ZT!YX$_)[[SL+ M8O_O`FM=@#1+;O![7"`W/H10D1L11131K/^[OE"2+QLWBF+5]%6X^%.,?_A` M.>;<6SHYWRK''.,J%7"`*J8BJ9!,<_B$\KG$S81OSS%Y)S.!ECCHO;DZNWP'5//@\\^]?I]3.A%` M:5POQG43^PY=F*J8A1/*=4ZH]N1?*%V"ZQKOO%]2I'2*2P>3$,N%X#:-1Q9, M>%S^-(R]X$[X#0[M9(3`-/2(!C:Q:P]6243B@/4];'O6TZ%VU`E6J$ZTJ`>:Q2PT6(I'9( M1H7')R^IMOAE_=FKO;MOM?<*XF^CS[;1:;/(AT1A:/.&08A$PA$T#_-1+_PPVW4B].6,[ADV2.R>33YWVY\+$;>I^:K7W<6@M MZ?O;5J=SD(47XI!^8U1RHGF5<-[X6+7X%E1:2A[/ZCB_M1C]5-T+.W3`2075P>MSOGW]$:N)*C9&\_]#OU]#[ZT28@72]#]=28A&E M,X%2+-;_'/85C8:GY*NP7&)Z@34U3@8\&V/%4G61:EE%"5N`(K8P_M04+89! M#)/HFY^)H1<_O(#??H/"P'7PHL:$>D$J#O,E'KR$3FMY!C-459(4$,$A;,KH M$&1DFC46ZR;-9!CY1;-A+$S",TWD_RH&'C:) M-$'?K*Y3KK&PND1W!G1O@'JYOWQN[-![]]GN>NL$EZD7!DM_Q+!4%M+ MP3`U-VP7,UMFO.1>3H1I_$5?+4:9D_/W@_/^&4IUMA)EJ#S,ZPPW0ICK^)&U M?>,Z5DDEPU+R*\V8D6<;S26%9&0.5-2]T)9.&4T=U91/T;1RJJJC_`AGJYCJ M'(L2WA3I='[!JB=PYTEWU/6ZFK&S\]*&IV'6"DSQ5#PI9CTI,)EOE1_&">X99*B>.+)#A@U7- M)B'`DT7IY*O/NPU0D6(8"LD5':X/J27I<%VJ?C($O^!!V7/G%J%@&7LJQ#AT M@D9HW#X-T*ZG:$2TD^TX81HDFOQH/CT[FO=F1_/W94?SP>QH MDG68#V5'LYP=V8[^8'8TGYP=U:Y/RX[FT[*C^>CL:#XJ.V90&X.;WN#D[:7. MDN05M75P3\BB]TMC35XSGY#7S#^2U\SEO&86\YJI0F5F<\6\EIO4M_*:N9K7 MS"?E-?.[Y#5S-:^9H!E;Y#7-I8Z[.J]Q1*%S3^BNG"95J6ZK)J'.=J=KHB\&>5?B&4OS].,0#?8Y[H-TW-=F2[E!N>;2,YM8UV3+YOC*W._3VV?-636FS[>8L-H#%X"Z=G@Y/+WL55[_R]ND?4>/&%+#^8AG>E MB['J?HU:M7PK5NA3CT)NR%*[%Z6/HF93(Y=\1J=C[47#D"\*551#E\IZZ]1U MU4WUZG9-M5QQOYZZ74%KE2$U(A,V!)JB7,@GY8A.J8F1!O]<_K_$MY86Z0V!191>T?T=1[?PM1/6` M*SCW&:[S#5=PC(](U#]3']K;T-KIMMK=SNZ#KN`8[T+M"MO01`=J=EO?<(7V M-E:HA;[+MM5I9OK^P0LPSN66%SJ+[5RXN7GS_L--O_?J\OCR?VYNX.41[*XOISO6=C-S[!^P MD*4[^9.W9R<_WZ#=&JV5T7>G.SR*YX-LXN)X,/AX:E2X."/#F`TKA>G!V^/3 M\X]Z6C%6*:!]U7L_>(NSZC,&FGF6M9?^A2&?+L>/H/)3]8>:UA>T&GO0VJ^C MFNND6G@W'30JA^HP@,HXN/_P0"5A>]%Y^JHVV]JD(\^F,1#TX0`639Y]Z^>7 M3U/;3X7Z(B#A$-]@:#K8<3M8BJ2JR-W$I98F'5?1J?1+=ORC=QJ-D1E\II.6 M/@YOJ3:^\4:H&SPZ5+-[S<)XJ/Q7=?LI"6$5D#IZ>@B;6)P1>#3CQ,T=Z0T\ M4R54CB_!HI$=TN2BC;>`9)*IEOK4WMG]O#(CRU.*0>X#4JIC^K'8-UY3.S7$ MHE&&$P'Z3,37;?9P2AW'A@+<(B1D\]'LAF[D2+W<23@L3&BJ<:HT/!).*&ET MD$88_3Y@""S-#ST2<&6K-"C'PO=74*59P[.`?3%$H\OY7MEYEO"/%@D?80TD M)\:B3L'4&*L18^TQ%-E116R-X8Q%$[!J2)^UNM]Z(N`J.#'@+'*RX!4+M8R+H_8 MW-6;KHK4WU("8\"A:N'B81:$I M,RAJD[03+5O0TD42%HZ2BV"5PMWL8XD`57B_E65<8%$(<"H^EG?,6U&L#7@?)OKKPMP5@E`MT,F. M[W,I`^)1>286GQ#*.R]2+8%A&M,IDRKC+*EOZ(J!+J.8SOQJV=`97)=]$/KD M9,G\4,TM$C2M^]3:58U-A0T91,.F"1ZD"SWO4*!9S-Q;\RQ` MRF7/8GRI\EE,%(N?PDYKZI^(B2+CG.&B:O,1D?G$I@\5N-EHPX?>*30S$;,2 ME5$5XW*FDE*)!`]GVSRU%D)8'E'J+]=DO;\BZ97W5G/#0@#^6\9?]D8$TVKE[?F[,R113;:4.61S_?,WU):N9!?&*P!T M`;`T6T`]>'O6[^.P3'51K^:U"ZL.S+;5WLG_Y\%W)+AHX_>1O0KS:.+7-F3V M]ZW.P4XN_3Q,K76*0MA1]6RK6*UR<5NH*PJA.[^_5)!K8A8&+?CM-X;(OL[! MPI=C<35:L*TC[,J`^BI`W;1J[FF7FN[3=':LSO9"94_D7)7O2-``` ` end sysvinit-2.90/contrib/alexander.viro0000644017777601777760000000160113303017401017447 0ustar nobodynogroupI proposed moving some stuff to a seperate file, such as the re-exec routines. Alexander wrote: According to Alexander Viro : > As for the code separation - I think it's nice. Actually, read_inittab() > with get_part() and newFamily are also pretty separatable. Another good > set is any(), spawn(), startup(), spawn_emerg() and start_if_needed(). > BTW, fail_check();process_signals(); is equivalent to process_signal(); > fail_check();. I think that swapping them (in main loop) would be a good > idea - then we can move fail_check() into start_if_needed(). And one more > - I'ld propose to move start_if_needed to the beginning of the main loop, > as in > foo(); > while(1) { bar();foo(); > #if 0 > baz(); > #endif > } > to > while(1) { foo();bar(); > #if 0 > baz(); > #endif > } > > > What do you think about it? sysvinit-2.90/contrib/migrate-svn-git0000755017777601777760000000152013303017401017546 0ustar nobodynogroup#!/bin/sh # # Script to migrate sysvinit project source code from subversion to git. # Used february 2018. authorsmap=$(tempfile) cat > $authorsmap < wfink = Werner Fink EOF for p in sysvinit startpar insserv; do git svn clone http://svn.savannah.nongnu.org/svn/sysvinit/$p \ --authors-file=$authorsmap \ --no-metadata \ --tags=tags \ --trunk=trunk \ --prefix=$p/ \ $p-git ( cd $p-git for tag in `git branch -r | grep "tags/" | sed "s/ $p\/tags\///"`; do git tag -a -m"Converting SVN tags" $tag refs/remotes/$p/tags/$tag done if [ "sysvinit" = "$p" ]; then remote=$p.git else remote=sysvinit/$p.git fi git remote add origin ssh://git.savannah.gnu.org:/srv/git/$remote ) done rm $authorsmap sysvinit-2.90/contrib/notify-pam-dead.patch0000644017777601777760000001340513303017401020607 0ustar nobodynogroupIndex: src/init.sample =================================================================== --- src/init.sample (revision 0) +++ src/init.sample (revision 0) @@ -0,0 +1,9 @@ +#%PAM-1.0 +# +# The PAM configuration file for /sbin/init +# Used for updating the lastlog logging file +# +auth sufficient pam_rootok.so +account include common-account +session include common-session +session requisite pam_lastlog.so silent Index: src/init.c =================================================================== --- src/init.c (revision 56) +++ src/init.c (working copy) @@ -76,6 +76,10 @@ #include "reboot.h" #include "set.h" +#ifdef USE_PAM +extern void notify_pam_dead_session(const char *id); +#endif + #ifndef SIGPWR # define SIGPWR SIGUSR2 #endif @@ -1129,6 +1133,9 @@ } dup(f); dup(f); +#ifdef USE_PAM + notify_pam_dead_session(ch->id); +#endif } /* @@ -1548,6 +1555,9 @@ INITDBG(L_VB, "Updating utmp for pid %d [id %s]", ch->pid, ch->id); ch->flags &= ~RUNNING; +#ifdef USE_PAM + notify_pam_dead_session(ch->id); +#endif if (ch->process[0] != '+') write_utmp_wtmp("", ch->id, ch->pid, DEAD_PROCESS, NULL); } @@ -2009,6 +2019,9 @@ if (ch->flags & ZOMBIE) { INITDBG(L_VB, "Child died, PID= %d", ch->pid); ch->flags &= ~(RUNNING|ZOMBIE|WAITING); +#ifdef USE_PAM + notify_pam_dead_session(ch->id); +#endif if (ch->process[0] != '+') write_utmp_wtmp("", ch->id, ch->pid, DEAD_PROCESS, NULL); } @@ -2453,6 +2466,9 @@ if (ch->flags & ZOMBIE) { INITDBG(L_VB, "Child died, PID= %d", ch->pid); ch->flags &= ~(RUNNING|ZOMBIE|WAITING); +#ifdef USE_PAM + notify_pam_dead_session(ch->id); +#endif if (ch->process[0] != '+') write_utmp_wtmp("", ch->id, ch->pid, DEAD_PROCESS, NULL); } Index: src/utmp.c =================================================================== --- src/utmp.c (revision 51) +++ src/utmp.c (working copy) @@ -34,10 +34,18 @@ #include #include +#if defined(USE_PAM) && defined(INIT_MAIN) +# include +# include +#endif + #include "init.h" #include "initreq.h" #include "paths.h" +#ifndef _PATH_DEV +# define _PATH_DEV "/dev/" +#endif #if defined(__GLIBC__) # if (__GLIBC__ == 2) && (__GLIBC_MINOR__ == 0) && defined(__powerpc__) @@ -127,9 +135,9 @@ strncpy(utmp.ut_name, user, sizeof(utmp.ut_name)); strncpy(utmp.ut_id , id , sizeof(utmp.ut_id )); strncpy(utmp.ut_line, line, sizeof(utmp.ut_line)); - - /* Put the OS version in place of the hostname */ - if (uname(&uname_buf) == 0) + + /* Put the OS version in place of the hostname */ + if (uname(&uname_buf) == 0) strncpy(utmp.ut_host, uname_buf.release, sizeof(utmp.ut_host)); #if HAVE_UPDWTMP @@ -262,3 +270,75 @@ write_wtmp(user, id, pid, type, line && line[0] ? line : oldline); } +#if defined(USE_PAM) && defined(INIT_MAIN) +static pam_handle_t *pamh = NULL; +# ifdef __GNUC__ +static int +null_conv(int num_msg, const struct pam_message **msgm, + struct pam_response **response __attribute__((unused)), + void *appdata_ptr __attribute__((unused))) +# else +static int +null_conv(int num_msg, const struct pam_message **msgm, + struct pam_response **response, void *appdata_ptr) +# endif +{ + int i; + for (i = 0; i < num_msg; i++) { + const struct pam_message *msg = msgm[i]; + if (msg == (const struct pam_message*)0) + continue; + if (msg->msg == (char*)0) + continue; + switch (msg->msg_style) { + case PAM_ERROR_MSG: + case PAM_TEXT_INFO: + initlog(L_VB, "pam_message %s", msg->msg); + default: + break; + } + } + return 0; +} +static const struct pam_conv conv = { null_conv, NULL }; +# define PAM_FAIL_CHECK(func, args...) \ + { \ + if ((pam_ret = (func)(args)) != PAM_SUCCESS) { \ + initlog(L_VB, "%s", pam_strerror(pamh, pam_ret)); \ + goto pam_error; \ + } \ + } + +void notify_pam_dead_session(const char *id) +{ + struct utmp *oldut, ut; + + setutent(); + + memset(&ut, 0, sizeof(ut)); + ut.ut_type = DEAD_PROCESS; + strncpy(ut.ut_id, id, sizeof(ut.ut_id)); + + if ((oldut = getutid(&ut)) && (oldut->ut_type == USER_PROCESS)) { + int pam_ret; + char tty[UT_LINESIZE+ strlen(_PATH_DEV) + 1]; + + if (strncmp(oldut->ut_line, _PATH_DEV, strlen(_PATH_DEV))) + snprintf(tty, sizeof(tty), _PATH_DEV "%.*s", + UT_LINESIZE, oldut->ut_line); + else + snprintf(tty, sizeof(tty), "%.*s", + UT_LINESIZE, oldut->ut_line); + + PAM_FAIL_CHECK(pam_start, "init", oldut->ut_user, &conv, &pamh); + PAM_FAIL_CHECK(pam_set_item, pamh, PAM_TTY, tty); + PAM_FAIL_CHECK(pam_set_item, pamh, PAM_RHOST, oldut->ut_host); + PAM_FAIL_CHECK(pam_close_session, pamh, PAM_SILENT); + pam_error: + pam_end(pamh, pam_ret); + } + + endutent(); +} +#endif /* USE_PAM && INIT_MAIN */ + Index: src/Makefile =================================================================== --- src/Makefile (revision 58) +++ src/Makefile (working copy) @@ -8,7 +8,7 @@ # Version: @(#)Makefile 2.85-13 23-Mar-2004 miquels@cistron.nl # -CPPFLAGS = +CPPFLAGS = -DUSE_PAM CFLAGS ?= -ansi -O2 -fomit-frame-pointer override CFLAGS += -W -Wall -D_GNU_SOURCE STATIC = @@ -79,6 +79,13 @@ endif # Additional libs for GNU libc. +ifneq ($(findstring -DUSE_PAM,$(CPPFLAGS)),) + INITLIBS += -lpam + PAMDOTD = /etc/pam.d + PAMINIT = $(PAMDOTD)/init +endif + +# Additional libs for GNU libc. ifneq ($(wildcard /usr/lib*/libcrypt.a),) SULOGINLIBS += -lcrypt endif @@ -153,6 +160,11 @@ $(STRIP) $$i ; \ $(INSTALL_EXEC) $$i $(ROOT)/usr/bin/ ; \ done +ifneq ($(findstring -DUSE_PAM,$(CPPFLAGS)),) + $(INSTALL_DIR) $(ROOT)$(PAMDOTD) + test -s $(ROOT)$(PAMINIT) || \ + $(INSTALL_DATA) init.sample $(ROOT)$(PAMINIT) +endif # $(INSTALL_DIR) $(ROOT)/etc/ # $(INSTALL_EXEC) initscript.sample $(ROOT)/etc/ ln -sf halt $(ROOT)/sbin/reboot sysvinit-2.90/contrib/TODO0000644017777601777760000000125713303017401015302 0ustar nobodynogroup There are several things on the wishlist. See also the "wishlist" bugs filed against sysvinit in the debian bugs system (http://www.debian.org/Bugs/). 1. A special target for kbrequest, so that extra CHILDs are created (each one needs its own utmp/wtmp bookkeeping) 2. Extend the initreq.h interface? 3. Add GNU last long options to last 4. Write all boot messages to a logfile Problem: TIOCCONS ioctl redirects console output, it doesn't copy it. I think this is not easily possible without kernel support. I do not like the idea of booting with a pseudo tty as console and a redirect process behind it writing to both the real console and a logfile - too fragile. sysvinit-2.90/COPYRIGHT0000644017777601777760000000175113303017401014444 0ustar nobodynogroupSysvinit is Copyright (C) 1991-2004 Miquel van Smoorenburg Updated Copyright (C) 2018 Jesse Smith This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA On Debian GNU/Linux systems, the complete text of the GNU General Public License can be found in `/usr/share/common-licenses/GPL-2'. Send patches to sysvinit-devel@nongnu.org sysvinit-2.90/Makefile0000644017777601777760000000255413303017401014613 0ustar nobodynogroupall install clean distclean: $(MAKE) -C src $@ PACKAGE=sysvinit VERSION=$(shell sed -rn '1s/.*[[:blank:]]\((.*)\)[[:blank:]].*/\1/p' doc/Changelog) GITLOGIN=$(shell git remote -v | head -n 1 | cut -f 1 -d '@' | sed 's/origin\t//g') override TMP:=$(shell mktemp -d $(VERSION).XXXXXXXX) override TARBALL:=$(TMP)/$(PACKAGE)-$(VERSION).tar.xz override SFTPBATCH:=$(TMP)/$(VERSION)-sftpbatch SOURCES=contrib COPYING COPYRIGHT doc Makefile man README src dist: $(TARBALL) @cp $(TARBALL) . @echo "tarball $(PACKAGE)-$(VERSION).tar.bz2 ready" rm -rf $(TMP) upload: $(SFTPBATCH) echo @sftp -b $< $(GITLOGIN)@dl.sv.nongnu.org:/releases/$(PACKAGE) rm -rf $(TMP) $(SFTPBATCH): $(TARBALL).sig @echo progress > $@ @echo put $(TARBALL) >> $@ @echo chmod 664 $(notdir $(TARBALL)) >> $@ @echo put $(TARBALL).sig >> $@ @echo chmod 664 $(notdir $(TARBALL)).sig >> $@ @echo rm $(PACKAGE)-latest.tar.bz2 >> $@ @echo symlink $(notdir $(TARBALL)) $(PACKAGE)-latest.tar.bz2 >> $@ @echo quit >> $@ $(TARBALL).sig: $(TARBALL) @gpg -q -ba --use-agent -o $@ $< $(TARBALL): $(TMP)/$(PACKAGE)-$(VERSION) @tar --exclude=.git --owner=nobody --group=nogroup -cf $@ -C $(TMP) $(PACKAGE)-$(VERSION) $(TMP)/$(PACKAGE)-$(VERSION): @mkdir $(TMP)/$(PACKAGE)-$(VERSION) @cp -R $(SOURCES) $(TMP)/$(PACKAGE)-$(VERSION)/ @chmod -R a+r,u+w,og-w $@ @find $@ -type d | xargs -r chmod a+rx,u+w,og-w sysvinit-2.90/README0000644017777601777760000000234413303017401014030 0ustar nobodynogroupREADME for SysV init ==================== SysV init is a classic initilization program (PID 1) for GNU/Linux and other UNIX/POSIX systems. It is designed to be small, simple and to stay out of the way. Init is the parent (or grandparent) of all other processes on the system. It kicks off the starting of other system services and can act as a parent process to services which no longer have an active parent process. SysV init uses the concept of runlevels. A runlevel is a configuration of the system which allows only a selected group of processes to exist. The processes spawned by init for each of these runlevels are defined in the /etc/inittab file. Init can be in one of eight runlevels. The runlevel is changed by the administrator running the telinit command which selects which runlevel we want to use. More information on init, runlevels and switching between them can be found in the init manual page. (See "man init".) contrib Unofficial stuff, add-on programs doc Documentation man Manual pages src Source code For instructions on building and installing SysV init, please see the "doc/Install" file. The project home is on https://savannah.nongnu.org/projects/sysvinit Send patches to sysvinit-devel@nongnu.org . sysvinit-2.90/src/0000755017777601777760000000000013303017401013734 5ustar nobodynogroupsysvinit-2.90/src/set.h0000644017777601777760000000214013303017401014675 0ustar nobodynogroup/* * set.h Macros that look like sigaddset et al. but * aren't. They are used to manipulate bits in * an integer, to do our signal bookeeping. * * Copyright (C) 2005 Miquel van Smoorenburg. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #define ISMEMBER(set, val) ((set) & (1 << (val))) #define DELSET(set, val) ((set) &= ~(1 << (val))) #define ADDSET(set, val) ((set) |= (1 << (val))) #define EMPTYSET(set) ((set) = 0) sysvinit-2.90/src/reboot.h0000644017777601777760000000265713303017401015411 0ustar nobodynogroup/* * reboot.h Headerfile that defines how to handle * the reboot() system call. * * Version: @(#)reboot.h 2.85-17 04-Jun-2004 miquels@cistron.nl * * Copyright (C) (C) 1991-2004 Miquel van Smoorenburg. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #ifdef RB_ENABLE_CAD # define BMAGIC_HARD RB_ENABLE_CAD #endif #ifdef RB_DISABLE_CAD # define BMAGIC_SOFT RB_DISABLE_CAD #endif #ifdef RB_HALT_SYSTEM # define BMAGIC_HALT RB_HALT_SYSTEM #else # define BMAGIC_HALT RB_HALT #endif #define BMAGIC_REBOOT RB_AUTOBOOT #ifdef RB_POWER_OFF # define BMAGIC_POWEROFF RB_POWER_OFF #elif defined(RB_POWEROFF) # define BMAGIC_POWEROFF RB_POWEROFF #else # define BMAGIC_POWEROFF BMAGIC_HALT #endif #define init_reboot(magic) reboot(magic) sysvinit-2.90/src/consoles.h0000644017777601777760000000254013303017401015733 0ustar nobodynogroup/* * consoles.h Header file for routines to detect the system consoles * * Copyright (c) 2011 SuSE LINUX Products GmbH, All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program (see the file COPYING); if not, write to the * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, * MA 02110-1301, USA. * * Author: Werner Fink */ #include #include #include #include struct chardata { uint8_t erase; uint8_t kill; uint8_t eol; uint8_t parity; }; struct console { char *tty; FILE *file; uint32_t flags; int fd, id; #define CON_SERIAL 0x0001 #define CON_NOTTY 0x0002 pid_t pid; struct chardata cp; struct termios tio; struct console *next; }; extern struct console *consoles; extern int detect_consoles(const char *, int); sysvinit-2.90/src/sulogin.c0000644017777601777760000005440413303017401015567 0ustar nobodynogroup/* * sulogin This program gives Linux machines a reasonable * secure way to boot single user. It forces the * user to supply the root password before a * shell is started. * * If there is a shadow password file and the * encrypted root password is "x" the shadow * password will be used. * * Version: @(#)sulogin 2.85-3 23-Apr-2003 miquels@cistron.nl * * Copyright (C) 1998-2003 Miquel van Smoorenburg. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #include #include #include #include #ifdef __linux__ #include #endif #include #include #include #include #include /* added to make this compile on Fedora 28 */ #include #include #include #include #include #include #include #include #ifdef __linux__ # include # include # include # include # include # ifndef TMPFS_MAGIC # define TMPFS_MAGIC 0x01021994 # endif # ifndef MNT_DETACH # define MNT_DETACH 2 # endif # define dovoid(f) if ((f)){} #endif #define BS CTRL('h') #define NL CTRL('j') #define CR CTRL('m') #ifdef WITH_SELINUX # include # include #endif #include "consoles.h" #define CONMAX 16 #define CHECK_DES 1 #define CHECK_MD5 1 #define F_PASSWD "/etc/passwd" #define F_SHADOW "/etc/shadow" #define BINSH "/bin/sh" #define STATICSH "/bin/sash" char *Version = "@(#)sulogin 2.85-3 23-Apr-2003 miquels@cistron.nl"; static int timeout; static int profile; static volatile uint32_t openfd; /* Remember higher file descriptors */ static volatile uint32_t *usemask; static sighandler_t saved_sigint = SIG_DFL; static sighandler_t saved_sigtstp = SIG_DFL; static sighandler_t saved_sigquit = SIG_DFL; static sighandler_t saved_sighup = SIG_DFL; static volatile sig_atomic_t alarm_rised; static volatile sig_atomic_t sigchild; #ifndef IUCLC # define IUCLC 0 #endif /* * Fix the tty modes and set reasonable defaults. */ static void tcinit(struct console *con) { int serial, flags; struct termios *tio = &con->tio; int fd = con->fd; /* Expected error */ serial = errno = 0; /* Get line attributes */ if (tcgetattr(fd, tio) < 0) { con->flags |= CON_NOTTY; return; } /* Handle serial lines here */ if (ioctl (fd, TIOCMGET, (char*)&serial) == 0) { speed_t ispeed, ospeed; struct winsize ws; /* this is a modem line */ con->flags |= CON_SERIAL; /* Flush input and output queues on modem lines */ (void) tcflush(fd, TCIOFLUSH); ispeed = cfgetispeed(tio); ospeed = cfgetospeed(tio); if (!ispeed) ispeed = TTYDEF_SPEED; if (!ospeed) ospeed = TTYDEF_SPEED; tio->c_iflag = tio->c_lflag = tio->c_oflag = 0; tio->c_cflag = CREAD | CS8 | HUPCL | (tio->c_cflag & CLOCAL); cfsetispeed(tio, ispeed); cfsetospeed(tio, ospeed); tio->c_line = 0; tio->c_cc[VTIME] = 0; tio->c_cc[VMIN] = 1; if (ioctl(fd, TIOCGWINSZ, &ws) == 0) { int set = 0; if (ws.ws_row == 0) { ws.ws_row = 24; set++; } if (ws.ws_col == 0) { ws.ws_col = 80; set++; } (void)ioctl(fd, TIOCSWINSZ, &ws); } goto setattr; } #if defined(SANE_TIO) && (SANE_TIO == 1) /* * Use defaults of for base settings * of a local terminal line like a virtual console. */ tio->c_iflag |= TTYDEF_IFLAG; tio->c_oflag |= TTYDEF_OFLAG; tio->c_lflag |= TTYDEF_LFLAG; # ifdef CBAUD tio->c_lflag &= ~CBAUD; # endif tio->c_cflag |= (B38400 | TTYDEF_CFLAG); /* Sane setting, allow eight bit characters, no carriage return delay * the same result as `stty sane cr0 pass8' */ tio->c_iflag |= (BRKINT | ICRNL | IMAXBEL); tio->c_iflag &= ~(IGNBRK | INLCR | IGNCR | IXOFF | IUCLC | IXANY | INPCK | ISTRIP); tio->c_oflag |= (OPOST | ONLCR | NL0 | CR0 | TAB0 | BS0 | VT0 | FF0); tio->c_oflag &= ~(OLCUC | OCRNL | ONOCR | ONLRET | OFILL | OFDEL |\ NLDLY|CRDLY|TABDLY|BSDLY|VTDLY|FFDLY); tio->c_lflag |= (ISIG | ICANON | IEXTEN | ECHO|ECHOE|ECHOK|ECHOKE); tio->c_lflag &= ~(ECHONL|ECHOCTL|ECHOPRT | NOFLSH | XCASE | TOSTOP); tio->c_cflag |= (CREAD | CS8 | HUPCL); tio->c_cflag &= ~(PARODD | PARENB); /* * VTIME and VMIN can overlap with VEOF and VEOL since they are * only used for non-canonical mode. We just set the at the * beginning, so nothing bad should happen. */ tio->c_cc[VTIME] = 0; tio->c_cc[VMIN] = CMIN; tio->c_cc[VINTR] = CINTR; tio->c_cc[VQUIT] = CQUIT; tio->c_cc[VERASE] = CERASE; /* ASCII DEL (0177) */ tio->c_cc[VKILL] = CKILL; tio->c_cc[VEOF] = CEOF; # ifdef VSWTC tio->c_cc[VSWTC] = _POSIX_VDISABLE; # else tio->c_cc[VSWTCH] = _POSIX_VDISABLE; # endif tio->c_cc[VSTART] = CSTART; tio->c_cc[VSTOP] = CSTOP; tio->c_cc[VSUSP] = CSUSP; tio->c_cc[VEOL] = _POSIX_VDISABLE; tio->c_cc[VREPRINT] = CREPRINT; tio->c_cc[VDISCARD] = CDISCARD; tio->c_cc[VWERASE] = CWERASE; tio->c_cc[VLNEXT] = CLNEXT; tio->c_cc[VEOL2] = _POSIX_VDISABLE; #endif setattr: /* Set line attributes */ tcsetattr(fd, TCSANOW, tio); /* Enable blocking mode for read and write */ if ((flags = fcntl(fd, F_GETFL, 0)) != -1) (void)fcntl(fd, F_SETFL, flags & ~O_NONBLOCK); } /* * Finalize the tty modes on modem lines. */ static void tcfinal(struct console *con) { struct termios *tio = &con->tio; int fd = con->fd; /* Expected error */ errno = 0; if ((con->flags & CON_SERIAL) == 0) { #ifdef __linux__ setenv("TERM", "linux", 1); #else setenv("TERM", "vt100", 1); #endif return; } if (con->flags & CON_NOTTY) return; setenv("TERM", "vt100", 1); tio->c_iflag |= (IXON | IXOFF); tio->c_lflag |= (ICANON | ISIG | ECHO|ECHOE|ECHOK|ECHOKE); tio->c_oflag |= OPOST; tio->c_cc[VINTR] = CINTR; tio->c_cc[VQUIT] = CQUIT; tio->c_cc[VERASE] = con->cp.erase; tio->c_cc[VKILL] = con->cp.kill; tio->c_cc[VEOF] = CEOF; #ifdef VSWTC tio->c_cc[VSWTC] = _POSIX_VDISABLE; #else tio->c_cc[VSWTCH] = _POSIX_VDISABLE; #endif tio->c_cc[VSTART] = CSTART; tio->c_cc[VSTOP] = CSTOP; tio->c_cc[VSUSP] = CSUSP; tio->c_cc[VEOL] = _POSIX_VDISABLE; if (con->cp.eol == CR) { tio->c_iflag |= ICRNL; tio->c_iflag &= ~(INLCR|IGNCR); tio->c_oflag |= ONLCR; tio->c_oflag &= ~(OCRNL|ONLRET); } switch (con->cp.parity) { default: case 0: tio->c_cflag &= ~(PARODD | PARENB); tio->c_iflag &= ~(INPCK | ISTRIP); break; case 1: /* odd parity */ tio->c_cflag |= PARODD; /* fall through */ case 2: /* even parity */ tio->c_cflag |= PARENB; tio->c_iflag |= (INPCK | ISTRIP); /* fall through */ case (1 | 2): /* no parity bit */ tio->c_cflag &= ~CSIZE; tio->c_cflag |= CS7; break; } /* Set line attributes */ (void)tcsetattr(fd, TCSANOW, tio); } /* * Called at timeout. */ static # ifdef __GNUC__ __attribute__((__noinline__)) void alrm_handler(int sig __attribute__((unused))) # else void alrm_handler(int sig) # endif { alarm_rised++; } /* * Called at timeout. */ static # ifdef __GNUC__ __attribute__((__noinline__)) void chld_handler(int sig __attribute__((unused))) # else void chld_handler(int sig) # endif { sigchild++; } /* * See if an encrypted password is valid. The encrypted * password is checked for traditional-style DES and * FreeBSD-style MD5 encryption. */ static int valid(const char *pass) { const char *s; char id[5]; size_t len; off_t off; if (pass[0] == 0) return 1; #if CHECK_MD5 if (pass[0] != '$') goto check_des; /* * up to 4 bytes for the signature e.g. $1$ */ for(s = pass+1; *s && *s != '$'; s++) ; if (*s++ != '$') return 0; if ((off = (off_t)(s-pass)) > 4 || off < 3) return 0; memset(id, '\0', sizeof(id)); strncpy(id, pass, off); /* * up to 16 bytes for the salt */ for(; *s && *s != '$'; s++) ; if (*s++ != '$') return 0; if ((off_t)(s-pass) > 16) return 0; len = strlen(s); /* * the MD5 hash (128 bits or 16 bytes) encoded in base64 = 22 bytes */ if ((strcmp(id, "$1$") == 0) && (len < 22 || len > 24)) return 0; /* * the SHA-256 hash 43 bytes */ if ((strcmp(id, "$5$") == 0) && (len < 42 || len > 44)) return 0; /* * the SHA-512 hash 86 bytes */ if ((strcmp(id, "$6$") == 0) && (len < 85 || len > 87)) return 0; /* * e.g. Blowfish hash */ return 1; check_des: #endif #if CHECK_DES if (strlen(pass) != 13) return 0; for (s = pass; *s; s++) { if ((*s < '0' || *s > '9') && (*s < 'a' || *s > 'z') && (*s < 'A' || *s > 'Z') && *s != '.' && *s != '/') return 0; } #endif return 1; } /* * Set a variable if the value is not NULL. */ static void set(char **var, char *val) { if (val) *var = val; } /* * Get the root password entry. */ static struct passwd *getrootpwent(int try_manually) { static struct passwd pwd; struct passwd *pw; struct spwd *spw; FILE *fp; static char line[256]; static char sline[256]; char *p; /* * First, we try to get the password the standard * way using normal library calls. */ if ((pw = getpwnam("root")) && !strcmp(pw->pw_passwd, "x") && (spw = getspnam("root"))) pw->pw_passwd = spw->sp_pwdp; if (pw || !try_manually) return pw; /* * If we come here, we could not retrieve the root * password through library calls and we try to * read the password and shadow files manually. */ pwd.pw_name = "root"; pwd.pw_passwd = ""; pwd.pw_gecos = "Super User"; pwd.pw_dir = "/"; pwd.pw_shell = ""; pwd.pw_uid = 0; pwd.pw_gid = 0; if ((fp = fopen(F_PASSWD, "r")) == NULL) { perror(F_PASSWD); return &pwd; } /* * Find root in the password file. */ while((p = fgets(line, 256, fp)) != NULL) { if (strncmp(line, "root:", 5) != 0) continue; p += 5; set(&pwd.pw_passwd, strsep(&p, ":")); (void)strsep(&p, ":"); (void)strsep(&p, ":"); set(&pwd.pw_gecos, strsep(&p, ":")); set(&pwd.pw_dir, strsep(&p, ":")); set(&pwd.pw_shell, strsep(&p, "\n")); p = line; break; } fclose(fp); /* * If the encrypted password is valid * or not found, return. */ if (p == NULL) { fprintf(stderr, "sulogin: %s: no entry for root\n\r", F_PASSWD); return &pwd; } if (valid(pwd.pw_passwd)) return &pwd; /* * The password is invalid. If there is a * shadow password, try it. */ strcpy(pwd.pw_passwd, ""); if ((fp = fopen(F_SHADOW, "r")) == NULL) { fprintf(stderr, "sulogin: %s: root password garbled\n\r", F_PASSWD); return &pwd; } while((p = fgets(sline, 256, fp)) != NULL) { if (strncmp(sline, "root:", 5) != 0) continue; p += 5; set(&pwd.pw_passwd, strsep(&p, ":")); break; } fclose(fp); /* * If the password is still invalid, * NULL it, and return. */ if (p == NULL) { fprintf(stderr, "sulogin: %s: no entry for root\n\r", F_SHADOW); strcpy(pwd.pw_passwd, ""); } if (!valid(pwd.pw_passwd)) { fprintf(stderr, "sulogin: %s: root password garbled\n\r", F_SHADOW); strcpy(pwd.pw_passwd, ""); } return &pwd; } /* * Ask by prompt for the password. */ static void doprompt(const char *crypted, struct console *con) { struct termios tty; if (con->flags & CON_SERIAL) { tty = con->tio; /* * For prompting: map NL in output to CR-NL * otherwise we may see stairs in the output. */ tty.c_oflag |= (ONLCR | OPOST); (void) tcsetattr(con->fd, TCSADRAIN, &tty); } if (con->file == (FILE*)0) { if ((con->file = fdopen(con->fd, "r+")) == (FILE*)0) goto err; } #if defined(USE_ONELINE) if (crypted[0]) fprintf(con->file, "Give root password for login: "); else fprintf(con->file, "Press enter for login: "); #else if (crypted[0]) fprintf(con->file, "Give root password for maintenance\n\r"); else fprintf(con->file, "Press enter for maintenance"); fprintf(con->file, "(or type Control-D to continue): "); #endif fflush(con->file); err: if (con->flags & CON_SERIAL) (void) tcsetattr(con->fd, TCSADRAIN, &con->tio); } /* * Make sure to have an own session and controlling terminal */ static void setup(struct console *con) { pid_t pid, pgrp, ppgrp, ttypgrp; int fd; if (con->flags & CON_NOTTY) return; fd = con->fd; /* * Only go through this trouble if the new * tty doesn't fall in this process group. */ pid = getpid(); pgrp = getpgid(0); ppgrp = getpgid(getppid()); ttypgrp = tcgetpgrp(fd); if (pgrp != ttypgrp && ppgrp != ttypgrp) { if (pid != getsid(0)) { if (pid == getpgid(0)) setpgid(0, getpgid(getppid())); setsid(); } signal(SIGHUP, SIG_IGN); if (ttypgrp > 0) ioctl(0, TIOCNOTTY, (char *)1); signal(SIGHUP, saved_sighup); if (fd > 0) close(0); if (fd > 1) close(1); if (fd > 2) close(2); ioctl(fd, TIOCSCTTY, (char *)1); tcsetpgrp(fd, ppgrp); } dup2(fd, 0); dup2(fd, 1); dup2(fd, 2); con->fd = 0; for (fd = 3; fd < 32; fd++) { if (openfd & (1<flags & CON_NOTTY) goto out; fd = con->fd; cp = &con->cp; tty = con->tio; tty.c_iflag &= ~(IUCLC|IXON|IXOFF|IXANY); tty.c_lflag &= ~(ECHO|ECHOE|ECHOK|ECHONL|TOSTOP|ISIG); tc = (tcsetattr(fd, TCSAFLUSH, &tty) == 0); sa.sa_handler = alrm_handler; sa.sa_flags = 0; sigaction(SIGALRM, &sa, NULL); if (timeout) alarm(timeout); ptr = &pass[0]; cp->eol = *ptr = '\0'; eightbit = ((con->flags & CON_SERIAL) == 0 || (tty.c_cflag & (PARODD|PARENB)) == 0); while (cp->eol == '\0') { if (read(fd, &c, 1) < 1) { if (errno == EINTR || errno == EAGAIN) { usleep(1000); continue; } ret = (char*)0; switch (errno) { case 0: case EIO: case ESRCH: case EINVAL: case ENOENT: break; default: fprintf(stderr, "sulogin: read(%s): %m\n\r", con->tty); break; } goto quit; } if (eightbit) ascval = c; else if (c != (ascval = (c & 0177))) { uint32_t bits, mask; for (bits = 1, mask = 1; mask & 0177; mask <<= 1) { if (mask & ascval) bits++; } cp->parity |= ((bits & 1) ? 1 : 2); } switch (ascval) { case 0: *ptr = '\0'; goto quit; case CR: case NL: *ptr = '\0'; cp->eol = ascval; break; case BS: case CERASE: cp->erase = ascval; if (ptr > &pass[0]) ptr--; break; case CKILL: cp->kill = ascval; while (ptr > &pass[0]) ptr--; break; case CEOF: goto quit; default: if ((size_t)(ptr - &pass[0]) >= (sizeof(pass) -1 )) { fprintf(stderr, "sulogin: input overrun at %s\n\r", con->tty); ret = (char*)0; goto quit; } *ptr++ = ascval; break; } } quit: alarm(0); if (tc) (void)tcsetattr(fd, TCSAFLUSH, &con->tio); if (ret && *ret != '\0') tcfinal(con); printf("\r\n"); out: return ret; } /* * Password was OK, execute a shell. */ static void sushell(struct passwd *pwd) { char shell[128]; char home[128]; char *p; char *sushell; /* * Set directory and shell. */ if (chdir(pwd->pw_dir) < 0) { if (chdir("/") < 0) fprintf(stderr, "sulogin: change of working directory failed: %m\n\r"); } if ((p = getenv("SUSHELL")) != NULL) sushell = p; else if ((p = getenv("sushell")) != NULL) sushell = p; else { if (pwd->pw_shell[0]) sushell = pwd->pw_shell; else sushell = BINSH; } if ((p = strrchr(sushell, '/')) == NULL) p = sushell; else p++; snprintf(shell, sizeof(shell), profile ? "-%s" : "%s", p); /* * Set some important environment variables. */ if (getcwd(home, sizeof(home)) == (char*)0) strcpy(home, "/"); setenv("HOME", home, 1); setenv("LOGNAME", "root", 1); setenv("USER", "root", 1); if (!profile) setenv("SHLVL","0",1); /* * Try to execute a shell. */ setenv("SHELL", sushell, 1); signal(SIGINT, saved_sigint); signal(SIGTSTP, saved_sigtstp); signal(SIGQUIT, saved_sigquit); signal(SIGHUP, SIG_DFL); #ifdef WITH_SELINUX if (is_selinux_enabled() > 0) { security_context_t scon=NULL; char *seuser=NULL; char *level=NULL; if (getseuserbyname("root", &seuser, &level) == 0) if (get_default_context_with_level(seuser, level, 0, &scon) == 0) { if (setexeccon(scon) != 0) fprintf(stderr, "sulogin: setexeccon failed\n\r"); freecon(scon); } free(seuser); free(level); } #endif execl(sushell, shell, NULL); perror(sushell); setenv("SHELL", BINSH, 1); execl(BINSH, profile ? "-sh" : "sh", NULL); perror(BINSH); /* Fall back to staticly linked shell if both the users shell and /bin/sh failed to execute. */ setenv("SHELL", STATICSH, 1); execl(STATICSH, STATICSH, NULL); perror(STATICSH); } #ifdef __linux__ /* * Make C library standard calls like ttyname(3) work. */ static uint32_t mounts; #define MNT_PROCFS 0x0001 #define MNT_DEVTMPFS 0x0002 static __attribute__((__noinline__)) void putmounts(void) { if (mounts & MNT_DEVTMPFS) umount2("/dev", MNT_DETACH); if (mounts & MNT_PROCFS) umount2("/proc", MNT_DETACH); } static __attribute__((__constructor__)) void getmounts(void) { struct statfs st; if (statfs("/proc", &st) == 0 && st.f_type != PROC_SUPER_MAGIC) { if (mount("proc", "/proc", "proc", MS_RELATIME, NULL) == 0) mounts |= MNT_PROCFS; } if (statfs("/dev", &st) == 0 && st.f_type != TMPFS_MAGIC) { if (mount("devtmpfs", "/dev", "devtmpfs", MS_RELATIME, "mode=0755,nr_inodes=0") == 0) { mounts |= MNT_DEVTMPFS; (void)mknod("/dev/console", S_IFCHR|S_IRUSR|S_IWUSR, makedev(TTYAUX_MAJOR, 1)); if (symlink("/proc/self/fd", "/dev/fd") == 0) { dovoid(symlink("fd/0", "/dev/stdin")); dovoid(symlink("fd/1", "/dev/stdout")); dovoid(symlink("fd/2", "/dev/stderr")); } } } if (mounts) atexit(putmounts); } #endif static void usage(void) { fprintf(stderr, "Usage: sulogin [-e] [-p] [-t timeout] [tty device]\n\r"); } int main(int argc, char **argv) { char *tty = NULL; struct passwd *pwd; int c, status = 0; int reconnect = 0; int opt_e = 0; struct console *con; pid_t pid; /* * We are init. We hence need to set uo a session. */ if ((pid = getpid()) == 1) { setsid(); (void)ioctl(0, TIOCSCTTY, (char *)1); } /* * See if we have a timeout flag. */ opterr = 0; while((c = getopt(argc, argv, "ept:")) != EOF) switch(c) { case 't': timeout = atoi(optarg); break; case 'p': profile = 1; break; case 'e': opt_e = 1; break; default: usage(); /* Do not exit! */ break; } if (geteuid() != 0) { fprintf(stderr, "sulogin: only root can run sulogin.\n\r"); exit(1); } saved_sigint = signal(SIGINT, SIG_IGN); saved_sigquit = signal(SIGQUIT, SIG_IGN); saved_sigtstp = signal(SIGTSTP, SIG_IGN); saved_sighup = signal(SIGHUP, SIG_IGN); /* * See if we need to open an other tty device. */ if (optind < argc) tty = argv[optind]; if (!tty || *tty == '\0') tty = getenv("CONSOLE"); /* * Detect possible consoles, use stdin as fallback. * If an optional tty is given, reconnect it to stdin. */ reconnect = detect_consoles(tty, 0); /* * Should not happen */ if (!consoles) { if (!errno) errno = ENOMEM; fprintf(stderr, "sulogin: cannot open console: %m\n\r"); exit(1); } /* * If previous stdin was not the speified tty and therefore reconnected * to the specified tty also reconnect stdout and stderr. */ if (reconnect) { if (isatty(1) == 0) dup2(0, 1); if (isatty(2) == 0) dup2(0, 2); } /* * Get the root password. */ if ((pwd = getrootpwent(opt_e)) == NULL) { fprintf(stderr, "sulogin: cannot open password database!\n\r"); sleep(2); } /* * Prompt for input on the consoles */ for (con = consoles; con && con->id < CONMAX; con = con->next) { if (con->fd >= 0) { openfd |= (1<fd); tcinit(con); continue; } if ((con->fd = open(con->tty, O_RDWR | O_NOCTTY | O_NONBLOCK)) < 0) continue; openfd |= (1<fd); tcinit(con); } con = consoles; usemask = (uint32_t*)mmap(NULL, sizeof(uint32_t), PROT_READ|PROT_WRITE, MAP_ANONYMOUS|MAP_SHARED, -1, 0); if (con->next == (struct console*)0) goto nofork; signal(SIGCHLD, chld_handler); do { switch ((con->pid = fork())) { case 0: signal(SIGCHLD, SIG_DFL); /* fall through */ nofork: setup(con); while (1) { char *passwd = pwd->pw_passwd; char *answer; int failed = 0, doshell = 0; doprompt(passwd, con); if ((answer = getpasswd(con)) == NULL) break; if (passwd[0] == '\0') doshell++; else { char *cryptbuf; cryptbuf = crypt(answer, passwd); if (cryptbuf == NULL) fprintf(stderr, "sulogin: crypt failed: %m\n\r"); else if (strcmp(cryptbuf, pwd->pw_passwd) == 0) doshell++; } if (doshell) { *usemask |= (1<id); sushell(pwd); *usemask &= ~(1<id); failed++; } signal(SIGQUIT, SIG_IGN); signal(SIGTSTP, SIG_IGN); signal(SIGINT, SIG_IGN); if (failed) { fprintf(stderr, "sulogin: can not execute su shell.\n\r"); break; } fprintf(stderr, "Login incorrect.\n\r"); sleep(3); } if (alarm_rised) { tcfinal(con); fprintf(stderr, "Timed out.\n\r"); } /* * User may pressed Control-D. */ exit(0); case -1: fprintf(stderr, "sulogin: can not fork: %m\n\r"); /* fall through */ default: break; } } while ((con = con->next) && (con->id < CONMAX)); while ((pid = wait(&status))) { if (errno == ECHILD) break; if (pid < 0) continue; for (con = consoles; con && con->id < CONMAX; con = con->next) { if (con->pid == pid) { *usemask &= ~(1<id); continue; } if (kill(con->pid, 0) < 0) { *usemask &= ~(1<id); continue; } if (*usemask & (1<id)) continue; kill(con->pid, SIGHUP); usleep(5000); kill(con->pid, SIGKILL); } } signal(SIGCHLD, SIG_DFL); return 0; } sysvinit-2.90/src/fstab-decode.c0000644017777601777760000000357713303017401016434 0ustar nobodynogroup/* fstab-decode(8). Copyright (c) 2006 Red Hat, Inc. All rights reserved. This copyrighted material is made available to anyone wishing to use, modify, copy, or redistribute it subject to the terms and conditions of the GNU General Public License v.2. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Author: Miloslav Trmac */ #include #include #include #include #include /* Decode the fstab-encoded string in place. */ static void decode(char *s) { const char *src; char *dest; src = s; dest = s; while (*src != '\0') { if (*src != '\\') *dest = *src++; else { static const struct repl { char orig[4]; size_t len; char new; } repls[] = { #define R(X, Y) { X, sizeof(X) - 1, Y } R("\\", '\\'), R("011", '\t'), R("012", '\n'), R("040", ' '), R("134", '\\') #undef R }; size_t i; for (i = 0; i < sizeof (repls) / sizeof (repls[0]); i++) { if (memcmp(src + 1, repls[i].orig, repls[i].len) == 0) { *dest = repls[i].new; src += 1 + repls[i].len; goto found; } } *dest = *src++; found: ; } dest++; } *dest = '\0'; } int main (int argc, char *argv[]) { size_t i; if (argc < 2) { fprintf(stderr, "Usage: fstab-decode command [arguments]\n"); return EXIT_FAILURE; } for (i = 2; i < (size_t)argc; i++) decode(argv[i]); execvp(argv[1], argv + 1); fprintf(stderr, "fstab-decode: %s: %s\n", argv[1], strerror(errno)); return 127; } sysvinit-2.90/src/halt.c0000644017777601777760000001624213303017401015035 0ustar nobodynogroup/* * Halt Stop the system running. * It re-enables CTRL-ALT-DEL, so that a hard reboot can * be done. If called as reboot, it will reboot the system. * * If the system is not in runlevel 0 or 6, halt will just * execute a "shutdown -h" to halt the system, and reboot will * execute an "shutdown -r". This is for compatibility with * sysvinit 2.4. * * Usage: halt [-n] [-w] [-d] [-f] [-h] [-i] [-p] * -n: don't sync before halting the system * -w: only write a wtmp reboot record and exit. * -d: don't write a wtmp record. * -f: force halt/reboot, don't call shutdown. * -h: put harddisks in standby mode * -i: shut down all network interfaces. * -p: power down the system (if possible, otherwise halt). * * Reboot and halt are both this program. Reboot * is just a link to halt. Invoking the program * as poweroff implies the -p option. * * Author: Miquel van Smoorenburg, miquels@cistron.nl * * Version: 2.86, 30-Jul-2004 * * This file is part of the sysvinit suite, * Copyright (C) 1991-2004 Miquel van Smoorenburg. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "reboot.h" char *Version = "@(#)halt 2.86 31-Jul-2004 miquels@cistron.nl"; char *progname; #define KERNEL_MONITOR 1 /* If halt() puts you into the kernel monitor. */ #define RUNLVL_PICKY 0 /* Be picky about the runlevel */ extern int ifdown(void); extern int hddown(void); extern int hdflush(void); extern void write_wtmp(char *user, char *id, int pid, int type, char *line); /* * Send usage message. */ void usage(void) { fprintf(stderr, "usage: %s [-n] [-w] [-d] [-f] [-h] [-i]%s\n", progname, strcmp(progname, "halt") ? "" : " [-p]"); fprintf(stderr, "\t-n: don't sync before halting the system\n"); fprintf(stderr, "\t-w: only write a wtmp reboot record and exit.\n"); fprintf(stderr, "\t-d: don't write a wtmp record.\n"); fprintf(stderr, "\t-f: force halt/reboot, don't call shutdown.\n"); fprintf(stderr, "\t-h: put harddisks in standby mode.\n"); fprintf(stderr, "\t-i: shut down all network interfaces.\n"); if (!strcmp(progname, "halt")) fprintf(stderr, "\t-p: power down the system (if possible, otherwise halt).\n"); exit(1); } /* * See if we were started directly from init. * Get the runlevel from /var/run/utmp or the environment. */ int get_runlevel(void) { struct utmp *ut; char *r; #if RUNLVL_PICKY time_t boottime; #endif /* * First see if we were started directly from init. */ if (getenv("INIT_VERSION") && (r = getenv("RUNLEVEL")) != NULL) return *r; /* * Hmm, failed - read runlevel from /var/run/utmp.. */ #if RUNLVL_PICKY /* * Get boottime from the kernel. */ time(&boottime); boottime -= (times(NULL) / HZ); #endif /* * Find runlevel in utmp. */ setutent(); while ((ut = getutent()) != NULL) { #if RUNLVL_PICKY /* * Only accept value if it's from after boottime. */ if (ut->ut_type == RUN_LVL && ut->ut_time > boottime) return (ut->ut_pid & 255); #else if (ut->ut_type == RUN_LVL) return (ut->ut_pid & 255); #endif } endutent(); /* This should not happen but warn the user! */ fprintf(stderr, "WARNING: could not determine runlevel" " - doing soft %s\n", progname); fprintf(stderr, " (it's better to use shutdown instead of %s" " from the command line)\n", progname); return -1; } /* * Switch to another runlevel. */ void do_shutdown(char *fl, char *tm) { char *args[8]; int i = 0; args[i++] = "shutdown"; args[i++] = fl; if (tm) { args[i++] = "-t"; args[i++] = tm; } args[i++] = "now"; args[i++] = NULL; execv("/sbin/shutdown", args); execv("/etc/shutdown", args); execv("/bin/shutdown", args); perror("shutdown"); exit(1); } /* * Main program. * Write a wtmp entry and reboot cq. halt. */ int main(int argc, char **argv) { int do_reboot = 0; int do_sync = 1; int do_wtmp = 1; int do_nothing = 0; int do_hard = 0; int do_ifdown = 0; int do_hddown = 0; int do_poweroff = 0; int c; char *tm = NULL; /* * Find out who we are */ /* Remove dash passed on in argv[0] when used as login shell. */ if (argv[0][0] == '-') argv[0]++; if ((progname = strrchr(argv[0], '/')) != NULL) progname++; else progname = argv[0]; if (!strcmp(progname, "reboot")) do_reboot = 1; if (!strcmp(progname, "poweroff")) do_poweroff = 1; /* * Get flags */ while((c = getopt(argc, argv, ":ihdfnpwt:")) != EOF) { switch(c) { case 'n': do_sync = 0; do_wtmp = 0; break; case 'w': do_nothing = 1; break; case 'd': do_wtmp = 0; break; case 'f': do_hard = 1; break; case 'i': do_ifdown = 1; break; case 'h': do_hddown = 1; break; case 'p': do_poweroff = 1; break; case 't': tm = optarg; break; default: usage(); } } if (argc != optind) usage(); if (geteuid() != 0) { fprintf(stderr, "%s: must be superuser.\n", progname); exit(1); } if (chdir("/")) { fprintf(stderr, "%s: chdir(/): %m\n", progname); exit(1); } if (!do_hard && !do_nothing) { /* * See if we are in runlevel 0 or 6. */ c = get_runlevel(); if (c != '0' && c != '6') do_shutdown(do_reboot ? "-r" : "-h", tm); } /* * Record the fact that we're going down */ if (do_wtmp) write_wtmp("shutdown", "~~", 0, RUN_LVL, "~~"); /* * Exit if all we wanted to do was write a wtmp record. */ if (do_nothing && !do_hddown && !do_ifdown) exit(0); if (do_sync) { sync(); /* Sync should be fine on its own for making sure data is written. We probably call shutdown after this anyway to clean up. -- Jesse sleep(2); */ } if (do_ifdown) (void)ifdown(); if (do_hddown) (void)hddown(); else (void)hdflush(); if (do_nothing) exit(0); if (do_reboot) { init_reboot(BMAGIC_REBOOT); } else { /* * Turn on hard reboot, CTRL-ALT-DEL will reboot now */ #ifdef BMAGIC_HARD init_reboot(BMAGIC_HARD); #endif /* * Stop init; it is insensitive to the signals sent * by the kernel. */ kill(1, SIGTSTP); /* * Halt or poweroff. */ if (do_poweroff) init_reboot(BMAGIC_POWEROFF); /* * Fallthrough if failed. */ init_reboot(BMAGIC_HALT); } /* * If we return, we (c)ontinued from the kernel monitor. */ #ifdef BMAGIC_SOFT init_reboot(BMAGIC_SOFT); #endif kill(1, SIGCONT); exit(0); } sysvinit-2.90/src/killall5.c0000644017777601777760000006116213303017401015617 0ustar nobodynogroup/* * killall5.c Kill all processes except processes that have the * same session id, so that the shell that called us * won't be killed. Typically used in shutdown scripts. * * pidof.c Tries to get the pid of the process[es] named. * * Version: 2.86 30-Jul-2004 MvS * * Usage: killall5 [-][signal] * pidof [-s] [-o omitpid [-o omitpid]] program [program..] * * Authors: Miquel van Smoorenburg, miquels@cistron.nl * * Riku Meskanen, * - return all running pids of given program name * - single shot '-s' option for backwards combatibility * - omit pid '-o' option and %PPID (parent pid metavariable) * - syslog() only if not a connected to controlling terminal * - swapped out programs pids are caught now * * Werner Fink * - make omit dynamic * - provide '-n' to skip stat(2) syscall on network based FS * * This file is part of the sysvinit suite, * Copyright (C) 1991-2004 Miquel van Smoorenburg. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include char *Version = "@(#)killall5 2.86 31-Jul-2004 miquels@cistron.nl"; #ifndef PATH_MAX # ifdef MAXPATHLEN # define PATH_MAX MAXPATHLEN # else # define PATH_MAX 2048 # endif #endif #define STATNAMELEN 15 #define DO_NETFS 2 #define DO_STAT 1 #define NO_STAT 0 /* Info about a process. */ typedef struct proc { char *pathname; /* full path to executable */ char *argv0; /* Name as found out from argv[0] */ char *argv0base; /* `basename argv[1]` */ char *argv1; /* Name as found out from argv[1] */ char *argv1base; /* `basename argv[1]` */ char *statname; /* the statname without braces */ ino_t ino; /* Inode number */ dev_t dev; /* Device it is on */ pid_t pid; /* Process ID. */ pid_t sid; /* Session ID. */ char kernel; /* Kernel thread or zombie. */ char nfs; /* Name found on network FS. */ struct proc *next; /* Pointer to next struct. */ } PROC; /* pid queue */ typedef struct pidq { PROC *proc; struct pidq *next; } PIDQ; typedef struct { PIDQ *head; PIDQ *tail; PIDQ *next; } PIDQ_HEAD; typedef struct _s_omit { struct _s_omit *next; struct _s_omit *prev; pid_t pid; } OMIT; typedef struct _s_shadow { struct _s_shadow *next; struct _s_shadow *prev; size_t nlen; char * name; } SHADOW; typedef struct _s_nfs { struct _s_nfs *next; /* Pointer to next struct. */ struct _s_nfs *prev; /* Pointer to previous st. */ SHADOW *shadow; /* Pointer to shadows */ size_t nlen; char * name; } NFS; /* List of processes. */ PROC *plist; /* List of processes to omit. */ OMIT *omit; /* List of NFS mountes partitions. */ NFS *nlist; /* Did we stop all processes ? */ int sent_sigstop; int scripts_too = 0; char *progname; /* the name of the running program */ #ifdef __GNUC__ __attribute__ ((format (printf, 2, 3))) #endif void nsyslog(int pri, char *fmt, ...); #if !defined(__STDC_VERSION__) || (__STDC_VERSION__ < 199901L) # ifndef inline # define inline __inline__ # endif # ifndef restrict # define restrict __restrict__ # endif #endif #define alignof(type) ((sizeof(type)+(sizeof(void*)-1)) & ~(sizeof(void*)-1)) /* * Malloc space, barf if out of memory. */ #ifdef __GNUC__ static void *xmalloc(size_t) __attribute__ ((__malloc__)); #endif static void *xmalloc(size_t bytes) { void *p; if ((p = malloc(bytes)) == NULL) { if (sent_sigstop) kill(-1, SIGCONT); nsyslog(LOG_ERR, "out of memory"); exit(1); } return p; } #ifdef __GNUC__ static inline void xmemalign(void **, size_t, size_t) __attribute__ ((__nonnull__ (1))); #endif static inline void xmemalign(void **memptr, size_t alignment, size_t size) { if ((posix_memalign(memptr, alignment, size)) < 0) { if (sent_sigstop) kill(-1, SIGCONT); nsyslog(LOG_ERR, "out of memory"); exit(1); } } /* * See if the proc filesystem is there. Mount if needed. */ int mount_proc(void) { struct stat st; char *args[] = { "mount", "-t", "proc", "proc", "/proc", 0 }; pid_t pid, rc; int wst; int did_mount = 0; /* Stat /proc/version to see if /proc is mounted. */ if (stat("/proc/version", &st) < 0 && errno == ENOENT) { /* It's not there, so mount it. */ if ((pid = fork()) < 0) { nsyslog(LOG_ERR, "cannot fork"); exit(1); } if (pid == 0) { /* Try a few mount binaries. */ execv("/bin/mount", args); execv("/sbin/mount", args); /* Okay, I give up. */ nsyslog(LOG_ERR, "cannot execute mount"); exit(1); } /* Wait for child. */ while ((rc = wait(&wst)) != pid) if (rc < 0 && errno == ECHILD) break; if (rc != pid || WEXITSTATUS(wst) != 0) nsyslog(LOG_ERR, "mount returned non-zero exit status"); did_mount = 1; } /* See if mount succeeded. */ if (stat("/proc/version", &st) < 0) { if (errno == ENOENT) nsyslog(LOG_ERR, "/proc not mounted, failed to mount."); else nsyslog(LOG_ERR, "/proc unavailable."); exit(1); } return did_mount; } static inline int isnetfs(const char * type) { static const char* netfs[] = {"nfs", "nfs4", "smbfs", "cifs", "afs", "ncpfs", (char*)0}; int n; for (n = 0; netfs[n]; n++) { if (!strcasecmp(netfs[n], type)) return 1; } return 0; } /* * Remember all NFS typed partitions. */ void init_nfs(void) { struct stat st; struct mntent * ent; FILE * mnt; nlist = (NFS*)0; if (stat("/proc/version", &st) < 0) return; if ((mnt = setmntent("/proc/mounts", "r")) == (FILE*)0) return; while ((ent = getmntent(mnt))) { if (isnetfs(ent->mnt_type)) { size_t nlen = strlen(ent->mnt_dir); NFS *restrict p; xmemalign((void*)&p, sizeof(void*), alignof(NFS)+(nlen+1)); p->name = ((char*)p)+alignof(NFS); p->nlen = nlen; p->shadow = (SHADOW*)0; strcpy(p->name, ent->mnt_dir); if (nlist) nlist->prev = p; p->next = nlist; p->prev = (NFS*)0; nlist = p; } } endmntent(mnt); if ((mnt = setmntent("/proc/mounts", "r")) == (FILE*)0) return; while ((ent = getmntent(mnt))) { NFS *p; for (p = nlist; p; p = p->next) { SHADOW * restrict s; size_t nlen; if (strcmp(ent->mnt_dir, p->name) == 0) continue; if (strncmp(ent->mnt_dir, p->name, p->nlen) != 0) continue; nlen = strlen(ent->mnt_dir); xmemalign((void*)&s, sizeof(void*), alignof(SHADOW)+(nlen+1)); s->name = ((char*)s)+alignof(SHADOW); s->nlen = nlen; strcpy(s->name, ent->mnt_dir); if (p->shadow) p->shadow->prev = s; s->next = p->shadow; s->prev = (SHADOW*)0; p->shadow = s; } } endmntent(mnt); } static void clear_shadow(SHADOW *restrict shadow) { SHADOW *s, *n, *l; n = shadow; l = (SHADOW*)0; for (s = shadow; n; s = n) { l = s->prev; n = s->next; if (s == shadow) { if (n) n->prev = (SHADOW*)0; shadow = n; } else if (l) { if (n) n->prev = l; l->next = n; } free(s); } } static void clear_mnt(void) { NFS *p, *n, *l; n = nlist; l = (NFS*)0; for (p = nlist; n; p = n) { l = p->prev; n = p->next; if (p == nlist) { if (n) n->prev = (NFS*)0; nlist = n; } else if (l) { if (n) n->prev = l; l->next = n; } if (p->shadow) clear_shadow(p->shadow); free(p); } } /* * Check if path is a shadow off a NFS partition. */ static int shadow(SHADOW *restrict this, const char *restrict name, const size_t nlen) { SHADOW *s; if (!this) goto out; for (s = this; s; s = s->next) { if (nlen < s->nlen) continue; if (name[s->nlen] != '\0' && name[s->nlen] != '/') continue; if (strncmp(name, s->name, s->nlen) == 0) return 1; } out: return 0; } /* * Get the maximal number of symlinks to follow. Use sysconf() on * Hurd where the hardcoded value MAXSYMLINKS is not available. */ static int maxsymlinks(void) { int v = sysconf(_SC_SYMLOOP_MAX); #ifdef MAXSYMLINKS if (v == -1) return MAXSYMLINKS; #endif return v; } /* * Check path is located on a network based partition. */ int check4nfs(const char * path, char * real) { char buf[PATH_MAX+1]; const char *curr; int deep = maxsymlinks(); if (!nlist) return 0; curr = path; do { const char *prev; int len; if ((prev = strdupa(curr)) == NULL) { nsyslog(LOG_ERR, "strdupa(): %s\n", strerror(errno)); return 0; } errno = 0; if ((len = readlink(curr, buf, PATH_MAX)) < 0) break; buf[len] = '\0'; if (buf[0] != '/') { const char *slash; if ((slash = strrchr(prev, '/'))) { size_t off = slash - prev + 1; if (off + len > PATH_MAX) len = PATH_MAX - off; memmove(&buf[off], &buf[0], len + 1); memcpy(&buf[0], prev, off); } } curr = &buf[0]; if (deep-- <= 0) return 0; } while (1); if (real) strcpy(real, curr); if (errno == EINVAL) { const size_t nlen = strlen(curr); NFS *p; for (p = nlist; p; p = p->next) { if (nlen < p->nlen) continue; if (curr[p->nlen] != '\0' && curr[p->nlen] != '/') continue; if (!strncmp(curr, p->name, p->nlen)) { if (shadow(p->shadow, curr, nlen)) continue; return 1; } } } return 0; } int readarg(FILE *fp, char *buf, int sz) { int c = 0, f = 0; while (f < (sz-1) && (c = fgetc(fp)) != EOF && c) buf[f++] = c; buf[f] = 0; return (c == EOF && f == 0) ? c : f; } /* * Read the proc filesystem. * CWD must be /proc to avoid problems if / is affected by the killing (ie depend on fuse). */ int readproc(int do_stat) { DIR *dir; FILE *fp; PROC *p, *n; struct dirent *d; struct stat st; char path[PATH_MAX+1]; char buf[PATH_MAX+1]; char *s, *q; unsigned long startcode, endcode; int pid, f; ssize_t len; /* Open the /proc directory. */ if (chdir("/proc") == -1) { nsyslog(LOG_ERR, "chdir /proc failed"); return -1; } if ((dir = opendir(".")) == NULL) { nsyslog(LOG_ERR, "cannot opendir(/proc)"); return -1; } /* Free the already existing process list. */ n = plist; for (p = plist; n; p = n) { n = p->next; if (p->argv0) free(p->argv0); if (p->argv1) free(p->argv1); if (p->statname) free(p->statname); free(p->pathname); free(p); } plist = NULL; /* Walk through the directory. */ while ((d = readdir(dir)) != NULL) { /* See if this is a process */ if ((pid = atoi(d->d_name)) == 0) continue; /* Get a PROC struct . */ p = (PROC *)xmalloc(sizeof(PROC)); memset(p, 0, sizeof(PROC)); /* Open the status file. */ snprintf(path, sizeof(path), "%s/stat", d->d_name); /* Read SID & statname from it. */ if ((fp = fopen(path, "r")) != NULL) { size_t len; len = fread(buf, sizeof(char), sizeof(buf)-1, fp); buf[len] = '\0'; if (buf[0] == '\0') { nsyslog(LOG_ERR, "can't read from %s\n", path); fclose(fp); free(p); continue; } /* See if name starts with '(' */ s = buf; while (*s && *s != ' ') s++; if (*s) s++; if (*s == '(') { /* Read program name. */ q = strrchr(buf, ')'); if (q == NULL) { p->sid = 0; nsyslog(LOG_ERR, "can't get program name from /proc/%s\n", path); fclose(fp); if (p->argv0) free(p->argv0); if (p->argv1) free(p->argv1); if (p->statname) free(p->statname); free(p->pathname); free(p); continue; } s++; } else { q = s; while (*q && *q != ' ') q++; } if (*q) *q++ = 0; while (*q == ' ') q++; p->statname = (char *)xmalloc(strlen(s)+1); strcpy(p->statname, s); /* Get session, startcode, endcode. */ startcode = endcode = 0; if (sscanf(q, "%*c %*d %*d %d %*d %*d %*u %*u " "%*u %*u %*u %*u %*u %*d %*d " "%*d %*d %*d %*d %*u %*u %*d " "%*u %lu %lu", &p->sid, &startcode, &endcode) != 3) { p->sid = 0; nsyslog(LOG_ERR, "can't read sid from %s\n", path); fclose(fp); if (p->argv0) free(p->argv0); if (p->argv1) free(p->argv1); if (p->statname) free(p->statname); free(p->pathname); free(p); continue; } if (startcode == 0 && endcode == 0) p->kernel = 1; fclose(fp); } else { /* Process disappeared.. */ if (p->argv0) free(p->argv0); if (p->argv1) free(p->argv1); if (p->statname) free(p->statname); free(p->pathname); free(p); continue; } snprintf(path, sizeof(path), "%s/cmdline", d->d_name); if ((fp = fopen(path, "r")) != NULL) { /* Now read argv[0] */ f = readarg(fp, buf, sizeof(buf)); if (buf[0]) { /* Store the name into malloced memory. */ p->argv0 = (char *)xmalloc(f + 1); strcpy(p->argv0, buf); /* Get a pointer to the basename. */ p->argv0base = strrchr(p->argv0, '/'); if (p->argv0base != NULL) p->argv0base++; else p->argv0base = p->argv0; } /* And read argv[1] */ while ((f = readarg(fp, buf, sizeof(buf))) != EOF) if (buf[0] != '-') break; if (buf[0]) { /* Store the name into malloced memory. */ p->argv1 = (char *)xmalloc(f + 1); strcpy(p->argv1, buf); /* Get a pointer to the basename. */ p->argv1base = strrchr(p->argv1, '/'); if (p->argv1base != NULL) p->argv1base++; else p->argv1base = p->argv1; } fclose(fp); } else { /* Process disappeared.. */ if (p->argv0) free(p->argv0); if (p->argv1) free(p->argv1); if (p->statname) free(p->statname); free(p->pathname); free(p); continue; } /* Try to stat the executable. */ snprintf(path, sizeof(path), "/proc/%s/exe", d->d_name); p->nfs = 0; switch (do_stat) { case DO_NETFS: if ((p->nfs = check4nfs(path, buf))) goto link; /* else fall through */ case DO_STAT: if (stat(path, &st) != 0) { char * ptr; len = readlink(path, buf, PATH_MAX); if (len <= 0) break; buf[len] = '\0'; ptr = strstr(buf, " (deleted)"); if (!ptr) break; *ptr = '\0'; len -= strlen(" (deleted)"); if (stat(buf, &st) != 0) break; p->dev = st.st_dev; p->ino = st.st_ino; p->pathname = (char *)xmalloc(len + 1); memcpy(p->pathname, buf, len); p->pathname[len] = '\0'; /* All done */ break; } p->dev = st.st_dev; p->ino = st.st_ino; /* Fall through */ default: link: len = readlink(path, buf, PATH_MAX); if (len > 0) { p->pathname = (char *)xmalloc(len + 1); memcpy(p->pathname, buf, len); p->pathname[len] = '\0'; } break; } /* Link it into the list. */ p->next = plist; plist = p; p->pid = pid; } closedir(dir); /* Done. */ return 0; } PIDQ_HEAD *init_pid_q(PIDQ_HEAD *q) { q->head = q->next = q->tail = NULL; return q; } int empty_q(PIDQ_HEAD *q) { return (q->head == NULL); } int add_pid_to_q(PIDQ_HEAD *q, PROC *p) { PIDQ *tmp; tmp = (PIDQ *)xmalloc(sizeof(PIDQ)); tmp->proc = p; tmp->next = NULL; if (empty_q(q)) { q->head = tmp; q->tail = tmp; } else { q->tail->next = tmp; q->tail = tmp; } return 0; } PROC *get_next_from_pid_q(PIDQ_HEAD *q) { PROC *p; PIDQ *tmp = q->head; if (!empty_q(q)) { p = q->head->proc; q->head = tmp->next; free(tmp); return p; } return NULL; } /* Try to get the process ID of a given process. */ PIDQ_HEAD *pidof(char *prog) { PROC *p; PIDQ_HEAD *q; struct stat st; char *s; int nfs = 0; int dostat = 0; int foundone = 0; int ok = 0; const int root = (getuid() == 0); char real[PATH_MAX+1]; if (! prog) return NULL; /* Try to stat the executable. */ if (prog[0] == '/') { memset(&real[0], 0, sizeof(real)); if (check4nfs(prog, real)) nfs++; if (real[0] != '\0') prog = &real[0]; /* Binary located on network FS. */ if ((nfs == 0) && (stat(prog, &st) == 0)) dostat++; /* Binary located on a local FS. */ } /* Get basename of program. */ if ((s = strrchr(prog, '/')) == NULL) s = prog; else s++; if (! *s) return NULL; q = (PIDQ_HEAD *)xmalloc(sizeof(PIDQ_HEAD)); q = init_pid_q(q); /* First try to find a match based on dev/ino pair. */ if (dostat && !nfs) { for (p = plist; p; p = p->next) { if (p->nfs) continue; if (p->dev == st.st_dev && p->ino == st.st_ino) { add_pid_to_q(q, p); foundone++; } } } /* Second try to find a match based on full path name on * network FS located binaries */ if (!foundone && nfs) { for (p = plist; p; p = p->next) { if (!p->pathname) continue; if (!p->nfs) continue; if (strcmp(prog, p->pathname) != 0) continue; add_pid_to_q(q, p); foundone++; } } /* If we didn't find a match based on dev/ino, try the name. */ if (!foundone) for (p = plist; p; p = p->next) { if (prog[0] == '/') { if (!p->pathname) { if (root) continue; goto fallback; } if (strcmp(prog, p->pathname)) { int len = strlen(prog); if (strncmp(prog, p->pathname, len)) { if (scripts_too) goto fallback; continue; } if (strcmp(" (deleted)", p->pathname + len)) { if (scripts_too) goto fallback; continue; } } add_pid_to_q(q, p); continue; } fallback: ok = 0; /* matching nonmatching * proc name prog name prog name * --- ----------- ------------ * b b, p/b, q/b * p/b b, p/b q/b * * Algorithm: Match if: * cmd = arg * or cmd = base(arg) * or base(cmd) = arg * * Specifically, do not match just because base(cmd) = base(arg) * as was done in earlier versions of this program, since this * allows /aaa/foo to match /bbb/foo . */ ok |= (p->argv0 && strcmp(p->argv0, prog) == 0) || (p->argv0 && s != prog && strcmp(p->argv0, s) == 0) || (p->argv0base && strcmp(p->argv0base, prog) == 0); /* For scripts, compare argv[1] as well. */ if ( scripts_too && p->statname && p->argv1base && !strncmp(p->statname, p->argv1base, STATNAMELEN) ) { ok |= (p->argv1 && strcmp(p->argv1, prog) == 0) || (p->argv1 && s != prog && strcmp(p->argv1, s) == 0) || (p->argv1base && strcmp(p->argv1base, prog) == 0); } /* * if we have a space in argv0, process probably * used setproctitle so try statname. */ if (strlen(s) <= STATNAMELEN && (p->argv0 == NULL || p->argv0[0] == 0 || strchr(p->argv0, ' '))) { ok |= (strcmp(p->statname, s) == 0); } /* * if we have a `-' as the first character, process * probably used as a login shell */ if (strlen(s) <= STATNAMELEN && p->argv1 == NULL && (p->argv0 != NULL && p->argv0[0] == '-')) { ok |= (strcmp(p->statname, s) == 0); } if (ok) add_pid_to_q(q, p); } return q; } /* Give usage message and exit. */ void usage(void) { nsyslog(LOG_ERR, "only one argument, a signal number, allowed"); closelog(); exit(1); } /* write to syslog file if not open terminal */ #ifdef __GNUC__ __attribute__ ((format (printf, 2, 3))) #endif void nsyslog(int pri, char *fmt, ...) { va_list args; va_start(args, fmt); if (ttyname(0) == NULL) { vsyslog(pri, fmt, args); } else { fprintf(stderr, "%s: ",progname); vfprintf(stderr, fmt, args); fprintf(stderr, "\n"); } va_end(args); } #define PIDOF_SINGLE 0x01 #define PIDOF_OMIT 0x02 #define PIDOF_NETFS 0x04 /* * Pidof functionality. */ int main_pidof(int argc, char **argv) { PIDQ_HEAD *q; PROC *p; char *token, *here; int f; int first = 1; int opt, flags = 0; int chroot_check = 0; struct stat st; char tmp[512]; omit = (OMIT*)0; nlist = (NFS*)0; opterr = 0; if ((token = getenv("PIDOF_NETFS")) && (strcmp(token,"no") != 0)) flags |= PIDOF_NETFS; while ((opt = getopt(argc,argv,"hco:sxn")) != EOF) switch (opt) { case '?': nsyslog(LOG_ERR,"invalid options on command line!\n"); closelog(); exit(1); case 'c': if (geteuid() == 0) chroot_check = 1; break; case 'o': here = optarg; while ((token = strsep(&here, ",;:"))) { OMIT *restrict optr; pid_t opid; if (strcmp("%PPID", token) == 0) opid = getppid(); else opid = (pid_t)atoi(token); if (opid < 1) { nsyslog(LOG_ERR, "illegal omit pid value " "(%s)!\n", token); continue; } xmemalign((void*)&optr, sizeof(void*), alignof(OMIT)); optr->next = omit; optr->prev = (OMIT*)0; optr->pid = opid; omit = optr; } flags |= PIDOF_OMIT; break; case 's': flags |= PIDOF_SINGLE; break; case 'x': scripts_too++; break; case 'n': flags |= PIDOF_NETFS; break; default: /* Nothing */ break; } argc -= optind; argv += optind; /* Check if we are in a chroot */ if (chroot_check) { snprintf(tmp, 512, "/proc/%d/root", getpid()); if (stat(tmp, &st) < 0) { nsyslog(LOG_ERR, "stat failed for %s!\n", tmp); closelog(); exit(1); } } if (flags & PIDOF_NETFS) init_nfs(); /* Which network based FS are online? */ /* Print out process-ID's one by one. */ readproc((flags & PIDOF_NETFS) ? DO_NETFS : DO_STAT); for(f = 0; f < argc; f++) { if ((q = pidof(argv[f])) != NULL) { pid_t spid = 0; while ((p = get_next_from_pid_q(q))) { if ((flags & PIDOF_OMIT) && omit) { OMIT * optr; for (optr = omit; optr; optr = optr->next) { if (optr->pid == p->pid) break; } /* * On a match, continue with * the for loop above. */ if (optr) continue; } if (flags & PIDOF_SINGLE) { if (spid) continue; else spid = 1; } if (chroot_check) { struct stat st2; snprintf(tmp, 512, "/proc/%d/root", p->pid); if (stat(tmp, &st2) < 0 || st.st_dev != st2.st_dev || st.st_ino != st2.st_ino) { continue; } } if (!first) printf(" "); printf("%d", p->pid); first = 0; } } } if (!first) printf("\n"); clear_mnt(); closelog(); return(first ? 1 : 0); } /* Main for either killall or pidof. */ int main(int argc, char **argv) { PROC *p; int pid, sid = -1; int sig = SIGKILL; int c; /* return non-zero if no process was killed */ int retval = 2; /* Get program name. */ if ((progname = strrchr(argv[0], '/')) == NULL) progname = argv[0]; else progname++; /* Now connect to syslog. */ openlog(progname, LOG_CONS|LOG_PID, LOG_DAEMON); /* Were we called as 'pidof' ? */ if (strcmp(progname, "pidof") == 0) return main_pidof(argc, argv); /* Right, so we are "killall". */ omit = (OMIT*)0; if (argc > 1) { for (c = 1; c < argc; c++) { if (argv[c][0] == '-') (argv[c])++; if (argv[c][0] == 'o') { char * token, * here; if (++c >= argc) usage(); here = argv[c]; while ((token = strsep(&here, ",;:"))) { OMIT *restrict optr; pid_t opid = (pid_t)atoi(token); if (opid < 1) { nsyslog(LOG_ERR, "illegal omit pid value " "(%s)!\n", token); continue; } xmemalign((void*)&optr, sizeof(void*), alignof(OMIT)); optr->next = omit; optr->prev = (OMIT*)0; optr->pid = opid; omit = optr; } } else if ((sig = atoi(argv[1])) <= 0 || sig > 31) usage(); } } /* First get the /proc filesystem online. */ mount_proc(); /* * Ignoring SIGKILL and SIGSTOP do not make sense, but * someday kill(-1, sig) might kill ourself if we don't * do this. This certainly is a valid concern for SIGTERM- * Linux 2.1 might send the calling process the signal too. */ signal(SIGTERM, SIG_IGN); signal(SIGSTOP, SIG_IGN); signal(SIGKILL, SIG_IGN); /* lock us into memory */ mlockall(MCL_CURRENT | MCL_FUTURE); /* Now stop all processes. */ kill(-1, SIGSTOP); sent_sigstop = 1; /* Read /proc filesystem */ if (readproc(NO_STAT) < 0) { kill(-1, SIGCONT); return(1); } /* Now kill all processes except init (pid 1) and our session. */ sid = (int)getsid(0); pid = (int)getpid(); for (p = plist; p; p = p->next) { if (p->pid == 1 || p->pid == pid || p->sid == sid || p->kernel) continue; if (omit) { OMIT * optr; for (optr = omit; optr; optr = optr->next) { if (optr->pid == p->pid) break; } /* On a match, continue with the for loop above. */ if (optr) continue; } kill(p->pid, sig); retval = 0; } /* And let them continue. */ kill(-1, SIGCONT); /* Done. */ closelog(); /* Force the kernel to run the scheduler */ usleep(1); return retval; } sysvinit-2.90/src/mountpoint.c0000644017777601777760000001115013303017401016312 0ustar nobodynogroup/* * mountpoint See if a directory is a mountpoint. * * Author: Miquel van Smoorenburg. * * Version: @(#)mountpoint 2.85-12 17-Mar-2004 miquels@cistron.nl * * This file is part of the sysvinit suite, * Copyright (C) 1991-2004 Miquel van Smoorenburg. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include #include int dostat(char *path, struct stat *st, int do_lstat, int quiet) { int n; if (do_lstat) n = lstat(path, st); else n = stat(path, st); if (n != 0) { if (!quiet) fprintf(stderr, "mountpoint: %s: %s\n", path, strerror(errno)); return -1; } return 0; } /* This function checks to see if the passed path is listed in the /proc/mounts file. If /proc/mounts does not exist or cannot be read, we return false. If the path is nout found, we return false. If the path is found we return true. */ int do_proc_check(char *path) { FILE *mounts; char *found = NULL, *status; char *target_string; char line[512]; int last_character; target_string = (char *) calloc( strlen(path) + 3, sizeof(char)); if (! target_string) return 0; mounts = fopen("/proc/mounts", "r"); if (! mounts) { free(target_string); return 0; } /* copy path so we can adjust it without harming the original */ sprintf(target_string, "%s", path); /* trim trailing slash */ last_character = strlen(target_string) - 1; if ( (last_character >= 1) && (target_string[last_character] == '/') ) target_string[last_character] = '\0'; /* Search for path name in /proc/mounts file */ status = fgets(line, 512, mounts); while ( (status) && (! found) ) { found = strstr(line, target_string); if (! found) status = fgets(line, 512, mounts); } fclose(mounts); free(target_string); return found ? 1 : 0; } void usage(void) { fprintf(stderr, "Usage: mountpoint [-p] [-q] [-d] [-x] path\n"); exit(1); } int main(int argc, char **argv) { struct stat st, st2; char buf[256]; char *path; int quiet = 0; int showdev = 0; int xdev = 0; int c, r; int check_proc = 0; while ((c = getopt(argc, argv, "dpqx")) != EOF) switch(c) { case 'd': showdev = 1; break; case 'p': check_proc = 1; break; case 'q': quiet = 1; break; case 'x': xdev = 1; break; default: usage(); break; } if (optind != argc - 1) usage(); path = argv[optind]; if (dostat(path, &st, !xdev, quiet) < 0) return 1; if (xdev) { #ifdef __linux__ if (!S_ISBLK(st.st_mode)) #else if (!S_ISBLK(st.st_mode) && !S_ISCHR(st.st_mode)) #endif { if (quiet) printf("\n"); else fprintf(stderr, "mountpoint: %s: not a block device\n", path); return 1; } printf("%u:%u\n", major(st.st_rdev), minor(st.st_rdev)); return 0; } if (!S_ISDIR(st.st_mode)) { if (!quiet) fprintf(stderr, "mountpoint: %s: not a directory\n", path); return 1; } memset(buf, 0, sizeof(buf)); strncpy(buf, path, sizeof(buf) - 4); strcat(buf, "/.."); if (dostat(buf, &st2, 0, quiet) < 0) return 1; /* r = ( (st.st_dev != st2.st_dev) || ( (st.st_dev == st2.st_dev) && (st.st_ino == st2.st_ino) ) ; (A || (!A && B)) is the same as (A || B) so simplifying this below. Thanks to David Binderman for pointing this out. -- Jesse */ r = ( (st.st_dev != st2.st_dev) || (st.st_ino == st2.st_ino) ); /* Mount point was not found yet. If we have access to /proc we can check there too. */ if ( (!r) && (check_proc) ) { if ( do_proc_check(path) ) r = 1; } if (!quiet && !showdev) printf("%s is %sa mountpoint\n", path, r ? "" : "not "); if (showdev) printf("%u:%u\n", major(st.st_dev), minor(st.st_dev)); return r ? 0 : 1; } sysvinit-2.90/src/utmp.c0000644017777601777760000001537713303017401015102 0ustar nobodynogroup/* * utmp.c Routines to read/write the utmp and wtmp files. * Basically just wrappers around the library routines. * * Version: @(#)utmp.c 2.77 09-Jun-1999 miquels@cistron.nl * * Copyright (C) 1999 Miquel van Smoorenburg. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #include #include #include #include #include #include #include #include #include #include #include #include #include "init.h" #include "initreq.h" #include "paths.h" #if defined(__GLIBC__) # if (__GLIBC__ == 2) && (__GLIBC_MINOR__ == 0) && defined(__powerpc__) # define HAVE_UPDWTMP 0 # else # define HAVE_UPDWTMP 1 # endif #else # define HAVE_UPDWTMP 0 #endif /* * Log an event in the wtmp file (reboot, runlevel) */ void write_wtmp( char *user, /* name of user */ char *id, /* inittab ID */ int pid, /* PID of process */ int type, /* TYPE of entry */ char *line) /* Which line is this */ { int fd; struct utmp utmp; struct utsname uname_buf; struct timeval tv; /* * Can't do much if WTMP_FILE is not present or not writable. */ if (access(WTMP_FILE, W_OK) < 0) return; /* * Try to open the wtmp file. Note that we even try * this if we have updwtmp() so we can see if the * wtmp file is accessible. */ if ((fd = open(WTMP_FILE, O_WRONLY|O_APPEND)) < 0) return; #ifdef INIT_MAIN /* * Note if we are going to write a boot record. */ if (type == BOOT_TIME) wrote_wtmp_reboot++; /* * See if we need to write a reboot record. The reason that * we are being so paranoid is that when we first tried to * write the reboot record, /var was possibly not mounted * yet. As soon as we can open WTMP we write a delayed boot record. */ if (wrote_wtmp_reboot == 0 && type != BOOT_TIME) write_wtmp("reboot", "~~", 0, BOOT_TIME, "~"); /* * Note if we are going to write a runlevel record. */ if (type == RUN_LVL) wrote_wtmp_rlevel++; /* * See if we need to write a runlevel record. The reason that * we are being so paranoid is that when we first tried to * write the reboot record, /var was possibly not mounted * yet. As soon as we can open WTMP we write a delayed runlevel record. */ if (wrote_wtmp_rlevel == 0 && type != RUN_LVL) { int runlevel = thislevel; int oldlevel = prevlevel; write_wtmp("runlevel", "~~", runlevel + 256 * oldlevel, RUN_LVL, "~"); } #endif /* * Zero the fields and enter new fields. */ memset(&utmp, 0, sizeof(utmp)); #if defined(__GLIBC__) gettimeofday(&tv, NULL); utmp.ut_tv.tv_sec = tv.tv_sec; utmp.ut_tv.tv_usec = tv.tv_usec; #else time(&utmp.ut_time); #endif utmp.ut_pid = pid; utmp.ut_type = type; strncpy(utmp.ut_name, user, sizeof(utmp.ut_name)); strncpy(utmp.ut_id , id , sizeof(utmp.ut_id )); strncpy(utmp.ut_line, line, sizeof(utmp.ut_line)); /* Put the OS version in place of the hostname */ if (uname(&uname_buf) == 0) strncpy(utmp.ut_host, uname_buf.release, sizeof(utmp.ut_host)); #if HAVE_UPDWTMP updwtmp(WTMP_FILE, &utmp); #else write(fd, (char *)&utmp, sizeof(utmp)); #endif close(fd); } /* * Write an entry to the UTMP file. For DEAD_PROCESS, put * the previous ut_line into oldline if oldline != NULL. */ static void write_utmp( char *user, /* name of user */ char *id, /* inittab ID */ int pid, /* PID of process */ int type, /* TYPE of entry */ char *line, /* LINE if used. */ char *oldline) /* Line of old utmp entry. */ { struct utmp utmp; struct utmp tmp; struct utmp *utmptr; struct timeval tv; /* * Can't do much if UTMP_FILE is not present or not writable. */ if (access(UTMP_FILE, W_OK) < 0) return; #ifdef INIT_MAIN /* * Note if we are going to write a boot record. */ if (type == BOOT_TIME) wrote_utmp_reboot++; /* * See if we need to write a reboot record. The reason that * we are being so paranoid is that when we first tried to * write the reboot record, /var was possibly not mounted * yet. As soon as we can open UTMP we write a delayed boot record. */ if (wrote_utmp_reboot == 0 && type != BOOT_TIME) write_utmp("reboot", "~~", 0, BOOT_TIME, "~", NULL); /* * Note if we are going to write a runlevel record. */ if (type == RUN_LVL) wrote_utmp_rlevel++; /* * See if we need to write a runlevel record. The reason that * we are being so paranoid is that when we first tried to * write the reboot record, /var was possibly not mounted * yet. As soon as we can open UTMP we write a delayed runlevel record. */ if (wrote_utmp_rlevel == 0 && type != RUN_LVL) { int runlevel = thislevel; int oldlevel = prevlevel; write_utmp("runlevel", "~~", runlevel + 256 * oldlevel, RUN_LVL, "~", NULL); } #endif /* * Fill out an utmp struct. */ memset(&utmp, 0, sizeof(utmp)); utmp.ut_type = type; utmp.ut_pid = pid; strncpy(utmp.ut_id, id, sizeof(utmp.ut_id)); #if defined(__GLIBC__) gettimeofday(&tv, NULL); utmp.ut_tv.tv_sec = tv.tv_sec; utmp.ut_tv.tv_usec = tv.tv_usec; #else time(&utmp.ut_time); #endif strncpy(utmp.ut_user, user, UT_NAMESIZE); if (line) strncpy(utmp.ut_line, line, UT_LINESIZE); /* * We might need to find the existing entry first, to * find the tty of the process (for wtmp accounting). */ if (type == DEAD_PROCESS) { /* * Find existing entry for the tty line. */ setutent(); tmp = utmp; if ((utmptr = getutid(&tmp)) != NULL) { strncpy(utmp.ut_line, utmptr->ut_line, UT_LINESIZE); if (oldline) strncpy(oldline, utmptr->ut_line, UT_LINESIZE); } } /* * Update existing utmp file. */ setutent(); pututline(&utmp); endutent(); } /* * Write a record to both utmp and wtmp. */ void write_utmp_wtmp( char *user, /* name of user */ char *id, /* inittab ID */ int pid, /* PID of process */ int type, /* TYPE of entry */ char *line) /* LINE if used. */ { char oldline[UT_LINESIZE]; /* * For backwards compatibility we just return * if user == NULL (means : clean up utmp file). */ if (user == NULL) return; oldline[0] = 0; write_utmp(user, id, pid, type, line, oldline); write_wtmp(user, id, pid, type, line && line[0] ? line : oldline); } sysvinit-2.90/src/Makefile0000644017777601777760000001257213303017401015403 0ustar nobodynogroup# # 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 ?= -ansi -O2 -fomit-frame-pointer -fstack-protector override CFLAGS += -W -Wall -Wunreachable-code -Wformat -Werror=format-security -D_FORTIFY_SOURCE=2 -D_XOPEN_SOURCE -D_GNU_SOURCE 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 USRBIN = last mesg MAN1 = last.1 lastb.1 mesg.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 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 += -lsepol -lselinux SULOGINLIBS = -lselinux else SELINUX_DEF = INITLIBS = SULOGINLIBS = endif # Additional libs for GNU libc. ifneq ($(wildcard /usr/lib*/libcrypt.*),) SULOGINLIBS += -lcrypt endif # Additional libs for GNU libc / multiarch on Debian based systems. ifneq ($(wildcard /usr/lib/*/libcrypt.*),) SULOGINLIBS += -lcrypt endif all: $(BIN) $(SBIN) $(USRBIN) #%: %.o # $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $^ $(LDLIBS) #%.o: %.c # $(CC) $(CFLAGS) $(CPPFLAGS) -c $^ -o $@ init: LDLIBS += $(INITLIBS) $(STATIC) init: init.o init_utmp.o halt: LDLIBS += $(STATIC) halt: halt.o ifdown.o hddown.o utmp.o last: LDLIBS += $(STATIC) last: last.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 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 fstab-decode: LDLIBS += $(STATIC) fstab-decode: fstab-decode.o sulogin.o: CPPFLAGS += $(SELINUX_DEF) sulogin.o: sulogin.c init.o: CPPFLAGS += $(SELINUX_DEF) init.o: init.c init.h initreq.h paths.h reboot.h 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 $@ $< utmpdump.o: utmpdump.c oldutmp.h shutdown.o: shutdown.c paths.h reboot.h initreq.h init.h halt.o: halt.c reboot.h last.o: last.c oldutmp.h consoles.o: consoles.c consoles.h cleanobjs: rm -f *.o *.bak clean: cleanobjs @echo Type \"make clobber\" to really clean up. clobber: cleanobjs rm -f $(BIN) $(SBIN) $(USRBIN) distclean: clobber install: $(INSTALL_DIR) $(ROOT)/bin/ $(ROOT)/sbin/ $(INSTALL_DIR) $(ROOT)/usr/bin/ for i in $(BIN); do \ $(INSTALL_EXEC) $$i $(ROOT)/bin/ ; \ done for i in $(SBIN); do \ $(INSTALL_EXEC) $$i $(ROOT)/sbin/ ; \ done for i in $(USRBIN); do \ $(INSTALL_EXEC) $$i $(ROOT)/usr/bin/ ; \ done # $(INSTALL_DIR) $(ROOT)/etc/ # $(INSTALL_EXEC) ../doc/initscript.sample $(ROOT)/etc/ ln -sf halt $(ROOT)/sbin/reboot ln -sf halt $(ROOT)/sbin/poweroff ln -sf init $(ROOT)/sbin/telinit ln -sf /sbin/killall5 $(ROOT)/bin/pidof if [ ! -f $(ROOT)/usr/bin/lastb ]; then \ ln -sf last $(ROOT)/usr/bin/lastb; \ fi $(INSTALL_DIR) $(ROOT)/usr/include/ $(INSTALL_DATA) initreq.h $(ROOT)/usr/include/ $(INSTALL_DIR) $(ROOT)$(MANDIR)/man1/ $(INSTALL_DIR) $(ROOT)$(MANDIR)/man5/ $(INSTALL_DIR) $(ROOT)$(MANDIR)/man8/ for man in $(MAN1); do \ $(INSTALL_DATA) ../man/$$man $(ROOT)$(MANDIR)/man1/; \ sed -i "1{ $(MANDB); }" $(ROOT)$(MANDIR)/man1/$$man ; \ done for man in $(MAN5); do \ $(INSTALL_DATA) ../man/$$man $(ROOT)$(MANDIR)/man5/; \ sed -i "1{ $(MANDB); }" $(ROOT)$(MANDIR)/man5/$$man ; \ done for man in $(MAN8); do \ $(INSTALL_DATA) ../man/$$man $(ROOT)$(MANDIR)/man8/; \ sed -i "1{ $(MANDB); }" $(ROOT)$(MANDIR)/man8/$$man ; \ done ifeq ($(ROOT),) # # This part is skipped on Debian systems, the # debian.preinst script takes care of it. @if [ ! -p /run/initctl ]; then \ echo "Creating /run/initctl"; \ rm -f /run/initctl; \ mknod -m 600 /run/initctl p; fi endif sysvinit-2.90/src/consoles.c0000644017777601777760000002420613303017401015731 0ustar nobodynogroup/* * consoles.c Routines to detect the system consoles * * Copyright (c) 2011 SuSE LINUX Products GmbH, All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program (see the file COPYING); if not, write to the * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, * MA 02110-1301, USA. * * Author: Werner Fink */ #include #include #include #include #include #include #include #include #ifdef __linux__ # include # include # include #include #endif #include #include #include #include "consoles.h" #ifdef __linux__ # include #endif #if !defined(__STDC_VERSION__) || (__STDC_VERSION__ < 199901L) # ifndef typeof # define typeof __typeof__ # endif # ifndef restrict # define restrict __restrict__ # endif #endif #define alignof(type) ((sizeof(type)+(sizeof(void*)-1)) & ~(sizeof(void*)-1)) struct console *consoles; /* * Read and allocate one line from file, * the caller has to free the result */ static #ifdef __GNUC__ __attribute__((__nonnull__)) #endif char *oneline(const char *file) { FILE *fp; char *ret = (char*)0, *nl; size_t len = 0; if ((fp = fopen(file, "re")) == (FILE*)0) goto err; if (getline(&ret, &len, fp) < 0) goto out; if (len) ret[len-1] = '\0'; if ((nl = strchr(ret, '\n'))) *nl = '\0'; out: fclose(fp); err: return ret; } #ifdef __linux__ /* * Read and determine active attribute for tty below * /sys/class/tty, the caller has to free the result. */ static __attribute__((__malloc__)) char *actattr(const char *tty) { char *ret = (char*)0; char *path; if (!tty || *tty == '\0') goto err; if (asprintf(&path, "/sys/class/tty/%s/active", tty) < 0) goto err; if ((ret = oneline(path)) == (char*)0) goto out; out: free(path); err: return ret; } /* * Read and determine device attribute for tty below * /sys/class/tty. */ static dev_t devattr(const char *tty) { unsigned int maj, min; dev_t dev = 0; char *path, *value; if (!tty || *tty == '\0') goto err; if (asprintf(&path, "/sys/class/tty/%s/dev", tty) < 0) goto err; if ((value = oneline(path)) == (char*)0) goto out; if (sscanf(value, "%u:%u", &maj, &min) == 2) dev = makedev(maj, min); free(value); out: free(path); err: return dev; } #endif /* __linux__ */ /* * Search below /dev for the characer device in * the local `dev_t comparedev' variable. */ static dev_t comparedev; static #ifdef __GNUC__ __attribute__((__nonnull__,__malloc__,__hot__)) #endif char* scandev(DIR *dir) { char *name = (char*)0; struct dirent *dent; int fd; fd = dirfd(dir); rewinddir(dir); while ((dent = readdir(dir))) { char path[PATH_MAX]; struct stat st; if (fstatat(fd, dent->d_name, &st, 0) < 0) continue; if (!S_ISCHR(st.st_mode)) continue; if (comparedev != st.st_rdev) continue; if ((size_t)snprintf(path, sizeof(path), "/dev/%s", dent->d_name) >= sizeof(path)) continue; name = realpath(path, NULL); break; } return name; } /* * Default control characters for an unknown terminal line. */ static struct chardata initcp = { CERASE, CKILL, CTRL('r'), 0 }; /* * Allocate an aligned `struct console' memory area, * initialize its default values, and append it to * the global linked list. */ static int concount; /* Counter for console IDs */ static #ifdef __GNUC__ __attribute__((__nonnull__,__hot__)) #endif void consalloc(char * name) { struct console *restrict tail; if (posix_memalign((void*)&tail, sizeof(void*), alignof(typeof(struct console))) != 0) perror("memory allocation"); tail->next = (struct console*)0; tail->tty = name; tail->file = (FILE*)0; tail->flags = 0; tail->fd = -1; tail->id = concount++; tail->pid = 0; memset(&tail->tio, 0, sizeof(tail->tio)); memcpy(&tail->cp, &initcp, sizeof(struct chardata)); if (!consoles) consoles = tail; else consoles->next = tail; } /* * Try to detect the real device(s) used for the system console * /dev/console if but only if /dev/console is used. On Linux * this can be more than one device, e.g. a serial line as well * as a virtual console as well as a simple printer. * * Returns 1 if stdout and stderr should be reconnected and 0 * otherwise. */ int detect_consoles(const char *device, int fallback) { int fd, ret = 0; #ifdef __linux__ char *attrib, *cmdline; FILE *fc; #endif if (!device || *device == '\0') fd = dup(fallback); else { fd = open(device, O_RDWR|O_NONBLOCK|O_NOCTTY|O_CLOEXEC); ret = 1; } if (fd >= 0) { DIR *dir; char *name; struct stat st; #ifdef TIOCGDEV unsigned int devnum; #endif if (fstat(fd, &st) < 0) { close(fd); goto fallback; } comparedev = st.st_rdev; if (ret && (fstat(fallback, &st) < 0 || comparedev != st.st_rdev)) dup2(fd, fallback); #ifdef __linux__ /* * Check if the device detection for Linux system console should be used. */ if (comparedev == makedev(TTYAUX_MAJOR, 0)) { /* /dev/tty */ close(fd); device = "/dev/tty"; goto fallback; } if (comparedev == makedev(TTYAUX_MAJOR, 1)) { /* /dev/console */ close(fd); goto console; } if (comparedev == makedev(TTYAUX_MAJOR, 2)) { /* /dev/ptmx */ close(fd); device = "/dev/tty"; goto fallback; } if (comparedev == makedev(TTY_MAJOR, 0)) { /* /dev/tty0 */ struct vt_stat vt; if (ioctl(fd, VT_GETSTATE, &vt) < 0) { close(fd); goto fallback; } comparedev = makedev(TTY_MAJOR, (int)vt.v_active); } #endif #ifdef TIOCGDEV if (ioctl (fd, TIOCGDEV, &devnum) < 0) { close(fd); goto fallback; } comparedev = (dev_t)devnum; #endif close(fd); dir = opendir("/dev"); if (!dir) goto fallback; name = scandev(dir); if (name) consalloc(name); closedir(dir); if (!consoles) goto fallback; return ret; } #ifdef __linux__ console: /* * Detection of devices used for Linux system consolei using * the /proc/consoles API with kernel 2.6.38 and higher. */ if ((fc = fopen("/proc/consoles", "re"))) { char fbuf[16]; int maj, min; DIR *dir; dir = opendir("/dev"); if (!dir) { fclose(fc); goto fallback; } while ((fscanf(fc, "%*s %*s (%[^)]) %d:%d", &fbuf[0], &maj, &min) == 3)) { char * name; if (!strchr(fbuf, 'E')) continue; comparedev = makedev(maj, min); name = scandev(dir); if (!name) continue; consalloc(name); } closedir(dir); fclose(fc); return ret; } /* * Detection of devices used for Linux system console using * the sysfs /sys/class/tty/ API with kernel 2.6.37 and higher. */ if ((attrib = actattr("console"))) { char *words = attrib, *token; DIR *dir; dir = opendir("/dev"); if (!dir) { free(attrib); goto fallback; } while ((token = strsep(&words, " \t\r\n"))) { char * name; if (*token == '\0') continue; comparedev = devattr(token); if (comparedev == makedev(TTY_MAJOR, 0)) { char *tmp = actattr(token); if (!tmp) continue; comparedev = devattr(tmp); free(tmp); } name = scandev(dir); if (!name) continue; consalloc(name); } closedir(dir); free(attrib); if (!consoles) goto fallback; return ret; } /* * Detection of devices used for Linux system console using * kernel parameter on the kernels command line. */ if ((cmdline = oneline("/proc/cmdline"))) { char *words= cmdline, *token; DIR *dir; dir = opendir("/dev"); if (!dir) { free(cmdline); goto fallback; } while ((token = strsep(&words, " \t\r\n"))) { #ifdef TIOCGDEV unsigned int devnum; #else struct vt_stat vt; struct stat st; #endif char *colon, *name; if (*token != 'c') continue; if (strncmp(token, "console=", 8) != 0) continue; token += 8; if (strcmp(token, "brl") == 0) token += 4; if ((colon = strchr(token, ','))) *colon = '\0'; if (asprintf(&name, "/dev/%s", token) < 0) continue; if ((fd = open(name, O_RDWR|O_NONBLOCK|O_NOCTTY|O_CLOEXEC)) < 0) { free(name); continue; } free(name); #ifdef TIOCGDEV if (ioctl (fd, TIOCGDEV, &devnum) < 0) { close(fd); continue; } comparedev = (dev_t)devnum; #else if (fstat(fd, &st) < 0) { close(fd); continue; } comparedev = st.st_rdev; if (comparedev == makedev(TTY_MAJOR, 0)) { if (ioctl(fd, VT_GETSTATE, &vt) < 0) { close(fd); continue; } comparedev = makedev(TTY_MAJOR, (int)vt.v_active); } #endif close(fd); name = scandev(dir); if (!name) continue; consalloc(name); } closedir(dir); free(cmdline); /* * Detection of the device used for Linux system console using * the ioctl TIOCGDEV if available (e.g. official 2.6.38). */ if (!consoles) { #ifdef TIOCGDEV unsigned int devnum; const char *name; if (!device || *device == '\0') fd = dup(fallback); else fd = open(device, O_RDWR|O_NONBLOCK|O_NOCTTY|O_CLOEXEC); if (fd < 0) goto fallback; if (ioctl (fd, TIOCGDEV, &devnum) < 0) { close(fd); goto fallback; } comparedev = (dev_t)devnum; close(fd); if (device && *device != '\0') name = device; else name = ttyname(fallback); if (!name) name = "/dev/tty1"; consalloc(strdup(name)); if (consoles) { if (!device || *device == '\0') consoles->fd = fallback; return ret; } #endif goto fallback; } return ret; } #endif /* __linux __ */ fallback: if (fallback >= 0) { const char *name; if (device && *device != '\0') name = device; else name = ttyname(fallback); if (!name) name = "/dev/tty"; consalloc(strdup(name)); if (consoles) consoles->fd = fallback; } return ret; } sysvinit-2.90/src/last.c0000644017777601777760000005045313303017401015052 0ustar nobodynogroup/* * last.c Re-implementation of the 'last' command, this time * for Linux. Yes I know there is BSD last, but I * just felt like writing this. No thanks :-). * Also, this version gives lots more info (especially with -x) * * Author: Miquel van Smoorenburg, miquels@cistron.nl * * Version: @(#)last 2.85 30-Jul-2004 miquels@cistron.nl * * This file is part of the sysvinit suite, * Copyright (C) 1991-2004 Miquel van Smoorenburg. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "oldutmp.h" #ifndef SHUTDOWN_TIME # define SHUTDOWN_TIME 254 #endif char *Version = "@(#) last 2.85 31-Apr-2004 miquels"; #define CHOP_DOMAIN 0 /* Define to chop off local domainname. */ #define NEW_UTMP 1 /* Fancy & fast utmp read code. */ #define UCHUNKSIZE 16384 /* How much we read at once. */ /* Double linked list of struct utmp's */ struct utmplist { struct utmp ut; struct utmplist *next; struct utmplist *prev; }; struct utmplist *utmplist = NULL; /* Types of listing */ #define R_CRASH 1 /* No logout record, system boot in between */ #define R_DOWN 2 /* System brought down in decent way */ #define R_NORMAL 3 /* Normal */ #define R_NOW 4 /* Still logged in */ #define R_REBOOT 5 /* Reboot record. */ #define R_PHANTOM 6 /* No logout record but session is stale. */ #define R_TIMECHANGE 7 /* NEW_TIME or OLD_TIME */ /* Global variables */ int maxrecs = 0; /* Maximum number of records to list. */ int recsdone = 0; /* Number of records listed */ int showhost = 1; /* Show hostname too? */ int altlist = 0; /* Show hostname at the end. */ int usedns = 0; /* Use DNS to lookup the hostname. */ int useip = 0; /* Print IP address in number format */ int fulltime = 0; /* Print full dates and times */ int name_len = 8; /* Default print 8 characters of name */ int domain_len = 16; /* Default print 16 characters of domain */ int oldfmt = 0; /* Use old libc5 format? */ char **show = NULL; /* What do they want us to show */ char *ufile; /* Filename of this file */ time_t lastdate; /* Last date we've seen */ char *progname; /* Name of this program */ #if CHOP_DOMAIN char hostname[256]; /* For gethostbyname() */ char *domainname; /* Our domainname. */ #endif /* * Convert old utmp format to new. */ void uconv(struct oldutmp *oldut, struct utmp *utn) { memset(utn, 0, sizeof(struct utmp)); utn->ut_type = oldut->ut_type; utn->ut_pid = oldut->ut_pid; utn->ut_time = oldut->ut_oldtime; utn->ut_addr = oldut->ut_oldaddr; strncpy(utn->ut_line, oldut->ut_line, OLD_LINESIZE); strncpy(utn->ut_user, oldut->ut_user, OLD_NAMESIZE); strncpy(utn->ut_host, oldut->ut_host, OLD_HOSTSIZE); } #if NEW_UTMP /* * Read one utmp entry, return in new format. * Automatically reposition file pointer. */ int uread(FILE *fp, struct utmp *u, int *quit) { static int utsize; static char buf[UCHUNKSIZE]; char tmp[1024]; static off_t fpos; static int bpos; struct oldutmp uto; int r; off_t o; if (quit == NULL && u != NULL) { /* * Normal read. */ if (oldfmt) { r = fread(&uto, sizeof(uto), 1, fp); uconv(&uto, u); } else r = fread(u, sizeof(struct utmp), 1, fp); return r; } if (u == NULL) { /* * Initialize and position. */ utsize = oldfmt ? sizeof(uto) : sizeof(struct utmp); fseeko(fp, 0, SEEK_END); fpos = ftello(fp); if (fpos == 0) return 0; o = ((fpos - 1) / UCHUNKSIZE) * UCHUNKSIZE; if (fseeko(fp, o, SEEK_SET) < 0) { fprintf(stderr, "%s: seek failed!\n", progname); return 0; } bpos = (int)(fpos - o); if (fread(buf, bpos, 1, fp) != 1) { fprintf(stderr, "%s: read failed!\n", progname); return 0; } fpos = o; return 1; } /* * Read one struct. From the buffer if possible. */ bpos -= utsize; if (bpos >= 0) { if (oldfmt) uconv((struct oldutmp *)(buf + bpos), u); else memcpy(u, buf + bpos, sizeof(struct utmp)); return 1; } /* * Oops we went "below" the buffer. We should be able to * seek back UCHUNKSIZE bytes. */ fpos -= UCHUNKSIZE; if (fpos < 0) return 0; /* * Copy whatever is left in the buffer. */ memcpy(tmp + (-bpos), buf, utsize + bpos); if (fseeko(fp, fpos, SEEK_SET) < 0) { perror("fseek"); return 0; } /* * Read another UCHUNKSIZE bytes. */ if (fread(buf, UCHUNKSIZE, 1, fp) != 1) { perror("fread"); return 0; } /* * The end of the UCHUNKSIZE byte buffer should be the first * few bytes of the current struct utmp. */ memcpy(tmp, buf + UCHUNKSIZE + bpos, -bpos); bpos += UCHUNKSIZE; if (oldfmt) uconv((struct oldutmp *)tmp, u); else memcpy(u, tmp, sizeof(struct utmp)); return 1; } #else /* NEW_UTMP */ /* * Read one utmp entry, return in new format. * Automatically reposition file pointer. */ int uread(FILE *fp, struct utmp *u, int *quit) { struct oldutmp uto; off_t r; if (u == NULL) { r = oldfmt ? sizeof(struct oldutmp) : sizeof(struct utmp); fseek(fp, -1 * r, SEEK_END); return 1; } if (!oldfmt) { r = fread(u, sizeof(struct utmp), 1, fp); if (r == 1) { if (fseeko(fp, -2 * sizeof(struct utmp), SEEK_CUR) < 0) if (quit) *quit = 1; } return r; } r = fread(&uto, sizeof(struct oldutmp), 1, fp); if (r == 1) { if (fseeko(fp, -2 * sizeof(struct oldutmp), SEEK_CUR) < 0) if (quit) *quit = 1; uconv(&uto, u); } return r; } #endif /* * Try to be smart about the location of the BTMP file */ #ifndef BTMP_FILE #define BTMP_FILE getbtmp() char *getbtmp() { static char btmp[128]; char *p; strcpy(btmp, WTMP_FILE); if ((p = strrchr(btmp, '/')) == NULL) p = btmp; else p++; *p = 0; strcat(btmp, "btmp"); return btmp; } #endif /* * Print a short date. */ char *showdate() { char *s = ctime(&lastdate); s[16] = 0; return s; } /* * SIGINT handler */ void int_handler() { printf("Interrupted %s\n", showdate()); exit(1); } /* * SIGQUIT handler */ void quit_handler() { printf("Interrupted %s\n", showdate()); signal(SIGQUIT, quit_handler); } /* * Get the basename of a filename */ char *mybasename(char *s) { char *p; if ((p = strrchr(s, '/')) != NULL) p++; else p = s; return p; } /* * Lookup a host with DNS. */ int dns_lookup(char *result, int size, int useip, int32_t *a) { struct sockaddr_in sin; struct sockaddr_in6 sin6; struct sockaddr *sa; int salen, flags; int mapped = 0; flags = useip ? NI_NUMERICHOST : 0; /* * IPv4 or IPv6 ? * 1. If last 3 4bytes are 0, must be IPv4 * 2. If IPv6 in IPv4, handle as IPv4 * 3. Anything else is IPv6 * * Ugly. */ if (a[0] == 0 && a[1] == 0 && a[2] == (int32_t)htonl (0xffff)) mapped = 1; if (mapped || (a[1] == 0 && a[2] == 0 && a[3] == 0)) { /* IPv4 */ sin.sin_family = AF_INET; sin.sin_port = 0; sin.sin_addr.s_addr = mapped ? a[3] : a[0]; sa = (struct sockaddr *)&sin; salen = sizeof(sin); } else { /* IPv6 */ memset(&sin6, 0, sizeof(sin6)); sin6.sin6_family = AF_INET6; sin6.sin6_port = 0; memcpy(sin6.sin6_addr.s6_addr, a, 16); sa = (struct sockaddr *)&sin6; salen = sizeof(sin6); } return getnameinfo(sa, salen, result, size, NULL, 0, flags); } /* * Show one line of information on screen */ int list(struct utmp *p, time_t t, int what) { time_t secs, tmp; char logintime[32]; char logouttime[32]; char length[32]; char final[512]; char utline[UT_LINESIZE+1]; char domain[256]; char *s, **walk; int mins, hours, days; int r, len; /* * uucp and ftp have special-type entries */ utline[0] = 0; strncat(utline, p->ut_line, UT_LINESIZE); if (strncmp(utline, "ftp", 3) == 0 && isdigit(utline[3])) utline[3] = 0; if (strncmp(utline, "uucp", 4) == 0 && isdigit(utline[4])) utline[4] = 0; /* * Is this something we wanna show? */ if (show) { for (walk = show; *walk; walk++) { if (strncmp(p->ut_name, *walk, UT_NAMESIZE) == 0 || strcmp(utline, *walk) == 0 || (strncmp(utline, "tty", 3) == 0 && strcmp(utline + 3, *walk) == 0)) break; } if (*walk == NULL) return 0; } /* * Calculate times */ tmp = (time_t)p->ut_time; strncpy(logintime, ctime(&tmp), sizeof(logintime)); logintime[sizeof(logintime)-1] = 0; /* enforce null termination */ if (fulltime) sprintf(logouttime, "- %s", ctime(&t)); else { logintime[16] = 0; sprintf(logouttime, "- %s", ctime(&t) + 11); logouttime[7] = 0; } secs = t - p->ut_time; mins = (secs / 60) % 60; hours = (secs / 3600) % 24; days = secs / 86400; if (days) sprintf(length, "(%d+%02d:%02d)", days, hours, mins); else sprintf(length, " (%02d:%02d)", hours, mins); switch(what) { case R_CRASH: sprintf(logouttime, "- crash"); break; case R_DOWN: sprintf(logouttime, "- down "); break; case R_NOW: length[0] = 0; if (fulltime) sprintf(logouttime, " still logged in"); else { sprintf(logouttime, " still"); sprintf(length, "logged in"); } break; case R_PHANTOM: length[0] = 0; if (fulltime) sprintf(logouttime, " gone - no logout"); else { sprintf(logouttime, " gone"); sprintf(length, "- no logout"); } break; case R_REBOOT: break; case R_TIMECHANGE: logouttime[0] = 0; length[0] = 0; break; case R_NORMAL: break; } /* * Look up host with DNS if needed. */ r = -1; if (usedns || useip) r = dns_lookup(domain, sizeof(domain), useip, p->ut_addr_v6); if (r < 0) { len = UT_HOSTSIZE; if (len >= (int)sizeof(domain)) len = sizeof(domain) - 1; domain[0] = 0; strncat(domain, p->ut_host, len); } if (showhost) { #if CHOP_DOMAIN /* * See if this is in our domain. */ if (!usedns && (s = strchr(p->ut_host, '.')) != NULL && strcmp(s + 1, domainname) == 0) *s = 0; #endif if (!altlist) { len = snprintf(final, sizeof(final), fulltime ? "%-8.*s %-12.12s %-16.*s %-24.24s %-26.26s %-12.12s\n" : "%-8.*s %-12.12s %-16.*s %-16.16s %-7.7s %-12.12s\n", name_len, p->ut_name, utline, domain_len, domain, logintime, logouttime, length); } else { len = snprintf(final, sizeof(final), fulltime ? "%-8.*s %-12.12s %-24.24s %-26.26s %-12.12s %s\n" : "%-8.*s %-12.12s %-16.16s %-7.7s %-12.12s %s\n", name_len, p->ut_name, utline, logintime, logouttime, length, domain); } } else len = snprintf(final, sizeof(final), fulltime ? "%-8.*s %-12.12s %-24.24s %-26.26s %-12.12s\n" : "%-8.*s %-12.12s %-16.16s %-7.7s %-12.12s\n", name_len, p->ut_name, utline, logintime, logouttime, length); #if defined(__GLIBC__) # if (__GLIBC__ == 2) && (__GLIBC_MINOR__ == 0) final[sizeof(final)-1] = '\0'; # endif #endif /* * Print out "final" string safely. */ for (s = final; *s; s++) { if (*s == '\n' || (*s >= 32 && (unsigned char)*s <= 126)) putchar(*s); else putchar('*'); } if (len < 0 || (size_t)len >= sizeof(final)) putchar('\n'); recsdone++; if (maxrecs && recsdone >= maxrecs) return 1; return 0; } /* * show usage */ void usage(char *s) { fprintf(stderr, "Usage: %s [-num | -n num] [-f file] " "[-t YYYYMMDDHHMMSS] " "[-R] [-adioxFw] [username..] [tty..]\n", s); exit(1); } time_t parsetm(char *ts) { struct tm u, origu; time_t tm; memset(&tm, 0, sizeof(tm)); if (sscanf(ts, "%4d%2d%2d%2d%2d%2d", &u.tm_year, &u.tm_mon, &u.tm_mday, &u.tm_hour, &u.tm_min, &u.tm_sec) != 6) return (time_t)-1; u.tm_year -= 1900; u.tm_mon -= 1; u.tm_isdst = -1; origu = u; if ((tm = mktime(&u)) == (time_t)-1) return tm; /* * Unfortunately mktime() is much more forgiving than * it should be. For example, it'll gladly accept * "30" as a valid month number. This behavior is by * design, but we don't like it, so we want to detect * it and complain. */ if (u.tm_year != origu.tm_year || u.tm_mon != origu.tm_mon || u.tm_mday != origu.tm_mday || u.tm_hour != origu.tm_hour || u.tm_min != origu.tm_min || u.tm_sec != origu.tm_sec) return (time_t)-1; return tm; } int main(int argc, char **argv) { FILE *fp; /* Filepointer of wtmp file */ struct utmp ut; /* Current utmp entry */ struct utmp oldut; /* Old utmp entry to check for duplicates */ struct utmplist *p; /* Pointer into utmplist */ struct utmplist *next;/* Pointer into utmplist */ time_t lastboot = 0; /* Last boottime */ time_t lastrch = 0; /* Last run level change */ time_t lastdown; /* Last downtime */ time_t begintime; /* When wtmp begins */ int whydown = 0; /* Why we went down: crash or shutdown */ int c, x; /* Scratch */ struct stat st; /* To stat the [uw]tmp file */ int quit = 0; /* Flag */ int down = 0; /* Down flag */ int lastb = 0; /* Is this 'lastb' ? */ int extended = 0; /* Lots of info. */ char *altufile = NULL;/* Alternate wtmp */ time_t until = 0; /* at what time to stop parsing the file */ progname = mybasename(argv[0]); /* Process the arguments. */ while((c = getopt(argc, argv, "f:n:RxadFiot:0123456789w")) != EOF) switch(c) { case 'R': showhost = 0; break; case 'x': extended = 1; break; case 'n': maxrecs = atoi(optarg); break; case 'o': oldfmt = 1; break; case 'f': if((altufile = malloc(strlen(optarg)+1)) == NULL) { fprintf(stderr, "%s: out of memory\n", progname); exit(1); } strcpy(altufile, optarg); break; case 'd': usedns++; break; case 'i': useip++; break; case 'a': altlist++; break; case 'F': fulltime++; break; case 't': if ((until = parsetm(optarg)) == (time_t)-1) { fprintf(stderr, "%s: Invalid time value \"%s\"\n", progname, optarg); usage(progname); } break; case 'w': if (UT_NAMESIZE > name_len) name_len = UT_NAMESIZE; if (UT_HOSTSIZE > domain_len) domain_len = UT_HOSTSIZE; break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': maxrecs = 10*maxrecs + c - '0'; break; default: usage(progname); break; } if (optind < argc) show = argv + optind; /* * Which file do we want to read? */ if (strcmp(progname, "lastb") == 0) { ufile = BTMP_FILE; lastb = 1; } else ufile = WTMP_FILE; if (altufile) ufile = altufile; time(&lastdown); lastrch = lastdown; /* * Fill in 'lastdate' */ lastdate = lastdown; #if CHOP_DOMAIN /* * Find out domainname. * * This doesn't work on modern systems, where only a DNS * lookup of the result from hostname() will get you the domainname. * Remember that domainname() is the NIS domainname, not DNS. * So basically this whole piece of code is bullshit. */ hostname[0] = 0; (void) gethostname(hostname, sizeof(hostname)); if ((domainname = strchr(hostname, '.')) != NULL) domainname++; if (domainname == NULL || domainname[0] == 0) { hostname[0] = 0; (void) getdomainname(hostname, sizeof(hostname)); hostname[sizeof(hostname) - 1] = 0; domainname = hostname; if (strcmp(domainname, "(none)") == 0 || domainname[0] == 0) domainname = NULL; } #endif /* * Install signal handlers */ signal(SIGINT, int_handler); signal(SIGQUIT, quit_handler); /* * Open the utmp file */ if ((fp = fopen(ufile, "r")) == NULL) { x = errno; fprintf(stderr, "%s: %s: %s\n", progname, ufile, strerror(errno)); if (altufile == NULL && x == ENOENT) fprintf(stderr, "Perhaps this file was removed by the " "operator to prevent logging %s info.\n", progname); exit(1); } /* * Optimize the buffer size. */ setvbuf(fp, NULL, _IOFBF, UCHUNKSIZE); /* * Read first structure to capture the time field */ if (uread(fp, &ut, NULL) == 1) begintime = ut.ut_time; else { fstat(fileno(fp), &st); begintime = st.st_ctime; quit = 1; } /* * Go to end of file minus one structure * and/or initialize utmp reading code. */ uread(fp, NULL, NULL); /* * Read struct after struct backwards from the file. */ while(!quit) { if (uread(fp, &ut, &quit) != 1) break; if (until && until < ut.ut_time) continue; if (memcmp(&ut, &oldut, sizeof(struct utmp)) == 0) continue; memcpy(&oldut, &ut, sizeof(struct utmp)); lastdate = ut.ut_time; if (lastb) { quit = list(&ut, ut.ut_time, R_NORMAL); continue; } /* * Set ut_type to the correct type. */ if (strncmp(ut.ut_line, "~", 1) == 0) { if (strncmp(ut.ut_user, "shutdown", 8) == 0) ut.ut_type = SHUTDOWN_TIME; else if (strncmp(ut.ut_user, "reboot", 6) == 0) ut.ut_type = BOOT_TIME; else if (strncmp(ut.ut_user, "runlevel", 8) == 0) ut.ut_type = RUN_LVL; } #if 1 /*def COMPAT*/ /* * For stupid old applications that don't fill in * ut_type correctly. */ else { if (ut.ut_type != DEAD_PROCESS && ut.ut_name[0] && ut.ut_line[0] && strcmp(ut.ut_name, "LOGIN") != 0) ut.ut_type = USER_PROCESS; /* * Even worse, applications that write ghost * entries: ut_type set to USER_PROCESS but * empty ut_name... */ if (ut.ut_name[0] == 0) ut.ut_type = DEAD_PROCESS; /* * Clock changes. */ if (strcmp(ut.ut_name, "date") == 0) { if (ut.ut_line[0] == '|') ut.ut_type = OLD_TIME; if (ut.ut_line[0] == '{') ut.ut_type = NEW_TIME; } } #endif switch (ut.ut_type) { case SHUTDOWN_TIME: if (extended) { strcpy(ut.ut_line, "system down"); quit = list(&ut, lastboot, R_NORMAL); } lastdown = lastrch = ut.ut_time; down = 1; break; case OLD_TIME: case NEW_TIME: if (extended) { strcpy(ut.ut_line, ut.ut_type == NEW_TIME ? "new time" : "old time"); quit = list(&ut, lastdown, R_TIMECHANGE); } break; case BOOT_TIME: strcpy(ut.ut_line, "system boot"); quit = list(&ut, lastdown, R_REBOOT); lastboot = ut.ut_time; down = 1; break; case RUN_LVL: x = ut.ut_pid & 255; if (extended) { sprintf(ut.ut_line, "(to lvl %c)", x); quit = list(&ut, lastrch, R_NORMAL); } if (x == '0' || x == '6') { lastdown = ut.ut_time; down = 1; ut.ut_type = SHUTDOWN_TIME; } lastrch = ut.ut_time; break; case USER_PROCESS: /* * This was a login - show the first matching * logout record and delete all records with * the same ut_line. */ c = 0; for (p = utmplist; p; p = next) { next = p->next; if (strncmp(p->ut.ut_line, ut.ut_line, UT_LINESIZE) == 0) { /* Show it */ if (c == 0) { quit = list(&ut, p->ut.ut_time, R_NORMAL); c = 1; } if (p->next) p->next->prev = p->prev; if (p->prev) p->prev->next = p->next; else utmplist = p->next; free(p); } } /* * Not found? Then crashed, down, still * logged in, or missing logout record. */ if (c == 0) { if (lastboot == 0) { c = R_NOW; /* Is process still alive? */ if (ut.ut_pid > 0 && kill(ut.ut_pid, 0) != 0 && errno == ESRCH) c = R_PHANTOM; } else c = whydown; quit = list(&ut, lastboot, c); } /* FALLTHRU */ case DEAD_PROCESS: /* * Just store the data if it is * interesting enough. */ if (ut.ut_line[0] == 0) break; if ((p = malloc(sizeof(struct utmplist))) == NULL) { fprintf(stderr, "%s: out of memory\n", progname); exit(1); } memcpy(&p->ut, &ut, sizeof(struct utmp)); p->next = utmplist; p->prev = NULL; if (utmplist) utmplist->prev = p; utmplist = p; break; } /* * If we saw a shutdown/reboot record we can remove * the entire current utmplist. */ if (down) { lastboot = ut.ut_time; whydown = (ut.ut_type == SHUTDOWN_TIME) ? R_DOWN : R_CRASH; for (p = utmplist; p; p = next) { next = p->next; free(p); } utmplist = NULL; down = 0; } } printf("\n%s begins %s", mybasename(ufile), ctime(&begintime)); fclose(fp); /* * Should we free memory here? Nah. This is not NT :) */ return 0; } sysvinit-2.90/src/initreq.h0000644017777601777760000000461613303017401015567 0ustar nobodynogroup/* * initreq.h Interface to talk to init through /run/initctl. * * Copyright (C) 1995-2004 Miquel van Smoorenburg * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * Version: @(#)initreq.h 1.28 31-Mar-2004 MvS * */ #ifndef _INITREQ_H #define _INITREQ_H #include #ifndef INIT_FIFO #define INIT_FIFO "/run/initctl" #endif #define INIT_MAGIC 0x03091969 #define INIT_CMD_START 0 #define INIT_CMD_RUNLVL 1 #define INIT_CMD_POWERFAIL 2 #define INIT_CMD_POWERFAILNOW 3 #define INIT_CMD_POWEROK 4 #define INIT_CMD_BSD 5 #define INIT_CMD_SETENV 6 #define INIT_CMD_UNSETENV 7 #ifdef MAXHOSTNAMELEN # define INITRQ_HLEN MAXHOSTNAMELEN #else # define INITRQ_HLEN 64 #endif /* * This is what BSD 4.4 uses when talking to init. * Linux doesn't use this right now. */ struct init_request_bsd { char gen_id[8]; /* Beats me.. telnetd uses "fe" */ char tty_id[16]; /* Tty name minus /dev/tty */ char host[INITRQ_HLEN]; /* Hostname */ char term_type[16]; /* Terminal type */ int signal; /* Signal to send */ int pid; /* Process to send to */ char exec_name[128]; /* Program to execute */ char reserved[128]; /* For future expansion. */ }; /* * Because of legacy interfaces, "runlevel" and "sleeptime" * aren't in a seperate struct in the union. * * The weird sizes are because init expects the whole * struct to be 384 bytes. */ struct init_request { int magic; /* Magic number */ int cmd; /* What kind of request */ int runlevel; /* Runlevel to change to */ int sleeptime; /* Time between TERM and KILL */ union { struct init_request_bsd bsd; char data[368]; } i; }; #endif sysvinit-2.90/src/runlevel.c0000644017777601777760000000260713303017401015741 0ustar nobodynogroup/* * runlevel Prints out the previous and the current runlevel. * * Version: @(#)runlevel 1.20 16-Apr-1997 MvS * * This file is part of the sysvinit suite, * Copyright (C) 1991-1997 Miquel van Smoorenburg. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include int main(argc, argv) int argc; char **argv; { struct utmp *ut; char prev; if (argc > 1) utmpname(argv[1]); setutent(); while ((ut = getutent()) != NULL) { if (ut->ut_type == RUN_LVL) { prev = ut->ut_pid / 256; if (prev == 0) prev = 'N'; printf("%c %c\n", prev, ut->ut_pid % 256); endutent(); exit(0); } } printf("unknown\n"); endutent(); return(1); } sysvinit-2.90/src/utmpdump.c0000644017777601777760000001611113303017401015753 0ustar nobodynogroup/* * utmpdump Simple program to dump UTMP and WTMP files in * raw format, so they can be examined. * * Author: Miquel van Smoorenburg, * Danek Duvall * * Version: @(#)utmpdump 2.79 12-Sep-2000 * * This file is part of the sysvinit suite, * Copyright (C) 1991-2000 Miquel van Smoorenburg. * * Additional Copyright on this file 1998 Danek Duvall. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include #include #include #include "oldutmp.h" struct utmp oldtonew(struct oldutmp src) { struct utmp dest; memset(&dest, 0, sizeof dest); dest.ut_type = src.ut_type; dest.ut_pid = src.ut_pid; dest.ut_time = src.ut_oldtime; dest.ut_addr = src.ut_oldaddr; strncpy(dest.ut_id, src.ut_id, 4); strncpy(dest.ut_line, src.ut_line, OLD_LINESIZE); strncpy(dest.ut_user, src.ut_user, OLD_NAMESIZE); strncpy(dest.ut_host, src.ut_host, OLD_HOSTSIZE); return dest; } struct oldutmp newtoold(struct utmp src) { struct oldutmp dest; memset(&dest, 0, sizeof dest); dest.ut_type = src.ut_type; dest.ut_pid = src.ut_pid; dest.ut_oldtime = src.ut_time; dest.ut_oldaddr = src.ut_addr; strncpy(dest.ut_id, src.ut_id, 4); strncpy(dest.ut_line, src.ut_line, OLD_LINESIZE); strncpy(dest.ut_user, src.ut_user, OLD_NAMESIZE); strncpy(dest.ut_host, src.ut_host, OLD_HOSTSIZE); return dest; } char * timetostr(const time_t time) { static char s[29]; /* [Sun Sep 01 00:00:00 1998 PST] */ if (time != 0) strftime(s, 29, "%a %b %d %T %Y %Z", localtime(&time)); else s[0] = '\0'; return s; } time_t strtotime(const char *s_time) { struct tm tm; memset(&tm, '\0', sizeof(struct tm)); if (s_time[0] == ' ' || s_time[0] == '\0') return (time_t)0; strptime(s_time, "%a %b %d %T %Y", &tm); /* Cheesy way of checking for DST */ if (s_time[26] == 'D') tm.tm_isdst = 1; return mktime(&tm); } #define cleanse(x) xcleanse(x, sizeof(x)) void xcleanse(char *s, int len) { for ( ; *s && len-- > 0; s++) if (!isprint(*s) || *s == '[' || *s == ']') *s = '?'; } void unspace(char *s, int len) { while (*s && *s != ' ' && len--) ++s; if (len > 0) *s = '\0'; } void print_utline(struct utmp ut) { char addr_buf[INET6_ADDRSTRLEN+1]; const char *addr_string, *time_string; void *in_addr = &ut.ut_addr_v6; size_t addr_length = INET6_ADDRSTRLEN; int addr_family = AF_INET6; if (!ut.ut_addr_v6[1] && !ut.ut_addr_v6[2] && !ut.ut_addr_v6[3]) { addr_family = AF_INET; addr_length = INET_ADDRSTRLEN; in_addr = &ut.ut_addr; } if ((addr_string = inet_ntop(addr_family, in_addr, addr_buf, addr_length)) == 0) { addr_buf[0] = '\0'; addr_string = &addr_buf[0]; } time_string = timetostr(ut.ut_time); cleanse(ut.ut_id); cleanse(ut.ut_user); cleanse(ut.ut_line); cleanse(ut.ut_host); /* pid id user line host addr time */ printf("[%d] [%05d] [%-4.4s] [%-*.*s] [%-*.*s] [%-*.*s] [%-15.15s] [%-28.28s]\n", ut.ut_type, ut.ut_pid, ut.ut_id, 8, UT_NAMESIZE, ut.ut_user, 12, UT_LINESIZE, ut.ut_line, 20, UT_HOSTSIZE, ut.ut_host, addr_string, time_string); } void dump(FILE *fp, int forever, int oldfmt) { struct utmp ut; struct oldutmp uto; if (forever) fseek(fp, -10 * (oldfmt ? sizeof uto : sizeof ut), SEEK_END); do { if (oldfmt) while (fread(&uto, sizeof uto, 1, fp) == 1) print_utline(oldtonew(uto)); else while (fread(&ut, sizeof ut, 1, fp) == 1) print_utline(ut); if (forever) sleep(1); } while (forever); } /* This function won't work properly if there's a ']' or a ' ' in the real * token. Thankfully, this should never happen. */ int gettok(char *line, char *dest, int size, int eatspace) { int bpos, epos, eaten; char *t; bpos = strchr(line, '[') - line; if (bpos < 0) { fprintf(stderr, "Extraneous newline in file. Exiting."); exit(1); } line += 1 + bpos; epos = strchr(line, ']') - line; if (epos < 0) { fprintf(stderr, "Extraneous newline in file. Exiting."); exit(1); } line[epos] = '\0'; eaten = bpos + epos + 1; if (eatspace) if ((t = strchr(line, ' '))) *t = 0; strncpy(dest, line, size); return eaten + 1; } void # ifdef __GNUC__ undump(FILE *fp, int forever __attribute__((unused)), int oldfmt) #else undump(FILE *fp, int forever, int oldfmt) #endif { struct utmp ut; struct oldutmp uto; char s_addr[16], s_time[29], *linestart; linestart = malloc(1024 * sizeof *linestart); s_addr[15] = 0; s_time[28] = 0; while(fgets(linestart, 1023, fp)) { char *line = linestart; memset(&ut, '\0', sizeof(ut)); sscanf(line, "[%hd] [%d] [%4c] ", &ut.ut_type, &ut.ut_pid, ut.ut_id); line += 19; line += gettok(line, ut.ut_user, sizeof(ut.ut_user), 1); line += gettok(line, ut.ut_line, sizeof(ut.ut_line), 1); line += gettok(line, ut.ut_host, sizeof(ut.ut_host), 1); line += gettok(line, s_addr, sizeof(s_addr)-1, 1); line += gettok(line, s_time, sizeof(s_time)-1, 0); (void)line; /* Quiet down static source analyzers */ ut.ut_addr = inet_addr(s_addr); ut.ut_time = strtotime(s_time); if (oldfmt) { uto = newtoold(ut); fwrite(&uto, sizeof(uto), 1, stdout); } else fwrite(&ut, sizeof(ut), 1, stdout); } free(linestart); } void usage(int result) { printf("Usage: utmpdump [ -froh ] [ filename ]\n"); exit(result); } int main(int argc, char **argv) { int c; FILE *fp; int reverse = 0, forever = 0, oldfmt = 0; while ((c = getopt(argc, argv, "froh")) != EOF) { switch (c) { case 'r': reverse = 1; break; case 'f': forever = 1; break; case 'o': oldfmt = 1; break; case 'h': usage(0); break; default: usage(1); } } if (optind < argc) { fprintf(stderr, "Utmp %sdump of %s\n", reverse ? "un" : "", argv[optind]); if ((fp = fopen(argv[optind], "r")) == NULL) { perror("Unable to open file"); exit(1); } } else { fprintf(stderr, "Utmp %sdump of stdin\n", reverse ? "un" : ""); fp = stdin; } if (reverse) undump(fp, forever, oldfmt); else dump(fp, forever, oldfmt); fclose(fp); return 0; } sysvinit-2.90/src/.gitignore0000644017777601777760000000013313303017401015721 0ustar nobodynogroupbootlogd fstab-decode halt init killall5 last mesg runlevel shutdown sulogin utmpdump wall sysvinit-2.90/src/init.h0000644017777601777760000001064113303017401015052 0ustar nobodynogroup/* * init.h Several defines and declarations to be * included by all modules of the init program. * * Version: @(#)init.h 2.85-5 02-Jul-2003 miquels@cistron.nl * * Copyright (C) 1998-2003 Miquel van Smoorenburg. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ /* Standard configuration */ #define CHANGE_WAIT 0 /* Change runlevel while waiting for a process to exit? */ /* Debug and test modes */ #define DEBUG 0 /* Debug code off */ #define INITDEBUG 0 /* Fork at startup to debug init. */ /* Some constants */ #define INITPID 1 /* pid of first process */ #define PIPE_FD 10 /* Fileno of initfifo. */ #define STATE_PIPE 11 /* used to pass state through exec */ /* Failsafe configuration */ #define MAXSPAWN 10 /* Max times respawned in.. */ #define TESTTIME 120 /* this much seconds */ #define SLEEPTIME 300 /* Disable time */ /* Default path inherited by every child. */ #define PATH_DEFAULT "/sbin:/usr/sbin:/bin:/usr/bin" /* Prototypes. */ void write_utmp_wtmp(char *user, char *id, int pid, int type, char *line); void write_wtmp(char *user, char *id, int pid, int type, char *line); #ifdef __GNUC__ __attribute__ ((format (printf, 2, 3))) #endif void initlog(int loglevel, char *fmt, ...); void set_term(int how); void print(char *fmt); /* from dowall.c */ void wall(const char *text, int remote); #if DEBUG # define INITDBG(level, fmt, args...) initlog(level, fmt, ##args) #else # define INITDBG(level, fmt, args...) #endif /* Actions to be taken by init */ #define RESPAWN 1 #define WAIT 2 #define ONCE 3 #define BOOT 4 #define BOOTWAIT 5 #define POWERFAIL 6 #define POWERWAIT 7 #define POWEROKWAIT 8 #define CTRLALTDEL 9 #define OFF 10 #define ONDEMAND 11 #define INITDEFAULT 12 #define SYSINIT 13 #define POWERFAILNOW 14 #define KBREQUEST 15 /* Information about a process in the in-core inittab */ typedef struct _child_ { int flags; /* Status of this entry */ int exstat; /* Exit status of process */ int pid; /* Pid of this process */ time_t tm; /* When respawned last */ int count; /* Times respawned in the last 2 minutes */ char id[8]; /* Inittab id (must be unique) */ char rlevel[12]; /* run levels */ int action; /* what to do (see list below) */ char process[128]; /* The command line */ struct _child_ *new; /* New entry (after inittab re-read) */ struct _child_ *next; /* For the linked list */ } CHILD; /* Values for the 'flags' field */ #define RUNNING 2 /* Process is still running */ #define KILLME 4 /* Kill this process */ #define DEMAND 8 /* "runlevels" a b c */ #define FAILING 16 /* process respawns rapidly */ #define WAITING 32 /* We're waiting for this process */ #define ZOMBIE 64 /* This process is already dead */ #define XECUTED 128 /* Set if spawned once or more times */ /* Log levels. */ #define L_CO 1 /* Log on the console. */ #define L_SY 2 /* Log with syslog() */ #define L_VB (L_CO|L_SY) /* Log with both. */ #ifndef NO_PROCESS # define NO_PROCESS 0 #endif /* * Global variables. */ extern CHILD *family; extern int wrote_wtmp_reboot; extern int wrote_utmp_reboot; extern int wrote_wtmp_rlevel; extern int wrote_utmp_rlevel; extern char thislevel; extern char prevlevel; /* Tokens in state parser */ #define C_VER 1 #define C_END 2 #define C_REC 3 #define C_EOR 4 #define C_LEV 5 #define C_FLAG 6 #define C_ACTION 7 #define C_PROCESS 8 #define C_PID 9 #define C_EXS 10 #define C_EOF -1 #define D_RUNLEVEL -2 #define D_THISLEVEL -3 #define D_PREVLEVEL -4 #define D_GOTSIGN -5 #define D_WROTE_WTMP_REBOOT -6 #define D_WROTE_UTMP_REBOOT -7 #define D_SLTIME -8 #define D_DIDBOOT -9 #define D_WROTE_WTMP_RLEVEL -16 #define D_WROTE_UTMP_RLEVEL -17 sysvinit-2.90/src/dowall.c0000644017777601777760000001363713303017401015374 0ustar nobodynogroup/* * dowall.c Write to all users on the system. * * Author: Miquel van Smoorenburg, miquels@cistron.nl * * Version: @(#)dowall.c 2.85-5 02-Jul-2003 miquels@cistron.nl * * This file is part of the sysvinit suite, * Copyright (C) 1991-2003 Miquel van Smoorenburg. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifndef _PATH_DEV # define _PATH_DEV "/dev/" #endif #ifndef HOST_NAME_MAX # define HOST_NAME_MAX 255 #endif static sigjmp_buf jbuf; /* * Alarm handler */ /*ARGSUSED*/ # ifdef __GNUC__ static void handler(int arg __attribute__((unused))) # else static void handler(int arg) # endif { siglongjmp(jbuf, 1); } /* * Print a text, escape all characters not in Latin-1. */ static void feputs(const char *line, FILE *fp) { unsigned char *p; for (p = (unsigned char *)line; *p; p++) { if (strchr("\t\r\n", *p) || (*p >= 32 && *p <= 127) || (*p >= 160)) { fputc(*p, fp); } else { fprintf(fp, "^%c", (*p & 0x1f) + 'A' - 1); } } fflush(fp); } static void getuidtty(char **userp, char **ttyp) { struct passwd *pwd; uid_t uid; char *tty; static char uidbuf[32]; static char ttynm[UT_LINESIZE + 4]; static int init = 0; if (!init) { uid = getuid(); if ((pwd = getpwuid(uid)) != NULL) { uidbuf[0] = 0; strncat(uidbuf, pwd->pw_name, sizeof(uidbuf) - 1); } else { /* Using variable number of data parameters in one function makes the Clang compiler cry. -- Jesse sprintf(uidbuf, uid ? "uid %d" : "root", (int)uid); */ if (uid) sprintf(uidbuf, "uid %d", (int) uid); else sprintf(uidbuf, "root"); } if ((tty = ttyname(0)) != NULL) { const size_t plen = strlen(_PATH_DEV); if (strncmp(tty, _PATH_DEV, plen) == 0) { tty += plen; if (tty[0] == '/') tty++; } snprintf(ttynm, sizeof(ttynm), "(%.*s) ", UT_LINESIZE, tty); } else ttynm[0] = 0; init++; } *userp = uidbuf; *ttyp = ttynm; } /* * Check whether given filename looks like tty device. */ static int file_isatty(const char *fname) { struct stat st; int major; if (stat(fname, &st) < 0) return 0; if (st.st_nlink != 1 || !S_ISCHR(st.st_mode)) return 0; /* * It would be an impossible task to list all major/minors * of tty devices here, so we just exclude the obvious * majors of which just opening has side-effects: * printers and tapes. */ major = major(st.st_dev); if (major == 1 || major == 2 || major == 6 || major == 9 || major == 12 || major == 16 || major == 21 || major == 27 || major == 37 || major == 96 || major == 97 || major == 206 || major == 230) return 0; return 1; } /* * Wall function. */ void wall(const char *text, int remote) { FILE *tp; struct sigaction sa; struct utmp *utmp; time_t t; char term[UT_LINESIZE+ strlen(_PATH_DEV) + 1]; char line[81]; char hostname[HOST_NAME_MAX+1]; char *date, *p; char *user, *tty; int fd, flags; /* * Make sure tp and fd aren't in a register. Some versions * of gcc clobber those after longjmp (or so I understand). */ (void) &tp; (void) &fd; getuidtty(&user, &tty); /* Get and report current hostname, to make it easier to find out which machine is being shut down. */ if (0 != gethostname(hostname, sizeof(hostname))) { strncpy(hostname, "[unknown]", sizeof(hostname)-1); } /* If hostname is truncated, it is unspecified if the string is null terminated or not. Make sure we know it is null terminated. */ hostname[sizeof(hostname)-1] = 0; /* Get the time */ time(&t); date = ctime(&t); for(p = date; *p && *p != '\n'; p++) ; *p = 0; if (remote) { snprintf(line, sizeof(line), "\007\r\nRemote broadcast message (%s):\r\n\r\n", date); } else { snprintf(line, sizeof(line), "\007\r\nBroadcast message from %s@%s %s(%s):\r\n\r\n", user, hostname, tty, date); } /* * Fork to avoid us hanging in a write() */ if (fork() != 0) return; memset(&sa, 0, sizeof(sa)); sa.sa_handler = handler; sa.sa_flags = 0; sigemptyset(&sa.sa_mask); sigaction(SIGALRM, &sa, NULL); setutent(); while ((utmp = getutent()) != NULL) { if(utmp->ut_type != USER_PROCESS || utmp->ut_user[0] == 0) continue; if (strncmp(utmp->ut_line, _PATH_DEV, strlen(_PATH_DEV)) == 0) { term[0] = 0; strncat(term, utmp->ut_line, sizeof(term)-1); } else snprintf(term, sizeof(term), _PATH_DEV "%.*s", UT_LINESIZE, utmp->ut_line); if (strstr(term, "/../")) continue; fd = -1; tp = NULL; /* * Open it non-delay */ if (sigsetjmp(jbuf, 1) == 0) { alarm(2); flags = O_WRONLY|O_NDELAY|O_NOCTTY; if (file_isatty(term) && (fd = open(term, flags)) >= 0) { if (isatty(fd) && (tp = fdopen(fd, "w")) != NULL) { fputs(line, tp); feputs(text, tp); fflush(tp); } } } alarm(0); if (fd >= 0) close(fd); if (tp != NULL) fclose(tp); } endutent(); exit(0); } sysvinit-2.90/src/hddown.c0000644017777601777760000003111413303017401015363 0ustar nobodynogroup/* * hddown.c Find all disks on the system and * shut them down. * * Copyright (C) 2003 Miquel van Smoorenburg. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ char *v_hddown = "@(#)hddown.c 1.02 22-Apr-2003 miquels@cistron.nl"; #ifndef _GNU_SOURCE #define _GNU_SOURCE #endif #include #include #include #include #include #include #include #ifdef __linux__ #include #include #include #ifndef USE_SYSFS # define USE_SYSFS 1 #endif #if defined(USE_SYSFS) && (USE_SYSFS == 1) /* * sysfs part Find all disks on the system, list out IDE and unmanaged * SATA disks, flush the cache of those and shut them down. * Author: Werner Fink , 2007/06/12 * */ #include #include #include #include #ifdef WORDS_BIGENDIAN #include #endif #define SYS_BLK "/sys/block" #define SYS_CLASS "/sys/class/scsi_disk" #define DEV_BASE "/dev" #define ISSPACE(c) (((c)==' ')||((c)=='\n')||((c)=='\t')||((c)=='\v')||((c)=='\r')||((c)=='\f')) /* Used in flush_cache_ext(), compare with */ #define IDBYTES 512 #define MASK_EXT 0xE000 /* Bit 15 shall be zero, bit 14 shall be one, bit 13 flush cache ext */ #define TEST_EXT 0x6000 /* Maybe set in list_disks() and used in do_standby_disk() */ #define DISK_IS_IDE 0x00000001 #define DISK_IS_SATA 0x00000002 #define DISK_EXTFLUSH 0x00000004 #define DISK_REMOVABLE 0x00000008 #define DISK_MANAGED 0x00000010 #define DISK_FLUSHONLY 0x00000020 static char *strstrip(char *str); static FILE *hdopen(const char* const format, const char* const name); static int flush_cache_ext(const char *device); /* * Find all disks through /sys/block. */ static char *list_disks(DIR* blk, unsigned int* flags) { struct dirent *d; while ((d = readdir(blk))) { (*flags) = 0; if (d->d_name[1] == 'd' && (d->d_name[0] == 'h' || d->d_name[0] == 's')) { char buf[NAME_MAX+1], lnk[NAME_MAX+1], *ptr; FILE *fp; int ret; fp = hdopen(SYS_BLK "/%s/removable", d->d_name); if (0 == (long)fp || -1 == (long)fp) { if (-1 == (long)fp) goto empty; /* error */ continue; /* no entry `removable' */ } ret = getc(fp); fclose(fp); if (ret != '0') (*flags) |= DISK_REMOVABLE; if (d->d_name[0] == 'h') { if ((*flags) & DISK_REMOVABLE) continue; /* not a hard disk */ (*flags) |= DISK_IS_IDE; if ((ret = flush_cache_ext(d->d_name))) { if (ret < 0) goto empty; (*flags) |= DISK_EXTFLUSH; } break; /* old IDE disk not managed by kernel, out here */ } ret = snprintf(buf, sizeof(buf), SYS_BLK "/%s/device", d->d_name); if ((ret >= (int)sizeof(buf)) || (ret < 0)) goto empty; /* error */ ret = readlink(buf, lnk, sizeof(lnk)); if (ret >= (int)sizeof(lnk)) goto empty; /* error */ if (ret < 0) { if (errno != ENOENT) goto empty; /* error */ continue; /* no entry `device' */ } lnk[ret] = '\0'; ptr = basename(lnk); if (!ptr || !*ptr) continue; /* should not happen */ fp = hdopen(SYS_CLASS "/%s/manage_start_stop", ptr); if (0 == (long)fp || -1 == (long)fp) { if (-1 == (long)fp) goto empty; /* error */ } else { ret = getc(fp); fclose(fp); if (ret != '0') { (*flags) |= DISK_MANAGED; continue; } } fp = hdopen(SYS_BLK "/%s/device/vendor", d->d_name); if (0 == (long)fp || -1 == (long)fp) { if (-1 == (long)fp) goto empty; /* error */ continue; /* no entry `device/vendor' */ } ptr = fgets(buf, sizeof(buf), fp); fclose(fp); if (ptr == (char*)0) continue; /* should not happen */ ptr = strstrip(buf); if (*ptr == '\0') continue; /* should not happen */ if (strncmp(buf, "ATA", sizeof(buf)) == 0) { if ((*flags) & DISK_REMOVABLE) continue; /* not a hard disk */ (*flags) |= (DISK_IS_IDE|DISK_IS_SATA); if ((ret = flush_cache_ext(d->d_name))) { if (ret < 0) goto empty; (*flags) |= DISK_EXTFLUSH; } break; /* new SATA disk to shutdown, out here */ } if (((*flags) & DISK_REMOVABLE) == 0) continue; /* Seems to be a real SCSI disk */ if ((ret = flush_cache_ext(d->d_name))) { if (ret < 0) goto empty; (*flags) |= DISK_EXTFLUSH; } break; /* Removable disk like USB stick to shutdown */ } } if (d == (struct dirent*)0) goto empty; return d->d_name; empty: return (char*)0; } /* * Put an IDE/SCSI/SATA disk in standby mode. * Code stolen from hdparm.c */ static int do_standby_disk(char *device, unsigned int flags) { #ifndef WIN_STANDBYNOW1 #define WIN_STANDBYNOW1 0xE0 #endif #ifndef WIN_STANDBYNOW2 #define WIN_STANDBYNOW2 0x94 #endif #ifndef WIN_FLUSH_CACHE_EXT #define WIN_FLUSH_CACHE_EXT 0xEA #endif #ifndef WIN_FLUSH_CACHE #define WIN_FLUSH_CACHE 0xE7 #endif unsigned char flush1[4] = {WIN_FLUSH_CACHE_EXT,0,0,0}; unsigned char flush2[4] = {WIN_FLUSH_CACHE,0,0,0}; unsigned char stdby1[4] = {WIN_STANDBYNOW1,0,0,0}; unsigned char stdby2[4] = {WIN_STANDBYNOW2,0,0,0}; char buf[NAME_MAX+1]; int fd, ret; ret = snprintf(buf, sizeof(buf), DEV_BASE "/%s", device); if ((ret >= (int)sizeof(buf)) || (ret < 0)) return -1; if ((fd = open(buf, O_RDWR|O_NONBLOCK)) < 0) return -1; switch (flags & DISK_EXTFLUSH) { case DISK_EXTFLUSH: if ((ret = ioctl(fd, HDIO_DRIVE_CMD, &flush1)) == 0) break; /* Else Fall through */ /* Extend flush rejected, try standard flush */ default: ret = ioctl(fd, HDIO_DRIVE_CMD, &flush2) && ioctl(fd, BLKFLSBUF); break; } if ((flags & DISK_FLUSHONLY) == 0x0) { ret = ioctl(fd, HDIO_DRIVE_CMD, &stdby1) && ioctl(fd, HDIO_DRIVE_CMD, &stdby2); } close(fd); if (ret) return -1; return 0; } /* * List all disks and put them in standby mode. * This has the side-effect of flushing the writecache, * which is exactly what we want on poweroff. */ int hddown(void) { unsigned int flags; char *disk; DIR *blk; if ((blk = opendir(SYS_BLK)) == (DIR*)0) return -1; while ((disk = list_disks(blk, &flags))) do_standby_disk(disk, flags); return closedir(blk); } /* * List all disks and cause them to flush their buffers. */ int hdflush(void) { unsigned int flags; char *disk; DIR *blk; if ((blk = opendir(SYS_BLK)) == (DIR*)0) return -1; while ((disk = list_disks(blk, &flags))) do_standby_disk(disk, (flags|DISK_FLUSHONLY)); return closedir(blk); } /* * Strip off trailing white spaces */ static char *strstrip(char *str) { const size_t len = strlen(str); if (len) { char* end = str + len - 1; while ((end != str) && ISSPACE(*end)) end--; *(end + 1) = '\0'; /* remove trailing white spaces */ } return str; } /* * Open a sysfs file without getting a controlling tty and return * FILE* pointer. Return 0 if the file didn't exist, or (FILE*)-1 if * something else went wrong. */ static FILE *hdopen(const char* const format, const char* const name) { char buf[NAME_MAX+1]; FILE *fp = (FILE*)-1; int fd, ret; ret = snprintf(buf, sizeof(buf), format, name); if ((ret >= (int)sizeof(buf)) || (ret < 0)) goto error; /* error */ fd = open(buf, O_RDONLY|O_NOCTTY); if (fd < 0) { if (errno != ENOENT) goto error; /* error */ fp = (FILE*)0; goto error; /* no entry `removable' */ } fp = fdopen(fd, "r"); if (fp == (FILE*)0) close(fd); /* should not happen */ error: return fp; } /* * Check IDE/(S)ATA hard disk identity for * the FLUSH CACHE EXT bit set. */ static int flush_cache_ext(const char *device) { #ifndef WIN_IDENTIFY #define WIN_IDENTIFY 0xEC #endif unsigned char args[4+IDBYTES]; unsigned short *id = (unsigned short*)(&args[4]); char buf[NAME_MAX+1], *ptr; int fd = -1, ret = 0; FILE *fp; fp = hdopen(SYS_BLK "/%s/size", device); if (0 == (long)fp || -1 == (long)fp) { if (-1 == (long)fp) return -1; /* error */ goto out; /* no entry `size' */ } ptr = fgets(buf, sizeof(buf), fp); fclose(fp); if (ptr == (char*)0) goto out; /* should not happen */ ptr = strstrip(buf); if (*ptr == '\0') goto out; /* should not happen */ if ((size_t)atoll(buf) < (1<<28)) goto out; /* small disk */ ret = snprintf(buf, sizeof(buf), DEV_BASE "/%s", device); if ((ret >= (int)sizeof(buf)) || (ret < 0)) return -1; /* error */ if ((fd = open(buf, O_RDONLY|O_NONBLOCK)) < 0) goto out; memset(&args[0], 0, sizeof(args)); args[0] = WIN_IDENTIFY; args[3] = 1; if (ioctl(fd, HDIO_DRIVE_CMD, &args)) goto out; #ifdef WORDS_BIGENDIAN # if 0 { const unsigned short *end = id + IDBYTES/2; const unsigned short *from = id; unsigned short *to = id; while (from < end) *to++ = bswap_16(*from++); } # else id[83] = bswap_16(id[83]); # endif #endif if ((id[83] & MASK_EXT) == TEST_EXT) ret = 1; out: if (fd >= 0) close(fd); return ret; } #else /* ! USE_SYSFS */ #define MAX_DISKS 64 #define PROC_IDE "/proc/ide" #define DEV_BASE "/dev" /* * Find all IDE disks through /proc. */ static int find_idedisks(const char **dev, int maxdev, int *count) { DIR *dd; FILE *fp; struct dirent *d; char buf[256]; if ((dd = opendir(PROC_IDE)) == NULL) return -1; while (*count < maxdev && (d = readdir(dd)) != NULL) { if (strncmp(d->d_name, "hd", 2) != 0) continue; buf[0] = 0; snprintf(buf, sizeof(buf), PROC_IDE "/%s/media", d->d_name); if ((fp = fopen(buf, "r")) == NULL) continue; if (fgets(buf, sizeof(buf), fp) == 0 || strcmp(buf, "disk\n") != 0) { fclose(fp); continue; } fclose(fp); snprintf(buf, sizeof(buf), DEV_BASE "/%s", d->d_name); dev[(*count)++] = strdup(buf); } closedir(dd); return 0; } /* * Find all SCSI/SATA disks. */ static int find_scsidisks(const char **dev, int maxdev, int *count) { if (*count < maxdev) dev[(*count)++] = DEV_BASE "/sda"; if (*count < maxdev) dev[(*count)++] = DEV_BASE "/sdb"; if (*count < maxdev) dev[(*count)++] = DEV_BASE "/sdc"; if (*count < maxdev) dev[(*count)++] = DEV_BASE "/sdd"; if (*count < maxdev) dev[(*count)++] = DEV_BASE "/sde"; if (*count < maxdev) dev[(*count)++] = DEV_BASE "/sdf"; if (*count < maxdev) dev[(*count)++] = DEV_BASE "/sdg"; if (*count < maxdev) dev[(*count)++] = DEV_BASE "/sdh"; return 0; } /* * Open the device node of a disk. */ static int open_disk(const char *device) { return open(device, O_RDWR); } /* * Open device nodes of all disks, and store the file descriptors in fds. * This has to be done in advance because accessing the device nodes * might cause a disk to spin back up. */ static int open_disks(const char **disks, int *fds, int count) { int i; for (i = 0; i < count; i++) fds[i] = open_disk(disks[i]); return 0; } /* * Put an IDE/SCSI/SATA disk in standby mode. * Code stolen from hdparm.c */ static int do_standby_disk(int fd) { #ifndef WIN_STANDBYNOW1 #define WIN_STANDBYNOW1 0xE0 #endif #ifndef WIN_STANDBYNOW2 #define WIN_STANDBYNOW2 0x94 #endif unsigned char args1[4] = {WIN_STANDBYNOW1,0,0,0}; unsigned char args2[4] = {WIN_STANDBYNOW2,0,0,0}; if (fd < 0) return -1; if (ioctl(fd, HDIO_DRIVE_CMD, &args1) && ioctl(fd, HDIO_DRIVE_CMD, &args2)) return -1; return 0; } /* * Put all specified disks in standby mode. */ static int do_standby_disks(const int *fds, int count) { int i; for (i = 0; i < count; i++) do_standby_disk(fds[i]); return 0; } /* * First find all IDE/SCSI/SATA disks, then put them in standby mode. * This has the side-effect of flushing the writecache, * which is exactly what we want on poweroff. */ int hddown(void) { const char *disks[MAX_DISKS]; int fds[MAX_DISKS]; int count = 0; int result1, result2; result1 = find_idedisks(disks, MAX_DISKS, &count); result2 = find_scsidisks(disks, MAX_DISKS, &count); open_disks(disks, fds, count); do_standby_disks(fds, count); return (result1 ? result1 : result2); } int hdflush(void) { return 0; } #endif /* ! USE_SYSFS */ #else /* __linux__ */ int hddown(void) { return 0; } int hdflush(void) { return 0; } #endif /* __linux__ */ #ifdef STANDALONE int main(int argc, char **argv) { return (hddown() == 0); } #endif sysvinit-2.90/src/wall.c0000644017777601777760000000541513303017401015044 0ustar nobodynogroup/* * wall.c Write to all users logged in. * * Usage: wall [text] * * Version: @(#)wall 2.79 12-Sep-2000 miquels@cistron.nl * * This file is part of the sysvinit suite, * Copyright (C) 1991-2000 Miquel van Smoorenburg. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include #include "init.h" char *Version = "@(#) wall 2.79 12-Sep-2000 miquels@cistron.nl"; #define MAXLEN 4096 #define MAXLINES 20 int main(int argc, char **argv) { char buf[MAXLEN]; char line[83]; int i, f, ch; int len = 0; int remote = 0; char *p; char *whoami; struct passwd *pwd; buf[0] = 0; if ((pwd = getpwuid(getuid())) == NULL) { if (getuid() == 0) whoami = "root"; else { fprintf(stderr, "You don't exist. Go away.\n"); exit(1); } } else whoami = pwd->pw_name; while((ch = getopt(argc, argv, "n")) != EOF) switch(ch) { case 'n': /* * Undocumented option for suppressing * banner from rpc.rwalld. Only works if * we are root or if we're NOT setgid. */ if (geteuid() != 0 && getgid() != getegid()) { fprintf(stderr, "wall -n: not priviliged\n"); exit(1); } remote = 1; break; default: fprintf(stderr, "usage: wall [message]\n"); return 1; break; } if ((argc - optind) > 0) { for(f = optind; f < argc; f++) { len += strlen(argv[f]) + 1; if (len >= MAXLEN-2) break; strcat(buf, argv[f]); if (f < argc-1) strcat(buf, " "); } strcat(buf, "\r\n"); } else { while(fgets(line, 80, stdin)) { /* * Make sure that line ends in \r\n */ for(p = line; *p && *p != '\r' && *p != '\n'; p++) ; strcpy(p, "\r\n"); len += strlen(line); if (len >= MAXLEN) break; strcat(buf, line); } } i = 0; for (p = buf; *p; p++) { if (*p == '\n' && ++i >= MAXLINES) { *++p = 0; break; } } openlog("wall", LOG_PID, LOG_USER); syslog(LOG_INFO, "wall: user %s broadcasted %d lines (%zu chars)", whoami, i, strlen(buf)); closelog(); unsetenv("TZ"); wall(buf, remote); /*NOTREACHED*/ return 0; } sysvinit-2.90/src/init.c0000644017777601777760000020417213303017401015051 0ustar nobodynogroup/* * Init A System-V Init Clone. * * Usage: /sbin/init * init [0123456SsQqAaBbCc] * telinit [0123456SsQqAaBbCc] * * Version: @(#)init.c 2.86 30-Jul-2004 miquels@cistron.nl */ #define VERSION "2.90" #define DATE "26-Mar-2010" /* * This file is part of the sysvinit suite, * Copyright (C) 1991-2004 Miquel van Smoorenburg. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #include #include #include #include #ifdef __linux__ #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef WITH_SELINUX # include #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 # elif ( __GLIBC__ > 2) && ((__GLIBC__ == 2) && (__GLIBC_MINOR__ >= 1)) # include # endif # endif #endif #include "init.h" #include "initreq.h" #include "paths.h" #include "reboot.h" #include "set.h" #ifndef SIGPWR # define SIGPWR SIGUSR2 #endif #ifndef CBAUD # define CBAUD 0 #endif #ifndef CBAUDEX # define CBAUDEX 0 #endif /* Set a signal handler. */ #define SETSIG(sa, sig, fun, flags) \ do { \ 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 " " DATE " miquels@cistron.nl"; char *bootmsg = "version " VERSION " %s"; #define E_VERSION "INIT_VERSION=sysvinit-" VERSION CHILD *family = NULL; /* The linked list of all entries */ CHILD *newFamily = NULL; /* The list after inittab re-read */ CHILD ch_emerg = { /* Emergency shell */ WAITING, 0, 0, 0, 0, "~~", "S", 3, "/sbin/sulogin", NULL, NULL }; char runlevel = 'S'; /* The current run level */ char thislevel = 'S'; /* The current runlevel */ char prevlevel = 'N'; /* Previous runlevel */ int dfl_level = 0; /* Default runlevel */ sig_atomic_t got_cont = 0; /* Set if we received the SIGCONT signal */ sig_atomic_t got_signals; /* Set if we received a signal. */ int emerg_shell = 0; /* Start emergency shell? */ int wrote_wtmp_reboot = 1; /* Set when we wrote the reboot record */ int wrote_utmp_reboot = 1; /* Set when we wrote the reboot record */ int wrote_wtmp_rlevel = 1; /* Set when we wrote the runlevel record */ int wrote_utmp_rlevel = 1; /* Set when we wrote the runlevel record */ int sltime = 5; /* Sleep time between TERM and KILL */ char *argv0; /* First arguments; show up in ps listing */ int maxproclen; /* Maximal length of argv[0] with \0 */ struct utmp utproto; /* Only used for sizeof(utproto.ut_id) */ char *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="/sbin/init"; /* What should we exec */ int oops_error; /* Used by some of the re-exec code. */ const char *Signature = "12567362"; /* Signature for re-exec fd */ /* Macro to see if this is a special action */ #define ISPOWER(i) ((i) == POWERWAIT || (i) == POWERFAIL || \ (i) == POWEROKWAIT || (i) == POWERFAILNOW || \ (i) == CTRLALTDEL) /* ascii values for the `action' field. */ struct actions { char *name; int act; } actions[] = { { "respawn", RESPAWN }, { "wait", WAIT }, { "once", ONCE }, { "boot", BOOT }, { "bootwait", BOOTWAIT }, { "powerfail", POWERFAIL }, { "powerfailnow",POWERFAILNOW }, { "powerwait", POWERWAIT }, { "powerokwait", POWEROKWAIT }, { "ctrlaltdel", CTRLALTDEL }, { "off", OFF }, { "ondemand", ONDEMAND }, { "initdefault", INITDEFAULT }, { "sysinit", SYSINIT }, { "kbrequest", KBREQUEST }, { NULL, 0 }, }; /* * State parser token table (see receive_state) */ struct { char name[4]; int cmd; } cmds[] = { { "VER", C_VER }, { "END", C_END }, { "REC", C_REC }, { "EOR", C_EOR }, { "LEV", C_LEV }, { "FL ", C_FLAG }, { "AC ", C_ACTION }, { "CMD", C_PROCESS }, { "PID", C_PID }, { "EXS", C_EXS }, { "-RL", D_RUNLEVEL }, { "-TL", D_THISLEVEL }, { "-PL", D_PREVLEVEL }, { "-SI", D_GOTSIGN }, { "-WR", D_WROTE_WTMP_REBOOT}, { "-WU", D_WROTE_UTMP_REBOOT}, { "-ST", D_SLTIME }, { "-DB", D_DIDBOOT }, { "-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]; /* * Sleep a number of seconds. * * This only works correctly because the linux select updates * the elapsed time in the struct timeval passed to select! */ static void do_sleep(int sec) { struct timeval tv; tv.tv_sec = sec; tv.tv_usec = 0; while(select(0, NULL, NULL, NULL, &tv) < 0 && errno == EINTR) ; } /* * Non-failing allocation routines (init cannot fail). */ static void *imalloc(size_t size) { void *m; while ((m = malloc(size)) == NULL) { initlog(L_VB, "out of memory"); do_sleep(5); } memset(m, 0, size); return m; } 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", sltime); fprintf(fp, "-DB%d\n", did_boot); for (p = family; p; p = p->next) { fprintf(fp, "REC%s\n", p->id); fprintf(fp, "LEV%s\n", p->rlevel); for (i = 0, val = p->flags; flags[i].mask; i++) if (val & flags[i].mask) { val &= ~flags[i].mask; fprintf(fp, "FL %s\n",flags[i].name); } fprintf(fp, "PID%d\n",p->pid); fprintf(fp, "EXS%u\n",p->exstat); for(i = 0; actions[i].act; i++) if (actions[i].act == p->action) { fprintf(fp, "AC %s\n", actions[i].name); break; } fprintf(fp, "CMD%s\n", p->process); fprintf(fp, "EOR\n"); } fprintf(fp, "END\n"); fclose(fp); } /* * Read a string from a file descriptor. * 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", &sltime) == 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__ __attribute__ ((format (printf, 1, 2))) #endif static int setproctitle(char *fmt, ...) { va_list ap; int len; char buf[256]; buf[0] = 0; va_start(ap, fmt); len = vsnprintf(buf, sizeof(buf), fmt, ap); va_end(ap); if (maxproclen > 1) { memset(argv0, 0, maxproclen); strncpy(argv0, buf, maxproclen - 1); } return len; } /* * Set console_dev to a working console. */ 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_sleep(5); exit(0); } /* * OOPS: segmentation violation! * If we have the info, print where it occured. * 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_sleep && (void *)ctx.eip < (void *)main) p = " (code)"; initlog(L_VB, "PANIC: segmentation violation at %p%s! " "sleeping for 30 seconds.", (void *)ctx.eip, p); coredump(); do_sleep(30); errno = saved_errno; } #else # 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_sleep(30); 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|ECHOPRT|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_sleep(5); } 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_sleep(5); 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; 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 */ CHILD *ch, *old, *i; /* Pointers to CHILD structure */ CHILD *head = NULL; /* Head of linked list */ #ifdef INITLVL struct stat st; /* To stat INITLVL */ #endif sigset_t nmask, omask; /* For blocking SIGCHLD. */ char buf[256]; /* Line buffer */ char err[64]; /* Error message. */ char *id, *rlevel, *action, *process; /* Fields of a line */ char *p; int lineNo = 0; /* Line number in INITTAB file */ int actionNo; /* Decoded action field */ int f; /* Counter */ int round; /* round 0 for SIGTERM, 1 for SIGKILL */ int foundOne = 0; /* No killing no sleep */ int talk; /* Talk to the user */ int done = 0; /* Ready yet? */ #if DEBUG if (newFamily != NULL) { INITDBG(L_VB, "PANIC newFamily != NULL"); exit(1); } INITDBG(L_VB, "Reading inittab"); #endif /* * Open INITTAB and read line by line. */ if ((fp = fopen(INITTAB, "r")) == NULL) initlog(L_VB, "No inittab file found"); while(!done) { /* * Add single user shell entry at the end. */ if (fp == NULL || fgets(buf, sizeof(buf), fp) == NULL) { done = 1; /* * See if we have a single user entry. */ for(old = newFamily; old; old = old->next) if (strpbrk(old->rlevel, "S")) break; if (old == NULL) snprintf(buf, sizeof(buf), "~~:S:wait:%s\n", SULOGIN); else continue; } lineNo++; /* * Skip comments and empty lines */ for(p = buf; *p == ' ' || *p == '\t'; p++) ; if (*p == '#' || *p == '\n') continue; /* * Decode the fields */ id = strsep(&p, ":"); rlevel = strsep(&p, ":"); action = strsep(&p, ":"); process = strsep(&p, "\n"); /* * Check if syntax is OK. Be very verbose here, to * avoid newbie postings on comp.os.linux.setup :) */ err[0] = 0; if (!id || !*id) strcpy(err, "missing id field"); if (!rlevel) strcpy(err, "missing runlevel field"); if (!process) strcpy(err, "missing process field"); if (!action || !*action) strcpy(err, "missing action field"); if (id && strlen(id) > sizeof(utproto.ut_id)) sprintf(err, "id field too long (max %d characters)", (int)sizeof(utproto.ut_id)); if (rlevel && strlen(rlevel) > 11) strcpy(err, "rlevel field too long (max 11 characters)"); if (process && strlen(process) > 127) strcpy(err, "process field too long (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); #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 5 seconds */ if (foundOne && round == 0) { /* * Yup, but check every second if we still have children. */ for(f = 0; f < sltime; f++) { for(ch = family; ch; ch = ch->next) { if (!(ch->flags & KILLME)) continue; if ((ch->flags & RUNNING) && !(ch->flags & ZOMBIE)) break; } if (ch == NULL) { /* * No running children, skip SIGKILL */ round = 1; foundOne = 0; /* Skip the sleep below. */ break; } do_sleep(1); } } } /* * Now give all processes the chance to die and collect exit statuses. */ if (foundOne) do_sleep(1); for(ch = family; ch; ch = ch->next) if (ch->flags & KILLME) { if (!(ch->flags & ZOMBIE)) initlog(L_CO, "Pid %d [id %s] seems to hang", ch->pid, ch->id); else { INITDBG(L_VB, "Updating utmp for pid %d [id %s]", ch->pid, ch->id); ch->flags &= ~RUNNING; if (ch->process[0] != '+') write_utmp_wtmp("", ch->id, ch->pid, DEAD_PROCESS, NULL); } } /* * Both rounds done; clean up the list. */ sigemptyset(&nmask); sigaddset(&nmask, SIGCHLD); sigprocmask(SIG_BLOCK, &nmask, &omask); for(ch = family; ch; ch = old) { old = ch->next; free(ch); } family = newFamily; for(ch = family; ch; ch = ch->next) ch->new = NULL; newFamily = NULL; sigprocmask(SIG_SETMASK, &omask, NULL); #ifdef INITLVL /* * Dispose of INITLVL file. */ if (lstat(INITLVL, &st) >= 0 && S_ISLNK(st.st_mode)) { /* * INITLVL is a symbolic link, so just truncate the file. */ close(open(INITLVL, O_WRONLY|O_TRUNC)); } else { /* * Delete INITLVL file. */ unlink(INITLVL); } #endif #ifdef INITLVL2 /* * Dispose of INITLVL2 file. */ if (lstat(INITLVL2, &st) >= 0 && S_ISLNK(st.st_mode)) { /* * INITLVL2 is a symbolic link, so just truncate the file. */ close(open(INITLVL2, O_WRONLY|O_TRUNC)); } else { /* * Delete INITLVL2 file. */ unlink(INITLVL2); } #endif } /* * Walk through the family list and start up children. * The entries that do not belong here at all are removed * from the list. */ 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. */ 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) sltime = st; #endif /* INITLVL */ if (islower(foo)) foo = toupper(foo); if (ok < 1 || ok > 2 || strchr("QS0123456789ABCU", foo) == NULL) { initlog(L_VB, "bad runlevel: %c", foo); return runlevel; } /* Log this action */ switch(foo) { case 'S': initlog(L_VB, "Going single user"); break; case 'Q': initlog(L_SY, "Re-reading inittab"); break; case 'A': case 'B': case 'C': initlog(L_SY, "Activating demand-procedures for '%c'", foo); break; case 'U': initlog(L_SY, "Trying to re-exec init"); return 'U'; default: initlog(L_VB, "Switching to runlevel: %c", foo); } if (foo == 'Q') { #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; 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. */ static void re_exec(void) { CHILD *ch; sigset_t mask, oldset; pid_t pid; char **env; int fd; if (strchr("S0123456",runlevel) == NULL) return; /* * Reset the alarm, and block all signals. */ alarm(0); sigfillset(&mask); sigprocmask(SIG_BLOCK, &mask, &oldset); /* * construct a pipe fd --> STATE_PIPE and write a signature */ 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. * Bitch, 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; 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); } } } /* * 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: sltime = request.sleeptime; fifo_new_level(request.runlevel); quit = 1; break; case INIT_CMD_POWERFAIL: sltime = request.sleeptime; do_power_fail('F'); quit = 1; break; case INIT_CMD_POWERFAILNOW: sltime = request.sleeptime; do_power_fail('L'); quit = 1; break; case INIT_CMD_POWEROK: sltime = request.sleeptime; do_power_fail('O'); quit = 1; break; case INIT_CMD_SETENV: initcmd_setenv(request.i.data, sizeof(request.i.data)); break; default: initlog(L_VB, "got unimplemented initrequest."); break; } } /* * 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); } } } /* * 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; 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); } } } 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': sltime = atoi(optarg); break; case 'e': if (env == NULL) env = request.i.data; l = strlen(optarg); if (env + l + 2 > request.i.data + sizeof(request.i.data)) { fprintf(stderr, "%s: -e option data " "too large\n", progname); exit(1); } memcpy(env, optarg, l); env += l; *env++ = 0; break; default: usage(progname); break; } if (env) *env++ = 0; if (env) { if (argc != optind) usage(progname); request.cmd = INIT_CMD_SETENV; } else { if (argc - optind != 1 || strlen(argv[optind]) != 1) usage(progname); if (!strchr("0123456789SsQqAaBbCcUu", argv[optind][0])) usage(progname); request.cmd = INIT_CMD_RUNLVL; request.runlevel = argv[optind][0]; request.sleeptime = sltime; } /* 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], sltime); fclose(fp); /* And tell init about the pending runlevel change. */ if (kill(INITPID, SIGHUP) < 0) perror(progname); return 0; } #endif fprintf(stderr, "%s: ", progname); if (ISMEMBER(got_signals, SIGALRM)) { fprintf(stderr, "timeout opening/writing control channel %s\n", INIT_FIFO); } else { perror(INIT_FIFO); } return 1; } /* * Main entry for init and telinit. */ int main(int argc, char **argv) { char *p; int f; int isinit; #ifdef WITH_SELINUX int enforce = 0; #endif /* Get my own name */ if ((p = strrchr(argv[0], '/')) != NULL) p++; else p = argv[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; } sysvinit-2.90/src/shutdown.c0000644017777601777760000004500413303017401015756 0ustar nobodynogroup/* * 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 #include #include #ifdef __linux__ #include /* brought in my LFS patch */ #endif #include #include #include #include #include #include #include #include #include #include #include #include #include "paths.h" #include "reboot.h" #include "initreq.h" #include "init.h" char *Version = "@(#) shutdown 2.90-1 31-Jul-2004 miquels@cistron.nl"; #define MESSAGELEN 256 int dontshut = 0; /* Don't shutdown, only warn */ char down_level[2]; /* What runlevel to go to. */ int dosync = 1; /* Sync before reboot or halt */ int fastboot = 0; /* Do a 'fast' reboot */ int forcefsck = 0; /* Force fsck on reboot */ char message[MESSAGELEN]; /* Warning message */ char *sltime = 0; /* Sleep time */ char newstate[64]; /* What are we gonna do */ int doself = 0; /* Don't use init */ int got_alrm = 0; char *clean_env[] = { "HOME=/", "PATH=" 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 -H: halt action is to just halt.\n" "\t\t -f: do a 'fast' reboot (skip fsck).\n" "\t\t -F: Force fsck on reboot.\n" "\t\t -n: do not go through \"init\" but go down real fast.\n" "\t\t -c: cancel a running shutdown.\n" "\t\t -t secs: delay between warning and kill signal.\n" "\t\t ** the \"time\" argument is mandatory! (try \"now\") **\n"); exit(1); } void alrm_handler(int sig) { got_alrm = sig; } /* * Set environment variables in the init process. */ int init_setenv(char *name, char *value) { struct init_request request; struct sigaction sa; int fd; size_t nl, vl; memset(&request, 0, sizeof(request)); request.magic = INIT_MAGIC; request.cmd = INIT_CMD_SETENV; nl = strlen(name); vl = value ? strlen(value) : 0; if (nl + vl + 3 >= (int)sizeof(request.i.data)) return -1; memcpy(request.i.data, name, nl); if (value) { request.i.data[nl] = '='; memcpy(request.i.data + nl + 1, value, vl); } /* * Open the fifo and write the command. * Make sure we don't hang on opening /run/initctl */ memset(&sa, 0, sizeof(sa)); sa.sa_handler = alrm_handler; sigaction(SIGALRM, &sa, NULL); got_alrm = 0; alarm(3); if ((fd = open(INIT_FIFO, O_WRONLY)) >= 0) { ssize_t p = 0; size_t s = sizeof(request); void *ptr = &request; while (s > 0) { p = write(fd, ptr, s); if (p < 0) { if (errno == EINTR || errno == EAGAIN) continue; break; } ptr += p; s -= p; } close(fd); alarm(0); return 0; } fprintf(stderr, "shutdown: "); if (got_alrm) { fprintf(stderr, "timeout opening/writing control channel %s\n", INIT_FIFO); } else { perror(INIT_FIFO); } return -1; } /* * Tell everyone the system is going down in 'mins' minutes. */ void issue_warn(int mins) { char buf[MESSAGELEN + sizeof(newstate)]; int len; buf[0] = 0; strncat(buf, message, sizeof(buf) - 1); len = strlen(buf); if (mins == 0) snprintf(buf + len, sizeof(buf) - len, "\rThe system is going down %s NOW!\r\n", newstate); else snprintf(buf + len, sizeof(buf) - len, "\rThe system is going DOWN %s in %d minute%s!\r\n", newstate, mins, mins == 1 ? "" : "s"); wall(buf, 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) : 3); fprintf(stderr, "shutdown: sending all processes the KILL signal.\r\n"); (void) kill(-1, SIGKILL); #if 0 /* See if we can run /etc/init.d/halt */ if (access(script, X_OK) == 0) { spawn(1, cmd, "fast", NULL); fprintf(stderr, "shutdown: %s returned - falling back " "on default routines\r\n", script); } #endif /* script failed or not present: do it ourself. */ /* 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 ret; if (wt < 10) ret = 1; else if (wt < 60) ret = (wt % 15 == 0); else if (wt < 180) ret = (wt % 30 == 0); else ret = (wt % 60 == 0); return ret; } /* * Main program. * Process the options and do the final countdown. */ int main(int argc, char **argv) { FILE *fp; extern int getopt(); extern int optind; struct sigaction sa; struct tm *lt; struct stat st; struct utmp *ut; time_t t, target_time; char *halttype; char *downusers[32]; char buf[128]; char term[UT_LINESIZE + 6]; char *sp; char *when = NULL; int c, i, wt; int hours, mins; int didnolog = 0; int cancel = 0; int useacl = 0; int pid = 0; int user_ok = 0; /* We can be installed setuid root (executable for a special group) */ /* 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; /* Process the options. */ while((c = getopt(argc, argv, "HPacqkrhnfFyt:g:i:")) != EOF) { switch(c) { case 'H': halttype = "HALT"; break; case 'P': halttype = "POWERDOWN"; break; case 'a': /* Access control. */ useacl = 1; break; case 'c': /* Cancel an already running shutdown. */ cancel = 1; break; case 'k': /* Don't really shutdown, only warn.*/ dontshut = 1; break; case 'r': /* Automatic reboot */ down_level[0] = '6'; break; case 'h': /* Halt after shutdown */ down_level[0] = '0'; break; case 'f': /* Don't perform fsck after next boot */ fastboot = 1; break; case 'F': /* Force fsck after next boot */ forcefsck = 1; break; case 'n': /* Don't switch runlevels. */ doself = 1; break; case 't': /* Delay between TERM and KILL */ sltime = optarg; break; case 'y': /* Ignored for sysV compatibility */ break; case 'g': /* sysv style to specify time. */ when = optarg; break; case 'i': /* Level to go to. */ if (!strchr("0156aAbBcCsS", optarg[0])) { fprintf(stderr, "shutdown: `%s': bad runlevel\n", optarg); exit(1); } down_level[0] = optarg[0]; break; default: usage(); break; } } if (NULL != halttype && down_level[0] != '0') { fprintf(stderr, "shutdown: -H and -P flags can only be used along with -h flag.\n"); usage(); exit(1); } /* Do we need to use the shutdown.allow file ? */ if (useacl && (fp = fopen(SDALLOW, "r")) != NULL) { /* Read /etc/shutdown.allow. */ i = 0; while(fgets(buf, 128, fp)) { if (buf[0] == '#' || buf[0] == '\n') continue; if (i > 31) continue; for(sp = buf; *sp; sp++) if (*sp == '\n') *sp = 0; downusers[i++] = strdup(buf); } if (i < 32) downusers[i] = 0; fclose(fp); /* Now walk through /var/run/utmp to find logged in users. */ while(!user_ok && (ut = getutent()) != NULL) { /* See if this is a user process on a VC. */ if (ut->ut_type != USER_PROCESS) continue; sprintf(term, "/dev/%.*s", UT_LINESIZE, ut->ut_line); if (stat(term, &st) < 0) continue; #ifdef major /* glibc */ if (major(st.st_rdev) != 4 || minor(st.st_rdev) > 63) continue; #else if ((st.st_rdev & 0xFFC0) != 0x0400) continue; #endif /* Root is always OK. */ if (strcmp(ut->ut_user, "root") == 0) { user_ok++; break; } /* See if this is an allowed user. */ for(i = 0; i < 32 && downusers[i]; i++) if (!strncmp(downusers[i], ut->ut_user, UT_NAMESIZE)) { user_ok++; break; } } endutent(); /* See if user was allowed. */ if (!user_ok) { if ((fp = fopen(CONSOLE, "w")) != NULL) { fprintf(fp, "\rshutdown: no authorized users " "logged in.\r\n"); fclose(fp); } exit(1); } } /* Read pid of running shutdown from a file */ if ((fp = fopen(SDPID, "r")) != NULL) { 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); 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 == NULL) { if (optind == argc) usage(); when = argv[optind++]; } /* See if we are already running. */ if (pid > 0 && kill(pid, 0) == 0) { fprintf(stderr, "\rshutdown: already running.\r\n"); exit(1); } /* Extra check. */ if (doself && down_level[0] != '0' && down_level[0] != '6') { fprintf(stderr, "shutdown: can use \"-n\" for halt or reboot only.\r\n"); exit(1); } /* Tell users what we're gonna do. */ switch(down_level[0]) { case '0': strcpy(newstate, "for system halt"); break; case '6': strcpy(newstate, "for reboot"); break; case '1': strcpy(newstate, "to maintenance mode"); break; default: sprintf(newstate, "to runlevel %s", down_level); break; } /* 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"); if (when[0] == '+') when++; /* Decode shutdown time. */ for (sp = when; *sp; sp++) { if (*sp != ':' && (*sp < '0' || *sp > '9')) usage(); } if (strchr(when, ':') == NULL) { /* Time in minutes. */ wt = atoi(when); if (wt == 0 && when[0] != '0') usage(); } else { /* Time in hh:mm format. */ if (sscanf(when, "%d:%2d", &hours, &mins) != 2) usage(); if (hours > 23 || mins > 59) usage(); time(&t); lt = localtime(&t); wt = (60*hours + mins) - (60*lt->tm_hour + lt->tm_min); if (wt < 0) wt += 1440; } /* Shutdown NOW if time == 0 */ if (wt == 0) issue_shutdown(halttype); /* Rather than loop and reduce wt (wait time) once per minute, we shall check the current time against the target time. Then calculate the remaining wating time based on the difference between current time and target time. This avoids missing shutdown time (target time) after the computer has been asleep. -- Jesse */ /* target time, in seconds = current time + wait time */ time(&t); target_time = t + (60 * wt); /* Give warnings on regular intervals and finally shutdown. */ if (wt < 15 && !needwarning(wt)) issue_warn(wt); while(wt) { if (wt <= 5 && !didnolog) { donologin(wt); didnolog++; } if (needwarning(wt)) 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 thsn 1 min remains */ wt = (int) (target_time - t) / 60; } issue_shutdown(halttype); return 0; /* Never happens */ } sysvinit-2.90/src/paths.h0000644017777601777760000000447613303017401015237 0ustar nobodynogroup/* * paths.h Paths of files that init and related utilities need. * * Version: @(#) paths.h 2.85-8 05-Nov-2003 * * Author: Miquel van Smoorenburg, * * This file is part of the sysvinit suite, * Copyright (C) 1991-2001 Miquel van Smoorenburg. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #define VT_MASTER "/dev/tty0" /* Virtual console master */ #define CONSOLE "/dev/console" /* Logical system console */ #define SECURETTY "/etc/securetty" /* List of root terminals */ #define SDALLOW "/etc/shutdown.allow" /* Users allowed to shutdown */ #define INITTAB "/etc/inittab" /* Location of inittab */ #define INIT "/sbin/init" /* Location of init itself. */ #define NOLOGIN "/etc/nologin" /* Stop user logging in. */ #define FASTBOOT "/fastboot" /* Enable fast boot. */ #define FORCEFSCK "/forcefsck" /* Force fsck on boot */ #define SDPID "/var/run/shutdown.pid" /* PID of shutdown program */ #define SHELL "/bin/sh" /* Default shell */ #define SULOGIN "/sbin/sulogin" /* Sulogin */ #define INITSCRIPT "/etc/initscript" /* Initscript. */ #define PWRSTAT_OLD "/etc/powerstatus" /* COMPAT: SIGPWR reason (OK/BAD) */ #define PWRSTAT "/var/run/powerstatus" /* COMPAT: SIGPWR reason (OK/BAD) */ #if 0 #define INITLVL "/etc/initrunlvl" /* COMPAT: New runlevel */ #define INITLVL2 "/var/log/initrunlvl" /* COMPAT: New runlevel */ /* Note: INITLVL2 definition needs INITLVL */ #define HALTSCRIPT1 "/etc/init.d/halt" /* Called by "fast" shutdown */ #define HALTSCRIPT2 "/etc/rc.d/rc.0" /* Called by "fast" shutdown */ #define REBOOTSCRIPT1 "/etc/init.d/reboot" /* Ditto. */ #define REBOOTSCRIPT2 "/etc/rc.d/rc.6" /* Ditto. */ #endif sysvinit-2.90/src/oldutmp.h0000644017777601777760000000230213303017401015566 0ustar nobodynogroup/* * oldutmp.h Definition of the old libc5 utmp structure. * * Version: @(#)oldutmp.h 1.00 29-Mar-1998 miquels@cistron.nl * * Copyright (C) 1991-2000 Miquel van Smoorenburg. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #ifndef OLD_UTMP_H #define OLD_UTMP_H #define OLD_LINESIZE 12 #define OLD_NAMESIZE 8 #define OLD_HOSTSIZE 16 struct oldutmp { short ut_type; int ut_pid; char ut_line[OLD_LINESIZE]; char ut_id[4]; long ut_oldtime; char ut_user[OLD_NAMESIZE]; char ut_host[OLD_HOSTSIZE]; long ut_oldaddr; }; #endif sysvinit-2.90/src/bootlogd.c0000644017777601777760000003703013303017401015714 0ustar nobodynogroup/* * bootlogd.c Store output from the console during bootup into a file. * The file is usually located on the /var partition, and * gets written (and fsynced) as soon as possible. * * Version: @(#)bootlogd 2.86pre 12-Jan-2004 miquels@cistron.nl * * Bugs: Uses openpty(), only available in glibc. Sorry. * * This file is part of the sysvinit suite, * Copyright (C) 1991-2004 Miquel van Smoorenburg. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * *NOTE* *NOTE* *NOTE* * This is a PROOF OF CONCEPT IMPLEMENTATION * * I have bigger plans for Debian, but for now * this has to do ;) * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef __linux__ #include #endif char *Version = "@(#) bootlogd 2.86 03-Jun-2004 miquels@cistron.nl"; #define LOGFILE "/var/log/boot" #define MAX_CONSOLES 16 char ringbuf[32768]; char *endptr = ringbuf + sizeof(ringbuf); char *inptr = ringbuf; char *outptr = ringbuf; int got_signal = 0; int didnl = 1; int createlogfile = 0; int syncalot = 0; struct real_cons { char name[1024]; int fd; }; /* * Console devices as listed on the kernel command line and * the mapping to actual devices in /dev */ struct consdev { char *cmdline; char *dev1; char *dev2; } consdev[] = { { "ttyB", "/dev/ttyB%s", NULL }, { "ttySC", "/dev/ttySC%s", "/dev/ttsc/%s" }, { "ttyS", "/dev/ttyS%s", "/dev/tts/%s" }, { "tty", "/dev/tty%s", "/dev/vc/%s" }, { "hvc", "/dev/hvc%s", "/dev/hvc/%s" }, { NULL, NULL, NULL }, }; /* * Devices to try as console if not found on kernel command line. * Tried from left to right (as opposed to kernel cmdline). */ char *defcons[] = { "tty0", "hvc0", "ttyS0", "ttySC0", "ttyB0", NULL }; /* * Catch signals. */ void handler(int sig) { got_signal = sig; } /* * Scan /dev and find the device name. */ /* This function does not appear to be called anymore. Commenting it out for now, can probably be removed entirely in the future. static int findtty(char *res, const char *startdir, int rlen, dev_t dev) { DIR *dir; struct dirent *ent; struct stat st; int r = -1; char *olddir = getcwd(NULL, 0); if (chdir(startdir) < 0 || (dir = opendir(".")) == NULL) { int msglen = strlen(startdir) + 11; char *msg = malloc(msglen); snprintf(msg, msglen, "bootlogd: %s", startdir); perror(msg); free(msg); chdir(olddir); return -1; } while ((ent = readdir(dir)) != NULL) { if (lstat(ent->d_name, &st) != 0) continue; if (S_ISDIR(st.st_mode) && 0 != strcmp(".", ent->d_name) && 0 != strcmp("..", ent->d_name)) { char *path = malloc(rlen); snprintf(path, rlen, "%s/%s", startdir, ent->d_name); r = findtty(res, path, rlen, dev); free(path); if (0 == r) { closedir(dir); chdir(olddir); return 0; } continue; } if (!S_ISCHR(st.st_mode)) continue; if (st.st_rdev == dev) { if ( (int) (strlen(ent->d_name) + strlen(startdir) + 1) >= rlen) { fprintf(stderr, "bootlogd: console device name too long\n"); closedir(dir); chdir(olddir); return -1; } else { snprintf(res, rlen, "%s/%s", startdir, ent->d_name); closedir(dir); chdir(olddir); return 0; } } } closedir(dir); chdir(olddir); return r; } */ /* * For some reason, openpty() in glibc sometimes doesn't * work at boot-time. It must be a bug with old-style pty * names, as new-style (/dev/pts) is not available at that * point. So, we find a pty/tty pair ourself if openpty() * fails for whatever reason. */ int findpty(int *master, int *slave, char *name) { char pty[16]; char tty[16]; int i, j; int found; if (openpty(master, slave, name, NULL, NULL) >= 0) return 0; found = 0; for (i = 'p'; i <= 'z'; i++) { for (j = '0'; j <= 'f'; j++) { if (j == '9' + 1) j = 'a'; sprintf(pty, "/dev/pty%c%c", i, j); sprintf(tty, "/dev/tty%c%c", i, j); if ((*master = open(pty, O_RDWR|O_NOCTTY)) >= 0) { *slave = open(tty, O_RDWR|O_NOCTTY); if (*slave >= 0) { found = 1; break; } } } if (found) break; } if (!found) return -1; if (name) strcpy(name, tty); return 0; } /* * See if a console taken from the kernel command line maps * to a character device we know about, and if we can open it. */ int isconsole(char *s, char *res, int rlen) { struct consdev *c; int l, sl, i, fd; char *p, *q; sl = strlen(s); for (c = consdev; c->cmdline; c++) { l = strlen(c->cmdline); if (sl <= l) continue; p = s + l; if (strncmp(s, c->cmdline, l) != 0 || !isdigit(*p)) continue; for (i = 0; i < 2; i++) { snprintf(res, rlen, i ? c->dev1 : c->dev2, p); if ((q = strchr(res, ',')) != NULL) *q = 0; if ((fd = open(res, O_RDONLY|O_NONBLOCK)) >= 0) { close(fd); return 1; } } } return 0; } /* * Find out the _real_ console(s). Assume that stdin is connected to * the console device (/dev/console). */ int consolenames(struct real_cons *cons, int max_consoles) { #ifdef TIOCGDEV /* This appears to be unused. unsigned int kdev; */ #endif struct stat st, st2; char buf[256]; char *p; int didmount = 0; int n; int fd; int considx, num_consoles = 0; #ifdef __linux__ /* * Read /proc/cmdline. */ stat("/", &st); if (stat("/proc", &st2) < 0) { perror("bootlogd: /proc"); return 0; } if (st.st_dev == st2.st_dev) { if (mount("proc", "/proc", "proc", 0, NULL) < 0) { perror("bootlogd: mount /proc"); return -1; } didmount = 1; } n = -1; if ((fd = open("/proc/cmdline", O_RDONLY)) < 0) { perror("bootlogd: /proc/cmdline"); } else { buf[0] = 0; if ((n = read(fd, buf, sizeof(buf) - 1)) < 0) perror("bootlogd: /proc/cmdline"); close(fd); } if (didmount) umount("/proc"); if (n < 0) return 0; /* * OK, so find console= in /proc/cmdline. * Parse in reverse, opening as we go. */ p = buf + n; *p-- = 0; while (p >= buf) { if (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') { *p-- = 0; continue; } if (strncmp(p, "console=", 8) == 0 && isconsole(p + 8, cons[num_consoles].name, sizeof(cons[num_consoles].name))) { /* * Suppress duplicates */ for (considx = 0; considx < num_consoles; considx++) { if (!strcmp(cons[num_consoles].name, cons[considx].name)) { goto dontuse; } } num_consoles++; if (num_consoles >= max_consoles) { break; } } dontuse: p--; } if (num_consoles > 0) return num_consoles; #endif /* * Okay, no console on the command line - * guess the default console. */ for (n = 0; defcons[n]; n++) if (isconsole(defcons[n], cons[0].name, sizeof(cons[0].name))) return 1; fprintf(stderr, "bootlogd: cannot deduce real console device\n"); return 0; } /* * Write data and make sure it's on disk. */ void writelog(FILE *fp, unsigned char *ptr, int len) { int dosync = 0; int i; static int first_run = 1; static int inside_esc = 0; for (i = 0; i < len; i++) { int ignore = 0; /* prepend date to every line */ if (*(ptr-1) == '\n' || first_run) { time_t t; char *s; time(&t); s = ctime(&t); fprintf(fp, "%.24s: ", s); dosync = 1; first_run = 0; } /* remove escape sequences, but do it in a way that allows us to stop * in the middle in case the string was cut off */ if (inside_esc == 1) { /* first '[' is special because if we encounter it again, it should be considered the final byte */ if (*ptr == '[') { /* multi char sequence */ ignore = 1; inside_esc = 2; } else { /* single char sequence */ if (*ptr >= 64 && *ptr <= 95) { ignore = 1; } inside_esc = 0; } } else if (inside_esc == 2) { switch (*ptr) { case '0' ... '9': /* intermediate chars of escape sequence */ case ';': case 32 ... 47: if (inside_esc) { ignore = 1; } break; case 64 ... 126: /* final char of escape sequence */ if (inside_esc) { ignore = 1; inside_esc = 0; } break; } } else { switch (*ptr) { case '\r': ignore = 1; break; case 27: /* ESC */ ignore = 1; inside_esc = 1; break; } } if (!ignore) { fwrite(ptr, sizeof(char), 1, fp); } ptr++; } if (dosync) { fflush(fp); if (syncalot) { fdatasync(fileno(fp)); } } outptr += len; if (outptr >= endptr) outptr = ringbuf; } /* * Print usage message and exit. */ void usage(void) { fprintf(stderr, "Usage: bootlogd [-v] [-r] [-d] [-s] [-c] [-p pidfile] [-l logfile]\n"); exit(1); } int open_nb(char *buf) { int fd, n; if ((fd = open(buf, O_WRONLY|O_NONBLOCK|O_NOCTTY)) < 0) return -1; n = fcntl(fd, F_GETFL); n &= ~(O_NONBLOCK); fcntl(fd, F_SETFL, n); return fd; } /* * We got a write error on the real console. If its an EIO, * somebody hung up our filedescriptor, so try to re-open it. */ int write_err(int pts, int realfd, char *realcons, int e) { int fd; if (e != EIO) { werr: close(pts); fprintf(stderr, "bootlogd: writing to console: %s\n", strerror(e)); return -1; } close(realfd); if ((fd = open_nb(realcons)) < 0) goto werr; return fd; } int main(int argc, char **argv) { FILE *fp; struct timeval tv; fd_set fds; char buf[1024]; char *p; char *logfile; char *pidfile; int rotate; int dontfork; int ptm, pts; /* int realfd; -- this is now unused */ int n, m, i; int todo; #ifndef __linux__ /* BSD-style ioctl needs an argument. */ int on = 1; #endif int considx; struct real_cons cons[MAX_CONSOLES]; int num_consoles, consoles_left; fp = NULL; logfile = LOGFILE; pidfile = NULL; rotate = 0; dontfork = 0; while ((i = getopt(argc, argv, "cdsl:p:rv")) != EOF) switch(i) { case 'l': logfile = optarg; break; case 'r': rotate = 1; break; case 'v': printf("%s\n", Version); exit(0); break; case 'p': pidfile = optarg; break; case 'c': createlogfile = 1; break; case 'd': dontfork = 1; break; case 's': syncalot = 1; break; default: usage(); break; } if (optind < argc) usage(); signal(SIGTERM, handler); signal(SIGQUIT, handler); signal(SIGINT, handler); signal(SIGTTIN, SIG_IGN); signal(SIGTTOU, SIG_IGN); signal(SIGTSTP, SIG_IGN); /* * Open console device directly. */ /* if (consolename(realcons, sizeof(realcons)) < 0) return 1; if (strcmp(realcons, "/dev/tty0") == 0) strcpy(realcons, "/dev/tty1"); if (strcmp(realcons, "/dev/vc/0") == 0) strcpy(realcons, "/dev/vc/1"); if ((realfd = open_nb(realcons)) < 0) { fprintf(stderr, "bootlogd: %s: %s\n", realcons, strerror(errno)); return 1; } */ if ((num_consoles = consolenames(cons, MAX_CONSOLES)) <= 0) return 1; consoles_left = num_consoles; for (considx = 0; considx < num_consoles; considx++) { if (strcmp(cons[considx].name, "/dev/tty0") == 0) strcpy(cons[considx].name, "/dev/tty1"); if (strcmp(cons[considx].name, "/dev/vc/0") == 0) strcpy(cons[considx].name, "/dev/vc/1"); if ((cons[considx].fd = open_nb(cons[considx].name)) < 0) { fprintf(stderr, "bootlogd: %s: %s\n", cons[considx].name, strerror(errno)); consoles_left--; } } if (!consoles_left) return 1; /* * Grab a pty, and redirect console messages to it. */ ptm = -1; pts = -1; buf[0] = 0; if (findpty(&ptm, &pts, buf) < 0) { fprintf(stderr, "bootlogd: cannot allocate pseudo tty: %s\n", strerror(errno)); return 1; } #ifdef __linux__ (void)ioctl(0, TIOCCONS, NULL); /* Work around bug in 2.1/2.2 kernels. Fixed in 2.2.13 and 2.3.18 */ if ((n = open("/dev/tty0", O_RDWR)) >= 0) { (void)ioctl(n, TIOCCONS, NULL); close(n); } #endif #ifdef __linux__ if (ioctl(pts, TIOCCONS, NULL) < 0) #else /* BSD usage of ioctl TIOCCONS. */ if (ioctl(pts, TIOCCONS, &on) < 0) #endif { fprintf(stderr, "bootlogd: ioctl(%s, TIOCCONS): %s\n", buf, strerror(errno)); return 1; } /* * Fork and write pidfile if needed. */ if (!dontfork) { pid_t child_pid = fork(); switch (child_pid) { case -1: /* I am parent and the attempt to create a child failed */ fprintf(stderr, "bootlogd: fork failed: %s\n", strerror(errno)); exit(1); break; case 0: /* I am the child */ break; default: /* I am parent and got child's pid */ exit(0); break; } setsid(); } if (pidfile) { unlink(pidfile); if ((fp = fopen(pidfile, "w")) != NULL) { fprintf(fp, "%d\n", (int)getpid()); fclose(fp); } fp = NULL; } /* * Read the console messages from the pty, and write * to the real console and the logfile. */ while (!got_signal) { /* * We timeout after 5 seconds if we still need to * open the logfile. There might be buffered messages * we want to write. */ tv.tv_sec = 0; tv.tv_usec = 500000; FD_ZERO(&fds); FD_SET(ptm, &fds); if (select(ptm + 1, &fds, NULL, NULL, &tv) == 1) { /* * See how much space there is left, read. */ if ((n = read(ptm, inptr, endptr - inptr)) >= 0) { /* * Write data (in chunks if needed) * to the real output devices. */ for (considx = 0; considx < num_consoles; considx++) { if (cons[considx].fd < 0) continue; m = n; p = inptr; while (m > 0) { i = write(cons[considx].fd, p, m); if (i >= 0) { m -= i; p += i; continue; } /* * Handle EIO (somebody hung * up our filedescriptor) */ cons[considx].fd = write_err(pts, cons[considx].fd, cons[considx].name, errno); if (cons[considx].fd >= 0) continue; /* * If this was the last console, * generate a fake signal */ if (--consoles_left <= 0) got_signal = 1; break; } } /* * Increment buffer position. Handle * wraps, and also drag output pointer * along if we cross it. */ inptr += n; if (inptr - n < outptr && inptr > outptr) outptr = inptr; if (inptr >= endptr) inptr = ringbuf; if (outptr >= endptr) outptr = ringbuf; } } /* * Perhaps we need to open the logfile. */ if (fp == NULL && access(logfile, F_OK) == 0) { if (rotate) { snprintf(buf, sizeof(buf), "%s~", logfile); rename(logfile, buf); } fp = fopen(logfile, "a"); } if (fp == NULL && createlogfile) fp = fopen(logfile, "a"); if (inptr >= outptr) todo = inptr - outptr; else todo = endptr - outptr; if (fp && todo) writelog(fp, (unsigned char *)outptr, todo); } if (fp) { if (!didnl) fputc('\n', fp); fclose(fp); } close(pts); close(ptm); for (considx = 0; considx < num_consoles; considx++) { close(cons[considx].fd); } return 0; } sysvinit-2.90/src/ifdown.c0000644017777601777760000000673213303017401015376 0ustar nobodynogroup/* * ifdown.c Find all network interfaces on the system and * shut them down. * * Copyright (C) 1998 Miquel van Smoorenburg. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ char *v_ifdown = "@(#)ifdown.c 1.11 02-Jun-1998 miquels@cistron.nl"; #include #include #include #include #include #include #include #include #include #include #include #define MAX_IFS 64 /* XXX: Ideally this would get detected at configure time... */ #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || \ defined(__NetBSD__) || defined(__OpenBSD__) #define HAVE_SOCKADDR_SA_LEN 1 #endif #ifndef _SIZEOF_ADDR_IFREQ #ifdef HAVE_SOCKADDR_SA_LEN #define _SIZEOF_ADDR_IFREQ(ifr) \ ((ifr).ifr_addr.sa_len > sizeof(struct sockaddr) ? \ (sizeof((ifr).ifr_name) + (ifr).ifr_addr.sa_len) : \ sizeof(struct ifreq)) #else #define _SIZEOF_ADDR_IFREQ(ifr) sizeof(struct ifreq) #endif #endif /* * First, we find all shaper devices and down them. Then we * down all real interfaces. This is because the comment in the * shaper driver says "if you down the shaper device before the * attached inerface your computer will follow". */ int ifdown(void) { char ifr_buf[sizeof(struct ifreq) * MAX_IFS]; char *ifr_end; struct ifconf ifc; int fd; int shaper; if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { fprintf(stderr, "ifdown: "); perror("socket"); return -1; } ifc.ifc_len = sizeof(ifr_buf); ifc.ifc_buf = ifr_buf; if (ioctl(fd, SIOCGIFCONF, &ifc) < 0) { fprintf(stderr, "ifdown: "); perror("SIOCGIFCONF"); close(fd); return -1; } ifr_end = ifr_buf + ifc.ifc_len; for (shaper = 1; shaper >= 0; shaper--) { char *ifr_next = ifr_buf; while (ifr_next < ifr_end) { struct ifreq *ifr; int flags; ifr = (struct ifreq *)ifr_next; ifr_next += _SIZEOF_ADDR_IFREQ(*ifr); if ((strncmp(ifr->ifr_name, "shaper", 6) == 0) != shaper) continue; if (strncmp(ifr->ifr_name, "lo", 2) == 0) continue; if (strchr(ifr->ifr_name, ':') != NULL) continue; /* Read interface flags */ if (ioctl(fd, SIOCGIFFLAGS, ifr) < 0) { fprintf(stderr, "ifdown: shutdown "); perror(ifr->ifr_name); continue; } /* * Expected in according to * "UNIX Network Programming". */ #ifdef ifr_flagshigh flags = (ifr->ifr_flags & 0xffff) | (ifr->ifr_flagshigh << 16); #else flags = ifr->ifr_flags; #endif if (flags & IFF_UP) { flags &= ~(IFF_UP); #ifdef ifr_flagshigh ifr->ifr_flags = flags & 0xffff; ifr->ifr_flagshigh = flags >> 16; #else ifr->ifr_flags = flags; #endif if (ioctl(fd, SIOCSIFFLAGS, ifr) < 0) { fprintf(stderr, "ifdown: shutdown "); perror(ifr->ifr_name); } } } } close(fd); return 0; } sysvinit-2.90/src/mesg.c0000644017777601777760000000551213303017401015036 0ustar nobodynogroup/* * mesg.c The "mesg" utility. Gives / restrict access to * your terminal by others. * * Usage: mesg [y|n]. * Without arguments prints out the current settings. * * This file is part of the sysvinit suite, * Copyright (C) 1991-2001 Miquel van Smoorenburg. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include #include char *Version = "@(#) mesg 2.81 31-Jul-2001 miquels@cistron.nl"; #define TTYGRP "tty" /* * See if the system has a special 'tty' group. * If it does, and the tty device is in that group, * we set the modes to -rw--w--- instead if -rw--w--w. */ int hasttygrp(void) { struct group *grp; if ((grp = getgrnam(TTYGRP)) != NULL) return 1; return 0; } /* * See if the tty devices group is indeed 'tty' */ int tty_in_ttygrp(struct stat *st) { struct group *gr; if ((gr = getgrgid(st->st_gid)) == NULL) return 0; if (strcmp(gr->gr_name, TTYGRP) != 0) return 0; return 1; } int main(int argc, char **argv) { struct stat st; unsigned int ttymode, st_mode_old; int ht; int it; int e; if (!isatty(0)) { /* Or should we look in /var/run/utmp? */ fprintf(stderr, "stdin: is not a tty\n"); return(1); } if (fstat(0, &st) < 0) { perror("fstat"); return(1); } ht = hasttygrp(); it = tty_in_ttygrp(&st); if (argc < 2) { ttymode = (ht && it) ? 020 : 002; printf("is %s\n", (st.st_mode & ttymode) ? "y" : "n"); return 0; } if (argc > 2 || (argv[1][0] != 'y' && argv[1][0] != 'n')) { fprintf(stderr, "Usage: mesg [y|n]\n"); return 1; } /* * Security check: allow mesg n when group is * weird, but don't allow mesg y. */ ttymode = ht ? 020 : 022; if (ht && !it && argv[1][0] == 'y') { fprintf(stderr, "mesg: error: tty device is not owned " "by group `%s'\n", TTYGRP); exit(1); } st_mode_old = st.st_mode; if (argv[1][0] == 'y') st.st_mode |= ttymode; else st.st_mode &= ~(ttymode); if (st_mode_old != st.st_mode && fchmod(0, st.st_mode) != 0) { e = errno; fprintf(stderr, "mesg: %s: %s\n", ttyname(0), strerror(e)); exit(1); } return 0; } sysvinit-2.90/man/0000755017777601777760000000000013303017401013720 5ustar nobodynogroupsysvinit-2.90/man/wall.10000644017777601777760000000365013303017401014745 0ustar nobodynogroup'\" -*- coding: UTF-8 -*- .\" Copyright (C) 1998-2003 Miquel van Smoorenburg. .\" .\" This program is free software; you can redistribute it and/or modify .\" it under the terms of the GNU General Public License as published by .\" the Free Software Foundation; either version 2 of the License, or .\" (at your option) any later version. .\" .\" This program is distributed in the hope that it will be useful, .\" but WITHOUT ANY WARRANTY; without even the implied warranty of .\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the .\" GNU General Public License for more details. .\" .\" You should have received a copy of the GNU General Public License .\" along with this program; if not, write to the Free Software .\" Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA .\" .TH WALL 1 "15 April 2003" "" "Linux User's Manual" .SH NAME wall -- send a message to everybody's terminal. .SH SYNOPSIS .B wall .RB [ \-n ] .RB [ " message " ] .SH DESCRIPTION .B Wall sends a message to everybody logged in with their .IR mesg (1) permission set to .BR yes . The message can be given as an argument to .IR wall , or it can be sent to .IR wall 's standard input. When using the standard input from a terminal, the message should be terminated with the .B EOF key (usually Control-D). .PP The length of the message is limited to 20 lines. For every invocation of .I wall a notification will be written to syslog, with facility .B LOG_USER and level .BR LOG_INFO . .SH OPTIONS .IP \fB\-n\fn Suppresses the normal banner printed by .IR wall , changing it to "Remote broadcast message". This option is only available for root if .I wall is installed set-group-id, and is used by .IR rpc.walld (8). .PP .SH ENVIRONMENT .I Wall ignores the .B TZ variable - the time printed in the banner is based on the system's local time. .SH SEE ALSO .IR mesg (1), .IR rpc.rwalld (8). .SH AUTHOR Miquel van Smoorenburg, miquels@cistron.nl sysvinit-2.90/man/inittab.50000644017777601777760000002023113303017401015436 0ustar nobodynogroup'\" -*- coding: UTF-8 -*- .\" Copyright (C) 1998-2001 Miquel van Smoorenburg. .\" .\" This program is free software; you can redistribute it and/or modify .\" it under the terms of the GNU General Public License as published by .\" the Free Software Foundation; either version 2 of the License, or .\" (at your option) any later version. .\" .\" This program is distributed in the hope that it will be useful, .\" but WITHOUT ANY WARRANTY; without even the implied warranty of .\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the .\" GNU General Public License for more details. .\" .\" You should have received a copy of the GNU General Public License .\" along with this program; if not, write to the Free Software .\" Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA .\" .\"{{{}}} .\"{{{ Title .TH INITTAB 5 "Dec 4, 2001" "" "Linux System Administrator's Manual" .\"}}} .\"{{{ Name .SH NAME inittab \- format of the inittab file used by the sysv-compatible init process .\"}}} .\"{{{ Description .SH DESCRIPTION The \fBinittab\fP file describes which processes are started at bootup and during normal operation (e.g.\& /etc/init.d/boot, /etc/init.d/rc, gettys...). .BR Init (8) distinguishes multiple \fIrunlevels\fP, each of which can have its own set of processes that are started. Valid runlevels are \fB0\fP\-\fB6\fP plus \fBA\fP, \fBB\fP, and \fBC\fP for \fBondemand\fP entries. An entry in the \fBinittab\fP file has the following format: .RS .sp \fIid\fP:\fIrunlevels\fP:\fIaction\fP:\fIprocess\fP .sp .RE Lines beginning with `#' are ignored. .\"{{{ id .IP \fIid\fP is a unique sequence of 1-4 characters which identifies an entry in .B inittab (for versions of sysvinit compiled with the \fIold\fP libc5 (< 5.2.18) or a.out libraries the limit is 2 characters). .sp Note: traditionally, for getty and other login processes, the value of the \fIid\fP field is kept the same as the suffix of the corresponding tty, e.g.\& \fB1\fP for \fBtty1\fP. Some ancient login accounting programs might expect this, though I can't think of any. .\"}}} .\"{{{ runlevels .IP \fIrunlevels\fP lists the runlevels for which the specified action should be taken. .\"}}} .\"{{{ action .IP \fIaction\fP describes which action should be taken. .\"}}} .\"{{{ process .IP \fIprocess\fP specifies the process to be executed. If the process field starts with a `+' character, .B init will not do utmp and wtmp accounting for that process. This is needed for gettys that insist on doing their own utmp/wtmp housekeeping. This is also a historic bug. The length of this field is limited to 127 characters. .\"}}} .PP The \fIrunlevels\fP field may contain multiple characters for different runlevels. For example, \fB123\fP specifies that the process should be started in runlevels 1, 2, and 3. The \fIrunlevels\fP for \fBondemand\fP entries may contain an \fBA\fP, \fBB\fP, or \fBC\fP. The \fIrunlevels\fP field of \fBsysinit\fP, \fBboot\fP, and \fBbootwait\fP entries are ignored. .PP When the system runlevel is changed, any running processes that are not specified for the new runlevel are killed, first with \s-2SIGTERM\s0, then with \s-2SIGKILL\s0. .PP Valid actions for the \fIaction\fP field are: .\"{{{ respawn .IP \fBrespawn\fP The process will be restarted whenever it terminates (e.g.\& getty). .\"}}} .\"{{{ wait .IP \fBwait\fP The process will be started once when the specified runlevel is entered and .B init will wait for its termination. .\"}}} .\"{{{ once .IP \fBonce\fP The process will be executed once when the specified runlevel is entered. .\"}}} .\"{{{ boot .IP \fBboot\fP The process will be executed during system boot. The \fIrunlevels\fP field is ignored. .\"}}} .\"{{{ bootwait .IP \fBbootwait\fP The process will be executed during system boot, while .B init waits for its termination (e.g.\& /etc/rc). The \fIrunlevels\fP field is ignored. .\"}}} .\"{{{ off .IP \fBoff\fP This does nothing. .\"}}} .\"{{{ ondemand .IP \fBondemand\fP A process marked with an \fBondemand\fP runlevel will be executed whenever the specified \fBondemand\fP runlevel is called. However, no runlevel change will occur (\fBondemand\fP runlevels are `a', `b', and `c'). .\"}}} .\"{{{ initdefault .IP \fBinitdefault\fP An \fBinitdefault\fP entry specifies the runlevel which should be entered after system boot. If none exists, .B init will ask for a runlevel on the console. The \fIprocess\fP field is ignored. .\"}}} .\"{{{ sysinit .IP \fBsysinit\fP The process will be executed during system boot. It will be executed before any \fBboot\fP or \fB bootwait\fP entries. The \fIrunlevels\fP field is ignored. .\"}}} .\"{{{ powerwait .IP \fBpowerwait\fP The process will be executed when the power goes down. Init is usually informed about this by a process talking to a UPS connected to the computer. \fBInit\fP will wait for the process to finish before continuing. .\"}}} .\"{{{ powerfail .IP \fBpowerfail\fP As for \fBpowerwait\fP, except that \fBinit\fP does not wait for the process's completion. .\"}}} .\"{{{ powerokwait .IP \fBpowerokwait\fP This process will be executed as soon as \fBinit\fP is informed that the power has been restored. .\"}}} .\"{{{ powerfailnow .IP \fBpowerfailnow\fP This process will be executed when \fBinit\fP is told that the battery of the external UPS is almost empty and the power is failing (provided that the external UPS and the monitoring process are able to detect this condition). .\"}}} .\"{{{ ctrlaltdel .IP \fBctrlaltdel\fP The process will be executed when \fBinit\fP receives the SIGINT signal. This means that someone on the system console has pressed the \fBCTRL\-ALT\-DEL\fP key combination. Typically one wants to execute some sort of \fBshutdown\fP either to get into single\-user level or to reboot the machine. .\"}}} .\"{{{ kbrequest .IP \fBkbrequest\fP The process will be executed when \fBinit\fP receives a signal from the keyboard handler that a special key combination was pressed on the console keyboard. .sp The documentation for this function is not complete yet; more documentation can be found in the kbd-x.xx packages (most recent was kbd-0.94 at the time of this writing). Basically you want to map some keyboard combination to the "KeyboardSignal" action. For example, to map Alt-Uparrow for this purpose use the following in your keymaps file: .RS .sp alt keycode 103 = KeyboardSignal .sp .RE .\"}}} .\"}}} .\"{{{ Examples .SH EXAMPLES This is an example of a inittab which resembles the old Linux inittab: .RS .sp .nf .ne 7 # inittab for linux id:1:initdefault: rc::bootwait:/etc/rc 1:1:respawn:/etc/getty 9600 tty1 2:1:respawn:/etc/getty 9600 tty2 3:1:respawn:/etc/getty 9600 tty3 4:1:respawn:/etc/getty 9600 tty4 .fi .sp .RE This inittab file executes \fB/etc/rc\fP during boot and starts gettys on tty1\-tty4. .PP A more elaborate \fBinittab\fP with different runlevels (see the comments inside): .RS .sp .nf .ne 19 # Level to run in id:2:initdefault: # Boot-time system configuration/initialization script. si::sysinit:/etc/init.d/rcS # What to do in single-user mode. ~:S:wait:/sbin/sulogin # /etc/init.d executes the S and K scripts upon change # of runlevel. # # Runlevel 0 is halt. # Runlevel 1 is single-user. # Runlevels 2-5 are multi-user. # Runlevel 6 is reboot. l0:0:wait:/etc/init.d/rc 0 l1:1:wait:/etc/init.d/rc 1 l2:2:wait:/etc/init.d/rc 2 l3:3:wait:/etc/init.d/rc 3 l4:4:wait:/etc/init.d/rc 4 l5:5:wait:/etc/init.d/rc 5 l6:6:wait:/etc/init.d/rc 6 # What to do at the "3 finger salute". ca::ctrlaltdel:/sbin/shutdown -t1 -h now # Runlevel 2,3: getty on virtual consoles # Runlevel 3: getty on terminal (ttyS0) and modem (ttyS1) 1:23:respawn:/sbin/getty tty1 VC linux 2:23:respawn:/sbin/getty tty2 VC linux 3:23:respawn:/sbin/getty tty3 VC linux 4:23:respawn:/sbin/getty tty4 VC linux S0:3:respawn:/sbin/getty -L 9600 ttyS0 vt320 S1:3:respawn:/sbin/mgetty -x0 -D ttyS1 .fi .sp .RE .\"}}} .\"{{{ Files .SH FILES /etc/inittab .\"}}} .\"{{{ Author .SH AUTHOR \fBInit\fP was written by Miquel van Smoorenburg (miquels@cistron.nl). This manual page was written by Sebastian Lederer (lederer@francium.informatik.uni-bonn.de) and modified by Michael Haardt (u31b3hs@pool.informatik.rwth-aachen.de). .\"}}} .\"{{{ See also .SH "SEE ALSO" .BR init (8), .BR telinit (8) .\"}}} sysvinit-2.90/man/last.10000644017777601777760000001022213303017401014742 0ustar nobodynogroup'\" -*- coding: UTF-8 -*- .\" Copyright (C) 1998-2004 Miquel van Smoorenburg. .\" .\" This program is free software; you can redistribute it and/or modify .\" it under the terms of the GNU General Public License as published by .\" the Free Software Foundation; either version 2 of the License, or .\" (at your option) any later version. .\" .\" This program is distributed in the hope that it will be useful, .\" but WITHOUT ANY WARRANTY; without even the implied warranty of .\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the .\" GNU General Public License for more details. .\" .\" You should have received a copy of the GNU General Public License .\" along with this program; if not, write to the Free Software .\" Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA .\" .\"{{{}}} .\"{{{ Title .TH LAST,LASTB 1 "Jul 31, 2004" "" "Linux System Administrator's Manual" .\"}}} .\"{{{ Name .SH NAME last, lastb \- show listing of last logged in users .\"}}} .\"{{{ Synopsis .SH SYNOPSIS .B last .RB [ \-R ] .RB [ \-\fInum\fP ] .RB "[ \-\fBn\fP \fInum\fP ]" .RB [ \-adFiowx ] .RB "[ \-\fBf\fP \fIfile\fP ]" .RB "[ \-\fBt\fP \fIYYYYMMDDHHMMSS\fP ]" .RI [ name... ] .RI [ tty... ] .br .B lastb .RB [ \-R ] .RB [ \-\fInum\fP ] .RB "[ \-\fBn\fP \fInum\fP ]" .RB "[ \-\fBf\fP \fIfile\fP ]" .RB [ \-adFiowx ] .RI [ name... ] .RI [ tty... ] .\"}}} .\"{{{ Description .SH DESCRIPTION .B Last searches back through the file \fB/var/log/wtmp\fP (or the file designated by the \fB\-f\fP flag) and displays a list of all users logged in (and out) since that file was created. Names of users and tty's can be given, in which case \fBlast\fP will show only those entries matching the arguments. Names of ttys can be abbreviated, thus \fBlast 0\fP is the same as \fBlast tty0\fP. .PP When \fBlast\fP catches a \s-2SIGINT\s0 signal (generated by the interrupt key, usually control-C) or a \s-2SIGQUIT\s0 signal (generated by the quit key, usually control-\e), \fBlast\fP will show how far it has searched through the file; in the case of the \s-2SIGINT\s0 signal \fBlast\fP will then terminate. .PP The pseudo user \fBreboot\fP logs in each time the system is rebooted. Thus \fBlast reboot\fP will show a log of all reboots since the log file was created. .PP \fBLastb\fP is the same as \fBlast\fP, except that by default it shows a log of the file \fB/var/log/btmp\fP, which contains all the bad login attempts. .\"}}} .\"{{{ Options .SH OPTIONS .IP "\fB\-f\fP \fIfile\fP" Tells \fBlast\fP to use a specific file instead of \fB/var/log/wtmp\fP. .IP \fB\-\fP\fInum\fP This is a count telling \fBlast\fP how many lines to show. .IP "\fB\-n\fP \fInum\fP" The same. .IP "\fB\-t\fP \fIYYYYMMDDHHMMSS\fP" Display the state of logins as of the specified time. This is useful, e.g., to determine easily who was logged in at a particular time -- specify that time with \fB\-t\fP and look for "still logged in". .IP \fB\-R\fP Suppresses the display of the hostname field. .IP \fB\-a\fP Display the hostname in the last column. Useful in combination with the next flag. .IP \fB\-d\fP For non-local logins, Linux stores not only the host name of the remote host but its IP number as well. This option translates the IP number back into a hostname. .IP \fB\-F\fP Print full login and logout times and dates. .IP \fB\-i\fP This option is like \fB-d\fP in that it displays the IP number of the remote host, but it displays the IP number in numbers-and-dots notation. .IP \fB\-o\fP Read an old-type wtmp file (written by linux-libc5 applications). .IP \fB\-w\fP Display full user and domain names in the output. .IP \fB\-x\fP Display the system shutdown entries and run level changes. .\"}}} .SH NOTES The files \fIwtmp\fP and \fIbtmp\fP might not be found. The system only logs information in these files if they are present. This is a local configuration issue. If you want the files to be used, they can be created with a simple \fBtouch\fP(1) command (for example, \fItouch /var/log/wtmp\fP). .\"{{{ Files .SH FILES /var/log/wtmp .br /var/log/btmp .\"}}} .\"{{{ Author .SH AUTHOR Miquel van Smoorenburg, miquels@cistron.nl .\"}}} .\"{{{ See also .SH "SEE ALSO" .BR shutdown (8), .BR login (1), .BR init (8) .\"}}} sysvinit-2.90/man/runlevel.80000644017777601777760000000352013303017401015645 0ustar nobodynogroup'\" -*- coding: UTF-8 -*- .\" Copyright (C) 1997 Miquel van Smoorenburg. .\" .\" This program is free software; you can redistribute it and/or modify .\" it under the terms of the GNU General Public License as published by .\" the Free Software Foundation; either version 2 of the License, or .\" (at your option) any later version. .\" .\" This program is distributed in the hope that it will be useful, .\" but WITHOUT ANY WARRANTY; without even the implied warranty of .\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the .\" GNU General Public License for more details. .\" .\" You should have received a copy of the GNU General Public License .\" along with this program; if not, write to the Free Software .\" Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA .\" .TH RUNLEVEL 8 "May 27, 1997" "" "Linux System Administrator's Manual" .SH NAME runlevel -- find the previous and current system runlevel. .SH SYNOPSIS .B runlevel .RI [ utmp ] .SH DESCRIPTION .B Runlevel reads the system .I utmp file (typically .IR /var/run/utmp ) to locate the runlevel record, and then prints the previous and current system runlevel on its standard output, separated by a single space. If there is no previous system runlevel, the letter \fBN\fP will be printed instead. .PP If no .I utmp file exists, or if no runlevel record can be found, .B runlevel prints the word \fBunknown\fP and exits with an error. .PP .B Runlevel can be used in \fIrc\fP scripts as a substitute for the System-V \fBwho -r\fP command. However, in newer versions of \fBinit\fP(8) this information is also available in the environment variables \fBRUNLEVEL\fP and \fBPREVLEVEL\fP. .SH OPTIONS .\"{{{ utmp .IP \fIutmp\fP The name of the \fIutmp\fP file to read. .\"}}} .SH SEE ALSO .BR init (8), .BR utmp (5) .SH AUTHOR Miquel van Smoorenburg, miquels@cistron.nl sysvinit-2.90/man/poweroff.80000644017777601777760000000002013303017401015630 0ustar nobodynogroup.so man8/halt.8 sysvinit-2.90/man/halt.80000644017777601777760000000755013303017401014750 0ustar nobodynogroup'\" -*- coding: UTF-8 -*- .\" Copyright (C) 1998-2001 Miquel van Smoorenburg. .\" .\" This program is free software; you can redistribute it and/or modify .\" it under the terms of the GNU General Public License as published by .\" the Free Software Foundation; either version 2 of the License, or .\" (at your option) any later version. .\" .\" This program is distributed in the hope that it will be useful, .\" but WITHOUT ANY WARRANTY; without even the implied warranty of .\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the .\" GNU General Public License for more details. .\" .\" You should have received a copy of the GNU General Public License .\" along with this program; if not, write to the Free Software .\" Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA .\" .\"{{{}}} .\"{{{ Title .TH HALT 8 "Nov 6, 2001" "" "Linux System Administrator's Manual" .\"}}} .\"{{{ Name .SH NAME halt, reboot, poweroff \- stop the system. .\"}}} .\"{{{ Synopsis .SH SYNOPSIS .B /sbin/halt .RB [ \-n ] .RB [ \-w ] .RB [ \-d ] .RB [ \-f ] .RB [ \-i ] .RB [ \-p ] .RB [ \-h ] .br .B /sbin/reboot .RB [ \-n ] .RB [ \-w ] .RB [ \-d ] .RB [ \-f ] .RB [ \-i ] .br .B /sbin/poweroff .RB [ \-n ] .RB [ \-w ] .RB [ \-d ] .RB [ \-f ] .RB [ \-i ] .RB [ \-h ] .\"}}} .\"{{{ Description .SH DESCRIPTION \fBhalt\fP notes that the system is being brought down in the file \fI/var/log/wtmp\fP, and then either tells the kernel to halt, reboot or power-off the system. .PP If \fBhalt\fP or \fBreboot\fP is called when the system is \fInot\fP in runlevel \fB0\fP or \fB6\fP, in other words when it's running normally, \fBshutdown\fP will be invoked instead (with the \fB-h\fP or \fB-r\fP flag). For more info see the \fBshutdown\fP(8) manpage. .PP The rest of this manpage describes the behaviour in runlevels 0 and 6, that is when the systems shutdown scripts are being run. .\"}}} .\"{{{ Options .SH OPTIONS .IP \fB\-n\fP Don't sync before reboot or halt. Note that the kernel and storage drivers may still sync. This implies \fB\-d\fP. .IP \fB\-w\fP Don't actually reboot or halt but only write the wtmp record (in the \fI/var/log/wtmp\fP file). .IP \fB\-d\fP Don't write the wtmp record. .IP \fB\-f\fP Force halt or reboot, don't call \fBshutdown\fP(8). .IP \fB\-i\fP Shut down all network interfaces just before halt or reboot. .IP \fB\-h\fP Put all hard drives on the system in stand-by mode just before halt or power-off. .IP \fB\-p\fP When halting the system, switch off the power. This is the default when halt is called as \fBpoweroff\fP. .\"}}} .\"{{{ Diagnostics .SH DIAGNOSTICS If you're not the superuser, you will get the message `must be superuser'. .\"}}} .\"{{{ Notes .SH NOTES Under older \fBsysvinit\fP releases , \fBreboot\fP and \fBhalt\fP should never be called directly. From release 2.74 on \fBhalt\fP and \fBreboot\fP invoke \fBshutdown\fP(8) if the system is not in runlevel 0 or 6. This means that if \fBhalt\fP or \fBreboot\fP cannot find out the current runlevel (for example, when \fI/var/run/utmp\fP hasn't been initialized correctly) \fBshutdown\fP will be called, which might not be what you want. Use the \fB-f\fP flag if you want to do a hard \fBhalt\fP or \fBreboot\fP. .PP The \fB-h\fP flag puts all hard disks in standby mode just before halt or power-off. Right now this is only implemented for IDE drives. A side effect of putting the drive in stand-by mode is that the write cache on the disk is flushed. This is important for IDE drives, since the kernel doesn't flush the write cache itself before power-off. .PP The \fBhalt\fP program uses /proc/ide/hd* to find all IDE disk devices, which means that \fI/proc\fP needs to be mounted when \fBhalt\fP or \fBpoweroff\fP is called or the \fB-h\fP switch will do nothing. .PP .\"}}} .\"{{{ Author .SH AUTHOR Miquel van Smoorenburg, miquels@cistron.nl .\"}}} .\"{{{ See also .SH "SEE ALSO" .BR shutdown (8), .BR init (8) .\"}}} sysvinit-2.90/man/shutdown.80000644017777601777760000001752113303017401015672 0ustar nobodynogroup'\" -*- coding: UTF-8 -*- .\" Copyright (C) 1998-2003 Miquel van Smoorenburg. .\" .\" This program is free software; you can redistribute it and/or modify .\" it under the terms of the GNU General Public License as published by .\" the Free Software Foundation; either version 2 of the License, or .\" (at your option) any later version. .\" .\" This program is distributed in the hope that it will be useful, .\" but WITHOUT ANY WARRANTY; without even the implied warranty of .\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the .\" GNU General Public License for more details. .\" .\" You should have received a copy of the GNU General Public License .\" along with this program; if not, write to the Free Software .\" Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA .\" .\"{{{}}} .\"{{{ Title .TH SHUTDOWN 8 "November 12, 2003" "" "Linux System Administrator's Manual" .\"}}} .\"{{{ Name .SH NAME shutdown \- bring the system down .\"}}} .\"{{{ Synopsis .SH SYNOPSIS .B /sbin/shutdown .RB [ \-akrhPHfFnc ] .RB [ \-t .IR sec ] .I time .RI [ "warning message" ] .\"}}} .\"{{{ Description .SH DESCRIPTION \fBshutdown\fP brings the system down in a secure way. All logged-in users are notified that the system is going down, and \fBlogin\fP(1) is blocked. It is possible to shut the system down immediately or after a specified delay. All processes are first notified that the system is going down by the signal \s-2SIGTERM\s0. This gives programs like \fBvi\fP(1) the time to save the file being edited, mail and news processing programs a chance to exit cleanly, etc. \fBshutdown\fP does its job by signalling the \fBinit\fP process, asking it to change the runlevel. Runlevel \fB0\fP is used to halt the system, runlevel \fB6\fP is used to reboot the system, and runlevel \fB1\fP is used to put to system into a state where administrative tasks can be performed; this is the default if neither the \fI-h\fP or \fI-r\fP flag is given to \fBshutdown\fP. To see which actions are taken on halt or reboot see the appropriate entries for these runlevels in the file \fI/etc/inittab\fP. .\"}}} .\"{{{ Options .SH OPTIONS .\"{{{ -a .IP "\fB\-a\fP Use \fB/etc/shutdown.allow\fP. .\"}}} .\"{{{ -k .IP \fB\-k\fP Don't really shutdown; only send the warning messages to everybody. .\"}}} .\"{{{ -r .IP \fB\-r\fP Reboot after shutdown. .\"}}} .\"{{{ -h .IP \fB\-h\fP Halt or power off after shutdown. .\"}}} .\"{{{ -P .IP \fB\-P\fP Halt action is to turn off the power. .\"}}} .\"{{{ -H .IP \fB\-H\fP Modifier to the -h flag. Halt action is to halt or drop into boot monitor on systems that support it. Must be used with the -h flag. .\"}}} .\"{{{ -f .IP \fB\-f\fP Skip fsck on reboot. .\"}}} .\"{{{ -F .IP \fB\-F\fP Force fsck on reboot. .\"}}} .\"{{{ -n .IP \fB\-n\fP [DEPRECATED] Don't call \fBinit\fP(8) to do the shutdown but do it ourself. The use of this option is discouraged, and its results are not always what you'd expect. .\"}}} .\"{{{ -c .IP \fB\-c\fP Cancel a waiting shutdown. ("shutdown now" is no longer waiting.) With this option it is of course not possible to give the time argument, but you can enter explanatory message arguments on the command line that will be sent to all users. .\"}}} .\"{{{ -t sec .IP "\fB\-t\fP \fIsec\fP" Tell \fBinit\fP(8) to wait \fIsec\fP seconds between sending processes the warning and the kill signal, before changing to another runlevel. .\"}}} .\"{{{ time .IP \fItime\fP When to shutdown. .\"}}} .\"{{{ warning-message .IP "\fIwarning message\fP" Message to send to all users. .\"}}} .PP The \fItime\fP argument can have different formats. First, it can be an absolute time in the format \fIhh:mm\fP, in which \fIhh\fP is the hour (1 or 2 digits) and \fImm\fP is the minute of the hour (in two digits). Second, it can be in the format \fB+\fP\fIm\fP, in which \fIm\fP is the number of minutes to wait. The word \fBnow\fP is an alias for \fB+0\fP. .PP If shutdown is called with a delay, it will create the advisory file .I /etc/nologin which causes programs such as \fIlogin(1)\fP to not allow new user logins. This file is created five minutes before the shutdown sequence starts. Shutdown removes this file if it is stopped before it can signal init (i.e. it is cancelled or something goes wrong). It also removes it before calling init to change the runlevel. .PP The \fB\-f\fP flag means `reboot fast'. This only creates an advisory file \fI/fastboot\fP which can be tested by the system when it comes up again. The boot rc file can test if this file is present, and decide not to run \fBfsck\fP(1) since the system has been shut down in the proper way. After that, the boot process should remove \fI/fastboot\fP. .PP The \fB\-F\fP flag means `force fsck'. This only creates an advisory file \fI/forcefsck\fP which can be tested by the system when it comes up again. The boot rc file can test if this file is present, and decide to run \fBfsck\fP(1) with a special `force' flag so that even properly unmounted file systems get checked. After that, the boot process should remove \fI/forcefsck\fP. .PP The \fB-n\fP flag causes \fBshutdown\fP not to call \fBinit\fP, but to kill all running processes itself. \fBshutdown\fP will then turn off quota, accounting, and swapping and unmount all file systems. .\"}}} .\"{{{ Files .SH ACCESS CONTROL \fBshutdown\fP can be called from \fBinit\fP(8) when the magic keys \fBCTRL-ALT-DEL\fP are pressed, by creating an appropriate entry in \fI/etc/inittab\fP. This means that everyone who has physical access to the console keyboard can shut the system down. To prevent this, \fBshutdown\fP can check to see if an authorized user is logged in on one of the virtual consoles. If \fBshutdown\fP is called with the \fB-a\fP argument (add this to the invocation of shutdown in /etc/inittab), it checks to see if the file \fI/etc/shutdown.allow\fP is present. It then compares the login names in that file with the list of people that are logged in on a virtual console (from \fI/var/run/utmp\fP). Only if one of those authorized users \fBor root\fP is logged in, it will proceed. Otherwise it will write the message .sp 1 .nf \fBshutdown: no authorized users logged in\fP .fi .sp 1 to the (physical) system console. The format of \fI/etc/shutdown.allow\fP is one user name per line. Empty lines and comment lines (prefixed by a \fB#\fP) are allowed. Currently there is a limit of 32 users in this file. .sp 1 Note that if \fI/etc/shutdown.allow\fP is not present, the \fB-a\fP argument is ignored. .SH HALT OR POWEROFF The \fB-H\fP option just sets the \fIinit\fP environment variable \fIINIT_HALT\fP to \fIHALT\fP, and the \fB-P\fP option just sets that variable to \fIPOWEROFF\fP. The shutdown script that calls \fBhalt\fP(8) as the last thing in the shutdown sequence should check these environment variables and call \fBhalt\fP(8) with the right options for these options to actually have any effect. Debian 3.1 (sarge) supports this. .SH FILES .nf /fastboot /etc/inittab /etc/init.d/halt /etc/init.d/reboot /etc/shutdown.allow .fi .\"}}} .SH NOTES A lot of users forget to give the \fItime\fP argument and are then puzzled by the error message \fBshutdown\fP produces. The \fItime\fP argument is mandatory; in 90 percent of all cases this argument will be the word \fBnow\fP. .PP Init can only capture CTRL-ALT-DEL and start shutdown in console mode. If the system is running the X window System, the X server processes all key strokes. Some X11 environments make it possible to capture CTRL-ALT-DEL, but what exactly is done with that event depends on that environment. .PP Shutdown wasn't designed to be run setuid. /etc/shutdown.allow is not used to find out who is executing shutdown, it ONLY checks who is currently logged in on (one of the) console(s). .\"{{{ Author .SH AUTHOR Miquel van Smoorenburg, miquels@cistron.nl .\"}}} .\"{{{ See also .SH "SEE ALSO" .BR fsck (8), .BR init (8), .BR halt (8), .BR poweroff (8), .BR reboot (8) .\"}}} sysvinit-2.90/man/pidof.80000644017777601777760000000623613303017401015121 0ustar nobodynogroup'\" -*- coding: UTF-8 -*- .\" Copyright (C) 1998 Miquel van Smoorenburg. .\" .\" This program is free software; you can redistribute it and/or modify .\" it under the terms of the GNU General Public License as published by .\" the Free Software Foundation; either version 2 of the License, or .\" (at your option) any later version. .\" .\" This program is distributed in the hope that it will be useful, .\" but WITHOUT ANY WARRANTY; without even the implied warranty of .\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the .\" GNU General Public License for more details. .\" .\" You should have received a copy of the GNU General Public License .\" along with this program; if not, write to the Free Software .\" Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA .\" .TH PIDOF 8 "01 Sep 1998" "" "Linux System Administrator's Manual" .SH NAME pidof -- find the process ID of a running program. .SH SYNOPSIS .B pidof .RB [ \-s ] .RB [ \-c ] .RB [ \-n ] .RB [ \-x ] .RB [ \-o .IR omitpid[,omitpid..] ] .RB [ \-o .IR omitpid[,omitpid..].. ] .B program .RB [ program.. ] .SH DESCRIPTION .B Pidof finds the process id's (pids) of the named programs. It prints those id's on the standard output. This program is on some systems used in run-level change scripts, especially when the system has a \fISystem-V\fP like \fIrc\fP structure. In that case these scripts are located in /etc/rc?.d, where ? is the runlevel. If the system has a .B start-stop-daemon (8) program that should be used instead. .SH OPTIONS .IP \-s Single shot - this instructs the program to only return one \fIpid\fP. .IP \-c Only return process ids that are running with the same root directory. This option is ignored for non-root users, as they will be unable to check the current root directory of processes they do not own. .IP \-n Avoid .BR stat (2) system function call on all binaries which are located on network based file systems like .BR NFS . Instead of using this option the the variable .B PIDOF_NETFS may be set and exported. .IP \-x Scripts too - this causes the program to also return process id's of shells running the named scripts. .IP "-o \fIomitpid\fP" Tells \fIpidof\fP to omit processes with that process id. The special pid \fB%PPID\fP can be used to name the parent process of the \fIpidof\fP program, in other words the calling shell or shell script. .SH "EXIT STATUS" .TP .B 0 At least one program was found with the requested name. .TP .B 1 No program was found with the requested name. .SH NOTES \fIpidof\fP is actually the same program as \fIkillall5\fP; the program behaves according to the name under which it is called. .PP When \fIpidof\fP is invoked with a full pathname to the program it should find the pid of, it is reasonably safe. Otherwise it is possible that it returns pids of running programs that happen to have the same name as the program you're after but are actually other programs. Note that that the executable name of running processes is calculated with .BR readlink (2), so symbolic links to executables will also match. .SH SEE ALSO .BR shutdown (8), .BR init (8), .BR halt (8), .BR reboot (8), .BR killall5 (8) .SH AUTHOR Miquel van Smoorenburg, miquels@cistron.nl sysvinit-2.90/man/fstab-decode.80000644017777601777760000000272413303017401016336 0ustar nobodynogroup'\" -*- coding: UTF-8 -*- .\" A man page for fstab-decode(8). .\" .\" Copyright (C) 2006 Red Hat, Inc. All rights reserved. .\" .\" This copyrighted material is made available to anyone wishing to use, .\" modify, copy, or redistribute it subject to the terms and conditions of the .\" GNU General Public License v.2. .\" .\" This program is distributed in the hope that it will be useful, but WITHOUT .\" ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or .\" FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for .\" more details. .\" .\" You should have received a copy of the GNU General Public License along .\" with this program; if not, write to the Free Software Foundation, Inc., .\" 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. .\" .\" Author: Miloslav Trmac .TH fstab-decode 8 "May 2006" .SH NAME fstab-decode \- run a command with fstab-encoded arguments .SH SYNOPSIS \fBfstab-decode\fR \fICOMMAND\fR [\fIARGUMENT\fR]... .SH DESCRIPTION .B fstab-decode decodes escapes in the specified \fIARGUMENT\fRs and uses them to run \fICOMMAND\fR. The argument escaping uses the same rules as path escaping in \fB/etc/fstab\fR, .B /etc/mtab and \fB/proc/mtab\fR. .SH EXIT STATUS .B fstab-decode exits with status 127 if .I COMMAND can't be run. Otherwise it exits with the status returned by \fICOMMAND\fR. .SH EXAMPLES .nf .B fstab-decode umount $(awk \[aq]$3 == \[dq]vfat\[dq] { print $2 }\[aq] /etc/fstab) .fi sysvinit-2.90/man/init.80000644017777601777760000003160613303017401014762 0ustar nobodynogroup'\" -*- coding: UTF-8 -*- .\" Copyright (C) 1998-2004 Miquel van Smoorenburg. .\" .\" This program is free software; you can redistribute it and/or modify .\" it under the terms of the GNU General Public License as published by .\" the Free Software Foundation; either version 2 of the License, or .\" (at your option) any later version. .\" .\" This program is distributed in the hope that it will be useful, .\" but WITHOUT ANY WARRANTY; without even the implied warranty of .\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the .\" GNU General Public License for more details. .\" .\" You should have received a copy of the GNU General Public License .\" along with this program; if not, write to the Free Software .\" Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA .\" .\"{{{}}} .\"{{{ Title .TH INIT 8 "29 Jul 2004" "" "Linux System Administrator's Manual" .\"}}} .\"{{{ Name .SH NAME init, telinit \- process control initialization .\"}}} .\"{{{ Synopsis .SH SYNOPSIS .B /sbin/init .RB [ " -a " ] .RB [ " -s " ] .RB [ " -b " ] [ \fB\-z\fP \fIxxx\fP ] .RB [ " 0123456Ss " ] .br .B /sbin/telinit [ \fB\-t\fP \fISECONDS\fP ] .RB [ " 0123456sSQqabcUu " ] .br .B /sbin/telinit [ \fB\-e\fP \fIVAR\fP[\fB=\fP\fIVAL\fP] ] .\"}}} .\"{{{ Description .SH DESCRIPTION .\"{{{ init .SS Init .B Init is the parent of all processes. Its primary role is to create processes from a script stored in the file \fB/etc/inittab\fP (see \fIinittab\fP(5)). This file usually has entries which cause \fBinit\fP to spawn \fBgetty\fPs on each line that users can log in. It also controls autonomous processes required by any particular system. .PP .\"{{{ Runlevels .SH RUNLEVELS A \fIrunlevel\fP is a software configuration of the system which allows only a selected group of processes to exist. The processes spawned by \fBinit\fP for each of these runlevels are defined in the \fB/etc/inittab\fP file. \fBInit\fP can be in one of eight runlevels: \fB0\(en6\fP and \fBS\fP (a.k.a. \fBs\fP). The runlevel is changed by having a privileged user run \fBtelinit\fP, which sends appropriate signals to \fBinit\fP, telling it which runlevel to change to. .PP Runlevels \fBS\fP, \fB0\fP, \fB1\fP, and \fB6\fP are reserved. Runlevel S is used to initialize the system on boot. When starting runlevel S (on boot) or runlevel 1 (switching from a multi-user runlevel) the system is entering ``single-user mode'', after which the current runlevel is S. Runlevel 0 is used to halt the system; runlevel 6 is used to reboot the system. .PP After booting through S the system automatically enters one of the multi-user runlevels 2 through 5, unless there was some problem that needs to be fixed by the administrator in single-user mode. Normally after entering single-user mode the administrator performs maintenance and then reboots the system. .PP For more information, see the manpages for \fBshutdown\fP(8) and \fBinittab\fP(5). .PP Runlevels 7-9 are also valid, though not really documented. This is because "traditional" Unix variants don't use them. .PP Runlevels \fIS\fP and \fIs\fP are the same. Internally they are aliases for the same runlevel. .\"}}} .PP .SH BOOTING After \fBinit\fP is invoked as the last step of the kernel boot sequence, it looks for the file \fB/etc/inittab\fP to see if there is an entry of the type \fBinitdefault\fP (see \fIinittab\fP(5)). The \fBinitdefault\fP entry determines the initial runlevel of the system. If there is no such entry (or no \fB/etc/inittab\fP at all), a runlevel must be entered at the system console. .PP Runlevel \fBS\fP or \fBs\fP initialize the system and do not require an \fB/etc/inittab\fP file. .PP In single user mode, \fB/sbin/sulogin\fP is invoked on \fB/dev/console\fP. .PP When entering single user mode, \fBinit\fP initializes the consoles \fBstty\fP settings to sane values. Clocal mode is set. Hardware speed and handshaking are not changed. .PP When entering a multi-user mode for the first time, \fBinit\fP performs the \fBboot\fP and \fBbootwait\fP entries to allow file systems to be mounted before users can log in. Then all entries matching the runlevel are processed. .PP When starting a new process, \fBinit\fP first checks whether the file \fI/etc/initscript\fP exists. If it does, it uses this script to start the process. .PP Each time a child terminates, \fBinit\fP records the fact and the reason it died in \fB/var/run/utmp\fP and \fB/var/log/wtmp\fP, provided that these files exist. .SH CHANGING RUNLEVELS After it has spawned all of the processes specified, \fBinit\fP waits for one of its descendant processes to die, a powerfail signal, or until it is signaled by \fBtelinit\fP to change the system's runlevel. When one of the above three conditions occurs, it re-examines the \fB/etc/inittab\fP file. New entries can be added to this file at any time. However, \fBinit\fP still waits for one of the above three conditions to occur. To provide for an instantaneous response, the \fBtelinit Q\fP or \fBq\fP command can wake up \fBinit\fP to re-examine (reload) the \fB/etc/inittab\fP file. .PP If \fBinit\fP is not in single user mode and receives a powerfail signal (SIGPWR), it reads the file \fB/etc/powerstatus\fP. It then starts a command based on the contents of this file: .IP F(AIL) Power is failing, UPS is providing the power. Execute the \fBpowerwait\fP and \fBpowerfail\fP entries. .IP O(K) The power has been restored, execute the \fBpowerokwait\fP entries. .IP L(OW) The power is failing and the UPS has a low battery. Execute the \fBpowerfailnow\fP entries. .PP If /etc/powerstatus doesn't exist or contains anything else then the letters \fBF\fP, \fBO\fP or \fBL\fP, init will behave as if it has read the letter \fBF\fP. .PP Usage of \fBSIGPWR\fP and \fB/etc/powerstatus\fP is discouraged. Someone wanting to interact with \fBinit\fP should use the \fB/run/initctl\fP control channel - see the initctl manual page for more documentation about this. .PP When \fBinit\fP is requested to change the runlevel, it sends the warning signal \s-1\fBSIGTERM\fP\s0 to all processes that are undefined in the new runlevel. It then waits 5 seconds before forcibly terminating these processes via the \s-1\fBSIGKILL\fP\s0 signal. Note that \fBinit\fP assumes that all these processes (and their descendants) remain in the same process group which \fBinit\fP originally created for them. If any process changes its process group affiliation it will not receive these signals. Such processes need to be terminated separately. .\"}}} .\"{{{ telinit .SH TELINIT \fB/sbin/telinit\fP is linked to \fB/sbin/init\fP. It takes a one-character argument and signals \fBinit\fP to perform the appropriate action. The following arguments serve as directives to \fBtelinit\fP: .IP "\fB0\fP,\fB1\fP,\fB2\fP,\fB3\fP,\fB4\fP,\fB5\fP or \fB6\fP" tell \fBinit\fP to switch to the specified run level. .IP \fBa\fP,\fBb\fP,\fBc\fP tell \fBinit\fP to process only those \fB/etc/inittab\fP file entries having runlevel \fBa\fP,\fBb\fP or \fBc\fP. .IP "\fBQ\fP or \fBq\fP" tell \fBinit\fP to re-examine the \fB/etc/inittab\fP file. .IP "\fBS\fP or \fBs\fP" tell \fBinit\fP to switch to single user mode. .IP "\fBU\fP or \fBu\fP" tell \fBinit\fP to re-execute itself (preserving the state). No re-examining of \fB/etc/inittab\fP file happens. Run level should be one of \fBSs0123456\fP otherwise request would be silently ignored. .PP \fBtelinit\fP can tell \fBinit\fP how long it should wait between sending processes the SIGTERM and SIGKILL signals. The default is 5 seconds, but this can be changed with the \fB-t\fP option. .PP \fBtelinit -e\fP tells \fBinit\fP to change the environment for processes it spawns. The argument of \fB-e\fP is either of the form \fIVAR\fP=\fIVAL\fP which sets variable \fIVAR\fP to value \fIVAL\fP, or of the form \fIVAR\fP (without an equality sign) which unsets variable \fIVAR\fP. .PP \fBtelinit\fP can be invoked only by users with appropriate privileges. .PP The \fBinit\fP binary checks if it is \fBinit\fP or \fBtelinit\fP by looking at its \fIprocess id\fP; the real \fBinit\fP's process id is always \fB1\fP. From this it follows that instead of calling \fBtelinit\fP one can also just use \fBinit\fP instead as a shortcut. .\"}}} .\"}}} .SH ENVIRONMENT \fBInit\fP sets the following environment variables for all its children: .IP \fBPATH\fP \fI/bin:/usr/bin:/sbin:/usr/sbin\fP .IP \fBINIT_VERSION\fP As the name says. Useful to determine if a script runs directly from \fBinit\fP. .IP \fBRUNLEVEL\fP The current system runlevel. .IP \fBPREVLEVEL\fP The previous runlevel (useful after a runlevel switch). .IP \fBCONSOLE\fP The system console. This is really inherited from the kernel; however if it is not set \fBinit\fP will set it to \fB/dev/console\fP by default. .SH BOOTFLAGS It is possible to pass a number of flags to \fBinit\fP from the boot monitor (eg. LILO). \fBInit\fP accepts the following flags: .TP 0.5i .B -s, S, single Single user mode boot. In this mode \fI/etc/inittab\fP is examined and the bootup rc scripts are usually run before the single user mode shell is started. .PP .TP 0.5i .B 1-5 Runlevel to boot into. .PP .TP 0.5i .B -b, emergency Boot directly into a single user shell without running any other startup scripts. .PP .TP 0.5i .B -a, auto The LILO boot loader adds the word "auto" to the command line if it booted the kernel with the default command line (without user intervention). If this is found \fBinit\fP sets the "AUTOBOOT" environment variable to "yes". Note that you cannot use this for any security measures - of course the user could specify "auto" or \-a on the command line manually. .PP .TP 0.5i .BI "-z " xxx The argument to \fB-z\fP is ignored. You can use this to expand the command line a bit, so that it takes some more space on the stack. \fBInit\fP can then manipulate the command line so that \fBps\fP(1) shows the current runlevel. .PP .SH INTERFACE Init listens on a \fIfifo\fP in /dev, \fI/run/initctl\fP, for messages. \fBTelinit\fP uses this to communicate with init. The interface is not very well documented or finished. Those interested should study the \fIinitreq.h\fP file in the \fIsrc/\fP subdirectory of the \fBinit\fP source code tar archive. .SH SIGNALS Init reacts to several signals: .TP 0.5i .B SIGHUP Has the same effect as \fBtelinit q\fP. .PP .TP 0.5i .B SIGUSR1 On receipt of this signals, init closes and re-opens its control fifo, \fB/run/initctl\fP. Useful for bootscripts when /dev is remounted. .TP 0.5i .B SIGUSR2 When init receives SIGUSR2, init closes and leaves the control fifo, \fB/run/initctl\f\P, closed. This may be used to make sure init is not holding open any files. However, it also prevents init from switching runlevels. Which means commands like shutdown no longer work. The fifo can be re-opened by sending init the SIGUSR1 signal. .TP 0.5i .B SIGINT Normally the kernel sends this signal to init when CTRL-ALT-DEL is pressed. It activates the \fIctrlaltdel\fP action. .TP 0.5i .B SIGWINCH The kernel sends this signal when the \fIKeyboardSignal\fP key is hit. It activates the \fIkbrequest\fP action. \"{{{ Conforming to .SH CONFORMING TO \fBInit\fP is compatible with the System V init. It works closely together with the scripts in the directories \fI/etc/init.d\fP and \fI/etc/rc{runlevel}.d\fP. If your system uses this convention, there should be a \fIREADME\fP file in the directory \fI/etc/init.d\fP explaining how these scripts work. .\"}}} .\"{{{ Files .SH FILES .nf /etc/inittab /etc/initscript /dev/console /var/run/utmp /var/log/wtmp /run/initctl .fi .\"}}} .\"{{{ Warnings .SH WARNINGS \fBInit\fP assumes that processes and descendants of processes remain in the same process group which was originally created for them. If the processes change their group, \fBinit\fP can't kill them and you may end up with two processes reading from one terminal line. .PP On a Debian system, entering runlevel 1 causes all processes to be killed except for kernel threads and the script that does the killing and other processes in its session. As a consequence of this, it isn't safe to return from runlevel 1 to a multi-user runlevel: daemons that were started in runlevel S and are needed for normal operation are no longer running. The system should be rebooted. .\"}}} .\"{{{ Diagnostics .SH DIAGNOSTICS If \fBinit\fP finds that it is continuously respawning an entry more than 10 times in 2 minutes, it will assume that there is an error in the command string, generate an error message on the system console, and refuse to respawn this entry until either 5 minutes has elapsed or it receives a signal. This prevents it from eating up system resources when someone makes a typographical error in the \fB/etc/inittab\fP file or the program for the entry is removed. .\"}}} .\"{{{ Author .SH AUTHOR Miquel van Smoorenburg (miquels@cistron.nl), initial manual page by Michael Haardt (u31b3hs@pool.informatik.rwth-aachen.de). .\"}}} .\"{{{ See also .SH "SEE ALSO" .BR getty (1), .BR login (1), .BR sh (1), .BR runlevel (8), .BR shutdown(8), .BR kill (1), .BR initctl (5), .BR inittab (5), .BR initscript (5), .BR utmp (5) .\"}}} sysvinit-2.90/man/sulogin.80000644017777601777760000000635513303017401015502 0ustar nobodynogroup'\" -*- coding: UTF-8 -*- .\" Copyright (C) 1998-2006 Miquel van Smoorenburg. .\" .\" This program is free software; you can redistribute it and/or modify .\" it under the terms of the GNU General Public License as published by .\" the Free Software Foundation; either version 2 of the License, or .\" (at your option) any later version. .\" .\" This program is distributed in the hope that it will be useful, .\" but WITHOUT ANY WARRANTY; without even the implied warranty of .\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the .\" GNU General Public License for more details. .\" .\" You should have received a copy of the GNU General Public License .\" along with this program; if not, write to the Free Software .\" Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA .\" .TH SULOGIN 8 "17 Jan 2006" "" "Linux System Administrator's Manual" .SH NAME sulogin \- Single-user login .SH SYNOPSIS .B sulogin [ \fB\-e\fP ] [ \fB\-p\fP ] [ \fB\-t\fP \fISECONDS\fP ] [ \fITTY\fP ] .SH DESCRIPTION .I sulogin is invoked by \fBinit(8)\fP when the system goes into single user mode. (This is done through an entry in \fIinittab(5)\fP.) \fBInit\fP also tries to execute \fIsulogin\fP when the boot loader (e.g., \fBgrub\fP(8)) passes it the \fB\-b\fP option. .PP The user is prompted .IP "" .5i Give root password for system maintenance .br (or type Control\-D for normal startup): .PP \fIsulogin\fP will be connected to the current terminal, or to the optional device that can be specified on the command line (typically \fB/dev/console\fP). .PP If the \fB\-t\fP option is used then the program only waits the given number of seconds for user input. .PP If the \fB\-p\fP option is used then the single-user shell is invoked with a \fIdash\fP as the first character in \fIargv[0]\fP. This causes the shell process to behave as a login shell. The default is \fInot\fP to do this, so that the shell will \fInot\fP read \fB/etc/profile\fP or \fB$HOME/.profile\fP at startup. .PP After the user exits the single-user shell, or presses control\-D at the prompt, the system will (continue to) boot to the default runlevel. .SH ENVIRONMENT VARIABLES \fIsulogin\fP looks for the environment variable \fBSUSHELL\fP or \fBsushell\fP to determine what shell to start. If the environment variable is not set, it will try to execute root's shell from /etc/passwd. If that fails it will fall back to \fB/bin/sh\fP. .PP This is very valuable together with the \fB\-b\fP option to init. To boot the system into single user mode, with the root file system mounted read/write, using a special "fail safe" shell that is statically linked (this example is valid for the LILO bootprompt) .PP boot: linux \-b rw sushell=/sbin/sash .SH FALLBACK METHODS \fIsulogin\fP checks the root password using the standard method (getpwnam) first. Then, if the \fB\-e\fP option was specified, \fIsulogin\fP examines these files directly to find the root password: .PP /etc/passwd, .br /etc/shadow (if present) .PP If they are damaged or nonexistent, sulogin will start a root shell without asking for a password. Only use the \fB\-e\fP option if you are sure the console is physically protected against unauthorized access. .SH AUTHOR Miquel van Smoorenburg .SH SEE ALSO init(8), inittab(5). sysvinit-2.90/man/bootlogd.8.todo0000644017777601777760000000366313303017401016576 0ustar nobodynogroup'\" -*- coding: UTF-8 -*- .\" Copyright (C) 1998-1999 Miquel van Smoorenburg. .\" .\" This program is free software; you can redistribute it and/or modify .\" it under the terms of the GNU General Public License as published by .\" the Free Software Foundation; either version 2 of the License, or .\" (at your option) any later version. .\" .\" This program is distributed in the hope that it will be useful, .\" but WITHOUT ANY WARRANTY; without even the implied warranty of .\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the .\" GNU General Public License for more details. .\" .\" You should have received a copy of the GNU General Public License .\" along with this program; if not, write to the Free Software .\" Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA .\" .TH BOOTLOGD 8 "Aug 24, 1999" "" "Linux System Administrator's Manual" .SH NAME bootlogd \- record boot messages .SH SYNOPSIS .B /sbin/bootlogd .RB [ \-d ] .RB [ \-r ] .RB [ \-v ] .RB [ " -l logfile " ] .RB [ " -p pidfile " ] .SH DESCRIPTION \fBBootlogd\fP runs in the background and copies all strings sent to the \fI/dev/console\fP device to a logfile. If the logfile is not accessible, the messages will be buffered in-memory until it is. .SH OPTIONS .IP \fB\-d\fP Do not fork and run in the background. .IP \fB\-r\fP If there is an existing logfile called \fIlogfile\fP rename it to \fIlogfile~\fP unless \fIlogfile~\fP already exists. .IP \fB\-v\fP Show version. .IP \fB\-l logfile\fP Log to this logfile. The default is \fI/var/log/boot.log\fP. .IP \fB\-p pidfile\fP Put process-id in this file. The default is no pidfile. .SH NOTES There is no standard way to find out the real console device if you have a new-style \fI/dev/console\fP device (major 5, minor 1). \fBBootlogd\fP might have some difficulties to do this, especially under very old or very new kernels. .SH AUTHOR Miquel van Smoorenburg, miquels@cistron.nl .SH "SEE ALSO" .BR dmesg (8) sysvinit-2.90/man/mountpoint.10000644017777601777760000000540713303017401016224 0ustar nobodynogroup'\" -*- coding: UTF-8 -*- .\" Copyright (C) 1998-2004 Miquel van Smoorenburg. .\" .\" This program is free software; you can redistribute it and/or modify .\" it under the terms of the GNU General Public License as published by .\" the Free Software Foundation; either version 2 of the License, or .\" (at your option) any later version. .\" .\" This program is distributed in the hope that it will be useful, .\" but WITHOUT ANY WARRANTY; without even the implied warranty of .\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the .\" GNU General Public License for more details. .\" .\" You should have received a copy of the GNU General Public License .\" along with this program; if not, write to the Free Software .\" Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA .\" .TH MOUNTPOINT 1 "Mar 15, 2004" "" "Linux System Administrator's Manual" .SH NAME mountpoint \- see if a directory is a mountpoint .SH SYNOPSIS .B /bin/mountpoint .RB [ \-q ] .RB [ \-d ] .I /path/to/directory .br .B /bin/mountpoint .RB \-x .I /dev/device .SH DESCRIPTION \fBMountpoint\fP checks if the directory is a mountpoint. .SH OPTIONS .IP \fB\-q\fP Be quiet - don't print anything. .IP \fB\-d\fP Print major/minor device number of the filesystem on stdout. .IP \fB\-p\fP Check Linux's /proc/mounts file to try to detect circular mount points. .IP \fB\-x\fP Print major/minor device number of the blockdevice on stdout. .SH EXIT STATUS Zero if the directory is a mountpoint, non-zero if not. .SH NOTES Symbolic links are not followed, except when the \fB-x\fP option is used. To force following symlinks, add a trailing slash to the path of the directory. .PP The name of the command is misleading when the -x option is used, but the option is useful for comparing if a directory and a device match up, and there is no other command that can print the info easily. .PP The mountpoint command fails when a directory is binded to one of its grandparents. For example, if /a/b/c/d is a mount point for /a/b then mountpoint will report /a/b/c/d is not a valid mount point. This is because both the original directory and its new mount point share the same inode and device number. .PP The circular mount problem can be worked around on Linux systems by using the -p flag to check the /proc/mounts file for references to the circular mount bind. When using the -p flag, make sure to specify the full path (ie /home/user/mp and not just mp). Also, mountpoint may still fail if there are spaces in the mount point's path, even when using the -p flag because of the way /proc/mounts mangles the spaces in the path name. Of course, if the admin is using circular mount points with spaces in the name, there are bigger concerns. .SH AUTHOR Miquel van Smoorenburg, miquels@cistron.nl .SH "SEE ALSO" .BR stat (1) sysvinit-2.90/man/killall5.80000644017777601777760000000351213303017401015523 0ustar nobodynogroup'\" -*- coding: UTF-8 -*- .\" Copyright (C) 1998-2003 Miquel van Smoorenburg. .\" .\" This program is free software; you can redistribute it and/or modify .\" it under the terms of the GNU General Public License as published by .\" the Free Software Foundation; either version 2 of the License, or .\" (at your option) any later version. .\" .\" This program is distributed in the hope that it will be useful, .\" but WITHOUT ANY WARRANTY; without even the implied warranty of .\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the .\" GNU General Public License for more details. .\" .\" You should have received a copy of the GNU General Public License .\" along with this program; if not, write to the Free Software .\" Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA .\" .TH KILLALL5 8 "04 Nov 2003" "" "Linux System Administrator's Manual" .SH NAME killall5 -- send a signal to all processes. .SH SYNOPSIS .B killall5 .RB -signalnumber .RB [ \-o .IR omitpid[,omitpid..]] .RB [ \-o .IR omitpid[,omitpid..].. ] .SH DESCRIPTION .B killall5 is the SystemV killall command. It sends a signal to all processes except kernel threads and the processes in its own session, so it won't kill the shell that is running the script it was called from. Its primary (only) use is in the \fBrc\fP scripts found in the /etc/init.d directory. .SH OPTIONS .IP "-o \fIomitpid\fP" Tells \fIkillall5\fP to omit processes with that process id. .SH NOTES \fIkillall5\fP can also be invoked as pidof, which is simply a (symbolic) link to the \fIkillall5\fP program. .SH EXIT STATUS The program return zero if it killed processes. It return 2 if no process were killed, and 1 if it was unable to find any processes (/proc/ is missing). .SH SEE ALSO .BR halt (8), .BR reboot (8), .BR pidof (8) .SH AUTHOR Miquel van Smoorenburg, miquels@cistron.nl sysvinit-2.90/man/telinit.80000644017777601777760000000002013303017401015451 0ustar nobodynogroup.so man8/init.8 sysvinit-2.90/man/initscript.50000644017777601777760000000457413303017401016210 0ustar nobodynogroup'\" -*- coding: UTF-8 -*- .\" Copyright (C) 1998-2003 Miquel van Smoorenburg. .\" .\" This program is free software; you can redistribute it and/or modify .\" it under the terms of the GNU General Public License as published by .\" the Free Software Foundation; either version 2 of the License, or .\" (at your option) any later version. .\" .\" This program is distributed in the hope that it will be useful, .\" but WITHOUT ANY WARRANTY; without even the implied warranty of .\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the .\" GNU General Public License for more details. .\" .\" You should have received a copy of the GNU General Public License .\" along with this program; if not, write to the Free Software .\" Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA .\" .TH INITSCRIPT 5 "July 10, 2003" "" "Linux System Administrator's Manual" .SH NAME initscript \- script that executes inittab commands. .SH SYNOPSIS /bin/sh /etc/initscript id runlevels action process .SH DESCRIPTION When the shell script \fI/etc/initscript\fP is present, \fBinit\fP will use it to execute the commands from \fIinittab\fP. This script can be used to set things like \fBulimit\fP and \fBumask\fP default values for every process. .SH EXAMPLES This is a sample initscript, which might be installed on your system as \fI/etc/initscript.sample\fP. .RS .sp .nf .ne 7 # # initscript Executed by init(8) for every program it # wants to spawn like this: # # /bin/sh /etc/initscript # # Set umask to safe level, and enable core dumps. umask 022 ulimit -c 2097151 PATH=/bin:/sbin:/usr/bin:/usr/sbin export PATH # Increase the hard file descriptor limit for all processes # to 8192. The soft limit is still 1024, but any unprivileged # process can increase its soft limit up to the hard limit # with "ulimit -Sn xxx" (needs a 2.2.13 or later Linux kernel). ulimit -Hn 8192 # Execute the program. eval exec "$4" .sp .RE .SH NOTES This script is not meant as startup script for daemons or somesuch. It has nothing to do with a \fIrc.local\fP style script. It's just a handler for things executed from \fB/etc/inittab\fP. Experimenting with this can make your system un(re)bootable. .RE .SH FILES /etc/inittab, /etc/initscript. .SH AUTHOR Miquel van Smoorenburg , .SH "SEE ALSO" init(8), inittab(5). sysvinit-2.90/man/lastb.10000644017777601777760000000002013303017401015077 0ustar nobodynogroup.so man1/last.1 sysvinit-2.90/man/bootlogd.80000644017777601777760000000543713303017401015633 0ustar nobodynogroup'\" -*- coding: UTF-8 -*- .\" Copyright (C) 1998-2003 Miquel van Smoorenburg. .\" .\" This program is free software; you can redistribute it and/or modify .\" it under the terms of the GNU General Public License as published by .\" the Free Software Foundation; either version 2 of the License, or .\" (at your option) any later version. .\" .\" This program is distributed in the hope that it will be useful, .\" but WITHOUT ANY WARRANTY; without even the implied warranty of .\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the .\" GNU General Public License for more details. .\" .\" You should have received a copy of the GNU General Public License .\" along with this program; if not, write to the Free Software .\" Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA .\" .TH BOOTLOGD 8 "Jul 21, 2003" "" "Linux System Administrator's Manual" .SH NAME bootlogd \- record boot messages .SH SYNOPSIS .B /sbin/bootlogd .RB [ \-c ] .RB [ \-d ] .RB [ \-r ] .RB [ \-s ] .RB [ \-v ] .RB [ " -l logfile " ] .RB [ " -p pidfile " ] .SH DESCRIPTION \fBBootlogd\fP runs in the background and copies all strings sent to the \fI/dev/console\fP device to a logfile. If the logfile is not accessible, the messages will be kept in memory until it is. .SH OPTIONS .IP \fB\-d\fP Do not fork and run in the background. .IP \fB\-c\fP Attempt to write to the logfile even if it does not yet exist. Without this option, .B bootlogd will wait for the logfile to appear before attempting to write to it. This behavior prevents bootlogd from creating logfiles under mount points. .IP \fB\-r\fP If there is an existing logfile called \fIlogfile\fP rename it to \fIlogfile~\fP unless \fIlogfile~\fP already exists. .IP \fB\-s\fP Ensure that the data is written to the file after each line by calling .BR fdatasync (3). This will slow down a .BR fsck (8) process running in parallel. .IP \fB\-v\fP Show version. .IP "\fB\-l\fP \fIlogfile\fP" Log to this logfile. The default is \fI/var/log/boot\fP. .IP "\fB\-p\fP \fIpidfile\fP" Put process-id in this file. The default is no pidfile. .SH BUGS Bootlogd works by redirecting the console output from the console device. (Consequently \fBbootlogd\fP requires PTY support in the kernel configuration.) It copies that output to the real console device and to a log file. There is no standard way of ascertaining the real console device if you have a new-style \fI/dev/console\fP device (major 5, minor 1) so \fBbootlogd\fP parses the kernel command line looking for \fBconsole=...\fP lines and deduces the real console device from that. If that syntax is ever changed by the kernel, or a console type is used that \fBbootlogd\fP does not know about then \fBbootlogd\fP will not work. .SH AUTHOR Miquel van Smoorenburg, miquels@cistron.nl .SH "SEE ALSO" .BR dmesg (8), fdatasync (3). sysvinit-2.90/man/initctl.50000644017777601777760000001411213303017401015453 0ustar nobodynogroup'\" -*- coding: UTF-8 -*- .\" Copyright (C) 2018 Jesse Smith .\" .\" This program is free software; you can redistribute it and/or modify .\" it under the terms of the GNU General Public License as published by .\" the Free Software Foundation; either version 2 of the License. .\" .\" This program is distributed in the hope that it will be useful, .\" but WITHOUT ANY WARRANTY; without even the implied warranty of .\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the .\" GNU General Public License for more details. .\" .\" You should have received a copy of the GNU General Public License .\" along with this program; if not, write to the Free Software .\" Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA .\" .TH INITCTL 5 "April 13, 2018" "" "Linux System Administrator's Manual" .SH NAME initctl \- /run/initctl is a named pipe which passes commands to SysV init. .SH SYNOPSIS /run/initctl .SH DESCRIPTION This document describes the communiction pipe set up by SysV init at /run/initctl. This named pipe allows programs with the proper permissions (typically programs run by root have read+write access to the pipe) to send signals to the init program (PID 1). The init manual page has, up until recently, simply stated that people wishing to understand how to send messages to init should read the init program's source code, but that is not usually practical. Messages sent to the pipe to talk to init must have a special format. This format is defined as a C structure and the technical break-down is presented here: /* * Because of legacy interfaces, "runlevel" and "sleeptime" * aren't in a seperate struct in the union. * * The weird sizes are because init expects the whole * struct to be 384 bytes. */ struct init_request { int magic; /* Magic number */ int cmd; /* What kind of request */ int runlevel; /* Runlevel to change to */ int sleeptime; /* Time between TERM and KILL */ union { struct init_request_bsd bsd; char data[368]; } i; }; Let's go through the init_request structure one line at a time. The first variable, the "magic" number must be of the value 0x03091969. The init program then knows that only programs with root access which send this magic number are authorized to communicate with init. The cmd variable is a value in the range of 0-8 (currently). This cmd variable tells init what we want it to do. Here are the possible options: 1 - Set the current runlevel, specified by the runlevel variable. 2 - The power will fail soon (probably low battery) prepare to shutdown. 3 - The power is failing, do shutdown immediately. 4 - The power is okay, cancel shutdown. 6 - Set an environment variable to a value to be specified in the data variable of this structure. Other cmd options may be added to init later. For example, command values 0, 5 and 7 are defined but currently not implemented. The runlevel variable will specify the runlevel to switch to (0-6). The sleeptime variable is to be used when we want to tell init to change the time spent waiting between sending SIGTERM and SIGKILL during the shutdown process. Changing this at run time is not yet implemented. The data variable (in the union) can be used to pass misc data which init might need to process our request. For example, when setting environment variables. When setting an environment variable through init's /run/initctl pipe, the data variable should have the format VARIABLE=VALUE. The string should be terminated with a NULL character. .SH EXAMPLES The following C code example shows how to send a set environment variable request to the init process using the /run/initctl pipe. This example is simplified and skips the error checking. A more comlpete example can be found in the shutdown.c program's init_setnv() function. .nf struct init_request request; /* structure defined above */ int fd; /* file descriptor for pipe */ memset(&request, 0, sizeof(request)); /* initialize structure */ request.magic = 0x03091969; /* magic number required */ request.cmd = 6; /* 6 is to set a variable */ sprintf(request.data, "VARIABLE=VALUE"); /* set VAR to VALUE in init */ if ((fd = open(INIT_FIFO, O_WRONLY)) >= 0) /* open pipe for writing */ { size_t s = sizeof(request); /* size of structure to write */ void *ptr = &request; /* temporary pointer */ write(fd, ptr, s); /* send structure to the pipe */ close(fd); /* close the pipe when done */ } .fi .sp .RE .SH NOTES Usually the /run/initctl pipe would only be used by low-level programs to request a power-related shutdown or change the runlevel, like telinit would do. Most of the time there is no need to talk to init directly, but this gives us an extenable approach so init can be taught how to learn more commands. .PP The commands passed through the /run/initctl pipe must be sent in a specific binary format and be of a specific length. Larger data structures or ones not using the proper format will be ignored. Typically, only root has the ability to write to the initctl pipe for security reasons. .PP The /run/initctl pipe can be closed by sending init (PID 1) the SIGUSR2 signal. This closes the pipe and leaves it closed. This may be useful for making sure init is not keeping any files open. However, when the pipe is closed, init no longer receives signals, such as those sent by shutdown or telinit. In other words if we close the pipe, init cannot change its runlevel directly. The pipe may be re-opened by sending init (PID 1) the SIGUSR1 signal. .PP If the /run/initctl pipe is closed then it may still be possible to bring down the system using the shutdown command's -n flag, but this is not always clean and not recommended. .RE .SH FILES /run/initctl /sbin/init .SH AUTHOR Jesse Smith .SH "SEE ALSO" init(8) sysvinit-2.90/man/mesg.10000644017777601777760000000351313303017401014737 0ustar nobodynogroup'\" -*- coding: UTF-8 -*- .\" Copyright (C) 1998-2001 Miquel van Smoorenburg. .\" .\" This program is free software; you can redistribute it and/or modify .\" it under the terms of the GNU General Public License as published by .\" the Free Software Foundation; either version 2 of the License, or .\" (at your option) any later version. .\" .\" This program is distributed in the hope that it will be useful, .\" but WITHOUT ANY WARRANTY; without even the implied warranty of .\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the .\" GNU General Public License for more details. .\" .\" You should have received a copy of the GNU General Public License .\" along with this program; if not, write to the Free Software .\" Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA .\" .\"{{{}}} .\"{{{ Title .TH MESG 1 "Feb 26, 2001" "" "Linux User's Manual" .\"}}} .\"{{{ Name .SH NAME mesg \- control write access to your terminal .\"}}} .\"{{{ Synopsis .SH SYNOPSIS .B mesg .RB [ y | n ] .\"}}} .\"{{{ Description .SH DESCRIPTION .B Mesg controls the access to your terminal by others. It's typically used to allow or disallow other users to write to your terminal (see \fBwrite\fP(1)). .\"}}} .\"{{{ Options .SH OPTIONS .IP \fBy\fP Allow write access to your terminal. .IP \fBn\fP Disallow write access to your terminal. .PP If no option is given, \fBmesg\fP prints out the current access state of your terminal. .\"}}} .\"{{{ Notes .SH NOTES \fBMesg\fP assumes that its standard input is connected to your terminal. That also means that if you are logged in multiple times, you can get/set the mesg status of other sessions by using redirection. For example "mesg n < /dev/pts/46". .SH AUTHOR Miquel van Smoorenburg (miquels@cistron.nl) .\"}}} .\"{{{ See also .SH "SEE ALSO" .BR talk (1), .BR write (1), .BR wall (1) .\"}}} sysvinit-2.90/man/utmpdump.10000644017777601777760000000363513303017401015664 0ustar nobodynogroup'\" -*- coding: UTF-8 -*- .\" Copyright (C) 2010 Michael Krapp .\" .\" This program is free software; you can redistribute it and/or modify .\" it under the terms of the GNU General Public License as published by .\" the Free Software Foundation; either version 2 of the License, or .\" (at your option) any later version. .\" .\" This program is distributed in the hope that it will be useful, .\" but WITHOUT ANY WARRANTY; without even the implied warranty of .\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the .\" GNU General Public License for more details. .\" .\" You should have received a copy of the GNU General Public License .\" along with this program; if not, write to the Free Software .\" Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA .\" .TH UTMPDUMP 1 "Februar 8, 2010" "" "Linux System Administrator's Manual" .SH NAME utmpdump \- dump UTMP and WTMP files in raw format .SH SYNOPSIS .B utmpdump .RB [ \-froh ] .I filename .SH DESCRIPTION \fButmpdump\fP is a simple program to dump UTMP and WTMP files in raw format, so they can be examined. .SH OPTIONS .IP \fB\-f\fP output appended data as the file grows. .IP "\fB\-r\fP" reverse. Write back edited login information into utmp or wtmp files. .IP \fB\-o\fP use old libc5 format. .IP \fB\-h\fP usage information. .PP utmpdump can be useful in cases of corrupted utmp or wtmp entries. It can dump out utmp/wtmp to an ASCII file, then that file can be edited to remove bogus entries and reintegrated, using .PP .sp 1 .in +1c .nf \fButmpdump -r < ascii file > wtmp\fP .fi .in -1c .sp 1 but be warned as .B utmpdump was written for debugging purpose only. .SH BUGS You may .B not use the option \fB\-r\fP as the format for the utmp/wtmp files strongly depends on the input format. This tool was .B not written for normal use but for debugging. .SH AUTHOR Michael Krapp .SH "SEE ALSO" .BR last (1), .BR w (1), .BR who (1), .BR utmp (5), sysvinit-2.90/man/reboot.80000644017777601777760000000002013303017401015273 0ustar nobodynogroup.so man8/halt.8 sysvinit-2.90/doc/0000755017777601777760000000000013303017401013712 5ustar nobodynogroupsysvinit-2.90/doc/Install0000644017777601777760000000530013303017401015241 0ustar nobodynogroup Install instructions for the System V style init init, shutdown, halt, reboot, wall, last, mesg, runlevel, killall5, pidof, sulogin. All programs, files and scripts in this package are covered by the GNU General Public License version 2, and copyrighted by Miquel van Smoorenburg (1991-2004) and, Jesse Smith (2018). If you are not using Debian and the debianized package, you may have to install the new init by hand if Debian is using an init system other than SysV (eg systemd). You should be able to drop the binaries into a Slackware or Devuan system, I think. The SysV init software, core programs and manual pages can be installed by running the following two commands from the top-level source directory. make sudo make install If sudo is not installed, the "make install" command may be run as the root user. Other than the GNU make utility, SysV init has few dependencies. SysV can be built on virtually any Linux system featuring the GNU C library or musl libc. A C compiler, such as the GNU Compiler Collection (GCC) or Clang is also required. Here is a list of preferred directories to install the progs & manpages, this should be done for you automatically when you run "make install" as the root user, or via sudo, ie "sudo make install". wall.1, last.1, mesg.1 /usr/man/man1 inittab.5, initscript.5 /usr/man/man5 init.8, halt.8, reboot.8, shutdown.8, powerd.8, killall5.8, pidof.8, runlevel.8, sulogin.8 /usr/man/man8 init /sbin/init inittab /etc/inittab initscript.sample /etc/initscript.sample telinit a link (with ln(1) ) to init, either in /bin or in /sbin. halt /sbin/halt reboot a link to /sbin/halt in the same directory killall5 /sbin/killall5 pidof a link to /sbin/killall5 in the same directory. runlevel /sbin/runlevel shutdown /sbin/shutdown. wall /usr/bin/wall mesg /usr/bin/mesg last /usr/bin/last sulogin /sbin/sulogin bootlogd /sbin/bootlogd utmpdump don't install, it's just a debug thingy. If you already _have_ a "wall" in /bin (the SLS release had, for example) do _not_ install this version of wall. Chances are that the wall you are already using is linked to /bin/write. Either first _remove_ /bin/wall before installing the new one, or don't install the new one at all. You might want to create a file called "/etc/shutdown.allow". Read the manual page on shutdown to find out more about this. Running from a read-only file system (CDROM?): * All communication to init goes through the FIFO /run/initctl. There should be no problem using a read-only root file system If you use a Linux kernel > 1.3.66. Older kernels don't allow writing to a FIFO on a read-only file system. sysvinit-2.90/doc/initctl0000644017777601777760000001022113303017401015277 0ustar nobodynogroupThis document describes the communiction pipe set up by SysV init at /run/initctl. This named pipe allows programs with the proper permissions (typically programs run by root have read+write access to the pipe) to send signals to the init program (PID 1). The init manual page has, up until recently, simply stated that people wishing to understand how to send messages to init should read the init program's source code, but that is not usually practical. Messages sent to the pipe to talk to init must have a special format. This format is defined as a C structure and the technical break-down is presented here: /* * Because of legacy interfaces, "runlevel" and "sleeptime" * aren't in a seperate struct in the union. * * The weird sizes are because init expects the whole * struct to be 384 bytes. */ struct init_request { int magic; /* Magic number */ int cmd; /* What kind of request */ int runlevel; /* Runlevel to change to */ int sleeptime; /* Time between TERM and KILL */ union { struct init_request_bsd bsd; char data[368]; } i; }; Let's go through the init_request structure one line at a time. The first variable, the "magic" number must be of the value 0x03091969. The init program then knows that only programs with root access which send this magic number are authorized to communicate with init. The cmd variable is a value in the range of 0-8 (currently). This cmd variable tells init what we want it to do. Here are the possible options: 1 - Set the current runlevel, specified by the runlevel variable. 2 - The power will fail soon (probably low battery) prepare to shutdown. 3 - The power is failing, do shutdown immediately. 4 - The power is okay, cancel shutdown. 6 - Set an environment variable to a value to be specified in the data variable of this structure. Other cmd options may be added to init later. For example, command values 0, 5 and 7 are defined but currently not implemented. The runlevel variable will specify the runlevel to switch to (0-6). The sleeptime variable is to be used when we want to tell init to change the time spent waiting between sending SIGTERM and SIGKILL during the shutdown process. Changing this at run time is not yet implemented. The data variable (in the union) can be used to pass misc data which init might need to process our request. For example, when setting environment variables. When setting an environment variable through init's /run/initctl pipe, the data variable should have the format VARIABLE=VALUE. The string should be terminated with a NULL '\0' character. The following C code example shows how to send a set environment variable request to the init process using the /run/initctl pipe. This example is simplified and skips the error checking. A more comlpete example can be found in the shutdown.c program's init_setnv() function. struct init_request request; /* this is the structure defined above */ int fd; /* file descriptor for the pipe */ memset(&request, 0, sizeof(request)); /* initialize structure */ request.magic = 0x03091969; /* this magic number must be included */ request.cmd = 6; /* 6 is the command to set a variable */ sprintf(request.data, "VARIABLE=VALUE"); /* set VARIABLE to VALUE in init */ if ((fd = open(INIT_FIFO, O_WRONLY)) >= 0) /* open pipe for writing */ { size_t s = sizeof(request); /* size of the structure to write */ void *ptr = &request; /* temporary pointer */ write(fd, ptr, s); /* send the structure to the pipe */ close(fd); /* close the pipe when done */ } Usually the /run/initctl pipe would only be used by low-level programs to request a power-related shutdown or change the runlevel, like telinit would do. Most of the time there is no need to talk to init directly, but this gives us an extenable approach so init can be taught how to learn more commands. sysvinit-2.90/doc/Propaganda0000644017777601777760000000633713303017401015722 0ustar nobodynogroup Propaganda for version 2.89 of sysvinit & utilities ================================================== NOTE: If you use a standard distribution like Slackware, Devuan or Gentoo there probably is no need to upgrade. Installing sysvinit is only for those that upgrade their system by hand or for people that create Linux distributions. SysV init was probably the most widely used init package for Linux. Most distributions once used it. sysvinit 2.4 was really a good package, and it was not the need for bug fixes but the need for more features that made me work on sysvinit again. SysV init is now a Debian package. Debian source packages are not special in any way -- in fact you can just unpack and compile it on any other Linux distribution. There was a 2.50 release of sysvinit but that was not very popular- some of the included scripts broke with certain shells and other minor things like that. Unfortunately I was not able to fix this at the time because I was abroad for some time. Therefore the description below is a comparison of 2.4 and 2.58 (actually the same blurb as from the 2.50 announce but updated). Wrt 2.4, some of the code has been made simpler. Everything, from halt to reboot to single user mode is now done by shell scripts that are executed directly by init (from /etc/inittab), so shutdown does not kill processes anymore and then calls reboot - it merely does some wall's to the logged in users and then switches to runlevel 0 (halt), 1 (single user) or 6 (reboot). I have removed support for the old style scripts; the included example scripts are the Debian GNU/Linux distribution scripts. This does not mean that eg the Slackware scripts stop to work; you can probably drop this init into Slackware 3.0 without problems. Most people have an entry in inittab to run shutdown when CTRL-ALT-DEL is pressed; a feature has been added to shutdown to check if a authorized user is logged in on one of the consoles to see if a shutdown is allowed. This can be configured with an access file. Some other general changes: - utility "runlevel" to read the current and previous runlevel from /var/run/utmp (it's also shown on the command line if you do a "ps"). - unreckognized options are silently ignored (such as the infamous "ro" - mount root file system read only). - if the file /etc/initscript is present it will be used to launch all programs that init starts (so that you can set a generic umask, ulimit eg for ALL processes - see initscript.sample). - A "sulogin" program added that always asks for the root passsword before entering single user mode. - A "-b" flag to init that starts a shell at boot time before _any_ other processing. - I moved /etc/fastboot to /fastboot - wonder what that's gonna break :) - I even updated the manpages! Right, now some boring stuff you already know since it's the same as in the 2.4 release: The sysvinit package includes * a SysV compatible /sbin/init program * a telinit program (er, just a link to /sbin/init) to change runlevels * a featureful shutdown * halt and reboot to assist shutdown * a very forgiving last utility * the wall & mesg programs (not installed by default) * manpages for everything The new SysV init can be found on: http://download.savannah.nongnu.org/releases/sysvinit/ sysvinit-2.90/doc/Changelog0000644017777601777760000012633313303017401015534 0ustar nobodynogroupsysvinit (2.90) UNRELEASED; urgency=low [ Jesse Smith ] * Updated some comments in init.c to answer questions or remove old notes we no longer need. * Removed unneeded "count" variable in utmpdump.c. * Fixed typo in accidental wrote_wtmp_rlevel == 0 || wrote_wtmp_rlevel comparison so the latter is wrote_utmp_rlevel. * Simplified logic in mountpoint.c when testing for same device or same inode. Thanks to David Binderman for pointing out the above three issues. * When we run shutdown and then the computer is put to sleep, the shutdown command recognizes time has passed and continues its countdown taking the time lapse into consideration. This prevents longer waits if we slept past the time we should have shutdown. Accurate to the nearest minute. Closes Savannah bug #36279. * Added document article and manual page for the initctl (/run/initctl) named pipe. Makes it easier for people to communicate with and extend communication to init. * Added check that named pipe is open before trying to close it when something goes wrong or we receive SIGUSER1. Avoids potential crash if we receive SIGUSR1 while pipe is not open. * Added new signal handler for SIGUSR2. When init receives SIGUSR2 it closes (and leaves closed) the named pipe /run/initctl. This can be used to make sure init does not have any files open. However, it also means we cannot switch run levels or bring down the system. The pipe can be re-opened by sending init the SIGUSR1 signal. * Added "Fall through" comments to some switch statements where multiple approaches are tried in order to prevent warnings from GCC 7 (and newer). (Thanks to Matias Fonzo for this fix.) * Added includes on Linux for sys/sysmacros.h as the old defines in the sys/types.h file will be removed in the future. (Thanks to Matias Fonzo for this fix.) * Removed old LSM file. Added .gitignore files to avoid git tracking object files. Removed old start-stop-daemon from contrib directory. (Patches provided by Guillem Jover.) * Cleaned up most warnings generated by GCC 7 & 8. We still get some from faulty "nonstring" reports, but silencing them on GCC 8 results in more warnings on GCC 7 and Clang, so leaving them for now. * Fixed compile error on Fedora 28 where crypt() will not link due to undocumented dependency change. * Updated Makefile to make sure correct version number and correct version of fiels are used. Makefile was pulling from master to create tarballs with old version information when trying to create beta snapshot. * Updated version information in init.c sysvinit (2.89) world; urgency=low [ Jesse Smith ] * Updated mountpoint command with -p flag. The -p flag causes mountpoint to search for circular mount points. For example, if /a/b/c/d is a mount point for /a/b then the former is a valid mount point. This only works on Linux since it uses /proc/mounts. Updated manual page to match. This fix closes Savannah bug #37114. * Removed two sleep calls when we are doing sync anyway to make sure data is being written. Speeds up reboot time by about two seconds. * Fixed Clang compiler warning regarding variable data parameters to sprintf(). * Updated top-level Makefile to work with git repo instead of old svn repo. * Removed unused variables and findtty() function in bootlogd * Add checks to return code for fscanf() called in init.c. This mostly just cleans up compiler warnings. * Perform error check on setuid() call as suggested in manual page. * Fix typo in killall5.c Move initscript sample file from "src" to "doc" directory and updated Makefile to match. * Allow multiple console output When booting a kernel with multiple serial console support, or multuiple console arguments ala "console=tty1 console=ttyS0,9600" the kernel will output messages to all consoles, init however will not. It will only send output to, and accept input from, the last of the consoles. This patch fixes it. (Patch provided by Martin Buck.) * Added Patch from Debian developer Mats Erik Andersson to make ioctl work on GNU/kFreeBSD. Patches bootlogd. * Added Robert Millan's Debian patch to set TERM variable to xterm (instead of cons25) when running on GNU/kFreeBSD. * Added Robert Millan's Debian patch to use /run/initctl as the named pipe for communicating. This works around a limitation on the kFreeBSD branch which prevents us from using /dev/initctl for pipes. * Confirmed we have applied Guillem Jover's patch to make ifdown work on FreeBSD. * Confirmed we have Debian patch to fix enabling SELinux. (Credit to Petter Reinholdtsen) * Confirmed we have Debian patch to make sure utf-8 flag is not cleared from tty. (Credit to Samuel Thibault) * Confirmed we have Roger Leigh's Makefile patch to allow building with multiarch libcrypt. * Applied Justus Winter's symlink patch to make sure killall5 builds and runs on Hurd. * Confirmed we have Werner Fink's PATH_MAX patch for getting killall5 to build on Hurd. * Made sure we have Petter Reinholdtsen's patch to init.c which allows init to build on GNU/kFreeBSD despite missing VSWTC. * Dropping Debian patch to use /run/nologin instead of /etc/nologin in paths.h. Seems every distribution uses a different location. Oracle uses /etc/nologin, CentOS seems to use /var/run/nologin. We will use /etc/nologin and let distros patch to suit their own preference. * Updated halt.8 man page with corrections from Christoph Anton Mitterer. * Confirmed we have applied patch from Bjarni Ingi Gislason which fixes typo in fstab-decode man page. * Applied Debian patch to allow init to build on GNU Hurd. (Credit: Roger Leigh) * Confirmed we have Debian patch from developer Johannes Truschnigg which informs the user of field size limit. * Applied patch from Debian to the init manual page (init.8) to better address runlevels. (Author unknown) * The pidof command used to discover the correct PID of a process even if the wrong path was given. For example pidof /wrongpath/sleep would find the PID of a command run as "sleep". This bug was reported on Launchpad for Ubuntu and on Savannah. https://bugs.launchpad.net/ubuntu/+source/sysvinit/+bug/1546126 http://savannah.nongnu.org/bugs/?47196 This bug appears to have been fixed in the development branch, allowing these bugs to be tested/closed. * Confirmed Savannah bug #37114 (mountpoint does not detect mount points using --bind on same file system) still exists, but fixed in Debian and Red Hat. Considering this bug closed since distributions are using util-linux's mountpoint program and ours is no longer built/used by default. Considered importing util-linux mountpoint but that would duplicate effort and pull in a new dependency on libmount. * Problem with pidof breaks after prelink (Savannah bug #34992) fixed. Patch supplied by Dave Dykstra. * Patch to automatically spawn a getty on kernel consoles The feature is useful for developers and admins that occasionally need to boot with e.g. console=ttyS0. The built in default can be overridden via inittab for each device. An entry like "S0::off:" turns off the getty on ttyS0. characters in log file. Also makes parsing easier. This should close Savannah bug report 36528. http://savannah.nongnu.org/bugs/?36528 * Applied patches provided in Savannah bug report 49991. Fix tabs in bootlogd and avoid printing unitialized "buf" variable when consolename() fails. [ Werner Fink ] * Do not forget room for last NULL of new environment (was local bug 35866) * Handle deleted binaries in pidof (was local bug #34992) * Allow init to delete extra environment variables (was local bug #35858) * Avoid that init double environment variables for its childs (was local bug #35855) * Remove man-db tag for encoding for canonical man * Sulogin: try to detect the real device(s) used for the system console /dev/console if but only if /dev/console is used. On Linux this can be more than one device, e.g. a serial line as well as a virtual console as well as a simple printer. * Fix counting message lines in wall. Patch from Petr Lautrbach. * Fix bad printf conversion specifier in wall. Patch from Sébastien Luttringer. * Add patches from Openwall project. Thanks goes to Solar Designer. * Add code to detect the system consoles with the help of the new /proc/consoles files of linux kernel 2.6.38+ * Try to make utmpdump IPv6 valid, change based on suggestion from Navdeep Bhatia (see local bug #32429) * Fix signal and alarm handling based on the patch from Florent Viard. (was local bug #32304) * Add fix for Redhat bug #573346: last incorrectly displays IPv6 addresses (was local bug #29497) * Correct fix for Debian bug #547073: use IUTF8 flag if defined and if already set to make sure the utf-8 flag is not cleared from the tty. Patch from Samuel Thibault. * Include limits.h in killall.c to enforce definition of PATH_MAX * Fix sysvinit bug #29758 Linker invocation should not contain headers. Change based on patch from Elias Pipping. * Add fix for Debian bug #580272: use return value 1 of is_selinux_enabled() to determine if SELinux is enabled, otherwise initialize SELinux and load the policy. Patch from Petter Reinholdtsen. * Make quotes visible in example of the manual page of fstab-decode * Sulogin: enforce reconnection of stdin/stdout/stderr if a device was specified. * Sulogin: if zero is read at reading the passwd guess it's done. * Sulogin: respect byte order that is do not mix chars and ints * Shutdown: use PATH_DEFAULT as suggested by Paul Arthur in local bug #36101 * Killall5/pidof: handle strange names of executables (local bug #36252) * Sulogin: be aware the crypt(3) may fail (local bug #36313) [ Petter Reinholdtsen ] * Next release will be 2.89dsf. * Add #ifdef in bootlogd.c to avoid gcc warnings about unused variable on non-linux platforms. * Only set the VSWTC field for termios in init if it is available, to get the source building on FreeBSD. * Add some code to be able to detect programs even as user with kernel 3.0 and above * Improve message printed when signaling processes to stop. Patch from Matias A. Fonzo at the dragora project. * Rename internal functions warn() and shutdown() in the shutdown binary to avoid surprising dynamic library name resolution conflict with NSS modules. Patch from Richard Tollerton. * Try harder to find libcrypt.*, even if there is no static library available. Also look in /usr/lib/*/ for the library, to handle Debian multiarch systems. Based on patch from Andrew Gregory. * Adjust included headers to be compatible with the musl C library. Patch from Matias A. Fonzo and Dragora. * Move dovoid() macro from #ifdef__GLIBC__ to #ifdef __linux__, to match the condutions of the place where it is used. Thanks to Matias A. Fonzo for noticing. * Define _XOPEN_SOURCE when building to get crypt() from instead of using in sulogin.c, to get the source building with the musl C library. * Use sysconf(_SC_SYMLOOP_MAX) instead of MAXSYMLINKS. If sysconf returns an error, fall back to MAXSYMLINKS on platforms that define it. Fixes build on Hurd. Patch from Justus Winter and Debian. * Adjust makefile to make it easier to link all binaries statically. Patch from Matias A. Fonzo and Dragora. * Rewrite findtty() in bootlogd.c to not chance working directory, to reduce the amount of failure that can happin in that function. * Adapt bootlogd TIOCCONS call to kfreebsd. Patch from Mats Erik Andersson and Debian. * Document length limit for the process field in the inittab. Patch from Johannes Truschnigg and Debian. * Fix typo in fstab-decode(8) font escape. Patch from Bjarni Ingi Gislason and Debian. * Port ifdown.c to FreeBSD. Patch from Guillem Jover and Debian. * Drop dsf part from version number. It no longer make sense to keep. * Remove obsolete/ directory from tarball. None of it have been useful for many years. * Make it possible to specify the initctl path as a compile time define INIT_FIFO. * Use narrowly scoped file descriptor for handling opened TTY in spawn(). Patch from Michał Kulling. * Check exit code from dup() in spawn() and log error if it fail. Patch from Michał Kulling. -- Petter Reinholdtsen Sun Apr 11 11:28:55 CEST 2010 sysvinit (2.88dsf) world; urgency=low [ Petter Reinholdtsen ] * Mention new home on Savannah in README. * Revert change from Fedora/RedHat where the now obsolete command INIT_CMD_CHANGECONS was introduced. Based on feedback and patch from Bill Nottingham. * Adjust makefile to make sure the install directories are created before files are copied into them. * Simplify build rules, based on patch from Mike Frysinger and Gentoo. * Fix minor bug in optimizing of argument parsing. Based on report from jakemus on freshmeat. * Add casts to get rid of compiler warning about signed/unsigned issues. * Change tty handling in init to make sure the UTF-8 flag is not cleared on boot. Patch from Samuel Thibault. * Add Makefile in toplevel directory. * Print usage information when shutdown is used by non-root user. Patch from Mike Frysinger and Gentoo. * Sync shutdown manual page and usage information. Patch from Mike Frysinger and Gentoo. * Fix race condition in utmp writing. Patch from Gil Kloepfer via Mike Frysinger and Gentoo. * Rewrite findtty() in bootlogd to recursively search /dev/ for the correct device, to handle terminal devices for example in /dev/pty/. Patch from Debian. * Make sure bootlogd findpty() returns an error value when it fails to find a usable pty. Patch from Rob Leslie via Debian. * Make sure bootlogd fflush() every line, even if asked not to flush to disk using fdatasync(). Patch from Scott Gifford via Debian. * Add compatibility code to handle old path "/etc/powerstatus" for a while. * Incude definition for MNT_DETACH which is missing in older GNU libc headers. * Do not strip binaries before installing them, to make it easier to get binaries with debug information installed. [ Werner Fink ] * Add the comment from Andrea Arcangeli about the correct place of setting the default childhandler within spawn(). * Make sure that newline is printed out for last(1) even if an utmp record entry is truncated. * Check if utmp not only exists but also is writable and delay writing out of the utmp runlevel record if utmp is not writable. * Be able to find libcrypt also on 64 bit based architectures. * Add option -w to the last command to display the full user and domain names in the output. Patch from Petr Lautrbach. * Add a manual page for utmpdump as this tool is sometimes very useful even if not intended for normal use. * Use paths.h macros for wall * Change path "/etc/powerstatus" to "/var/run/powerstatus" * Detected also removable block devices at halt/reboot to be able to flush data and send them the ATA standby command. This should avoid data loss on USB sticks and other removable block devices. * Flush block devices on halt/reboot if not done by the kernel. * Set SHELL to /bin/sh in the environmant of shutdown. * Retry to write out shutdown messages if interrupted. * pidof/killall5 - make omit pid list a dynamic one. * pidof - provide '-n' to skip stat(2) syscall on network based FS. * init - avoid compiler warnings * init - initialize console by using the macros from ttydefaults.h * init - add the possiblity to ignore further interrupts from keyboard * init - add the possiblity to set sane terminal line settings * sulogin - add the possibility to reset the terminal io * Fix some minor problems * init - enable is_selinux_enabled() to detect selinuxfs * Add fix for Debian bug #536574 -- Can be enabled by -DACCTON_OFF * Add helper program fstab-decode to make it easier to handle /etc/mtab content. Patch by Miloslav Trmac and Fedora. * Add fix for Debian bug #335023 - Make sure TERM is set on FreeBSD. * Add fix for Debian bug #374038 - Make it clear that shutdown -c can only cancel a waiting shutdown, not an active one. * Add note to pidof manual page about the use of readlink(2). Patch by Bill Nottingham and Fedora. * Add PAM patch contrib/notify-pam-dead.patch based on Debian bug #68621, which will add PAM support for programs spawned by init on the console like sulogin. Based on patch by Topi Miettinen. This patch is not applied by default yet while we review its usefullness. It is only helpful for session handling, as sulogin do not use and will not use a PAM conv() function. The current sulogin is able to handle DES as well as MD5, SHA, and Blowfish encrypted passwords due using getpwnam(3). * Move utmp/wtmp before the execvp() in spawn() to be sure to use the correct pid even on a controlling tty * Remaining problem is that the pid of the second fork() for getting a controlling tty isn't that reported by spawn() * Re-enable writting utmp/wtmp for boot scripts * Extend sulogin to support additional encryption algorithms * Re-enable maintenance message of sulogin * Enable the sulogin fallback password check to handle MD5, SHA, and Blowfish encrypted passwords in case of getpwnam(3) fails. * sulogin picking the SELinux context was broken. Patch by Daniel Walsh -- Petter Reinholdtsen Sun Apr 11 11:28:55 CEST 2010 sysvinit (2.87dsf) world; urgency=low * Fix typos and do minor updates in the manual pages. * Correct section of mountpoint(1). * Document -e and -t options for telinit in init(8). * Update address of FSF in the COPYRIGHT file. * Document in halt(8) that -n might not disable all syncing. Patch by Bill Nottingham and Fedora * Adjust output from "last -x". In reboot lines, print endpoint of uptime too. In shutdown lines print downtimes rather than the time between downs. Fix typo in string compare in last.c. Patch by Thomas Hood. * Improve handling of IPv6 addresses in last. Patch from Fedora. * Document last options in usage information, previously only mentioned in the manual page. * Add new option -F to last, to output full date string instead of the short form provided by default. Patch from Olaf Dabrunz and SuSe. * Adjust build rules to make sure the installed binaries are stripped. * Increase the compiler warning level when building. * Fix utmp/wtmp updating on 64-bit platforms. Patch by Bill Nottingham and Fedora. * Avoid unchecked return value from malloc() in utmpdump. Patch from Christian 'Dr. Disk' Hechelmann and Fedora. * Make sure to use execle and no execl when passing environment to the new process. Patch from RedHat. * Correct init to make sure the waiting status is preserved across re-exec. Patch from RedHat. * Correct init to avoid race condition when starting programs during boot. Patch from SuSe. * Allow 'telinit u' in runlevels 0 and 6. Patch from Thomas Hood. * Change install rules to make pidof an absolute symlink. Patch from Thomas Hood. * Improve error message from init if fork() fail. Patch found in Suse. * Add support for SE Linux capability handling. Patch from Manoj Srivastava, adjusted to avoid aborting if SE policy was loaded in the initrd with patch from Bill Nottingham and Fedora. * Add -c option to pidof for only matching processes with the same process root. Ignore -c when not running as root. Patch from Thomas Woerner and Fedora. * Adjust init to terminate argv0 with one 0 rather than two so that process name can be one character longer. Patch by Kir Kolyshkin. * Make sure bootlogd exit with non-error exit code when forking of the child successfully. * Add bootlogd option -s to make it possible to control the use of fdatasync(). Patch from Thomas Hood. * Add bootlogd option -c to tell it to create the log file if it does not exist. Patch from Thomas Hood. * Let bootlogd also look at ttyB* devices to work on HPPA. Patch from Thomas Hood. * Change init to use setenv() instead of putenv, make sure the PATH value is usable on re-exec. Patch from Thomas Hood. * Add usleep in killall5 after killing processes, to force the kernel to reschedule. Patch from SuSe. * Modify pidof to not print empty line if no pid was found. * Modify init and sulogin to fix emergency mode's tty, making sure ^C and ^Z work when booting with 'emergency' kernel option. Patch from Samuel Thibault. * Modify init to allow some time for failed opens to resolve themselves. Patch from Bill Nottingham and Fedora. * Modify init to shut down IDE, SCSI and SATA disks properly. Patches from Sebastian Reichelt, Werner Fink and SuSe. * Modify wall to use UT_LINESIZE from instead of hardcoded string lengths. Patch from SuSe. * Change wall to make halt include hostname in output. * Change killall to avoid killing init by mistake. Patch from SuSe. * Change killall5 to use the exit value to report if it found any processes to kill. Patch from Debian. * Add option -o opmitpid to killall5, to make it possible to skip some pids during shutdown. Based on patch from Colin Watson and Ubuntu. * Add references between killall5 and pidof manual pages. Patch from Debian. * Modify killall to work better with user space file system, by changing cwd to /proc when stopping and killing processes, and avoiding stat() when the value isn't used. Also, lock process pages in memory to avoid paging when user processes are stopped. Patch from Debian and Goswin von Brederlow with changes by Kel Modderman. * Change shutdown to only accept flags -H and -P with the -h flag, and document this requirement in the manual page. * Change reboot/halt to work properly when used as a login shell. Patch by Dale R. Worley and Fedora. * Let sulogin fall back to the staticly linked /bin/sash if both roots shell and /bin/sh fail to execute. -- Petter Reinholdtsen Sun, 12 Jul 2009 19:58:10 +0200 sysvinit (2.86) cistron; urgency=low * Fixed up bootlogd to read /proc/cmdline. Also keep an internal linebuffer to process \r, \t and ^H. It is becoming useable. * Applied trivial OWL patches * Block signals in syslog(), since syslog() is not re-entrant (James Olin Oden , redhat bug #97534) * Minor adjustements so that sysvinit compiles on the Hurd * killall5 now skips kernel threads * Inittab entries with both 'S' and other runlevels were broken. Fix by Bryan Kadzban * Changed initreq.h to be more flexible and forwards-compatible. * You can now through /dev/initctl set environment variables in init that will be inherited by its children. For now, only variables prefixed with INIT_ can be set and the maximum is 16 variables. There's also a length limit due to the size of struct init_request, so it should be safe from abuse. * Option -P and -H to shutdown set INIT_HALT=POWERDOWN and INIT_HALT=HALT as environment variables as described above * Add "mountpoint" utility. * Slightly better algorithm in killall5.c:pidof() * Added some patches from fedora-core (halt-usage, last -t, sulogin-message, user-console) -- Miquel van Smoorenburg Fri, 30 Jul 2004 14:14:58 +0200 sysvinit (2.85) cistron; urgency=low * Add IPv6 support in last(1) * Sulogin: even if the root password is empty, ask for a password- otherwise there is no way to set a timeout. * Removed support for ioctl.save. * Turned of support for /etc/initrunlvl and /var/run/initrunlvl * Fixed warts in dowall.c ("Dmitry V. Levin" ) * Fix init.c::spawn(). The "f" variable was used both as file descriptor and waitpid(2) return code. In certain circumstances, this leads to TIOCSCTTY with wrong file descriptor (Vladimir N. Oleynik). * Fix fd leak in sulogin (Dmitry V. Levin). * More error checking in all wait() calling code (Dmitry V. Levin). * Fix argv[] initialization in spawn() (Dmitry V. Levin). * Change strncpy to strncat in most places (Dmitry V. Levin). -- Miquel van Smoorenburg Tue, 15 Apr 2003 16:37:57 +0200 sysvinit (2.84) cistron; urgency=low * Don't use /etc/initlvl interface for telinit; only use /dev/initctl, and give a clear error when that fails. * Add -i/--init command line flag to init - this tells init 'behave as system init even if you're not PID#1'. Useful for testing in chroot/jail type environments. -- Miquel van Smoorenburg Tue, 27 Nov 2001 13:10:08 +0100 sysvinit (2.83) cistron; urgency=low * Fix bug in shutdown where it didn't check correctly for a virtual console when checking /etc/shutdown.allow * Fix race condition in waitpid() [Andrea Arcangeli] * Call closelog() after openlog()/syslog() since recent libc's keep the logging fd open and that is fd#0 aka stdin. -- Miquel van Smoorenburg Tue, 2 Oct 2001 23:27:06 +0200 sysvinit (2.82) cistron; urgency=low * Print out correct version number at startup. * Fix spelling of initttab in init(8) -- Miquel van Smoorenburg Thu, 23 Aug 2001 17:50:44 +0200 sysvinit (2.81) cistron; urgency=low * Fix typo/bug in killall5/pidof, -o option failed to work since 2.79. Reformatted source code to prevent this from happening again. * shutdown.8: applied redhat manpage update * sulogin: applied redhat sysvinit-2.78-sulogin-nologin.patch * sulogin: applied redhat sysvinit-2.78-notty.patch * sulogin: applied redhat sysvinit-2.78-sigint.patch sysvinit (2.80) cistron; urgency=low * Grammar/spelling fixes in shutdown.c (Christian Steinrueck) * Don't set controlling tty for non-(sysinit,boot,single) runlevels -- Miquel van Smoorenburg Thu, 26 Jul 2001 13:26:56 +0200 sysvinit (2.79) cistron; urgency=low * New upstream version * several fixes to wall by Tim Robbins * Several extra boundary checks by Solar Designer * Make /dev/console controlling tty * Stricter checks on ownership of tty by mesg(1) * Documented and restricted -n option to wall(1) * Make it compile with glibc 2.2.2 * Document IO redirection in wall manpage (closes: #79491) * Update README (closes: #85650) * Fix init.8 manpage (closes: #75268) * Fix typo in halt(8) manpage (closes: #67875) * Check time argument of shutdown(8) for correctness (closes: #67825) * Check for stale sessions in last(1) (Chris Wolf ) -- Miquel van Smoorenburg Wed, 4 Jul 2001 15:04:36 +0200 sysvinit (2.78-2) frozen unstable; urgency=high * Change "booting" to "reloading" message at reload * Add "-z xxx" dummy command line argument (closes: #54717) -- Miquel van Smoorenburg Fri, 11 Feb 2000 12:17:54 +0100 sysvinit (2.78-1) unstable; urgency=low * 2.78 will be the new upstream version, I'm skipping 2.77 * Shutdown now calls sync before switching the runlevel to 0 or 6, or before unmounting filesystems if -n was used (closes: #46461) * Some cosmetic changes to init.c (closes: #32079) -- Miquel van Smoorenburg Thu, 30 Dec 1999 20:40:23 +0100 sysvinit (2.77-2) unstable; urgency=low * Fix last -i option -- Miquel van Smoorenburg Tue, 5 Oct 1999 21:51:50 +0200 sysvinit (2.77-1) unstable; urgency=low * Write reboot record into utmp file as well to make rms happy * Fork and dump core in / if SIGSEGV is received for debugging purposes * Patch by Craig Sanders for "last" -i option -- Miquel van Smoorenburg Wed, 4 Aug 1999 11:16:23 +0200 sysvinit (2.76-4) unstable; urgency=low * Change dowall.c to handle Unix98 ptys correctly * Add comment in rcS about usage of setup.sh and unconfigured.sh * Shutdown now removes nologin file just before calling telinit * SEGV handler now tries to continue after sleep of 30 seconds. On a 386-class processor it also prints out the value of EIP. * Fix for racecondition in check_init_fifo() by Richard Gooch -- Miquel van Smoorenburg Sat, 8 May 1999 17:22:57 +0200 sysvinit (2.76-3) frozen unstable; urgency=high * Small bugfix to last.c courtesy of Danek Duvall -- Miquel van Smoorenburg Tue, 12 Jan 1999 12:12:44 +0100 sysvinit (2.76-1) frozen unstable; urgency=high * Fix bug in check_pipe() which crashes init on the Alpha. -- Miquel van Smoorenburg Tue, 3 Nov 1998 11:09:13 +0100 sysvinit (2.75-4) unstable; urgency=low * Change sulogin password buffer to 128 characters. * Don't print control characters in dowall.c * Try to open getenv ("CONSOLE"), /dev/console and /dev/tty0 in order. For backwards compatibility when you try to boot a 2.0.x kernel with a linux > 2.1.70 /dev/console device. * Change src/Makefile for non-debian systems (mainly, RedHat) * Try to create /dev/initctl if not present; check every time to see if the dev/ino of /dev/initctl has changed and re-open it. This should help devfs a bit. * Send SIGUSR1 to init at bootup to let it re-open /dev/initctl; again in support of devfs. * Moved pidof to /bin (it's only a link to killall5 anyway) -- Miquel van Smoorenburg Mon, 5 Oct 1998 14:03:14 +0200 sysvinit (2.75-2) frozen unstable; urgency=medium * Fix last.c again. * Add check to see if /dev/initctl is really a FIFO * In ifdown.c first down all shaper devices then the real devices -- Miquel van Smoorenburg Tue, 2 Jun 1998 22:43:01 +0200 sysvinit (2.75-1) frozen unstable; urgency=low * Rewrote last.c to be much more memory friendly and correct, thanks to Nick Andrew and David Parrish * Fixes bugs: #21616: sysvinit: sulogin thinks md5 root password is bad #21765: sysvinit: Typo in `killall5.c' #21775: sysvinit: sysvinit does not support MD5 hashed passwords #21990: /usr/bin/last: unnecessary memset and off-by-one bug #22084: sysvinit 2.74-4: SIGPWR missing on sparc #21900: init, powerfail events, and shutdown.allow #21702: init 0 does not work as expected... #21728: sysvinit: Typo in `init.c' #22363: sysvinit: discrepance btw. manpage and /sbin/init -- Miquel van Smoorenburg Tue, 19 May 1998 11:02:29 +0200 sysvinit (2.74-4) frozen unstable; urgency=medium * Add -o option to last to process libc5 utmp files. * Buffer overflow fixed in init.c (not very serious; only exploitable by root). Thanks to Chris Evans -- Miquel van Smoorenburg Wed, 15 Apr 1998 17:04:33 +0200 sysvinit (2.74-1) unstable; urgency=low * Should compile with glibc 1.99 :) * Change behaviour of reboot(1) and halt(1) so that the default when the runlevel can't be determined is to call shutdown. * Added re-exec patch from Al Viro (21 Feb 1998): 'U' flag added to telinit. It forces init to re-exec itself (passing its state through exec, certainly). May be useful for smoother (heh) upgrades. 24 Feb 1998, AV: did_boot made global and added to state - thanks, Miquel. Yet another file descriptors leak - close state pipe if re_exec fails. -- Miquel van Smoorenburg Thu, 12 Mar 1998 17:42:46 +0100 sysvinit (2.73-2) unstable; urgency=low * Change _NSIG to NSIG for 2.1.x kernel includes. -- Miquel van Smoorenburg Thu, 8 Jan 1998 16:01:02 +0100 sysvinit (2.73-1) unstable; urgency=low * Use siginterrupt, now that system calls are restarted by default. Main symptom was that the sulogin timeout didn't work but there might have been more hidden problems. * Kill process immidiately if turned off in inittab * Fixed sulogin check on tty arg. * Use strerror() instead of sys_errlist * wall now supports a '-n' option to suppress [most of] the banner. Debian doesn't use sysvinit's wall, but apparently Redhat does. * Add '-F' (forcefsck) option to shutdown * Close and reopen /dev/initctl on SIGUSR1 (mainly for a /dev in ram) -- Miquel van Smoorenburg Sat, 3 Jan 1998 16:32:39 +0100 sysvinit (2.72-3) unstable; urgency=low * Add extra fork() in dowall.c to avoid hanging in rare cases -- Miquel van Smoorenburg Wed, 22 Oct 1997 14:44:00 +0200 sysvinit (2.72) unstable; urgency=low * Applied manual page patches by Bill Hawes . Thanks Bill! * Applied patches to the sample Slackware scripts by "Jonathan I. Kamens" * Fix halt and reboot runlevels 0 & 6 check. * Only say "no more processes left in runlevel x" once * Fix race condition with SIGCHLD in spawn() (thanks to Alon Ziv ) * Compress all manpages (missed 2) * Compiled for libc6 * Added poweroff patch by Roderich Schupp -- Miquel van Smoorenburg Sun, 12 Oct 1997 17:20:17 +0200 sysvinit (2.71-2) frozen unstable; urgency=low * Print 2.71 instead of 2.70 on startup :) -- Miquel van Smoorenburg Mon, 5 May 1997 12:45:25 +0200 sysvinit (2.71-1) frozen unstable; urgency=high * Added code for updwtmp() in utmp.c for glibc (2.0.3) * Fixed all programs to use functions from utmp.c and getutent() * Do not try to clean up utmp in init itself (Bug#9022) * Removed sync() from main loop. * Hopefully fixes bug #8657 (shutdown signal handling) -- Miquel van Smoorenburg Sat, 26 Apr 1997 19:57:27 +0200 sysvinit (2.70-1) unstable; urgency=low * Respawn fix * Removed StUdLy CaPs from source code * Moved files in source archive around * Fixes for glibc (utmp handling, signal handling). * Fixed '-d' option to last (now also works without '-a'). * Added extra checking in last.c to prevent showing dead entries -- Miquel van Smoorenburg Fri, 7 Feb 1997 15:31:30 +0100 sysvinit (2.69-1) frozen unstable; urgency=medium * Fixed bug that can throw X in a loop (or any other app that reads from /dev/tty0) -- Miquel van Smoorenburg Sun, 1 Dec 1996 15:32:24 +0100 sysvinit (2.67-1) frozen unstable; urgency=high * Fixes problem with /dev/console being controlling terminal of some daemons * Puts copyright file in the right place -- Miquel van Smoorenburg Fri, 15 Nov 1996 12:23:33 +0100 sysvinit (2.66-1) unstable; urgency=medium * Skipped 2.65. A development 2.65 got out by accident and is apparently being used.. * Also compiles and runs with GNU libc (and on the Alpha) * Fixed dowall.c not to exit when getpwuid() fails and uid == 0. * Fixed init panic'ing on empty lines in /etc/inittab * Changed default PATH to include /usr/local/sbin * Set /dev/console as controlling terminal for sysinit,bootwait,wait,powerwait This allows using ^C to interrupt some parts of eg the boot process. * Remove old symlink in /var/log/initlvl; let init check both /var/log and /etc itself. -- Miquel van Smoorenburg Tue, 29 Oct 1996 13:46:54 +0100 2.66 29-Oct-1996 - Skipped 2.65. A development 2.65 got out by accident and is apparently being used.. - Fixed dowall.c not to exit when getpwuid() fails and uid == 0. - Fixed init panic'ing on empty lines in /etc/inittab - Changed default PATH to include /usr/local/sbin - Ported to Linux/Alpha and GNU libc. - Set /dev/console as controlling terminal for sysinit,bootwait,wait,powerwait. This allows using ^C to interrupt some parts of eg the boot process. - Remove old symlink in /var/log/initlvl; let init check both /var/log and /etc itself. 2.64 28-Jun-1996 - Init checks CONSOLE environment variable on startup (overrides /dev/console) - Init sets CONSOLE variable for all its children. - Wtmp(): when zeroing out old utmp entries, keep ut_id field - Wtmp(): try to re-use ut_id field if possible. - SetTerm(): only read from /etc/ioctl.save if written once. - Included start-stop-daemon, C version (source only). - Fixed wait() for the emergency shell. - killall5: ignore signal before doing kill(-1, pid). 2.63 14-Jun-1996 - Fixed preinst script for Debian - Fixed init.c to become init daemon if name is init.new - Fixed pidof to not return PIDs of shell scripts 2.62-2 09-Jun-1996 - Changed debian /etc/init.d/boot script to create a nologin file at boot and to remove it just before going multiuser. 2.62 31-May-1996 - Decided to release a 2.62 version with a BIG WARNING about upgrading init in it. Will send a patch to Linus for the linux/Documentation/Changes file so that 2.62 or later is mentioned as the version to upgrade to. - Added docs for Slackware 2.61-3 29-May-1996 - Fixed debian/etc/init.d/network for the lo device. - Added "-xdev" to the cd /tmp && find in debian/etc/init.d/boot - Made remove time for files in /tmp configurable. 2.61 29-Apr-1996 - Changed /etc/init.d/boot script again - Fixed problem in init.c with trailing whitespace after entries in inittab - Fixed killall5 problems - Added manpage for lastb - Added SHELL= environment variable to sulogin - Fixed sulogin & shadow problems - Added timeout option to sulogin 2.60-2 16-Apr-1996 - Fixed sulogin (didn't work if root wasn't first entry in shadow file) - Fixed mesg for systems with "tty" group (such as Debian) - Fixed nsyslog() in killall5.c 2.60 01-Apr-1996 - Fixed race condition in init.c, resulting in hanging shutdowns. Courtesy of Max Neunhoeffer . - Fixed debian/etc/init.d/boot for swapon and mdadd - Added architecture to debian.control - Added manpages for rc.boot and rc.local - Updated inittab manpage for 4-character runlevel field - Added debian replaces for bsdutils < version_without_mesg - Fixed init.c so that it also works with kernels 1.3.81 and up 2.59 10-Mar-1996 - Init logs less to syslog (suspected to hang in syslog() or openlog() ) - removed closelog() from init.c - removed time check of runlevel record in halt. - Added options to last to get hostname from ut_addr field - Added last and mesg to installation targets - rewrote /etc/init.d/boot a bit. 2.58-2 04-Jan-1996 - Changed etc/init.d/rc to do a stty onlcr - Added /var/log/initrunlvl symlink 2.58-1 31-Dec-1995 - Added the latest debian files. - Added support for 4-character id fields (if you have libc5). - Fixed pidof (in killall5) parsing of /proc/.../stat - Save restore GMT setting in /etc/init.d/boot 2.57d 03-Dec-1995 - Added sulogin - Added "-b" flag to init, gives a shell before anything else (in case the startup scripts are screwed) - Moved fastboot to /fastboot - Folded in Debian patches. - Removed old scripts - Added debian /etc/directory. 2.57c 08-Oct-1995 - Changed over to init_request (with initreq.h) - Processes no longer killed when "process" field changes, change takes effect after next respawn. 2.57b xx-Aug-1995 - Bugfix release for Debian and Slackware 3.0 2.57a 10-Jul-1995 - Fixed race condition init init.c wrt got_chld - Fixed one-off for malloc in killall5.c - Changed dowall.c - Console code: no relink to /dev/systty on CTRL-ALT-DEL) 2.57 22-May-1995 - Changed a few things here and there, didn't really document it :) 2.55 17-Jan-1995 - Added option to shutdown to run standalone. 2.54 12-Jan-1995 - Added GNU copyrigh to all *.[ch] files. - added /etc/initscript - reboot and halt now call shutdown in runlevels 1-5 - Can run from read-only root (CDROM) 2.53 10-Oct-1994 - Renamed pidof to killall5, updated all scripts to use killall5 instead of kill -1 .... - Rewrote scripts to use this, and some general changes. - Added SysV command line compatibility to shutdown. 2.52 30-Aug-1994 - Added `powerfailnow' keyword, for when UPS battery is low. - Updated `last'. - Fixed utmp handling (wrt. CLEAN_UTMP) TODO: * Make last compatible with GNU/BSD (long options?) * update powerd * remote SIGPWR broadcast? in powerd? (with priv. port) * remote shutdown 2.50 14-Feb-1994 - Ignores unknown command line arguments. - Modelled more after the "real" sysVinit - Lots of changes all over the place. (like showing runlevel in "ps" listing, logging runlevel into utmp file etc) - now using "reliable" signals instead of V7 style. - Updated all scripts. Examples are in two directories: etc (normal) and etc-sysv (sysv style scripts). - runlevel 0 = halt, 1 = single user, 6 = reboot. - added support for serial console. - updated Propaganda, manpages. - added shutdown access control. 2.4 24-May-93 - Send out the official version into the world as SysVinit-2.4.tar.z. 2.4g 15-May-93 - Changed init to really catch SIGPWR 'cause we hooked up an UPS to the Linux machine. The keyword for catching the TreeFingerSalute is now "ctrlaltdel" instead of "power{wait,fail}". 2.4a 22-Apr-93 - Fixed last to reckognize BSD style wtmp logging. - Changed init to write wtmp records that are SysV compliant but are also reckognized by the BSD last. Added a '+' option to the 'process' field of inittab, for getties that want to do their own utmp/wtmp housekeeping (kludge!). - Now accepts a runlevel on the command line, and reckognizes the 'auto' argument. (Sets the environment variable AUTOBOOT to YES) 2.2.3 24-Mar-93 - Ripped out the 'leave' action. To difficult, and unneeded. - Going single user now kills _all_ processes. - Added '-t secs' option to all commands. - This version is stable enough to post. 2.2 02-Mar-93 - Made wait()'s asynchronous - Changed whole thing to one big state machine - Now using 'pseudo levels' # & * for SYSINIT & BOOT - Added a new type of 'action', called leave. This process will be executed when the system goes from a runlevel specified in it's runlevel field to a level that's not. Nice to bring down NFS and the like. 2.1 28-Jan-93 - Fixed a bug with 'boot' and 'once'. - Check 'initdefault' for validity. - Reckognizes "single" as command line argument. - Retries execvp with 'sh -c exec ..' if command is a shell script. (shouldn't execvp do this?) - Added patches to use syslog if defined. 2.0 08-Dec-92 - Rewrote the code totally, so started with a new version number. - Dropped Minix support, this code now is Linux - specific. - With TEST switch on, this init & telinit can run standalone for test purposes. 1.3, 05-Jul-92 - Got a 386, so installed Linux. Added 'soft' reboot to be default under linux. Fixed some typos. 1.2, 16-Jun-92 - Bugreport from Michael Haardt ; removed deadlock and added 'waitpid' instead of 'wait' for SYSV. 1.1, 30-Apr-92 - Read manual wrong: there is no 'action' field called process, but all entries are of type process. Every 'process' get exec'ed by /bin/sh -c 'exec command'. - Rapidly respawning processes are caught in the act. - _SYSV support is really Linux support, done by poe@daimi.aau.dk on 25-Mar-92. 1.0, 01-Feb-92 - Initial version, very primitive for the Minix operating system. Required some mods. to the kernel. sysvinit-2.90/doc/bootlogd.README0000644017777601777760000000131213303017401016377 0ustar nobodynogroup bootlogd: a way to capture all console output during bootup in a logfile. - bootlogd opens /dev/console and finds out what the real console is with an ioctl() if TIOCCONS is available - otherwise bootlogd tries to parse /proc/cmdline for console= kernel command line arguments - then opens the (most probable) real console - allocates a pty pair - redirects console I/O to the pty pair - then goes in a loop reading from the pty, writing to the real console and a logfile as soon as a r/w partition is available, buffering in memory until then. As soon as bootlogd exits or gets killed, the pty is closed and the redirection will be automatically undone by the kernel. So that's pretty safe. sysvinit-2.90/doc/initscript.sample0000755017777601777760000000130213303017401017304 0ustar nobodynogroup# # initscript If this script is intalled as /etc/initscript, # it is executed by init(8) for every program it # wants to spawn like this: # # /bin/sh /etc/initscript # # It can be used to set the default umask and ulimit # of all processes. By default this script is installed # as /etc/initscript.sample, so to enable it you must # rename this script first to /etc/initscript. # # Version: @(#)initscript 1.10 10-Dec-1995 MvS. # # Author: Miquel van Smoorenburg, # # Set umask to safe level, and enable core dumps. umask 022 ulimit -c 2097151 PATH=/bin:/sbin:/usr/bin:/usr/sbin export PATH # Execute the program. eval exec "$4" sysvinit-2.90/COPYING0000644017777601777760000004310313303017401014201 0ustar nobodynogroup GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License.