init.c 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137
  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. syscall_open("/dev/null", 0, 0);
  45. syscall_open("/dev/null", 1, 0);
  46. syscall_open("/dev/null", 1, 0);
  47. }
  48. /* Run a startup script and wait for it to finish */
  49. int start_options(char * args[]) {
  50. /* Fork child to run script */
  51. int cpid = syscall_fork();
  52. /* Child process... */
  53. if (!cpid) {
  54. /* Pass environment from init to child */
  55. syscall_execve(args[0], args, environ);
  56. /* exec failed, exit this subprocess */
  57. syscall_exit(0);
  58. }
  59. /* Wait for the child process to finish */
  60. int pid = 0;
  61. do {
  62. /*
  63. * Wait, ignoring kernel threads
  64. * (which also end up as children to init)
  65. */
  66. pid = waitpid(-1, NULL, WNOKERN);
  67. if (pid == -1 && errno == ECHILD) {
  68. /* There are no more children */
  69. break;
  70. }
  71. if (pid == cpid) {
  72. /* The child process finished */
  73. break;
  74. }
  75. /* Continue while no error (or error was "interrupted") */
  76. } while ((pid > 0) || (pid == -1 && errno == EINTR));
  77. return cpid;
  78. }
  79. int main(int argc, char * argv[]) {
  80. /* Initialize stdin/out/err */
  81. set_console();
  82. /* Get directory listing for /etc/startup.d */
  83. int initd_dir = syscall_open(INITD_PATH, 0, 0);
  84. if (initd_dir < 0) {
  85. /* No init scripts; try to start getty as a fallback */
  86. start_options((char *[]){"/bin/getty",NULL});
  87. } else {
  88. int count = 0, i = 0, ret = 0;
  89. /* Figure out how many entries we have with a dry run */
  90. do {
  91. struct dirent ent;
  92. ret = syscall_readdir(initd_dir, ++count, &ent);
  93. } while (ret > 0);
  94. /* Read each directory entry */
  95. struct dirent entries[count];
  96. do {
  97. syscall_readdir(initd_dir, i, &entries[i]);
  98. i++;
  99. } while (i < count);
  100. /* Sort the directory entries */
  101. int comparator(const void * c1, const void * c2) {
  102. const struct dirent * d1 = c1;
  103. const struct dirent * d2 = c2;
  104. return strcmp(d1->d_name, d2->d_name);
  105. }
  106. qsort(entries, count, sizeof(struct dirent), comparator);
  107. /* Run scripts */
  108. for (int i = 0; i < count; ++i) {
  109. if (entries[i].d_name[0] != '.') {
  110. char path[256];
  111. sprintf(path, "/etc/startup.d/%s", entries[i].d_name);
  112. start_options((char *[]){path, NULL});
  113. }
  114. }
  115. }
  116. /* Self-explanatory */
  117. syscall_reboot();
  118. return 0;
  119. }