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 #define _GNU_SOURCE 16 #include <sys/types.h> 17 #include <sys/stat.h> 18 #include <sys/ioctl.h> 19 20 #include <fcntl.h> 21 #include <stdio.h> 22 #include <stdlib.h> 23 #include <unistd.h> 24 #include <regex.h> 25 #include <ctype.h> 26 27 #include <libubox/utils.h> 28 #include <libubox/list.h> 29 30 #include "utils/utils.h" 31 #include "procd.h" 32 #include "rcS.h" 33 34 #ifndef O_PATH 35 #define O_PATH 010000000 36 #endif 37 38 #define TAG_ID 0 39 #define TAG_RUNLVL 1 40 #define TAG_ACTION 2 41 #define TAG_PROCESS 3 42 43 #define MAX_ARGS 8 44 45 struct init_action; 46 char *console = NULL; 47 48 struct init_handler { 49 const char *name; 50 void (*cb) (struct init_action *a); 51 int multi; 52 }; 53 54 struct init_action { 55 struct list_head list; 56 57 char *id; 58 char *argv[MAX_ARGS]; 59 char *line; 60 61 struct init_handler *handler; 62 struct uloop_process proc; 63 64 int respawn; 65 struct uloop_timeout tout; 66 }; 67 68 static const char *tab = "/etc/inittab"; 69 static char *ask = "/sbin/askfirst"; 70 71 static LIST_HEAD(actions); 72 73 static int dev_exist(const char *dev) 74 { 75 int dfd, fd; 76 77 dfd = open("/dev", O_PATH|O_DIRECTORY); 78 79 if (dfd < 0) 80 return 0; 81 82 fd = openat(dfd, dev, O_RDONLY); 83 close(dfd); 84 85 if (fd < 0) 86 return 0; 87 88 close(fd); 89 return 1; 90 } 91 92 static void fork_worker(struct init_action *a) 93 { 94 pid_t p; 95 96 a->proc.pid = fork(); 97 if (!a->proc.pid) { 98 p = setsid(); 99 100 if (patch_stdio(a->id)) 101 ERROR("Failed to setup i/o redirection\n"); 102 103 ioctl(STDIN_FILENO, TIOCSCTTY, 1); 104 tcsetpgrp(STDIN_FILENO, p); 105 106 execvp(a->argv[0], a->argv); 107 ERROR("Failed to execute %s: %m\n", a->argv[0]); 108 exit(-1); 109 } 110 111 if (a->proc.pid > 0) { 112 DEBUG(4, "Launched new %s action, pid=%d\n", 113 a->handler->name, 114 (int) a->proc.pid); 115 uloop_process_add(&a->proc); 116 } 117 } 118 119 static void child_exit(struct uloop_process *proc, int ret) 120 { 121 struct init_action *a = container_of(proc, struct init_action, proc); 122 123 DEBUG(4, "pid:%d, exitcode:%d\n", proc->pid, ret); 124 proc->pid = 0; 125 126 if (a->respawn < 0) 127 return; 128 129 if (!dev_exist(a->id)) { 130 DEBUG(4, "Skipping respawn: device '%s' does not exist anymore\n", a->id); 131 return; 132 } 133 134 uloop_timeout_set(&a->tout, a->respawn); 135 } 136 137 static void respawn(struct uloop_timeout *tout) 138 { 139 struct init_action *a = container_of(tout, struct init_action, tout); 140 if (!a->proc.pid) 141 fork_worker(a); 142 } 143 144 static void rcdone(struct runqueue *q) 145 { 146 procd_state_next(); 147 } 148 149 static void runrc(struct init_action *a) 150 { 151 if (!a->argv[1] || !a->argv[2]) { 152 ERROR("valid format is rcS <S|K> <param>\n"); 153 return; 154 } 155 156 /* proceed even if no init or shutdown scripts run */ 157 if (rcS(a->argv[1], a->argv[2], rcdone)) 158 rcdone(NULL); 159 } 160 161 static void askfirst(struct init_action *a) 162 { 163 int i; 164 165 if (!dev_exist(a->id) || (console && !strcmp(console, a->id))) { 166 DEBUG(4, "Skipping %s\n", a->id); 167 return; 168 } 169 170 a->tout.cb = respawn; 171 /* shift arguments only if not yet done */ 172 if (a->argv[0] != ask) { 173 for (i = MAX_ARGS - 1; i >= 1; i--) 174 a->argv[i] = a->argv[i - 1]; 175 a->argv[0] = ask; 176 } 177 a->respawn = 500; 178 179 a->proc.cb = child_exit; 180 if (!a->proc.pid) 181 fork_worker(a); 182 } 183 184 static void askconsole(struct init_action *a) 185 { 186 char line[256], *tty, *split; 187 int i; 188 189 /* First, try console= on the kernel command line, 190 * then fallback to /sys/class/tty/console/active, 191 * which should work when linux,stdout-path (or equivalent) 192 * is in the device tree 193 */ 194 tty = get_cmdline_val("console", line, sizeof(line)); 195 if (tty == NULL) { 196 if (dev_exist("console")) 197 tty = "console"; 198 else 199 tty = get_active_console(line, sizeof(line)); 200 } 201 if (tty != NULL) { 202 split = strchr(tty, ','); 203 if (split != NULL) 204 *split = '\0'; 205 206 if (!dev_exist(tty)) { 207 DEBUG(4, "skipping %s\n", tty); 208 return; 209 } 210 211 console = strdup(tty); 212 a->id = strdup(tty); 213 } 214 else { 215 console = NULL; 216 a->id = NULL; 217 } 218 219 a->tout.cb = respawn; 220 /* shift arguments only if not yet done */ 221 if (a->argv[0] != ask) { 222 for (i = MAX_ARGS - 1; i >= 1; i--) 223 a->argv[i] = a->argv[i - 1]; 224 a->argv[0] = ask; 225 } 226 a->respawn = 500; 227 228 a->proc.cb = child_exit; 229 if (!a->proc.pid) 230 fork_worker(a); 231 } 232 233 static void rcrespawn(struct init_action *a) 234 { 235 a->tout.cb = respawn; 236 a->respawn = 500; 237 238 a->proc.cb = child_exit; 239 if (!a->proc.pid) 240 fork_worker(a); 241 } 242 243 static struct init_handler handlers[] = { 244 { 245 .name = "sysinit", 246 .cb = runrc, 247 }, { 248 .name = "shutdown", 249 .cb = runrc, 250 }, { 251 .name = "askfirst", 252 .cb = askfirst, 253 .multi = 1, 254 }, { 255 .name = "askconsole", 256 .cb = askconsole, 257 .multi = 1, 258 }, { 259 .name = "respawn", 260 .cb = rcrespawn, 261 .multi = 1, 262 }, { 263 .name = "askconsolelate", 264 .cb = askconsole, 265 .multi = 1, 266 }, { 267 .name = "respawnlate", 268 .cb = rcrespawn, 269 .multi = 1, 270 } 271 }; 272 273 static int add_action(struct init_action *a, const char *name) 274 { 275 int i; 276 277 for (i = 0; i < ARRAY_SIZE(handlers); i++) 278 if (!strcmp(handlers[i].name, name)) { 279 a->handler = &handlers[i]; 280 list_add_tail(&a->list, &actions); 281 return 0; 282 } 283 ERROR("Unknown init handler %s\n", name); 284 return -1; 285 } 286 287 void procd_inittab_run(const char *handler) 288 { 289 struct init_action *a; 290 291 list_for_each_entry(a, &actions, list) 292 if (!strcmp(a->handler->name, handler)) { 293 a->handler->cb(a); 294 if (!a->handler->multi) 295 break; 296 } 297 } 298 299 void procd_inittab_kill(void) 300 { 301 struct init_action *a; 302 303 list_for_each_entry(a, &actions, list) { 304 a->respawn = -1; 305 if (a->proc.pid) 306 kill(a->proc.pid, SIGKILL); 307 } 308 } 309 310 void procd_inittab(void) 311 { 312 #define LINE_LEN 128 313 FILE *fp = fopen(tab, "r"); 314 struct init_action *a; 315 regex_t pat_inittab; 316 regmatch_t matches[5]; 317 char *line; 318 319 if (!fp) { 320 ERROR("Failed to open %s: %m\n", tab); 321 return; 322 } 323 324 regcomp(&pat_inittab, "([a-zA-Z0-9]*):([a-zA-Z0-9]*):([a-zA-Z0-9]*):(.*)", REG_EXTENDED); 325 line = malloc(LINE_LEN); 326 a = calloc(1, sizeof(struct init_action)); 327 328 while (fgets(line, LINE_LEN, fp)) { 329 char *tags[TAG_PROCESS + 1]; 330 char *tok; 331 int i; 332 int len = strlen(line); 333 334 while (isspace(line[len - 1])) 335 len--; 336 line[len] = 0; 337 338 if (*line == '#') 339 continue; 340 341 if (regexec(&pat_inittab, line, 5, matches, 0)) 342 continue; 343 344 DEBUG(4, "Parsing inittab - %s\n", line); 345 346 for (i = TAG_ID; i <= TAG_PROCESS; i++) { 347 line[matches[i].rm_eo] = '\0'; 348 tags[i] = &line[matches[i + 1].rm_so]; 349 }; 350 351 tok = strtok(tags[TAG_PROCESS], " "); 352 for (i = 0; i < (MAX_ARGS - 1) && tok; i++) { 353 a->argv[i] = tok; 354 tok = strtok(NULL, " "); 355 } 356 a->argv[i] = NULL; 357 a->id = tags[TAG_ID]; 358 a->line = line; 359 360 if (add_action(a, tags[TAG_ACTION])) 361 continue; 362 line = malloc(LINE_LEN); 363 a = calloc(1, sizeof(struct init_action)); 364 } 365 366 fclose(fp); 367 free(line); 368 free(a); 369 regfree(&pat_inittab); 370 } 371
This page was automatically generated by LXR 0.3.1. • OpenWrt