init.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511
  1. /* init.c - init program.
  2. *
  3. * Copyright 2012 Harvind Singh <harvindsingh1981@gmail.com>
  4. * Copyright 2013 Kyungwan Han <asura321@gmail.com>
  5. *
  6. * No Standard
  7. USE_INIT(NEWTOY(init, "", TOYFLAG_SBIN))
  8. config INIT
  9. bool "init"
  10. default n
  11. help
  12. usage: init
  13. System V style init.
  14. First program to run (as PID 1) when the system comes up, reading
  15. /etc/inittab to determine actions.
  16. */
  17. #include "toys.h"
  18. #include <sys/reboot.h>
  19. struct action_list_seed {
  20. struct action_list_seed *next;
  21. pid_t pid;
  22. uint8_t action;
  23. char *terminal_name;
  24. char *command;
  25. } *action_list_pointer = NULL;
  26. int caught_signal;
  27. //INITTAB action defination
  28. #define SYSINIT 0x01
  29. #define WAIT 0x02
  30. #define ONCE 0x04
  31. #define RESPAWN 0x08
  32. #define ASKFIRST 0x10
  33. #define CTRLALTDEL 0x20
  34. #define SHUTDOWN 0x40
  35. #define RESTART 0x80
  36. static void initialize_console(void)
  37. {
  38. int fd;
  39. char *p = getenv("CONSOLE");
  40. if (!p) p = getenv("console");
  41. if (!p) {
  42. fd = open("/dev/null", O_RDWR);
  43. if (fd >= 0) {
  44. while (fd < 2) fd = dup(fd);
  45. while (fd > 2) close(fd--);
  46. }
  47. } else {
  48. fd = open(p, O_RDWR | O_NONBLOCK | O_NOCTTY);
  49. if (fd < 0) printf("Unable to open console %s\n",p);
  50. else {
  51. dup2(fd,0);
  52. dup2(fd,1);
  53. dup2(fd,2);
  54. }
  55. }
  56. if (!getenv("TERM")) putenv("TERM=linux");
  57. }
  58. static void reset_term(int fd)
  59. {
  60. struct termios terminal;
  61. tcgetattr(fd, &terminal);
  62. terminal.c_cc[VINTR] = 3; //ctrl-c
  63. terminal.c_cc[VQUIT] = 28; /*ctrl-\*/
  64. terminal.c_cc[VERASE] = 127; //ctrl-?
  65. terminal.c_cc[VKILL] = 21; //ctrl-u
  66. terminal.c_cc[VEOF] = 4; //ctrl-d
  67. terminal.c_cc[VSTART] = 17; //ctrl-q
  68. terminal.c_cc[VSTOP] = 19; //ctrl-s
  69. terminal.c_cc[VSUSP] = 26; //ctrl-z
  70. terminal.c_line = 0;
  71. terminal.c_cflag &= CRTSCTS|PARODD|PARENB|CSTOPB|CSIZE|CBAUDEX|CBAUD;
  72. terminal.c_cflag |= CLOCAL|HUPCL|CREAD;
  73. //enable start/stop input and output control + map CR to NL on input
  74. terminal.c_iflag = IXON|IXOFF|ICRNL;
  75. //Map NL to CR-NL on output
  76. terminal.c_oflag = ONLCR|OPOST;
  77. terminal.c_lflag = IEXTEN|ECHOKE|ECHOCTL|ECHOK|ECHOE|ECHO|ICANON|ISIG;
  78. tcsetattr(fd, TCSANOW, &terminal);
  79. }
  80. static void add_new_action(int action, char *command, char *term)
  81. {
  82. struct action_list_seed *x,**y;
  83. y = &action_list_pointer;
  84. x = *y;
  85. while (x) {
  86. if (!(strcmp(x->command, command)) && !(strcmp(x->terminal_name, term))) {
  87. *y = x->next; //remove from the list
  88. while(*y) y = &(*y)->next; //traverse through list till end
  89. x->next = NULL;
  90. break;
  91. }
  92. y = &(x)->next;
  93. x = *y;
  94. }
  95. //create a new node
  96. if (!x) {
  97. x = xzalloc(sizeof(*x));
  98. x->command = xstrdup(command);
  99. x->terminal_name = xstrdup(term);
  100. }
  101. x->action = action;
  102. *y = x;
  103. }
  104. static void parse_inittab(void)
  105. {
  106. char *line = 0;
  107. size_t allocated_length = 0;
  108. int line_number = 0;
  109. char *act_name = "sysinit\0wait\0once\0respawn\0askfirst\0ctrlaltdel\0"
  110. "shutdown\0restart\0";
  111. FILE *fp = fopen("/etc/inittab", "r");
  112. if (!fp) {
  113. error_msg("Unable to open /etc/inittab. Using Default inittab");
  114. add_new_action(SYSINIT, "/etc/init.d/rcS", "");
  115. add_new_action(RESPAWN, "/sbin/getty -n -l /bin/sh -L 115200 tty1 vt100", "");
  116. return;
  117. }
  118. while (getline(&line, &allocated_length, fp) > 0) {
  119. char *p = line, *x, *tty_name = 0, *command = 0, *extracted_token, *tmp;
  120. int action = 0, token_count = 0, i;
  121. if ((x = strchr(p, '#'))) *x = '\0';
  122. line_number++;
  123. action = 0;
  124. while ((extracted_token = strsep(&p,":"))) {
  125. token_count++;
  126. switch (token_count) {
  127. case 1:
  128. if (*extracted_token) {
  129. if (!strncmp(extracted_token, "/dev/", 5))
  130. tty_name = xmprintf("%s",extracted_token);
  131. else tty_name = xmprintf("/dev/%s",extracted_token);
  132. } else tty_name = xstrdup("");
  133. break;
  134. case 2:
  135. break;
  136. case 3:
  137. for (tmp = act_name, i = 0; *tmp; i++, tmp += strlen(tmp) +1) {
  138. if (!strcmp(tmp, extracted_token)) {
  139. action = 1 << i;
  140. break;
  141. }
  142. }
  143. if (!*tmp) error_msg("Invalid action at line number %d ---- ignoring",line_number);
  144. break;
  145. case 4:
  146. command = xstrdup(extracted_token);
  147. break;
  148. default:
  149. error_msg("Bad inittab entry at line %d", line_number);
  150. break;
  151. }
  152. } //while token
  153. if (token_count == 4 && action) add_new_action(action, command, tty_name);
  154. free(tty_name);
  155. free(command);
  156. }
  157. free(line);
  158. fclose(fp);
  159. }
  160. static void reload_inittab(void)
  161. {
  162. // Remove all inactive actions, then reload /etc/inittab
  163. struct action_list_seed **y;
  164. y = &action_list_pointer;
  165. while (*y) {
  166. if (!(*y)->pid) {
  167. struct action_list_seed *x = *y;
  168. free(x->terminal_name);
  169. free(x->command);
  170. *y = (*y)->next;
  171. free(x);
  172. continue;
  173. }
  174. y = &(*y)->next;
  175. }
  176. parse_inittab();
  177. }
  178. static void run_command(char *command)
  179. {
  180. char *final_command[128];
  181. int hyphen = (command[0]=='-');
  182. command = command + hyphen;
  183. if (!strpbrk(command, "?<>'\";[]{}\\|=()*&^$!`~")) {
  184. char *next_command;
  185. char *extracted_command;
  186. int x = 0;
  187. next_command = strncpy(toybuf, command - hyphen, sizeof(toybuf));
  188. next_command[sizeof(toybuf) - 1] = toybuf[sizeof(toybuf) - 1 ] = '\0';
  189. command = next_command + hyphen;
  190. while ((extracted_command = strsep(&next_command," \t"))) {
  191. if (*extracted_command) {
  192. final_command[x] = extracted_command;
  193. x++;
  194. }
  195. }
  196. final_command[x] = NULL;
  197. } else {
  198. snprintf(toybuf, sizeof(toybuf), "exec %s", command);
  199. command = "-/bin/sh"+1;
  200. final_command[0] = ("-/bin/sh"+!hyphen);
  201. final_command[1] = "-c";
  202. final_command[2] = toybuf;
  203. final_command[3] = NULL;
  204. }
  205. if (hyphen) ioctl(0, TIOCSCTTY, 0);
  206. execvp(command, final_command);
  207. error_msg("unable to run %s",command);
  208. }
  209. //runs all same type of actions
  210. static pid_t final_run(struct action_list_seed *x)
  211. {
  212. pid_t pid;
  213. int fd;
  214. sigset_t signal_set;
  215. sigfillset(&signal_set);
  216. sigprocmask(SIG_BLOCK, &signal_set, NULL);
  217. if (x->action & ASKFIRST) pid = fork();
  218. else pid = vfork();
  219. if (pid > 0) {
  220. //parent process or error
  221. //unblock the signals
  222. sigfillset(&signal_set);
  223. sigprocmask(SIG_UNBLOCK, &signal_set, NULL);
  224. return pid;
  225. } else if (pid < 0) {
  226. perror_msg("fork fail");
  227. sleep(1);
  228. return 0;
  229. }
  230. //new born child process
  231. sigset_t signal_set_c;
  232. sigfillset(&signal_set_c);
  233. sigprocmask(SIG_UNBLOCK, &signal_set_c, NULL);
  234. setsid(); //new session
  235. if (x->terminal_name[0]) {
  236. close(0);
  237. fd = open(x->terminal_name, (O_RDWR|O_NONBLOCK),0600);
  238. if (fd != 0) {
  239. error_msg("Unable to open %s,%s\n", x->terminal_name, strerror(errno));
  240. _exit(EXIT_FAILURE);
  241. } else {
  242. dup2(0, 1);
  243. dup2(0, 2);
  244. }
  245. }
  246. reset_term(0);
  247. run_command(x->command);
  248. _exit(-1);
  249. }
  250. static struct action_list_seed* mark_as_terminated_process(pid_t pid)
  251. {
  252. struct action_list_seed *x;
  253. if (pid > 0) {
  254. for (x = action_list_pointer; x; x = x->next) {
  255. if (x->pid == pid) {
  256. x->pid = 0;
  257. return x;
  258. }
  259. }
  260. }
  261. return NULL;
  262. }
  263. static void waitforpid(pid_t pid)
  264. {
  265. if (pid <= 0) return;
  266. while (!kill(pid, 0)) mark_as_terminated_process(wait(NULL));
  267. }
  268. static void run_action_from_list(int action)
  269. {
  270. pid_t pid;
  271. struct action_list_seed *x = action_list_pointer;
  272. for (; x; x = x->next) {
  273. if (!(x->action & action)) continue;
  274. if (x->action & (SHUTDOWN|ONCE|SYSINIT|CTRLALTDEL|WAIT)) {
  275. pid = final_run(x);
  276. if (!pid) return;
  277. if (x->action & (SHUTDOWN|SYSINIT|CTRLALTDEL|WAIT)) waitforpid(pid);
  278. }
  279. if (x->action & (ASKFIRST|RESPAWN))
  280. if (!(x->pid)) x->pid = final_run(x);
  281. }
  282. }
  283. static void set_default(void)
  284. {
  285. sigset_t signal_set_c;
  286. xsignal_all_killers(SIG_DFL);
  287. sigfillset(&signal_set_c);
  288. sigprocmask(SIG_UNBLOCK,&signal_set_c, NULL);
  289. run_action_from_list(SHUTDOWN);
  290. error_msg("The system is going down NOW!");
  291. kill(-1, SIGTERM);
  292. error_msg("Sent SIGTERM to all processes");
  293. sync();
  294. sleep(1);
  295. kill(-1,SIGKILL);
  296. sync();
  297. }
  298. static void halt_poweroff_reboot_handler(int sig_no)
  299. {
  300. unsigned int reboot_magic_no = 0;
  301. pid_t pid;
  302. set_default();
  303. switch (sig_no) {
  304. case SIGUSR1:
  305. error_msg("Requesting system halt");
  306. reboot_magic_no=RB_HALT_SYSTEM;
  307. break;
  308. case SIGUSR2:
  309. error_msg("Requesting system poweroff");
  310. reboot_magic_no=RB_POWER_OFF;
  311. break;
  312. case SIGTERM:
  313. error_msg("Requesting system reboot");
  314. reboot_magic_no=RB_AUTOBOOT;
  315. break;
  316. default:
  317. break;
  318. }
  319. sleep(1);
  320. pid = vfork();
  321. if (pid == 0) {
  322. reboot(reboot_magic_no);
  323. _exit(EXIT_SUCCESS);
  324. }
  325. while(1) sleep(1);
  326. }
  327. static void restart_init_handler(int sig_no)
  328. {
  329. struct action_list_seed *x;
  330. pid_t pid;
  331. int fd;
  332. for (x = action_list_pointer; x; x = x->next) {
  333. if (!(x->action & RESTART)) continue;
  334. set_default();
  335. if (x->terminal_name[0]) {
  336. close(0);
  337. fd = open(x->terminal_name, (O_RDWR|O_NONBLOCK),0600);
  338. if (fd != 0) {
  339. error_msg("Unable to open %s,%s\n", x->terminal_name, strerror(errno));
  340. sleep(1);
  341. pid = vfork();
  342. if (pid == 0) {
  343. reboot(RB_HALT_SYSTEM);
  344. _exit(EXIT_SUCCESS);
  345. }
  346. while(1) sleep(1);
  347. } else {
  348. dup2(0, 1);
  349. dup2(0, 2);
  350. reset_term(0);
  351. run_command(x->command);
  352. }
  353. }
  354. }
  355. }
  356. static void catch_signal(int sig_no)
  357. {
  358. caught_signal = sig_no;
  359. error_msg("signal seen: %d", sig_no);
  360. }
  361. static void pause_handler(int sig_no)
  362. {
  363. int signal_backup,errno_backup;
  364. pid_t pid;
  365. errno_backup = errno;
  366. signal_backup = caught_signal;
  367. xsignal(SIGCONT, catch_signal);
  368. while(1) {
  369. if (caught_signal == SIGCONT) break;
  370. do pid = waitpid(-1,NULL,WNOHANG); while((pid==-1) && (errno=EINTR));
  371. mark_as_terminated_process(pid);
  372. sleep(1);
  373. }
  374. signal(SIGCONT, SIG_DFL);
  375. errno = errno_backup;
  376. caught_signal = signal_backup;
  377. }
  378. static int check_if_pending_signals(void)
  379. {
  380. int signal_caught = 0;
  381. while(1) {
  382. int sig = caught_signal;
  383. if (!sig) return signal_caught;
  384. caught_signal = 0;
  385. signal_caught = 1;
  386. if (sig == SIGINT) run_action_from_list(CTRLALTDEL);
  387. else if (sig == SIGHUP) {
  388. error_msg("reloading inittab");
  389. reload_inittab();
  390. }
  391. }
  392. }
  393. void init_main(void)
  394. {
  395. struct sigaction sig_act;
  396. if (getpid() != 1) error_exit("Already running");
  397. printf("Started init\n");
  398. initialize_console();
  399. reset_term(0);
  400. if (chdir("/")) perror_exit("Can't cd to /");
  401. setsid();
  402. putenv("HOME=/");
  403. putenv("PATH=/sbin:/usr/sbin:/bin:/usr/bin");
  404. putenv("SHELL=/bin/sh");
  405. putenv("USER=root");
  406. parse_inittab();
  407. xsignal(SIGUSR1, halt_poweroff_reboot_handler);//halt
  408. xsignal(SIGUSR2, halt_poweroff_reboot_handler);//poweroff
  409. xsignal(SIGTERM, halt_poweroff_reboot_handler);//reboot
  410. xsignal(SIGQUIT, restart_init_handler);//restart init
  411. memset(&sig_act, 0, sizeof(sig_act));
  412. sigfillset(&sig_act.sa_mask);
  413. sigdelset(&sig_act.sa_mask, SIGCONT);
  414. sig_act.sa_handler = pause_handler;
  415. sigaction(SIGTSTP, &sig_act, NULL);
  416. memset(&sig_act, 0, sizeof(sig_act));
  417. sig_act.sa_handler = catch_signal;
  418. sigaction(SIGINT, &sig_act, NULL);
  419. sigaction(SIGHUP, &sig_act, NULL);
  420. run_action_from_list(SYSINIT);
  421. check_if_pending_signals();
  422. run_action_from_list(WAIT);
  423. check_if_pending_signals();
  424. run_action_from_list(ONCE);
  425. while (1) {
  426. int suspected_WNOHANG = check_if_pending_signals();
  427. run_action_from_list(RESPAWN | ASKFIRST);
  428. suspected_WNOHANG = suspected_WNOHANG|check_if_pending_signals();
  429. sleep(1);//let cpu breath
  430. suspected_WNOHANG = suspected_WNOHANG|check_if_pending_signals();
  431. if (suspected_WNOHANG) suspected_WNOHANG=WNOHANG;
  432. while(1) {
  433. pid_t pid = waitpid(-1, NULL, suspected_WNOHANG);
  434. if (pid <= 0) break;
  435. mark_as_terminated_process(pid);
  436. suspected_WNOHANG = WNOHANG;
  437. }
  438. }
  439. }