fiss-minit

A standalone service supervisor based on minit
Log | Files | Refs | README | LICENSE

commit a17662d10c520f18054ad4c4d9ac2445c0f656b0
Author: leitner <leitner>
Date:   Fri,  2 Feb 2001 19:14:53 +0000

Initial revision

Diffstat:
AMakefile | 24++++++++++++++++++++++++
AREADME | 39+++++++++++++++++++++++++++++++++++++++
Aminit.c | 367+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Amsvc.c | 187+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aopenreadclose.c | 22++++++++++++++++++++++
Asplit.c | 26++++++++++++++++++++++++++
At.c | 7+++++++
7 files changed, 672 insertions(+), 0 deletions(-)

diff --git a/Makefile b/Makefile @@ -0,0 +1,24 @@ +all: minit msvc + +minit: minit.o split.o openreadclose.o + gcc -g -o minit $^ + +msvc: msvc.o + gcc -g -o msvc msvc.o + +%.o: %.c + gcc -pipe -g -c $^ + +diet: minit.c split.o openreadclose.o + gcc -nostdlib -o minit -pipe -Os -m386 minit.c split.c openreadclose.c ../dietlibc/start.o ../dietlibc/dietlibc.a + gcc -nostdlib -o msvc -pipe -Os -m386 msvc.c ../dietlibc/start.o ../dietlibc/dietlibc.a + strip -R .note -R .comment minit msvc + +diet2: minit.c split.o openreadclose.o + gcc -nostdlib -g -o minit -pipe minit.c split.c openreadclose.c ../dietlibc/start.o ../dietlibc/dietlibc.a + +clean: + rm -f *.o minit msvc + +test: test.c + gcc -nostdlib -o $@ $^ -I../dietlibc/include ../dietlibc/start.o ../dietlibc/dietlibc.a diff --git a/README b/README @@ -0,0 +1,39 @@ +Each service gets its own directory under /etc/minit (change this in the +source, it's a #define right at the start of minit.c). + +Each service directory can contain the following files/symlinks: + + depends + + a plain text file containing a service name per line. + Example: /etc/minit/sshd/depends could contain "network". + Each of these services will be started before this service is + started. If you need to wait for static initializations to + complete, use the sync flag. + + run + + a symbolic link to the program name. No hard link, because argv[0] + for the programs is created by extracting the part after the last + slash in the contents of the symbolic link. + Example: "/usr/bin/sshd" would be run with argv[0]="sshd". + + params + + a plain text file containing command line parameters for the + service, one parameter per line. No shell expansion is done. If + you need shell expansion, have run point to a shell script instead + of the real daemon. Note: Have the shell script exec the daemon + instead of simply running it to save system ressources. + + respawn + + touch this file to make minit respawn the process when it dies. + This should be touched for getty and network servers. + + sync + + touch this file to make minit wait until the service ends. sync is + mutually exclusive with respawn. This is meant for static + initializations like "ifconfig". + diff --git a/minit.c b/minit.c @@ -0,0 +1,367 @@ +#include <sys/types.h> +#include <time.h> +#include <string.h> +#include <sys/fcntl.h> +#include <unistd.h> +#include <limits.h> +#include <errno.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <sys/poll.h> +#include <signal.h> +#include <sys/wait.h> +#include <stdio.h> + +#define MINITROOT "/etc/minit" + +#undef printf +extern int printf(const char *format,...); + +extern int openreadclose(char *fn, char **buf, unsigned long *len); +extern char **split(char *buf,int c,int *len,int plus,int ofs); + +extern char **environ; + +static struct process { + char *name; +/* char **argv; */ + pid_t pid; + char respawn; + char circular; + time_t startedat; +} *root; + +static int maxprocess=-1; + +static int processalloc=0; + +/* return index of service in process data structure or -1 if not found */ +int findservice(char *service) { + int i; + for (i=0; i<=maxprocess; i++) { + if (!strcmp(root[i].name,service)) + return i; + } + return -1; +} + +/* look up process index in data structure by PID */ +int findbypid(pid_t pid) { + int i; + for (i=0; i<=maxprocess; i++) { + if (root[i].pid == pid) + return i; + } + return -1; +} + +/* clear circular dependency detection flags */ +void circsweep() { + int i; + for (i=0; i<=maxprocess; i++) + root[i].circular=0; +} + +/* add process to data structure, return index or -1 */ +int addprocess(struct process *p) { + if (maxprocess+1>=processalloc) { + struct process *fump; + processalloc+=8; + if ((fump=(struct process *)realloc(root,processalloc*sizeof(struct process)))==0) return -1; + root=fump; + } + memmove(&root[++maxprocess],p,sizeof(struct process)); + return maxprocess; +} + +/* load a service into the process data structure and return index or -1 + * if failed */ +int loadservice(char *service) { + struct process tmp; + int fd; + if (*service==0) return 0; + fd=findservice(service); + if (fd>=0) return fd; + if (chdir(MINITROOT) || chdir(service)) return -1; + if (!(tmp.name=strdup(service))) return -1; + tmp.pid=0; + fd=open("respawn",O_RDONLY); + if (fd>=0) { + tmp.respawn=1; + close(fd); + } else + tmp.respawn=0; + tmp.startedat=0; + tmp.circular=0; + addprocess(&tmp); +} + +/* usage: isup(findservice("sshd")). + * returns nonzero if process is up */ +int isup(int service) { + if (service<0) return 0; + return (root[service].pid!=0); +} + +/* called from inside the service directory, return the PID or 0 on error */ +pid_t forkandexec(int pause) { + char **argv=0; + int count=0; + pid_t p; + int fd; + unsigned long len; + char *s=0; + int argc; + char *argv0=0; + +again: + switch (p=fork()) { + case -1: + if (count>3) return 0; + sleep(++count*2); + goto again; + case 0: + /* child */ + if (pause) { + struct timespec req; + req.tv_sec=0; + req.tv_nsec=500000000; + nanosleep(&req,0); + } + if (!openreadclose("params",&s,&len)) { + argv=split(s,'\n',&argc,2,1); + if (argv[argc-1]) argv[argc-1]=0; else argv[argc]=0; + } else { + argv=(char**)malloc(2*sizeof(char*)); + argv[1]=0; + } + argv0=(char*)malloc(PATH_MAX+1); + if (!argv || !argv0) goto abort; + if (readlink("run",argv0,PATH_MAX)<0) { + if (errno!=EINVAL) goto abort; /* not a symbolic link */ + argv0=strdup("./run"); + } +/* chdir("/"); */ + argv[0]=strrchr(argv0,'/'); + if (argv[0]) + argv[0]++; + else + argv[0]=argv0; + execve("run",argv,environ); + _exit(0); + abort: + free(argv0); + free(argv); + return 0; + default: + fd=open("sync",O_RDONLY); + if (fd>=0) { + close(fd); + waitpid(p,0,0); + } + return p; + } +} + +/* start a service, return nonzero on error */ +int startnodep(int service,int pause) { + pid_t p; + /* step 1: see if the process is already up */ + if (isup(service)) return 0; +#if 0 + printf("launching %s\n",root[service].name); +#endif + /* step 2: fork and exec service, put PID in data structure */ + if (chdir(MINITROOT) || chdir(root[service].name)) return -1; + root[service].startedat=time(0); + root[service].pid=forkandexec(pause); + return root[service].pid; +} + +int startservice(int service,int pause) { + int dir=-1,fd; + unsigned long len; + char *s=0; + pid_t pid; + if (service<0) return 0; +#if 0 + write(1,"startservice ",13); + write(1,root[service].name,strlen(root[service].name)); + write(1,"\n",1); +#endif + if (chdir(MINITROOT) || chdir(root[service].name)) return -1; + if (root[service].circular) + return 0; + root[service].circular=1; + if ((dir=open(".",O_RDONLY))>=0) { + if (!openreadclose("depends",&s,&len)) { + char **argv; + int argc,i; + argv=split(s,'\n',&argc,0,0); + for (i=0; i<argc; i++) + startservice(loadservice(argv[i]),0); + fchdir(dir); + } + pid=startnodep(service,pause); + +#if 0 + write(1,"started service ",17); + write(1,root[service].name,strlen(root[service].name)); + write(1," -> ",4); + { + char buf[10]; + snprintf(buf,10,"%d\n",pid); + write(1,buf,strlen(buf)); + } +#endif + close(dir); + dir=-1; + return pid; + } + return 0; +} + +void sulogin() { /* exiting on an initialization failure is not a good idea for init */ + char *argv[]={"sulogin",0}; + execve("/sbin/sulogin",argv,environ); + exit(1); +} + +void childhandler() { + int status,i; + pid_t killed; +#undef debug +#ifdef debug + write(2,"wait...",7); +#endif + killed=waitpid(-1,&status,WNOHANG); +#ifdef debug + { + char buf[50]; + snprintf(buf,50," %d\n",killed); + write(2,buf,strlen(buf)); + } +#endif + if (killed == (pid_t)-1) { + write(2,"all services exited.\n",21); + exit(0); + } + if (killed==0) return; + i=findbypid(killed); +#if 0 + printf("%d exited, idx %d -> service %s\n",killed,i,i>=0?root[i].name:"[unknown]"); +#endif + if (i>=0) { + root[i].pid=0; + if (root[i].respawn) { +#if 0 + printf("restarting %s\n",root[i].name); +#endif + circsweep(); + startservice(i,time(0)-root[i].startedat<1); + } else + root[i].pid=1; + } +} + +static volatile int dowait=0; + +void sigchild(int whatever) { dowait=1; } + +main(int argc, char *argv[]) { + /* Schritt 1: argv[1] als Service nehmen und starten */ + int count=0; + int i; + int infd=open("/etc/minit/in",O_RDWR),outfd=open("/etc/minit/out",O_RDWR|O_NONBLOCK); + struct pollfd pfd; +/* int s=bindsocket(); */ +/* printf("%d %d\n",infd,outfd); + exit(0); */ + int nfds=1; + + signal(SIGCHLD,sigchild); + if (infd<0 || outfd<0) { + puts("minit: could not open /etc/minit/in or /etc/minit/out\n"); + nfds=0; + } else + pfd.fd=infd; + pfd.events=POLLIN; + + for (i=1; i<argc; i++) { + circsweep(); + if (startservice(loadservice(argv[i]),0)) count++; + } + circsweep(); + if (!count) startservice(loadservice("default"),0); + for (;;) { + int status; + int i; + char buf[1501]; +/* if (dowait) { + dowait=0; */ + childhandler(); +/* } */ + switch (poll(&pfd,nfds,500)) { + case -1: + if (errno==EINTR) { + childhandler(); + break; + } + puts("poll failed!\n"); + sulogin(); + /* what should we do if poll fails?! */ + break; + case 1: + i=read(infd,buf,1500); + if (i>1) { + pid_t pid; + int idx,tmp; + buf[i]=0; + +/* write(1,buf,strlen(buf)); write(1,"\n",1); */ + if (buf[0]!='s' && ((idx=findservice(buf+1))<0)) +error: + write(outfd,"0",1); + else { + switch (buf[0]) { + case 'p': + i=snprintf(buf,10,"%d",root[idx].pid); + write(outfd,buf,strlen(buf)); + break; + case 'r': + root[idx].respawn=0; + goto ok; + case 'R': + root[idx].respawn=1; + goto ok; + case 'P': + tmp=strtol(buf+strlen(buf)+1,0); + if (tmp>0) pid=tmp; + root[idx].pid=tmp; + goto ok; + case 's': + idx=loadservice(buf+1); + if (idx<0) goto error; + if (root[idx].pid<2) { + root[idx].pid=0; + circsweep(); + idx=startservice(idx,0); + if (idx==0) { + write(outfd,"0",1); + break; + } + } +ok: + write(outfd,"1",1); + break; + case 'u': + snprintf(buf,10,"%d",time(0)-root[idx].startedat); + write(outfd,buf,strlen(buf)); + } + } + } + break; + default: + } + } +} diff --git a/msvc.c b/msvc.c @@ -0,0 +1,187 @@ +#include <sys/fcntl.h> +#include <sys/file.h> +#include <signal.h> +#include <stdio.h> + +static int infd,outfd; + +static char buf[1500]; + +unsigned int fmt_ulong(register char *s,register unsigned long u) +{ + register unsigned int len; register unsigned long q; + len = 1; q = u; + while (q > 9) { ++len; q /= 10; } + if (s) { + s += len; + do { *--s = '0' + (u % 10); u /= 10; } while(u); /* handles u == 0 */ + } + return len; +} + +/* return PID, 0 if error */ +pid_t getpid(char *service) { + int len; + buf[0]='p'; + strncpy(buf+1,service,1400); + write(infd,buf,strlen(buf)); + len=read(outfd,buf,1500); + if (len<0) return 0; + buf[len]=0; + return atoi(buf); +} + +/* return nonzero if error */ +int respawn(char *service,int yesno) { + int len; + buf[0]=yesno?'R':'r'; + strncpy(buf+1,service,1400); + write(infd,buf,strlen(buf)); + len=read(outfd,buf,1500); + return (len!=1 || buf[0]=='0'); +} + +/* return nonzero if error */ +int setpid(char *service, pid_t pid) { + char *tmp; + int len; + buf[0]='P'; + tmp=buf+1+strncpy(buf+1,service,1400); + tmp[fmt_ulong(tmp,pid)]=0; + write(infd,buf,strlen(buf)); + len=read(outfd,buf,1500); + return (len!=1 || buf[0]=='0'); +} + +/* return nonzero if error */ +int startservice(char *service) { + int len; + buf[0]='s'; + strncpy(buf+1,service,1400); + write(infd,buf,strlen(buf)); + len=read(outfd,buf,1500); + return (len!=1 || buf[0]=='0'); +} + +/* return uptime, 0 if error */ +unsigned long uptime(char *service) { + int len; + buf[0]='u'; + strncpy(buf+1,service,1400); + write(infd,buf,strlen(buf)); + len=read(outfd,buf,1500); + if (len<0) return 0; + buf[len]=0; + return atoi(buf); +} + +main(int argc,char *argv[]) { + int len; + if (argc<2) { + puts("usage: msvc -[uodpchaitko] service\n" + " msvc -Ppid service\n" + " -u up; start service with respawn\n" + " -o once; start service without respawn\n" + " -d down; disable respawn, stop service\n" + " -p pause; send SIGSTOP\n" + " -c continue; send SIGCONT\n" + " -h hangup; send SIGHUP\n" + " -a alarm; send SIGALRM\n" + " -i intr; send SIGINT\n" + " -t terminate; send SIGTERM\n" + " -k kill; send SIGKILL\n"); + } + infd=open("/etc/minit/in",O_WRONLY); + outfd=open("/etc/minit/out",O_RDONLY); + if (infd>=0) { + while (lockf(infd,F_LOCK,1)) { + puts("could not aquire lock!"); + sleep(1); + } + if (argc==2) { + pid_t pid=getpid(argv[1]); + if (buf[0]!='0') { + unsigned long len; + write(1,argv[1],strlen(argv[1])); + write(1,": ",2); + if (pid==0) write(1,"down ",5); + else if (pid==1) write(1,"finished ",9); + else { + write(1,"up (pid ",8); + snprintf(buf,30,"%d) ",pid); + write(1,buf,strlen(buf)); + } + len=uptime(argv[1]); + snprintf(buf,30,"%d seconds\n",len); + write(1,buf,strlen(buf)); +/* puts(buf); */ + } + } else { + int i; + int sig=0; + pid_t pid; + if (argv[1][0]=='-') { + switch (argv[1][1]) { + case 'p': sig=SIGSTOP; goto dokill; break; + case 'c': sig=SIGCONT; goto dokill; break; + case 'h': sig=SIGHUP; goto dokill; break; + case 'a': sig=SIGALRM; goto dokill; break; + case 'i': sig=SIGINT; goto dokill; break; + case 't': sig=SIGTERM; goto dokill; break; + case 'k': sig=SIGKILL; goto dokill; break; + case 'o': /* TODO: start but don't restart */ + if (startservice(argv[2]) || respawn(argv[2],0)) + fprintf(stderr,"Could not start %s\n",argv[2]); + break; + case 'd': /* TODO: down */ + pid=getpid(argv[2]); + if (pid==0) { + puts("service not found"); + return 1; + } else if (pid==1) + return 0; + if (respawn(argv[2],0) || kill(pid,SIGTERM)); + break; + case 'u': /* TODO: up */ + if (startservice(argv[2]) || respawn(argv[2],1)) + fprintf(stderr,"Could not start %s\n",argv[2]); + break; + case 'P': + pid=atoi(argv[1]+2); + if (pid>1) + if (setpid(argv[2],pid)) + fprintf(stderr,"Could not set pid of service %s\n",argv[2]); + } + } + return 0; +dokill: + for (i=2; i<=argc; i++) { + pid=getpid(argv[2]); + if (kill(pid,sig)) { + fprintf(stderr,"Could not send signal to PID %d\n",pid); + } + } + } + } +} + +/* + -u Up. If the service is not running, start it. If the service stops, + restart it. + -d Down. If the service is running, send it a TERM signal and then a CONT + signal. After it stops, do not restart it. + -o Once. If the service is not running, start it. Do not restart it if it + stops. + -r Tell supervise that the service is normally running; this affects status + messages. + -s Tell supervise that the service is normally stopped; this affects status + messages. + -p Pause. Send the service a STOP signal. + -c Continue. Send the service a CONT signal. + -h Hangup. Send the service a HUP signal. + -a Alarm. Send the service an ALRM signal. + -i Interrupt. Send the service an INT signal. + -t Terminate. Send the service a TERM signal. + -k Kill. Send the service a KILL signal. + -x Exit. supervise will quit as soon as the service is down. +*/ diff --git a/openreadclose.c b/openreadclose.c @@ -0,0 +1,22 @@ +#ifndef EMBEDDED +#include <unistd.h> +#include <sys/fcntl.h> +#endif + +int openreadclose(char *fn, char **buf, unsigned long *len) { + int fd=open(fn,O_RDONLY); + if (fd<0) return -1; + if (!*buf) { + *len=lseek(fd,0,SEEK_END); + lseek(fd,0,SEEK_SET); + *buf=(char*)malloc(*len+1); + if (!*buf) { + close(fd); + return -1; + } + } + *len=read(fd,*buf,*len); + if (*len != (unsigned long)-1) + (*buf)[*len]=0; + return close(fd); +} diff --git a/split.c b/split.c @@ -0,0 +1,26 @@ + +/* split buf into n strings that are separated by c. return n as *len. + * Allocate plus more slots and leave the first ofs of them alone. */ +char **split(char *buf,int c,int *len,int plus,int ofs) { + int n=1; + char **v=0; + char **w; + /* step 1: count tokens */ + char *s; + for (s=buf; *s; s++) if (*s==c) n++; + /* step 2: allocate space for pointers */ + v=(char **)malloc((n+plus)*sizeof(char*)); + if (!v) return 0; + w=v+ofs; + *w++=buf; + for (s=buf; ; s++) { + while (*s && *s!=c) s++; + if (*s==0) break; + if (*s==c) { + *s=0; + *w++=s+1; + } + } + *len=w-v; + return v; +} diff --git a/t.c b/t.c @@ -0,0 +1,7 @@ +#include <sys/types.h> +#include <stdio.h> + +main() { + pid_t p=30056; + printf("spawned pid %d\n",p); +}