unix/fiss-minit

minit.c in master
Repositories | Summary | Log | Files | README | COPYING

minit.c (14838B) download


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