| 120 | | // gvdl@next.com 14 Aug 1995 |
| 121 | | // - from ~apps/loginwindow_proj/loginwindow/common.h |
| 122 | | #define REALLY_EXIT_TO_CONSOLE 229 |
| 123 | | |
| 124 | | // From old init.c |
| 125 | | // These flags are used in the se_flags field of the init_session structure |
| 126 | | #define SE_SHUTDOWN 0x1 /* session won't be restarted */ |
| 127 | | |
| 128 | | // The flags below control what sort of getty is launched. |
| 129 | | #define SE_GETTY_LAUNCH 0x30 /* What type of getty to launch */ |
| 130 | | #define SE_COMMON 0x00 /* Usual command that is run - getty */ |
| 131 | | #define SE_ONERROR 0x10 /* Command to run if error condition occurs. |
| 132 | | * This will almost always be the windowserver |
| 133 | | * and loginwindow. This is so if the w.s. |
| 134 | | * ever dies, that the naive user (stan) |
| 135 | | * doesn't ever see the console window. */ |
| 136 | | #define SE_ONOPTION 0x20 /* Command to run when loginwindow exits with |
| 137 | | * special error code (229). This signifies |
| 138 | | * that the user typed "console" at l.w. and |
| 139 | | * l.w. wants to exit and have init run getty |
| 140 | | * which will then put up a console window. */ |
| 141 | | |
| 142 | | typedef struct _se_command { |
| 143 | | char *path; /* what to run on that port */ |
| 144 | | char **argv; /* pre-parsed argument array */ |
| 145 | | } se_cmd_t; |
| 146 | | |
| 147 | | typedef struct init_session { |
| 148 | | kq_callback se_callback; /* run loop callback */ |
| 149 | | int se_index; /* index of entry in ttys file */ |
| 150 | | pid_t se_process; /* controlling process */ |
| 151 | | time_t se_started; /* used to avoid thrashing */ |
| 152 | | int se_flags; /* status of session */ |
| 153 | | char *se_device; /* filename of port */ |
| 154 | | se_cmd_t se_getty; /* what to run on that port */ |
| 155 | | se_cmd_t se_onerror; /* See SE_ONERROR above */ |
| 156 | | se_cmd_t se_onoption; /* See SE_ONOPTION above */ |
| 157 | | TAILQ_ENTRY(init_session) tqe; |
| 158 | | } *session_t; |
| 159 | | |
| 160 | | static TAILQ_HEAD(sessionshead, init_session) sessions = TAILQ_HEAD_INITIALIZER(sessions); |
| 161 | | |
| 162 | | static void session_new(int, struct ttyent *); |
| 163 | | static void session_free(session_t); |
| 164 | | static void session_launch(session_t); |
| 165 | | static void session_reap(session_t); |
| 166 | | static void session_callback(void *, struct kevent *); |
| 167 | | |
| 168 | | static char **construct_argv(char *); |
| 424 | | /* |
| 425 | | * Construct an argument vector from a command line. |
| 426 | | */ |
| 427 | | char ** |
| 428 | | construct_argv(command) |
| 429 | | char *command; |
| 430 | | { |
| 431 | | int argc = 0; |
| 432 | | char **argv = (char **) malloc(((strlen(command) + 1) / 2 + 1) |
| 433 | | * sizeof (char *)); |
| 434 | | static const char separators[] = " \t"; |
| 435 | | |
| 436 | | if ((argv[argc++] = strtok(command, separators)) == 0) |
| 437 | | return 0; |
| 438 | | while ((argv[argc++] = strtok(NULL, separators))) |
| 439 | | continue; |
| 440 | | return argv; |
| 441 | | } |
| 442 | | |
| 443 | | /* |
| 444 | | * Deallocate a session descriptor. |
| 445 | | */ |
| 446 | | |
| 447 | | static void free_command(se_cmd_t *se_cmd) |
| 448 | | { |
| 449 | | if (se_cmd->path) { |
| 450 | | free(se_cmd->path); |
| 451 | | free(se_cmd->argv); |
| 452 | | } |
| 453 | | } |
| 454 | | |
| 455 | | void |
| 456 | | session_free(session_t s) |
| 457 | | { |
| 458 | | TAILQ_REMOVE(&sessions, s, tqe); |
| 459 | | if (s->se_process) { |
| 460 | | if (kevent_mod(s->se_process, EVFILT_PROC, EV_ADD, |
| 461 | | NOTE_EXIT, 0, &kqsimple_zombie_reaper) == -1) |
| 462 | | session_reap(s); |
| 463 | | else |
| 464 | | kill(s->se_process, SIGHUP); |
| 465 | | } |
| 466 | | free(s->se_device); |
| 467 | | free_command(&s->se_getty); |
| 468 | | free_command(&s->se_onerror); |
| 469 | | free_command(&s->se_onoption); |
| 470 | | free(s); |
| 471 | | } |
| 472 | | |
| 473 | | static int setup_command(se_cmd_t *se_cmd, char *command, char *arg ) |
| 474 | | { |
| 475 | | char *commandWithArg; |
| 476 | | |
| 477 | | asprintf(&commandWithArg, "%s %s", command, arg); |
| 478 | | |
| 479 | | free_command(se_cmd); |
| 480 | | |
| 481 | | se_cmd->path = commandWithArg; |
| 482 | | se_cmd->argv = construct_argv(commandWithArg); |
| 483 | | if (se_cmd->argv == NULL) { |
| 484 | | free(se_cmd->path); |
| 485 | | se_cmd->path = NULL; |
| 486 | | return 0; |
| 487 | | } |
| 488 | | return 1; |
| 489 | | } |
| 490 | | |
| 491 | | /* |
| 492 | | * Calculate getty and if useful window argv vectors. |
| 493 | | */ |
| 494 | | static int |
| 495 | | setupargv(sp, typ) |
| 496 | | session_t sp; |
| 497 | | struct ttyent *typ; |
| 498 | | { |
| 499 | | char *type; |
| 500 | | |
| 501 | | if ( !setup_command(&sp->se_getty, typ->ty_getty, typ->ty_name) ) |
| 502 | | { |
| 503 | | type = "getty"; |
| 504 | | goto bad_args; |
| 505 | | } |
| 506 | | |
| 507 | | if (typ->ty_onerror |
| 508 | | && !setup_command(&sp->se_onerror, typ->ty_onerror, typ->ty_name) ) |
| 509 | | { |
| 510 | | type = "onerror"; |
| 511 | | goto bad_args; |
| 512 | | } |
| 513 | | |
| 514 | | if (typ->ty_onoption |
| 515 | | && !setup_command(&sp->se_onoption, typ->ty_onoption, typ->ty_name) ) |
| 516 | | { |
| 517 | | type = "onoption"; |
| 518 | | goto bad_args; |
| 519 | | } |
| 520 | | |
| 521 | | return 1; |
| 522 | | |
| 523 | | bad_args: |
| 524 | | syslog(LOG_WARNING, "can't parse %s for port %s", type, sp->se_device); |
| 525 | | return 0; |
| 526 | | } |
| 527 | | |
| 528 | | |
| 529 | | /* |
| 530 | | * Allocate a new session descriptor. |
| 531 | | */ |
| 532 | | void |
| 533 | | session_new(session_index, typ) |
| 534 | | int session_index; |
| 535 | | struct ttyent *typ; |
| 536 | | { |
| 537 | | session_t s; |
| 538 | | |
| 539 | | if ((typ->ty_status & TTY_ON) == 0 || |
| 540 | | typ->ty_name == 0 || |
| 541 | | typ->ty_getty == 0) |
| 542 | | return; |
| 543 | | |
| 544 | | s = calloc(1, sizeof(struct init_session)); |
| 545 | | |
| 546 | | s->se_callback = session_callback; |
| 547 | | s->se_index = session_index; |
| 548 | | |
| 549 | | TAILQ_INSERT_TAIL(&sessions, s, tqe); |
| 550 | | |
| 551 | | asprintf(&s->se_device, "%s%s", _PATH_DEV, typ->ty_name); |
| 552 | | |
| 553 | | if (setupargv(s, typ) == 0) |
| 554 | | session_free(s); |
| 555 | | } |
| 556 | | |
| 557 | | static void |
| 558 | | session_launch(session_t s) |
| 559 | | { |
| 560 | | pid_t pid; |
| 561 | | sigset_t mask; |
| 562 | | se_cmd_t *se_cmd; |
| 563 | | const char *session_type = NULL; |
| 564 | | time_t current_time = time(NULL); |
| 565 | | bool is_loginwindow = false; |
| 566 | | |
| 567 | | // Setup the default values; |
| 568 | | switch (s->se_flags & SE_GETTY_LAUNCH) { |
| 569 | | case SE_ONOPTION: |
| 570 | | if (s->se_onoption.path) { |
| 571 | | se_cmd = &s->se_onoption; |
| 572 | | session_type = "onoption"; |
| 573 | | break; |
| 574 | | } |
| 575 | | /* No break */ |
| 576 | | case SE_ONERROR: |
| 577 | | if (s->se_onerror.path) { |
| 578 | | se_cmd = &s->se_onerror; |
| 579 | | session_type = "onerror"; |
| 580 | | break; |
| 581 | | } |
| 582 | | /* No break */ |
| 583 | | case SE_COMMON: |
| 584 | | default: |
| 585 | | se_cmd = &s->se_getty; |
| 586 | | session_type = "getty"; |
| 587 | | break; |
| 588 | | } |
| 589 | | |
| 590 | | if (strcmp(se_cmd->argv[0], "/System/Library/CoreServices/loginwindow.app/Contents/MacOS/loginwindow") == 0) |
| 591 | | is_loginwindow = true; |
| 592 | | |
| 593 | | pid = launchd_fork(); |
| 594 | | |
| 595 | | if (pid == -1) { |
| 596 | | syslog(LOG_ERR, "can't fork for %s on port %s: %m", |
| 597 | | session_type, s->se_device); |
| 598 | | return; |
| 599 | | } |
| 600 | | |
| 601 | | if (pid) { |
| 602 | | s->se_process = pid; |
| 603 | | s->se_started = time(NULL); |
| 604 | | s->se_flags &= ~SE_GETTY_LAUNCH; // clear down getty launch type |
| 605 | | if (kevent_mod(pid, EVFILT_PROC, EV_ADD, NOTE_EXIT, 0, &s->se_callback) == -1) |
| 606 | | session_reap(s); |
| 607 | | return; |
| 608 | | } |
| 609 | | |
| 610 | | if (current_time > s->se_started && |
| 611 | | current_time - s->se_started < GETTY_SPACING) { |
| 612 | | syslog(LOG_WARNING, "%s repeating too quickly on port %s, sleeping", |
| 613 | | session_type, s->se_device); |
| 614 | | sleep(GETTY_SLEEP); |
| 615 | | } |
| 616 | | |
| 617 | | sigemptyset(&mask); |
| 618 | | sigprocmask(SIG_SETMASK, &mask, NULL); |
| 619 | | |
| 620 | | |
| 621 | | if (!is_loginwindow) |
| 622 | | launchd_SessionCreate(); |
| 623 | | |
| 624 | | execv(se_cmd->argv[0], se_cmd->argv); |
| 625 | | stall("can't exec %s '%s' for port %s: %m", session_type, |
| 626 | | se_cmd->argv[0], s->se_device); |
| 627 | | exit(EXIT_FAILURE); |
| 628 | | } |
| 629 | | |
| 630 | | static void |
| 631 | | session_callback(void *obj, struct kevent *kev __attribute__((unused))) |
| 632 | | { |
| 633 | | session_t s = obj; |
| 634 | | |
| 635 | | session_reap(s); |
| 636 | | if (s->se_flags & SE_SHUTDOWN) { |
| 637 | | session_free(s); |
| 638 | | } else { |
| 639 | | session_launch(s); |
| 640 | | } |
| 641 | | } |
| 642 | | |
| 643 | | static void |
| 644 | | session_reap(session_t s) |
| 645 | | { |
| 646 | | char *line; |
| 647 | | int status; |
| 648 | | |
| 649 | | if (!launchd_assumes(waitpid(s->se_process, &status, 0) == s->se_process)) |
| 650 | | return; |
| 651 | | |
| 652 | | if (WIFSIGNALED(status)) { |
| 653 | | syslog(LOG_WARNING, "%s port %s exited abnormally: %s", |
| 654 | | s->se_getty.path, s->se_device, strsignal(WTERMSIG(status))); |
| 655 | | s->se_flags |= SE_ONERROR; |
| 656 | | } else if (WEXITSTATUS(status) == REALLY_EXIT_TO_CONSOLE) { |
| 657 | | /* WIFEXITED(status) assumed */ |
| 658 | | s->se_flags |= SE_ONOPTION; |
| 659 | | } else { |
| 660 | | s->se_flags |= SE_ONERROR; |
| 661 | | } |
| 662 | | |
| 663 | | s->se_process = 0; |
| 664 | | line = s->se_device + sizeof(_PATH_DEV) - 1; |
| 665 | | |
| 666 | | if (logout(line)) |
| 667 | | logwtmp(line, "", ""); |
| 668 | | } |
| 669 | | |
| 670 | | /* |
| 671 | | * This is an n-squared algorithm. We hope it isn't run often... |
| 672 | | */ |
| 673 | | void |
| 674 | | update_ttys(void) |
| 675 | | { |
| 676 | | session_t sp; |
| 677 | | struct ttyent *typ; |
| 678 | | int session_index = 0; |
| 679 | | int devlen; |
| 680 | | |
| 681 | | devlen = sizeof(_PATH_DEV) - 1; |
| 682 | | while ((typ = getttyent())) { |
| 683 | | ++session_index; |
| 684 | | |
| 685 | | TAILQ_FOREACH(sp, &sessions, tqe) { |
| 686 | | if (strcmp(typ->ty_name, sp->se_device + devlen) == 0) |
| 687 | | break; |
| 688 | | } |
| 689 | | |
| 690 | | if (sp == NULL) { |
| 691 | | session_new(session_index, typ); |
| 692 | | continue; |
| 693 | | } |
| 694 | | |
| 695 | | if (sp->se_index != session_index) { |
| 696 | | syslog(LOG_INFO, "port %s changed utmp index from %d to %d", |
| 697 | | sp->se_device, sp->se_index, |
| 698 | | session_index); |
| 699 | | sp->se_index = session_index; |
| 700 | | } |
| 701 | | |
| 702 | | if ((typ->ty_status & TTY_ON) == 0 || |
| 703 | | typ->ty_getty == 0) { |
| 704 | | session_free(sp); |
| 705 | | continue; |
| 706 | | } |
| 707 | | |
| 708 | | sp->se_flags &= ~SE_SHUTDOWN; |
| 709 | | |
| 710 | | if (setupargv(sp, typ) == 0) { |
| 711 | | syslog(LOG_WARNING, "can't parse getty for port %s", |
| 712 | | sp->se_device); |
| 713 | | session_free(sp); |
| 714 | | } |
| 715 | | } |
| 716 | | |
| 717 | | endttyent(); |
| 718 | | } |
| 719 | | |
| 720 | | /* |
| 721 | | * Block further logins. |
| 722 | | */ |
| 723 | | void |
| 724 | | catatonia(void) |
| 725 | | { |
| 726 | | session_t s; |
| 727 | | |
| 728 | | TAILQ_FOREACH(s, &sessions, tqe) |
| 729 | | s->se_flags |= SE_SHUTDOWN; |
| 730 | | } |
| 731 | | |
| 732 | | bool init_check_pid(pid_t p) |
| 733 | | { |
| 734 | | session_t s; |
| 735 | | |
| 736 | | TAILQ_FOREACH(s, &sessions, tqe) { |
| 737 | | if (s->se_process == p) |
| 738 | | return true; |
| 739 | | } |
| 740 | | |
| | 358 | bool |
| | 359 | init_check_pid(pid_t p) |
| | 360 | { |