From: Philip Homburg Date: Fri, 20 Oct 2006 15:01:32 +0000 (+0000) Subject: (Incomplete) support for access control in PCI (pci_set_acl). X-Git-Tag: v3.1.3~165 X-Git-Url: http://zhaoyanbai.com/repos/man.dig.html?a=commitdiff_plain;h=f9ccfca2a1005a1ee612c1e646e852161d16a4b0;p=minix.git (Incomplete) support for access control in PCI (pci_set_acl). -script argument to service for crash recovery scripts -config argument to service for driver resource configuration restart command in service to restart a driver after a crash (for use in crash recovery scripts). down and refresh now take labels instead of pids. verious changes in rs to make this work. --- diff --git a/drivers/pci/main.c b/drivers/pci/main.c index be1b0ca29..1c4b8066f 100644 --- a/drivers/pci/main.c +++ b/drivers/pci/main.c @@ -5,6 +5,7 @@ main.c #include "../drivers.h" #include +#include #include "pci.h" @@ -16,6 +17,12 @@ PRIVATE struct name int tasknr; } names[NR_DRIVERS]; +PRIVATE struct acl +{ + int inuse; + struct rs_pci acl; +} acl[NR_DRIVERS]; + FORWARD _PROTOTYPE( void do_init, (message *mp) ); FORWARD _PROTOTYPE( void do_sig_handler, (void) ); FORWARD _PROTOTYPE( void do_first_dev, (message *mp) ); @@ -26,6 +33,7 @@ FORWARD _PROTOTYPE( void do_dev_name, (message *mp) ); FORWARD _PROTOTYPE( void do_dev_name_s, (message *mp) ); FORWARD _PROTOTYPE( void do_slot_name, (message *mp) ); FORWARD _PROTOTYPE( void do_slot_name_s, (message *mp) ); +FORWARD _PROTOTYPE( void do_acl, (message *mp) ); FORWARD _PROTOTYPE( void do_reserve, (message *mp) ); FORWARD _PROTOTYPE( void do_attr_r8, (message *mp) ); FORWARD _PROTOTYPE( void do_attr_r16, (message *mp) ); @@ -34,6 +42,8 @@ FORWARD _PROTOTYPE( void do_attr_w8, (message *mp) ); FORWARD _PROTOTYPE( void do_attr_w16, (message *mp) ); FORWARD _PROTOTYPE( void do_attr_w32, (message *mp) ); FORWARD _PROTOTYPE( void do_rescan_bus, (message *mp) ); +FORWARD _PROTOTYPE( void reply, (message *mp, int result) ); +FORWARD _PROTOTYPE( struct rs_pci *find_acl, (int endpoint) ); int main(void) { @@ -72,6 +82,7 @@ int main(void) case BUSC_PCI_RESCAN: do_rescan_bus(&m); break; case BUSC_PCI_DEV_NAME_S: do_dev_name_s(&m); break; case BUSC_PCI_SLOT_NAME_S: do_slot_name_s(&m); break; + case BUSC_PCI_ACL: do_acl(&m); break; case PROC_EVENT: do_sig_handler(); break; default: printf("PCI: got message from %d, type %d\n", @@ -140,10 +151,16 @@ message *mp; PRIVATE void do_first_dev(mp) message *mp; { - int r, devind; + int i, r, devind; u16_t vid, did; + struct rs_pci *aclp; + + aclp= find_acl(mp->m_source); + + if (!aclp) + printf("do_first_dev: no acl for caller %d\n", mp->m_source); - r= pci_first_dev(&devind, &vid, &did); + r= pci_first_dev_a(aclp, &devind, &vid, &did); if (r == 1) { mp->m1_i1= devind; @@ -164,10 +181,12 @@ message *mp; { int r, devind; u16_t vid, did; + struct rs_pci *aclp; devind= mp->m1_i1; + aclp= find_acl(mp->m_source); - r= pci_next_dev(&devind, &vid, &did); + r= pci_next_dev_a(aclp, &devind, &vid, &did); if (r == 1) { mp->m1_i1= devind; @@ -354,6 +373,48 @@ message *mp; } } +PRIVATE void do_acl(mp) +message *mp; +{ + int i, r, gid; + + if (mp->m_source != RS_PROC_NR) + { +printf("do_acl: not from RS\n"); + reply(mp, EPERM); + return; + } + + for (i= 0; i= NR_DRIVERS) + { +printf("do_acl: table is full\n"); + reply(mp, ENOMEM); + return; + } + + gid= mp->m1_i1; + + r= sys_safecopyfrom(mp->m_source, gid, 0, (vir_bytes)&acl[i].acl, + sizeof(acl[i].acl), D); + if (r != OK) + { +printf("do_acl: safecopyfrom failed\n"); + reply(mp, r); + return; + } + acl[i].inuse= 1; + printf("do_acl: setting ACL for %d ('%s') at entry %d\n", + acl[i].acl.rsp_endpoint, acl[i].acl.rsp_label, + i); + + reply(mp, OK); +} + PRIVATE void do_reserve(mp) message *mp; { @@ -521,3 +582,33 @@ message *mp; } } + +PRIVATE void reply(mp, result) +message *mp; +int result; +{ + int r; + message m; + + m.m_type= result; + r= send(mp->m_source, &m); + if (r != 0) + printf("reply: unable to send to %d: %d\n", mp->m_source, r); +} + + +PRIVATE struct rs_pci *find_acl(endpoint) +int endpoint; +{ + int i; + + /* Find ACL entry for caller */ + for (i= 0; i #include #include #include +#include #include #include "pci.h" @@ -146,6 +147,7 @@ FORWARD _PROTOTYPE( void pcii_wreg32, (int busind, int devind, int port, FORWARD _PROTOTYPE( u16_t pcii_rsts, (int busind) ); FORWARD _PROTOTYPE( void pcii_wsts, (int busind, U16_t value) ); FORWARD _PROTOTYPE( void print_capabilities, (int devind) ); +FORWARD _PROTOTYPE( int visible, (struct rs_pci *aclp, int devind) ); /*===========================================================================* * helper functions for I/O * @@ -248,19 +250,23 @@ int *devindp; } /*===========================================================================* - * pci_first_dev * + * pci_first_dev_a * *===========================================================================*/ -PUBLIC int pci_first_dev(devindp, vidp, didp) +PUBLIC int pci_first_dev_a(aclp, devindp, vidp, didp) +struct rs_pci *aclp; int *devindp; u16_t *vidp; u16_t *didp; { - int devind; + int i, devind; for (devind= 0; devind < nr_pcidev; devind++) { - if (!pcidev[devind].pd_inuse) - break; + if (pcidev[devind].pd_inuse) + continue; + if (!visible(aclp, devind)) + continue; + break; } if (devind >= nr_pcidev) return 0; @@ -273,7 +279,8 @@ u16_t *didp; /*===========================================================================* * pci_next_dev * *===========================================================================*/ -PUBLIC int pci_next_dev(devindp, vidp, didp) +PUBLIC int pci_next_dev_a(aclp, devindp, vidp, didp) +struct rs_pci *aclp; int *devindp; u16_t *vidp; u16_t *didp; @@ -282,8 +289,11 @@ u16_t *didp; for (devind= *devindp+1; devind < nr_pcidev; devind++) { - if (!pcidev[devind].pd_inuse) - break; + if (pcidev[devind].pd_inuse) + continue; + if (!visible(aclp, devind)) + continue; + break; } if (devind >= nr_pcidev) return 0; @@ -2428,6 +2438,48 @@ int devind; } } + +/*===========================================================================* + * visible * + *===========================================================================*/ +PRIVATE int visible(aclp, devind) +struct rs_pci *aclp; +int devind; +{ + int i; + u32_t class_id; + + if (!aclp) + return TRUE; /* Should be changed when ACLs become + * mandatory. + */ + /* Check whether the caller is allowed to get this device. */ + for (i= 0; irsp_nr_device; i++) + { + if (aclp->rsp_device[i].vid == pcidev[devind].pd_vid && + aclp->rsp_device[i].did == pcidev[devind].pd_did) + { + return TRUE; + } + } + if (!aclp->rsp_nr_class) + return FALSE; + + class_id= (pcidev[devind].pd_baseclass << 16) | + (pcidev[devind].pd_subclass << 8) | + pcidev[devind].pd_infclass; + for (i= 0; irsp_nr_class; i++) + { + if (aclp->rsp_class[i].class == + (class_id & aclp->rsp_class[i].mask)) + { + return TRUE; + } + } + + return FALSE; +} + /* * $PchId: pci.c,v 1.7 2003/08/07 09:06:51 philip Exp $ */ diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index 5a56fffba..898090484 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -84,6 +84,10 @@ extern struct pci_pcibridge pci_pcibridge[]; /* Utility functions */ _PROTOTYPE( void pci_reserve3, (int devind, int proc, char name[M3_STRING])); _PROTOTYPE( void pci_release, (char name[M3_STRING]) ); +_PROTOTYPE( int pci_first_dev_a, (struct rs_pci *aclp, int *devindp, + u16_t *vidp, u16_t *didp) ); +_PROTOTYPE( int pci_next_dev_a, (struct rs_pci *aclp, int *devindp, + u16_t *vidp, u16_t *didp) ); /* * $PchId: pci.h,v 1.4 2001/12/06 20:21:22 philip Exp $ diff --git a/include/minix/com.h b/include/minix/com.h index 1705d128b..b143cf1d0 100755 --- a/include/minix/com.h +++ b/include/minix/com.h @@ -131,6 +131,9 @@ #define BUSC_PCI_SLOT_NAME_S (BUSC_RQ_BASE + 16) /* Get the name of a * PCI slot (safecopy) */ +#define BUSC_PCI_ACL (BUSC_RQ_BASE + 17) /* Set the ACL for a + * driver (safecopy) + */ /*===========================================================================* * Messages for BLOCK and CHARACTER device drivers * @@ -537,13 +540,18 @@ #define RS_UP (RS_RQ_BASE + 0) /* start system service */ #define RS_DOWN (RS_RQ_BASE + 1) /* stop system service */ -#define RS_REFRESH (RS_RQ_BASE + 2) /* restart system service */ -#define RS_RESCUE (RS_RQ_BASE + 3) /* set rescue directory */ -#define RS_SHUTDOWN (RS_RQ_BASE + 4) /* alert about shutdown */ -#define RS_UP_COPY (RS_RQ_BASE + 5) /* start system service and +#define RS_REFRESH (RS_RQ_BASE + 2) /* refresh system service */ +#define RS_RESTART (RS_RQ_BASE + 3) /* restart system service */ +#define RS_RESCUE (RS_RQ_BASE + 4) /* set rescue directory */ +#define RS_SHUTDOWN (RS_RQ_BASE + 5) /* alert about shutdown */ +#define RS_UP_COPY (RS_RQ_BASE + 6) /* start system service and * keep the binary in memory */ -#define RS_RUN (RS_RQ_BASE + 6) /* run without restart */ +#define RS_RUN (RS_RQ_BASE + 7) /* run without restart */ +#define RS_START (RS_RQ_BASE + 8) /* start a driver/service + * arguments are passed in + * a struct rs_start + */ # define RS_CMD_ADDR m1_p1 /* command string */ # define RS_CMD_LEN m1_i1 /* length of command */ diff --git a/include/minix/rs.h b/include/minix/rs.h new file mode 100644 index 000000000..371d871a5 --- /dev/null +++ b/include/minix/rs.h @@ -0,0 +1,53 @@ +/* +minix/rs.h + +Interface to the reincarnation server +*/ + +#define RSS_NR_IRQ 16 +#define RSS_NR_IO 16 +#define RSS_NR_PCI_ID 16 +#define RSS_NR_PCI_CLASS 4 +#define RSS_NR_SYSTEM 2 + +/* Arguments needed to start a new driver or server */ +struct rs_start +{ + unsigned rss_flags; + char *rss_cmd; + size_t rss_cmdlen; + uid_t rss_uid; + int rss_nice; + int rss_major; + long rss_period; + char *rss_script; + size_t rss_scriptlen; + int rss_nr_irq; + int rss_irq[RSS_NR_IRQ]; + int rss_nr_io; + struct { unsigned base; unsigned len; } rss_io[RSS_NR_IO]; + int rss_nr_pci_id; + struct { u16_t vid; u16_t did; } rss_pci_id[RSS_NR_PCI_ID]; + int rss_nr_pci_class; + struct { u32_t class; u32_t mask; } rss_pci_class[RSS_NR_PCI_CLASS]; + u32_t rss_system[RSS_NR_SYSTEM]; +}; + +#define RF_COPY 0x01 /* Copy the brinary into RS to make it possible + * to restart the driver without accessing FS + */ + +#define RSP_LABEL_SIZE 16 +#define RSP_NR_DEVICE 16 +#define RSP_NR_CLASS 4 + +/* ACL information for access to PCI devices */ +struct rs_pci +{ + char rsp_label[RSP_LABEL_SIZE]; /* Name of the driver */ + int rsp_endpoint; + int rsp_nr_device; + struct { u16_t vid; u16_t did; } rsp_device[RSP_NR_DEVICE]; + int rsp_nr_class; + struct { u32_t class; u32_t mask; } rsp_class[RSP_NR_CLASS]; +}; diff --git a/include/minix/syslib.h b/include/minix/syslib.h index 2d04987fe..4ae273c30 100755 --- a/include/minix/syslib.h +++ b/include/minix/syslib.h @@ -19,6 +19,7 @@ /* Forward declaration */ struct reg86u; +struct rs_pci; #define SYSTASK SYSTEM @@ -194,6 +195,7 @@ _PROTOTYPE( void pci_attr_w16, (int devind, int port, U16_t value) ); _PROTOTYPE( void pci_attr_w32, (int devind, int port, u32_t value) ); _PROTOTYPE( char *pci_dev_name, (U16_t vid, U16_t did) ); _PROTOTYPE( char *pci_slot_name, (int devind) ); +_PROTOTYPE( int pci_set_acl, (struct rs_pci *rs_pci) ); #endif /* _SYSLIB_H */ diff --git a/lib/syslib/Makefile.in b/lib/syslib/Makefile.in index 63c0b14e3..48aeb2ae3 100644 --- a/lib/syslib/Makefile.in +++ b/lib/syslib/Makefile.in @@ -22,6 +22,7 @@ libsys_FILES=" \ pci_next_dev.c \ pci_rescan_bus.c \ pci_reserve.c \ + pci_set_acl.c \ pci_slot_name.c \ safecopies.c \ sys_abort.c \ diff --git a/lib/syslib/pci_set_acl.c b/lib/syslib/pci_set_acl.c new file mode 100644 index 000000000..b9f2082ab --- /dev/null +++ b/lib/syslib/pci_set_acl.c @@ -0,0 +1,56 @@ +/* +pci_set_acl.c +*/ + +#include "pci.h" +#include "syslib.h" +#include +#include + +/*===========================================================================* + * pci_set_acl * + *===========================================================================*/ +PUBLIC int pci_set_acl(rs_pci) +struct rs_pci *rs_pci; +{ + int r; + cp_grant_id_t gid; + message m; + + if (pci_procnr == ANY) + { + r= _pm_findproc("pci", &pci_procnr); + if (r != 0) + { + panic("pci", + "pci_set_acl: _pm_findproc failed for 'pci'", + r); + } + } + + +printf("pci_set_acl: before cpf_grant_direct\n"); + gid= cpf_grant_direct(pci_procnr, (vir_bytes)rs_pci, sizeof(*rs_pci), + CPF_READ); +printf("pci_set_acl: after cpf_grant_direct: gid %d\n", gid); + if (gid == -1) + { + printf("pci_set_acl: cpf_grant_direct failed: %d\n", + errno); + return EINVAL; + } + + m.m_type= BUSC_PCI_ACL; + m.m1_i1= gid; + +printf("pci_set_acl: before sendrec to %d\n", pci_procnr); + r= sendrec(pci_procnr, &m); +printf("pci_set_acl: after sendrec to %d\n", pci_procnr); + cpf_revoke(gid); +printf("pci_set_acl: after cpf_revoke\n"); + if (r != 0) + panic("pci", "pci_set_acl: can't talk to PCI", r); + + return r; +} + diff --git a/servers/rs/inc.h b/servers/rs/inc.h index 6248046fa..78407500c 100644 --- a/servers/rs/inc.h +++ b/servers/rs/inc.h @@ -26,6 +26,9 @@ #include #include +#include /* For priv.h */ +#include "../../kernel/priv.h" + #include #include #include diff --git a/servers/rs/main.c b/servers/rs/main.c index 2e75aa12b..140423bfe 100644 --- a/servers/rs/main.c +++ b/servers/rs/main.c @@ -84,9 +84,11 @@ PUBLIC int main(void) case RS_UP: result = do_up(&m, FALSE, 0); break; case RS_UP_COPY: result = do_up(&m, TRUE, 0); break; case RS_RUN: result = do_up(&m, FALSE, RS_EXITING); break; + case RS_START: result = do_start(&m); break; case RS_DOWN: result = do_down(&m); break; case RS_REFRESH: result = do_refresh(&m); break; case RS_RESCUE: result = do_rescue(&m); break; + case RS_RESTART: result = do_restart(&m); break; case RS_SHUTDOWN: result = do_shutdown(&m); break; case GETSYSINFO: result = do_getsysinfo(&m); break; default: diff --git a/servers/rs/manager.c b/servers/rs/manager.c index 506825eb5..048da7ab6 100644 --- a/servers/rs/manager.c +++ b/servers/rs/manager.c @@ -11,8 +11,12 @@ #include #include #include +#include #include +#include /* For priv.h */ +#include "../../kernel/priv.h" + /* Allocate variables. */ struct rproc rproc[NR_SYS_PROCS]; /* system process table */ struct rproc *rproc_ptr[NR_PROCS]; /* mapping for fast access */ @@ -24,6 +28,9 @@ FORWARD _PROTOTYPE( int start_service, (struct rproc *rp, int flags) ); FORWARD _PROTOTYPE( int stop_service, (struct rproc *rp,int how) ); FORWARD _PROTOTYPE( int fork_nb, (void) ); FORWARD _PROTOTYPE( int read_exec, (struct rproc *rp) ); +FORWARD _PROTOTYPE( void run_script, (struct rproc *rp) ); +FORWARD _PROTOTYPE( void init_privs, (struct rproc *rp, struct priv *privp) ); +FORWARD _PROTOTYPE( void init_pci, (struct rproc *rp, int endpoint) ); PRIVATE int shutting_down = FALSE; @@ -45,8 +52,10 @@ int flags; /* extra flags, if any */ int slot_nr; /* local table entry */ int arg_count; /* number of arguments */ char *cmd_ptr; /* parse command string */ + char *label; /* unique name of command */ enum dev_style dev_style; /* device style */ int s; /* status variable */ + int len; /* length of string */ /* See if there is a free entry in the table with system processes. */ if (nr_in_use >= NR_SYS_PROCS) return(EAGAIN); @@ -66,6 +75,8 @@ int flags; /* extra flags, if any */ rp->r_cmd[m_ptr->RS_CMD_LEN] = '\0'; /* ensure it is terminated */ if (rp->r_cmd[0] != '/') return(EINVAL); /* insist on absolute path */ + rp->r_script[0]= '\0'; + /* Build argument vector to be passed to execute call. The format of the * arguments vector is: path, arguments, NULL. */ @@ -85,6 +96,22 @@ int flags; /* extra flags, if any */ rp->r_argv[arg_count] = NULL; /* end with NULL pointer */ rp->r_argc = arg_count; + /* Default label for the driver */ + label= strrchr(rp->r_argv[0], '/'); + if (label) + label++; + else + label= rp->r_argv[0]; + len= strlen(label); + if (len > MAX_LABEL_LEN-1) + len= MAX_LABEL_LEN-1; /* truncate name */ + memcpy(rp->r_label, label, len); + rp->r_label[len]= '\0'; + printf("using label '%s'\n", rp->r_label); + + rp->r_uid= 0; + rp->r_nice= 0; + rp->r_exec= NULL; if (do_copy) { @@ -98,31 +125,291 @@ int flags; /* extra flags, if any */ rp->r_dev_nr = m_ptr->RS_DEV_MAJOR; rp->r_dev_style = STYLE_DEV; rp->r_restarts = -1; /* will be incremented */ + rp->r_set_resources= 0; /* old style */ /* All information was gathered. Now try to start the system service. */ return(start_service(rp, flags)); } +/*===========================================================================* + * do_start * + *===========================================================================*/ +PUBLIC int do_start(m_ptr) +message *m_ptr; /* request message pointer */ +{ +/* A request was made to start a new system service. + */ + register struct rproc *rp; /* system process table */ + int slot_nr; /* local table entry */ + int arg_count; /* number of arguments */ + char *cmd_ptr; /* parse command string */ + char *label; /* unique name of command */ + enum dev_style dev_style; /* device style */ + int s; /* status variable */ + int len; /* length of string */ + int i; + struct rproc *tmp_rp; + struct rs_start rs_start; + + /* Get the request structure */ + s= sys_datacopy(m_ptr->m_source, (vir_bytes) m_ptr->RS_CMD_ADDR, + SELF, (vir_bytes) &rs_start, sizeof(rs_start)); + if (s != OK) return(s); + + /* See if there is a free entry in the table with system processes. */ + if (nr_in_use >= NR_SYS_PROCS) return(EAGAIN); + for (slot_nr = 0; slot_nr < NR_SYS_PROCS; slot_nr++) { + rp = &rproc[slot_nr]; /* get pointer to slot */ + if (! rp->r_flags & RS_IN_USE) /* check if available */ + break; + } + nr_in_use ++; /* update administration */ + + /* Obtain command name and parameters. This is a space-separated string + * that looks like "/sbin/service arg1 arg2 ...". Arguments are optional. + */ + if (rs_start.rss_cmdlen > MAX_COMMAND_LEN-1) return(E2BIG); + s=sys_datacopy(m_ptr->m_source, (vir_bytes) rs_start.rss_cmd, + SELF, (vir_bytes) rp->r_cmd, rs_start.rss_cmdlen); + if (s != OK) return(s); + rp->r_cmd[rs_start.rss_cmdlen] = '\0'; /* ensure it is terminated */ + if (rp->r_cmd[0] != '/') return(EINVAL); /* insist on absolute path */ + + /* Build argument vector to be passed to execute call. The format of the + * arguments vector is: path, arguments, NULL. + */ + arg_count = 0; /* initialize arg count */ + rp->r_argv[arg_count++] = rp->r_cmd; /* start with path */ + cmd_ptr = rp->r_cmd; /* do some parsing */ + while(*cmd_ptr != '\0') { /* stop at end of string */ + if (*cmd_ptr == ' ') { /* next argument */ + *cmd_ptr = '\0'; /* terminate previous */ + while (*++cmd_ptr == ' ') ; /* skip spaces */ + if (*cmd_ptr == '\0') break; /* no arg following */ + if (arg_count>MAX_NR_ARGS+1) break; /* arg vector full */ + rp->r_argv[arg_count++] = cmd_ptr; /* add to arg vector */ + } + cmd_ptr ++; /* continue parsing */ + } + rp->r_argv[arg_count] = NULL; /* end with NULL pointer */ + rp->r_argc = arg_count; + + /* Default label for the driver */ + label= strrchr(rp->r_argv[0], '/'); + if (label) + label++; + else + label= rp->r_argv[0]; + len= strlen(label); + if (len > MAX_LABEL_LEN-1) + len= MAX_LABEL_LEN-1; /* truncate name */ + memcpy(rp->r_label, label, len); + rp->r_label[len]= '\0'; + printf("using label '%s'\n", rp->r_label); + + /* Check for duplicates */ + for (slot_nr = 0; slot_nr < NR_SYS_PROCS; slot_nr++) { + tmp_rp = &rproc[slot_nr]; /* get pointer to slot */ + if (!(tmp_rp->r_flags & RS_IN_USE)) /* check if available */ + continue; + if (tmp_rp == rp) + continue; /* Our slot */ + if (strcmp(tmp_rp->r_label, rp->r_label) == 0) + { + printf("found duplicate: slot %d\n", slot_nr); + return EBUSY; + } + } + + rp->r_script[0]= '\0'; + if (rs_start.rss_scriptlen > MAX_SCRIPT_LEN-1) return(E2BIG); + if (rs_start.rss_script != NULL) + { + s=sys_datacopy(m_ptr->m_source, (vir_bytes) rs_start.rss_script, + SELF, (vir_bytes) rp->r_script, rs_start.rss_scriptlen); + if (s != OK) return(s); + rp->r_script[rs_start.rss_scriptlen] = '\0'; + } + rp->r_uid= rs_start.rss_uid; + rp->r_nice= rs_start.rss_nice; + + rp->r_exec= NULL; + if (rs_start.rss_flags & RF_COPY) + { + s= read_exec(rp); + if (s != OK) + return s; + } + + /* Copy granted resources */ + if (rs_start.rss_nr_irq > NR_IRQ) + { + printf("do_start: too many IRQs requested\n"); + return EINVAL; + } + rp->r_priv.s_nr_irq= rs_start.rss_nr_irq; + for (i= 0; ir_priv.s_nr_irq; i++) + { + rp->r_priv.s_irq_tab[i]= rs_start.rss_irq[i]; + printf("do_start: IRQ %d\n", rp->r_priv.s_irq_tab[i]); + } + + if (rs_start.rss_nr_io > NR_IO_RANGE) + { + printf("do_start: too many I/O ranges requested\n"); + return EINVAL; + } + rp->r_priv.s_nr_io_range= rs_start.rss_nr_io; + for (i= 0; ir_priv.s_nr_io_range; i++) + { + rp->r_priv.s_io_tab[i].ior_base= rs_start.rss_io[i].base; + rp->r_priv.s_io_tab[i].ior_limit= + rs_start.rss_io[i].base+rs_start.rss_io[i].len-1; + printf("do_start: I/O [%x..%x]\n", + rp->r_priv.s_io_tab[i].ior_base, + rp->r_priv.s_io_tab[i].ior_limit); + } + + if (rs_start.rss_nr_pci_id > MAX_NR_PCI_ID) + { + printf("do_start: too many PCI device IDs\n"); + return EINVAL; + } + rp->r_nr_pci_id= rs_start.rss_nr_pci_id; + for (i= 0; ir_nr_pci_id; i++) + { + rp->r_pci_id[i].vid= rs_start.rss_pci_id[i].vid; + rp->r_pci_id[i].did= rs_start.rss_pci_id[i].did; + printf("do_start: PCI %04x/%04x\n", + rp->r_pci_id[i].vid, rp->r_pci_id[i].did); + } + if (rs_start.rss_nr_pci_class > MAX_NR_PCI_CLASS) + { + printf("do_start: too many PCI class IDs\n"); + return EINVAL; + } + rp->r_nr_pci_class= rs_start.rss_nr_pci_class; + for (i= 0; ir_nr_pci_class; i++) + { + rp->r_pci_class[i].class= rs_start.rss_pci_class[i].class; + rp->r_pci_class[i].mask= rs_start.rss_pci_class[i].mask; + printf("do_start: PCI class %06x mask %06x\n", + rp->r_pci_class[i].class, rp->r_pci_class[i].mask); + } + + /* Copy 'system' call number bits */ + if (sizeof(rs_start.rss_system[0]) == sizeof(rp->r_call_mask[0]) && + sizeof(rs_start.rss_system) == sizeof(rp->r_call_mask)) + { + for (i= 0; ir_call_mask[i]= rs_start.rss_system[i]; + } + else + { + printf( + "do_start: internal inconsistency: bad size of r_call_mask\n"); + memset(rp->r_call_mask, '\0', sizeof(rp->r_call_mask)); + } + + /* Initialize some fields. */ + rp->r_period = rs_start.rss_period; + rp->r_dev_nr = rs_start.rss_major; + rp->r_dev_style = STYLE_DEV; + rp->r_restarts = -1; /* will be incremented */ + rp->r_set_resources= 1; /* new style, enforece + * I/O resources + */ + + /* All information was gathered. Now try to start the system service. */ + return(start_service(rp, 0)); +} + + /*===========================================================================* * do_down * *===========================================================================*/ PUBLIC int do_down(message *m_ptr) { register struct rproc *rp; - pid_t pid = (pid_t) m_ptr->RS_PID; + size_t len; + int s, proc; + char label[MAX_LABEL_LEN]; + + len= m_ptr->RS_CMD_LEN; + if (len >= sizeof(label)) + return EINVAL; /* Too long */ + + s= sys_datacopy(m_ptr->m_source, (vir_bytes) m_ptr->RS_CMD_ADDR, + SELF, (vir_bytes) label, len); + if (s != OK) return(s); + label[len]= '\0'; for (rp=BEG_RPROC_ADDR; rpr_flags & RS_IN_USE && rp->r_pid == pid) { + if (rp->r_flags & RS_IN_USE && strcmp(rp->r_label, label) == 0) { #if VERBOSE - printf("stopping %d (%d)\n", pid, m_ptr->RS_PID); + printf("stopping '%s' (%d)\n", label, rp->r_pid); #endif stop_service(rp,RS_EXITING); + if (rp->r_pid == -1) + { + /* Process is already gone */ + rp->r_flags = 0; /* release slot */ + if (rp->r_exec) + { + free(rp->r_exec); + rp->r_exec= NULL; + } + proc = _ENDPOINT_P(rp->r_proc_nr_e); + rproc_ptr[proc] = NULL; + } + return(OK); + } + } +#if VERBOSE + printf("do_down: '%s' not found\n", label); +#endif + return(ESRCH); +} + + +/*===========================================================================* + * do_restart * + *===========================================================================*/ +PUBLIC int do_restart(message *m_ptr) +{ + register struct rproc *rp; + size_t len; + int s, proc; + char label[MAX_LABEL_LEN]; + + len= m_ptr->RS_CMD_LEN; + if (len >= sizeof(label)) + return EINVAL; /* Too long */ + + s= sys_datacopy(m_ptr->m_source, (vir_bytes) m_ptr->RS_CMD_ADDR, + SELF, (vir_bytes) label, len); + if (s != OK) return(s); + label[len]= '\0'; + + for (rp=BEG_RPROC_ADDR; rpr_flags & RS_IN_USE && strcmp(rp->r_label, label) == 0) { +#if VERBOSE + printf("restarting '%s' (%d)\n", label, rp->r_pid); +#endif + if (rp->r_pid >= 0) + { + printf("do_restart: '%s' is (still) running, pid = %d\n", + rp->r_pid); + return EBUSY; + } + rp->r_flags &= ~(RS_EXITING|RS_REFRESHING|RS_NOPINGREPLY); + start_service(rp, 0); return(OK); } } #if VERBOSE - printf("not found %d (%d)\n", pid, m_ptr->RS_PID); + printf("do_restart: '%s' not found\n", label); #endif return(ESRCH); } @@ -134,19 +421,30 @@ PUBLIC int do_down(message *m_ptr) PUBLIC int do_refresh(message *m_ptr) { register struct rproc *rp; - pid_t pid = (pid_t) m_ptr->RS_PID; + size_t len; + int s; + char label[MAX_LABEL_LEN]; + + len= m_ptr->RS_CMD_LEN; + if (len >= sizeof(label)) + return EINVAL; /* Too long */ + + s= sys_datacopy(m_ptr->m_source, (vir_bytes) m_ptr->RS_CMD_ADDR, + SELF, (vir_bytes) label, len); + if (s != OK) return(s); + label[len]= '\0'; for (rp=BEG_RPROC_ADDR; rpr_flags & RS_IN_USE && rp->r_pid == pid) { + if (rp->r_flags & RS_IN_USE && strcmp(rp->r_label, label) == 0) { #if VERBOSE - printf("refreshing %d (%d)\n", pid, m_ptr->RS_PID); + printf("refreshing %s (%d)\n", rp->r_label, rp->r_pid); #endif stop_service(rp,RS_REFRESHING); return(OK); } } #if VERBOSE - printf("not found %d (%d)\n", pid, m_ptr->RS_PID); + printf("do_refresh: '%s' not found\n", label); #endif return(ESRCH); } @@ -225,6 +523,7 @@ PUBLIC void do_exit(message *m_ptr) proc = _ENDPOINT_P(rp->r_proc_nr_e); rproc_ptr[proc] = NULL; /* invalidate */ + rp->r_pid= -1; if ((rp->r_flags & RS_EXITING) || shutting_down) { rp->r_flags = 0; /* release slot */ @@ -237,7 +536,10 @@ PUBLIC void do_exit(message *m_ptr) } else if(rp->r_flags & RS_REFRESHING) { rp->r_restarts = -1; /* reset counter */ - start_service(rp, 0); /* direct restart */ + if (rp->r_script[0] != '\0') + run_script(rp); + else + start_service(rp, 0); /* direct restart */ } else if (WIFEXITED(exit_status) && WEXITSTATUS(exit_status) == EXEC_FAILED) { @@ -254,7 +556,19 @@ PUBLIC void do_exit(message *m_ptr) #if 0 rp->r_restarts= 0; #endif - if (rp->r_restarts > 0) { + if (WIFSIGNALED(exit_status)) { + switch(WTERMSIG(exit_status)) + { + case SIGKILL: rp->r_flags |= RS_KILLED; break; + default: rp->r_flags |= RS_CRASHED; break; + } + } + else + rp->r_flags |= RS_CRASHED; + + if (rp->r_script[0] != '\0') + run_script(rp); + else if (rp->r_restarts > 0) { rp->r_backoff = 1 << MIN(rp->r_restarts,(BACKOFF_BITS-2)); rp->r_backoff = MIN(rp->r_backoff,MAX_BACKOFF); if (rp->r_exec != NULL && rp->r_backoff > 1) @@ -315,10 +629,11 @@ message *m_ptr; */ if (rp->r_alive_tm < rp->r_check_tm) { if (now - rp->r_alive_tm > 2*rp->r_period && - rp->r_pid > 0) { + rp->r_pid > 0 && !(rp->r_flags & RS_NOPINGREPLY)) { #if VERBOSE printf("RS: service %d reported late\n", rp->r_proc_nr_e); #endif + rp->r_flags |= RS_NOPINGREPLY; kill(rp->r_pid, SIGKILL); /* simulate crash */ } } @@ -327,7 +642,7 @@ message *m_ptr; * check and, if so request the system service's status. */ else if (now - rp->r_check_tm > rp->r_period) { -#if VERBOSE +#if VERBOSE && 0 printf("RS: status request sent to %d\n", rp->r_proc_nr_e); #endif notify(rp->r_proc_nr_e); /* request status */ @@ -358,6 +673,7 @@ int flags; pid_t child_pid; /* child's process id */ char *file_only; int s, use_copy; + struct priv *privp; message m; use_copy= (rp->r_exec != NULL); @@ -378,6 +694,10 @@ int flags; * e.g., because the root file system cannot be read, try to strip of * the path, and see if the command is in RS' current working dir. */ + nice(rp->r_nice); /* Nice before setuid, to allow negative + * nice values. + */ + setuid(rp->r_uid); if (!use_copy) { execve(rp->r_argv[0], rp->r_argv, NULL); /* POSIX execute */ @@ -399,10 +719,20 @@ int flags; environ); } + privp= NULL; + if (rp->r_set_resources) + { + init_privs(rp, &rp->r_priv); + privp= &rp->r_priv; + + /* Inform the PCI server about the driver */ + init_pci(rp, child_proc_nr_e); + } + /* Set the privilege structure for the child process to let is run. * This should succeed: we tested number in use above. */ - if ((s = sys_privctl(child_proc_nr_e, SYS_PRIV_INIT, 0, NULL)) < 0) { + if ((s = sys_privctl(child_proc_nr_e, SYS_PRIV_INIT, 0, privp)) < 0) { report("RS","sys_privctl call failed", s); /* to let child run */ rp->r_flags |= RS_EXITING; /* expect exit */ if(child_pid > 0) kill(child_pid, SIGKILL); /* kill driver */ @@ -462,7 +792,9 @@ int how; rp->r_flags |= how; /* what to on exit? */ if(rp->r_pid > 0) kill(rp->r_pid, SIGTERM); /* first try friendly */ - else report("RS", "didn't kill pid", rp->r_pid); +#if VERBOSE + else printf("no process to kill\n"); +#endif getuptime(&rp->r_stop_tm); /* record current time */ } @@ -543,3 +875,169 @@ struct rproc *rp; else return -e; } + +/*===========================================================================* + * run_script * + *===========================================================================*/ +PRIVATE void run_script(rp) +struct rproc *rp; +{ + int r, proc_nr_e; + pid_t pid; + char *reason; + char incarnation_str[20]; /* Enough for a counter? */ + + if (rp->r_flags & RS_EXITING) + reason= "exit"; + else if (rp->r_flags & RS_REFRESHING) + reason= "restart"; + else if (rp->r_flags & RS_NOPINGREPLY) + reason= "no-heartbeat"; + else if (rp->r_flags & RS_KILLED) + reason= "killed"; + else if (rp->r_flags & RS_CRASHED) + reason= "crashed"; + else + { + printf( + "run_script: can't find reason for termination of '%s'\n", + rp->r_label); + return; + } + sprintf(incarnation_str, "%d", rp->r_restarts); + + printf("should call script '%s'\n", rp->r_script); + printf("sevice name: '%s'\n", rp->r_label); + printf("reason: '%s'\n", reason); + printf("incarnation: '%s'\n", incarnation_str); + + pid= fork(); + switch(pid) + { + case -1: + printf("run_script: fork failed: %s\n", strerror(errno)); + break; + case 0: + execle(rp->r_script, rp->r_script, rp->r_label, reason, + incarnation_str, NULL, NULL); + { + extern int kputc_use_private_grants; + kputc_use_private_grants= 1; + } + printf("run_script: execl '%s' failed: %s\n", + rp->r_script, strerror(errno)); + exit(1); + default: + /* Set the privilege structure for the child process to let it + * run. + */ + proc_nr_e = getnprocnr(pid); + r= sys_privctl(proc_nr_e, SYS_PRIV_USER, 0, NULL); + if (r < 0) + printf("run_script: sys_privctl call failed: %d\n", r); + + /* Do not wait for the child */ + break; + } +} + + +/*===========================================================================* + * init_privs * + *===========================================================================*/ +PRIVATE void init_privs(rp, privp) +struct rproc *rp; +struct priv *privp; +{ + int i, src_bits_per_word, dst_bits_per_word, src_word, dst_word, + src_bit, call_nr; + unsigned long mask; + + /* Clear the privilege structure */ + memset(privp, '\0', sizeof(*privp)); + + src_bits_per_word= 8*sizeof(rp->r_call_mask[0]); + dst_bits_per_word= 8*sizeof(privp->s_k_call_mask[0]); + for (src_word= 0; src_word < MAX_NR_SYSTEM; src_word++) + { + for (src_bit= 0; src_bit < src_bits_per_word; src_bit++) + { + mask= (1UL << src_bit); + if (!(rp->r_call_mask[src_word] & mask)) + continue; + call_nr= src_word*src_bits_per_word+src_bit; + printf("init_privs: system call %d\n", call_nr); + dst_word= call_nr / dst_bits_per_word; + mask= (1UL << (call_nr % dst_bits_per_word)); + if (dst_word >= CALL_MASK_SIZE) + { + printf( + "init_privs: call number %d doesn't fit\n", + call_nr); + } + privp->s_k_call_mask[dst_word] |= mask; + } + } +} + + +/*===========================================================================* + * init_pci * + *===========================================================================*/ +PRIVATE void init_pci(rp, endpoint) +struct rproc *rp; +int endpoint; +{ + /* Tell the PCI driver about the new driver */ + size_t len; + int i, r; + struct rs_pci rs_pci; + + len= strlen(rp->r_label); + if (len+1 > sizeof(rs_pci.rsp_label)) + { + printf("init_pci: label '%s' too long for rsp_label\n", + rp->r_label); + return; + } + strcpy(rs_pci.rsp_label, rp->r_label); + rs_pci.rsp_endpoint= endpoint; + + rs_pci.rsp_nr_device= rp->r_nr_pci_id; + if (rs_pci.rsp_nr_device > RSP_NR_DEVICE) + { + printf("init_pci: too many PCI devices (max %d) truncating\n", + RSP_NR_DEVICE); + rs_pci.rsp_nr_device= RSP_NR_DEVICE; + } + for (i= 0; ir_pci_id[i].vid; + rs_pci.rsp_device[i].did= rp->r_pci_id[i].did; + } + + rs_pci.rsp_nr_class= rp->r_nr_pci_class; + if (rs_pci.rsp_nr_class > RSP_NR_CLASS) + { + printf("init_pci: too many PCI classes (max %d) truncating\n", + RSP_NR_CLASS); + rs_pci.rsp_nr_class= RSP_NR_CLASS; + } + for (i= 0; ir_pci_class[i].class; + rs_pci.rsp_class[i].mask= rp->r_pci_class[i].mask; + } + + printf("init_pci: calling pci_set_acl\n"); + + r= pci_set_acl(&rs_pci); + + printf("init_pci: after pci_set_acl\n"); + + if (r != OK) + { + printf("init_pci: pci_set_acl failed: %s\n", strerror(errno)); + return; + } +} diff --git a/servers/rs/manager.h b/servers/rs/manager.h index 953d31685..f6da67fe3 100644 --- a/servers/rs/manager.h +++ b/servers/rs/manager.h @@ -5,15 +5,23 @@ /* Space reserved for program and arguments. */ #define MAX_COMMAND_LEN 512 /* maximum argument string length */ +#define MAX_LABEL_LEN 16 /* Unique name of (this instance of) + * the driver + */ +#define MAX_SCRIPT_LEN 256 /* maximum restart script name length */ #define MAX_NR_ARGS 4 /* maximum number of arguments */ #define MAX_RESCUE_DIR_LEN 64 /* maximum rescue dir length */ +#define MAX_NR_PCI_ID 4 /* maximum number of PCI device IDs */ +#define MAX_NR_PCI_CLASS 4 /* maximum number of PCI class IDs */ +#define MAX_NR_SYSTEM 2 /* should match RSS_NR_SYSTEM */ + /* Definition of the system process table. This table only has entries for * the servers and drivers, and thus is not directly indexed by slot number. */ extern struct rproc { int r_proc_nr_e; /* process endpoint number */ - pid_t r_pid; /* process id */ + pid_t r_pid; /* process id, -1 if the process is not there */ dev_t r_dev_nr; /* major device number */ int r_dev_style; /* device style */ @@ -29,9 +37,25 @@ extern struct rproc { char *r_exec; /* Executable image */ size_t r_exec_len; /* Length of image */ + char r_label[MAX_LABEL_LEN]; /* unique name of this driver */ char r_cmd[MAX_COMMAND_LEN]; /* raw command plus arguments */ + char r_script[MAX_SCRIPT_LEN]; /* name of the restart script executable */ char *r_argv[MAX_NR_ARGS+2]; /* parsed arguments vector */ int r_argc; /* number of arguments */ + + /* Resources */ + int r_set_resources; + struct priv r_priv; /* Privilege structure to be passed to the + * kernel. + */ + uid_t r_uid; + int r_nice; + int r_nr_pci_id; /* Number of PCI devices IDs */ + struct { u16_t vid; u16_t did; } r_pci_id[MAX_NR_PCI_ID]; + int r_nr_pci_class; /* Number of PCI class IDs */ + struct { u32_t class; u32_t mask; } r_pci_class[MAX_NR_PCI_CLASS]; + + u32_t r_call_mask[MAX_NR_SYSTEM]; } rproc[NR_SYS_PROCS]; /* Mapping for fast access to the system process table. */ @@ -40,8 +64,11 @@ extern int nr_in_use; /* Flag values. */ #define RS_IN_USE 0x001 /* set when process slot is in use */ -#define RS_EXITING 0x002 /* set when exit is expected */ -#define RS_REFRESHING 0x004 /* set when refresh must be done */ +#define RS_EXITING 0x004 /* set when exit is expected */ +#define RS_REFRESHING 0x008 /* set when refresh must be done */ +#define RS_NOPINGREPLY 0x010 /* driver failed to reply to a ping request */ +#define RS_KILLED 0x020 /* driver is killed */ +#define RS_CRASHED 0x040 /* driver crashed */ /* Constants determining RS period and binary exponential backoff. */ #define RS_DELTA_T 60 /* check every T ticks */ diff --git a/servers/rs/proto.h b/servers/rs/proto.h index df862313d..1e0ccf5e4 100644 --- a/servers/rs/proto.h +++ b/servers/rs/proto.h @@ -9,9 +9,11 @@ _PROTOTYPE( int main, (void)); /* manager.c */ _PROTOTYPE( int do_up, (message *m, int do_copy, int flags)); +_PROTOTYPE( int do_start, (message *m)); _PROTOTYPE( int do_down, (message *m)); _PROTOTYPE( int do_refresh, (message *m)); _PROTOTYPE( int do_rescue, (message *m)); +_PROTOTYPE( int do_restart, (message *m)); _PROTOTYPE( int do_shutdown, (message *m)); _PROTOTYPE( void do_period, (message *m)); _PROTOTYPE( void do_exit, (message *m)); diff --git a/servers/rs/service.c b/servers/rs/service.c index 4e70ea8d9..470cf060d 100644 --- a/servers/rs/service.c +++ b/servers/rs/service.c @@ -5,19 +5,23 @@ * Jul 22, 2005: Created (Jorrit N. Herder) */ +#include #include #include #include #include +#include #include #include #include #include #include #include +#include #include #include #include +#include /* This array defines all known requests. */ @@ -25,6 +29,7 @@ PRIVATE char *known_requests[] = { "up", "down", "refresh", + "restart", "rescue", "shutdown", "upcopy", /* fill for RS_UP_COPY */ @@ -46,7 +51,7 @@ extern int errno; /* The following are relative to optind */ #define ARG_REQUEST 0 /* request to perform */ #define ARG_PATH 1 /* rescue dir or system service */ -#define ARG_PID 1 /* pid of system service */ +#define ARG_LABEL 1 /* name of system service */ #define MIN_ARG_COUNT 1 /* require an action */ @@ -54,22 +59,37 @@ extern int errno; #define ARG_DEV "-dev" /* major device number for drivers */ #define ARG_PRIV "-priv" /* required privileges */ #define ARG_PERIOD "-period" /* heartbeat period in ticks */ +#define ARG_SCRIPT "-script" /* name of the script to restart a + * driver + */ +#define ARG_CONFIG "-config" /* name of the file with the resource + * configuration + */ + +#define DRIVER_LOGIN "driver" /* Passwd file entry for drivers */ + +#define MAX_CLASS_RECURS 100 /* Max nesting level for classes */ /* The function parse_arguments() verifies and parses the command line * parameters passed to this utility. Request parameters that are needed * are stored globally in the following variables: */ PRIVATE int req_type; -PRIVATE int req_pid; +PRIVATE char *req_label; PRIVATE char *req_path; PRIVATE char *req_args; PRIVATE int req_major; PRIVATE long req_period; -PRIVATE char *req_priv; +PRIVATE char *req_script; +PRIVATE char *req_config; +PRIVATE int class_recurs; /* Nesting level of class statements */ /* Buffer to build "/command arg1 arg2 ..." string to pass to RS server. */ PRIVATE char command[4096]; +/* Arguments for RS to start a new service */ +PRIVATE struct rs_start rs_start; + /* An error occurred. Report the problem, print the usage, and exit. */ PRIVATE void print_usage(char *app_name, char *problem) @@ -78,8 +98,9 @@ PRIVATE void print_usage(char *app_name, char *problem) printf("Usage:\n"); printf(" %s [-c] (up|run) [%s ] [%s ] [%s ]\n", app_name, ARG_ARGS, ARG_DEV, ARG_PERIOD); - printf(" %s down \n", app_name); - printf(" %s refresh \n", app_name); + printf(" %s down label\n", app_name); + printf(" %s refresh label\n", app_name); + printf(" %s restart label\n", app_name); printf(" %s rescue \n", app_name); printf(" %s shutdown\n", app_name); printf("\n"); @@ -141,6 +162,10 @@ PRIVATE int parse_arguments(int argc, char **argv) if (req_nr == RS_UP || req_nr == RS_RUN) { + rs_start.rss_flags= 0; + if (c_flag) + rs_start.rss_flags |= RF_COPY; + if (req_nr == RS_UP && c_flag) req_nr= RS_UP_COPY; @@ -195,8 +220,13 @@ PRIVATE int parse_arguments(int argc, char **argv) } req_major = (stat_buf.st_rdev >> MAJOR) & BYTE; } - else if (strcmp(argv[i], ARG_ARGS)==0) { - req_priv = argv[i+1]; + else if (strcmp(argv[i], ARG_SCRIPT)==0) { + req_script = argv[i+1]; + req_nr = RS_START; + } + else if (strcmp(argv[i], ARG_CONFIG)==0) { + req_config = argv[i+1]; + req_nr = RS_START; } else { print_usage(argv[ARG_NAME], "unknown optional argument given"); @@ -204,17 +234,14 @@ PRIVATE int parse_arguments(int argc, char **argv) } } } - else if (req_nr == RS_DOWN || req_nr == RS_REFRESH) { + else if (req_nr == RS_DOWN || req_nr == RS_REFRESH || req_nr == RS_RESTART) { /* Verify argument count. */ - if (argc - 1 < optind+ARG_PID) { - print_usage(argv[ARG_NAME], "action requires a pid to stop"); - exit(EINVAL); - } - if (! (req_pid = atoi(argv[optind+ARG_PID])) > 0) { - print_usage(argv[ARG_NAME], "pid must be greater than zero"); + if (argc - 1 < optind+ARG_LABEL) { + print_usage(argv[ARG_NAME], "action requires a label to stop"); exit(EINVAL); } + req_label= argv[optind+ARG_LABEL]; } else if (req_nr == RS_RESCUE) { @@ -245,6 +272,574 @@ PRIVATE int parse_arguments(int argc, char **argv) return(req_nr); } +PRIVATE void fatal(char *fmt, ...) +{ + va_list ap; + + fprintf(stderr, "fatal error: "); + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + fprintf(stderr, "\n"); + + exit(1); +} + +#define KW_DRIVER "driver" +#define KW_UID "uid" +#define KW_NICE "nice" +#define KW_IRQ "irq" +#define KW_IO "io" +#define KW_PCI "pci" +#define KW_DEVICE "device" +#define KW_CLASS "class" +#define KW_SYSTEM "system" + +FORWARD void do_driver(config_t *cpe, config_t *config); + +PRIVATE void do_class(config_t *cpe, config_t *config) +{ + config_t *cp, *cp1; + + if (class_recurs > MAX_CLASS_RECURS) + { + fatal( + "do_class: nesting level too high for class '%s' at %s:%d", + cpe->word, cpe->file, cpe->line); + } + class_recurs++; + + /* Process classes */ + for (; cpe; cpe= cpe->next) + { + if (cpe->flags & CFG_SUBLIST) + { + fatal("do_class: unexpected sublist at %s:%d", + cpe->file, cpe->line); + } + if (cpe->flags & CFG_STRING) + { + fatal("do_uid: unexpected string at %s:%d", + cpe->file, cpe->line); + } + + /* Find entry for the class */ + for (cp= config; cp; cp= cp->next) + { + if (!(cp->flags & CFG_SUBLIST)) + { + fatal("do_class: expected list at %s:%d\n", + cp->file, cp->line); + } + cp1= cp->list; + if ((cp1->flags & CFG_STRING) || + (cp1->flags & CFG_SUBLIST)) + { + fatal("do_class: expected word at %s:%d\n", + cp1->file, cp1->line); + } + + /* At this place we expect the word 'driver' */ + if (strcmp(cp1->word, KW_DRIVER) != 0) + fatal("do_class: exected word '%S' at %s:%d\n", + KW_DRIVER, cp1->file, cp1->line); + + cp1= cp1->next; + if ((cp1->flags & CFG_STRING) || + (cp1->flags & CFG_SUBLIST)) + { + fatal("do_class: expected word at %s:%d\n", + cp1->file, cp1->line); + } + + /* At this place we expect the name of the driver */ + if (strcmp(cp1->word, cpe->word) == 0) + break; + } + if (cp == NULL) + { + fatal( + "do_class: no entry found for class '%s' at %s:%d\n", + cpe->word, cpe->file, cpe->line); + } + do_driver(cp1->next, config); + } + + class_recurs--; +} + +PRIVATE void do_uid(config_t *cpe) +{ + uid_t uid; + struct passwd *pw; + char *check; + + /* Process a uid */ + if (cpe->next != NULL) + { + fatal("do_uid: just one uid/login expected at %s:%d", + cpe->file, cpe->line); + } + + if (cpe->flags & CFG_SUBLIST) + { + fatal("do_uid: unexpected sublist at %s:%d", + cpe->file, cpe->line); + } + if (cpe->flags & CFG_STRING) + { + fatal("do_uid: unexpected string at %s:%d", + cpe->file, cpe->line); + } + pw= getpwnam(cpe->word); + if (pw != NULL) + uid= pw->pw_uid; + else + { + uid= strtol(cpe->word, &check, 0); + if (check[0] != '\0') + { + fatal("do_uid: bad uid/login '%s' at %s:%d", + cpe->word, cpe->file, cpe->line); + } + } + + rs_start.rss_uid= uid; +} + +PRIVATE void do_nice(config_t *cpe) +{ + int nice_val; + char *check; + + /* Process a nice value */ + if (cpe->next != NULL) + { + fatal("do_nice: just one nice value expected at %s:%d", + cpe->file, cpe->line); + } + + + if (cpe->flags & CFG_SUBLIST) + { + fatal("do_nice: unexpected sublist at %s:%d", + cpe->file, cpe->line); + } + if (cpe->flags & CFG_STRING) + { + fatal("do_nice: unexpected string at %s:%d", + cpe->file, cpe->line); + } + nice_val= strtol(cpe->word, &check, 0); + if (check[0] != '\0') + { + fatal("do_nice: bad nice value '%s' at %s:%d", + cpe->word, cpe->file, cpe->line); + } + /* Check range? */ + + rs_start.rss_nice= nice_val; +} + +PRIVATE void do_irq(config_t *cpe) +{ + int irq; + char *check; + + /* Process a list of IRQs */ + for (; cpe; cpe= cpe->next) + { + if (cpe->flags & CFG_SUBLIST) + { + fatal("do_irq: unexpected sublist at %s:%d", + cpe->file, cpe->line); + } + if (cpe->flags & CFG_STRING) + { + fatal("do_irq: unexpected string at %s:%d", + cpe->file, cpe->line); + } + irq= strtoul(cpe->word, &check, 0); + if (check[0] != '\0') + { + fatal("do_irq: bad irq '%s' at %s:%d", + cpe->word, cpe->file, cpe->line); + } + if (rs_start.rss_nr_irq >= RSS_NR_IRQ) + fatal("do_irq: too many IRQs (max %d)", RSS_NR_IRQ); + rs_start.rss_irq[rs_start.rss_nr_irq]= irq; + rs_start.rss_nr_irq++; + } +} + +PRIVATE void do_io(config_t *cpe) +{ + int irq; + unsigned base, len; + char *check; + + /* Process a list of I/O ranges */ + for (; cpe; cpe= cpe->next) + { + if (cpe->flags & CFG_SUBLIST) + { + fatal("do_io: unexpected sublist at %s:%d", + cpe->file, cpe->line); + } + if (cpe->flags & CFG_STRING) + { + fatal("do_io: unexpected string at %s:%d", + cpe->file, cpe->line); + } + base= strtoul(cpe->word, &check, 0x10); + len= 1; + if (check[0] == ':') + { + len= strtoul(check+1, &check, 0x10); + } + if (check[0] != '\0') + { + fatal("do_io: bad I/O range '%s' at %s:%d", + cpe->word, cpe->file, cpe->line); + } + + if (rs_start.rss_nr_io >= RSS_NR_IO) + fatal("do_io: too many I/O ranges (max %d)", RSS_NR_IO); + rs_start.rss_io[rs_start.rss_nr_io].base= base; + rs_start.rss_io[rs_start.rss_nr_io].len= len; + rs_start.rss_nr_io++; + } +} + +PRIVATE void do_pci_device(config_t *cpe) +{ + u16_t vid, did; + char *check, *check2; + + /* Process a list of PCI device IDs */ + for (; cpe; cpe= cpe->next) + { + if (cpe->flags & CFG_SUBLIST) + { + fatal("do_pci_device: unexpected sublist at %s:%d", + cpe->file, cpe->line); + } + if (cpe->flags & CFG_STRING) + { + fatal("do_pci_device: unexpected string at %s:%d", + cpe->file, cpe->line); + } + vid= strtoul(cpe->word, &check, 0x10); + if (check[0] == '/') + did= strtoul(check+1, &check2, 0x10); + if (check[0] != '/' || check2[0] != '\0') + { + fatal("do_pci_device: bad ID '%s' at %s:%d", + cpe->word, cpe->file, cpe->line); + } + if (rs_start.rss_nr_pci_id >= RSS_NR_PCI_ID) + { + fatal("do_pci_device: too many device IDs (max %d)", + RSS_NR_PCI_ID); + } + rs_start.rss_pci_id[rs_start.rss_nr_pci_id].vid= vid; + rs_start.rss_pci_id[rs_start.rss_nr_pci_id].did= did; + rs_start.rss_nr_pci_id++; + } +} + +PRIVATE void do_pci_class(config_t *cpe) +{ + u8_t baseclass, subclass, interface; + u32_t class_id, mask; + char *check; + + /* Process a list of PCI device class IDs */ + for (; cpe; cpe= cpe->next) + { + if (cpe->flags & CFG_SUBLIST) + { + fatal("do_pci_device: unexpected sublist at %s:%d", + cpe->file, cpe->line); + } + if (cpe->flags & CFG_STRING) + { + fatal("do_pci_device: unexpected string at %s:%d", + cpe->file, cpe->line); + } + + baseclass= strtoul(cpe->word, &check, 0x10); + subclass= 0; + interface= 0; + mask= 0xff0000; + if (check[0] == '/') + { + subclass= strtoul(check+1, &check, 0x10); + mask= 0xffff00; + if (check[0] == '/') + { + interface= strtoul(check+1, &check, 0x10); + mask= 0xffffff; + } + } + + if (check[0] != '\0') + { + fatal("do_pci_class: bad class ID '%s' at %s:%d", + cpe->word, cpe->file, cpe->line); + } + class_id= (baseclass << 16) | (subclass << 8) | interface; + if (rs_start.rss_nr_pci_class >= RSS_NR_PCI_CLASS) + { + fatal("do_pci_class: too many class IDs (max %d)", + RSS_NR_PCI_CLASS); + } + rs_start.rss_pci_class[rs_start.rss_nr_pci_class].class= + class_id; + rs_start.rss_pci_class[rs_start.rss_nr_pci_class].mask= mask; + rs_start.rss_nr_pci_class++; + } +} + +PRIVATE void do_pci(config_t *cpe) +{ + int i, call_nr, word, bits_per_word; + unsigned long mask; + + if (cpe == NULL) + return; /* Empty PCI statement */ + + if (cpe->flags & CFG_SUBLIST) + { + fatal("do_pci: unexpected sublist at %s:%d", + cpe->file, cpe->line); + } + if (cpe->flags & CFG_STRING) + { + fatal("do_pci: unexpected string at %s:%d", + cpe->file, cpe->line); + } + + if (strcmp(cpe->word, KW_DEVICE) == 0) + { + do_pci_device(cpe->next); + return; + } + if (strcmp(cpe->word, KW_CLASS) == 0) + { + do_pci_class(cpe->next); + return; + } + fatal("do_pci: unexpected word '%s' at %s:%d", + cpe->word, cpe->file, cpe->line); +} + +struct +{ + char *label; + int call_nr; +} system_tab[]= +{ + { "KILL", SYS_KILL }, + { "UMAP", SYS_UMAP }, + { "VIRCOPY", SYS_VIRCOPY }, + { "IRQCTL", SYS_IRQCTL }, + { "DEVIO", SYS_DEVIO }, + { "SDEVIO", SYS_SDEVIO }, + { "VDEVIO", SYS_VDEVIO }, + { "SETALARM", SYS_SETALARM }, + { "TIMES", SYS_TIMES }, + { "GETINFO", SYS_GETINFO }, + { "SAFECOPYFROM", SYS_SAFECOPYFROM }, + { "SAFECOPYTO", SYS_SAFECOPYTO }, + { "SETGRANT", SYS_SETGRANT }, + { NULL, 0 } +}; + +PRIVATE void do_system(config_t *cpe) +{ + int i, call_nr, word, bits_per_word; + unsigned long mask; + + bits_per_word= sizeof(rs_start.rss_system[0])*8; + + /* Process a list of 'system' calls that are allowed */ + for (; cpe; cpe= cpe->next) + { + if (cpe->flags & CFG_SUBLIST) + { + fatal("do_system: unexpected sublist at %s:%d", + cpe->file, cpe->line); + } + if (cpe->flags & CFG_STRING) + { + fatal("do_system: unexpected string at %s:%d", + cpe->file, cpe->line); + } + + /* Get call number */ + for (i= 0; system_tab[i].label != NULL; i++) + { + if (strcmp(cpe->word, system_tab[i].label) == 0) + break; + } + if (system_tab[i].label == NULL) + { + fatal("do_system: unknown call '%s' at %s:%d", + cpe->word, cpe->file, cpe->line); + } + call_nr= system_tab[i].call_nr; + + /* Subtract KERNEL_CALL */ + if (call_nr < KERNEL_CALL) + { + fatal( + "do_system: bad call number %d in system tab for '%s'\n", + call_nr, system_tab[i].label); + } + call_nr -= KERNEL_CALL; + + word= call_nr / bits_per_word; + mask= (1UL << (call_nr % bits_per_word)); + + if (word >= RSS_NR_SYSTEM) + { + fatal( + "do_system: RSS_NR_SYSTEM is too small (%d needed)\n", + word+1); + } + rs_start.rss_system[word] |= mask; + } +} + +PRIVATE void do_driver(config_t *cpe, config_t *config) +{ + config_t *cp; + + /* At this point we expect one sublist that contains the varios + * resource allocations + */ + if (!(cpe->flags & CFG_SUBLIST)) + { + fatal("do_driver: expected list at %s:%d\n", + cpe->file, cpe->line); + } + if (cpe->next != NULL) + { + cpe= cpe->next; + fatal("do_driver: expected end of list at %s:%d\n", + cpe->file, cpe->line); + } + cpe= cpe->list; + + /* Process the list */ + for (cp= cpe; cp; cp= cp->next) + { + if (!(cp->flags & CFG_SUBLIST)) + { + fatal("do_driver: expected list at %s:%d\n", + cp->file, cp->line); + } + cpe= cp->list; + if ((cpe->flags & CFG_STRING) || (cpe->flags & CFG_SUBLIST)) + { + fatal("do_driver: expected word at %s:%d\n", + cpe->file, cpe->line); + } + + if (strcmp(cpe->word, KW_CLASS) == 0) + { + do_class(cpe->next, config); + continue; + } + if (strcmp(cpe->word, KW_UID) == 0) + { + do_uid(cpe->next); + continue; + } + if (strcmp(cpe->word, KW_NICE) == 0) + { + do_nice(cpe->next); + continue; + } + if (strcmp(cpe->word, KW_IRQ) == 0) + { + do_irq(cpe->next); + continue; + } + if (strcmp(cpe->word, KW_IO) == 0) + { + do_io(cpe->next); + continue; + } + if (strcmp(cpe->word, KW_PCI) == 0) + { + do_pci(cpe->next); + continue; + } + if (strcmp(cpe->word, KW_SYSTEM) == 0) + { + do_system(cpe->next); + continue; + } + + printf("found word '%s'\n", cpe->word); + } +} + +PRIVATE void do_config(char *label, char *filename) +{ + config_t *config, *cp, *cpe; + + config= config_read(filename, 0, NULL); + if (config == NULL) + { + fprintf(stderr, "config_read failed for '%s': %s\n", + filename, strerror(errno)); + exit(1); + } + + /* Find an entry for our driver */ + for (cp= config; cp; cp= cp->next) + { + if (!(cp->flags & CFG_SUBLIST)) + { + fatal("do_config: expected list at %s:%d\n", + cp->file, cp->line); + } + cpe= cp->list; + if ((cpe->flags & CFG_STRING) || (cpe->flags & CFG_SUBLIST)) + { + fatal("do_config: expected word at %s:%d\n", + cpe->file, cpe->line); + } + + /* At this place we expect the word 'driver' */ + if (strcmp(cpe->word, KW_DRIVER) != 0) + fatal("do_config: exected word '%S' at %s:%d\n", + KW_DRIVER, cpe->file, cpe->line); + + cpe= cpe->next; + if ((cpe->flags & CFG_STRING) || (cpe->flags & CFG_SUBLIST)) + { + fatal("do_config: expected word at %s:%d\n", + cpe->file, cpe->line); + } + + /* At this place we expect the name of the driver */ + if (strcmp(cpe->word, label) == 0) + break; + } + if (cp == NULL) + { + printf("driver '%s' not found\n", label); + return; + } + + cpe= cpe->next; + + do_driver(cpe, config); +} /* Main program. */ @@ -253,7 +848,9 @@ PUBLIC int main(int argc, char **argv) message m; int result; int request; - int s; + int i, s; + char *label; + struct passwd *pw; /* Verify and parse the command line arguments. All arguments are checked * here. If an error occurs, the problem is reported and exit(2) is called. @@ -283,9 +880,47 @@ PUBLIC int main(int argc, char **argv) failure(-s); result = m.m_type; break; + case RS_START: + /* Build space-separated command string to be passed to RS server. */ + strcpy(command, req_path); + command[strlen(req_path)] = ' '; + strcpy(command+strlen(req_path)+1, req_args); + + rs_start.rss_cmd= command; + rs_start.rss_cmdlen= strlen(command); + rs_start.rss_major= req_major; + rs_start.rss_period= req_period; + rs_start.rss_script= req_script; + if (req_script) + rs_start.rss_scriptlen= strlen(req_script); + else + rs_start.rss_scriptlen= 0; + + pw= getpwnam(DRIVER_LOGIN); + if (pw == NULL) + fatal("no passwd file entry for '%s'", DRIVER_LOGIN); + rs_start.rss_uid= pw->pw_uid; + + /* The name of the driver */ + (label= strrchr(req_path, '/')) ? label++ : (label= req_path); + + if (req_config) + do_config(label, req_config); + + m.RS_CMD_ADDR = (char *) &rs_start; + + /* Build request message and send the request. */ + if (OK != (s=_taskcall(RS_PROC_NR, request, &m))) + failure(-s); + result = m.m_type; + break; + case RS_DOWN: case RS_REFRESH: - m.RS_PID = req_pid; + case RS_RESTART: + m.RS_CMD_ADDR = req_label; + m.RS_CMD_LEN = strlen(req_label); +printf("RS_CMD_LEN = %d\n", m.RS_CMD_LEN); if (OK != (s=_taskcall(RS_PROC_NR, request, &m))) failure(-s); break;