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 get_cmdline_val_offset("console", line, sizeof(line), 1)) { 197 if (dev_exist("console")) 198 tty = "console"; 199 else 200 tty = get_active_console(line, sizeof(line)); 201 } 202 if (tty != NULL) { 203 split = strchr(tty, ','); 204 if (split != NULL) 205 *split = '\0'; 206 207 if (!dev_exist(tty)) { 208 DEBUG(4, "skipping %s\n", tty); 209 return; 210 } 211 212 console = strdup(tty); 213 a->id = strdup(tty); 214 } 215 else { 216 console = NULL; 217 a->id = NULL; 218 } 219 220 a->tout.cb = respawn; 221 /* shift arguments only if not yet done */ 222 if (a->argv[0] != ask) { 223 for (i = MAX_ARGS - 1; i >= 1; i--) 224 a->argv[i] = a->argv[i - 1]; 225 a->argv[0] = ask; 226 } 227 a->respawn = 500; 228 229 a->proc.cb = child_exit; 230 if (!a->proc.pid) 231 fork_worker(a); 232 } 233 234 static void rcrespawn(struct init_action *a) 235 { 236 a->tout.cb = respawn; 237 a->respawn = 500; 238 239 a->proc.cb = child_exit; 240 if (!a->proc.pid) 241 fork_worker(a); 242 } 243 244 static struct init_handler handlers[] = { 245 { 246 .name = "sysinit", 247 .cb = runrc, 248 }, { 249 .name = "shutdown", 250 .cb = runrc, 251 }, { 252 .name = "askfirst", 253 .cb = askfirst, 254 .multi = 1, 255 }, { 256 .name = "askconsole", 257 .cb = askconsole, 258 .multi = 1, 259 }, { 260 .name = "respawn", 261 .cb = rcrespawn, 262 .multi = 1, 263 }, { 264 .name = "askconsolelate", 265 .cb = askconsole, 266 .multi = 1, 267 }, { 268 .name = "respawnlate", 269 .cb = rcrespawn, 270 .multi = 1, 271 } 272 }; 273 274 static int add_action(struct init_action *a, const char *name) 275 { 276 int i; 277 278 for (i = 0; i < ARRAY_SIZE(handlers); i++) 279 if (!strcmp(handlers[i].name, name)) { 280 a->handler = &handlers[i]; 281 list_add_tail(&a->list, &actions); 282 return 0; 283 } 284 ERROR("Unknown init handler %s\n", name); 285 return -1; 286 } 287 288 void procd_inittab_run(const char *handler) 289 { 290 struct init_action *a; 291 292 list_for_each_entry(a, &actions, list) 293 if (!strcmp(a->handler->name, handler)) { 294 a->handler->cb(a); 295 if (!a->handler->multi) 296 break; 297 } 298 } 299 300 void procd_inittab_kill(void) 301 { 302 struct init_action *a; 303 304 list_for_each_entry(a, &actions, list) { 305 a->respawn = -1; 306 if (a->proc.pid) 307 kill(a->proc.pid, SIGKILL); 308 } 309 } 310 311 void procd_inittab(void) 312 { 313 #define LINE_LEN 128 314 FILE *fp = fopen(tab, "r"); 315 struct init_action *a; 316 regex_t pat_inittab; 317 regmatch_t matches[5]; 318 char *line; 319 320 if (!fp) { 321 ERROR("Failed to open %s: %m\n", tab); 322 return; 323 } 324 325 regcomp(&pat_inittab, "([a-zA-Z0-9]*):([a-zA-Z0-9]*):([a-zA-Z0-9]*):(.*)", REG_EXTENDED); 326 line = malloc(LINE_LEN); 327 a = calloc(1, sizeof(struct init_action)); 328 329 while (fgets(line, LINE_LEN, fp)) { 330 char *tags[TAG_PROCESS + 1]; 331 char *tok; 332 int i; 333 int len = strlen(line); 334 335 while (isspace(line[len - 1])) 336 len--; 337 line[len] = 0; 338 339 if (*line == '#') 340 continue; 341 342 if (regexec(&pat_inittab, line, 5, matches, 0)) 343 continue; 344 345 DEBUG(4, "Parsing inittab - %s\n", line); 346 347 for (i = TAG_ID; i <= TAG_PROCESS; i++) { 348 line[matches[i].rm_eo] = '\0'; 349 tags[i] = &line[matches[i + 1].rm_so]; 350 }; 351 352 tok = strtok(tags[TAG_PROCESS], " "); 353 for (i = 0; i < (MAX_ARGS - 1) && tok; i++) { 354 a->argv[i] = tok; 355 tok = strtok(NULL, " "); 356 } 357 a->argv[i] = NULL; 358 a->id = tags[TAG_ID]; 359 a->line = line; 360 361 if (add_action(a, tags[TAG_ACTION])) 362 continue; 363 line = malloc(LINE_LEN); 364 a = calloc(1, sizeof(struct init_action)); 365 } 366 367 fclose(fp); 368 free(line); 369 free(a); 370 regfree(&pat_inittab); 371 } 372
This page was automatically generated by LXR 0.3.1. • OpenWrt