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