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