From 5a9dec8bd2cf2a639a3365785c237e98708dbfac Mon Sep 17 00:00:00 2001 From: Jorrit Herder Date: Wed, 12 Oct 2005 15:07:38 +0000 Subject: [PATCH] New signal handling behaviour at PM (services can be killed). New Shift-F6 dump for RS server at IS. New getnpid, getnproc, getpproc library calls at PM. New reincarnation server (basic functionality is there now). --- servers/fs/main.c | 1 + servers/is/Makefile | 2 +- servers/is/dmp.c | 13 +- servers/is/dmp_kernel.c | 2 +- servers/is/dmp_rs.c | 62 +++++++ servers/is/glo.h | 3 + servers/is/is.c | 10 +- servers/is/proto.h | 3 + servers/pm/getset.c | 2 + servers/pm/main.c | 25 ++- servers/pm/misc.c | 9 +- servers/pm/param.h | 2 + servers/pm/signal.c | 6 - servers/pm/utility.c | 16 ++ servers/rs/Makefile | 2 +- servers/rs/main.c | 226 +++++++++++++++++++++++++ servers/rs/manager.c | 358 ++++++++++++++++++++++++++++++---------- servers/rs/proto.h | 6 +- servers/rs/rproc.h | 44 +++++ servers/rs/rs.c | 135 --------------- servers/rs/rs.h | 4 +- servers/rs/service.c | 153 +++++++++++------ 22 files changed, 785 insertions(+), 299 deletions(-) create mode 100644 servers/is/dmp_rs.c create mode 100644 servers/rs/main.c create mode 100644 servers/rs/rproc.h delete mode 100644 servers/rs/rs.c diff --git a/servers/fs/main.c b/servers/fs/main.c index 3bdd7ebfe..1092e2b2d 100644 --- a/servers/fs/main.c +++ b/servers/fs/main.c @@ -60,6 +60,7 @@ PUBLIC int main() if (call_nr == SYS_SIG) { sigset = m_in.NOTIFY_ARG; if (sigismember(&sigset, SIGKSTOP)) { + printf("FS got SIGKSTOP\n"); do_sync(); sys_exit(0); /* never returns */ } diff --git a/servers/is/Makefile b/servers/is/Makefile index 724f46a5b..dc519f0f0 100644 --- a/servers/is/Makefile +++ b/servers/is/Makefile @@ -17,7 +17,7 @@ CFLAGS = -I$i LDFLAGS = -i LIBS = -lsys -lsysutil -OBJ = is.o dmp.o dmp_kernel.o dmp_pm.o dmp_fs.o +OBJ = is.o dmp.o dmp_kernel.o dmp_pm.o dmp_fs.o dmp_rs.o # build local binary all build: $(SERVER) diff --git a/servers/is/dmp.c b/servers/is/dmp.c index 8f4ecaa28..21f34813f 100644 --- a/servers/is/dmp.c +++ b/servers/is/dmp.c @@ -9,7 +9,7 @@ #include "is.h" -#define NHOOKS 15 +#define NHOOKS 17 struct hook_entry { int key; @@ -23,14 +23,16 @@ struct hook_entry { { F5, monparams_dmp, "Boot monitor parameters" }, { F6, irqtab_dmp, "IRQ hooks and policies" }, { F7, kmessages_dmp, "Kernel messages" }, + { F9, sched_dmp, "Scheduling queues" }, { F10, kenv_dmp, "Kernel parameters" }, { F11, timing_dmp, "Timing details (if enabled)" }, - { F12, sched_dmp, "Scheduling queues" }, + { F12, reboot_dmp, "Reboot system after panic." }, { SF1, mproc_dmp, "Process manager process table" }, { SF2, sigaction_dmp, "Signals" }, { SF3, fproc_dmp, "Filesystem process table" }, { SF4, dtab_dmp, "Device/Driver mapping" }, { SF5, mapping_dmp, "Print key mappings" }, + { SF6, rproc_dmp, "Reincarnation server process table" }, }; /*===========================================================================* @@ -74,6 +76,13 @@ PRIVATE char *keyname(int key) return name; } + +PUBLIC void reboot_dmp(void) +{ + if (sys_panic) sys_abort(RBT_HALT); +} + + PUBLIC void mapping_dmp(void) { int h; diff --git a/servers/is/dmp_kernel.c b/servers/is/dmp_kernel.c index 16baf4843..b4e56254a 100644 --- a/servers/is/dmp_kernel.c +++ b/servers/is/dmp_kernel.c @@ -435,7 +435,7 @@ PRIVATE char *p_rts_flags_str(int flags) str[0] = (flags & NO_MAP) ? 'M' : '-'; str[1] = (flags & SENDING) ? 'S' : '-'; str[2] = (flags & RECEIVING) ? 'R' : '-'; - str[3] = (flags & SIGNALED) ? 'S' : '-'; + str[3] = (flags & SIGNALED) ? 'I' : '-'; str[4] = (flags & SIG_PENDING) ? 'P' : '-'; str[5] = (flags & P_STOP) ? 'T' : '-'; str[6] = '\0'; diff --git a/servers/is/dmp_rs.c b/servers/is/dmp_rs.c new file mode 100644 index 000000000..c9726440b --- /dev/null +++ b/servers/is/dmp_rs.c @@ -0,0 +1,62 @@ +/* This file contains procedures to dump RS data structures. + * + * The entry points into this file are + * rproc_dump: display RS system process table + * + * Created: + * Oct 03, 2005: by Jorrit N. Herder + */ + +#include "is.h" +#include "../rs/rproc.h" + +PUBLIC struct rproc rproc[NR_SYS_PROCS]; + +FORWARD _PROTOTYPE( char *s_flags_str, (int flags) ); + +/*===========================================================================* + * rproc_dmp * + *===========================================================================*/ +PUBLIC void rproc_dmp() +{ + struct rproc *rp; + int i,j, n=0; + static int prev_i=0; + + getsysinfo(RS_PROC_NR, SI_PROC_TAB, rproc); + + printf("Reincarnation Server (RS) system process table dump\n"); + printf("-proc nr-pid- -dev nr/ style- -ticks-checked-alive-- --flags-- -command (argc)-\n"); + for (i=prev_i; ir_flags & IN_USE) continue; + if (++n > 22) break; + printf("%3d %5d %3d/%2d %3u %8u %8u %s %s (%d)", + rp->r_proc_nr, rp->r_pid, + rp->r_dev_nr, rp->r_dev_style, + rp->r_period, + rp->r_check_tm, + rp->r_alive_tm, + s_flags_str(rp->r_flags), + rp->r_cmd, + rp->r_argc + ); + printf("\n"); + } + if (i >= NR_SYS_PROCS) i = 0; + else printf("--more--\r"); + prev_i = i; +} + + +PRIVATE char *s_flags_str(int flags) +{ + static char str[5]; + str[0] = (flags & IN_USE) ? 'U' : '-'; + str[1] = (flags & EXIT_PENDING) ? 'E' : '-'; + str[2] = '-'; + str[3] = '\0'; + + return(str); +} + diff --git a/servers/is/glo.h b/servers/is/glo.h index e489ef900..35277f248 100644 --- a/servers/is/glo.h +++ b/servers/is/glo.h @@ -6,6 +6,9 @@ extern char diag_buf[DIAG_BUF_SIZE]; /* buffer for messages */ extern int diag_next; /* next index to be written */ extern int diag_size; /* size of all messages */ +/* Flag to indicate system-wide panic. */ +extern int sys_panic; /* if set, shutdown can be done */ + /* The parameters of the call are kept here. */ extern message m_in; /* the input message itself */ extern message m_out; /* the output message used for reply */ diff --git a/servers/is/is.c b/servers/is/is.c index 3f29d4399..087c326df 100644 --- a/servers/is/is.c +++ b/servers/is/is.c @@ -18,6 +18,7 @@ message m_in; /* the input message itself */ message m_out; /* the output message used for reply */ int who; /* caller's proc number */ int callnr; /* system call number */ +int sys_panic; /* flag to indicate system-wide panic */ extern int errno; /* error number set by system library */ @@ -55,11 +56,16 @@ PUBLIC int main(int argc, char **argv) exit_server(); } continue; + case PANIC_DUMPS: + printf("Oops ... panic in %d. ", who); + printf("Hit F-keys for debug dumps or F12 to shut down.\n"); + sys_panic = TRUE; /* set flag to allow exit */ + continue; case FKEY_PRESSED: result = do_fkey_pressed(&m_in); break; default: - report("IS","warning, got illegal request from %d\n", m_in.m_source); + report("IS","warning, got illegal request from:", m_in.m_source); result = EINVAL; } @@ -79,7 +85,6 @@ PRIVATE void init_server(int argc, char **argv) /* Initialize the information service. */ int fkeys, sfkeys; int i, s; -#if DEAD_CODE struct sigaction sigact; /* Install signal handler. Ask PM to transform signal into message. */ @@ -88,7 +93,6 @@ PRIVATE void init_server(int argc, char **argv) sigact.sa_flags = 0; /* default behaviour */ if (sigaction(SIGTERM, &sigact, NULL) < 0) report("IS","warning, sigaction() failed", errno); -#endif /* Set key mappings. IS takes all of F1-F12 and Shift+F1-F6. */ fkeys = sfkeys = 0; diff --git a/servers/is/proto.h b/servers/is/proto.h index 5d27e0ae7..43f15cb69 100644 --- a/servers/is/proto.h +++ b/servers/is/proto.h @@ -6,6 +6,7 @@ _PROTOTYPE( int main, (int argc, char **argv) ); /* dmp.c */ _PROTOTYPE( int do_fkey_pressed, (message *m) ); _PROTOTYPE( void mapping_dmp, (void) ); +_PROTOTYPE( void reboot_dmp, (void) ); /* dmp_kernel.c */ _PROTOTYPE( void proctab_dmp, (void) ); @@ -28,3 +29,5 @@ _PROTOTYPE( void sigaction_dmp, (void) ); _PROTOTYPE( void dtab_dmp, (void) ); _PROTOTYPE( void fproc_dmp, (void) ); +/* dmp_rs.c */ +_PROTOTYPE( void rproc_dmp, (void) ); diff --git a/servers/pm/getset.c b/servers/pm/getset.c index 10f4166ec..20810574b 100644 --- a/servers/pm/getset.c +++ b/servers/pm/getset.c @@ -38,6 +38,8 @@ PUBLIC int do_getset() case GETPID: r = mproc[who].mp_pid; rmp->mp_reply.reply_res2 = mproc[rmp->mp_parent].mp_pid; + if (m_in.procnr >= 0 && m_in.procnr < NR_PROCS) + rmp->mp_reply.reply_res3 = mproc[m_in.procnr].mp_pid; break; case SETUID: diff --git a/servers/pm/main.c b/servers/pm/main.c index 66731e601..f2fec7e83 100644 --- a/servers/pm/main.c +++ b/servers/pm/main.c @@ -155,7 +155,9 @@ PRIVATE void pm_init() static char core_sigs[] = { SIGQUIT, SIGILL, SIGTRAP, SIGABRT, SIGEMT, SIGFPE, SIGUSR1, SIGSEGV, SIGUSR2 }; static char ign_sigs[] = { SIGCHLD, SIGWINCH }; + static char mess_sigs[] = { SIGTERM, SIGHUP, SIGABRT, SIGQUIT }; register struct mproc *rmp; + register int i; register char *sig_ptr; phys_clicks total_clicks, minix_clicks, free_clicks; message mess; @@ -209,19 +211,24 @@ PRIVATE void pm_init() strncpy(rmp->mp_name, ip->proc_name, PROC_NAME_LEN); rmp->mp_parent = RS_PROC_NR; rmp->mp_nice = get_nice_value(ip->priority); + sigemptyset(&rmp->mp_sig2mess); + sigemptyset(&rmp->mp_ignore); + sigemptyset(&rmp->mp_sigmask); + sigemptyset(&rmp->mp_catch); if (ip->proc_nr == INIT_PROC_NR) { /* user process */ rmp->mp_pid = INIT_PID; rmp->mp_flags |= IN_USE; - sigemptyset(&rmp->mp_ignore); } else { /* system process */ rmp->mp_pid = get_free_pid(); rmp->mp_flags |= IN_USE | DONT_SWAP | PRIV_PROC; - sigfillset(&rmp->mp_ignore); +#if DEAD_CODE + for (sig_ptr = mess_sigs; + sig_ptr < mess_sigs+sizeof(mess_sigs); + sig_ptr++) + sigaddset(&rmp->mp_sig2mess, *sig_ptr); +#endif } - sigemptyset(&rmp->mp_sigmask); - sigemptyset(&rmp->mp_catch); - sigemptyset(&rmp->mp_sig2mess); /* Get memory map for this process from the kernel. */ if ((s=get_mem_map(ip->proc_nr, rmp->mp_seg)) != OK) @@ -241,9 +248,11 @@ PRIVATE void pm_init() } printf(".\n"); /* last process done */ - /* Override some details. PM is somewhat special. */ - mproc[PM_PROC_NR].mp_pid = PM_PID; /* magically override pid */ - mproc[PM_PROC_NR].mp_parent = PM_PROC_NR; /* PM doesn't have parent */ + /* Override some details. INIT, PM, FS and RS are somewhat special. */ + mproc[PM_PROC_NR].mp_pid = PM_PID; /* PM has magic pid */ + mproc[RS_PROC_NR].mp_parent = INIT_PROC_NR; /* INIT is root */ + sigfillset(&mproc[PM_PROC_NR].mp_ignore); /* guard against signals */ + sigfillset(&mproc[FS_PROC_NR].mp_sig2mess); /* forward signals */ /* Tell FS that no more system processes follow and synchronize. */ mess.PR_PROC_NR = NONE; diff --git a/servers/pm/misc.c b/servers/pm/misc.c index 26828cf86..0364e5975 100644 --- a/servers/pm/misc.c +++ b/servers/pm/misc.c @@ -95,7 +95,7 @@ PUBLIC int do_getprocnr() int key_len; int s; - if (m_in.pid >= 0) { /* lookup process by pid */ + if (m_in.pid >= 0) { /* lookup process by pid */ for (rmp = &mproc[0]; rmp < &mproc[NR_PROCS]; rmp++) { if ((rmp->mp_flags & IN_USE) && (rmp->mp_pid==m_in.pid)) { mp->mp_reply.procnr = (int) (rmp - mproc); @@ -103,7 +103,7 @@ PUBLIC int do_getprocnr() } } return(ESRCH); - } else if (m_in.namelen > 0) { /* lookup process by name */ + } else if (m_in.namelen > 0) { /* lookup process by name */ key_len = MIN(m_in.namelen, PROC_NAME_LEN); if (OK != (s=sys_datacopy(who, (vir_bytes) m_in.addr, SELF, (vir_bytes) search_key, key_len))) @@ -117,8 +117,9 @@ PUBLIC int do_getprocnr() } } return(ESRCH); - } else { /* return own process number */ + } else { /* return own/parent process number */ mp->mp_reply.procnr = who; + mp->mp_reply.pprocnr = mp->mp_parent; } return(OK); } @@ -159,8 +160,8 @@ PUBLIC int do_reboot() return(EINVAL); } + tell_fs(REBOOT, 0, 0, 0); /* tell FS to synchronize */ check_sig(-1, SIGKILL); /* kill all processes except init */ - tell_fs(REBOOT,0,0,0); /* tell FS to prepare for shutdown */ /* Ask the kernel to abort. All system services, including the PM, will * get a HARD_STOP notification. Await the notification in the main loop. diff --git a/servers/pm/param.h b/servers/pm/param.h index 15b0e4fd2..dbcd9d36f 100644 --- a/servers/pm/param.h +++ b/servers/pm/param.h @@ -7,6 +7,7 @@ #define namelen m1_i2 #define pid m1_i1 #define procnr m1_i1 +#define pprocnr m1_i2 #define seconds m1_i1 #define sig m6_i1 #define stack_bytes m1_i2 @@ -41,6 +42,7 @@ /* The following names are synonyms for the variables in a reply message. */ #define reply_res m_type #define reply_res2 m2_i1 +#define reply_res3 m2_i2 #define reply_ptr m2_p1 #define reply_mask m2_l1 #define reply_trace m2_l2 diff --git a/servers/pm/signal.c b/servers/pm/signal.c index b2bc1a649..dee78d546 100644 --- a/servers/pm/signal.c +++ b/servers/pm/signal.c @@ -257,12 +257,6 @@ sigset_t sig_map; id = 0; break; /* broadcast to process group */ case SIGKILL: id = -1; break; /* broadcast to all except INIT */ -#if DEAD_CODE - case SIGALRM: - if ((rmp->mp_flags & ALARM_ON) == 0) continue; - rmp->mp_flags &= ~ALARM_ON; - /* fall through */ -#endif default: id = proc_id; break; diff --git a/servers/pm/utility.c b/servers/pm/utility.c index 1935279d2..866db003b 100644 --- a/servers/pm/utility.c +++ b/servers/pm/utility.c @@ -113,9 +113,21 @@ int num; /* number to go with it */ * defined constant. The process manager decides to shut down. This results * in a HARD_STOP notification to all system processes to allow local cleanup. */ + message m; + int s; + + /* Switch to primary console and print panic message. */ + check_sig(mproc[TTY_PROC_NR].mp_pid, SIGTERM); printf("PM panic (%s): %s", who, mess); if (num != NO_NUM) printf(": %d",num); printf("\n"); + + /* Allow for debug dumps if the IS server is available. */ + m.m_type = PANIC_DUMPS; + if (OK == (s= nb_send(11, &m))) { + return; /* IS responsible for exit */ + } + printf("Shutting down: IS is not answering: %d\n", s); sys_abort(RBT_PANIC); } @@ -135,9 +147,13 @@ int what, p1, p2, p3; * tell_fs(SETUID, proc, realuid, effuid) * tell_fs(UNPAUSE, proc, signr, 0) * tell_fs(STIME, time, 0, 0) + * Ignore this call if the FS is already dead, e.g. on shutdown. */ message m; + if ((mproc[FS_PROC_NR].mp_flags & (IN_USE|ZOMBIE)) != IN_USE) + return; + m.tell_fs_arg1 = p1; m.tell_fs_arg2 = p2; m.tell_fs_arg3 = p3; diff --git a/servers/rs/Makefile b/servers/rs/Makefile index ae8b16734..296e1fd00 100644 --- a/servers/rs/Makefile +++ b/servers/rs/Makefile @@ -17,7 +17,7 @@ UTIL_LIBS = -lsys LIBS = -lsys -lsysutil UTIL_OBJ = service.o -OBJ = rs.o manager.o +OBJ = main.o manager.o # build local binary all build: $(SERVER) $(UTIL) diff --git a/servers/rs/main.c b/servers/rs/main.c new file mode 100644 index 000000000..d168ae9d4 --- /dev/null +++ b/servers/rs/main.c @@ -0,0 +1,226 @@ +/* Reincarnation Server. This servers starts new system services and detects + * they are exiting. In case of errors, system services can be restarted. + * The RS server periodically checks the status of all registered services + * services to see whether they are still alive. The system services are + * expected to periodically send a heartbeat message. + * + * Created: + * Jul 22, 2005 by Jorrit N. Herder + */ +#include "rs.h" +#include +#include "../../kernel/const.h" +#include "../../kernel/type.h" + +/* Declare some local functions. */ +FORWARD _PROTOTYPE(void init_server, (void) ); +FORWARD _PROTOTYPE(void get_work, (message *m) ); +FORWARD _PROTOTYPE(void reply, (int whom, int result) ); +FORWARD _PROTOTYPE(int do_getsysinfo, (message *m) ); + +/* Data buffers to retrieve info during initialization. */ +PRIVATE struct boot_image image[NR_BOOT_PROCS]; +PUBLIC struct dmap dmap[NR_DEVICES]; + +/*===========================================================================* + * main * + *===========================================================================*/ +PUBLIC int main(void) +{ +/* This is the main routine of this service. The main loop consists of + * three major activities: getting new work, processing the work, and + * sending the reply. The loop never terminates, unless a panic occurs. + */ + message m; /* request message */ + int call_nr, who; /* call number and caller */ + int result; /* result to return */ + sigset_t sigset; /* system signal set */ + int s; + + /* Initialize the server, then go to work. */ + init_server(); + + /* Main loop - get work and do it, forever. */ + while (TRUE) { + + /* Wait for request message. */ + get_work(&m); + who = m.m_source; + call_nr = m.m_type; + + /* Now determine what to do. Three types of requests are expected: + * - Heartbeat messages (notifications from registered system services) + * - System notifications (POSIX signals or synchronous alarm) + * - User requests (control messages to manage system services) + */ + + /* Notification messages are control messages and do not need a reply. + * These include heartbeat messages and system notifications. + */ + if (m.m_type & NOTIFY_MESSAGE) { + switch (call_nr) { + case SYN_ALARM: + do_period(&m); /* check drivers status */ + continue; /* no reply is expected */ + case SYS_SIG: + sigset = (sigset_t) m.NOTIFY_ARG; + if (sigismember(&sigset, SIGCHLD)) { + do_exit(&m); + } + if (sigismember(&sigset, SIGTERM) || + sigismember(&sigset, SIGKSTOP)) { + /* Prevent restarting services. */ + do_shutdown(NULL); + } + continue; /* no reply is expected */ + default: /* heartbeat notification */ + printf("Got heartbeat from %d\n", who); + if (rproc_ptr[who] != NULL) /* mark heartbeat time */ + rproc_ptr[who]->r_alive_tm = m.NOTIFY_TIMESTAMP; + } + } + + /* If this is not a notification message, it is a normal request. + * Handle the request and send a reply to the caller. + */ + else { + switch(call_nr) { + case SRV_UP: + result = do_start(&m); + break; + case SRV_DOWN: + result = do_stop(&m); + break; + case SRV_SHUTDOWN: + result = do_shutdown(&m); + break; + case GETSYSINFO: + printf("RS got GETSYSINFO request from %d\n", m.m_source); + result = do_getsysinfo(&m); + break; + default: + printf("Warning, RS got unexpected request %d from %d\n", + m.m_type, m.m_source); + result = EINVAL; + } + + /* Finally send reply message, unless disabled. */ + if (result != EDONTREPLY) { + reply(who, result); + } + } + } +} + + +/*===========================================================================* + * init_server * + *===========================================================================*/ +PRIVATE void init_server(void) +{ +/* Initialize the reincarnation server. */ + struct sigaction sa; + struct boot_image *ip; + int s,t; + + /* Install signal handlers. Ask PM to transform signal into message. */ + sa.sa_handler = SIG_MESS; + sigemptyset(&sa.sa_mask); + sa.sa_flags = 0; + if (sigaction(SIGCHLD,&sa,NULL)<0) panic("RS","sigaction failed", errno); + if (sigaction(SIGTERM,&sa,NULL)<0) panic("RS","sigaction failed", errno); + if (sigaction(SIGABRT,&sa,NULL)<0) panic("RS","sigaction failed", errno); + if (sigaction(SIGHUP, &sa,NULL)<0) panic("RS","sigaction failed", errno); + + /* Initialize the system process table. Use the boot image from the kernel + * and the device map from the FS to gather all needed information. + */ + if ((s = sys_getimage(image)) != OK) + panic("RS","warning: couldn't get copy of image table", s); + if ((s = getsysinfo(FS_PROC_NR, SI_DMAP_TAB, dmap)) < 0) + panic("RS","warning: couldn't get copy of dmap table", errno); + + /* Change working directory to /sbin, where the binaries for the programs + * in the system image are. + */ + chdir("/sbin/"); + for (s=0; s< NR_BOOT_PROCS; s++) { + ip = &image[s]; + if (ip->proc_nr >= 0) { + nr_in_use ++; + rproc[s].r_flags = IN_USE; + rproc[s].r_proc_nr = ip->proc_nr; + rproc[s].r_pid = getnpid(ip->proc_nr); + for(t=0; t< NR_DEVICES; t++) + if (dmap[t].dmap_driver == ip->proc_nr) + rproc[s].r_dev_nr = t; + strcpy(rproc[s].r_cmd, ip->proc_name); + rproc[s].r_argc = 1; + rproc[s].r_argv[0] = rproc[s].r_cmd; + rproc[s].r_argv[1] = NULL; + } + } + + /* Set alarm to periodically check driver status. */ + if (OK != (s=sys_setalarm(HZ, 0))) + panic("RS", "couldn't set alarm", s); + +} + + +/*===========================================================================* + * do_getsysinfo * + *===========================================================================*/ +PRIVATE int do_getsysinfo(m_ptr) +message *m_ptr; +{ + vir_bytes src_addr, dst_addr; + int dst_proc; + size_t len; + int s; + + switch(m_ptr->m1_i1) { + case SI_PROC_TAB: + src_addr = (vir_bytes) rproc; + len = sizeof(struct rproc) * 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); +} + +/*===========================================================================* + * get_work * + *===========================================================================*/ +PRIVATE void get_work(m_in) +message *m_in; /* pointer to message */ +{ + int s; /* receive status */ + if (OK != (s=receive(ANY, m_in))) /* wait for message */ + panic("RS","receive failed", s); +} + + +/*===========================================================================* + * reply * + *===========================================================================*/ +PRIVATE void reply(who, result) +int who; /* replyee */ +int result; /* report result */ +{ + message m_out; /* reply message */ + int s; /* send status */ + + m_out.m_type = result; /* build reply message */ + if (OK != (s=send(who, &m_out))) /* send the message */ + panic("RS", "unable to send reply", s); +} + + + diff --git a/servers/rs/manager.c b/servers/rs/manager.c index 1a300fa20..214e06f82 100644 --- a/servers/rs/manager.c +++ b/servers/rs/manager.c @@ -1,10 +1,4 @@ -/* This file contains procedures to manage the system processes. - * - * The entry points into this file are - * do_start: - * do_stop: - * do_exit: a child of this server exited - * +/* * Changes: * Jul 22, 2005: Created (Jorrit N. Herder) */ @@ -15,88 +9,81 @@ #include #include -extern int errno; +/* Allocate variables. */ +struct rproc rproc[NR_SYS_PROCS]; /* system process table */ +struct rproc *rproc_ptr[NR_PROCS]; /* mapping for fast access */ +int nr_in_use; /* number of services */ +extern int errno; /* error status */ -#define EXEC_FAILED 49 /* arbitrary, recognizable status */ -#define MAX_PATH_LEN 256 /* maximum path string length */ -#define MAX_ARGS_LEN 4096 /* maximum argument string length */ -#define MAX_ARG_COUNT 1 /* parsed arguments count */ +/* Prototypes for internal functions that do the hard work. */ +FORWARD _PROTOTYPE( int start_service, (struct rproc *rp) ); +FORWARD _PROTOTYPE( int stop_service, (struct rproc *rp) ); -PRIVATE char command[MAX_PATH_LEN+1]; -PRIVATE char arg_buf[MAX_ARGS_LEN+1]; +PRIVATE int shutting_down = FALSE; + +#define EXEC_FAILED 49 /* recognizable status */ /*===========================================================================* * do_start * *===========================================================================*/ -PUBLIC int do_start(message *m_ptr) +PUBLIC int do_start(m_ptr) +message *m_ptr; /* request message pointer */ { - message m; - int child_proc_nr; - int major_nr; - enum dev_style dev_style; - pid_t child_pid; - char *args[MAX_ARG_COUNT+1]; - int s; +/* A request was made to start a new system service. Dismember the request + * message and gather all information needed to start the service. Starting + * is done by a helper routine. + */ + 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 */ + enum dev_style dev_style; /* device style */ + int s; /* status variable */ - /* Obtain command name and parameters. */ - if (m_ptr->SRV_PATH_LEN > MAX_PATH_LEN) return(E2BIG); - if (OK != (s=sys_datacopy(m_ptr->m_source, (vir_bytes) m_ptr->SRV_PATH_ADDR, - SELF, (vir_bytes) command, m_ptr->SRV_PATH_LEN))) return(s); - command[m_ptr->SRV_PATH_LEN] = '\0'; - if (command[0] != '/') return(EINVAL); - - args[0] = command; - if (m_ptr->SRV_ARGS_LEN > 0) { - if (m_ptr->SRV_ARGS_LEN > MAX_ARGS_LEN) return(E2BIG); - if (OK != (s=sys_datacopy(m_ptr->m_source, (vir_bytes) m_ptr->SRV_ARGS_ADDR, - SELF, (vir_bytes) arg_buf, m_ptr->SRV_ARGS_LEN))) return(s); - arg_buf[m_ptr->SRV_ARGS_LEN] = '\0'; - args[1] = &arg_buf[0]; - args[2] = NULL; - } else { - args[1] = NULL; + /* 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 & IN_USE) /* check if available */ + break; } - - /* Now try to execute the new 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. + 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 ((s = _taskcall(PM_PROC_NR, FORK, &m)) < 0) /* use raw interface */ - report("SM", "_taskcall to PM failed", s); /* to get both */ - child_pid = m.m_type; /* - child's pid */ - child_proc_nr = m.PR_PROC_NR; /* - process nr */ + if (m_ptr->SRV_CMD_LEN > MAX_COMMAND_LEN) return(E2BIG); + if (OK!=(s=sys_datacopy(m_ptr->m_source, (vir_bytes) m_ptr->SRV_CMD_ADDR, + SELF, (vir_bytes) rp->r_cmd, m_ptr->SRV_CMD_LEN))) return(s); + rp->r_cmd[m_ptr->SRV_CMD_LEN] = '\0'; /* ensure it is terminated */ + if (rp->r_cmd[0] != '/') return(EINVAL); /* insist on absolute path */ - /* Now branch for parent and child process, and check for error. */ - switch(child_pid) { /* see fork(2) */ - case 0: /* child process */ - execve(command, args, NULL); /* POSIX exec */ - report("SM", "warning, exec() failed", errno); /* shouldn't happen */ - exit(EXEC_FAILED); /* terminate child */ - break; - case -1: /* fork failed */ - report("SM", "warning, fork() failed", errno); /* shouldn't happen */ - return(errno); - default: /* parent process */ - if ((major_nr = m_ptr->SRV_DEV_MAJOR) > 0) { /* set driver map */ - dev_style = STYLE_DEV; - if ((s=mapdriver(child_proc_nr, major_nr, dev_style)) < 0) { - -#if VERBOSE - printf("SM: '%s %s', major %d, pid %d, proc_nr %d", - command, arg_buf, major_nr, child_pid, child_proc_nr); -#endif - report("SM", "couldn't map driver", errno); - } + /* 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 */ } - if ((s = _taskcall(SYSTEM, SYS_PRIVCTL, &m)) < 0) /* set privileges */ - report("SM", "_taskcall to SYSTEM failed", s); /* to let child run */ -#if VERBOSE - printf("SM: started '%s %s', major %d, pid %d, proc_nr %d", - command, arg_buf, major_nr, child_pid, child_proc_nr); -#endif - /* update tables */ + cmd_ptr ++; /* continue parsing */ } - return(OK); + rp->r_argv[arg_count] = NULL; /* end with NULL pointer */ + rp->r_argc = arg_count; + + /* Check if a heartbeat period was given. */ + rp->r_period = m_ptr->SRV_PERIOD; + rp->r_dev_nr = m_ptr->SRV_DEV_MAJOR; + rp->r_dev_style = STYLE_DEV; + + /* All information was gathered. Now try to start the system service. */ + return(start_service(rp)); } @@ -105,19 +92,42 @@ PUBLIC int do_start(message *m_ptr) *===========================================================================*/ PUBLIC int do_stop(message *m_ptr) { - return(ENOSYS); + register struct rproc *rp; + pid_t pid = (pid_t) m_ptr->SRV_PID; + + for (rp=BEG_RPROC_ADDR; rpr_flags & IN_USE && rp->r_pid == pid) { + printf("stopping %d (%d)\n", pid, m_ptr->SRV_PID); + stop_service(rp); + return(OK); + } + } + printf("not found %d (%d)\n", pid, m_ptr->SRV_PID); + return(ESRCH); +} + + +/*===========================================================================* + * do_shutdown * + *===========================================================================*/ +PUBLIC int do_shutdown(message *m_ptr) +{ + /* Set flag so that RS server knows services shouldn't be restarted. */ + shutting_down = TRUE; + return(OK); } /*===========================================================================* * do_exit * *===========================================================================*/ -PUBLIC int do_exit(message *m_ptr) +PUBLIC void do_exit(message *m_ptr) { + register struct rproc *rp; pid_t exit_pid; int exit_status; #if VERBOSE - printf("SM: got SIGCHLD signal, doing wait to get exited child.\n"); + printf("RS: got SIGCHLD signal, doing wait to get exited child.\n"); #endif /* See which child exited and what the exit status is. This is done in a @@ -128,16 +138,192 @@ PUBLIC int do_exit(message *m_ptr) while ( (exit_pid = waitpid(-1, &exit_status, WNOHANG)) != 0 ) { #if VERBOSE - printf("SM: pid %d,", exit_pid); - if (WIFSIGNALED(exit_status)) { - printf("killed, signal number %d\n", WTERMSIG(exit_status)); - } else if (WIFEXITED(exit_status)) { - printf("normal exit, status %d\n", WEXITSTATUS(exit_status)); - } + printf("RS: proc %d, pid %d,", rp->r_proc_nr, exit_pid); + if (WIFSIGNALED(exit_status)) { + printf("killed, signal number %d\n", WTERMSIG(exit_status)); + } + else if (WIFEXITED(exit_status)) { + printf("normal exit, status %d\n", WEXITSTATUS(exit_status)); + } #endif + /* Search the system process table to see who exited. + * This should always succeed. + */ + for (rp=BEG_RPROC_ADDR; rpr_flags & IN_USE) && rp->r_pid == exit_pid) { + + printf("Slot found!\n"); + rproc_ptr[rp->r_proc_nr] = NULL; /* invalidate */ + + if ((rp->r_flags & EXIT_PENDING) || shutting_down) { + printf("Expected exit. Doing nothing.\n"); + rp->r_flags = 0; /* release slot */ + rproc_ptr[rp->r_proc_nr] = NULL; + } + else if (WIFEXITED(exit_status) && + WEXITSTATUS(exit_status) == EXEC_FAILED) { + printf("Exit because EXEC() failed. Doing nothing.\n"); + rp->r_flags = 0; /* release slot */ + } + else { + printf("Unexpected exit. Restarting %s\n", rp->r_cmd); + start_service(rp); /* restart */ + } + break; + } + } } - return(OK); } +/*===========================================================================* + * do_period * + *===========================================================================*/ +PUBLIC void do_period(m_ptr) +message *m_ptr; +{ + register struct rproc *rp; + clock_t now = m_ptr->NOTIFY_TIMESTAMP; + int s; + + /* Search system services table. Only check slots that are in use. */ + for (rp=BEG_RPROC_ADDR; rpr_flags & IN_USE) { + + /* If the service has a period assigned check its status. */ + if (rp->r_period > 0) { + + /* Check if an answer to a status request is still pending. If + * the driver 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*rp->r_period) { +#if VERBOSE + printf("RS: service %d reported late\n", rp->r_proc_nr); +#endif + 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 > rp->r_period) { +#if VERBOSE + printf("RS: status request sent to %d\n", rp->r_proc_nr); +#endif + notify(rp->r_proc_nr); /* request status */ + rp->r_check_tm = now; /* mark time */ + } + } + + /* If the service was signaled with a SIGTERM and fails to respond, + * kill the system service with a SIGKILL signal. + */ + if (rp->r_stop_tm > 0 && now - rp->r_stop_tm > 2*HZ) { + kill(rp->r_pid, SIGKILL); /* terminate */ + } + } + } + + /* Reschedule a synchronous alarm for the next period. */ + if (OK != (s=sys_setalarm(HZ, 0))) + panic("RS", "couldn't set alarm", s); +} + + +/*===========================================================================* + * start_service * + *===========================================================================*/ +PRIVATE int start_service(rp) +struct rproc *rp; +{ +/* 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; /* child process slot */ + pid_t child_pid; /* child's process id */ + int s; + message m; + + /* Now fork and branch for parent and child process (and check for error). */ + 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 */ + execve(rp->r_argv[0], rp->r_argv, NULL); /* POSIX execute */ + report("RS", "warning, exec() failed", errno); /* shouldn't happen */ + exit(EXEC_FAILED); /* terminate child */ + + default: /* parent process */ + child_proc_nr = getnprocnr(child_pid); /* get child slot */ + break; /* continue below */ + } + + /* Only the parent process (the RS server) gets to this point. The child + * is still inhibited from running because it's privilege structure is + * not yet set. First try to set the device driver mapping at the FS. + */ + if (rp->r_dev_nr > 0) { /* set driver map */ + if ((s=mapdriver(child_proc_nr, rp->r_dev_nr, rp->r_dev_style)) < 0) { + report("RS", "couldn't map driver", errno); + kill(child_pid, SIGKILL); /* kill driver */ + rp->r_flags |= EXIT_PENDING; /* expect exit */ + return(s); /* return error */ + } + } + + /* The device driver mapping has been set, or the service was not a driver. + * Now, set the privilege structure for the child process to let is run. + * This should succeed: we tested number in use above. + */ + m.PR_PROC_NR = child_proc_nr; + if ((s = _taskcall(SYSTEM, SYS_PRIVCTL, &m)) < 0) { /* set privileges */ + report("RS","call to SYSTEM failed", s); /* to let child run */ + kill(child_pid, SIGKILL); /* kill driver */ + rp->r_flags |= EXIT_PENDING; /* expect exit */ + return(s); /* return error */ + } + +#if VERBOSE + printf("RS: started '%s', major %d, pid %d, proc_nr %d", + rp->r_cmd, rp->r_dev_nr, child_pid, child_proc_nr); +#endif + + /* The system service now has been successfully started. Update the rest + * of the system process table that is maintain by the RS server. The only + * thing that can go wrong now, is that execution fails at the child. If + * that's the case, the child will exit. + */ + rp->r_flags = IN_USE; /* mark slot in use */ + rp->r_proc_nr = child_proc_nr; /* set child details */ + rp->r_pid = child_pid; + rp->r_check_tm = 0; /* not check yet */ + getuptime(&rp->r_alive_tm); /* currently alive */ + rp->r_stop_tm = 0; /* not exiting yet */ + rproc_ptr[child_proc_nr] = rp; /* mapping for fast access */ + return(OK); +} +/*===========================================================================* + * stop_service * + *===========================================================================*/ +PRIVATE int stop_service(rp) +struct rproc *rp; +{ + printf("RS tries to stop %s (pid %d)\n", rp->r_cmd, rp->r_pid); + /* 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. + */ + rp->r_flags |= EXIT_PENDING; /* expect exit */ + kill(rp->r_pid, SIGTERM); /* first try friendly */ + getuptime(&rp->r_stop_tm); /* record current time */ +} diff --git a/servers/rs/proto.h b/servers/rs/proto.h index d118c3c4a..26e7cc115 100644 --- a/servers/rs/proto.h +++ b/servers/rs/proto.h @@ -1,11 +1,13 @@ /* Function prototypes. */ -/* sm.c */ +/* main.c */ _PROTOTYPE( int main, (void)); /* manager.c */ -_PROTOTYPE( int do_exit, (message *m)); _PROTOTYPE( int do_start, (message *m)); _PROTOTYPE( int do_stop, (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/rproc.h b/servers/rs/rproc.h new file mode 100644 index 000000000..b60fb63f4 --- /dev/null +++ b/servers/rs/rproc.h @@ -0,0 +1,44 @@ +/* This table has one slot per system process. It contains information for + * servers and driver needed by the reincarnation server to keep track of + * each process' status. + */ + +/* Space reserved for program and arguments. */ +#define MAX_COMMAND_LEN 512 /* maximum argument string length */ +#define MAX_NR_ARGS 4 /* maximum number of arguments */ + +/* 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; /* process slot number */ + pid_t r_pid; /* process id */ + dev_t r_dev_nr; /* major device number */ + int r_dev_style; /* device style */ + + unsigned r_flags; /* status and policy flags */ + + long r_period; /* heartbeat period (or zero) */ + clock_t r_check_tm; /* timestamp of last check */ + clock_t r_alive_tm; /* timestamp of last heartbeat */ + clock_t r_stop_tm; /* timestamp of SIGTERM signal */ + + char r_cmd[MAX_COMMAND_LEN]; /* raw command plus arguments */ + char *r_argv[MAX_NR_ARGS+2]; /* parsed arguments vector */ + int r_argc; /* number of arguments */ +} rproc[NR_SYS_PROCS]; + +/* Mapping for fast access to the system process table. */ +extern struct rproc *rproc_ptr[NR_PROCS]; +extern int nr_in_use; + +/* Flag values. */ +#define IN_USE 0x001 /* set when process slot is in use */ +#define EXIT_PENDING 0x002 /* set when exit is expected */ +#define STAT_PENDING 0x003 /* set when heartbeat is expected */ + +/* Magic process table addresses. */ +#define BEG_RPROC_ADDR (&rproc[0]) +#define END_RPROC_ADDR (&rproc[NR_SYS_PROCS]) +#define NIL_RPROC ((struct mproc *) 0) + diff --git a/servers/rs/rs.c b/servers/rs/rs.c deleted file mode 100644 index 31cf35648..000000000 --- a/servers/rs/rs.c +++ /dev/null @@ -1,135 +0,0 @@ -/* Reincarnation Server. This servers starts new system services and detects - * they are exiting. In case of errors, system services can be restarted. - * - * Created: - * Jul 22, 2005 by Jorrit N. Herder - */ - -#include "rs.h" - -/* Set debugging level to 0, 1, or 2 to see no, some, all debug output. */ -#define DEBUG_LEVEL 1 -#define DPRINTF if (DEBUG_LEVEL > 0) printf - -/* Allocate space for the global variables. */ -message m_in; /* the input message itself */ -message m_out; /* the output message used for reply */ -int who; /* caller's proc number */ -int callnr; /* system call number */ - -/* Declare some local functions. */ -FORWARD _PROTOTYPE(void init_server, (void) ); -FORWARD _PROTOTYPE(void get_work, (void) ); -FORWARD _PROTOTYPE(void reply, (int whom, int result) ); - -/*===========================================================================* - * main * - *===========================================================================*/ -PUBLIC int main(void) -{ -/* This is the main routine of this service. The main loop consists of - * three major activities: getting new work, processing the work, and - * sending the reply. The loop never terminates, unless a panic occurs. - */ - int result; - sigset_t sigset; - - /* Initialize the server, then go to work. */ - init_server(); - - /* Main loop - get work and do it, forever. */ - while (TRUE) { - - /* Wait for incoming message, sets 'callnr' and 'who'. */ - get_work(); - - switch (callnr) { - case SYS_SIG: - /* Signals are passed by means of a notification message from SYSTEM. - * Extract the map of pending signals from the notification argument. - */ - sigset = (sigset_t) m_in.NOTIFY_ARG; - - if (sigismember(&sigset, SIGCHLD)) { - /* A child of this server exited. Take action. */ - do_exit(&m_in); - } - if (sigismember(&sigset, SIGUSR1)) { - do_start(&m_in); - } - if (sigismember(&sigset, SIGTERM)) { - /* Nothing to do on shutdown. */ - } - if (sigismember(&sigset, SIGKSTOP)) { - /* Nothing to do on shutdown. */ - } - continue; - case SRV_UP: - result = do_start(&m_in); - break; - case SRV_DOWN: - result = do_stop(&m_in); - break; - default: - printf("Warning, RS got unexpected request %d from %d\n", - m_in.m_type, m_in.m_source); - result = EINVAL; - } - - /* Finally send reply message, unless disabled. */ - if (result != EDONTREPLY) { - reply(who, result); - } - } -} - - -/*===========================================================================* - * init_server * - *===========================================================================*/ -PRIVATE void init_server(void) -{ -/* Initialize the reincarnation server. */ - struct sigaction sa; - - /* Install signal handlers. Ask PM to transform signal into message. */ - sa.sa_handler = SIG_MESS; - sigemptyset(&sa.sa_mask); - sa.sa_flags = 0; - if (sigaction(SIGCHLD, &sa, NULL)<0) panic("RS","sigaction failed", errno); - if (sigaction(SIGTERM, &sa, NULL)<0) panic("RS","sigaction failed", errno); - if (sigaction(SIGABRT, &sa, NULL)<0) panic("RS","sigaction failed", errno); - if (sigaction(SIGHUP, &sa, NULL)<0) panic("RS","sigaction failed", errno); -} - - -/*===========================================================================* - * get_work * - *===========================================================================*/ -PRIVATE void get_work() -{ - int status = 0; - status = receive(ANY, &m_in); /* this blocks until message arrives */ - if (OK != status) - panic("RS","failed to receive message!", status); - who = m_in.m_source; /* message arrived! set sender */ - callnr = m_in.m_type; /* set function call number */ -} - - -/*===========================================================================* - * reply * - *===========================================================================*/ -PRIVATE void reply(who, result) -int who; /* destination */ -int result; /* report result to replyee */ -{ - int send_status; - m_out.m_type = result; /* build reply message */ - send_status = send(who, &m_out); /* send the message */ - if (OK != send_status) - panic("RS", "unable to send reply!", send_status); -} - - - diff --git a/servers/rs/rs.h b/servers/rs/rs.h index a4c51aef2..edd81fb51 100644 --- a/servers/rs/rs.h +++ b/servers/rs/rs.h @@ -7,13 +7,14 @@ #define _SYSTEM 1 /* get OK and negative error codes */ #define _MINIX 1 /* tell headers to include MINIX stuff */ -#define VERBOSE 0 /* display diagnostics */ +#define VERBOSE 1 /* display diagnostics */ #include #include #include #include #include +#include #include #include @@ -32,4 +33,5 @@ #include #include "proto.h" +#include "rproc.h" diff --git a/servers/rs/service.c b/servers/rs/service.c index 8719ca9de..c2a5b120f 100644 --- a/servers/rs/service.c +++ b/servers/rs/service.c @@ -23,6 +23,7 @@ PRIVATE char *known_requests[] = { "up", "down", + "shutdown", "catch for illegal requests" }; #define ILLEGAL_REQUEST sizeof(known_requests)/sizeof(char *) @@ -38,42 +39,49 @@ extern int errno; #define ARG_NAME 0 /* own application name */ #define ARG_REQUEST 1 /* request to perform */ #define ARG_PATH 2 /* binary of system service */ +#define ARG_PID 2 /* pid of system service */ -#define MIN_ARG_COUNT 3 /* minimum number of arguments */ +#define MIN_ARG_COUNT 2 /* require an action */ #define ARG_ARGS "-args" /* list of arguments to be passed */ #define ARG_DEV "-dev" /* major device number for drivers */ #define ARG_PRIV "-priv" /* required privileges */ +#define ARG_PERIOD "-period" /* heartbeat period in ticks */ /* 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_path; PRIVATE char *req_args; PRIVATE int req_major; +PRIVATE long req_period; PRIVATE char *req_priv; +/* Buffer to build "/command arg1 arg2 ..." string to pass to RS server. */ +PRIVATE char command[4096]; + /* An error occurred. Report the problem, print the usage, and exit. */ PRIVATE void print_usage(char *app_name, char *problem) { printf("Warning, %s\n", problem); printf("Usage:\n"); - printf(" %s [%s ] [%s ]\n", - app_name, ARG_ARGS, ARG_DEV); + printf(" %s up [%s ] [%s ] [%s ]\n", + app_name, ARG_ARGS, ARG_DEV, ARG_PERIOD); + printf(" %s down \n", app_name); + printf(" %s shutdown\n", app_name); printf("\n"); } -/* An unexpected, unrecoverable error occurred. Report and exit. +/* A request to the RS server failed. Report and exit. */ -PRIVATE void panic(char *app_name, char *mess, int num) +PRIVATE void failure(int num) { - printf("Panic in %s: %s", app_name, mess); - if (num != NO_NUM) printf(": %d", num); - printf("\n"); - exit(EGENERIC); + printf("Request to RS failed: %s (%d)\n", strerror(num), num); + exit(num); } @@ -83,10 +91,12 @@ PRIVATE void panic(char *app_name, char *mess, int num) PRIVATE int parse_arguments(int argc, char **argv) { struct stat stat_buf; + char *hz; + int req_nr; int i; /* Verify argument count. */ - if (! argc >= MIN_ARG_COUNT) { + if (argc < MIN_ARG_COUNT) { print_usage(argv[ARG_NAME], "wrong number of arguments"); exit(EINVAL); } @@ -100,52 +110,85 @@ PRIVATE int parse_arguments(int argc, char **argv) exit(ENOSYS); } - /* Verify the name of the binary of the system service. */ - req_path = argv[ARG_PATH]; - if (req_path[0] != '/') { - print_usage(argv[ARG_NAME], "binary should be absolute path"); - exit(EINVAL); - } - if (stat(req_path, &stat_buf) == -1) { - print_usage(argv[ARG_NAME], "couldn't get status of binary"); - exit(errno); - } - if (! (stat_buf.st_mode & S_IFREG)) { - print_usage(argv[ARG_NAME], "binary is not a regular file"); - exit(EINVAL); - } + req_nr = SRV_RQ_BASE + req_type; + if (req_nr == SRV_UP) { + + /* Verify argument count. */ + if (argc - 1 < ARG_PATH) { + print_usage(argv[ARG_NAME], "action requires a binary to start"); + exit(EINVAL); + } - /* Check optional arguments that come in pairs like "-args arglist". */ - for (i=MIN_ARG_COUNT; i> MAJOR) & BYTE; + } + else if (strcmp(argv[i], ARG_ARGS)==0) { + req_priv = argv[i+1]; } - if ( ! (stat_buf.st_mode & (S_IFBLK | S_IFCHR))) { - print_usage(argv[ARG_NAME], "special file is not a device node"); + else { + print_usage(argv[ARG_NAME], "unknown optional argument given"); exit(EINVAL); - } - req_major = (stat_buf.st_rdev >> MAJOR) & BYTE; + } } - else if (strcmp(argv[i], ARG_ARGS)==0) { - req_priv = argv[i+1]; + } + else if (req_nr == SRV_DOWN) { + + /* Verify argument count. */ + if (argc - 1 < ARG_PID) { + print_usage(argv[ARG_NAME], "action requires a pid to stop"); + exit(EINVAL); } - else { - print_usage(argv[ARG_NAME], "unknown optional argument given"); + if (! (req_pid = atoi(argv[ARG_PID])) > 0) { + print_usage(argv[ARG_NAME], "pid must be greater than zero"); exit(EINVAL); } + } + else if (req_nr == SRV_SHUTDOWN) { + /* no extra arguments required */ } /* Return the request number if no error were found. */ - return(i); + return(req_nr); } @@ -169,17 +212,29 @@ PUBLIC int main(int argc, char **argv) */ switch(req_type+SRV_RQ_BASE) { case SRV_UP: - m.SRV_PATH_ADDR = req_path; - m.SRV_PATH_LEN = strlen(req_path); - m.SRV_ARGS_ADDR = req_args; - m.SRV_ARGS_LEN = strlen(req_args); + /* 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); + + /* Build request message and send the request. */ + m.SRV_CMD_ADDR = command; + m.SRV_CMD_LEN = strlen(command); m.SRV_DEV_MAJOR = req_major; + m.SRV_PERIOD = req_period; if (OK != (s=_taskcall(RS_PROC_NR, SRV_UP, &m))) - panic(argv[ARG_NAME], "sendrec to manager server failed", s); + failure(s); result = m.m_type; break; case SRV_DOWN: - case SRV_STATUS: + m.SRV_PID = req_pid; + if (OK != (s=_taskcall(RS_PROC_NR, SRV_DOWN, &m))) + failure(s); + break; + case SRV_SHUTDOWN: + if (OK != (s=_taskcall(RS_PROC_NR, SRV_SHUTDOWN, &m))) + failure(s); + break; default: print_usage(argv[ARG_NAME], "request is not yet supported"); result = EGENERIC; -- 2.44.0