jail: fix and simplify userns uid/gid maps from OCI
[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 LOG("- ubus -\n");
144 p = getpwnam("ubus");
145 if (p) {
146 mkdir(p->pw_dir, 0755);
147 chown(p->pw_dir, p->pw_uid, p->pw_gid);
148 }
149 procd_connect_ubus();
150 service_start_early("ubus", ubus_cmd, p?"ubus":NULL, p?"ubus":NULL);
151 break;
152
153 case STATE_INIT:
154 LOG("- init -\n");
155 procd_inittab();
156 procd_inittab_run("respawn");
157 procd_inittab_run("askconsole");
158 procd_inittab_run("askfirst");
159 procd_inittab_run("sysinit");
160
161 // switch to syslog log channel
162 ulog_open(ULOG_SYSLOG, LOG_DAEMON, "procd");
163 break;
164
165 case STATE_RUNNING:
166 LOG("- init complete -\n");
167 procd_inittab_run("respawnlate");
168 procd_inittab_run("askconsolelate");
169 break;
170
171 case STATE_SHUTDOWN:
172 /* Redirect output to the console for the users' benefit */
173 set_console();
174 LOG("- shutdown -\n");
175 procd_inittab_run("shutdown");
176 sync();
177 break;
178
179 case STATE_HALT:
180 // To prevent killed processes from interrupting the sleep
181 signal(SIGCHLD, SIG_IGN);
182 LOG("- SIGTERM processes -\n");
183 kill(-1, SIGTERM);
184 sync();
185 sleep(1);
186 LOG("- SIGKILL processes -\n");
187 kill(-1, SIGKILL);
188 sync();
189 sleep(1);
190 #ifndef DISABLE_INIT
191 perform_halt();
192 #else
193 exit(EXIT_SUCCESS);
194 #endif
195 break;
196
197 default:
198 ERROR("Unhandled state %d\n", state);
199 return;
200 };
201 }
202
203 void procd_state_next(void)
204 {
205 DEBUG(4, "Change state %d -> %d\n", state, state + 1);
206 state++;
207 state_enter();
208 }
209
210 void procd_state_ubus_connect(void)
211 {
212 if (state == STATE_UBUS)
213 procd_state_next();
214 }
215
216 void procd_shutdown(int event)
217 {
218 if (state >= STATE_SHUTDOWN)
219 return;
220 DEBUG(2, "Shutting down system with event %x\n", event);
221 reboot_event = event;
222 state = STATE_SHUTDOWN;
223 state_enter();
224 }