minix/servers/rs/manager.c
Cristiano Giuffrida d1fd04e72a Initialization protocol for system services.
SYSLIB CHANGES:
- SEF framework now supports a new SEF Init request type from RS. 3 different
callbacks are available (init_fresh, init_lu, init_restart) to specify
initialization code when a service starts fresh, starts after a live update,
or restarts.

SYSTEM SERVICE CHANGES:
- Initialization code for system services is now enclosed in a callback SEF will
automatically call at init time. The return code of the callback will
tell RS whether the initialization completed successfully.
- Each init callback can access information passed by RS to initialize. As of
now, each system service has access to the public entries of RS's system process
table to gather all the information required to initialize. This design
eliminates many existing or potential races at boot time and provides a uniform
initialization interface to system services. The same interface will be reused
for the upcoming publish/subscribe model to handle dynamic 
registration / deregistration of system services.

VM CHANGES:
- Uniform privilege management for all system services. Every service uses the
same call mask format. For boot services, VM copies the call mask from init
data. For dynamic services, VM still receives the call mask via rs_set_priv
call that will be soon replaced by the upcoming publish/subscribe model.

RS CHANGES:
- The system process table has been reorganized and split into private entries
and public entries. Only the latter ones are exposed to system services.
- VM call masks are now entirely configured in rs/table.c
- RS has now its own slot in the system process table. Only kernel tasks and
user processes not included in the boot image are now left out from the system
process table.
- RS implements the initialization protocol for system services.
- For services in the boot image, RS blocks till initialization is complete and
panics when failure is reported back. Services are initialized in their order of
appearance in the boot image priv table and RS blocks to implements synchronous
initialization for every system service having the flag SF_SYNCH_BOOT set.
- For services started dynamically, the initialization protocol is implemented
as though it were the first ping for the service. In this case, if the
system service fails to report back (or reports failure), RS brings the service
down rather than trying to restart it.
2010-01-08 01:20:42 +00:00

1883 lines
54 KiB
C

/*
* Changes:
* Nov 22, 2009: added basic live update support (Cristiano Giuffrida)
* Mar 02, 2009: Extended isolation policies (Jorrit N. Herder)
* Jul 22, 2005: Created (Jorrit N. Herder)
*/
#include "inc.h"
#include <ctype.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <sys/vm.h>
#include <minix/vm.h>
#include <lib.h>
#include <minix/sysutil.h>
/* Prototypes for internal functions that do the hard work. */
FORWARD _PROTOTYPE( int caller_is_root, (endpoint_t endpoint) );
FORWARD _PROTOTYPE( int caller_can_control, (endpoint_t endpoint,
char *label) );
FORWARD _PROTOTYPE( int copy_label, (endpoint_t src_e,
struct rss_label *src_label, char *dst_label, size_t dst_len) );
FORWARD _PROTOTYPE( int start_service, (struct rproc *rp, int flags,
endpoint_t *ep) );
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( int share_exec, (struct rproc *rp_src,
struct rproc *rp_dst) );
FORWARD _PROTOTYPE( void free_slot, (struct rproc *rp) );
FORWARD _PROTOTYPE( void run_script, (struct rproc *rp) );
FORWARD _PROTOTYPE( char *get_next_label, (char *ptr, char *label,
char *caller_label) );
FORWARD _PROTOTYPE( void add_forward_ipc, (struct rproc *rp,
struct priv *privp) );
FORWARD _PROTOTYPE( void add_backward_ipc, (struct rproc *rp,
struct priv *privp) );
FORWARD _PROTOTYPE( void init_privs, (struct rproc *rp, struct priv *privp) );
FORWARD _PROTOTYPE( void init_pci, (struct rproc *rp, int endpoint) );
FORWARD _PROTOTYPE( void update_period, (message *m_ptr) );
FORWARD _PROTOTYPE( void end_update, (clock_t now) );
PRIVATE int shutting_down = FALSE;
/*===========================================================================*
* caller_is_root *
*===========================================================================*/
PRIVATE int caller_is_root(endpoint)
endpoint_t endpoint; /* caller endpoint */
{
uid_t euid;
/* Check if caller has root user ID. */
euid = getnuid(endpoint);
if (rs_verbose && euid != 0)
{
printf("RS: got unauthorized request from endpoint %d\n", endpoint);
}
return euid == 0;
}
/*===========================================================================*
* caller_can_control *
*===========================================================================*/
PRIVATE int caller_can_control(endpoint, label)
endpoint_t endpoint;
char *label;
{
int control_allowed = 0;
register struct rproc *rp;
register struct rprocpub *rpub;
int c;
char *progname;
/* Find name of binary for given label. */
for (rp = BEG_RPROC_ADDR; rp < END_RPROC_ADDR; rp++) {
rpub = rp->r_pub;
if (strcmp(rpub->label, label) == 0) {
break;
}
}
if (rp == END_RPROC_ADDR) return 0;
progname = strrchr(rp->r_argv[0], '/');
if (progname != NULL)
progname++;
else
progname = rp->r_argv[0];
/* Check if label is listed in caller's isolation policy. */
for (rp = BEG_RPROC_ADDR; rp < END_RPROC_ADDR; rp++) {
rpub = rp->r_pub;
if (rpub->endpoint == endpoint) {
break;
}
}
if (rp == END_RPROC_ADDR) return 0;
if (rp->r_nr_control > 0) {
for (c = 0; c < rp->r_nr_control; c++) {
if (strcmp(rp->r_control[c], progname) == 0)
control_allowed = 1;
}
}
if (rs_verbose) {
printf("RS: allowing %u control over %s via policy: %s\n",
endpoint, label, control_allowed ? "yes" : "no");
}
return control_allowed;
}
/*===========================================================================*
* copy_label *
*===========================================================================*/
PRIVATE int copy_label(src_e, src_label, dst_label, dst_len)
endpoint_t src_e;
struct rss_label *src_label;
char *dst_label;
size_t dst_len;
{
int s, len;
len = MIN(dst_len-1, src_label->l_len);
s = sys_datacopy(src_e, (vir_bytes) src_label->l_addr,
SELF, (vir_bytes) dst_label, len);
if (s != OK) return s;
dst_label[len] = 0;
return OK;
}
/*===========================================================================*
* do_up *
*===========================================================================*/
PUBLIC int do_up(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 */
register struct rprocpub *rpub; /* public entry */
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;
int r;
endpoint_t ep;
struct rproc *tmp_rp;
struct rprocpub *tmp_rpub;
struct rs_start rs_start;
/* This call requires special privileges. */
if (!caller_is_root(m_ptr->m_source)) return(EPERM);
/* See if there is a free entry in the table with system processes. */
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;
}
if (slot_nr >= NR_SYS_PROCS)
{
printf("RS: do_up: system process table full\n");
return ENOMEM;
}
rpub = rp->r_pub;
/* Ok, there is space. 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);
/* 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;
/* Process name for the service. */
cmd_ptr = strrchr(rp->r_argv[0], '/');
if (cmd_ptr)
cmd_ptr++;
else
cmd_ptr= rp->r_argv[0];
len= strlen(cmd_ptr);
if (len > RS_MAX_LABEL_LEN-1)
len= RS_MAX_LABEL_LEN-1; /* truncate name */
memcpy(rpub->proc_name, cmd_ptr, len);
rpub->proc_name[len]= '\0';
if(rs_verbose)
printf("RS: do_up: using proc_name (from binary %s) '%s'\n",
rp->r_argv[0], rpub->proc_name);
if(rs_start.rss_label.l_len > 0) {
/* RS_UP caller has supplied a custom label for this service. */
int s = copy_label(m_ptr->m_source, &rs_start.rss_label,
rpub->label, sizeof(rpub->label));
if(s != OK)
return s;
if(rs_verbose)
printf("RS: do_up: using label (custom) '%s'\n", rpub->label);
} else {
/* Default label for the service. */
label = rpub->proc_name;
len= strlen(label);
memcpy(rpub->label, label, len);
rpub->label[len]= '\0';
if(rs_verbose)
printf("RS: do_up: using label (from proc_name) '%s'\n",
rpub->label);
}
if(rs_start.rss_nr_control > 0) {
int i, s;
if (rs_start.rss_nr_control > RS_NR_CONTROL)
{
printf("RS: do_up: too many control labels\n");
return EINVAL;
}
for (i=0; i<rs_start.rss_nr_control; i++) {
s = copy_label(m_ptr->m_source, &rs_start.rss_control[i],
rp->r_control[i], sizeof(rp->r_control[i]));
if(s != OK)
return s;
}
rp->r_nr_control = rs_start.rss_nr_control;
if (rs_verbose) {
printf("RS: do_up: control labels:");
for (i=0; i<rp->r_nr_control; i++)
printf(" %s", rp->r_control[i]);
printf("\n");
}
}
/* 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 */
tmp_rpub = tmp_rp->r_pub;
if (strcmp(tmp_rpub->label, rpub->label) == 0)
{
printf("RS: found duplicate label '%s': slot %d\n",
rpub->label, 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;
if (rs_start.rss_flags & RSS_IPC_VALID)
{
if (rs_start.rss_ipclen+1 > sizeof(rp->r_ipc_list))
{
printf("rs: ipc list too long for '%s'\n", rpub->label);
return EINVAL;
}
s=sys_datacopy(m_ptr->m_source, (vir_bytes) rs_start.rss_ipc,
SELF, (vir_bytes) rp->r_ipc_list, rs_start.rss_ipclen);
if (s != OK) return(s);
rp->r_ipc_list[rs_start.rss_ipclen]= '\0';
}
else
rp->r_ipc_list[0]= '\0';
rpub->sys_flags = DSRV_SF;
rp->r_exec= NULL;
if (rs_start.rss_flags & RSS_COPY) {
int exst_cpy;
struct rproc *rp2;
struct rprocpub *rpub2;
exst_cpy = 0;
if(rs_start.rss_flags & RSS_REUSE) {
int i;
for(i = 0; i < NR_SYS_PROCS; i++) {
rp2 = &rproc[i];
rpub2 = rproc[i].r_pub;
if(strcmp(rpub->proc_name, rpub2->proc_name) == 0 &&
(rpub2->sys_flags & SF_USE_COPY)) {
/* We have found the same binary that's
* already been copied */
exst_cpy = 1;
break;
}
}
}
if(!exst_cpy)
s = read_exec(rp);
else
s = share_exec(rp, rp2);
if (s != OK)
return s;
rpub->sys_flags |= SF_USE_COPY;
}
/* All dynamically created services get the same privilege flags, and
* allowed traps. Other privilege settings can be specified at runtime.
* The privilege id is dynamically allocated by the kernel.
*/
rp->r_priv.s_flags = DSRV_F; /* privilege flags */
rp->r_priv.s_trap_mask = DSRV_T; /* allowed traps */
/* Copy granted resources */
if (rs_start.rss_nr_irq > NR_IRQ)
{
printf("RS: do_up: too many IRQs requested\n");
return EINVAL;
}
rp->r_priv.s_nr_irq= rs_start.rss_nr_irq;
for (i= 0; i<rp->r_priv.s_nr_irq; i++)
{
rp->r_priv.s_irq_tab[i]= rs_start.rss_irq[i];
if(rs_verbose)
printf("RS: do_up: IRQ %d\n", rp->r_priv.s_irq_tab[i]);
}
if (rs_start.rss_nr_io > NR_IO_RANGE)
{
printf("RS: do_up: too many I/O ranges requested\n");
return EINVAL;
}
rp->r_priv.s_nr_io_range= rs_start.rss_nr_io;
for (i= 0; i<rp->r_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;
if(rs_verbose)
printf("RS: do_up: 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 > RS_NR_PCI_DEVICE)
{
printf("RS: do_up: too many PCI device IDs\n");
return EINVAL;
}
rpub->pci_acl.rsp_nr_device = rs_start.rss_nr_pci_id;
for (i= 0; i<rpub->pci_acl.rsp_nr_device; i++)
{
rpub->pci_acl.rsp_device[i].vid= rs_start.rss_pci_id[i].vid;
rpub->pci_acl.rsp_device[i].did= rs_start.rss_pci_id[i].did;
if(rs_verbose)
printf("RS: do_up: PCI %04x/%04x\n",
rpub->pci_acl.rsp_device[i].vid,
rpub->pci_acl.rsp_device[i].did);
}
if (rs_start.rss_nr_pci_class > RS_NR_PCI_CLASS)
{
printf("RS: do_up: too many PCI class IDs\n");
return EINVAL;
}
rpub->pci_acl.rsp_nr_class= rs_start.rss_nr_pci_class;
for (i= 0; i<rpub->pci_acl.rsp_nr_class; i++)
{
rpub->pci_acl.rsp_class[i].class= rs_start.rss_pci_class[i].class;
rpub->pci_acl.rsp_class[i].mask= rs_start.rss_pci_class[i].mask;
if(rs_verbose)
printf("RS: do_up: PCI class %06x mask %06x\n",
rpub->pci_acl.rsp_class[i].class,
rpub->pci_acl.rsp_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; i<RS_SYS_CALL_MASK_SIZE; i++)
rp->r_call_mask[i]= rs_start.rss_system[i];
}
else
{
printf(
"RS: do_up: internal inconsistency: bad size of r_call_mask\n");
memset(rp->r_call_mask, '\0', sizeof(rp->r_call_mask));
}
/* Initialize some fields. */
rpub->period = rs_start.rss_period;
rpub->dev_nr = rs_start.rss_major;
rpub->dev_style = STYLE_DEV;
rp->r_restarts = -1; /* will be incremented */
rp->r_set_resources= 1; /* set resources */
if (sizeof(rpub->vm_call_mask) == sizeof(rs_start.rss_vm) &&
sizeof(rpub->vm_call_mask[0]) == sizeof(rs_start.rss_vm[0]))
{
int basic_vmc[] = { VM_BASIC_CALLS, SYS_NULL_C };
memcpy(rpub->vm_call_mask, rs_start.rss_vm,
sizeof(rpub->vm_call_mask));
fill_call_mask(basic_vmc, NR_VM_CALLS,
rpub->vm_call_mask, VM_RQ_BASE, FALSE);
}
else
{
printf("RS: internal inconsistency: bad size of vm_call_mask\n");
memset(rpub->vm_call_mask, '\0', sizeof(rpub->vm_call_mask));
}
/* All information was gathered. Now try to start the system service. */
r = start_service(rp, 0, &ep);
m_ptr->RS_ENDPOINT = ep;
return r;
}
/*===========================================================================*
* do_down *
*===========================================================================*/
PUBLIC int do_down(message *m_ptr)
{
register struct rproc *rp;
register struct rprocpub *rpub;
size_t len;
int s, proc;
char label[RS_MAX_LABEL_LEN];
/* This call requires special privileges. */
if (!caller_is_root(m_ptr->m_source)) return(EPERM);
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; rp<END_RPROC_ADDR; rp++) {
rpub = rp->r_pub;
if (rp->r_flags & RS_IN_USE && strcmp(rpub->label, label) == 0) {
/* Core system services should never go down. */
if (rpub->sys_flags & SF_CORE_SRV) return(EPERM);
if(rs_verbose)
printf("RS: stopping '%s' (%d)\n", label, rp->r_pid);
stop_service(rp,RS_EXITING);
if (rp->r_pid == -1)
{
/* Process is already gone, release slot. */
free_slot(rp);
return(OK);
}
/* Late reply - send a reply when process dies. */
rp->r_flags |= RS_LATEREPLY;
rp->r_caller = m_ptr->m_source;
return EDONTREPLY;
}
}
if(rs_verbose) printf("RS: do_down: '%s' not found\n", label);
return(ESRCH);
}
/*===========================================================================*
* do_restart *
*===========================================================================*/
PUBLIC int do_restart(message *m_ptr)
{
register struct rproc *rp;
register struct rprocpub *rpub;
size_t len;
int s, proc, r;
char label[RS_MAX_LABEL_LEN];
endpoint_t ep;
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';
/* This call requires special privileges. */
if (! (caller_can_control(m_ptr->m_source, label) ||
caller_is_root(m_ptr->m_source))) {
return(EPERM);
}
for (rp=BEG_RPROC_ADDR; rp<END_RPROC_ADDR; rp++) {
rpub = rp->r_pub;
if ((rp->r_flags & RS_IN_USE) && strcmp(rpub->label, label) == 0) {
if(rs_verbose) printf("RS: restarting '%s' (%d)\n", label, rp->r_pid);
if (rp->r_pid >= 0)
{
if(rs_verbose)
printf("RS: do_restart: '%s' is (still) running, pid = %d\n",
rp->r_pid);
return EBUSY;
}
rp->r_flags &= ~(RS_REFRESHING|RS_NOPINGREPLY);
r = start_service(rp, 0, &ep);
if (r != OK) printf("do_restart: start_service failed: %d\n", r);
m_ptr->RS_ENDPOINT = ep;
return(r);
}
}
if(rs_verbose) {
printf("RS: do_restart: '%s' not found\n", label);
}
return(ESRCH);
}
/*===========================================================================*
* do_refresh *
*===========================================================================*/
PUBLIC int do_refresh(message *m_ptr)
{
register struct rproc *rp;
register struct rprocpub *rpub;
size_t len;
int s;
char label[RS_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';
/* This call requires special privileges. */
if (! (caller_can_control(m_ptr->m_source, label) ||
caller_is_root(m_ptr->m_source))) {
return(EPERM);
}
for (rp=BEG_RPROC_ADDR; rp<END_RPROC_ADDR; rp++) {
rpub = rp->r_pub;
if (rp->r_flags & RS_IN_USE && strcmp(rpub->label, label) == 0) {
/* Only system processes not including RS can refresh. */
if(!(rp->r_priv.s_flags & SYS_PROC) || rpub->endpoint == RS_PROC_NR) {
return EPERM;
}
if(rs_verbose) {
printf("RS: refreshing %s (%d)\n", rpub->label, rp->r_pid);
}
stop_service(rp,RS_REFRESHING);
return(OK);
}
}
if(rs_verbose) {
printf("RS: do_refresh: '%s' not found\n", label);
}
return(ESRCH);
}
/*===========================================================================*
* do_shutdown *
*===========================================================================*/
PUBLIC int do_shutdown(message *m_ptr)
{
/* This call requires special privileges. */
if (m_ptr != NULL && !caller_is_root(m_ptr->m_source)) return(EPERM);
/* Set flag so that RS server knows services shouldn't be restarted. */
shutting_down = TRUE;
return(OK);
}
/*===========================================================================*
* do_init_ready *
*===========================================================================*/
PUBLIC int do_init_ready(message *m_ptr)
{
int who_p;
struct rproc *rp;
struct rprocpub *rpub;
int result;
who_p = _ENDPOINT_P(m_ptr->m_source);
rp = rproc_ptr[who_p];
rpub = rp->r_pub;
result = m_ptr->RS_INIT_RESULT;
/* Make sure the originating service was requested to initialize. */
if(! (rp->r_flags & RS_INITIALIZING) ) {
if(rs_verbose) {
printf("RS: do_init_ready: got unexpected init ready msg from %d\n",
m_ptr->m_source);
}
return(EINVAL);
}
/* Mark the slot as no longer initializing. */
rp->r_flags &= ~RS_INITIALIZING;
rp->r_check_tm = 0;
getuptime(&rp->r_alive_tm);
/* Check if something went wrong and the service failed to init.
* In that case, kill it and make sure it won't be restarted.
*/
if(result != OK) {
if(rs_verbose)
printf("RS: initialization failed for service %d: %d\n",
rpub->endpoint, result);
rp->r_flags |= RS_EXITING;
kill(rp->r_pid, SIGKILL);
}
else {
if(rs_verbose)
printf("RS: initialization succeeded for service %d\n",
rpub->endpoint);
}
return(EDONTREPLY);
}
/*===========================================================================*
* do_update *
*===========================================================================*/
PUBLIC int do_update(message *m_ptr)
{
register struct rproc *rp;
register struct rprocpub *rpub;
size_t len;
int s;
char label[RS_MAX_LABEL_LEN];
int lu_state;
int prepare_maxtime;
/* Retrieve label. */
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';
/* This call requires special privileges. */
if (! (caller_can_control(m_ptr->m_source, label) ||
caller_is_root(m_ptr->m_source))) {
return(EPERM);
}
/* Retrieve live update state. */
lu_state = m_ptr->RS_LU_STATE;
if(lu_state == SEF_LU_STATE_NULL) {
return(EINVAL);
}
/* Retrieve prepare max time. */
prepare_maxtime = m_ptr->RS_LU_PREPARE_MAXTIME;
if(prepare_maxtime) {
if(prepare_maxtime < 0 || prepare_maxtime > RS_MAX_PREPARE_MAXTIME) {
return(EINVAL);
}
}
else {
prepare_maxtime = RS_DEFAULT_PREPARE_MAXTIME;
}
/* Make sure we are not already updating. */
if(rupdate.flags & RS_UPDATING) {
if(rs_verbose) {
printf("RS: do_update: an update is already in progress");
}
return(EBUSY);
}
/* Try to start the update process. */
for (rp=BEG_RPROC_ADDR; rp<END_RPROC_ADDR; rp++) {
rpub = rp->r_pub;
if (rp->r_flags & RS_IN_USE && strcmp(rpub->label, label) == 0) {
/* Only system processes not including RS can update. */
if(!(rp->r_priv.s_flags & SYS_PROC) || rpub->endpoint == RS_PROC_NR) {
return EPERM;
}
if(rs_verbose) {
printf("RS: updating %s (%d)\n", rpub->label, rp->r_pid);
}
rp->r_flags |= RS_UPDATING;
rupdate.flags |= RS_UPDATING;
getuptime(&rupdate.prepare_tm);
rupdate.prepare_maxtime = prepare_maxtime;
rupdate.rp = rp;
m_ptr->m_type = RS_LU_PREPARE;
asynsend(rpub->endpoint, m_ptr); /* request to update */
return(OK);
}
}
if(rs_verbose) {
printf("RS: do_update: '%s' not found\n", label);
}
return(ESRCH);
}
/*===========================================================================*
* do_upd_ready *
*===========================================================================*/
PUBLIC int do_upd_ready(message *m_ptr)
{
register struct rproc *rp;
int who_p;
clock_t now = m_ptr->NOTIFY_TIMESTAMP;
int result;
who_p = _ENDPOINT_P(m_ptr->m_source);
rp = rproc_ptr[who_p];
result = m_ptr->RS_LU_RESULT;
/* Make sure the originating service was requested to prepare for update. */
if(! (rp->r_flags & RS_UPDATING) ) {
if(rs_verbose) {
printf("RS: do_upd_ready: got unexpected update ready msg from %d\n",
m_ptr->m_source);
}
return(EINVAL);
}
/* Check if something went wrong and the service failed to prepare
* for the update. In that case, end the update process.
*/
if(result != OK) {
end_update(now);
switch(result) {
case EACCES:
printf("RS: update failed: %s\n",
"service does not support live update");
break;
case EINVAL:
printf("RS: update failed: %s\n",
"service does not support the required state");
break;
case EBUSY:
printf("RS: update failed: %s\n",
"service is not able to prepare for the update now");
break;
case EGENERIC:
printf("RS: update failed: %s\n",
"a generic error occurred while preparing for the update");
break;
default:
printf("RS: update failed: %s (%d)\n",
"an unknown error occurred while preparing for the update\n",
result);
break;
}
return(OK);
}
/* Kill the process now and mark it for refresh, the new version will
* be automatically restarted.
*/
rp->r_flags &= ~RS_EXITING;
rp->r_flags |= RS_REFRESHING;
kill(rp->r_pid, SIGKILL);
return(EDONTREPLY);
}
/*===========================================================================*
* update_period *
*===========================================================================*/
PRIVATE void update_period(message *m_ptr)
{
clock_t now = m_ptr->NOTIFY_TIMESTAMP;
short has_update_timed_out;
message m;
struct rprocpub *rpub;
rpub = rupdate.rp->r_pub;
/* See if a timeout has occurred. */
has_update_timed_out = (now - rupdate.prepare_tm > rupdate.prepare_maxtime);
/* If an update timed out, end the update process and notify the service. */
if(has_update_timed_out) {
end_update(now);
printf("RS: update failed: maximum prepare time reached\n");
/* Prepare cancel request. */
m.m_type = RS_LU_PREPARE;
m.RS_LU_STATE = SEF_LU_STATE_NULL;
asynsend(rpub->endpoint, &m);
}
}
/*===========================================================================*
* end_update *
*===========================================================================*/
PRIVATE void end_update(clock_t now)
{
/* End the update process and mark the affected service as no longer under
* update. Eventual late ready to update message (if any) will simply be
* ignored and the service can continue executing.
* We reset the check timestamp, so that if the service has a period a status
* request will be forced in the next period.
*/
rupdate.flags &= ~RS_UPDATING;
rupdate.rp->r_flags &= ~RS_UPDATING;
rupdate.rp->r_check_tm = 0;
}
/*===========================================================================*
* do_exit *
*===========================================================================*/
PUBLIC void do_exit(message *m_ptr)
{
register struct rproc *rp;
register struct rprocpub *rpub;
pid_t exit_pid;
int exit_status, r, slot_nr;
endpoint_t ep;
clock_t now = m_ptr->NOTIFY_TIMESTAMP;
if(rs_verbose)
printf("RS: got SIGCHLD signal, doing wait to get exited child.\n");
/* See which child exited and what the exit status is. This is done in a
* loop because multiple children may have exited, all reported by one
* SIGCHLD signal. The WNOHANG options is used to prevent blocking if,
* somehow, no exited child can be found.
*/
while ( (exit_pid = waitpid(-1, &exit_status, WNOHANG)) != 0 ) {
if(rs_verbose) {
#if 0
printf("RS: pid %d, ", exit_pid);
#endif
if (WIFSIGNALED(exit_status)) {
#if 0
printf("killed, signal number %d\n", WTERMSIG(exit_status));
#endif
}
else if (WIFEXITED(exit_status)) {
#if 0
printf("normal exit, status %d\n", WEXITSTATUS(exit_status));
#endif
}
}
/* Read from the exec pipe */
for (;;)
{
r= read(exec_pipe[0], &slot_nr, sizeof(slot_nr));
if (r == -1)
{
break; /* No data */
}
if (r != sizeof(slot_nr))
{
panic("RS", "do_exit: unaligned read from exec pipe",
r);
}
printf("do_exit: got slot %d\n", slot_nr);
if (slot_nr < 0 || slot_nr >= NR_SYS_PROCS)
{
panic("RS", "do_exit: bad slot number from exec pipe",
slot_nr);
}
rp= &rproc[slot_nr];
rp->r_flags |= RS_EXITING;
}
/* Search the system process table to see who exited.
* This should always succeed.
*/
for (rp=BEG_RPROC_ADDR; rp<END_RPROC_ADDR; rp++) {
rpub = rp->r_pub;
if ((rp->r_flags & RS_IN_USE) && rp->r_pid == exit_pid) {
int proc;
proc = _ENDPOINT_P(rpub->endpoint);
rproc_ptr[proc] = NULL; /* invalidate */
rp->r_pid= -1;
/* If PCI properties are set, inform the PCI driver. */
if(rpub->pci_acl.rsp_nr_device || rpub->pci_acl.rsp_nr_class) {
pci_del_acl(rpub->endpoint);
}
if ((rp->r_flags & RS_EXITING) || shutting_down) {
/* No reply sent to RS_DOWN yet. */
if(rp->r_flags & RS_LATEREPLY) {
message rsm;
rsm.m_type = OK;
send(rp->r_caller, &rsm);
}
/* Release slot. */
free_slot(rp);
}
else if(rp->r_flags & RS_REFRESHING) {
short is_updating = rp->r_flags & RS_UPDATING;
/* Refresh */
if (rp->r_script[0] != '\0')
run_script(rp);
else {
start_service(rp, 0, &ep); /* direct restart */
if(m_ptr)
m_ptr->RS_ENDPOINT = ep;
}
/* If updating, end the update process. */
if(is_updating) {
end_update(now);
printf("RS: update succeeded\n");
}
}
else {
/* Determine what to do. If this is the first unexpected
* exit, immediately restart this service. Otherwise use
* a binary exponential backoff.
*/
#if 0
rp->r_restarts= 0;
#endif
if (WIFSIGNALED(exit_status)) {
switch(WTERMSIG(exit_status))
{
case SIGKILL: rp->r_flags |= RS_KILLED; break;
default: rp->r_flags |= RS_SIGNALED; break;
}
}
else
rp->r_flags |= RS_CRASHED;
if (rp->r_script[0] != '\0') {
if(rs_verbose)
printf("RS: running restart script for %s\n",
rp->r_cmd);
run_script(rp);
} else if (rp->r_restarts > 0) {
printf("RS: restarting %s, restarts %d\n",
rp->r_cmd, rp->r_backoff);
rp->r_backoff = 1 << MIN(rp->r_restarts,(BACKOFF_BITS-2));
rp->r_backoff = MIN(rp->r_backoff,MAX_BACKOFF);
if ((rpub->sys_flags & SF_USE_COPY) && rp->r_backoff > 1)
rp->r_backoff= 1;
}
else {
printf("RS: restarting %s\n", rp->r_cmd);
start_service(rp, 0, &ep); /* direct restart */
if(m_ptr)
m_ptr->RS_ENDPOINT = ep;
/* Do this even if no I/O happens with the ioctl, in
* order to disambiguate requests with DEV_IOCTL_S.
*/
}
}
break;
}
}
}
}
/*===========================================================================*
* do_period *
*===========================================================================*/
PUBLIC void do_period(m_ptr)
message *m_ptr;
{
register struct rproc *rp;
register struct rprocpub *rpub;
clock_t now = m_ptr->NOTIFY_TIMESTAMP;
int s;
endpoint_t ep;
long period;
/* If an update is in progress, check its status. */
if(rupdate.flags & RS_UPDATING) {
update_period(m_ptr);
}
/* Search system services table. Only check slots that are in use and not
* updating.
*/
for (rp=BEG_RPROC_ADDR; rp<END_RPROC_ADDR; rp++) {
rpub = rp->r_pub;
if ((rp->r_flags & RS_IN_USE) && !(rp->r_flags & RS_UPDATING)) {
/* Compute period. */
period = rpub->period;
if(rp->r_flags & RS_INITIALIZING) {
period = RS_INIT_T;
}
/* If the service is to be revived (because it repeatedly exited,
* and was not directly restarted), the binary backoff field is
* greater than zero.
*/
if (rp->r_backoff > 0) {
rp->r_backoff -= 1;
if (rp->r_backoff == 0) {
start_service(rp, 0, &ep);
m_ptr->RS_ENDPOINT = ep;
}
}
/* If the service was signaled with a SIGTERM and fails to respond,
* kill the system service with a SIGKILL signal.
*/
else if (rp->r_stop_tm > 0 && now - rp->r_stop_tm > 2*RS_DELTA_T
&& rp->r_pid > 0) {
kill(rp->r_pid, SIGKILL); /* terminate */
}
/* There seems to be no special conditions. If the service has a
* period assigned check its status.
*/
else if (period > 0) {
/* Check if an answer to a status request is still pending. If
* the service didn't respond within time, kill it to simulate
* a crash. The failure will be detected and the service will
* be restarted automatically.
*/
if (rp->r_alive_tm < rp->r_check_tm) {
if (now - rp->r_alive_tm > 2*period &&
rp->r_pid > 0 && !(rp->r_flags & RS_NOPINGREPLY)) {
if(rs_verbose)
printf("RS: service %d reported late\n",
rpub->endpoint);
rp->r_flags |= RS_NOPINGREPLY;
if(rp->r_flags & RS_INITIALIZING) {
rp->r_flags |= RS_EXITING; /* don't restart */
}
kill(rp->r_pid, SIGKILL); /* simulate crash */
}
}
/* No answer pending. Check if a period expired since the last
* check and, if so request the system service's status.
*/
else if (now - rp->r_check_tm > rpub->period) {
#if 0
if(rs_verbose)
printf("RS: status request sent to %d\n", rpub->endpoint);
#endif
notify(rpub->endpoint); /* request status */
rp->r_check_tm = now; /* mark time */
}
}
}
}
/* Reschedule a synchronous alarm for the next period. */
if (OK != (s=sys_setalarm(RS_DELTA_T, 0)))
panic("RS", "couldn't set alarm", s);
}
/*===========================================================================*
* start_service *
*===========================================================================*/
PRIVATE int start_service(rp, flags, endpoint)
struct rproc *rp;
int flags;
endpoint_t *endpoint;
{
/* Try to execute the given system service. Fork a new process. The child
* process will be inhibited from running by the NO_PRIV flag. Only let the
* child run once its privileges have been set by the parent.
*/
int child_proc_nr_e, child_proc_nr_n; /* child process slot */
pid_t child_pid; /* child's process id */
char *file_only;
int s, use_copy, slot_nr, init_type;
bitchunk_t *vm_mask;
message m;
char * null_env = NULL;
struct rprocpub *rpub;
rpub = rp->r_pub;
use_copy= (rpub->sys_flags & SF_USE_COPY);
/* See if we are not using a copy but we do need one to start the service. */
if(!use_copy && (rpub->sys_flags & SF_NEED_COPY)) {
printf("RS: unable to start service %s without an in-memory copy\n",
rpub->label);
free_slot(rp);
return(EPERM);
}
/* Now fork and branch for parent and child process (and check for error). */
if (use_copy) {
if(rs_verbose) printf("RS: fork_nb..\n");
child_pid= fork_nb();
} else {
if(rs_verbose) printf("RS: fork regular..\n");
child_pid = fork();
}
switch(child_pid) { /* see fork(2) */
case -1: /* fork failed */
report("RS", "warning, fork() failed", errno); /* shouldn't happen */
return(errno); /* return error */
case 0: /* child process */
/* Try to execute the binary that has an absolute path. If this fails,
* e.g., because the root file system cannot be read, try to strip off
* 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);
cpf_reload(); /* Tell kernel about grant table */
if (!use_copy)
{
execve(rp->r_argv[0], rp->r_argv, &null_env); /* POSIX execute */
file_only = strrchr(rp->r_argv[0], '/') + 1;
execve(file_only, rp->r_argv, &null_env); /* POSIX execute */
}
printf("RS: exec failed for %s: %d\n", rp->r_argv[0], errno);
slot_nr= rp-rproc;
s= write(exec_pipe[1], &slot_nr, sizeof(slot_nr));
if (s != sizeof(slot_nr))
printf("RS: write to exec pipe failed: %d/%d\n", s, errno);
exit(1); /* terminate child */
default: /* parent process */
#if 0
if(rs_verbose) printf("RS: parent forked, pid %d..\n", child_pid);
#endif
child_proc_nr_e = getnprocnr(child_pid); /* get child slot */
#if 0
if(rs_verbose) printf("RS: forked into %d..\n", child_proc_nr_e);
#endif
break; /* continue below */
}
/* Regardless of any following failures, there is now a child process.
* Update the system process table that is maintained by the RS server.
*/
child_proc_nr_n = _ENDPOINT_P(child_proc_nr_e);
rp->r_flags = RS_IN_USE | flags; /* mark slot in use */
rp->r_restarts += 1; /* raise nr of restarts */
rpub->endpoint = child_proc_nr_e; /* set child details */
rp->r_pid = child_pid;
rp->r_check_tm = 0; /* not checked yet */
getuptime(&rp->r_alive_tm); /* currently alive */
rp->r_stop_tm = 0; /* not exiting yet */
rp->r_backoff = 0; /* not to be restarted */
rproc_ptr[child_proc_nr_n] = rp; /* mapping for fast access */
rpub->in_use = TRUE; /* public entry is now in use */
/* If any of the calls below fail, the RS_EXITING flag is set. This implies
* that the process will be removed from RS's process table once it has
* terminated. The assumption is that it is not useful to try to restart the
* process later in these failure cases.
*/
if (use_copy)
{
extern char **environ;
/* Copy the executable image into the child process. If this call
* fails, the child process may or may not be killed already. If it is
* not killed, it's blocked because of NO_PRIV. Kill it now either way.
*/
s = dev_execve(child_proc_nr_e, rp->r_exec, rp->r_exec_len, rp->r_argv,
environ);
if (s != OK) {
report("RS", "dev_execve call failed", s);
kill(child_pid, SIGKILL);
rp->r_flags |= RS_EXITING; /* don't try again */
return(s);
}
}
/* Set resources when asked to. */
if (rp->r_set_resources)
{
/* Initialize privilege structure. */
init_privs(rp, &rp->r_priv);
/* Tell VM about allowed calls. */
vm_mask = &rpub->vm_call_mask[0];
if ((s = vm_set_priv(child_proc_nr_e, vm_mask)) < 0) {
report("RS", "vm_set_priv call failed", s);
kill(child_pid, SIGKILL);
rp->r_flags |= RS_EXITING;
return (s);
}
}
/* Set and synch the privilege structure for the new service. */
if ((s = sys_privctl(child_proc_nr_e, SYS_PRIV_SET_SYS, &rp->r_priv)) != OK
|| (s = sys_getpriv(&rp->r_priv, child_proc_nr_e)) != OK) {
report("RS","unable to set privileges", s);
kill(child_pid, SIGKILL); /* kill the service */
rp->r_flags |= RS_EXITING; /* expect exit */
return(s); /* return error */
}
/* If PCI properties are set, inform the PCI driver about the new service. */
if(rpub->pci_acl.rsp_nr_device || rpub->pci_acl.rsp_nr_class) {
init_pci(rp, child_proc_nr_e);
}
/* Publish the new system service. */
s = publish_service(rp);
if (s != OK) {
printf("RS: warning: publish_service failed: %d\n", s);
}
/* Allow the service to run.
* XXX: We should let the service run/init only after publishing information
* about the new system service, but this is not currently possible due to
* the blocking nature of mapdriver() that expects the service to be running.
* The current solution is not race-free. This hack can go once service
* publishing is made fully asynchronous in RS.
*/
if ((s = sys_privctl(child_proc_nr_e, SYS_PRIV_ALLOW, NULL)) != OK) {
report("RS","unable to allow the service to run", s);
kill(child_pid, SIGKILL); /* kill the service */
rp->r_flags |= RS_EXITING; /* expect exit */
return(s); /* return error */
}
/* Initialize service. */
init_type = rp->r_restarts > 0 ? SEF_INIT_RESTART : SEF_INIT_FRESH;
if((s = init_service(rp, init_type)) != OK) {
panic("RS", "unable to initialize service", s);
}
/* The purpose of non-blocking forks is to avoid involving VFS in the forking
* process, because VFS may be blocked on a sendrec() to a MFS that is
* waiting for a endpoint update for a dead driver. We have just published
* that update, but VFS may still be blocked. As a result, VFS may not yet
* have received PM's fork message. Hence, if we call mapdriver()
* immediately, VFS may not know about the process and thus refuse to add the
* driver entry. The following temporary hack works around this by forcing
* blocking communication from PM to VFS. Once VFS has been made non-blocking
* towards MFS instances, this hack and the entire fork_nb() call can go.
*/
if (use_copy)
setuid(0);
/* Map the new service. */
if (rpub->dev_nr > 0) { /* set driver map */
if ((s=mapdriver(rpub->label,
rpub->dev_nr, rpub->dev_style, !!use_copy /* force */)) < 0) {
report("RS", "couldn't map driver (continuing)", errno);
}
}
if(rs_verbose)
printf("RS: started '%s', major %d, pid %d, endpoint %d, proc %d\n",
rp->r_cmd, rpub->dev_nr, child_pid,
child_proc_nr_e, child_proc_nr_n);
/* The system service now has been successfully started. The only thing
* that can go wrong now, is that execution fails at the child. If that's
* the case, the child will exit.
*/
if(endpoint) *endpoint = child_proc_nr_e; /* send back child endpoint */
return(OK);
}
/*===========================================================================*
* stop_service *
*===========================================================================*/
PRIVATE int stop_service(rp,how)
struct rproc *rp;
int how;
{
/* Try to stop the system service. First send a SIGTERM signal to ask the
* system service to terminate. If the service didn't install a signal
* handler, it will be killed. If it did and ignores the signal, we'll
* find out because we record the time here and send a SIGKILL.
*/
if(rs_verbose) printf("RS tries to stop %s (pid %d)\n", rp->r_cmd, rp->r_pid);
rp->r_flags |= how; /* what to on exit? */
if(rp->r_pid > 0) kill(rp->r_pid, SIGTERM); /* first try friendly */
else if(rs_verbose) printf("RS: no process to kill\n");
getuptime(&rp->r_stop_tm); /* record current time */
}
/*===========================================================================*
* do_getsysinfo *
*===========================================================================*/
PUBLIC int do_getsysinfo(m_ptr)
message *m_ptr;
{
vir_bytes src_addr, dst_addr;
int dst_proc;
size_t len;
int s;
/* This call requires special privileges. */
if (!caller_is_root(m_ptr->m_source)) return(EPERM);
switch(m_ptr->m1_i1) {
case SI_PROC_TAB:
src_addr = (vir_bytes) rproc;
len = sizeof(struct rproc) * NR_SYS_PROCS;
break;
case SI_PROCPUB_TAB:
src_addr = (vir_bytes) rprocpub;
len = sizeof(struct rprocpub) * NR_SYS_PROCS;
break;
default:
return(EINVAL);
}
dst_proc = m_ptr->m_source;
dst_addr = (vir_bytes) m_ptr->m1_p1;
if (OK != (s=sys_datacopy(SELF, src_addr, dst_proc, dst_addr, len)))
return(s);
return(OK);
}
/*===========================================================================*
* fork_nb *
*===========================================================================*/
PRIVATE pid_t fork_nb()
{
message m;
return(_syscall(PM_PROC_NR, FORK_NB, &m));
}
/*===========================================================================*
* share_exec *
*===========================================================================*/
PRIVATE int share_exec(rp_dst, rp_src)
struct rproc *rp_dst, *rp_src;
{
struct rprocpub *rpub_src;
struct rprocpub *rpub_dst;
rpub_src = rp_src->r_pub;
rpub_dst = rp_dst->r_pub;
if(rs_verbose) {
printf("RS: share_exec: sharing exec image from %s to %s\n",
rpub_src->label, rpub_dst->label);
}
/* Share exec image from rp_src to rp_dst. */
rp_dst->r_exec_len = rp_src->r_exec_len;
rp_dst->r_exec = rp_src->r_exec;
return OK;
}
/*===========================================================================*
* read_exec *
*===========================================================================*/
PRIVATE int read_exec(rp)
struct rproc *rp;
{
int e, r, fd;
char *e_name;
struct stat sb;
e_name= rp->r_argv[0];
if(rs_verbose) {
printf("RS: read_exec: copying exec image from: %s\n", e_name);
}
r= stat(e_name, &sb);
if (r != 0)
return -errno;
fd= open(e_name, O_RDONLY);
if (fd == -1)
return -errno;
rp->r_exec_len= sb.st_size;
rp->r_exec= malloc(rp->r_exec_len);
if (rp->r_exec == NULL)
{
printf("RS: read_exec: unable to allocate %d bytes\n",
rp->r_exec_len);
close(fd);
return ENOMEM;
}
r= read(fd, rp->r_exec, rp->r_exec_len);
e= errno;
close(fd);
if (r == rp->r_exec_len)
return OK;
printf("RS: read_exec: read failed %d, errno %d\n", r, e);
free(rp->r_exec);
rp->r_exec= NULL;
if (r >= 0)
return EIO;
else
return -e;
}
/*===========================================================================*
* free_slot *
*===========================================================================*/
PRIVATE void free_slot(rp)
struct rproc *rp;
{
int slot_nr, has_shared_exec;
struct rprocpub *rpub;
struct rproc *other_rp;
rpub = rp->r_pub;
/* Free memory if necessary. */
if(rpub->sys_flags & SF_USE_COPY) {
/* Search for some other slot sharing the same exec image. */
has_shared_exec = FALSE;
for (slot_nr = 0; slot_nr < NR_SYS_PROCS; slot_nr++) {
other_rp = &rproc[slot_nr]; /* get pointer to slot */
if (other_rp->r_flags & RS_IN_USE && other_rp != rp
&& other_rp->r_exec == rp->r_exec) { /* found! */
has_shared_exec = TRUE;
}
}
/* If nobody uses our copy of the exec image, we can get rid of it. */
if(!has_shared_exec) {
if(rs_verbose) {
printf("RS: free_slot: free exec image from %s\n",
rpub->label);
}
free(rp->r_exec);
rp->r_exec = NULL;
rp->r_exec_len = 0;
}
}
/* Mark slot as no longer in use.. */
rp->r_flags = 0;
rpub->in_use = FALSE;
rproc_ptr[_ENDPOINT_P(rpub->endpoint)] = NULL;
}
/*===========================================================================*
* run_script *
*===========================================================================*/
PRIVATE void run_script(rp)
struct rproc *rp;
{
int r, endpoint;
pid_t pid;
char *reason;
char incarnation_str[20]; /* Enough for a counter? */
char *envp[1] = { NULL };
struct rprocpub *rpub;
rpub = rp->r_pub;
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 if (rp->r_flags & RS_SIGNALED)
reason= "signaled";
else
{
printf(
"RS: run_script: can't find reason for termination of '%s'\n",
rpub->label);
return;
}
sprintf(incarnation_str, "%d", rp->r_restarts);
if(rs_verbose) {
printf("RS: calling script '%s'\n", rp->r_script);
printf("RS: sevice name: '%s'\n", rpub->label);
printf("RS: reason: '%s'\n", reason);
printf("RS: incarnation: '%s'\n", incarnation_str);
}
pid= fork();
switch(pid)
{
case -1:
printf("RS: run_script: fork failed: %s\n", strerror(errno));
break;
case 0:
execle(rp->r_script, rp->r_script, rpub->label, reason,
incarnation_str, NULL, envp);
printf("RS: run_script: execl '%s' failed: %s\n",
rp->r_script, strerror(errno));
exit(1);
default:
/* Set the privilege structure for the child process. */
endpoint = getnprocnr(pid);
if ((r = sys_privctl(endpoint, SYS_PRIV_SET_USER, NULL))
!= OK) {
printf("RS: run_script: can't set privileges: %d\n",r);
}
/* Allow the service to run. */
if ((r = sys_privctl(endpoint, SYS_PRIV_ALLOW, NULL)) != OK) {
printf("RS: run_script: process can't run: %d\n",r);
}
/* Do not wait for the child */
break;
}
}
/*===========================================================================*
* get_next_label *
*===========================================================================*/
PRIVATE char *get_next_label(ptr, label, caller_label)
char *ptr;
char *label;
char *caller_label;
{
/* Get the next label from the list of (IPC) labels.
*/
char *p, *q;
size_t len;
for (p= ptr; p[0] != '\0'; p= q)
{
/* Skip leading space */
while (p[0] != '\0' && isspace((unsigned char)p[0]))
p++;
/* Find start of next word */
q= p;
while (q[0] != '\0' && !isspace((unsigned char)q[0]))
q++;
if (q == p)
continue;
len= q-p;
if (len > RS_MAX_LABEL_LEN)
{
printf(
"rs:get_next_label: bad ipc list entry '.*s' for %s: too long\n",
len, p, caller_label);
continue;
}
memcpy(label, p, len);
label[len]= '\0';
return q; /* found another */
}
return NULL; /* done */
}
/*===========================================================================*
* add_forward_ipc *
*===========================================================================*/
PRIVATE void add_forward_ipc(rp, privp)
struct rproc *rp;
struct priv *privp;
{
/* Add IPC send permissions to a process based on that process's IPC
* list.
*/
char label[RS_MAX_LABEL_LEN+1], *p;
struct rproc *tmp_rp;
struct rprocpub *tmp_rpub;
endpoint_t endpoint;
int r;
int slot_nr, priv_id;
struct priv priv;
struct rprocpub *rpub;
rpub = rp->r_pub;
p = rp->r_ipc_list;
while ((p = get_next_label(p, label, rpub->label)) != NULL) {
if (strcmp(label, "SYSTEM") == 0)
endpoint= SYSTEM;
else if (strcmp(label, "USER") == 0)
endpoint= INIT_PROC_NR; /* all user procs */
else if (strcmp(label, "PM") == 0)
endpoint= PM_PROC_NR;
else if (strcmp(label, "VFS") == 0)
endpoint= FS_PROC_NR;
else if (strcmp(label, "RS") == 0)
endpoint= RS_PROC_NR;
else if (strcmp(label, "LOG") == 0)
endpoint= LOG_PROC_NR;
else if (strcmp(label, "TTY") == 0)
endpoint= TTY_PROC_NR;
else if (strcmp(label, "DS") == 0)
endpoint= DS_PROC_NR;
else if (strcmp(label, "VM") == 0)
endpoint= VM_PROC_NR;
else
{
/* Try to find process */
for (slot_nr = 0; slot_nr < NR_SYS_PROCS;
slot_nr++)
{
tmp_rp = &rproc[slot_nr];
if (!(tmp_rp->r_flags & RS_IN_USE))
continue;
tmp_rpub = tmp_rp->r_pub;
if (strcmp(tmp_rpub->label, label) == 0)
break;
}
if (slot_nr >= NR_SYS_PROCS)
{
if (rs_verbose)
printf(
"add_forward_ipc: unable to find '%s'\n", label);
continue;
}
endpoint= tmp_rpub->endpoint;
}
if ((r = sys_getpriv(&priv, endpoint)) < 0)
{
printf(
"add_forward_ipc: unable to get priv_id for '%s': %d\n",
label, r);
continue;
}
priv_id= priv.s_id;
set_sys_bit(privp->s_ipc_to, priv_id);
}
}
/*===========================================================================*
* add_backward_ipc *
*===========================================================================*/
PRIVATE void add_backward_ipc(rp, privp)
struct rproc *rp;
struct priv *privp;
{
/* Add IPC send permissions to a process based on other processes' IPC
* lists. This is enough to allow each such two processes to talk to
* each other, as the kernel guarantees send mask symmetry. We need to
* add these permissions now because the current process may not yet
* have existed at the time that the other process was initialized.
*/
char label[RS_MAX_LABEL_LEN+1], *p;
struct rproc *rrp;
struct rprocpub *rrpub;
int priv_id, found;
for (rrp=BEG_RPROC_ADDR; rrp<END_RPROC_ADDR; rrp++) {
if (!(rrp->r_flags & RS_IN_USE))
continue;
/* If an IPC target list was provided for the process being
* checked here, make sure that the label of the new process
* is in that process's list.
*/
if (rrp->r_ipc_list[0]) {
found = 0;
rrpub = rrp->r_pub;
p = rrp->r_ipc_list;
while ((p = get_next_label(p, label,
rrpub->label)) != NULL) {
if (!strcmp(rrpub->label, label)) {
found = 1;
break;
}
}
if (!found)
continue;
}
priv_id= rrp->r_priv.s_id;
set_sys_bit(privp->s_ipc_to, priv_id);
}
}
/*===========================================================================*
* 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 s_k_call_mask */
memset(privp->s_k_call_mask, '\0', sizeof(privp->s_k_call_mask));
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 < RS_SYS_CALL_MASK_SIZE; 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;
#if 0
if(rs_verbose)
printf("RS: init_privs: system call %d\n", call_nr);
#endif
dst_word= call_nr / dst_bits_per_word;
mask= (1UL << (call_nr % dst_bits_per_word));
if (dst_word >= SYS_CALL_MASK_SIZE)
{
printf(
"RS: init_privs: call number %d doesn't fit\n",
call_nr);
}
privp->s_k_call_mask[dst_word] |= mask;
}
}
/* Clear s_ipc_to */
memset(&privp->s_ipc_to, '\0', sizeof(privp->s_ipc_to));
if (strlen(rp->r_ipc_list) != 0)
{
add_forward_ipc(rp, privp);
add_backward_ipc(rp, privp);
}
else
{
for (i= 0; i<NR_SYS_PROCS; i++)
{
if (i != USER_PRIV_ID)
set_sys_bit(privp->s_ipc_to, i);
}
}
}
/*===========================================================================*
* init_pci *
*===========================================================================*/
PRIVATE void init_pci(rp, endpoint)
struct rproc *rp;
int endpoint;
{
/* Inform the PCI driver about the new service. */
size_t len;
int i, r;
struct rs_pci rs_pci;
struct rprocpub *rpub;
rpub = rp->r_pub;
rs_pci = rpub->pci_acl;
strcpy(rs_pci.rsp_label, rpub->label);
rs_pci.rsp_endpoint= endpoint;
if(rs_verbose)
printf("RS: init_pci: calling pci_set_acl\n");
r= pci_set_acl(&rs_pci);
if(rs_verbose)
printf("RS: init_pci: after pci_set_acl\n");
if (r != OK)
{
printf("RS: init_pci: pci_set_acl failed: %s\n",
strerror(errno));
return;
}
}
/*===========================================================================*
* do_lookup *
*===========================================================================*/
PUBLIC int do_lookup(m_ptr)
message *m_ptr;
{
static char namebuf[100];
int len, r;
struct rproc *rrp;
struct rprocpub *rrpub;
len = m_ptr->RS_NAME_LEN;
if(len < 2 || len >= sizeof(namebuf)) {
printf("RS: len too weird (%d)\n", len);
return EINVAL;
}
if((r=sys_vircopy(m_ptr->m_source, D, (vir_bytes) m_ptr->RS_NAME,
SELF, D, (vir_bytes) namebuf, len)) != OK) {
printf("RS: name copy failed\n");
return r;
}
namebuf[len] = '\0';
for (rrp=BEG_RPROC_ADDR; rrp<END_RPROC_ADDR; rrp++) {
if (!(rrp->r_flags & RS_IN_USE))
continue;
rrpub = rrp->r_pub;
if (!strcmp(rrpub->label, namebuf)) {
m_ptr->RS_ENDPOINT = rrpub->endpoint;
return OK;
}
}
return ESRCH;
}