minit.c (14838B)
1 #define _GNU_SOURCE 2 3 #include "minit.h" 4 5 #include "write12.h" 6 7 #include <alloca.h> 8 #include <errno.h> 9 #include <fcntl.h> 10 #include <limits.h> 11 #include <linux/kd.h> 12 #include <poll.h> 13 #include <signal.h> 14 #include <stdio.h> 15 #include <stdlib.h> 16 #include <string.h> 17 #include <sys/ioctl.h> 18 #include <sys/reboot.h> 19 #include <sys/socket.h> 20 #include <sys/types.h> 21 #include <sys/un.h> 22 #include <sys/wait.h> 23 #include <time.h> 24 #include <unistd.h> 25 26 static struct process* root; 27 static int maxprocess = -1; 28 static int processalloc; 29 30 static char** Argv; 31 32 #undef printf 33 extern int printf(const char* format, ...); 34 35 extern void opendevconsole(); 36 37 #define UPDATE 38 #ifdef UPDATE 39 static int doupdate; 40 #endif 41 42 extern int openreadclose(char* fn, char** buf, size_t* len); 43 extern char** split(char* buf, int c, size_t* len, size_t plus, size_t ofs); 44 45 #define HISTORY 10 46 #ifdef HISTORY 47 static int history[HISTORY]; 48 #endif 49 50 /* return index of service in process data structure or -1 if not found */ 51 static int findservice(char* service) { 52 int i; 53 for (i = 0; i <= maxprocess; ++i) { 54 if (!strcmp(root[i].name, service)) 55 return i; 56 } 57 return -1; 58 } 59 60 /* look up process index in data structure by PID */ 61 static int findbypid(pid_t pid) { 62 int i; 63 for (i = 0; i <= maxprocess; ++i) { 64 if (root[i].pid == pid) 65 return i; 66 } 67 return -1; 68 } 69 70 /* clear circular dependency detection flags */ 71 static void circsweep() { 72 int i; 73 for (i = 0; i <= maxprocess; ++i) 74 root[i].circular = 0; 75 } 76 77 /* add process to data structure, return index or -1 */ 78 static int addprocess(struct process* p) { 79 if (maxprocess + 1 >= processalloc) { 80 struct process* fump; 81 processalloc += 8; 82 if ((fump = (struct process*) realloc(root, processalloc * sizeof(struct process))) == 0) return -1; 83 root = fump; 84 } 85 memmove(&root[++maxprocess], p, sizeof(struct process)); 86 return maxprocess; 87 } 88 89 /* load a service into the process data structure and return index or -1 90 * if failed */ 91 static int loadservice(char* service) { 92 struct process tmp; 93 int fd; 94 if (*service == 0) return -1; 95 fd = findservice(service); 96 if (fd >= 0) return fd; 97 if (chdir(MINITROOT) || chdir(service)) return -1; 98 if (!(tmp.name = strdup(service))) return -1; 99 tmp.pid = 0; 100 fd = open("respawn", O_RDONLY); 101 if (fd >= 0) { 102 tmp.respawn = 1; 103 close(fd); 104 } else 105 tmp.respawn = 0; 106 tmp.startedat = 0; 107 tmp.circular = 0; 108 tmp.__stdin = 0; 109 tmp.__stdout = 1; 110 { 111 char* logservice = alloca(strlen(service) + 5); 112 strcpy(logservice, service); 113 strcat(logservice, "/log"); 114 tmp.logservice = loadservice(logservice); 115 if (tmp.logservice >= 0) { 116 int pipefd[2]; 117 if (pipe(pipefd)) return -1; 118 fcntl(pipefd[0], F_SETFD, FD_CLOEXEC); 119 fcntl(pipefd[1], F_SETFD, FD_CLOEXEC); 120 root[tmp.logservice].__stdin = pipefd[0]; 121 tmp.__stdout = pipefd[1]; 122 } 123 } 124 return addprocess(&tmp); 125 } 126 127 /* usage: isup(findservice("sshd")). 128 * returns nonzero if process is up */ 129 static int isup(int service) { 130 if (service < 0) return 0; 131 return (root[service].pid != 0); 132 } 133 134 static int startservice(int service, int pause, int father); 135 136 #undef debug 137 static void handlekilled(pid_t killed) { 138 int i; 139 #ifdef debug 140 { 141 char buf[50]; 142 snprintf(buf, 50, " %d\n", killed); 143 write(2, buf, strlen(buf)); 144 } 145 #endif 146 if (killed == (pid_t) -1) { 147 static int saidso; 148 if (!saidso) { 149 write(2, "all services exited.\n", 21); 150 saidso = 1; 151 } 152 exit(0); 153 } 154 if (killed == 0) return; 155 i = findbypid(killed); 156 if (i >= 0) { 157 root[i].pid = 0; 158 if (root[i].respawn) { 159 circsweep(); 160 startservice(i, time(0) - root[i].startedat < 1, root[i].father); 161 } else { 162 root[i].startedat = time(0); 163 root[i].pid = 1; 164 } 165 } 166 } 167 168 /* called from inside the service directory, return the PID or 0 on error */ 169 static pid_t forkandexec(int pause, int service) { 170 char** argv = 0; 171 int count = 0; 172 pid_t p; 173 int fd; 174 size_t len; 175 int islink; 176 char* s = 0; 177 size_t argc; 178 char* argv0 = 0; 179 again: 180 switch (p = fork()) { 181 case (pid_t) -1: 182 if (count > 3) return 0; 183 sleep(++count * 2); 184 goto again; 185 case 0: 186 /* child */ 187 188 if (pause) { 189 struct timespec req; 190 req.tv_sec = 0; 191 req.tv_nsec = 500000000; 192 nanosleep(&req, 0); 193 } 194 if ((fd = open("in", O_RDONLY)) != -1) { 195 if (dup2(fd, 0) != 0) { 196 __write2("dup2 failed unexpectedly.\n"); 197 // This should never fail. init is run as root. 198 // If it does fail, don't exit for fear of bricking the system 199 } 200 fcntl(0, F_SETFD, 0); 201 } 202 if ((fd = open("out", O_WRONLY)) != -1) { 203 if (dup2(fd, 1) != 1 || dup2(fd, 2) != 2) { 204 __write2("dup2 failed unexpectedly.\n"); 205 // This should never fail. init is run as root. 206 // If it does fail, don't exit for fear of bricking the system 207 } 208 fcntl(1, F_SETFD, 0); 209 fcntl(2, F_SETFD, 0); 210 } 211 // openreadclose and split allocate memory. 212 // We leak it here, because we are in the child process that is 213 // about to execve somebody else, at which point the OS frees all. 214 if (!openreadclose("nice", &s, &len)) { 215 int n = atoi(s); 216 nice(n); 217 s = 0; 218 } 219 if (!openreadclose("params", &s, &len)) { 220 argv = split(s, '\n', &argc, 2, 1); 221 if (argc && argv[argc - 1][0] == 0) --argc; // if params ended on newline, don't pass empty last arg 222 argv[argc] = 0; 223 } else { 224 argv = (char**) alloca(2 * sizeof(char*)); 225 argv[1] = 0; 226 } 227 argv0 = (char*) alloca(PATH_MAX + 1); 228 if (!argv) _exit(1); 229 if (readlink("run", argv0, PATH_MAX) < 0) { 230 if (errno != EINVAL) _exit(1); /* not a symbolic link */ 231 strcpy(argv0, "run"); 232 islink = 0; 233 } else 234 islink = 1; 235 argv[0] = strrchr(argv0, '/'); 236 if (argv[0]) 237 argv[0]++; 238 else 239 argv[0] = argv0; 240 argv[0] = strdupa(argv[0]); 241 if (islink && argv0[0] != '/') { 242 char* c = alloca(strlen(argv0) + sizeof(MINITROOT) + strlen(root[service].name + 2)); 243 char* d; 244 int fd = open(".", O_RDONLY | O_DIRECTORY); 245 stpcpy(stpcpy(stpcpy(stpcpy(c, MINITROOT "/"), root[service].name), "/"), argv0); 246 if (fd != -1) { 247 /* c is now something like /etc/minit/default/../../../var/whatever/doit 248 * attempt to clean it up a little */ 249 d = strrchr(c, '/'); 250 *d = 0; 251 if (chdir(c) == 0) { 252 *d = '/'; 253 if (getcwd(argv0, PATH_MAX)) 254 strncat(argv0, d, PATH_MAX); 255 fchdir(fd); 256 close(fd); 257 } else { 258 *d = '/'; 259 argv0 = c; 260 } 261 } else 262 argv0 = c; 263 } 264 if (root[service].__stdin != 0) { 265 if (dup2(root[service].__stdin, 0) != 0) { 266 __write2("dup2 failed unexpectedly.\n"); 267 // This should never fail. init is run as root. 268 // If it does fail, don't exit for fear of bricking the system 269 } 270 fcntl(0, F_SETFD, 0); 271 } 272 if (root[service].__stdout != 1) { 273 if (dup2(root[service].__stdout, 1) != 1 || dup2(root[service].__stdout, 2) != 2) { 274 __write2("dup2 failed unexpectedly.\n"); 275 // This should never fail. init is run as root. 276 // If it does fail, don't exit for fear of bricking the system 277 } 278 fcntl(1, F_SETFD, 0); 279 fcntl(2, F_SETFD, 0); 280 } 281 { 282 int i; 283 for (i = 3; i < 1024; ++i) close(i); 284 } 285 chdir("root"); 286 execve(argv0, argv, environ); 287 _exit(1); 288 default: 289 fd = open("sync", O_RDONLY); 290 if (fd >= 0) { 291 close(fd); 292 waitpid(p, 0, 0); 293 return 1; 294 } 295 return p; 296 } 297 } 298 299 /* start a service, return nonzero on error */ 300 static int startnodep(int service, int pause) { 301 /* step 1: see if the process is already up */ 302 if (isup(service)) return 0; 303 /* step 2: fork and exec service, put PID in data structure */ 304 if (chdir(MINITROOT) || chdir(root[service].name)) return -1; 305 root[service].startedat = time(0); 306 root[service].pid = forkandexec(pause, service); 307 return root[service].pid; 308 } 309 310 static int startservice(int service, int pause, int father) { 311 int dir = -1; 312 unsigned long len; 313 char* s = 0; 314 pid_t pid = 0; 315 if (service < 0) return 0; 316 if (root[service].circular) 317 return 0; 318 root[service].circular = 1; 319 root[service].father = father; 320 #ifdef HISTORY 321 { 322 memmove(history + 1, history, sizeof(int) * ((HISTORY) -1)); 323 history[0] = service; 324 } 325 #endif 326 if (root[service].logservice >= 0) 327 startservice(root[service].logservice, pause, service); 328 if (chdir(MINITROOT) || chdir(root[service].name)) return -1; 329 if ((dir = open(".", O_RDONLY)) >= 0) { 330 // openreadclose allocates memory and reads the file contents into it. 331 // Need to free(s) independent of openreadclose return value 332 if (!openreadclose("depends", &s, &len)) { 333 char** deps = 0; 334 size_t depc, i; 335 deps = split(s, '\n', &depc, 0, 0); 336 for (i = 0; i < depc; i++) { 337 int Service, blacklisted, j; 338 if (deps[i][0] == '#') continue; 339 Service = loadservice(deps[i]); 340 341 #if 1 342 for (j = blacklisted = 0; Argv[j]; ++j) 343 if (Argv[j][0] == '-' && !strcmp(Argv[j] + 1, deps[i])) { 344 blacklisted = 1; 345 ++Argv[j]; 346 break; 347 } 348 #endif 349 350 if (Service >= 0 && root[Service].pid != 1 && !blacklisted) 351 startservice(Service, 0, service); 352 } 353 fchdir(dir); 354 free(deps); 355 } 356 free(s); 357 pid = startnodep(service, pause); 358 359 close(dir); 360 dir = -1; 361 } 362 chdir(MINITROOT); 363 return pid; 364 } 365 366 static void _puts(const char* s) { 367 write(1, s, strlen(s)); 368 } 369 370 static void childhandler() { 371 int status; 372 pid_t killed; 373 #ifdef debug 374 write(2, "wait...", 7); 375 #endif 376 #ifdef UPDATE 377 if (doupdate) return; 378 #endif 379 380 do { 381 killed = waitpid(-1, &status, WNOHANG); 382 handlekilled(killed); 383 } while (killed && killed != (pid_t) -1); 384 } 385 386 static volatile int dowinch = 0; 387 static volatile int doint = 0; 388 389 static void sigchild(int sig) { (void) sig; } 390 391 int main(int argc, char* argv[]) { 392 /* Schritt 1: argv[1] als Service nehmen und starten */ 393 int count = 0; 394 int i; 395 struct pollfd pfd; 396 time_t last = time(0); 397 int nfds = 1; 398 int outfd, infd; 399 400 #ifdef HISTORY 401 for (i = 0; i < HISTORY; ++i) 402 history[i] = -1; 403 #endif 404 405 Argv = argv; 406 407 infd = open(MINITROOT "/in", O_RDWR); 408 outfd = open(MINITROOT "/out", O_RDWR | O_NONBLOCK); 409 410 /* signal(SIGPWR,sighandler); don't know what to do about it */ 411 /* signal(SIGHUP,sighandler); ??? */ 412 { 413 static struct sigaction sa; 414 sigemptyset(&sa.sa_mask); 415 errno = 0; 416 sa.sa_sigaction = 0; 417 sa.sa_flags = SA_RESTART | SA_NOCLDSTOP; 418 sa.sa_handler = sigchild; 419 sigaction(SIGCHLD, &sa, 0); 420 sa.sa_flags = SA_RESTART; 421 if (errno) _puts("sigaction failed!\n"); 422 } 423 424 if (infd < 0 || outfd < 0) { 425 _puts("minit: could not open " MINITROOT "/in or " MINITROOT "/out\n"); 426 exit(1); 427 nfds = 0; 428 } else 429 pfd.fd = infd; 430 pfd.events = POLLIN; 431 432 fcntl(infd, F_SETFD, FD_CLOEXEC); 433 fcntl(outfd, F_SETFD, FD_CLOEXEC); 434 435 #ifdef UPDATE 436 { 437 struct flock fl; 438 fl.l_whence = SEEK_CUR; 439 fl.l_start = 0; 440 fl.l_len = 0; 441 fl.l_pid = 0; 442 if ((0 == fcntl(infd, F_GETLK, &fl)) && 443 (fl.l_type != F_UNLCK)) doupdate = 1; 444 } 445 446 if (!doupdate) { 447 #endif 448 for (i = 1; i < argc; i++) { 449 circsweep(); 450 if (startservice(loadservice(argv[i]), 0, -1)) count++; 451 } 452 circsweep(); 453 if (!count) startservice(loadservice("default"), 0, -1); 454 #ifdef UPDATE 455 } 456 #endif 457 for (;;) { 458 int i; 459 char buf[1501]; 460 time_t now; 461 if (doint) { 462 doint = 0; 463 startservice(loadservice("ctrlaltdel"), 0, -1); 464 } 465 if (dowinch) { 466 dowinch = 0; 467 startservice(loadservice("kbreq"), 0, -1); 468 } 469 childhandler(); 470 now = time(0); 471 if (now < last || now - last > 30) { 472 /* The system clock was reset. Compensate. */ 473 long diff = last - now; 474 int j; 475 476 for (j = 0; j <= maxprocess; ++j) 477 root[j].startedat -= diff; 478 } 479 last = now; 480 switch (poll(&pfd, nfds, 5000)) { 481 case -1: 482 if (errno == EINTR) { 483 childhandler(); 484 break; 485 } 486 opendevconsole(); 487 _puts("poll failed!\n"); 488 exit(1); 489 /* what should we do if poll fails?! */ 490 break; 491 case 1: 492 i = read(infd, buf, 1500); 493 if (i > 1) { 494 int idx = 0, tmp; 495 buf[i] = 0; 496 497 /* write(1,buf,strlen(buf)); write(1,"\n",1); */ 498 #ifdef UPDATE 499 if (!strcmp(buf, "update")) { 500 execve("/sbin/minit", argv, environ); 501 } 502 503 if (((buf[0] != 'U') && buf[0] != 's') && ((idx = findservice(buf + 1)) < 0) && strcmp(buf, "d-")) 504 #else 505 if (buf[0] != 's' && ((idx = findservice(buf + 1)) < 0) && strcmp(buf, "d-")) 506 #endif 507 error: 508 write(outfd, "0", 1); 509 else { 510 switch (buf[0]) { 511 case 'p': 512 dprintf(outfd, "%d", root[idx].pid); 513 break; 514 #ifdef UPDATE 515 case 'D': 516 doupdate = 1; 517 write(outfd, &root[idx], sizeof(struct process)); 518 break; 519 case 'U': 520 doupdate = 1; 521 write(outfd, "1", 1); 522 if (1 == poll(&pfd, nfds, 5000)) { 523 struct process tmp; 524 if (read(infd, &tmp, sizeof tmp) != sizeof(tmp)) { 525 __write2("update failed, struct size mismatch.\n"); 526 goto ok; 527 } 528 tmp.name = strdup(buf + 1); 529 addprocess(&tmp); 530 } 531 goto ok; 532 #endif 533 case 'r': 534 root[idx].respawn = 0; 535 goto ok; 536 case 'R': 537 root[idx].respawn = 1; 538 goto ok; 539 case 'C': 540 if (kill(root[idx].pid, 0)) { /* check if still active */ 541 handlekilled(root[idx].pid); /* no!?! remove form active list */ 542 goto error; 543 } 544 goto ok; 545 case 'P': { 546 unsigned char* x = (unsigned char*) buf + strlen(buf) + 1; 547 unsigned char c; 548 tmp = 0; 549 while ((c = *x++ - '0') < 10) tmp = tmp * 10 + c; 550 } 551 if (tmp > 0) { 552 if (kill(tmp, 0)) goto error; 553 } 554 root[idx].pid = tmp; 555 goto ok; 556 case 's': 557 idx = loadservice(buf + 1); 558 if (idx < 0) goto error; 559 if (root[idx].pid < 2) { 560 root[idx].pid = 0; 561 circsweep(); 562 idx = startservice(idx, 0, -1); 563 if (idx == 0) { 564 write(outfd, "0", 1); 565 break; 566 } 567 } 568 ok: 569 write(outfd, "1", 1); 570 break; 571 case 'u': 572 dprintf(outfd, "%lu", time(0) - root[idx].startedat); 573 break; 574 case 'd': 575 write(outfd, "1:", 2); 576 { 577 int i; 578 for (i = 0; i <= maxprocess; ++i) { 579 if (root[i].father == idx) 580 write(outfd, root[i].name, strlen(root[i].name) + 1); 581 } 582 write(outfd, "\0", 2); 583 } 584 break; 585 } 586 } 587 } else { 588 if (buf[0] == 'h') { 589 #ifdef HISTORY 590 write(outfd, "1:", 2); 591 { 592 int i; 593 for (i = 0; i < HISTORY; ++i) 594 if (history[i] != -1) 595 write(outfd, root[history[i]].name, strlen(root[history[i]].name) + 1); 596 write(outfd, "\0", 2); 597 } 598 #else 599 write(outfd, "0", 1); 600 #endif 601 } 602 } 603 break; 604 default: 605 #ifdef UPDATE 606 doupdate = 0; 607 #endif 608 break; 609 } 610 } 611 }