The VM server now manages its call masks such that all user processes share the same call mask. As a result, an update for the call mask of any user process will apply to all user processes. This is similar to the privilege infrastructure employed by the kernel, and may serve as a template for similar fine-grained restrictions in other servers. Concretely, this patch fixes the problem of "service edit init" not applying the given VM call mask to user processes started from RC scripts during system startup. In addition, this patch makes RS set a proper VM call mask for each recovery script it spawns. Change-Id: I520a30d85a0d3f3502d2b158293a2258825358cf
982 lines
27 KiB
C
Executable file
982 lines
27 KiB
C
Executable file
/*
|
|
* Changes:
|
|
* Jan 22, 2010: Created (Cristiano Giuffrida)
|
|
*/
|
|
|
|
#include "inc.h"
|
|
|
|
#include "kernel/proc.h"
|
|
|
|
static int check_request(struct rs_start *rs_start);
|
|
|
|
/*===========================================================================*
|
|
* do_up *
|
|
*===========================================================================*/
|
|
int do_up(m_ptr)
|
|
message *m_ptr; /* request message pointer */
|
|
{
|
|
/* A request was made to start a new system service. */
|
|
struct rproc *rp;
|
|
struct rprocpub *rpub;
|
|
int r;
|
|
struct rs_start rs_start;
|
|
int noblock;
|
|
|
|
/* Check if the call can be allowed. */
|
|
if((r = check_call_permission(m_ptr->m_source, RS_UP, NULL)) != OK)
|
|
return r;
|
|
|
|
/* Allocate a new system service slot. */
|
|
r = alloc_slot(&rp);
|
|
if(r != OK) {
|
|
printf("RS: do_up: unable to allocate a new slot: %d\n", r);
|
|
return r;
|
|
}
|
|
rpub = rp->r_pub;
|
|
|
|
/* Copy the request structure. */
|
|
r = copy_rs_start(m_ptr->m_source, m_ptr->RS_CMD_ADDR, &rs_start);
|
|
if (r != OK) {
|
|
return r;
|
|
}
|
|
r = check_request(&rs_start);
|
|
if (r != OK) {
|
|
return r;
|
|
}
|
|
noblock = (rs_start.rss_flags & RSS_NOBLOCK);
|
|
|
|
/* Initialize the slot as requested. */
|
|
r = init_slot(rp, &rs_start, m_ptr->m_source);
|
|
if(r != OK) {
|
|
printf("RS: do_up: unable to init the new slot: %d\n", r);
|
|
return r;
|
|
}
|
|
|
|
/* Check for duplicates */
|
|
if(lookup_slot_by_label(rpub->label)) {
|
|
printf("RS: service with the same label '%s' already exists\n",
|
|
rpub->label);
|
|
return EBUSY;
|
|
}
|
|
if(rpub->dev_nr>0 && lookup_slot_by_dev_nr(rpub->dev_nr)) {
|
|
printf("RS: service with the same device number %d already exists\n",
|
|
rpub->dev_nr);
|
|
return EBUSY;
|
|
}
|
|
|
|
/* All information was gathered. Now try to start the system service. */
|
|
r = start_service(rp);
|
|
if(r != OK) {
|
|
return r;
|
|
}
|
|
|
|
/* Unblock the caller immediately if requested. */
|
|
if(noblock) {
|
|
return OK;
|
|
}
|
|
|
|
/* Late reply - send a reply when service completes initialization. */
|
|
rp->r_flags |= RS_LATEREPLY;
|
|
rp->r_caller = m_ptr->m_source;
|
|
rp->r_caller_request = RS_UP;
|
|
|
|
return EDONTREPLY;
|
|
}
|
|
|
|
/*===========================================================================*
|
|
* do_down *
|
|
*===========================================================================*/
|
|
int do_down(message *m_ptr)
|
|
{
|
|
register struct rproc *rp;
|
|
int s;
|
|
char label[RS_MAX_LABEL_LEN];
|
|
|
|
/* Copy label. */
|
|
s = copy_label(m_ptr->m_source, m_ptr->RS_CMD_ADDR,
|
|
m_ptr->RS_CMD_LEN, label, sizeof(label));
|
|
if(s != OK) {
|
|
return s;
|
|
}
|
|
|
|
/* Lookup slot by label. */
|
|
rp = lookup_slot_by_label(label);
|
|
if(!rp) {
|
|
if(rs_verbose)
|
|
printf("RS: do_down: service '%s' not found\n", label);
|
|
return(ESRCH);
|
|
}
|
|
|
|
/* Check if the call can be allowed. */
|
|
if((s = check_call_permission(m_ptr->m_source, RS_DOWN, rp)) != OK)
|
|
return s;
|
|
|
|
/* Stop service. */
|
|
if (rp->r_flags & RS_TERMINATED) {
|
|
/* A recovery script is requesting us to bring down the service.
|
|
* The service is already gone, simply perform cleanup.
|
|
*/
|
|
if(rs_verbose)
|
|
printf("RS: recovery script performs service down...\n");
|
|
unpublish_service(rp);
|
|
cleanup_service(rp);
|
|
return(OK);
|
|
}
|
|
stop_service(rp,RS_EXITING);
|
|
|
|
/* Late reply - send a reply when service dies. */
|
|
rp->r_flags |= RS_LATEREPLY;
|
|
rp->r_caller = m_ptr->m_source;
|
|
rp->r_caller_request = RS_DOWN;
|
|
|
|
return EDONTREPLY;
|
|
}
|
|
|
|
/*===========================================================================*
|
|
* do_restart *
|
|
*===========================================================================*/
|
|
int do_restart(message *m_ptr)
|
|
{
|
|
struct rproc *rp;
|
|
int s, r;
|
|
char label[RS_MAX_LABEL_LEN];
|
|
char script[MAX_SCRIPT_LEN];
|
|
|
|
/* Copy label. */
|
|
s = copy_label(m_ptr->m_source, m_ptr->RS_CMD_ADDR,
|
|
m_ptr->RS_CMD_LEN, label, sizeof(label));
|
|
if(s != OK) {
|
|
return s;
|
|
}
|
|
|
|
/* Lookup slot by label. */
|
|
rp = lookup_slot_by_label(label);
|
|
if(!rp) {
|
|
if(rs_verbose)
|
|
printf("RS: do_restart: service '%s' not found\n", label);
|
|
return(ESRCH);
|
|
}
|
|
|
|
/* Check if the call can be allowed. */
|
|
if((r = check_call_permission(m_ptr->m_source, RS_RESTART, rp)) != OK)
|
|
return r;
|
|
|
|
/* We can only be asked to restart a service from a recovery script. */
|
|
if (! (rp->r_flags & RS_TERMINATED) ) {
|
|
if(rs_verbose)
|
|
printf("RS: %s is still running\n", srv_to_string(rp));
|
|
return EBUSY;
|
|
}
|
|
|
|
if(rs_verbose)
|
|
printf("RS: recovery script performs service restart...\n");
|
|
|
|
/* Restart the service, but make sure we don't call the script again. */
|
|
strcpy(script, rp->r_script);
|
|
rp->r_script[0] = '\0';
|
|
restart_service(rp);
|
|
strcpy(rp->r_script, script);
|
|
|
|
return OK;
|
|
}
|
|
|
|
/*===========================================================================*
|
|
* do_clone *
|
|
*===========================================================================*/
|
|
int do_clone(message *m_ptr)
|
|
{
|
|
struct rproc *rp;
|
|
struct rprocpub *rpub;
|
|
int s, r;
|
|
char label[RS_MAX_LABEL_LEN];
|
|
|
|
/* Copy label. */
|
|
s = copy_label(m_ptr->m_source, m_ptr->RS_CMD_ADDR,
|
|
m_ptr->RS_CMD_LEN, label, sizeof(label));
|
|
if(s != OK) {
|
|
return s;
|
|
}
|
|
|
|
/* Lookup slot by label. */
|
|
rp = lookup_slot_by_label(label);
|
|
if(!rp) {
|
|
if(rs_verbose)
|
|
printf("RS: do_clone: service '%s' not found\n", label);
|
|
return(ESRCH);
|
|
}
|
|
rpub = rp->r_pub;
|
|
|
|
/* Check if the call can be allowed. */
|
|
if((r = check_call_permission(m_ptr->m_source, RS_CLONE, rp)) != OK)
|
|
return r;
|
|
|
|
/* Don't clone if a replica is already available. */
|
|
if(rp->r_next_rp) {
|
|
return EEXIST;
|
|
}
|
|
|
|
/* Clone the service as requested. */
|
|
rpub->sys_flags |= SF_USE_REPL;
|
|
if ((r = clone_service(rp, RST_SYS_PROC)) != OK) {
|
|
rpub->sys_flags &= ~SF_USE_REPL;
|
|
return r;
|
|
}
|
|
|
|
return OK;
|
|
}
|
|
|
|
/*===========================================================================*
|
|
* do_edit *
|
|
*===========================================================================*/
|
|
int do_edit(message *m_ptr)
|
|
{
|
|
struct rproc *rp;
|
|
struct rprocpub *rpub;
|
|
struct rs_start rs_start;
|
|
int r;
|
|
char label[RS_MAX_LABEL_LEN];
|
|
|
|
/* Copy the request structure. */
|
|
r = copy_rs_start(m_ptr->m_source, m_ptr->RS_CMD_ADDR, &rs_start);
|
|
if (r != OK) {
|
|
return r;
|
|
}
|
|
|
|
/* Copy label. */
|
|
r = copy_label(m_ptr->m_source, rs_start.rss_label.l_addr,
|
|
rs_start.rss_label.l_len, label, sizeof(label));
|
|
if(r != OK) {
|
|
return r;
|
|
}
|
|
|
|
/* Lookup slot by label. */
|
|
rp = lookup_slot_by_label(label);
|
|
if(!rp) {
|
|
if(rs_verbose)
|
|
printf("RS: do_edit: service '%s' not found\n", label);
|
|
return ESRCH;
|
|
}
|
|
rpub = rp->r_pub;
|
|
|
|
/* Check if the call can be allowed. */
|
|
if((r = check_call_permission(m_ptr->m_source, RS_EDIT, rp)) != OK)
|
|
return r;
|
|
|
|
if(rs_verbose)
|
|
printf("RS: %s edits settings\n", srv_to_string(rp));
|
|
|
|
/* Synch the privilege structure with the kernel. */
|
|
if ((r = sys_getpriv(&rp->r_priv, rpub->endpoint)) != OK) {
|
|
printf("RS: do_edit: unable to synch privilege structure: %d\n", r);
|
|
return r;
|
|
}
|
|
|
|
/* Tell scheduler this process is finished */
|
|
if ((r = sched_stop(rp->r_scheduler, rpub->endpoint)) != OK) {
|
|
printf("RS: do_edit: scheduler won't give up process: %d\n", r);
|
|
return r;
|
|
}
|
|
|
|
/* Edit the slot as requested. */
|
|
if((r = edit_slot(rp, &rs_start, m_ptr->m_source)) != OK) {
|
|
printf("RS: do_edit: unable to edit the existing slot: %d\n", r);
|
|
return r;
|
|
}
|
|
|
|
/* Update privilege structure. */
|
|
r = sys_privctl(rpub->endpoint, SYS_PRIV_UPDATE_SYS, &rp->r_priv);
|
|
if(r != OK) {
|
|
printf("RS: do_edit: unable to update privilege structure: %d\n", r);
|
|
return r;
|
|
}
|
|
|
|
/* Update VM calls. */
|
|
if ((r = vm_set_priv(rpub->endpoint, &rpub->vm_call_mask[0],
|
|
!!(rp->r_priv.s_flags & SYS_PROC))) != OK) {
|
|
printf("RS: do_edit: failed: %d\n", r);
|
|
return r;
|
|
}
|
|
|
|
/* Reinitialize scheduling. */
|
|
if ((r = sched_init_proc(rp)) != OK) {
|
|
printf("RS: do_edit: unable to reinitialize scheduling: %d\n", r);
|
|
return r;
|
|
}
|
|
|
|
/* Cleanup old replicas and create a new one, if necessary. */
|
|
if(rpub->sys_flags & SF_USE_REPL) {
|
|
if(rp->r_next_rp) {
|
|
cleanup_service(rp->r_next_rp);
|
|
rp->r_next_rp = NULL;
|
|
}
|
|
if ((r = clone_service(rp, RST_SYS_PROC)) != OK) {
|
|
printf("RS: warning: unable to clone %s\n", srv_to_string(rp));
|
|
}
|
|
}
|
|
|
|
return OK;
|
|
}
|
|
|
|
/*===========================================================================*
|
|
* do_refresh *
|
|
*===========================================================================*/
|
|
int do_refresh(message *m_ptr)
|
|
{
|
|
register struct rproc *rp;
|
|
int s;
|
|
char label[RS_MAX_LABEL_LEN];
|
|
|
|
/* Copy label. */
|
|
s = copy_label(m_ptr->m_source, m_ptr->RS_CMD_ADDR,
|
|
m_ptr->RS_CMD_LEN, label, sizeof(label));
|
|
if(s != OK) {
|
|
return s;
|
|
}
|
|
|
|
/* Lookup slot by label. */
|
|
rp = lookup_slot_by_label(label);
|
|
if(!rp) {
|
|
if(rs_verbose)
|
|
printf("RS: do_refresh: service '%s' not found\n", label);
|
|
return(ESRCH);
|
|
}
|
|
|
|
/* Check if the call can be allowed. */
|
|
if((s = check_call_permission(m_ptr->m_source, RS_REFRESH, rp)) != OK)
|
|
return s;
|
|
|
|
/* Refresh service. */
|
|
if(rs_verbose)
|
|
printf("RS: %s refreshing\n", srv_to_string(rp));
|
|
stop_service(rp,RS_REFRESHING);
|
|
|
|
return OK;
|
|
}
|
|
|
|
/*===========================================================================*
|
|
* do_shutdown *
|
|
*===========================================================================*/
|
|
int do_shutdown(message *m_ptr)
|
|
{
|
|
int slot_nr;
|
|
struct rproc *rp;
|
|
int r;
|
|
|
|
/* Check if the call can be allowed. */
|
|
if (m_ptr != NULL) {
|
|
if((r = check_call_permission(m_ptr->m_source, RS_SHUTDOWN, NULL)) != OK)
|
|
return r;
|
|
}
|
|
|
|
if(rs_verbose)
|
|
printf("RS: shutting down...\n");
|
|
|
|
/* Set flag to tell RS we are shutting down. */
|
|
shutting_down = TRUE;
|
|
|
|
/* Don't restart dead services. */
|
|
for (slot_nr = 0; slot_nr < NR_SYS_PROCS; slot_nr++) {
|
|
rp = &rproc[slot_nr];
|
|
if (rp->r_flags & RS_IN_USE) {
|
|
rp->r_flags |= RS_EXITING;
|
|
}
|
|
}
|
|
return(OK);
|
|
}
|
|
|
|
/*===========================================================================*
|
|
* do_init_ready *
|
|
*===========================================================================*/
|
|
int do_init_ready(message *m_ptr)
|
|
{
|
|
int who_p;
|
|
message m;
|
|
struct rproc *rp;
|
|
struct rprocpub *rpub;
|
|
int result, is_rs;
|
|
int r;
|
|
|
|
is_rs = (m_ptr->m_source == RS_PROC_NR);
|
|
who_p = _ENDPOINT_P(m_ptr->m_source);
|
|
result = m_ptr->RS_INIT_RESULT;
|
|
|
|
/* Check for RS failing initialization first. */
|
|
if(is_rs && result != OK) {
|
|
return result;
|
|
}
|
|
|
|
rp = rproc_ptr[who_p];
|
|
rpub = rp->r_pub;
|
|
|
|
/* 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;
|
|
}
|
|
|
|
/* Check if something went wrong and the service failed to init.
|
|
* In that case, kill the service.
|
|
*/
|
|
if(result != OK) {
|
|
if(rs_verbose)
|
|
printf("RS: %s initialization error: %s\n", srv_to_string(rp),
|
|
init_strerror(result));
|
|
if (result == ERESTART)
|
|
rp->r_flags |= RS_REINCARNATE;
|
|
crash_service(rp); /* simulate crash */
|
|
return EDONTREPLY;
|
|
}
|
|
|
|
/* Mark the slot as no longer initializing. */
|
|
rp->r_flags &= ~RS_INITIALIZING;
|
|
rp->r_check_tm = 0;
|
|
getticks(&rp->r_alive_tm);
|
|
|
|
/* Reply and unblock the service before doing anything else. */
|
|
m.m_type = OK;
|
|
reply(rpub->endpoint, rp, &m);
|
|
|
|
/* See if a late reply has to be sent. */
|
|
late_reply(rp, OK);
|
|
|
|
if(rs_verbose)
|
|
printf("RS: %s initialized\n", srv_to_string(rp));
|
|
|
|
/* If the service has completed initialization after a live
|
|
* update, end the update now.
|
|
*/
|
|
if(rp->r_flags & RS_UPDATING) {
|
|
printf("RS: update succeeded\n");
|
|
end_update(OK, RS_DONTREPLY);
|
|
}
|
|
|
|
/* If the service has completed initialization after a crash
|
|
* make the new instance active and cleanup the old replica.
|
|
*/
|
|
if(rp->r_prev_rp) {
|
|
cleanup_service(rp->r_prev_rp);
|
|
rp->r_prev_rp = NULL;
|
|
rp->r_restarts += 1;
|
|
|
|
if(rs_verbose)
|
|
printf("RS: %s completed restart\n", srv_to_string(rp));
|
|
}
|
|
|
|
/* If we must keep a replica of this system service, create it now. */
|
|
if(rpub->sys_flags & SF_USE_REPL) {
|
|
if ((r = clone_service(rp, RST_SYS_PROC)) != OK) {
|
|
printf("RS: warning: unable to clone %s\n", srv_to_string(rp));
|
|
}
|
|
}
|
|
|
|
return is_rs ? OK : EDONTREPLY; /* return what the caller expects */
|
|
}
|
|
|
|
/*===========================================================================*
|
|
* do_update *
|
|
*===========================================================================*/
|
|
int do_update(message *m_ptr)
|
|
{
|
|
struct rproc *rp;
|
|
struct rproc *new_rp;
|
|
struct rprocpub *rpub;
|
|
struct rs_start rs_start;
|
|
int noblock, do_self_update;
|
|
int s;
|
|
char label[RS_MAX_LABEL_LEN];
|
|
int lu_state;
|
|
int prepare_maxtime;
|
|
|
|
/* Copy the request structure. */
|
|
s = copy_rs_start(m_ptr->m_source, m_ptr->RS_CMD_ADDR, &rs_start);
|
|
if (s != OK) {
|
|
return s;
|
|
}
|
|
noblock = (rs_start.rss_flags & RSS_NOBLOCK);
|
|
do_self_update = (rs_start.rss_flags & RSS_SELF_LU);
|
|
s = check_request(&rs_start);
|
|
if (s != OK) {
|
|
return s;
|
|
}
|
|
|
|
/* Copy label. */
|
|
s = copy_label(m_ptr->m_source, rs_start.rss_label.l_addr,
|
|
rs_start.rss_label.l_len, label, sizeof(label));
|
|
if(s != OK) {
|
|
return s;
|
|
}
|
|
|
|
/* Lookup slot by label. */
|
|
rp = lookup_slot_by_label(label);
|
|
if(!rp) {
|
|
if(rs_verbose)
|
|
printf("RS: do_update: service '%s' not found\n", label);
|
|
return ESRCH;
|
|
}
|
|
rpub = rp->r_pub;
|
|
|
|
/* Check if the call can be allowed. */
|
|
if((s = check_call_permission(m_ptr->m_source, RS_UPDATE, rp)) != OK)
|
|
return s;
|
|
|
|
/* 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\n");
|
|
return EBUSY;
|
|
}
|
|
|
|
/* A self update live updates a service instance into a replica, a regular
|
|
* update live updates a service instance into a new version, as specified
|
|
* by the given binary.
|
|
*/
|
|
if(do_self_update) {
|
|
if(rs_verbose)
|
|
printf("RS: %s performs self update\n", srv_to_string(rp));
|
|
|
|
/* Clone the system service and use the replica as the new version. */
|
|
s = clone_service(rp, LU_SYS_PROC);
|
|
if(s != OK) {
|
|
printf("RS: do_update: unable to clone service: %d\n", s);
|
|
return s;
|
|
}
|
|
}
|
|
else {
|
|
if(rs_verbose)
|
|
printf("RS: %s performs regular update\n", srv_to_string(rp));
|
|
|
|
/* Allocate a system service slot for the new version. */
|
|
s = alloc_slot(&new_rp);
|
|
if(s != OK) {
|
|
printf("RS: do_update: unable to allocate a new slot: %d\n", s);
|
|
return s;
|
|
}
|
|
|
|
/* Initialize the slot as requested. */
|
|
s = init_slot(new_rp, &rs_start, m_ptr->m_source);
|
|
if(s != OK) {
|
|
printf("RS: do_update: unable to init the new slot: %d\n", s);
|
|
return s;
|
|
}
|
|
|
|
/* Let the new version inherit defaults from the old one. */
|
|
inherit_service_defaults(rp, new_rp);
|
|
|
|
/* Link the two versions. */
|
|
rp->r_new_rp = new_rp;
|
|
new_rp->r_old_rp = rp;
|
|
|
|
/* Create new version of the service but don't let it run. */
|
|
new_rp->r_priv.s_flags |= LU_SYS_PROC;
|
|
s = create_service(new_rp);
|
|
if(s != OK) {
|
|
printf("RS: do_update: unable to create a new service: %d\n", s);
|
|
return s;
|
|
}
|
|
}
|
|
|
|
/* Mark both versions as updating. */
|
|
rp->r_flags |= RS_UPDATING;
|
|
rp->r_new_rp->r_flags |= RS_UPDATING;
|
|
rupdate.flags |= RS_UPDATING;
|
|
getticks(&rupdate.prepare_tm);
|
|
rupdate.prepare_maxtime = prepare_maxtime;
|
|
rupdate.rp = rp;
|
|
|
|
if(rs_verbose)
|
|
printf("RS: %s updating\n", srv_to_string(rp));
|
|
|
|
/* If RS is updating, set up signal managers for the new instance.
|
|
* The current RS instance must be made the backup signal manager to
|
|
* support rollback in case of a crash during initialization.
|
|
*/
|
|
if(rp->r_priv.s_flags & ROOT_SYS_PROC) {
|
|
new_rp = rp->r_new_rp;
|
|
|
|
s = update_sig_mgrs(new_rp, SELF, new_rp->r_pub->endpoint);
|
|
if(s != OK) {
|
|
cleanup_service(new_rp);
|
|
return s;
|
|
}
|
|
}
|
|
|
|
if(noblock) {
|
|
/* Unblock the caller immediately if requested. */
|
|
m_ptr->m_type = OK;
|
|
reply(m_ptr->m_source, NULL, m_ptr);
|
|
}
|
|
else {
|
|
/* Send a reply when the new version completes initialization. */
|
|
rp->r_flags |= RS_LATEREPLY;
|
|
rp->r_caller = m_ptr->m_source;
|
|
rp->r_caller_request = RS_UPDATE;
|
|
}
|
|
|
|
/* Request to update. */
|
|
m_ptr->m_type = RS_LU_PREPARE;
|
|
if(rpub->endpoint == RS_PROC_NR) {
|
|
/* RS can process the request directly. */
|
|
do_sef_lu_request(m_ptr);
|
|
}
|
|
else {
|
|
/* Send request message to the system service. */
|
|
asynsend3(rpub->endpoint, m_ptr, AMF_NOREPLY);
|
|
}
|
|
|
|
return EDONTREPLY;
|
|
}
|
|
|
|
/*===========================================================================*
|
|
* do_upd_ready *
|
|
*===========================================================================*/
|
|
int do_upd_ready(message *m_ptr)
|
|
{
|
|
struct rproc *rp, *old_rp, *new_rp;
|
|
int who_p;
|
|
int result;
|
|
int is_rs;
|
|
int r;
|
|
|
|
who_p = _ENDPOINT_P(m_ptr->m_source);
|
|
rp = rproc_ptr[who_p];
|
|
result = m_ptr->RS_LU_RESULT;
|
|
is_rs = (m_ptr->m_source == RS_PROC_NR);
|
|
|
|
/* Make sure the originating service was requested to prepare for update. */
|
|
if(rp != rupdate.rp) {
|
|
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. The old version will
|
|
* be replied to and continue executing.
|
|
*/
|
|
if(result != OK) {
|
|
end_update(result, RS_REPLY);
|
|
|
|
printf("RS: update failed: %s\n", lu_strerror(result));
|
|
return is_rs ? result : EDONTREPLY; /* return what the caller expects */
|
|
}
|
|
|
|
old_rp = rp;
|
|
new_rp = rp->r_new_rp;
|
|
|
|
/* If RS itself is updating, yield control to the new version immediately. */
|
|
if(is_rs) {
|
|
r = init_service(new_rp, SEF_INIT_LU);
|
|
if(r != OK) {
|
|
panic("unable to initialize the new RS instance: %d", r);
|
|
}
|
|
r = sys_privctl(new_rp->r_pub->endpoint, SYS_PRIV_YIELD, NULL);
|
|
if(r != OK) {
|
|
panic("unable to yield control to the new RS instance: %d", r);
|
|
}
|
|
/* If we get this far, the new version failed to initialize. Rollback. */
|
|
r = srv_update(RS_PROC_NR, new_rp->r_pub->endpoint);
|
|
assert(r == OK); /* can't fail */
|
|
end_update(ERESTART, RS_REPLY);
|
|
return ERESTART;
|
|
}
|
|
|
|
/* Perform the update. */
|
|
r = update_service(&old_rp, &new_rp, RS_SWAP);
|
|
if(r != OK) {
|
|
end_update(r, RS_REPLY);
|
|
printf("RS: update failed: error %d\n", r);
|
|
return EDONTREPLY;
|
|
}
|
|
|
|
/* Let the new version run. */
|
|
r = run_service(new_rp, SEF_INIT_LU);
|
|
if(r != OK) {
|
|
/* Something went wrong. Rollback. */
|
|
r = update_service(&new_rp, &old_rp, RS_SWAP);
|
|
assert(r == OK); /* can't fail */
|
|
end_update(r, RS_REPLY);
|
|
printf("RS: update failed: error %d\n", r);
|
|
return EDONTREPLY;
|
|
}
|
|
|
|
return EDONTREPLY;
|
|
}
|
|
|
|
/*===========================================================================*
|
|
* do_period *
|
|
*===========================================================================*/
|
|
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;
|
|
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_ACTIVE) && !(rp->r_flags & RS_UPDATING)) {
|
|
|
|
/* Compute period. */
|
|
period = rp->r_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) {
|
|
restart_service(rp);
|
|
}
|
|
}
|
|
|
|
/* 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) {
|
|
rp->r_stop_tm = 0;
|
|
crash_service(rp); /* simulate crash */
|
|
}
|
|
|
|
/* 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. Give the service a free pass if
|
|
* somebody is initializing. There may be some weird dependencies
|
|
* if another service is, for example, restarting at the same
|
|
* time.
|
|
*/
|
|
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: %s reported late\n", srv_to_string(rp));
|
|
if(lookup_slot_by_flags(RS_INITIALIZING)) {
|
|
/* Skip for now. */
|
|
if(rs_verbose)
|
|
printf("RS: %s gets a free pass\n",
|
|
srv_to_string(rp));
|
|
rp->r_alive_tm = now;
|
|
rp->r_check_tm = now+1;
|
|
continue;
|
|
}
|
|
rp->r_flags |= RS_NOPINGREPLY;
|
|
crash_service(rp); /* 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 > rp->r_period) {
|
|
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("couldn't set alarm: %d", s);
|
|
}
|
|
|
|
/*===========================================================================*
|
|
* do_sigchld *
|
|
*===========================================================================*/
|
|
void do_sigchld()
|
|
{
|
|
/* PM informed us that there are dead children to cleanup. Go get them. */
|
|
pid_t pid;
|
|
int status;
|
|
struct rproc *rp;
|
|
struct rproc **rps;
|
|
int i, nr_rps;
|
|
|
|
if(rs_verbose)
|
|
printf("RS: got SIGCHLD signal, cleaning up dead children\n");
|
|
|
|
while ( (pid = waitpid(-1, &status, WNOHANG)) != 0 ) {
|
|
rp = lookup_slot_by_pid(pid);
|
|
if(rp != NULL) {
|
|
|
|
if(rs_verbose)
|
|
printf("RS: %s exited via another signal manager\n",
|
|
srv_to_string(rp));
|
|
|
|
/* The slot is still there. This means RS is not the signal
|
|
* manager assigned to the process. Ignore the event but
|
|
* free slots for all the service instances and send a late
|
|
* reply if necessary.
|
|
*/
|
|
get_service_instances(rp, &rps, &nr_rps);
|
|
for(i=0;i<nr_rps;i++) {
|
|
if(rupdate.flags & RS_UPDATING) {
|
|
rupdate.flags &= ~RS_UPDATING;
|
|
}
|
|
free_slot(rps[i]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*===========================================================================*
|
|
* do_getsysinfo *
|
|
*===========================================================================*/
|
|
int do_getsysinfo(m_ptr)
|
|
message *m_ptr;
|
|
{
|
|
vir_bytes src_addr, dst_addr;
|
|
int dst_proc;
|
|
size_t len;
|
|
int s;
|
|
|
|
/* Check if the call can be allowed. */
|
|
if((s = check_call_permission(m_ptr->m_source, 0, NULL)) != OK)
|
|
return s;
|
|
|
|
switch(m_ptr->SI_WHAT) {
|
|
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);
|
|
}
|
|
|
|
if (len != m_ptr->SI_SIZE)
|
|
return(EINVAL);
|
|
|
|
dst_proc = m_ptr->m_source;
|
|
dst_addr = (vir_bytes) m_ptr->SI_WHERE;
|
|
return sys_datacopy(SELF, src_addr, dst_proc, dst_addr, len);
|
|
}
|
|
|
|
/*===========================================================================*
|
|
* do_lookup *
|
|
*===========================================================================*/
|
|
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, (vir_bytes) m_ptr->RS_NAME,
|
|
SELF, (vir_bytes) namebuf, len)) != OK) {
|
|
printf("RS: name copy failed\n");
|
|
return r;
|
|
|
|
}
|
|
|
|
namebuf[len] = '\0';
|
|
|
|
rrp = lookup_slot_by_label(namebuf);
|
|
if(!rrp) {
|
|
return ESRCH;
|
|
}
|
|
rrpub = rrp->r_pub;
|
|
m_ptr->RS_ENDPOINT = rrpub->endpoint;
|
|
|
|
return OK;
|
|
}
|
|
|
|
/*===========================================================================*
|
|
* check_request *
|
|
*===========================================================================*/
|
|
static int check_request(struct rs_start *rs_start)
|
|
{
|
|
/* Verify scheduling parameters */
|
|
if (rs_start->rss_scheduler != KERNEL &&
|
|
(rs_start->rss_scheduler < 0 ||
|
|
rs_start->rss_scheduler > LAST_SPECIAL_PROC_NR)) {
|
|
printf("RS: check_request: invalid scheduler %d\n",
|
|
rs_start->rss_scheduler);
|
|
return EINVAL;
|
|
}
|
|
if (rs_start->rss_priority >= NR_SCHED_QUEUES) {
|
|
printf("RS: check_request: priority %u out of range\n",
|
|
rs_start->rss_priority);
|
|
return EINVAL;
|
|
}
|
|
if (rs_start->rss_quantum <= 0) {
|
|
printf("RS: check_request: quantum %u out of range\n",
|
|
rs_start->rss_quantum);
|
|
return EINVAL;
|
|
}
|
|
|
|
if (rs_start->rss_cpu == RS_CPU_BSP)
|
|
rs_start->rss_cpu = machine.bsp_id;
|
|
else if (rs_start->rss_cpu == RS_CPU_DEFAULT) {
|
|
/* keep the default value */
|
|
} else if (rs_start->rss_cpu < 0)
|
|
return EINVAL;
|
|
else if (rs_start->rss_cpu > machine.processors_count) {
|
|
printf("RS: cpu number %d out of range 0-%d, using BSP\n",
|
|
rs_start->rss_cpu, machine.processors_count);
|
|
rs_start->rss_cpu = machine.bsp_id;
|
|
}
|
|
|
|
/* Verify signal manager. */
|
|
if (rs_start->rss_sigmgr != SELF &&
|
|
(rs_start->rss_sigmgr < 0 ||
|
|
rs_start->rss_sigmgr > LAST_SPECIAL_PROC_NR)) {
|
|
printf("RS: check_request: invalid signal manager %d\n",
|
|
rs_start->rss_sigmgr);
|
|
return EINVAL;
|
|
}
|
|
|
|
return OK;
|
|
}
|
|
|