source: trunk/minix/servers/rs/manager.c@ 9

Last change on this file since 9 was 9, checked in by Mattia Monga, 13 years ago

Minix 3.1.2a

File size: 15.9 KB
Line 
1/*
2 * Changes:
3 * Jul 22, 2005: Created (Jorrit N. Herder)
4 */
5
6#include "inc.h"
7#include <unistd.h>
8#include <sys/types.h>
9#include <sys/wait.h>
10#include <minix/dmap.h>
11#include <minix/endpoint.h>
12
13/* Allocate variables. */
14struct rproc rproc[NR_SYS_PROCS]; /* system process table */
15struct rproc *rproc_ptr[NR_PROCS]; /* mapping for fast access */
16int nr_in_use; /* number of services */
17extern int errno; /* error status */
18
19/* Prototypes for internal functions that do the hard work. */
20FORWARD _PROTOTYPE( int start_service, (struct rproc *rp) );
21FORWARD _PROTOTYPE( int stop_service, (struct rproc *rp,int how) );
22
23PRIVATE int shutting_down = FALSE;
24
25#define EXEC_FAILED 49 /* recognizable status */
26
27/*===========================================================================*
28 * do_up *
29 *===========================================================================*/
30PUBLIC int do_up(m_ptr)
31message *m_ptr; /* request message pointer */
32{
33/* A request was made to start a new system service. Dismember the request
34 * message and gather all information needed to start the service. Starting
35 * is done by a helper routine.
36 */
37 register struct rproc *rp; /* system process table */
38 int slot_nr; /* local table entry */
39 int arg_count; /* number of arguments */
40 char *cmd_ptr; /* parse command string */
41 enum dev_style dev_style; /* device style */
42 int s; /* status variable */
43
44 /* See if there is a free entry in the table with system processes. */
45 if (nr_in_use >= NR_SYS_PROCS) return(EAGAIN);
46 for (slot_nr = 0; slot_nr < NR_SYS_PROCS; slot_nr++) {
47 rp = &rproc[slot_nr]; /* get pointer to slot */
48 if (! rp->r_flags & RS_IN_USE) /* check if available */
49 break;
50 }
51 nr_in_use ++; /* update administration */
52
53 /* Obtain command name and parameters. This is a space-separated string
54 * that looks like "/sbin/service arg1 arg2 ...". Arguments are optional.
55 */
56 if (m_ptr->RS_CMD_LEN > MAX_COMMAND_LEN) return(E2BIG);
57 if (OK!=(s=sys_datacopy(m_ptr->m_source, (vir_bytes) m_ptr->RS_CMD_ADDR,
58 SELF, (vir_bytes) rp->r_cmd, m_ptr->RS_CMD_LEN))) return(s);
59 rp->r_cmd[m_ptr->RS_CMD_LEN] = '\0'; /* ensure it is terminated */
60 if (rp->r_cmd[0] != '/') return(EINVAL); /* insist on absolute path */
61
62 /* Build argument vector to be passed to execute call. The format of the
63 * arguments vector is: path, arguments, NULL.
64 */
65 arg_count = 0; /* initialize arg count */
66 rp->r_argv[arg_count++] = rp->r_cmd; /* start with path */
67 cmd_ptr = rp->r_cmd; /* do some parsing */
68 while(*cmd_ptr != '\0') { /* stop at end of string */
69 if (*cmd_ptr == ' ') { /* next argument */
70 *cmd_ptr = '\0'; /* terminate previous */
71 while (*++cmd_ptr == ' ') ; /* skip spaces */
72 if (*cmd_ptr == '\0') break; /* no arg following */
73 if (arg_count>MAX_NR_ARGS+1) break; /* arg vector full */
74 rp->r_argv[arg_count++] = cmd_ptr; /* add to arg vector */
75 }
76 cmd_ptr ++; /* continue parsing */
77 }
78 rp->r_argv[arg_count] = NULL; /* end with NULL pointer */
79 rp->r_argc = arg_count;
80
81 /* Initialize some fields. */
82 rp->r_period = m_ptr->RS_PERIOD;
83 rp->r_dev_nr = m_ptr->RS_DEV_MAJOR;
84 rp->r_dev_style = STYLE_DEV;
85 rp->r_restarts = -1; /* will be incremented */
86
87 /* All information was gathered. Now try to start the system service. */
88 return(start_service(rp));
89}
90
91
92/*===========================================================================*
93 * do_down *
94 *===========================================================================*/
95PUBLIC int do_down(message *m_ptr)
96{
97 register struct rproc *rp;
98 pid_t pid = (pid_t) m_ptr->RS_PID;
99
100 for (rp=BEG_RPROC_ADDR; rp<END_RPROC_ADDR; rp++) {
101 if (rp->r_flags & RS_IN_USE && rp->r_pid == pid) {
102#if VERBOSE
103 printf("stopping %d (%d)\n", pid, m_ptr->RS_PID);
104#endif
105 stop_service(rp,RS_EXITING);
106 return(OK);
107 }
108 }
109#if VERBOSE
110 printf("not found %d (%d)\n", pid, m_ptr->RS_PID);
111#endif
112 return(ESRCH);
113}
114
115
116/*===========================================================================*
117 * do_refresh *
118 *===========================================================================*/
119PUBLIC int do_refresh(message *m_ptr)
120{
121 register struct rproc *rp;
122 pid_t pid = (pid_t) m_ptr->RS_PID;
123
124 for (rp=BEG_RPROC_ADDR; rp<END_RPROC_ADDR; rp++) {
125 if (rp->r_flags & RS_IN_USE && rp->r_pid == pid) {
126#if VERBOSE
127 printf("refreshing %d (%d)\n", pid, m_ptr->RS_PID);
128#endif
129 stop_service(rp,RS_REFRESHING);
130 return(OK);
131 }
132 }
133#if VERBOSE
134 printf("not found %d (%d)\n", pid, m_ptr->RS_PID);
135#endif
136 return(ESRCH);
137}
138
139/*===========================================================================*
140 * do_rescue *
141 *===========================================================================*/
142PUBLIC int do_rescue(message *m_ptr)
143{
144 char rescue_dir[MAX_RESCUE_DIR_LEN];
145 int s;
146
147 /* Copy rescue directory from user. */
148 if (m_ptr->RS_CMD_LEN > MAX_RESCUE_DIR_LEN) return(E2BIG);
149 if (OK!=(s=sys_datacopy(m_ptr->m_source, (vir_bytes) m_ptr->RS_CMD_ADDR,
150 SELF, (vir_bytes) rescue_dir, m_ptr->RS_CMD_LEN))) return(s);
151 rescue_dir[m_ptr->RS_CMD_LEN] = '\0'; /* ensure it is terminated */
152 if (rescue_dir[0] != '/') return(EINVAL); /* insist on absolute path */
153
154 /* Change RS' directory to the rescue directory. Provided that the needed
155 * binaries are in the rescue dir, this makes recovery possible even if the
156 * (root) file system is no longer available, because no directory lookups
157 * are required. Thus if an absolute path fails, we can try to strip the
158 * path an see if the command is in the rescue dir.
159 */
160 if (chdir(rescue_dir) != 0) return(errno);
161 return(OK);
162}
163
164/*===========================================================================*
165 * do_shutdown *
166 *===========================================================================*/
167PUBLIC int do_shutdown(message *m_ptr)
168{
169 /* Set flag so that RS server knows services shouldn't be restarted. */
170 shutting_down = TRUE;
171 return(OK);
172}
173
174/*===========================================================================*
175 * do_exit *
176 *===========================================================================*/
177PUBLIC void do_exit(message *m_ptr)
178{
179 register struct rproc *rp;
180 pid_t exit_pid;
181 int exit_status;
182
183#if VERBOSE
184 printf("RS: got SIGCHLD signal, doing wait to get exited child.\n");
185#endif
186
187 /* See which child exited and what the exit status is. This is done in a
188 * loop because multiple childs may have exited, all reported by one
189 * SIGCHLD signal. The WNOHANG options is used to prevent blocking if,
190 * somehow, no exited child can be found.
191 */
192 while ( (exit_pid = waitpid(-1, &exit_status, WNOHANG)) != 0 ) {
193
194#if VERBOSE
195 printf("RS: proc %d, pid %d, ", rp->r_proc_nr_e, exit_pid);
196 if (WIFSIGNALED(exit_status)) {
197 printf("killed, signal number %d\n", WTERMSIG(exit_status));
198 }
199 else if (WIFEXITED(exit_status)) {
200 printf("normal exit, status %d\n", WEXITSTATUS(exit_status));
201 }
202#endif
203
204 /* Search the system process table to see who exited.
205 * This should always succeed.
206 */
207 for (rp=BEG_RPROC_ADDR; rp<END_RPROC_ADDR; rp++) {
208 if ((rp->r_flags & RS_IN_USE) && rp->r_pid == exit_pid) {
209 int proc;
210 proc = _ENDPOINT_P(rp->r_proc_nr_e);
211
212 rproc_ptr[proc] = NULL; /* invalidate */
213
214 if ((rp->r_flags & RS_EXITING) || shutting_down) {
215 rp->r_flags = 0; /* release slot */
216 rproc_ptr[proc] = NULL;
217 }
218 else if(rp->r_flags & RS_REFRESHING) {
219 rp->r_restarts = -1; /* reset counter */
220 start_service(rp); /* direct restart */
221 }
222 else if (WIFEXITED(exit_status) &&
223 WEXITSTATUS(exit_status) == EXEC_FAILED) {
224 rp->r_flags = 0; /* release slot */
225 }
226 else {
227#if VERBOSE
228 printf("Unexpected exit. Restarting %s\n", rp->r_cmd);
229#endif
230 /* Determine what to do. If this is the first unexpected
231 * exit, immediately restart this service. Otherwise use
232 * a binary exponetial backoff.
233 */
234 if (rp->r_restarts > 0) {
235 rp->r_backoff = 1 << MIN(rp->r_restarts,(BACKOFF_BITS-1));
236 rp->r_backoff = MIN(rp->r_backoff,MAX_BACKOFF);
237 }
238 else {
239 start_service(rp); /* direct restart */
240 }
241 }
242 break;
243 }
244 }
245 }
246}
247
248/*===========================================================================*
249 * do_period *
250 *===========================================================================*/
251PUBLIC void do_period(m_ptr)
252message *m_ptr;
253{
254 register struct rproc *rp;
255 clock_t now = m_ptr->NOTIFY_TIMESTAMP;
256 int s;
257
258 /* Search system services table. Only check slots that are in use. */
259 for (rp=BEG_RPROC_ADDR; rp<END_RPROC_ADDR; rp++) {
260 if (rp->r_flags & RS_IN_USE) {
261
262 /* If the service is to be revived (because it repeatedly exited,
263 * and was not directly restarted), the binary backoff field is
264 * greater than zero.
265 */
266 if (rp->r_backoff > 0) {
267 rp->r_backoff -= 1;
268 if (rp->r_backoff == 0) {
269 start_service(rp);
270 }
271 }
272
273 /* If the service was signaled with a SIGTERM and fails to respond,
274 * kill the system service with a SIGKILL signal.
275 */
276 else if (rp->r_stop_tm > 0 && now - rp->r_stop_tm > 2*RS_DELTA_T
277 && rp->r_pid > 0) {
278 kill(rp->r_pid, SIGKILL); /* terminate */
279 }
280
281 /* There seems to be no special conditions. If the service has a
282 * period assigned check its status.
283 */
284 else if (rp->r_period > 0) {
285
286 /* Check if an answer to a status request is still pending. If
287 * the driver didn't respond within time, kill it to simulate
288 * a crash. The failure will be detected and the service will
289 * be restarted automatically.
290 */
291 if (rp->r_alive_tm < rp->r_check_tm) {
292 if (now - rp->r_alive_tm > 2*rp->r_period &&
293 rp->r_pid > 0) {
294#if VERBOSE
295 printf("RS: service %d reported late\n", rp->r_proc_nr_e);
296#endif
297 kill(rp->r_pid, SIGKILL); /* simulate crash */
298 }
299 }
300
301 /* No answer pending. Check if a period expired since the last
302 * check and, if so request the system service's status.
303 */
304 else if (now - rp->r_check_tm > rp->r_period) {
305#if VERBOSE
306 printf("RS: status request sent to %d\n", rp->r_proc_nr_e);
307#endif
308 notify(rp->r_proc_nr_e); /* request status */
309 rp->r_check_tm = now; /* mark time */
310 }
311 }
312 }
313 }
314
315 /* Reschedule a synchronous alarm for the next period. */
316 if (OK != (s=sys_setalarm(RS_DELTA_T, 0)))
317 panic("RS", "couldn't set alarm", s);
318}
319
320
321/*===========================================================================*
322 * start_service *
323 *===========================================================================*/
324PRIVATE int start_service(rp)
325struct rproc *rp;
326{
327/* Try to execute the given system service. Fork a new process. The child
328 * process will be inhibited from running by the NO_PRIV flag. Only let the
329 * child run once its privileges have been set by the parent.
330 */
331 int child_proc_nr_e, child_proc_nr_n; /* child process slot */
332 pid_t child_pid; /* child's process id */
333 char *file_only;
334 int s;
335 message m;
336
337 /* Now fork and branch for parent and child process (and check for error). */
338 child_pid = fork();
339 switch(child_pid) { /* see fork(2) */
340 case -1: /* fork failed */
341 report("RS", "warning, fork() failed", errno); /* shouldn't happen */
342 return(errno); /* return error */
343
344 case 0: /* child process */
345 /* Try to execute the binary that has an absolute path. If this fails,
346 * e.g., because the root file system cannot be read, try to strip of
347 * the path, and see if the command is in RS' current working dir.
348 */
349 execve(rp->r_argv[0], rp->r_argv, NULL); /* POSIX execute */
350 file_only = strrchr(rp->r_argv[0], '/') + 1;
351 execve(file_only, rp->r_argv, NULL); /* POSIX execute */
352 printf("RS: exec failed for %s: %d\n", rp->r_argv[0], errno);
353 exit(EXEC_FAILED); /* terminate child */
354
355 default: /* parent process */
356 child_proc_nr_e = getnprocnr(child_pid); /* get child slot */
357 break; /* continue below */
358 }
359
360 /* Only the parent process (the RS server) gets to this point. The child
361 * is still inhibited from running because it's privilege structure is
362 * not yet set. First try to set the device driver mapping at the FS.
363 */
364 if (rp->r_dev_nr > 0) { /* set driver map */
365 if ((s=mapdriver(child_proc_nr_e, rp->r_dev_nr, rp->r_dev_style)) < 0) {
366 report("RS", "couldn't map driver", errno);
367 rp->r_flags |= RS_EXITING; /* expect exit */
368 if(child_pid > 0) kill(child_pid, SIGKILL); /* kill driver */
369 else report("RS", "didn't kill pid", child_pid);
370 return(s); /* return error */
371 }
372 }
373
374 /* The device driver mapping has been set, or the service was not a driver.
375 * Now, set the privilege structure for the child process to let is run.
376 * This should succeed: we tested number in use above.
377 */
378 if ((s = sys_privctl(child_proc_nr_e, SYS_PRIV_INIT, 0, NULL)) < 0) {
379 report("RS","call to SYSTEM failed", s); /* to let child run */
380 rp->r_flags |= RS_EXITING; /* expect exit */
381 if(child_pid > 0) kill(child_pid, SIGKILL); /* kill driver */
382 else report("RS", "didn't kill pid", child_pid);
383 return(s); /* return error */
384 }
385
386#if VERBOSE
387 printf("RS: started '%s', major %d, pid %d, endpoint %d, proc %d\n",
388 rp->r_cmd, rp->r_dev_nr, child_pid,
389 child_proc_nr_e, child_proc_nr_n);
390#endif
391
392 /* The system service now has been successfully started. Update the rest
393 * of the system process table that is maintain by the RS server. The only
394 * thing that can go wrong now, is that execution fails at the child. If
395 * that's the case, the child will exit.
396 */
397 child_proc_nr_n = _ENDPOINT_P(child_proc_nr_e);
398 rp->r_flags = RS_IN_USE; /* mark slot in use */
399 rp->r_restarts += 1; /* raise nr of restarts */
400 rp->r_proc_nr_e = child_proc_nr_e; /* set child details */
401 rp->r_pid = child_pid;
402 rp->r_check_tm = 0; /* not check yet */
403 getuptime(&rp->r_alive_tm); /* currently alive */
404 rp->r_stop_tm = 0; /* not exiting yet */
405 rproc_ptr[child_proc_nr_n] = rp; /* mapping for fast access */
406 return(OK);
407}
408
409/*===========================================================================*
410 * stop_service *
411 *===========================================================================*/
412PRIVATE int stop_service(rp,how)
413struct rproc *rp;
414int how;
415{
416 /* Try to stop the system service. First send a SIGTERM signal to ask the
417 * system service to terminate. If the service didn't install a signal
418 * handler, it will be killed. If it did and ignores the signal, we'll
419 * find out because we record the time here and send a SIGKILL.
420 */
421#if VERBOSE
422 printf("RS tries to stop %s (pid %d)\n", rp->r_cmd, rp->r_pid);
423#endif
424
425 rp->r_flags |= how; /* what to on exit? */
426 if(rp->r_pid > 0) kill(rp->r_pid, SIGTERM); /* first try friendly */
427 else report("RS", "didn't kill pid", rp->r_pid);
428 getuptime(&rp->r_stop_tm); /* record current time */
429}
430
431
432/*===========================================================================*
433 * do_getsysinfo *
434 *===========================================================================*/
435PUBLIC int do_getsysinfo(m_ptr)
436message *m_ptr;
437{
438 vir_bytes src_addr, dst_addr;
439 int dst_proc;
440 size_t len;
441 int s;
442
443 switch(m_ptr->m1_i1) {
444 case SI_PROC_TAB:
445 src_addr = (vir_bytes) rproc;
446 len = sizeof(struct rproc) * NR_SYS_PROCS;
447 break;
448 default:
449 return(EINVAL);
450 }
451
452 dst_proc = m_ptr->m_source;
453 dst_addr = (vir_bytes) m_ptr->m1_p1;
454 if (OK != (s=sys_datacopy(SELF, src_addr, dst_proc, dst_addr, len)))
455 return(s);
456 return(OK);
457}
458
Note: See TracBrowser for help on using the repository browser.