init.c 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146
  1. /* vim: tabstop=4 shiftwidth=4 noexpandtab
  2. * This file is part of ToaruOS and is released under the terms
  3. * of the NCSA / University of Illinois License - see LICENSE.md
  4. * Copyright (C) 2018 K. Lange
  5. *
  6. * init - First process.
  7. *
  8. * `init` calls startup scripts and then waits for them to complete.
  9. * It also waits for orphaned proceses so they can be collected.
  10. *
  11. * `init` itself is statically-linked, so minimizing libc dependencies
  12. * is worthwhile as it reduces to the total size of init itself, which
  13. * remains in memory throughout the entire lifetime of the OS.
  14. *
  15. * Startup scripts for init are stored in /etc/startup.d and are run
  16. * in sorted alphabetical order. It is generally recommended that these
  17. * startup scripts be named with numbers at the front to ensure easy
  18. * ordering. This system of running a set of scripts on startup is
  19. * somewhat similar to how sysvinit worked, but no claims of
  20. * compatibility are made.
  21. *
  22. * Startup scripts can be any executable binary. Shell scripts are
  23. * generally used to allow easy editing, but you could also use
  24. * a binary (even a dynamically linked one) as a startup script.
  25. * `init` will wait for each startup script (that is, it will wait for
  26. * the original process it started to exit) before running the next one.
  27. * So if you wish to run daemons, be sure to fork them off and then
  28. * exit so that the rest of the startup process can continue.
  29. *
  30. * When the last startup script finishes, `init` will reboot the system.
  31. */
  32. #include <dirent.h>
  33. #include <errno.h>
  34. #include <stdio.h>
  35. #include <stdlib.h>
  36. #include <string.h>
  37. #include <syscall.h>
  38. #include <unistd.h>
  39. #include <wait.h>
  40. #include <sys/wait.h>
  41. #define INITD_PATH "/etc/startup.d"
  42. /* Initialize fd 0, 1, 2 */
  43. void set_console(void) {
  44. /* default to /dev/ttyS0 (serial COM1) */
  45. int _stdin = syscall_open("/dev/ttyS0", 0, 0);
  46. if (_stdin < 0) {
  47. /* if /dev/ttyS0 failed to open, fall back to /dev/null */
  48. syscall_open("/dev/null", 0, 0);
  49. syscall_open("/dev/null", 1, 0);
  50. syscall_open("/dev/null", 1, 0);
  51. } else {
  52. /* otherwise also use /dev/ttyS0 for stdout, stderr */
  53. syscall_open("/dev/ttyS0", 1, 0);
  54. syscall_open("/dev/ttyS0", 1, 0);
  55. }
  56. }
  57. /* Run a startup script and wait for it to finish */
  58. int start_options(char * args[]) {
  59. /* Fork child to run script */
  60. int cpid = syscall_fork();
  61. /* Child process... */
  62. if (!cpid) {
  63. /* Pass environment from init to child */
  64. syscall_execve(args[0], args, environ);
  65. /* exec failed, exit this subprocess */
  66. syscall_exit(0);
  67. }
  68. /* Wait for the child process to finish */
  69. int pid = 0;
  70. do {
  71. /*
  72. * Wait, ignoring kernel threads
  73. * (which also end up as children to init)
  74. */
  75. pid = waitpid(-1, NULL, WNOKERN);
  76. if (pid == -1 && errno == ECHILD) {
  77. /* There are no more children */
  78. break;
  79. }
  80. if (pid == cpid) {
  81. /* The child process finished */
  82. break;
  83. }
  84. /* Continue while no error (or error was "interrupted") */
  85. } while ((pid > 0) || (pid == -1 && errno == EINTR));
  86. return cpid;
  87. }
  88. int main(int argc, char * argv[]) {
  89. /* Initialize stdin/out/err */
  90. set_console();
  91. /* Get directory listing for /etc/startup.d */
  92. int initd_dir = syscall_open(INITD_PATH, 0, 0);
  93. if (initd_dir < 0) {
  94. /* No init scripts; try to start getty as a fallback */
  95. start_options((char *[]){"/bin/getty",NULL});
  96. } else {
  97. int count = 0, i = 0, ret = 0;
  98. /* Figure out how many entries we have with a dry run */
  99. do {
  100. struct dirent ent;
  101. ret = syscall_readdir(initd_dir, ++count, &ent);
  102. } while (ret > 0);
  103. /* Read each directory entry */
  104. struct dirent entries[count];
  105. do {
  106. syscall_readdir(initd_dir, i, &entries[i]);
  107. i++;
  108. } while (i < count);
  109. /* Sort the directory entries */
  110. int comparator(const void * c1, const void * c2) {
  111. const struct dirent * d1 = c1;
  112. const struct dirent * d2 = c2;
  113. return strcmp(d1->d_name, d2->d_name);
  114. }
  115. qsort(entries, count, sizeof(struct dirent), comparator);
  116. /* Run scripts */
  117. for (int i = 0; i < count; ++i) {
  118. if (entries[i].d_name[0] != '.') {
  119. char path[256];
  120. sprintf(path, "/etc/startup.d/%s", entries[i].d_name);
  121. start_options((char *[]){path, NULL});
  122. }
  123. }
  124. }
  125. /* Self-explanatory */
  126. syscall_reboot();
  127. return 0;
  128. }