minit-0.10/0000755000175000017500000000000010225537075011526 5ustar ericherichminit-0.10/Makefile0000644000175000017500000000507210225537064013170 0ustar ericherichall: minit msvc pidfilehack hard-reboot write_proc killall5 shutdown \ minit-update serdo #CFLAGS=-pipe -march=i386 -fomit-frame-pointer -Os -I../dietlibc/include CC=gcc CFLAGS=-Wall -W -pipe -fomit-frame-pointer -Os CROSS= #CROSS=arm-linux- LDFLAGS=-s MANDIR=/usr/man path = $(subst :, ,$(PATH)) diet_path = $(foreach dir,$(path),$(wildcard $(dir)/diet)) ifeq ($(strip $(diet_path)),) ifneq ($(wildcard /opt/diet/bin/diet),) DIET=/opt/diet/bin/diet else DIET= endif else DIET:=$(strip $(diet_path)) endif ifneq ($(DEBUG),) CFLAGS+=-g LDFLAGS+=-g else CFLAGS+=-O2 -fomit-frame-pointer LDFLAGS+=-s ifneq ($(DIET),) DIET+=-Os endif endif LDLIBS=-lowfat libowfat_path = $(strip $(foreach dir,../libowfat*,$(wildcard $(dir)/textcode.h))) ifneq ($(libowfat_path),) CFLAGS+=$(foreach fnord,$(libowfat_path),-I$(dir $(fnord))) LDFLAGS+=$(foreach fnord,$(libowfat_path),-L$(dir $(fnord))) endif minit: minit.o split.o openreadclose.o opendevconsole.o msvc: msvc.o minit-update: minit-update.o split.o openreadclose.o serdo: serdo.o shutdown: shutdown.o split.o openreadclose.o opendevconsole.o $(DIET) $(CROSS)$(CC) $(LDFLAGS) -o shutdown $^ %.o: %.c $(DIET) $(CROSS)$(CC) $(CFLAGS) -c $< %: %.o $(DIET) $(CROSS)$(CC) $(LDFLAGS) -o $@ $^ $(LDLIBS) clean: rm -f *.o minit msvc pidfilehack hard-reboot write_proc killall5 \ shutdown minit-update serdo test: test.c gcc -nostdlib -o $@ $^ -I../dietlibc/include ../dietlibc/start.o ../dietlibc/dietlibc.a pidfilehack: pidfilehack.c $(DIET) $(CROSS)$(CC) $(CFLAGS) -o $@ $^ hard-reboot: hard-reboot.c $(DIET) $(CROSS)$(CC) $(CFLAGS) -o $@ $^ write_proc: write_proc.c $(DIET) $(CROSS)$(CC) $(CFLAGS) -o $@ $^ killall5: killall5.c $(DIET) $(CROSS)$(CC) $(CFLAGS) -o $@ $^ install-files: install -d $(DESTDIR)/etc/minit $(DESTDIR)/sbin $(DESTDIR)/bin $(DESTDIR)$(MANDIR)/man8 install minit pidfilehack $(DESTDIR)/sbin install write_proc hard-reboot minit-update $(DESTDIR)/sbin install msvc serdo $(DESTDIR)/bin install -m 4750 shutdown $(DESTDIR)/sbin test -f $(DESTDIR)/sbin/init || ln $(DESTDIR)/sbin/minit $(DESTDIR)/sbin/init install -m 644 hard-reboot.8 minit-list.8 minit-shutdown.8 minit-update.8 minit.8 msvc.8 pidfilehack.8 serdo.8 $(DESTDIR)$(MANDIR)/man8 install-fifos: -mkfifo -m 600 $(DESTDIR)/etc/minit/in $(DESTDIR)/etc/minit/out install: install-files install-fifos VERSION=minit-$(shell head -n 1 CHANGES|sed 's/://') CURNAME=$(notdir $(shell pwd)) tar: clean rename cd ..; tar cvvf $(VERSION).tar.bz2 --use=bzip2 --exclude CVS $(VERSION) rename: if test $(CURNAME) != $(VERSION); then cd .. && mv $(CURNAME) $(VERSION); fi minit-0.10/contrib/0000755000175000017500000000000010225536763013171 5ustar ericherichminit-0.10/contrib/minit-graph0000644000175000017500000000176107652006451015334 0ustar ericherich#!/usr/bin/python # Contributed by Tommi Virtanen # Try: # minit-graph /etc/minit >foo.dot # dot -Tps -o foo.ps foo.dot # gv foo.ps # dot is part of graphviz (http://www.graphviz.org/) --fefe import os, errno, string def safe(s): r="" for c in s: if c not in "abcdefghijklmnopqrstuvwxyz0123456789_": c="_" r+=c return r import sys try: dir = sys.argv[1] except IndexError: dir = '.' print "digraph minit {" for svc in os.listdir(dir): if svc=="in" or svc=="out": continue if os.path.exists(os.path.join(dir, svc, "sync")): print "%s [shape=box];"%safe(svc) else: print "%s;"%safe(svc) try: file = open(os.path.join(dir, svc, "depends")) except IOError, e: if e.errno == errno.ENOENT: pass else: raise else: for dep in file.xreadlines(): print "%s -> %s;" % (safe(svc), safe(dep.strip())) file.close() print "};" minit-0.10/contrib/ctrlaltdel-run0000644000175000017500000000115307653207120016041 0ustar ericherich#!/bin/sh # if umount -a fails give services additional time to shut down fail(){ echo umount failed, try again. sleep 1 sync umount -a || sleep 3 } # Shut down "respawn" services - except getty/1. for i in `find /etc/minit -type f -name respawn | sed 's@/respawn$@@' | grep -v ^/etc/minit/getty/1$`;do msvc -d "$i" done #send sigterm to all processes. /sbin/killall5 -15 #send sighup to all processes. /sbin/killall5 -1 sync # umount everything, mount "/" readonly /bin/umount -a || fail /sbin/swapoff -a # power down. # params should contain one of "RESTART", "HALT" or "POWER_OFF" exec /sbin/hard-reboot $1 minit-0.10/write_proc.c0000644000175000017500000000054207446720160014050 0ustar ericherich#include #include #include #define USAGE "write_proc \n" int main(int argc,char*argv[]) { int fd; if (argc!=3) goto usage; if ((fd=open(argv[2],O_WRONLY))==-1) goto usage; write(fd,argv[1],strlen(argv[1])); close(fd); return 0; usage: write(2,USAGE,strlen(USAGE)); return 1; } minit-0.10/t.c0000644000175000017500000000014707236603455012143 0ustar ericherich#include #include main() { pid_t p=30056; printf("spawned pid %d\n",p); } minit-0.10/split.c0000644000175000017500000000116007660251043013020 0ustar ericherich#include /* 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; } minit-0.10/shutdown.c0000644000175000017500000001474410006215555013550 0ustar ericherich/* * Notes: * - uncomment `#define ALLOW_SUID' below if you want users other than * root to be able to shut down the system. * - after compiling, install under /sbin/shutdown with chgrp adm and * chmod 4750 for SUID root, or 0700 for root only * - uncomment `#define USE_MINIT' below if you want to use shutdown * with minit. If defined, shutdown will try to bring down the services * halt (for -h or -o) or reboot (-r) before killing all other processes. * Please make sure that you have a depends in reboot and halt that * will bring down all respawning services. A respawning service during * shutdown might cause you to wait for a fsck during the next boot * - If you do not use minit shutdown will bring your system down similar * to SysV-Inits shutdown with -n * * TODO: * - add a function for wall-messages * - cleanup */ #include #include #include #include #include #include #include #include #include "str.h" #ifdef __dietlibc__ #include #else #include static inline int __write1(const char*s) { return write(1,s,str_len(s)); } static inline int __write2(const char*s) { return write(2,s,str_len(s)); } #endif #define ALLOW_SUID #define USE_MINIT #ifdef USE_MINIT #define NOVARS #include "minit.h" #endif extern void opendevconsole(); extern char **environ; 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 *optarg; void wall(char *buf) { __write2(buf); } int exec_cmd(char *cmd, ...) { char *argv[10]; va_list arguments; pid_t pid; int i; va_start(arguments, cmd); for (i=0;i<9 && (argv[i] = va_arg(arguments, char *)) != NULL; i++); argv[i] = NULL; va_end(arguments); pid = fork(); if (pid < 0) return -1; if (pid > 0) { wait(NULL); } else { execve(cmd, argv, environ); //perror("execvp failed"); exit(0); } return 0; } #ifdef USE_MINIT static int infd, outfd; static char buf[1500]; int minit_serviceDown(char *service) { char *s=0; unsigned long len; pid_t pid=0; if (!service || !*service) return 0; if (chdir(MINITROOT) || chdir(service)) return -1; if (!openreadclose("depends", &s, &len)) { char **deps; int depc, i; deps=split(s, '\n', &depc, 0, 0); for (i=0; i 1) { int i; __write2("\t--> "); __write2(service); buf[0]='r'; // we want to disable respawning first strncpy(buf+1, service, 1400); buf[1400]=0; write(infd, buf, str_len(buf)); read(outfd, buf, 1500); i=kill(pid, SIGTERM); if (i == 0) __write2("\t\tdone\n"); else __write2("\t\tfailed\n"); } return 0; } int minit_shutdown(int level) { int retval; __write2("Shutting down minit services: \n"); infd=open(MINITROOT "/in", O_WRONLY); outfd=open(MINITROOT "/out", O_RDONLY); if (infd>=0) { while (lockf(infd, F_TLOCK, 1)) { __write2("could not acquire lock!\n"); sleep(1); } } retval=minit_serviceDown(level?"halt":"reboot"); close(infd); close(outfd); return retval; } #endif void printUsage() { __write2("usage: shutdown -[rhosmn] -[t secs]\n" "\t -r: reboot after shutdown\n" "\t -h: halt after shutdown\n" "\t -o: power-off after shutdown\n" "\t -s: single-user console after shutdown\n" "\t -m: only shutdown the minit-part\n" "\t -n: do not shutdown services using minit\n" "\t -t secs: delay between SIGTERM and SIGKILL\n"); } int main(int argc, char *const argv[]) { int c; int cfg_downlevel=2; /* 0: reboot * 1: halt * 2: power off */ unsigned int cfg_delay = 3; int cfg_minitonly = 0; int cfg_sulogin = 0; #ifdef ALLOW_SUID setuid(geteuid()); #endif if (getuid() != 0) { __write2("you are not root, go away!\n"); return 1; } if (argc<2) { printUsage(); return 0; } /* parse commandline options */ while((c = getopt(argc, argv, "rhosmnt:")) != EOF) { switch(c) { case 'r': /* do we have to reboot... */ cfg_downlevel = 0; break; case 'h': /* ...halt.. */ cfg_downlevel = 1; break; case 's': /* rescue system */ cfg_sulogin = 1; break; case 'm': /* exit after minit down */ cfg_minitonly = 1; break; case 'o': /* ..or power off? */ cfg_downlevel = 2; break; case 't': /* delay between SIGTERM and SIGKILL */ cfg_delay = atoi(optarg); break; default: printUsage(); return 1; } } opendevconsole(); switch (cfg_downlevel) { case 0: wall("system is going down for reboot NOW\n"); break; case 1: wall("system is going down for system halt NOW\n"); break; case 2: wall("system is going down for power-off NOW\n"); break; } /* * catch some signals; * getting killed after killing the controlling terminal wouldn't be * such a great thing... */ signal(SIGQUIT, SIG_IGN); signal(SIGCHLD, SIG_IGN); signal(SIGHUP, SIG_IGN); signal(SIGTSTP, SIG_IGN); signal(SIGTTIN, SIG_IGN); signal(SIGTTOU, SIG_IGN); // real shutdown? then lets rock.. #ifdef USE_MINIT minit_shutdown(cfg_downlevel); if (cfg_minitonly) return 0; #endif /* kill all processes still left */ __write2("sending all processes the TERM signal...\n"); kill(-1, SIGTERM); sleep(cfg_delay); __write2("sending all processes the KILL signal...\n"); kill(-1, SIGKILL); if (cfg_sulogin) { char* suargs[]={"sulogin",0}; execve("/sbin/sulogin", suargs, 0); __write2("execve() /sbin/sulogin failed\n"); return 1; } /* sync buffers */ sync(); exec_cmd("/sbin/swapoff", "swapoff", "-a", (char *) 0); exec_cmd("/bin/umount", "umount", "-a", (char *) 0); exec_cmd("/bin/mount", "mount", "-o", "remount,ro", "/", (char *) 0); sync(); /* and finally reboot, halt or power-off the system */ if (cfg_downlevel == 0) { reboot(RB_AUTOBOOT); } else if (cfg_downlevel == 1) { reboot(RB_HALT_SYSTEM); } else { reboot(RB_POWER_OFF); } return 0; } minit-0.10/serdo.c0000644000175000017500000000611110210125446012772 0ustar ericherich#include #include #include #include #include #include #include #include #define MAXENV 256 char* envp[MAXENV+2]; int envc; int continueonerror; int envset(char* s) { int i,l; if (s[l=str_chr(s,'=')]!='=') return -1; ++l; for (i=0; i32768) die(1,"file ",s," is too large"); map=alloca(ss.st_size+1); if (read(fd,map,ss.st_size)!=(long)ss.st_size) diesys(1,"read error"); map[ss.st_size]=0; close(fd); return execute(map); } int main(int argc,char* argv[],char* env[]) { int r; (void)argc; if (argc<2) die(1,"usage: serdo [-c] filename"); errmsg_iam("serdo"); for (envc=0; envc #include #include #include #include /* purpose: argv[1] is the full path to a PID file, * argv+2 is the daemon to run. * the daemon is expected to fork in the background and write its PID in * the pid file. */ extern char** environ; int main(int argc, char* argv[]) { int count=0; if (argc<3) { write(1,"usage: pidfilehack service /var/run/daemon.pid /usr/sbin/daemon args...\n",72); return 0; } if (unlink(argv[2])) { if (errno!=ENOENT) { perror("could not remove pid file"); return 1; } } switch (fork()) { case -1: perror("could not fork"); return 2; case 0: /* child */ execve(argv[3],argv+3,environ); perror("execvp failed"); return 3; } do { int fd=open(argv[2],O_RDONLY); if (fd>=0) { static char buf[100] = "-P"; int len=read(fd,buf+2,98); close(fd); if (len>0) { char* _argv[] = { "msvc", 0, 0, 0 }; if (buf[len+1]=='\n') buf[len+1]=0; else buf[len+2]=0; _argv[1]=buf; _argv[2]=argv[1]; /* printf("execvp %s %s %s\n",_argv[0],_argv[1],_argv[2]); */ execvp(_argv[0],_argv); perror("execvp failed"); return 0; } /* else printf("file there but open returned %d\n",fd); */ } /* else printf("%s not there yet\n",argv[2]); */ sleep(1); if (++count>=30) exit(0); } while (1); } minit-0.10/pidfilehack.80000644000175000017500000000231210015236434014050 0ustar ericherich.TH pidfilehack 8 .SH NAME pidfilehack \- work around daemons that always fork .SH SYNOPSIS .B pidfilehack .I servicename .I pidfile .I command .I [parameters] .SH DESCRIPTION .B pidfilehack is used to work around daemons that insist on forking into the background, but that do write a correct pid file. pidfilehack forks the actual service, then waits for the pidfile to be written. Once it can read the pid from the pidfile it will tell minit the real pid and quit. .SH USAGE usually pidfilehack is symlinked as \fIrun\fR command of a service. .TP servicename the name of the service pidfilehack is installed for. .TP pidfile the filename to read the pid from .TP command the real command to start .TP parameters additional parameters for the command A typical use of this command will look like this: .TP /etc/minit/apache .TP /etc/minit/apache/params apache .br /var/run/apache.pid .br apachectl .br start .TP /etc/minit/apache/run -> /sbin/pidfilehack .SH AUTHOR minit was written by Felix von Leitner and can be downloaded from his page at .I http://www.fefe.de/minit/ This manpage was written by Erich Schubert for the Debian GNU/Linux operating system. .SH "SEE ALSO" msvc(8), pidfilehack(8) minit-0.10/openreadclose.c0000644000175000017500000000072007662452700014516 0ustar ericherich#ifndef EMBEDDED #include #include #include #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); } minit-0.10/opendevconsole.c0000644000175000017500000000031707664642212014721 0ustar ericherich#include #include void opendevconsole() { int fd; if ((fd=open("/dev/console",O_RDWR|O_NOCTTY))>=0) { dup2(fd,0); dup2(fd,1); dup2(fd,2); if (fd>2) close(fd); } } minit-0.10/msvc.c0000644000175000017500000001647410223034610012637 0ustar ericherich#include #include #include #include #include #include #include #include "str.h" #include "fmt.h" #include "buffer.h" #define NOVARS #include "minit.h" #include #include static int infd,outfd; static char buf[1500]; void addservice(char* service) { char* x; if (str_start(service,MINITROOT "/")) service+=sizeof(MINITROOT "/") -1; x=service+str_len(service)-1; while (x>service && *x=='/') { *x=0; --x; } strncpy(buf+1,service,1400); buf[1400]=0; } int addreadwrite(char* service) { addservice(service); write(infd,buf,str_len(buf)); return read(outfd,buf,1500); } /* return PID, 0 if error */ pid_t __readpid(char *service) { int len; buf[0]='p'; len=addreadwrite(service); 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'; len=addreadwrite(service); return (len!=1 || buf[0]=='0'); } /* return nonzero if error */ int setpid(char *service, pid_t pid) { char *tmp; int len; buf[0]='P'; addservice(service); tmp=buf+str_len(buf)+1; tmp[fmt_ulong(tmp,pid)]=0; write(infd,buf,str_len(buf)+str_len(tmp)+2); len=read(outfd,buf,1500); return (len!=1 || buf[0]=='0'); } /* return nonzero if error */ int check_remove(char *service) { int len; buf[0]='C'; len=addreadwrite(service); return (len!=1 || buf[0]=='0'); } /* return nonzero if error */ int startservice(char *service) { int len; buf[0]='s'; len=addreadwrite(service); return (len!=1 || buf[0]=='0'); } /* return uptime, 0 if error */ unsigned long uptime(char *service) { int len; buf[0]='u'; len=addreadwrite(service); if (len<0) return 0; buf[len]=0; return atoi(buf); } void dumphistory() { char tmp[16384]; int i,j; char first,last; first=1; last='x'; write(infd,"h",1); for (;;) { int prev,done; j=read(outfd,tmp,sizeof(tmp)); if (j<1) break; done=i=0; if (first) { if (tmp[0]=='0') { carp("minit compiled without history support."); return; } i+=2; } else { if (!tmp[0] && last=='\n') break; } prev=i; for (; i=0) { while (lockf(infd,F_LOCK,1)) { carp("could not acquire lock!"); sleep(1); } if (argc==2 && argv[1][1]!='H') { pid_t pid=__readpid(argv[1]); if (buf[0]!='0') { unsigned long len; unsigned long ut=uptime(argv[1]); if (isatty(1)) { char tmp[FMT_ULONG+20]; char tmp2[FMT_ULONG]; char* what; if (pid==0) what="down "; else if (pid==1) what="finished "; else { len=fmt_str(tmp,"up (pid "); len+=fmt_ulong(tmp+len,pid); tmp[len+fmt_str(tmp+len,") ")]=0; what=tmp; } tmp2[fmt_ulong(tmp2,ut)]=0; msg(argv[1],": ",what,tmp2," seconds"); } else { char tmp[FMT_ULONG*2+5]; len=fmt_ulong(tmp,pid); tmp[len]=' '; ++len; len+=fmt_ulong(tmp+len,ut); tmp[len]='\n'; write(1,tmp,len+1); } if (pid==0) return 2; else if (pid==1) return 3; else return 0; } else carp(argv[1],": no such service."); return 1; } else { int i; int ret=0; int sig=0; pid_t pid; if (argv[1][0]=='-') { switch (argv[1][1]) { case 'g': for (i=2; i1) if (setpid(argv[2],pid)) { carp("Could not set PID of service ",argv[2]); ret=1; } break; case 'H': dumphistory(); break; case 'D': dumpdependencies(argv[2]); break; } } return ret; dokill: for (i=2; i #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "fmt.h" #include "str.h" #include "minit.h" #define MALLOC_TEST #if !defined(__dietlibc__) && !defined(__GLIBC__) #undef MALLOC_TEST #endif #ifdef MALLOC_TEST extern void* __libc_malloc(size_t size); extern void* __libc_realloc(void* x,size_t size); extern void __libc_free(void* x); static char malloc_buf[2048]; static unsigned long n; static struct process procbuf[100]; void *malloc(size_t size) { if (n+size=malloc_buf && (char*)x=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 -1; 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; tmp.__stdin=0; tmp.__stdout=1; { char *logservice=alloca(str_len(service)+5); strcpy(logservice,service); strcat(logservice,"/log"); tmp.logservice=loadservice(logservice); if (tmp.logservice>=0) { int pipefd[2]; if (pipe(pipefd)) return -1; fcntl(pipefd[0],F_SETFD,FD_CLOEXEC); fcntl(pipefd[1],F_SETFD,FD_CLOEXEC); root[tmp.logservice].__stdin=pipefd[0]; tmp.__stdout=pipefd[1]; } } return 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); } int startservice(int service,int pause,int father); #undef debug void handlekilled(pid_t killed) { int i; #ifdef debug { char buf[50]; snprintf(buf,50," %d\n",killed); write(2,buf,str_len(buf)); } #endif if (killed == (pid_t)-1) { static int saidso; if (!saidso) { write(2,"all services exited.\n",21); saidso=1; } if (i_am_init) 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,root[i].father); } else { root[i].startedat=time(0); root[i].pid=1; } } } /* called from inside the service directory, return the PID or 0 on error */ pid_t forkandexec(int pause,int service) { 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 (pid_t)-1: if (count>3) return 0; sleep(++count*2); goto again; case 0: /* child */ if (i_am_init) { ioctl(0, TIOCNOTTY, 0); setsid(); opendevconsole(); /* ioctl(0, TIOCSCTTY, 1); */ tcsetpgrp(0, getpgrp()); } 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**)alloca(2*sizeof(char*)); argv[1]=0; } argv0=(char*)alloca(PATH_MAX+1); if (!argv || !argv0) _exit(1); if (readlink("run",argv0,PATH_MAX)<0) { if (errno!=EINVAL) _exit(1); /* not a symbolic link */ argv0=strdup("./run"); } /* chdir("/"); */ argv[0]=strrchr(argv0,'/'); if (argv[0]) argv[0]++; else argv[0]=argv0; if (root[service].__stdin != 0) { dup2(root[service].__stdin,0); fcntl(0,F_SETFD,0); } if (root[service].__stdout != 1) { dup2(root[service].__stdout,1); dup2(root[service].__stdout,2); fcntl(1,F_SETFD,0); fcntl(2,F_SETFD,0); } { int i; for (i=3; i<1024; ++i) close(i); } execve(argv0,argv,environ); _exit(1); default: fd=open("sync",O_RDONLY); if (fd>=0) { pid_t p2; close(fd); p2=waitpid(p,0,0); return 1; } return p; } } /* start a service, return nonzero on error */ int startnodep(int service,int pause) { /* step 1: see if the process is already up */ if (isup(service)) return 0; /* 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,service); return root[service].pid; } int startservice(int service,int pause,int father) { int dir=-1; unsigned long len; char *s=0; pid_t pid=0; if (service<0) return 0; if (root[service].circular) return 0; root[service].circular=1; #if 0 printf("setting father of %d (%s) to %d (%s)\n", service,root[service].name,father,father>=0?root[father].name:"[msvc]"); #endif root[service].father=father; #ifdef HISTORY { memmove(history+1,history,sizeof(int)*((HISTORY)-1)); history[0]=service; } #endif if (root[service].logservice>=0) startservice(root[service].logservice,pause,service); if (chdir(MINITROOT) || chdir(root[service].name)) return -1; if ((dir=open(".",O_RDONLY))>=0) { if (!openreadclose("depends",&s,&len)) { char **deps; int depc,i; deps=split(s,'\n',&depc,0,0); for (i=0; i=0 && root[Service].pid!=1 && !blacklisted) startservice(Service,0,service); } fchdir(dir); } pid=startnodep(service,pause); #if 0 write(1,"started service ",17); write(1,root[service].name,str_len(root[service].name)); write(1," -> ",4); { char buf[10]; snprintf(buf,10,"%d\n",pid); write(1,buf,str_len(buf)); } #endif close(dir); dir=-1; } chdir(MINITROOT); return pid; } 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); } static void _puts(const char* s) { write(1,s,str_len(s)); } void childhandler() { int status; pid_t killed; #ifdef debug write(2,"wait...",7); #endif #if 0 if (getpid()!=1) { char buf[100]; _puts("childhandler() called from pid "); buf[fmt_ulong(buf,getpid())]=0; _puts(buf); _puts("\n"); return; } #endif #ifdef UPDATE if (doupdate) return; #endif do { killed=waitpid(-1,&status,WNOHANG); handlekilled(killed); } while (killed && killed!=(pid_t)-1); } static volatile int dowinch=0; static volatile int doint=0; void sigchild(int sig) { (void)sig; } void sigwinch(int sig) { (void)sig; dowinch=1; } void sigint(int sig) { (void)sig; doint=1; } int main(int argc, char *argv[]) { /* Schritt 1: argv[1] als Service nehmen und starten */ int count=0; int i; struct pollfd pfd; time_t last=time(0); int nfds=1; #ifdef HISTORY for (i=0; i30) { /* The system clock was reset. Compensate. */ long diff=last-now; int j; for (j=0; j<=maxprocess; ++j) root[j].startedat-=diff; } last=now; switch (poll(&pfd,nfds,5000)) { case -1: if (errno==EINTR) { childhandler(); break; } opendevconsole(); _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,str_len(buf)); write(1,"\n",1); */ #ifdef UPDATE if(!strcmp(buf,"update")) { execve("/sbin/minit",argv, environ); } if (((buf[0]!='U') && buf[0]!='s') && ((idx=findservice(buf+1))<0) && strcmp(buf,"d-")) #else if (buf[0]!='s' && ((idx=findservice(buf+1))<0) && strcmp(buf,"d-") ) #endif error: write(outfd,"0",1); else { switch (buf[0]) { case 'p': write(outfd,buf,fmt_ulong(buf,root[idx].pid)); break; #ifdef UPDATE case 'D': doupdate=1; write(outfd, &root[idx], sizeof(struct process)); break; case 'U': doupdate=1; write(outfd,"1",1); if (1==poll(&pfd,nfds,5000)) { struct process tmp; read(infd,&tmp,sizeof tmp); tmp.name=strdup(buf+1); addprocess(&tmp); } goto ok; #endif case 'r': root[idx].respawn=0; goto ok; case 'R': root[idx].respawn=1; goto ok; case 'C': if (kill(root[idx].pid,0)) { /* check if still active */ handlekilled(root[idx].pid); /* no!?! remove form active list */ goto error; } goto ok; case 'P': { unsigned char *x=buf+str_len(buf)+1; unsigned char c; tmp=0; while ((c=*x++-'0')<10) tmp=tmp*10+c; } if (tmp>0) { if (kill(tmp,0)) goto error; 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,-1); if (idx==0) { write(outfd,"0",1); break; } } ok: write(outfd,"1",1); break; case 'u': write(outfd,buf,fmt_ulong(buf,time(0)-root[idx].startedat)); break; case 'd': write(outfd,"1:",2); { int i; #if 0 printf("looking for father==%d\n",idx); #endif for (i=0; i<=maxprocess; ++i) { #if 0 printf("pid of %d(%s) is %lu, father is %d\n", i,root[i].name?root[i].name:"[none]",root[i].pid,root[i].father); #endif if (root[i].father==idx) write(outfd,root[i].name,str_len(root[i].name)+1); } write(outfd,"\0",2); } break; } } } else { if (buf[0]=='h') { #ifdef HISTORY write(outfd,"1:",2); { int i; for (i=0; i for the Debian GNU/Linux operating system. .SH "SEE ALSO" msvc(8), pidfilehack(8) minit-0.10/minit-update.c0000644000175000017500000001305310163315543014267 0ustar ericherich#include #include #include #include #include #include #include #include #include #include "str.h" #include "buffer.h" #include "minit.h" #define USAGE "Usage: minit-update [ -v [ -u ] ]\n" #define BUFLEN 1500 /* increases file size by almost 4k #define WITH_STRERROR */ static char buf[BUFLEN+1]; static unsigned int verbose; static int do_update; int openreadclose(char *fn, char **buf, unsigned long *len); char **split(char *buf,int c,int *len,int plus,int ofs); void feed_struct_to_minit(struct process *data); ssize_t read_outfd(void *buf, size_t count); void addprocess(struct process *p); void die(const char *msg) { buffer_putsflush(buffer_2, msg); _exit(111); } void buffer_putsnlflush(buffer *b, const char *msg) { buffer_puts(b, msg); buffer_putflush(b, "\n",1); } #ifdef WITH_STRERROR void buffer_puts_strerror(const char *msg) { buffer_puts(buffer_2, "minit-update: "); buffer_puts(buffer_2, msg); buffer_putsnlflush(buffer_2, strerror(errno)); } #else #define buffer_puts_strerror(a) buffer_putsflush(buffer_2, a) #endif void *xmalloc(size_t size) { void *ptr=malloc(size); if (!ptr) die("malloc() failed\n"); return ptr; } void copywrite (const char *service) { strncpy(buf+1,service,BUFLEN); buf[BUFLEN]=0; write(infd,buf,str_len(buf)); } int read_reply_from_minit(void) { if (read_outfd(buf,BUFLEN)==1) { if (buf[0]=='1') return 1; if (buf[0]=='0') buffer_puts(buffer_2,"expected '1' from minit, got '0' - minit too old?\n"); } /* XXX: Uuuh. Should this be checked? else buffer_putsflush(buffer_2, "minit response not understood\n"); */ return 0; } void find_service(int subdir, char *name, char *parent) { struct stat statbuf; char *service=0; DIR *dirstream=0; struct dirent *dir; if (chdir(name)) return; if (parent) { service=xmalloc(str_len(parent) + str_len(name) + 2 ); strcpy(service, parent); strcat(service, "/"); strcat(service, name); } else { if (subdir) { service=xmalloc(str_len(name)+1); strcpy(service, name); } } #if 0 buffer_putsnlflush(buffer_1,service); #endif if (service) { /* request and read a "struct process" from minit */ struct process tmp; #if 0 int j; for (j=0; j<=maxprocess; ++j) { /* skip duplicates */ if(!strcmp(root[j].name,service)) return 0; } #endif if (verbose) { buffer_puts(buffer_1, "minit-update: status for "); buffer_puts(buffer_1, service); } buf[0]='D'; copywrite(service); switch (read_outfd(&tmp,sizeof(tmp))) { case sizeof(tmp): tmp.name=strdup(service); addprocess(&tmp); if (verbose) buffer_puts(buffer_1, " saved.\n"); break; case 1: if (verbose) buffer_puts(buffer_1, " failed - minit has no information on this service\n"); #if 0 break; default: buffer_puts(buffer_1, " failed - read incomplete structure!\n"); #endif } } dirstream=opendir("."); if (!dirstream) goto ret; while ( (dir=readdir(dirstream))){ if (dir->d_name[0]!='.') { if(!lstat(dir->d_name, &statbuf)) { if (S_ISDIR(statbuf.st_mode)) { find_service(1, dir->d_name, service); #if 0 } else { buffer_putsnlflush(buffer_1,dir->d_name); #endif } } else { buffer_puts(buffer_2, dir->d_name); buffer_puts(buffer_2, ": cannot stat\n"); buffer_puts_strerror("lstat() failed: "); } } } /* while */ closedir(dirstream); ret: if (service) free(service); chdir(MINITROOT); if (parent) chdir(parent); buffer_flush(buffer_1); } int main(int argc, char **argv) { int i; if (argc < 2) die(USAGE); while (argc>1) { argc--; if (argv[argc][0]=='-') { switch(argv[argc][1]) { case 'v': verbose++; break; case 'u': do_update=1; break; default: buffer_puts(buffer_2,"minit-update: Unknown Option: "); buffer_putsnlflush(buffer_2,argv[argc]); } } else die(USAGE); } infd=open(MINITROOT "/in",O_WRONLY); outfd=open(MINITROOT "/out",O_RDONLY); if (infd<0 || outfd<0) die("could not open " MINITROOT "/in or " MINITROOT "/out\n"); while (lockf(infd,F_TLOCK,1)) { buffer_puts_strerror("could not acquire lock: "); sleep(1); } find_service(0,MINITROOT,0); if (maxprocess == -1) die("Could not extract running services from minit\n"); if (verbose) buffer_putsflush(buffer_1, "minit-update: telling minit to execve itself\n"); if (!do_update) { buffer_putsflush(buffer_2, "Test mode: No update done.\n"); return 0; } write(infd,"update",6); sleep(1); for (i=0; i<=maxprocess; i++) { if (verbose) { buffer_puts(buffer_1, "minit-update: restoring status for "); buffer_putsnlflush(buffer_1, root[i].name); } buf[0]='U'; copywrite(root[i].name); read_reply_from_minit(); write(infd,&root[i],sizeof (struct process)); if (read_reply_from_minit() && verbose) { buffer_puts(buffer_1, "minit-update: restored service "); buffer_putsnlflush(buffer_1, root[i].name); } } /* for() */ return 0; } ssize_t read_outfd(void *buf, size_t count) { ssize_t br=read(outfd,buf,count); if (br<0) buffer_puts_strerror("Error reading from outfd: "); return br; } void addprocess(struct process *p) { if (maxprocess+1>=processalloc) { struct process *fump; processalloc+=8; if ((fump=(struct process *)realloc(root,processalloc*sizeof(struct process)))==0) die("realloc() failed\n "); root=fump; } memmove(&root[++maxprocess],p,sizeof(struct process)); } minit-0.10/minit-update.80000644000175000017500000000152610015236434014213 0ustar ericherich.TH minit-update 8 .SH NAME minit\-update \- replace running minit with a new version .SH SYNOPSIS .B minit\-update [ \-v [ \-u ] ] .SH DESCRIPTION .B minit\-update is used to replace a running minit with a new version. It tries to retrieve the state information about running services from minit, then have minit replace itself with the new version and restore the stored state information. .SH USAGE Unless the \-u option is given, minit\-update assumes you are running in test mode. .TP 4 \-v verbose operation .TP \-u update mode .SH AUTHOR minit was written by Felix von Leitner and can be downloaded from his page at .I http://www.fefe.de/minit/ minit\-update was contributed by Florian Westphal. This manpage was written by Erich Schubert for the Debian GNU/Linux operating system. .SH "SEE ALSO" msvc(8), pidfilehack(8) minit-0.10/minit-shutdown.80000644000175000017500000000145610015236434014606 0ustar ericherich.TH minit-shutdown 8 .SH NAME minit-shutdown \- shutdown the minit init system. .SH SYNOPSIS .B minit-shutdown -[\fIrhosmn\fR] [-t \fIsecs\fR] .SH DESCRIPTION .B minit-shutdown tries to properly shutdown your system with minit. .SH USAGE .TP 9 -r reboot after shutdown .TP -h halt after shutdown .TP -o power-off after shutdown .TP -s single-user console after shutdown .TP -m only shutdown minit-part .TP -n do not shutdown services using minit .TP -t \fIsecs\fR delay between SIGTERM and SIGKILL .SH AUTHOR minit was written by Felix von Leitner and can be downloaded from his page at .I http://www.fefe.de/minit/ This shutdown was contributed by Bernd Wachter. This manpage was written by Erich Schubert for the Debian GNU/Linux operating system. .SH "SEE ALSO" msvc(8), pidfilehack(8) minit-0.10/minit-list.80000644000175000017500000000125110015236433013676 0ustar ericherich.TH minit-list 8 .SH NAME minit-list \- list running services .SH SYNOPSIS .B minit-list .SH DESCRIPTION .B minit-list will scan /etc/minit for services and use msvc to check if the service is currently running. It will then print a list of running services. minit-list was renamed from .I listpids for the debian package. .SH USAGE minit-list doesn't have any options. The output is a list of running services. .SH AUTHOR minit was written by Felix von Leitner and can be downloaded from his page at .I http://www.fefe.de/minit/ This manpage was written by Erich Schubert for the Debian GNU/Linux operating system. .SH "SEE ALSO" msvc(8), pidfilehack(8) minit-0.10/listpids0000755000175000017500000000033407652006713013307 0ustar ericherich#!/bin/sh # trivial shell script to list the running services for i in `find /etc/minit -type d | grep -v /etc/minit$ | sed 's@/etc/minit/@@'`; do msvc $i >/dev/null 2>&1 if test $? = 0; then echo $i fi done minit-0.10/killall5.c0000644000175000017500000000302707653207120013401 0ustar ericherich/* killall5 -- send a signal to all processes. killall5 is the SystemV killall command. It sends a signal to all processes except init(PID 1) and the processes in its own session, so it won't kill the shell that is running the script it was called from. Its primary (only) use is in the rc scripts found in the /etc/init.d directory. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. */ #include #include #include #include #include #include #define USAGE "Usage: killall5 SIGNAL\n" #define NOPROC "No processes found - /proc not mounted?\n" int main(int argc, char **argv) { struct dirent *dir; DIR *dirstream; register pid_t pid, sid, mypid, mysid; int signal=-1; unsigned int sig_sent=0; if (argc == 2) { if (argv[1][0] == '-') argv[1]++; signal=atoi(argv[1]); } if ( (signal < 1) || ( signal > 31) ) { write(2,USAGE,sizeof USAGE - 1); return 1; } kill(-1,SIGSTOP); if ( (dirstream=opendir("/proc"))) { mypid=getpid(); mysid=getsid(0); while ( (dir=readdir(dirstream))){ pid=atoi(dir->d_name); if (pid > 1 ){ sig_sent=1; sid=getsid(pid); if ( (pid != mypid) && ( sid !=mysid)) kill(pid,signal); } } } kill(-1,SIGCONT); if (!sig_sent) { write(2,NOPROC, sizeof NOPROC -1); return 1; } return 0; } minit-0.10/hard-reboot.c0000644000175000017500000000125207750521623014101 0ustar ericherich#include #include #include #include "str.h" #define ABORTMSG "hard-reboot: Aborted.\n" #define USAGE "Say \"hard-reboot (RESTART|HALT|POWER_OFF)\" if you really mean it.\n" void usage(void) { write(2, ABORTMSG, str_len(ABORTMSG)); write(2, USAGE, str_len(USAGE)); exit(1); } int main(int argc, char *argv[]) { if (argc!=2) usage(); sync(); sync(); sync(); if (strcmp(argv[1], "RESTART")==0) { reboot(RB_AUTOBOOT); } else if (strcmp(argv[1], "HALT")==0) { reboot(RB_HALT_SYSTEM); } else if (strcmp(argv[1], "POWER_OFF")==0) { reboot(RB_POWER_OFF); } else { usage(); } while(1) sleep(10); } minit-0.10/hard-reboot.80000644000175000017500000000167610015236433014026 0ustar ericherich.TH hard-reboot 8 .SH NAME hard-reboot \- reboot your system immedeately .SH SYNOPSIS .B hard-reboot .I RESTART .br .B hard-reboot .I HALT .br .B hard-reboot .I POWER_OFF .SH DESCRIPTION .B hard-reboot is used to reboot your system. It will not shut down services, unmount filesystems or notify your users, but expects that this has already been done when it is called. .SH USAGE To prevent accidential use of this application the parameters have to be written in uppercase letters. .TP 10 .I RESTART restart (reboot) the system .TP .I HALT halt the kernel .TP .I POWER_OFF power off the system if possible (supported by hardware) .SH AUTHOR minit was written by Felix von Leitner and can be downloaded from his page at .I http://www.fefe.de/minit/ hard-reboot was contributed by Tommi Virtanen. This manpage was written by Erich Schubert for the Debian GNU/Linux operating system. .SH "SEE ALSO" minit(8), msvc(8), pidfilehack(8) minit-0.10/TODO0000644000175000017500000000050407755224151012217 0ustar ericherichThe "sync" support should be done asynchronously, not with waitpid. if hwclock is used to adjust the system clock by a few hours, the minit times will be completely off. Maybe we should use the poll timeout to keep track of what we think how long we are running and use that as a basis to find out and correct this? minit-0.10/README0000644000175000017500000000526507430002535012406 0ustar ericherichEach 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". log if this directory exists, it is taken as service and minit creates a pipe between stdout of this service and stdin of the log service. If the log service can not be started, this service will block if it writes to stdout. File descriptors will be reused, i.e. if the log process dies and is restarted, no log entries will be lost and there will be no SIGPIPE. Please see http://cr.yp.to/daemontools/multilog.html for a very good logging tool. minit will try to run the command line arguments as services. The kernel passes its arguments to init. That means you can for example have a service /etc/minit/sos-shell that starts a /bin/sh and then use LILO to boot "bzImage sos-shell". minit will then run that service. If none of the services worked (or none were given), minit will spawn the service "default". The normal way to configure minit is to have default be an empty service and just list all the services you want minit to start at boot time in default/depends. Other special services (besides "default") are "ctrlaltdel" and "kbreq". ctrlaltdel will be run when the console user presses ctrl-alt-del and is meant to reboot the computer. kbreq is the keyboard request, which can be mapped using loadkeys. On my box, it is on Alt+Arrow-Up. I use it to shut down the computer. minit-0.10/COPYING0000644000175000017500000004313107437456007012571 0ustar ericherich GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. minit-0.10/CHANGES0000644000175000017500000001045010225536360012515 0ustar ericherich0.10: add sample script for /etc/minit/ctrlaltdel/run as control/ctrlaltdel-run (Florian Westphal) add killall5, needs /proc on Linux though (Florian Westphal) add shutdown (Bernd Wachter) add -Wall and -W and kill resulting compiler warnings several fixes (Bernd Wachter) add update support - tell minit to execve something else, a newer version of itself, preferably (Florian Westphal) also add minit-update program to download state from old minit and upload state to new minit after update (Florian Westphal) #include minit.h and use MINITROOT consistently (Lordy) Don't assume MINITROOT is 10 bytes long (Erich Schubert) add man pages!! (Erich Schubert) add a way to ask minit for dependencies msvc -H will now dump the ten least recently spawned processes. This is useful to see which process is looping. fix bug in msvc killing process group if readpid failed (Erich Schubert) You can now blacklist services temporarily by passing -servicename on the kernel command line. The first time they are to be started will then be skipped. Use this, for example, to not start the dhcp client when your notebook is not plugged in a network. add serdo (execute batch, no shell features whatsoever) make "msvc servicename" output easier to parse if not outputting to a tty require libowfat instead of shipping half of it ourselves replace malloc with alloca; minit is now 6644 bytes on i386. add usage for serdo (Nikola Vladov) fix msvc -d followed by msvc -h (would send SIGHUP to pid 1, i.e. kill minit; found by Paul Fox) added small special-purpose malloc using a static buffer before calling the real malloc (Nikola Vladov) 0.9.1: fix embarassing typo in msvc (Gelu G. Lupas) 0.9: For stopped services, minit did not record the time when it finished, but when it was started. add hard-reboot from Tommi Virtanen. He also asked me to include an explicit license statement and sent a patch to make debian packaging easier (deb files can't include FIFOs). Olaf: add write_proc. Olaf: lines in depends can now be commented out with a # Olaf: add a method to clear dead services (mark as terminated) minit now checks whether a PID actually exists before accepting msvc -P this removes a race condition when the forked service terminates before pidfilehack notifies minit of the PID msvc now accepts more than one service after -o, -d, ... msvc now accepts /etc/minit/sshd instead of sshd as service name better error handling in msvc (more and better error messages) minit now has a heuristic to detect time zone changes and still have msvc report the correct elapsed time since process start/end (don't look at the source code, it's ultra-kludgey) add msvc -g (get PID for shell scripts) 0.8: call waitpid repeatedly until it returns "no children". This reaps zombies faster. 0.7: found and fixed the bug that made the logging support not work when minit was running as PID 1. saw that the shutdown from sysvinit actually does what it's supposed to do, even with minit, if one uses the -n mode. It should be quite easy to write a nice and clean shutdown for minit now. if a service depends on a service that already finished, don't start it again. 0.6.1: fixed msvc and minit handling of msvc -P. pidfilehack has now actually been tested and works with ssh. 0.6: add tty and session leadership magic and I actually got getty to work as init on my laptop, i.e. in production use! add msvc man page. 0.5.1: cut&paste error, set wrong variable to 0 for keyboard request. Thanks, Schulti. 0.5: add "log" subdirectory (see README). get rid of stdio and *printf and switch to routines from libowfat. 0.4: add pidfilehack. It will take the first argument as filename for the PID file (i.e. /var/run/sshd.pid), the second argument as service name for minit (i.e. ssh) and the rest of the command line is run without path searching in a child process. The parent process will then try to open the pidfile several times (for up to 30 seconds), read the PID off it and run msvc to tell minit the PID of the service. minit can then track sshd and restart it if necessary. This gross hack does not work for daemons that do not write a PID file, but there are not many of those, fortunately. minit-0.10/.cvsignore0000644000175000017500000000012710163316622013520 0ustar ericherichminit duh msvc pidfilehack hard-reboot write_proc killall5 shutdown minit-update serdo