home *** CD-ROM | disk | FTP | other *** search
/ PC Plus SuperCD (UK) 1999 May / pcp151c.iso / misc / src / install / modutils / kerneld / kerneld.c < prev    next >
Encoding:
C/C++ Source or Header  |  1998-01-06  |  20.8 KB  |  909 lines

  1. /*
  2.  * This is kerneld, the user level handler of kernel requests.
  3.  * Copyright (C) 1995, 1996 Bjorn Ekwall <bj0rn@blox.se>
  4.  * See the file "COPYING" for your rights.
  5.  *
  6.  *
  7.  * The requests arrive on a specific IPC message queue,
  8.  * and the type of the message specifies the request.
  9.  *
  10.  * The message consists of a header followed by "char text[]".
  11.  * The parameter for the request is stored in the "text" field.
  12.  * If the "id" field in the header is non-zero, then the kernel expects
  13.  * an answer, where the message type of the answer should be "id".
  14.  * In the return message, the "id" will contain the status of the request.
  15.  * If the "id" of the request is zero, the kernel does not expect an answer.
  16.  *
  17.  * The "text" field can be used to return any generated information.
  18.  * (See e.g. KERNELD_SYSTEM)
  19.  *
  20.  * (C) Bjorn Ekwall <bj0rn@blox.se> in May 1995
  21.  * This software is placed under the GPL, the text of which can be found
  22.  * in the root directory of this release.
  23.  */
  24. /*
  25.  * Blocking/unblocking of SIGCHLD due to: Eduardo Blanco <ejbs@cs.cs.com.uy>
  26.  * Better error reports by: Lars Fenneberg <in5y050@public.uni-hamburg.de>
  27.  */
  28.  
  29. #ident "$Id: kerneld.c,v 1.1.1.1 1998/01/06 20:51:08 ewt Exp $"
  30.  
  31. #define CONFIG_KERNELD
  32. #include <linux/kerneld.h>
  33. #include <stdio.h>
  34. #include <stdarg.h>
  35. #include <stdlib.h>
  36. #include <string.h>
  37. #include <sys/types.h>
  38. #include <sys/stat.h>
  39. #include <sys/time.h>
  40. #include <sys/ipc.h>
  41. #include <sys/msg.h>
  42. #include <sys/wait.h>
  43. #include <sys/ioctl.h>
  44. #include <signal.h>
  45. #include <fcntl.h>
  46. #include <unistd.h>
  47. #include <syslog.h>
  48. #include <linux/unistd.h>
  49. #include <asm/param.h>
  50. #include <signal.h>
  51. #include <errno.h>
  52. #include <sys/utsname.h>
  53.  
  54. #include "version.h"
  55. #include "module.h"
  56.  
  57. #define MAXCMD 255
  58. /*
  59.  * The _real_ maximum message type is KERNELD_MAXCMD,
  60.  * but we reserve some types for other daemons...
  61.  */
  62.  
  63. #define MSIZE 1024 /* might be MSGMAX instead... almost 4k nowadays */
  64. #define DELAY_TIME 60 /* adjustable with the "delay=..." parameter */
  65. #define JOB_DONE 0
  66. #define REQUEST_ROUTE "/sbin/request-route"
  67.  
  68. #ifdef DEBUG
  69. time_t t0;
  70. #define DPRINT(arg) if (debug) { printf("%d:", (int)time(0) - (int)t0);printf arg; }
  71. #else
  72. #define DPRINT(arg)
  73. #endif
  74.  
  75.  
  76. struct clear_text {
  77.     int message;
  78.     char *text;
  79. } clear_text[] = {
  80.     { KERNELD_CANCEL_RELEASE_MODULE, "cancel_release_module" },
  81.     { KERNELD_SYSTEM, "system" },
  82.     { KERNELD_REQUEST_MODULE, "request_module" },
  83.     { KERNELD_DELAYED_RELEASE_MODULE, "delayed_release_module" },
  84.     { KERNELD_RELEASE_MODULE, "release_module" },
  85.     { KERNELD_REQUEST_ROUTE, "request_route" },
  86.     { KERNELD_BLANKER, "screenblanker" },
  87. #ifdef KERNELD_GET_PERSIST
  88.     { KERNELD_GET_PERSIST, "get_persist" },
  89.     { KERNELD_SET_PERSIST, "set_persist" },
  90. #endif
  91.     { MAXCMD, "debug" },
  92.     { 0, 0 }
  93. };
  94.  
  95. #ifdef NO_GDBM
  96. typedef struct {
  97.     char *dptr;
  98.     int   dsize;
  99. } datum;
  100. #else
  101. #include <gdbm.h>
  102. #endif
  103.  
  104. struct persist_t {
  105.     struct persist_t *next;
  106.     datum val;
  107.     datum key;
  108. };
  109.  
  110. struct persist_t *persist_head;
  111.  
  112. char kd_dbmfile[200]; /* or whatever... */
  113. int have_request_route; /* set in gdbm_reload, i.e. also at "kill -1" */
  114.  
  115. #ifndef NO_GDBM
  116. GDBM_FILE dbf;
  117.  
  118. GDBM_FILE kddbmopen(int how)
  119. {
  120.     return dbf = gdbm_open(kd_dbmfile, 0, how, 0600, NULL);
  121. }
  122. #endif
  123.  
  124. void gdbm_reload(int dummy)
  125. {
  126. #ifndef NO_GDBM
  127.     datum key, nextkey, data;
  128.     struct persist_t *p = persist_head;
  129.     struct persist_t *x;
  130.  
  131.     if (kddbmopen(GDBM_READER)) {
  132.         /* clean any up old data */
  133.         while ((x = p)) {
  134.             p = p->next;
  135.             free(x->key.dptr);
  136.             free(x->val.dptr);
  137.             free(x);
  138.         }
  139.         persist_head = NULL;
  140.  
  141.         key = gdbm_firstkey(dbf);
  142.         while (key.dptr) {
  143.             data = gdbm_fetch(dbf, key);
  144.             /* Create entry */
  145.             x = (struct persist_t *)malloc(sizeof(*x));
  146.             if (x == NULL)
  147.                 /* fail, ignore request */
  148.                 return;
  149.             x->key = key;
  150.             x->val = data;
  151.             x->next = persist_head;
  152.             persist_head = x;
  153.             nextkey = gdbm_nextkey(dbf, key);
  154.             key = nextkey;
  155.         }
  156.         gdbm_close(dbf);
  157.     }
  158. #endif
  159.     if (access(REQUEST_ROUTE, X_OK) == 0)
  160.         have_request_route = 1;
  161.     else
  162.         have_request_route = 0;
  163. }
  164.  
  165. int qid;
  166.  
  167. /*
  168.  * If "kerneld" is called with the argument "keep",
  169.  * all requests for module release will be ignored.
  170.  *
  171.  * If "delay=10" is used as an argument, the default delay
  172.  * will be changed from the preset value of 60.
  173.  */
  174. int keep = 0;
  175. int delay = DELAY_TIME;
  176. int debug = 0;
  177. int want_type = -MAXCMD;
  178. int dev_null;
  179.  
  180. struct job {
  181.     struct job *next;
  182.     pid_t pid;
  183.     int status;
  184.     int reply_size;
  185.     int reply_fd;
  186.     struct kerneld_msg msg;
  187. };
  188.  
  189. struct job *job_head = NULL;
  190. /*
  191.  * Handle both old and new kerneld message formats
  192.  */
  193. struct oldkerneld_msg {
  194.     long mtype;
  195.     long id;
  196.     char text[1];
  197. };
  198.  
  199.  
  200. int do_msgsnd(int msqid, struct kerneld_msg *msg, size_t msgsz, int msgflg)
  201. {
  202. #ifdef NEW_KERNELD_PROTOCOL /* i.e. using extended kerneld protocol */
  203.     if (msg->version == 0) {
  204.         if (msgsz) {
  205.             struct oldkerneld_msg *omsg =
  206.                 (struct oldkerneld_msg *)msg;
  207.             memcpy(omsg->text, msg->text, msgsz);
  208.         }
  209.     }
  210.     else
  211.         msgsz += sizeof(short int) + sizeof(short int);
  212. #endif
  213.     msgsz += sizeof(long);
  214.     return msgsnd(msqid, (struct msgbuf *)msg, msgsz, msgflg);
  215. }
  216.  
  217.  
  218. int do_msgrcv(int msqid, struct kerneld_msg *msg, size_t msgsz, long msgtyp, int msgflg)
  219. {
  220.     int sz;
  221.  
  222.     sz = msgrcv(msqid, (struct msgbuf *)msg, msgsz, msgtyp, msgflg);
  223. #ifdef NEW_KERNELD_PROTOCOL /* i.e. using extended kerneld protocol */
  224.     if (sz > 0) {
  225.         if (msg->version != 2) {
  226.             struct oldkerneld_msg *omsg =
  227.                 (struct oldkerneld_msg *)msg;
  228.             memcpy(msg->text, omsg->text, sz);
  229.             msg->pid = msg->version = 0;
  230.         }
  231.     }
  232. #endif
  233.     return sz;
  234. }
  235.  
  236. void do_putenv(struct job *job)
  237. {
  238. #ifdef NEW_KERNELD_PROTOCOL /* i.e. using extended kerneld protocol */
  239.     char triggerpid[30];
  240.  
  241.     if (job->msg.version != 0) {
  242.         sprintf(triggerpid, "KERNELD_TRIGGER=%d", job->msg.pid);
  243.         putenv(triggerpid);
  244.     }
  245. #endif
  246. }
  247.  
  248.  
  249. /*
  250.  * Get clear text corresponding to message type
  251.  */
  252. char * type2text(int type)
  253. {
  254.     int chk;
  255.  
  256.     for (chk = 0; clear_text[chk].message != 0; ++chk) {
  257.         if (clear_text[chk].message == type)
  258.             break;
  259.     }
  260.     return (clear_text[chk].text)?(clear_text[chk].text):"unknown";
  261. }
  262.  
  263. /*
  264.     Generate an error message with the syslog facility.
  265. */
  266. void kerneld_error (const char *ctl, ...)
  267. {
  268.     char buf[1024];
  269.     va_list list;
  270.  
  271.     va_start (list,ctl);
  272.     vsnprintf (buf, sizeof buf, ctl, list);
  273.     va_end (list);
  274.     syslog (LOG_ERR,"%s",buf);
  275. }
  276.  
  277. void kerneld_perror (char *name)
  278. {
  279.     kerneld_error ("error: %s: %s", name, strerror(errno));
  280.     exit (1);
  281. }
  282.  
  283. volatile int timer = 0;
  284. extern int errno;
  285.  
  286. void
  287. handle_child(int sig)
  288. {
  289.     struct job *job;
  290.     int pid;
  291.     int status;
  292.  
  293.     if ((pid = waitpid(-1, &status, WNOHANG)) <= 0)
  294.         return;
  295.  
  296.     for (job = job_head; job; job = job->next) {
  297.         if (job->pid == pid) {
  298.             job->pid = JOB_DONE;
  299.             job->status = WEXITSTATUS(status);
  300.             /* don't break, more jobs might be waiting... */
  301.             DPRINT(("SIGCHLD: job (%08lx), pid=%d, status=%d\n",
  302.                 (long)job, pid, job->status));
  303.         }
  304.     }
  305. }
  306.  
  307. void
  308. handle_timer(int sig)
  309. {
  310.     timer = 1;
  311.     alarm(delay);
  312. }
  313.  
  314.  
  315. /*
  316.     Check if a request for a module is pending.
  317.     If it is, then we just returned the pid of the original
  318.     request.
  319.  
  320.     When this request will return, all "job" with the same pid
  321.     will get the result. newjob->pid will be set to JOB_DONE
  322.     and kerneld will send back the return code to all caller.
  323. */
  324. static int check_pending_load (struct job *newjob)
  325. {
  326.     int pid = -1;
  327.     struct job *job;
  328.     for (job = job_head; job; job = job->next) {
  329.         if (job->msg.mtype == KERNELD_REQUEST_MODULE
  330.             && strcmp(job->msg.text,newjob->msg.text)==0){
  331.             pid = job->pid;
  332.         }
  333.     }
  334.     return pid;
  335. }
  336.  
  337. /*
  338.  * Execute the requested kerneld task, return pid or -errno.
  339.  * If pid is set to 0 then no process has been spawned.
  340.  *
  341.  * Note that the job has been allocated with calloc, so all
  342.  * fields are already zero (including the status).
  343.  */
  344. int
  345. spawn_it(struct job *newjob)
  346. {
  347.     struct job *job;
  348. #ifdef KERNELD_GET_PERSIST
  349.     struct persist_t *persist;
  350. #endif
  351.     int pipes[2];
  352.     int status;
  353.     int pid = 0;
  354.     int op;
  355.  
  356.     switch (op = newjob->msg.mtype) {
  357.     case KERNELD_CANCEL_RELEASE_MODULE:
  358.         if (keep)
  359.             break;
  360.         /* else */
  361.         for (job = job_head; job; job = job->next) {
  362.             if ((job->msg.mtype == KERNELD_DELAYED_RELEASE_MODULE)
  363.                  && (job->pid != JOB_DONE) &&
  364.                  (strcmp(job->msg.text, newjob->msg.text) == 0)) {
  365.                 kill(job->pid, SIGINT);
  366.                 DPRINT(("job (%08lx), pid %d terminated\n",
  367.                     (long)job, job->pid));
  368.             }
  369.         }
  370.         break;
  371.     
  372.     case KERNELD_SYSTEM:
  373.         if (newjob->msg.id) /* reply wanted */
  374.             pipe(pipes);
  375.         if ((pid = fork()) == 0) {
  376.             if (newjob->msg.id) { /* reply wanted */
  377.                 close(1);
  378.                 dup(pipes[1]);
  379.                 close(pipes[0]);
  380.                 close(pipes[1]);
  381.  
  382.                 close(0);close(2);
  383.                 dup(dev_null); dup(dev_null);
  384.             }
  385.             else {
  386.                 close(0);close(1);close(2);
  387.                 dup(dev_null); dup(dev_null); dup(dev_null);
  388.             }
  389.  
  390.             do_putenv(newjob);
  391.             /* signal(SIGCHLD, SIG_DFL); */
  392.             status = system(newjob->msg.text);
  393.             exit(WEXITSTATUS(status));
  394.         }
  395.         if ((pid > 0) && newjob->msg.id) { /* reply wanted */
  396.             newjob->reply_fd = pipes[0];
  397.             close(pipes[1]);
  398.         }
  399.         break;
  400.  
  401.     case KERNELD_REQUEST_MODULE:
  402.         if (!keep)
  403.             /*
  404.              * re-arm the timer for auto-removal,
  405.              * keep an auto-loaded module at least this long
  406.              */
  407.             alarm(delay);
  408.         pid = check_pending_load (newjob);
  409.         if (pid == -1
  410.             && (pid = fork()) == 0) {
  411.             close(0);close(1);close(2);
  412.             dup(dev_null); dup(dev_null); dup(dev_null);
  413.             do_putenv(newjob);
  414.             execlp("/sbin/modprobe", "modprobe", "-k", "-s",
  415.                         newjob->msg.text, 0);
  416.             kerneld_perror("/sbin/modprobe");
  417.             sleep(2);
  418.             exit(1);
  419.         }
  420.         break;
  421.         
  422.     case KERNELD_DELAYED_RELEASE_MODULE:
  423.     case KERNELD_RELEASE_MODULE:
  424.         if (keep)
  425.             pid = 0;
  426.         /* else */
  427.         if ((pid = fork()) == 0) {
  428.             close(0);close(1);close(2);
  429.             dup(dev_null); dup(dev_null); dup(dev_null);
  430.             if (op == KERNELD_DELAYED_RELEASE_MODULE)
  431.                 sleep(delay);
  432.             do_putenv(newjob);
  433.             execlp("/sbin/modprobe", "modprobe", "-r", "-s",
  434.                         newjob->msg.text, 0);
  435.             kerneld_perror("/sbin/modprobe");
  436.             sleep(2);
  437.             exit(1);
  438.         }
  439.         break;
  440.  
  441.     case KERNELD_REQUEST_ROUTE:
  442.         /* set in gdbm_reload, i.e. also at "kill -1" */
  443.         if (!have_request_route)
  444.             break;
  445.  
  446.         /* check if there is a previous similar route request running */
  447.         for (job = job_head; job; job = job->next) {
  448.             if ((job->msg.mtype == KERNELD_REQUEST_ROUTE)
  449.                  && job->pid && (job->pid != JOB_DONE) &&
  450.                  (strcmp(job->msg.text, newjob->msg.text) == 0)) {
  451.                 /* wait for the same pid */
  452.                 pid = job->pid;
  453.                 DPRINT(("job (%08lx), depends on job(%08lx)\n",
  454.                     (long)newjob, (long)job));
  455.                 break; /* for-loop */
  456.             }
  457.         }
  458.         /* no previous request is running, so start a new one */
  459.         if ((pid == 0) && ((pid = fork()) == 0)) {
  460.             close(0);close(1);close(2);
  461.             dup(dev_null); dup(dev_null); dup(dev_null);
  462.             do_putenv(newjob);
  463.             execlp(REQUEST_ROUTE, "request-route",
  464.                 newjob->msg.text, 0);
  465.             kerneld_perror(REQUEST_ROUTE);
  466.             sleep(2);
  467.             exit(1);
  468.         }
  469.         break;
  470.         
  471.     case KERNELD_BLANKER:
  472.         /* Find any previous running blanker */
  473.         for (job = job_head; job; job = job->next) {
  474.             if ((job->msg.mtype == KERNELD_BLANKER)
  475.                  && job->pid && (job->pid != JOB_DONE)) {
  476.                 pid = job->pid;
  477.                 DPRINT(("job (%08lx), depends on job(%08lx)\n",
  478.                     (long)newjob, (long)job));
  479.                 break; /* for-loop */
  480.             }
  481.         }
  482.  
  483.         if (strcmp(newjob->msg.text, "on") == 0) {
  484.             if ((pid == 0) && ((pid = fork()) == 0)) {
  485.                 /* no previus blanker is running */
  486.                 close(0);close(1);close(2);
  487.                 dup(dev_null); dup(dev_null); dup(dev_null);
  488.                 do_putenv(newjob);
  489.                 execlp("/sbin/screenblanker","screenblanker",0);
  490.                 kerneld_perror("/sbin/screenblanker");
  491.                 sleep(2);
  492.                 exit(1);
  493.             }
  494.         } else { /* turn the blanker off */
  495.             /* Note that pid is already set to the correct value */
  496.             /* Let the screen blanker report back instead */
  497.             if (pid != 0) {
  498.                 job->msg.id = newjob->msg.id;
  499.                 kill((pid_t)pid, SIGQUIT);
  500.             } else { /* something has happened to the blanker */
  501.                 /* fake a "finish_jobs()" */
  502.                 newjob->msg.mtype = newjob->msg.id;
  503.                 newjob->msg.id = 1;
  504.                 do_msgsnd(qid, &newjob->msg, 0, 0);
  505.             }
  506.             pid = 0;
  507.             newjob->msg.id = 0;
  508.         }
  509.         break;
  510.  
  511.     case MAXCMD: /* debug entry, used by kdstat */
  512.         {
  513.             char *p = newjob->msg.text;
  514.             struct msgbuf msgp;
  515.             int done = 0;
  516.             int count = 0;
  517.  
  518.             if (strcmp(p, "debug") == 0)
  519.                 debug = 1;
  520.             else if (strcmp(p, "nodebug") == 0)
  521.                 debug = 0;
  522.             else if (strcmp(p, "keep") == 0)
  523.                 keep = 1;
  524.             else if (strcmp(p, "nokeep") == 0)
  525.                 keep = 0;
  526.             else if (strncmp(p, "delay=", 6) == 0) {
  527.                 count = atoi(p + 6);
  528.                 if (count) {
  529.                     delay = count;
  530.                     alarm(delay);
  531.                 }
  532.             }
  533.             else if (strcmp(p, "flush") == 0) {
  534.                 count = 0;
  535.                 while (msgrcv(qid, &msgp, 1, 0,
  536.                     IPC_NOWAIT | MSG_NOERROR) >= 0)
  537.                     ++count;
  538.                 sprintf(p, "flushed %d entries\n", count);
  539.                 p += strlen(p);
  540.  
  541.             }
  542.  
  543.             sprintf(p, "Version " MODUTILS_VERSION ", pid=%d, delay=%d, %skeep, %sdebug\n",
  544.                 getpid(), delay, keep?"":"no", debug?"":"no");
  545.             p += strlen(p);
  546.  
  547.             count = 0;
  548.             for (job = job_head; job; job = job->next) {
  549.                 if (p - newjob->msg.text > MSIZE - 40) {
  550.                     done = 1;
  551.                     break; /* for-loop */
  552.                 }
  553.                 ++count;
  554.                 if (count == 1) {
  555.                     sprintf(p, "job queue:\n");
  556.                     p += strlen(p);
  557.                 }
  558.                 sprintf(p, "pid %d, type %s ('%s')"
  559. #ifdef NEW_KERNELD_PROTOCOL /* i.e. using extended kerneld protocol */
  560.                     " from pid %d"
  561. #endif
  562.                     "\n",
  563.                     job->pid,
  564.                     type2text((int)job->msg.mtype),
  565.                     job->msg.text
  566. #ifdef NEW_KERNELD_PROTOCOL /* i.e. using extended kerneld protocol */
  567.                     , job->msg.pid
  568. #endif
  569.                     );
  570.                 p += strlen(p);
  571.             }
  572.  
  573.             if (!done && !count) {
  574.                 sprintf(p, "no jobs waiting\n");
  575.                 p += strlen(p);
  576.             }
  577.  
  578.         }
  579.         newjob->reply_size = strlen(newjob->msg.text);
  580.         break;
  581. #if 0
  582.     case GENERIC_EXAMPLE:
  583.         /* get the parameter from newjob->msg.text */
  584.         /* if it's illegal, set newjob->status to the error number */
  585.         /* else if it is an internal job: */
  586.         /*     do it and copy any result back to newjob->msg.text */
  587.         /*     update newjob->reply_size with the size (in bytes) */
  588.         /*     update newjob->status if it shouldn't be 0 */
  589.         /* else the job should be spawned: */
  590.         /*     set up the parameters and fork a new process */
  591.         /*     set pid to the pid of the new process */
  592.         break;
  593. #endif
  594.  
  595. #ifdef KERNELD_GET_PERSIST
  596.     case KERNELD_GET_PERSIST:
  597.         for (persist = persist_head; persist; persist = persist->next) {
  598.             if ((strcmp(newjob->msg.text, persist->key.dptr) == 0)
  599.                     && persist->val.dsize) {
  600.                 newjob->reply_size = persist->val.dsize;
  601.                 memcpy(newjob->msg.text, persist->val.dptr,
  602.                     newjob->reply_size);
  603.                 newjob->status = newjob->reply_size;
  604.                 break;
  605.             }
  606.         }
  607.         break;
  608.  
  609.     case KERNELD_SET_PERSIST:
  610.         {
  611.         struct __persist *k = (struct __persist *)newjob->msg.text;
  612.         struct persist_t **prev;
  613.  
  614.         if (k->keylen == 0)
  615.             break;
  616.  
  617.         for (prev = &persist_head, persist = persist_head; persist;
  618.              prev = &(persist->next), persist = persist->next) {
  619.             if (strcmp(k->arr, persist->key.dptr) == 0)
  620.                 break;
  621.         }
  622.         if ((persist)) { /* Previously defined */
  623.             if (k->arglen == 0) {
  624.                 /* Remove entry */
  625.                 *prev = persist->next;
  626. #ifndef NO_GDBM
  627.                 if (kddbmopen(GDBM_WRITER)) {
  628.                     gdbm_delete(dbf, persist->val);
  629.                     gdbm_close(dbf);
  630.                 }
  631. #endif
  632.                 free(persist->key.dptr);
  633.                 free(persist->val.dptr);
  634.                 free(persist);
  635.                 break;
  636.             }
  637.             /* else */
  638.             if (k->arglen != persist->val.dsize) {
  639.                 char *p;
  640.  
  641.                 if ((p = malloc(k->arglen)) == NULL)
  642.                     /* fail, but keep the old value */
  643.                     break;
  644.                 /* else */
  645.                 free(persist->val.dptr);
  646.                 persist->val.dptr = p;
  647.                 persist->val.dsize = k->arglen;
  648.             }
  649.         }
  650.         else { /* Not previously defined */
  651.             if (k->arglen == 0)
  652.                 break;
  653.  
  654.             /* Create entry */
  655.             persist = (struct persist_t *)malloc(sizeof(*persist));
  656.             if (persist == NULL)
  657.                 /* fail, ignore request */
  658.                 break;
  659.  
  660.             if ((persist->key.dptr = malloc(k->keylen)) == NULL) {
  661.                 free(persist);
  662.                 /* fail, ignore request */
  663.                 break;
  664.             }
  665.             if ((persist->val.dptr = malloc(k->arglen)) == NULL) {
  666.                 free(persist->key.dptr);
  667.                 free(persist);
  668.                 /* fail, ignore request */
  669.                 break;
  670.             }
  671.             strcpy(persist->key.dptr, k->arr);
  672.             persist->key.dsize = k->keylen;
  673.             persist->val.dsize = k->arglen;
  674.             persist->next = persist_head;
  675.             persist_head = persist;
  676.         }
  677.         /* Update value */
  678.         if (memcmp(persist->val.dptr, k->arr + k->keylen, k->arglen) == 0)
  679.             break;
  680.         /* else */
  681.         memcpy(persist->val.dptr, k->arr + k->keylen, k->arglen);
  682. #ifndef NO_GDBM
  683.         if (kddbmopen(GDBM_WRCREAT)) {
  684.             gdbm_store(dbf, persist->key, persist->val, GDBM_REPLACE);
  685.             gdbm_close(dbf);
  686.         }
  687. #endif
  688.         }
  689.         break;
  690. #endif
  691.  
  692.     default: /* unknown */
  693.         kerneld_error ("kerneld: unknown message type: %d", op);
  694.         newjob->status = 1; /* failed... ? */
  695.         break;
  696.     }
  697.  
  698.     return pid;
  699. }
  700.  
  701. /*
  702.  * Look for finished jobs and take care of them.
  703.  * Return < 0 if fatal error
  704.  */
  705. int
  706. finish_jobs()
  707. {
  708.     struct job *job;
  709.     struct job **pjob;
  710.     int status = 0;
  711.  
  712.     DPRINT(("finish_jobs\n"));
  713.     for (pjob = &job_head, job = job_head; job && (status >= 0); job = *pjob) {
  714.         if (job->pid == JOB_DONE) {
  715.             /*
  716.              * Does the kernel expect an answer?
  717.              */
  718.             if (job->msg.id) {
  719.                 if (job->reply_fd) {
  720.                     struct timeval timeout = { 0, 0 };
  721.                     fd_set    readfds;
  722.  
  723.                     FD_ZERO(&readfds);
  724.                     FD_SET(job->reply_fd, &readfds);
  725.                     if ((select(job->reply_fd + 1, &readfds,
  726.                         (fd_set *)NULL,
  727.                         (fd_set *)NULL, &timeout) > 0) &&
  728.                         FD_ISSET(job->reply_fd, &readfds)) {
  729.                         job->reply_size = read(job->reply_fd,
  730.                             job->msg.text, MSIZE);
  731.                     }
  732.                     close(job->reply_fd);
  733.                 }
  734.                 job->msg.mtype = job->msg.id;
  735.                 job->msg.id = job->status;
  736.  
  737.                 status = do_msgsnd(qid, &job->msg,
  738.                         ((job->reply_size > 0) ?
  739.                         job->reply_size : 0)
  740.                         , 0);
  741.             }
  742.             /*
  743.              * unlink the job
  744.              */
  745.             DPRINT(("job (%08lx) unlinked\n", (long)job));
  746.             *pjob = job->next;
  747.             free(job);
  748.         }
  749.         else /* job not done */
  750.             pjob = &(job->next);
  751.     }
  752.  
  753.     return status;
  754. }
  755.  
  756. void
  757. do_timer()
  758. {
  759.     timer = 0;
  760.     if (!keep) {
  761.         /* Thanks Jacques!!! */
  762.         struct job *job;
  763.  
  764.         for (job = job_head; job; job = job->next)
  765.             if (job->msg.mtype == KERNELD_REQUEST_MODULE)
  766.                 break;
  767.         if (job == NULL)
  768.             delete_module(NULL);
  769.     }
  770. }
  771.  
  772. int
  773. main(int argc, char *argv[])
  774. {
  775.     struct job *job;
  776.     int pid;
  777.     int status;
  778.     struct utsname uts_info;
  779.     struct sigaction sa;
  780.     sigset_t hold_signals;
  781.  
  782.     /* make me a daemon */
  783.     if ((pid = fork()) != 0) {
  784.         printf("Starting kerneld, version " MODUTILS_VERSION " (pid %d)\n", pid);
  785.         exit(0);
  786.     }
  787.  
  788.     openlog ("kerneld",0,LOG_DAEMON);
  789.     if ((qid = msgget(IPC_PRIVATE, S_IRWXU | IPC_CREAT | IPC_KERNELD)) < 0){
  790.         kerneld_error("Can't initialise message queue: %s",strerror(errno));
  791.         exit(1);
  792.     }
  793.  
  794. #ifdef DEBUG
  795.     t0 = time(0);
  796. #endif
  797.  
  798.     setbuf(stdout, NULL);
  799.     syslog (LOG_INFO,"started, pid=%d, qid=%d", getpid(), qid);
  800.  
  801.     uname(&uts_info);
  802.     sprintf(kd_dbmfile, "/lib/modules/%s/persist.gdbm", uts_info.release);
  803.     gdbm_reload(0); /* dummy arg */
  804.  
  805.     setsid();
  806.  
  807.     sigemptyset(&hold_signals);
  808.     sigaddset(&hold_signals, SIGCHLD);
  809.     sigaddset(&hold_signals, SIGHUP);
  810.  
  811.     sa.sa_mask = hold_signals;
  812.     sa.sa_flags = SA_RESTART;
  813.  
  814.     sa.sa_handler = handle_child;
  815.     if (sigaction(SIGCHLD, &sa, NULL) < 0) {
  816.         kerneld_perror("sigaction(SIGCHLD)");
  817.     }
  818.  
  819.     sa.sa_handler = handle_timer;
  820.     if (sigaction(SIGALRM, &sa, NULL) < 0) {
  821.         kerneld_perror("sigaction(SIGARLM)");
  822.     }
  823.  
  824.     sa.sa_handler = gdbm_reload;
  825.     if (sigaction(SIGHUP, &sa, NULL) < 0) {
  826.         kerneld_perror("sigaction(SIGHUP)");
  827.     }
  828.  
  829.     while (argc > 1) {
  830.         if (strcmp(argv[1], "keep") == 0)
  831.             keep = 1;
  832.         else if (strncmp(argv[1], "delay=", 6) == 0)
  833.             delay = atoi(&(argv[1][6]));
  834.         else if (strcmp(argv[1], "debug") == 0)
  835.             debug = 1;
  836.         else if (strncmp(argv[1], "type=", 5) == 0)
  837.             want_type = atoi(&(argv[1][5]));
  838.         ++argv;
  839.         --argc;
  840.     }
  841.  
  842.     dev_null = open("/dev/null", O_RDWR);
  843.  
  844.     if (!keep)
  845.         alarm(delay); /* start the timer task */
  846.  
  847.     for (;;) {
  848.         job = (struct job *)calloc(1, sizeof(struct job) + MSIZE);
  849.         if (job == NULL) {
  850.             kerneld_perror("calloc(job)");
  851.         }
  852.  
  853.     restart:
  854.         while ((status = do_msgrcv(qid, &job->msg,
  855.             MSIZE, want_type, MSG_NOERROR)) == 0)
  856.             /*keep on*/;
  857.  
  858.         if (status < 0) {
  859.             /* was the msgrcv interrupted by a signal? */
  860.             if (errno == EINTR) {
  861.                 if (timer) {
  862.                     do_timer();
  863.                 }
  864.                 if (finish_jobs() < 0) {
  865.                     break; /* fatal error */
  866.                 }
  867.                 /* else */
  868.                 goto restart; /* wait for message */
  869.             }
  870.             /* else */
  871.             break; /* some other error */
  872.         }
  873.         /* else, we got a message */
  874.  
  875.         /* null-terminate the message */
  876.         status = (status >= MSIZE) ? (MSIZE - 1) : status;
  877.         ((struct msgbuf *)&job->msg)->mtext[status] = '\0';
  878.         DPRINT(("job (%08lx) scheduled, type %s ('%s')\n", (long)job,
  879.             type2text((int)job->msg.mtype), job->msg.text));
  880.  
  881.         /* Don't trap SIGCHLD til we add the job to the queue */
  882.         sigprocmask(SIG_BLOCK, &hold_signals, NULL);
  883.         if ((pid = spawn_it(job)) < 0)
  884.             break; /* fatal error */
  885.  
  886.         /*
  887.          * Link this job into the job queue if something was
  888.          * spawned or if someone is waiting for an answer.
  889.          */
  890.         if ((pid > 0) || (job->msg.id != 0)) {
  891.             job->pid = pid;
  892.             job->next = job_head;
  893.             job_head = job;
  894.             DPRINT(("job (%08lx) stored, pid=%d\n", (long)job,pid));
  895.             /* take care of all tasks, including non-spawned ones */
  896.             if (finish_jobs() < 0)
  897.                 break; /* fatal error */
  898.         }
  899.         else { /* all work requested has been performed */
  900.             DPRINT(("job (%08lx) released\n", (long)job));
  901.             free(job);
  902.         }
  903.         sigprocmask(SIG_UNBLOCK, &hold_signals, NULL);
  904.     }
  905.  
  906.     kerneld_perror("exit");
  907.     return 1;
  908. }
  909.