state.c 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227
  1. /*
  2. * Copyright (C) 2013 Felix Fietkau <nbd@openwrt.org>
  3. * Copyright (C) 2013 John Crispin <blogic@openwrt.org>
  4. *
  5. * This program is free software; you can redistribute it and/or modify
  6. * it under the terms of the GNU Lesser General Public License version 2.1
  7. * as published by the Free Software Foundation
  8. *
  9. * This program is distributed in the hope that it will be useful,
  10. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. * GNU General Public License for more details.
  13. */
  14. #include <fcntl.h>
  15. #include <pwd.h>
  16. #include <sys/reboot.h>
  17. #include <stdio.h>
  18. #include <stdlib.h>
  19. #include <unistd.h>
  20. #include <sys/types.h>
  21. #include <signal.h>
  22. #include "container.h"
  23. #include "procd.h"
  24. #include "syslog.h"
  25. #include "plug/hotplug.h"
  26. #include "watchdog.h"
  27. #include "service/service.h"
  28. #include "utils/utils.h"
  29. enum {
  30. STATE_NONE = 0,
  31. STATE_EARLY,
  32. STATE_UBUS,
  33. STATE_INIT,
  34. STATE_RUNNING,
  35. STATE_SHUTDOWN,
  36. STATE_HALT,
  37. __STATE_MAX,
  38. };
  39. static int state = STATE_NONE;
  40. static int reboot_event;
  41. static void set_stdio(const char* tty)
  42. {
  43. if (chdir("/dev") ||
  44. !freopen(tty, "r", stdin) ||
  45. !freopen(tty, "w", stdout) ||
  46. !freopen(tty, "w", stderr) ||
  47. chdir("/"))
  48. ERROR("failed to set stdio: %m\n");
  49. else
  50. fcntl(STDERR_FILENO, F_SETFL, fcntl(STDERR_FILENO, F_GETFL) | O_NONBLOCK);
  51. }
  52. static void set_console(void)
  53. {
  54. const char* tty;
  55. char* split;
  56. char line[ 20 ];
  57. const char* try[] = { "tty0", "console", NULL }; /* Try the most common outputs */
  58. int f, i = 0;
  59. tty = get_cmdline_val("console",line,sizeof(line));
  60. if (tty != NULL) {
  61. split = strchr(tty, ',');
  62. if ( split != NULL )
  63. *split = '\0';
  64. } else {
  65. // Try a default
  66. tty=try[i];
  67. i++;
  68. }
  69. if (chdir("/dev")) {
  70. ERROR("failed to change dir to /dev: %m\n");
  71. return;
  72. }
  73. while (tty!=NULL) {
  74. f = open(tty, O_RDONLY);
  75. if (f >= 0) {
  76. close(f);
  77. break;
  78. }
  79. tty=try[i];
  80. i++;
  81. }
  82. if (chdir("/"))
  83. ERROR("failed to change dir to /: %m\n");
  84. if (tty != NULL)
  85. set_stdio(tty);
  86. }
  87. static void perform_halt()
  88. {
  89. if (reboot_event == RB_POWER_OFF)
  90. LOG("- power down -\n");
  91. else
  92. LOG("- reboot -\n");
  93. /* Allow time for last message to reach serial console, etc */
  94. sleep(1);
  95. if (is_container()) {
  96. reboot(reboot_event);
  97. exit(EXIT_SUCCESS);
  98. return;
  99. }
  100. /* We have to fork here, since the kernel calls do_exit(EXIT_SUCCESS)
  101. * in linux/kernel/sys.c, which can cause the machine to panic when
  102. * the init process exits... */
  103. if (!vfork()) { /* child */
  104. reboot(reboot_event);
  105. _exit(EXIT_SUCCESS);
  106. }
  107. while (1)
  108. sleep(1);
  109. }
  110. static void state_enter(void)
  111. {
  112. char ubus_cmd[] = "/sbin/ubusd";
  113. struct passwd *p;
  114. switch (state) {
  115. case STATE_EARLY:
  116. LOG("- early -\n");
  117. watchdog_init(0);
  118. hotplug("/etc/hotplug.json");
  119. procd_coldplug();
  120. break;
  121. case STATE_UBUS:
  122. // try to reopen incase the wdt was not available before coldplug
  123. watchdog_init(0);
  124. set_stdio("console");
  125. p = getpwnam("ubus");
  126. if (p) {
  127. LOG("- ubus -\n");
  128. mkdir(p->pw_dir, 0755);
  129. chown(p->pw_dir, p->pw_uid, p->pw_gid);
  130. } else {
  131. LOG("- ubus (running as root!) -\n");
  132. }
  133. procd_connect_ubus();
  134. service_start_early("ubus", ubus_cmd, p?"ubus":NULL, p?"ubus":NULL);
  135. break;
  136. case STATE_INIT:
  137. LOG("- init -\n");
  138. procd_inittab();
  139. procd_inittab_run("respawn");
  140. procd_inittab_run("askconsole");
  141. procd_inittab_run("askfirst");
  142. procd_inittab_run("sysinit");
  143. // switch to syslog log channel
  144. ulog_open(ULOG_SYSLOG, LOG_DAEMON, "procd");
  145. break;
  146. case STATE_RUNNING:
  147. LOG("- init complete -\n");
  148. procd_inittab_run("respawnlate");
  149. procd_inittab_run("askconsolelate");
  150. break;
  151. case STATE_SHUTDOWN:
  152. /* Redirect output to the console for the users' benefit */
  153. set_console();
  154. LOG("- shutdown -\n");
  155. procd_inittab_run("shutdown");
  156. sync();
  157. break;
  158. case STATE_HALT:
  159. // To prevent killed processes from interrupting the sleep
  160. signal(SIGCHLD, SIG_IGN);
  161. LOG("- SIGTERM processes -\n");
  162. kill(-1, SIGTERM);
  163. sync();
  164. sleep(1);
  165. LOG("- SIGKILL processes -\n");
  166. kill(-1, SIGKILL);
  167. sync();
  168. sleep(1);
  169. #ifndef DISABLE_INIT
  170. perform_halt();
  171. #else
  172. exit(EXIT_SUCCESS);
  173. #endif
  174. break;
  175. default:
  176. ERROR("Unhandled state %d\n", state);
  177. return;
  178. };
  179. }
  180. void procd_state_next(void)
  181. {
  182. DEBUG(4, "Change state %d -> %d\n", state, state + 1);
  183. state++;
  184. state_enter();
  185. }
  186. void procd_state_ubus_connect(void)
  187. {
  188. if (state == STATE_UBUS)
  189. procd_state_next();
  190. }
  191. void procd_shutdown(int event)
  192. {
  193. if (state >= STATE_SHUTDOWN)
  194. return;
  195. DEBUG(2, "Shutting down system with event %x\n", event);
  196. reboot_event = event;
  197. state = STATE_SHUTDOWN;
  198. state_enter();
  199. }