state.c 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230
  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. int ret;
  128. LOG("- ubus -\n");
  129. mkdir(p->pw_dir, 0755);
  130. ret = chown(p->pw_dir, p->pw_uid, p->pw_gid);
  131. if (ret)
  132. LOG("- ubus - failed to chown(%s)\n", p->pw_dir);
  133. } else {
  134. LOG("- ubus (running as root!) -\n");
  135. }
  136. procd_connect_ubus();
  137. service_start_early("ubus", ubus_cmd, p?"ubus":NULL, p?"ubus":NULL);
  138. break;
  139. case STATE_INIT:
  140. LOG("- init -\n");
  141. procd_inittab();
  142. procd_inittab_run("respawn");
  143. procd_inittab_run("askconsole");
  144. procd_inittab_run("askfirst");
  145. procd_inittab_run("sysinit");
  146. // switch to syslog log channel
  147. ulog_open(ULOG_SYSLOG, LOG_DAEMON, "procd");
  148. break;
  149. case STATE_RUNNING:
  150. LOG("- init complete -\n");
  151. procd_inittab_run("respawnlate");
  152. procd_inittab_run("askconsolelate");
  153. break;
  154. case STATE_SHUTDOWN:
  155. /* Redirect output to the console for the users' benefit */
  156. set_console();
  157. LOG("- shutdown -\n");
  158. procd_inittab_run("shutdown");
  159. sync();
  160. break;
  161. case STATE_HALT:
  162. // To prevent killed processes from interrupting the sleep
  163. signal(SIGCHLD, SIG_IGN);
  164. LOG("- SIGTERM processes -\n");
  165. kill(-1, SIGTERM);
  166. sync();
  167. sleep(1);
  168. LOG("- SIGKILL processes -\n");
  169. kill(-1, SIGKILL);
  170. sync();
  171. sleep(1);
  172. #ifndef DISABLE_INIT
  173. perform_halt();
  174. #else
  175. exit(EXIT_SUCCESS);
  176. #endif
  177. break;
  178. default:
  179. ERROR("Unhandled state %d\n", state);
  180. return;
  181. };
  182. }
  183. void procd_state_next(void)
  184. {
  185. DEBUG(4, "Change state %d -> %d\n", state, state + 1);
  186. state++;
  187. state_enter();
  188. }
  189. void procd_state_ubus_connect(void)
  190. {
  191. if (state == STATE_UBUS)
  192. procd_state_next();
  193. }
  194. void procd_shutdown(int event)
  195. {
  196. if (state >= STATE_SHUTDOWN)
  197. return;
  198. DEBUG(2, "Shutting down system with event %x\n", event);
  199. reboot_event = event;
  200. state = STATE_SHUTDOWN;
  201. state_enter();
  202. }