The Pedigree Project  0.1
init/main.c
1 /*
2  * Copyright (c) 2008-2014, Pedigree Developers
3  *
4  * Please see the CONTRIB file in the root of the source tree for a full
5  * list of contributors.
6  *
7  * Permission to use, copy, modify, and distribute this software for any
8  * purpose with or without fee is hereby granted, provided that the above
9  * copyright notice and this permission notice appear in all copies.
10  *
11  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18  */
19 
20 #include <ctype.h>
21 #include <dirent.h>
22 #include <errno.h>
23 #include <fcntl.h>
24 #include <libgen.h>
25 #include <limits.h>
26 #include <signal.h>
27 #include <stdint.h>
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <sys/klog.h>
32 #include <sys/stat.h>
33 #include <sys/time.h>
34 #include <sys/wait.h>
35 #include <unistd.h>
36 #include <utmp.h>
37 #include <utmpx.h>
38 
39 extern void pedigree_reboot();
40 
41 static int g_Running = 1;
42 
43 // SIGTERM handler - shutdown.
44 static void sigterm(int sig)
45 {
47  g_Running = 0;
48 }
49 
50 static pid_t start(const char *proc)
51 {
52  pid_t f = fork();
53  if (f == -1)
54  {
55  klog(LOG_ALERT, "init: fork failed %s", strerror(errno));
56  exit(errno);
57  }
58  if (f == 0)
59  {
60  klog(LOG_INFO, "init: starting %s...", proc);
61  execl(proc, proc, 0);
62  klog(LOG_INFO, "init: loading %s failed: %s", proc, strerror(errno));
63  exit(errno);
64  }
65  klog(LOG_INFO, "init: %s running with pid %d", proc, f);
66 
67  // Avoid calling basename() on the given parameter, as basename is
68  // non-const.
69  char basename_buf[PATH_MAX];
70  strncpy(basename_buf, proc, PATH_MAX);
71 
72  // Add a utmp entry.
73  setutxent();
74  struct utmpx init;
75  struct timeval tv;
76  memset(&init, 0, sizeof(init));
77  gettimeofday(&tv, NULL);
78  init.ut_type = INIT_PROCESS;
79  init.ut_pid = f;
80  init.ut_tv = tv;
81  strncpy(init.ut_id, basename(basename_buf), UT_LINESIZE);
82  pututxline(&init);
83  endutxent();
84 
85  return f;
86 }
87 
88 static void startAndWait(const char *proc)
89 {
90  pid_t f = start(proc);
91  waitpid(f, 0, 0);
92 }
93 
94 static void runScripts()
95 {
96  struct dirent **namelist;
97 
98  int count = scandir("/system/initscripts", &namelist, 0, alphasort);
99  if (count < 0)
100  {
101  klog(
102  LOG_CRIT, "could not scan /system/initscripts: %s",
103  strerror(errno));
104  }
105  else
106  {
107  for (int i = 0; i < count; ++i)
108  {
109  char script[PATH_MAX];
110  snprintf(
111  script, PATH_MAX, "/system/initscripts/%s",
112  namelist[i]->d_name);
113 
114  if (!strcmp(namelist[i]->d_name, ".") ||
115  !strcmp(namelist[i]->d_name, ".."))
116  {
117  free(namelist[i]);
118  continue;
119  }
120 
121  free(namelist[i]);
122 
123  struct stat st;
124  int r = stat(script, &st);
125  if (r == 0)
126  {
127  if (S_ISREG(st.st_mode) &&
128  (st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)))
129  {
130  // OK - we can run this.
131  klog(LOG_INFO, "init: running %s", script);
132  startAndWait(script);
133  }
134  else
135  {
136  klog(
137  LOG_INFO,
138  "init: not running %s (not a file, or not executable)",
139  script);
140  }
141  }
142  else
143  {
144  klog(
145  LOG_INFO, "init: cannot stat %s (broken symlink?)", script);
146  }
147  }
148 
149  free(namelist);
150  }
151 }
152 
153 int main(int argc, char **argv)
154 {
155  klog(LOG_INFO, "init: starting...");
156 
157  // Make sure we have a utmp file.
158  int fd = open(UTMP_FILE, O_CREAT | O_RDWR, 0664);
159  if (fd >= 0)
160  close(fd);
161 
162  // Set default umask to be inherited by all our children.
163  umask(0022);
164 
165  // Set up utmp.
166  setutxent();
167 
168  // Boot time (for uptime etc).
169  struct timeval tv;
170  gettimeofday(&tv, NULL);
171 
172  struct utmpx boot;
173  memset(&boot, 0, sizeof(boot));
174  boot.ut_type = BOOT_TIME;
175  boot.ut_tv = tv;
176  pututxline(&boot);
177 
178  // All done with utmp.
179  endutxent();
180 
181  // Prepare signals.
182  signal(SIGTERM, sigterm);
183 
184 #ifdef HOSTED
185  // Reboot the system instead of starting up.
186  klog(LOG_INFO, "init: hosted build, triggering a reboot");
187  pedigree_reboot();
188 #else
189  runScripts();
190 #endif
191 
192  // Done, enter PID reaping loop.
193  klog(LOG_INFO, "init: complete!");
194  while (1)
195  {
198  int status = 0;
199  pid_t changer = waitpid(-1, &status, g_Running == 0 ? WNOHANG : 0);
200  if (changer > 0)
201  {
202  klog(
203  LOG_INFO, "init: child %d exited with status %d", changer,
204  WEXITSTATUS(status));
205  }
206  else if (!g_Running)
207  {
208  klog(
209  LOG_INFO, "init: no more children and have been asked to "
210  "terminate, terminating...");
211  break;
212  }
213  else
214  {
215  continue;
216  }
217 
218  // Register the dead process now.
219  struct utmpx *p = 0;
220  setutxent();
221  do
222  {
223  p = getutxent();
224  if (p && (p->ut_type == INIT_PROCESS && p->ut_pid == changer))
225  break;
226  } while (p);
227 
228  if (!p)
229  {
230  endutxent();
231  continue;
232  }
233 
234  setutxent();
235  struct utmpx dead;
236  memset(&dead, 0, sizeof(dead));
237  gettimeofday(&tv, NULL);
238  dead.ut_type = DEAD_PROCESS;
239  dead.ut_pid = changer;
240  dead.ut_tv = tv;
241  strncpy(dead.ut_id, p->ut_id, UT_LINESIZE);
242  pututxline(&dead);
243  endutxent();
244  }
245  return 0;
246 }