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