File: | state.c |
Warning: | line 117, column 3 This function call is prohibited after a successful vfork |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
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 | ||||
15 | #include <fcntl.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 | ||||
23 | #include "container.h" | |||
24 | #include "procd.h" | |||
25 | #include "syslog.h" | |||
26 | #include "plug/hotplug.h" | |||
27 | #include "watchdog.h" | |||
28 | #include "service/service.h" | |||
29 | #include "utils/utils.h" | |||
30 | ||||
31 | enum { | |||
32 | STATE_NONE = 0, | |||
33 | STATE_EARLY, | |||
34 | STATE_UBUS, | |||
35 | STATE_INIT, | |||
36 | STATE_RUNNING, | |||
37 | STATE_SHUTDOWN, | |||
38 | STATE_HALT, | |||
39 | __STATE_MAX, | |||
40 | }; | |||
41 | ||||
42 | static int state = STATE_NONE; | |||
43 | static int reboot_event; | |||
44 | ||||
45 | static void set_stdio(const char* tty) | |||
46 | { | |||
47 | if (chdir("/dev") || | |||
48 | !freopen(tty, "r", stdinstdin) || | |||
49 | !freopen(tty, "w", stdoutstdout) || | |||
50 | !freopen(tty, "w", stderrstderr) || | |||
51 | chdir("/")) | |||
52 | ERROR("failed to set stdio: %m\n")ulog(3, "failed to set stdio: %m\n"); | |||
53 | else | |||
54 | fcntl(STDERR_FILENO2, F_SETFL4, fcntl(STDERR_FILENO2, F_GETFL3) | O_NONBLOCK04000); | |||
55 | } | |||
56 | ||||
57 | static void set_console(void) | |||
58 | { | |||
59 | const char* tty; | |||
60 | char* split; | |||
61 | char line[ 20 ]; | |||
62 | const char* try[] = { "tty0", "console", NULL((void*)0) }; /* Try the most common outputs */ | |||
63 | int f, i = 0; | |||
64 | ||||
65 | tty = get_cmdline_val("console",line,sizeof(line)); | |||
66 | if (tty != NULL((void*)0)) { | |||
67 | split = strchr(tty, ','); | |||
68 | if ( split != NULL((void*)0) ) | |||
69 | *split = '\0'; | |||
70 | } else { | |||
71 | // Try a default | |||
72 | tty=try[i]; | |||
73 | i++; | |||
74 | } | |||
75 | ||||
76 | if (chdir("/dev")) { | |||
77 | ERROR("failed to change dir to /dev: %m\n")ulog(3, "failed to change dir to /dev: %m\n"); | |||
78 | return; | |||
79 | } | |||
80 | while (tty!=NULL((void*)0)) { | |||
81 | f = open(tty, O_RDONLY00); | |||
82 | if (f >= 0) { | |||
83 | close(f); | |||
84 | break; | |||
85 | } | |||
86 | ||||
87 | tty=try[i]; | |||
88 | i++; | |||
89 | } | |||
90 | if (chdir("/")) | |||
91 | ERROR("failed to change dir to /: %m\n")ulog(3, "failed to change dir to /: %m\n"); | |||
92 | ||||
93 | if (tty != NULL((void*)0)) | |||
94 | set_stdio(tty); | |||
95 | } | |||
96 | ||||
97 | static void perform_halt() | |||
98 | { | |||
99 | if (reboot_event == RB_POWER_OFF0x4321fedc) | |||
| ||||
100 | LOG("- power down -\n")ulog(6, "- power down -\n"); | |||
101 | else | |||
102 | LOG("- reboot -\n")ulog(6, "- reboot -\n"); | |||
103 | ||||
104 | /* Allow time for last message to reach serial console, etc */ | |||
105 | sleep(1); | |||
106 | ||||
107 | if (is_container()) { | |||
108 | reboot(reboot_event); | |||
109 | exit(EXIT_SUCCESS0); | |||
110 | return; | |||
111 | } | |||
112 | ||||
113 | /* We have to fork here, since the kernel calls do_exit(EXIT_SUCCESS) | |||
114 | * in linux/kernel/sys.c, which can cause the machine to panic when | |||
115 | * the init process exits... */ | |||
116 | if (!vfork()) { /* child */ | |||
117 | reboot(reboot_event); | |||
| ||||
118 | _exit(EXIT_SUCCESS0); | |||
119 | } | |||
120 | ||||
121 | while (1) | |||
122 | sleep(1); | |||
123 | } | |||
124 | ||||
125 | static void state_enter(void) | |||
126 | { | |||
127 | char ubus_cmd[] = "/sbin/ubusd"; | |||
128 | ||||
129 | switch (state) { | |||
130 | case STATE_EARLY: | |||
131 | LOG("- early -\n")ulog(6, "- early -\n"); | |||
132 | watchdog_init(0); | |||
133 | hotplug("/etc/hotplug.json"); | |||
134 | procd_coldplug(); | |||
135 | break; | |||
136 | ||||
137 | case STATE_UBUS: | |||
138 | // try to reopen incase the wdt was not available before coldplug | |||
139 | watchdog_init(0); | |||
140 | set_stdio("console"); | |||
141 | LOG("- ubus -\n")ulog(6, "- ubus -\n"); | |||
142 | procd_connect_ubus(); | |||
143 | service_start_early("ubus", ubus_cmd); | |||
144 | break; | |||
145 | ||||
146 | case STATE_INIT: | |||
147 | LOG("- init -\n")ulog(6, "- init -\n"); | |||
148 | procd_inittab(); | |||
149 | procd_inittab_run("respawn"); | |||
150 | procd_inittab_run("askconsole"); | |||
151 | procd_inittab_run("askfirst"); | |||
152 | procd_inittab_run("sysinit"); | |||
153 | ||||
154 | // switch to syslog log channel | |||
155 | ulog_open(ULOG_SYSLOG, LOG_DAEMON(3<<3), "procd"); | |||
156 | break; | |||
157 | ||||
158 | case STATE_RUNNING: | |||
159 | LOG("- init complete -\n")ulog(6, "- init complete -\n"); | |||
160 | procd_inittab_run("respawnlate"); | |||
161 | procd_inittab_run("askconsolelate"); | |||
162 | break; | |||
163 | ||||
164 | case STATE_SHUTDOWN: | |||
165 | /* Redirect output to the console for the users' benefit */ | |||
166 | set_console(); | |||
167 | LOG("- shutdown -\n")ulog(6, "- shutdown -\n"); | |||
168 | procd_inittab_run("shutdown"); | |||
169 | sync(); | |||
170 | break; | |||
171 | ||||
172 | case STATE_HALT: | |||
173 | // To prevent killed processes from interrupting the sleep | |||
174 | signal(SIGCHLD17, SIG_IGN((__sighandler_t) 1)); | |||
175 | LOG("- SIGTERM processes -\n")ulog(6, "- SIGTERM processes -\n"); | |||
176 | kill(-1, SIGTERM15); | |||
177 | sync(); | |||
178 | sleep(1); | |||
179 | LOG("- SIGKILL processes -\n")ulog(6, "- SIGKILL processes -\n"); | |||
180 | kill(-1, SIGKILL9); | |||
181 | sync(); | |||
182 | sleep(1); | |||
183 | #ifndef DISABLE_INIT | |||
184 | perform_halt(); | |||
185 | #else | |||
186 | exit(EXIT_SUCCESS0); | |||
187 | #endif | |||
188 | break; | |||
189 | ||||
190 | default: | |||
191 | ERROR("Unhandled state %d\n", state)ulog(3, "Unhandled state %d\n", state); | |||
192 | return; | |||
193 | }; | |||
194 | } | |||
195 | ||||
196 | void procd_state_next(void) | |||
197 | { | |||
198 | DEBUG(4, "Change state %d -> %d\n", state, state + 1)do { if (debug >= 4) { ulog(5, "Change state %d -> %d\n" , state, state + 1); } } while (0); | |||
199 | state++; | |||
200 | state_enter(); | |||
201 | } | |||
202 | ||||
203 | void procd_state_ubus_connect(void) | |||
204 | { | |||
205 | if (state == STATE_UBUS) | |||
206 | procd_state_next(); | |||
207 | } | |||
208 | ||||
209 | void procd_shutdown(int event) | |||
210 | { | |||
211 | if (state >= STATE_SHUTDOWN) | |||
212 | return; | |||
213 | DEBUG(2, "Shutting down system with event %x\n", event)do { if (debug >= 2) { ulog(5, "Shutting down system with event %x\n" , event); } } while (0); | |||
214 | reboot_event = event; | |||
215 | state = STATE_SHUTDOWN; | |||
216 | state_enter(); | |||
217 | } |