📄 simpleinit.c
字号:
/* simpleinit.c - poe@daimi.aau.dk *//* Version 2.0.2 *//* 1999-02-22 Arkadiusz Mi秌iewicz <misiek@pld.ORG.PL> * - added Native Language Support * 2001-01-25 Richard Gooch <rgooch@atnf.csiro.au> * - fixed bug with failed services so they may be later "reclaimed" * 2001-02-02 Richard Gooch <rgooch@atnf.csiro.au> * - fixed race when reading from pipe and reaping children * 2001-02-18 sam@quux.dropbear.id.au * - fixed bug in <get_path>: multiple INIT_PATH components did not work * 2001-02-21 Richard Gooch <rgooch@atnf.csiro.au> * - block signals in handlers, so that longjmp() doesn't kill context * 2001-02-25 Richard Gooch <rgooch@atnf.csiro.au> * - make default INIT_PATH the boot_prog (if it is a directory) - YECCH * 2002-11-20 patch from SuSE * - refuse initctl_fd if setting FD_CLOEXEC fails */#include <sys/types.h>#include <stdlib.h>#include <unistd.h>#include <stdio.h>#include <ctype.h>#include <fcntl.h>#include <string.h>#include <signal.h>#include <pwd.h>#include <sys/file.h>#include <sys/wait.h>#include <sys/stat.h>#include <sys/sysmacros.h>#include <sys/time.h>#include <sys/ioctl.h>#include <dirent.h>#include <termios.h>#include <utmp.h>#include <setjmp.h>#include <sched.h>#ifdef SHADOW_PWD# include <shadow.h>#endif#include "my_crypt.h"#include "pathnames.h"#include "linux_reboot.h"#include "xstrncpy.h"#include "nls.h"#include "simpleinit.h"#define CMDSIZ 150 /* max size of a line in inittab */#define NUMCMD 30 /* max number of lines in inittab */#define NUMTOK 20 /* max number of tokens in inittab command */#define PATH_SIZE (CMDSIZ+CMDSIZ+1)#define MAX_RESPAWN_RATE 5 /* number of respawns per 100 seconds */#define TZFILE "/etc/TZ"char tzone[CMDSIZ];/* #define DEBUGGING *//* Define this if you want init to ignore the termcap field in inittab for console ttys. *//* #define SPECIAL_CONSOLE_TERM */#define ever (;;)struct initline { pid_t pid; char tty[10]; char termcap[30]; char *toks[NUMTOK]; char line[CMDSIZ]; struct timeval last_start; signed long rate;};struct initline inittab[NUMCMD];int numcmd;int stopped = 0; /* are we stopped */static char boot_prog[PATH_SIZE] = _PATH_RC;static char script_prefix[PATH_SIZE] = "\0";static char final_prog[PATH_SIZE] = "\0";static char init_path[PATH_SIZE] = "\0";static int caught_sigint = 0;static int no_reboot = 0;static pid_t rc_child = -1;static const char *initctl_name = "/dev/initctl";static int initctl_fd = -1;static volatile int do_longjmp = 0;static sigjmp_buf jmp_env;static void do_single (void);static int do_rc_tty (const char *path);static int process_path (const char *path, int (*func) (const char *path), int ignore_dangling_symlink);static int preload_file (const char *path);static int run_file (const char *path);static void spawn (int i), read_inittab (void);static void sighup_handler (int sig);static void sigtstp_handler (int sig);static void sigint_handler (int sig);static void sigchild_handler (int sig);static void sigquit_handler (int sig);static void sigterm_handler (int sig);#ifdef SET_TZstatic void set_tz (void);#endifstatic void write_wtmp (void);static pid_t mywait (int *status);static int run_command (const char *file, const char *name, pid_t pid);static void err (char *s){ int fd; if((fd = open("/dev/console", O_WRONLY)) < 0) return; write(fd, "init: ", 6); write(fd, s, strlen(s)); close(fd);}static void enter_single (void){ pid_t pid; int i; err(_("Booting to single user mode.\n")); if((pid = fork()) == 0) { /* the child */ execl(_PATH_BSHELL, _PATH_BSHELL, NULL); err(_("exec of single user shell failed\n")); } else if(pid > 0) { while (waitpid (pid, &i, 0) != pid) /* Nothing */; } else if(pid < 0) { err(_("fork of single user shell failed\n")); } unlink(_PATH_SINGLE);}int main(int argc, char *argv[]){ int vec, i; int want_single = 0; pid_t pid; struct sigaction sa;#ifdef SET_TZ set_tz();#endif sigfillset (&sa.sa_mask); /* longjmp and nested signals don't mix */ sa.sa_flags = SA_ONESHOT; sa.sa_handler = sigint_handler; sigaction (SIGINT, &sa, NULL); sa.sa_flags = 0; sa.sa_handler = sigtstp_handler; sigaction (SIGTSTP, &sa, NULL); sa.sa_handler = sigterm_handler; sigaction (SIGTERM, &sa, NULL); sa.sa_handler = sigchild_handler; sigaction (SIGCHLD, &sa, NULL); sa.sa_handler = sigquit_handler; sigaction (SIGQUIT, &sa, NULL); setlocale(LC_ALL, ""); bindtextdomain(PACKAGE, LOCALEDIR); textdomain(PACKAGE); my_reboot (LINUX_REBOOT_CMD_CAD_OFF); /* Find script to run. Command-line overrides config file overrides built-in default */ for (i = 0; i < NUMCMD; i++) inittab[i].pid = -1; read_inittab (); for (i = 1; i < argc; i++) { if (strcmp (argv[i], "single") == 0) want_single = 1; else if (strcmp (argv[i], "-noreboot") == 0) no_reboot = 1; else if (strlen(script_prefix) + strlen(argv[i]) < PATH_SIZE) { char path[PATH_SIZE]; strcpy (path, script_prefix); strcat (path, argv[i]); if (access (path, R_OK | X_OK) == 0) strcpy (boot_prog, path); } } if (init_path[0] == '\0') { struct stat statbuf; if ( (stat (boot_prog, &statbuf) == 0) && S_ISDIR (statbuf.st_mode) ) { strcpy (init_path, boot_prog); i = strlen (init_path); if (init_path[i - 1] == '/') init_path[i - 1] = '\0'; } } if ( ( initctl_fd = open (initctl_name, O_RDWR, 0) ) < 0 ) { mkfifo (initctl_name, S_IRUSR | S_IWUSR); if ( ( initctl_fd = open (initctl_name, O_RDWR, 0) ) < 0 ) err ( _("error opening fifo\n") ); } if (initctl_fd >= 0 && fcntl(initctl_fd, F_SETFD, FD_CLOEXEC) != 0) { err ( _("error setting close-on-exec on /dev/initctl") ); /* Can the fcntl ever fail? If it does, and we leave the descriptor open in child processes, then any process on the system will be able to write to /dev/initctl and have us execute arbitrary commands as root. So let's refuse to use the fifo in this case. */ close(initctl_fd); initctl_fd = -1; } if ( want_single || (access (_PATH_SINGLE, R_OK) == 0) ) do_single (); /*If we get a SIGTSTP before multi-user mode, do nothing*/ while (stopped) pause(); if ( do_rc_tty (boot_prog) ) do_single (); while (stopped) /* Also if /etc/rc fails & we get SIGTSTP */ pause(); write_wtmp(); /* write boottime record */#ifdef DEBUGGING for(i = 0; i < numcmd; i++) { char **p; p = inittab[i].toks; printf("toks= %s %s %s %s\n",p[0], p[1], p[2], p[3]); printf("tty= %s\n", inittab[i].tty); printf("termcap= %s\n", inittab[i].termcap); } exit(0);#endif signal (SIGHUP, sighup_handler); /* Better semantics with signal(2) */ for (i = 0; i < getdtablesize (); i++) if (i != initctl_fd) close (i); for(i = 0; i < numcmd; i++) spawn(i); if (final_prog[0] != '\0') { switch ( fork () ) { case 0: /* Child */ execl (final_prog, final_prog, "start", NULL); err ( _("error running finalprog\n") ); _exit (1); break; case -1: /* Error */ err ( _("error forking finalprog\n") ); break; default: /* Parent */ break; } } for ever { pid = mywait (&vec); if (pid < 1) continue; /* clear utmp entry, and append to wtmp if possible */ { struct utmp *ut; int ut_fd, lf; utmpname(_PATH_UTMP); setutent(); while((ut = getutent())) { if(ut->ut_pid == pid) { time(&ut->ut_time); memset(&ut->ut_user, 0, UT_NAMESIZE); memset(&ut->ut_host, 0, sizeof(ut->ut_host)); ut->ut_type = DEAD_PROCESS; ut->ut_pid = 0; ut->ut_addr = 0; /*endutent();*/ pututline(ut); if ((lf = open(_PATH_WTMPLOCK, O_CREAT|O_WRONLY, 0660)) >= 0) { flock(lf, LOCK_EX|LOCK_NB); if((ut_fd = open(_PATH_WTMP, O_APPEND|O_WRONLY)) >= 0) { write(ut_fd, ut, sizeof(struct utmp)); close(ut_fd); } flock(lf, LOCK_UN|LOCK_NB); close(lf); } break; } } endutent(); } for(i = 0; i < numcmd; i++) { if(pid == inittab[i].pid || inittab[i].pid < 0) { if (stopped) inittab[i].pid = -1; else spawn(i); if (pid == inittab[i].pid) break; } } }} #define MAXTRIES 3 /* number of tries allowed when giving the password *//* * return true if singleuser mode is allowed. * If /etc/securesingle exists ask for root password, otherwise always OK. */static int check_single_ok (void){ char *pass, *rootpass = NULL; struct passwd *pwd; int i; if (access (_PATH_SECURE, R_OK) != 0) return 1; if ( ( pwd = getpwnam ("root") ) || ( pwd = getpwuid (0) ) ) rootpass = pwd->pw_passwd; else return 1; /* a bad /etc/passwd should not lock out */ for (i = 0; i < MAXTRIES; i++) { pass = getpass (_ ("Password: ") ); if (pass == NULL) continue; if ( !strcmp (crypt (pass, rootpass), rootpass) ) return 1; puts (_ ("\nWrong password.\n") ); } return 0;}static void do_single (void){ char path[PATH_SIZE]; if (caught_sigint) return; strcpy (path, script_prefix); strcat (path, "single"); if (access (path, R_OK | X_OK) == 0) if (do_rc_tty (path) == 0) return; if ( check_single_ok () ) enter_single ();} /* End Function do_single *//* * run boot script(s). The environment is passed to the script(s), so the RC * environment variable can be used to decide what to do. * RC may be set from LILO. * [RETURNS] 0 on success (exit status convention), otherwise error. */static int do_rc_tty (const char *path){ int status; pid_t pid; sigset_t ss; if (caught_sigint) return 0; process_path (path, preload_file, 0); /* Launch off a subprocess to start a new session (required for frobbing the TTY) and capture control-C */ switch ( rc_child = fork () ) { case 0: /* Child */ for (status = 1; status < NSIG; status++) signal (status, SIG_DFL); sigfillset (&ss); sigprocmask (SIG_UNBLOCK, &ss, NULL); sigdelset (&ss, SIGINT); sigdelset (&ss, SIGQUIT); setsid (); ioctl (0, TIOCSCTTY, 0); /* I want my control-C */ sigsuspend (&ss); /* Should never return, should just be killed */ break; /* No-one else is controlled by this TTY now */ case -1: /* Error */ return (1); /*break;*/ default: /* Parent */ break; } /* Parent */ process_path (path, run_file, 0); while (1) { if ( ( pid = mywait (&status) ) == rc_child ) return (WTERMSIG (status) == SIGINT) ? 0 : 1; if (pid < 0) break; } kill (rc_child, SIGKILL); while (waitpid (rc_child, NULL, 0) != rc_child) /* Nothing */; return 0;} /* End Function do_rc_tty */static int process_path (const char *path, int (*func) (const char *path), int ignore_dangling_symlink){ struct stat statbuf; DIR *dp; struct dirent *de; if (lstat (path, &statbuf) != 0)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -