/*
 * 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.
*/


Reply via email to