/*
* init A System-V init Clone.
* prototype version - already working on other concept!
*
* Usage: /bin/init
* init [0123456]
*
* 1999-11-07 [EMAIL PROTECTED]
*
* Copyright 1999 Mario Frasca
*
* 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 <sys/types.h>
#include <stdio.h>
#include <unistd.h>
#include <utmp.h>
#include <fcntl.h>
#include <string.h>
#include <signal.h>
#include <assert.h>
#include <memory.h>
#if 0
#define _(A) A
#else
#define _(A) ()
#endif
#define FLAG_RESPAWN 1
#define FLAG_WAIT 2
#define RUNLEVELS 12
#define BUFSIZE 256
#define INITTAB "/etc/inittab"
#define INITLVL "/etc/initlvl"
#define SHELL "/bin/sh"
#define GETTY "/bin/getty"
#define DEVTTY "/dev/tty1"
#define RESPAWN 362
#define WAIT 122
#define ONCE 62
#define BOOT 147
#define BOOTWAIT 465
#define POWERFAIL 403
#define POWERFAILNOW 951
#define POWERWAIT 577
#define POWEROKWAIT 829
#define CTRLALTDEL 504
#define OFF 39
#define ONDEMAND 240
#define INITDEFAULT 707
#define SYSINIT 398
#define KBREQUEST 622
/* `gently' exit */
#if 1
# define PANIC write(fileno(stderr), "init panic\n", 11),exit(1)
#else
# define PANIC exit(1)
#endif
/*
each of the entries from inittab corresponds to a child, each of which:
has a unique 2 chars identifier.
is allowed to run in some run-levels.
might need to be waited for completion when spawned.
might be running or not (pid different from zero).
might have to been respawned.
was respawned at a certain point in time.
*/
struct tabentry
{
char id[2];
char runlevels[RUNLEVELS];
char flags;
char *tospawn;
int action;
pid_t pid;
time_t spawned;
struct tabentry *next;
};
extern struct tabentry*
tabentry_read_from_file _((struct tabentry* head, const char *
filename));
struct tabentry*
tabentry_match _((struct tabentry* head, struct tabentry *entry));
void
tabentry_insert _((struct tabentry** head, struct tabentry *entry));
void
tabentry_update _((struct tabentry* match, struct tabentry *entry));
struct tabentry*
tabentry_parseline _((const char* line));
void
tabentry_clear _((struct tabentry* head));
void
tabentry_stopif _((struct tabentry* head, const char runlevel));
void
tabentry_startif _((struct tabentry* head, const char runlevel));
struct tabentry*
tabentry_findpid _((struct tabentry* head, pid_t pid));
struct tabentry*
tabentry_findaction _((struct tabentry* head, int action));
pid_t respawn _((const char *command));
struct tabentry*
tabentry_read_from_file(phead, filename)
struct tabentry **phead;
const char * filename;
{
int f, left;
char buf[BUFSIZE], *line, *next;
#ifdef DEBUG
printf("starting to read the file\n");fflush(stdout);
#endif
f = open(INITTAB, O_RDONLY);
if(-1 == f)
PANIC;
left = read(f, buf, BUFSIZE);
line = strtok(buf, "\n");
next = strtok(NULL, "\n");
while (left)
{
if (!next)
{
if(line == buf)
PANIC;
memmove(buf, line, left);
left += read(f, buf+left, BUFSIZE-left);
line = buf;
next = strtok(buf, "\n");
}
else
{
struct tabentry *entry, *match;
entry = tabentry_parseline(line);
match = tabentry_match((*phead), entry);
if(!match)
tabentry_insert(phead, entry);
else
{
tabentry_update(match, entry);
free(entry);
}
left -= next-line;
line = next;
next = strtok(NULL, "\n");
}
}
close(f);
#ifdef DEBUG
printf("done reading inittab\n");fflush(stdout);
#endif
}
struct tabentry*
tabentry_match(head, entry)
struct tabentry* head;
struct tabentry *entry;
{
struct tabentry* cur;
char id0, id1;
id0=entry->id[0];
id1=entry->id[1];
cur = head;
while(cur && (cur->id[0] != id0 || cur->id[1] != id1))
cur = cur->next;
return cur;
}
void
tabentry_insert(phead, entry)
struct tabentry** phead;
struct tabentry *entry;
{
if(!(*phead))
(*phead) = entry;
else
{
struct tabentry *cur = (*phead);
while(cur->next) cur=cur->next;
cur->next = entry;
}
entry->next = NULL;
}
void
tabentry_update(match, entry)
struct tabentry* match;
struct tabentry *entry;
{
if(match->id[0] != entry->id[0] || match->id[1] != entry->id[1])
PANIC;
memcpy(match->runlevels, entry->runlevels, RUNLEVELS);
}
void
tabentry_clearlist(cur)
struct tabentry* cur;
{
while (cur)
{
memset(cur->runlevels, 0, RUNLEVELS);
cur = cur->next;
}
}
void
tabentry_killif(cur, runlevel, sig)
struct tabentry* cur;
const char runlevel;
int sig;
{
while (cur)
{
if(!strchr(cur->runlevels, runlevel) && cur->pid)
kill(cur->pid, sig);
cur = cur->next;
}
}
void
tabentry_stopif(head, runlevel)
struct tabentry* head;
const char runlevel;
{
/*
* tabentry_killif(head, runlevel, SIGTERM);
* do_sleep(1);
*/
tabentry_killif(head, runlevel, SIGKILL);
}
void
tabentry_startif(cur, runlevel)
struct tabentry* cur;
const char runlevel;
{
while (cur)
{
if(strchr(cur->runlevels, runlevel)
&& !cur->pid
&& (cur->flags & FLAG_RESPAWN))
{
cur->pid = respawn(cur->tospawn);
if(cur->flags & FLAG_WAIT)
{
while(cur->pid != wait());
/*waitpid(cur->pid, NULL, 0);*/
cur->pid = 0;
}
}
cur = cur->next;
}
}
int hash(string)
char *string;
{
char *p;
int result = 0, i=1;
p = string;
while (*p)
result += ( (*p++)-'a')*(i++);
return result;
}
struct tabentry*
tabentry_parseline(line)
const char* line;
{
char *a[4];
int k = 0, hashvalue;
char buf[256], *p;
struct tabentry *entry;
strcpy(buf, line);
a[k++] = p = buf;
while(k<4)
{
/* looking for the k-th ':' */
while(*p && *p != ':') p++;
*p = 0;
a[k++] = ++p;
}
entry = (struct tabentry*)malloc(sizeof (struct tabentry) );
entry->next = NULL;
entry->id[0] = a[0][0];
entry->id[1] = a[0][1];
strncpy(entry->runlevels, a[1], RUNLEVELS);
entry->action = hash(a[2]);
entry->tospawn = strdup(a[3]);
switch(entry->action)
{
case WAIT:
case BOOTWAIT:
case POWERWAIT:
case POWEROKWAIT:
case SYSINIT:
{
entry->flags = FLAG_WAIT;
}
break;
case RESPAWN:
case ONDEMAND:
{
entry->flags = FLAG_RESPAWN;
}
break;
default:
{
entry->flags = 0;
}
break;
}
return entry;
}
struct tabentry*
tabentry_findpid (cur, pid)
struct tabentry* cur;
pid_t pid;
{
while (cur)
if(pid == cur->pid)
break;
else
cur = cur->next;
return cur;
}
struct tabentry*
tabentry_findaction (cur, action)
struct tabentry* cur;
int action;
{
while(cur)
if(action == cur->action)
break;
else
cur=cur->next;
return cur;
}
struct tabentry*
tabentry_respawnall (cur, action1, action2, rl)
struct tabentry* cur;
int action1, action2;
char rl;
{
while(cur)
{
if( ( (cur->action == action1) || (cur->action == action2))
&& (strchr(cur->runlevels, rl) || !cur->runlevels[0]))
{
cur->pid = respawn(cur->tospawn);
if(cur->flags & FLAG_WAIT)
{
while(cur->pid != wait());
/*waitpid(cur->pid, NULL, 0);*/
cur->pid = 0;
}
}
cur = cur->next;
}
}
pid_t respawn(command)
const char *command;
{
int pid, status;
#ifdef DEBUG
printf("spawning \"%s\"\n", command);
fflush(stdout);
#endif
if (command == 0)
return 1;
pid = fork();
if (-1 == pid)
PANIC;
if (0 == pid)
{
char *argv[4], buf[128];
strcpy(buf, command);
if(!strncmp(buf, GETTY, sizeof GETTY -1))
{
int fd;
char *devtty;
devtty = strchr(buf, ' ');
if(!devtty)
PANIC;
*(devtty++) = 0;
if ((fd = open(devtty, O_RDWR)) < 0)
PANIC;
dup2(fd ,STDIN_FILENO);
dup2(fd ,STDOUT_FILENO);
dup2(fd ,STDERR_FILENO);
argv[0] = GETTY;
argv[1] = NULL;
execv(argv[0], argv);
}
else
{
int fd;
if ((fd = open(DEVTTY, O_RDWR)) < 0)
PANIC;
dup2(fd ,STDIN_FILENO);
dup2(fd ,STDOUT_FILENO);
dup2(fd ,STDERR_FILENO);
argv[0] = SHELL;
argv[1] = "-e";
argv[2] = buf;
argv[3] = strtok(buf, " ");
argv[4] = NULL;
execv(argv[0], argv);
}
PANIC;
}
#ifdef DEBUG
printf("owner process owns %d\n", pid);
fflush(stdout);
#endif
/* here I must do something about utmp */
return pid;
}
struct tabentry *head;
struct sigaction sa;
struct utmp utentry;
char runlevel;
void handle_signal(sig)
int sig;
{
#ifdef DEBUG
printf("oops, got signaled! (%d)\n", sig);
fflush(stdout);
#endif
switch(sig)
{
case SIGHUP:
/* got signaled by another instance of init, change runlevel! */
{
/* -get the runlevel from /etc/initlvl */
int f = open(INITLVL, O_RDONLY);
read(f, &runlevel, 1);
close(f);
/* -update the lists from /etc/inittab */
tabentry_read_from_file(&head, INITTAB);
/* -stop all running children not needed in new run-level */
tabentry_stopif(head, runlevel);
/* -start all non running children needed in new run-level */
tabentry_startif(head, runlevel);
}
break;
}
}
int main(argv, argc)
char ** argv;
int argc;
{
int fd;
#ifdef DEBUG
fd = open(DEVTTY, 2);
dup2(fd, 0);
dup2(fd, 1);
dup2(fd, 2);
printf("entered /bin/init\n"); fflush(stdout);
#endif
memset(&utentry, 0, sizeof(struct utmp));
utentry.ut_type = INIT_PROCESS;
time(&utentry.ut_time);
utentry.ut_id[0] = 'l';
utentry.ut_id[1] = '1';
utentry.ut_pid = getpid();
strcpy(utentry.ut_user, "INIT");
setutent();
pututline(&utentry);
/* am I the No.1 init? */
if(getpid() == 1)
{
struct tabentry *entry;
/* signal(SIGALRM, handle_signal); */
signal(SIGHUP, handle_signal);
/* signal(SIGINT, handle_signal); */
/* signal(SIGCHLD, handle_signal); */
/* signal(SIGPWR, handle_signal); */
/* signal(SIGWINCH, handle_signal); */
/* signal(SIGUSR1, handle_signal); */
/* signal(SIGSTOP, handle_signal); */
/* signal(SIGTSTP, handle_signal); */
/* signal(SIGCONT, handle_signal); */
/* signal(SIGSEGV, handle_signal); */
setutent();
/* read all entries from INITTAB into list */
tabentry_read_from_file(&head, INITTAB);
/* set current run-level. */
if(argc>1)
runlevel = argv[1][0];
else
{
entry = tabentry_findaction(head, INITDEFAULT);
runlevel = entry->runlevels[0];
}
/* do what is specified as sysinit and wait for completion. */
tabentry_respawnall(head, SYSINIT, 0, runlevel);
/* execute whatever specified by (boot, bootwait) */
tabentry_respawnall(head, BOOT, BOOTWAIT, runlevel);
/* spawn `once' and `respawn' children. */
tabentry_respawnall(head, ONCE, WAIT, runlevel);
tabentry_respawnall(head, RESPAWN, ONDEMAND, runlevel);
endutent();
/* wait for signals. */
while(6 != runlevel)
{
pid_t pid;
struct tabentry * entry;
pid = wait();
if(-1 == pid)
continue;
#ifdef DEBUG
printf("child %d died...\n", pid);fflush(stdout);
#endif
entry = tabentry_findpid(head, pid);
#ifdef DEBUG
printf("it was spawned as %s\n", entry->tospawn);fflush(stdout);
#endif
entry->pid = 0;
if (entry->flags & FLAG_RESPAWN)
{
entry->pid = respawn(entry->tospawn);
strcpy(utentry.ut_line, strstr(entry->tospawn, "/tty")+1);
time(&utentry.ut_time);
utentry.ut_id[0] = entry->id[0];
utentry.ut_id[1] = entry->id[1];
utentry.ut_pid = entry->pid;
setutent();
pututline(&utentry);
}
}
}
else
{
/* store the new run-level into /etc/initrunlvl */
int f = open(INITLVL, O_WRONLY);
write(f, argv[1][0], 1);
close(f);
/* signal (SIGHUP) the No.1 init that we must switch run-level. */
kill(1, SIGHUP);
}
return 0;
}
/*
NAME WHAT? WAIT?
sysinit do this before anything else 1
initdefault set initial run level -
boot when inittab is read the first time 0
bootwait when inittab is read the first time 1
once start the process once 0
wait start the process once 1
off terminate the process if running,
somehow the opposite of once -
respawn always keep the process running 0
ondemand synonymous of respawn 0
ctrlaltdel allows init to reboot the system.
(does this work on a 8086 all the same as in the suite?)
powerfail,
powerwait,
powerokwait not on ELKS, I believe.
init has therefore this structure:
update the lists from /etc/inittab:
clear the runlevel fields of all entries.
get the runlevels for once,wait,respawn,ondemand elements from inittab.
*/