2 * Copyright (c) 1995, the EUROPAGATE consortium (see below).
4 * The EUROPAGATE consortium members are:
6 * University College Dublin
7 * Danmarks Teknologiske Videnscenter
8 * An Chomhairle Leabharlanna
9 * Consejo Superior de Investigaciones Cientificas
11 * Permission to use, copy, modify, distribute, and sell this software and
12 * its documentation, in whole or in part, for any purpose, is hereby granted,
15 * 1. This copyright and permission notice appear in all copies of the
16 * software and its documentation. Notices of copyright or attribution
17 * which appear at the beginning of any file must remain unchanged.
19 * 2. The names of EUROPAGATE or the project partners may not be used to
20 * endorse or promote products derived from this software without specific
21 * prior written permission.
23 * 3. Users of this software (implementors and gateway operators) agree to
24 * inform the EUROPAGATE consortium of their use of the software. This
25 * information will be used to evaluate the EUROPAGATE project and the
26 * software, and to plan further developments. The consortium may use
27 * the information in later publications.
29 * 4. Users of this software agree to make their best efforts, when
30 * documenting their use of the software, to acknowledge the EUROPAGATE
31 * consortium, and the role played by the software in their work.
33 * THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT WARRANTY OF ANY KIND,
34 * EXPRESS, IMPLIED, OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY
35 * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
36 * IN NO EVENT SHALL THE EUROPAGATE CONSORTIUM OR ITS MEMBERS BE LIABLE
37 * FOR ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF
38 * ANY KIND, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA
39 * OR PROFITS, WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND
40 * ON ANY THEORY OF LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE
41 * USE OR PERFORMANCE OF THIS SOFTWARE.
44 /* Gateway Resource Monitor
48 * Revision 1.15 1995/07/11 11:49:12 adam
49 * LINE_MAX renamed to STR_LINE_MAX.
51 * Revision 1.14 1995/05/23 08:12:59 adam
54 * Revision 1.13 1995/05/22 11:42:48 adam
55 * Minor changes on dtbsun.
57 * Revision 1.12 1995/05/19 14:51:06 adam
58 * Bug fix: stopped kernels sometimes got IPC messages from the monitor.
60 * Revision 1.11 1995/05/19 13:26:00 adam
61 * Bug fixes. Better command line options.
63 * Revision 1.10 1995/05/18 12:03:09 adam
64 * Bug fixes and minor improvements.
66 * Revision 1.9 1995/05/17 10:51:32 adam
67 * Added a few more error checks to the show command.
69 * Revision 1.8 1995/05/16 09:40:42 adam
70 * LICENSE. Setting of CCL token names (and/or/not/set) in read_kernel_res.
72 * Revision 1.7 1995/05/03 12:18:46 adam
73 * This code ran on dtbsun. Minor changes.
75 * Revision 1.6 1995/05/03 09:16:17 adam
78 * Revision 1.5 1995/05/03 07:37:42 adam
79 * CCL commands stop/continue implemented. New functions gw_res_{int,bool}
80 * are used when possible.
82 * Revision 1.4 1995/05/02 15:26:00 adam
83 * Monitor observes death of child (email kernel). The number
84 * of simultanous processes is controlled now. Email requests are
85 * queued if necessary. This scheme should only be forced if no kernels
88 * Revision 1.3 1995/05/02 07:20:10 adam
89 * Use pid of exited child to close fifos.
91 * Revision 1.2 1995/05/01 16:26:57 adam
92 * More work on resource monitor.
94 * Revision 1.1 1995/05/01 12:43:36 adam
95 * First work on resource monitor program.
110 #include <sys/file.h>
111 #include <sys/stat.h>
112 #include <sys/types.h>
113 #include <sys/time.h>
114 #include <sys/wait.h>
119 #include <strqueue.h>
122 #define STR_LINE_MAX 1024
124 #define MONITOR_FIFO_S "fifo.s.m"
125 #define MONITOR_FIFO_C "fifo.c.m"
127 static char *module = "monitor";
128 static jmp_buf retry_jmp;
130 static GwRes monitor_res = NULL;
131 static int no_process = 0;
132 static int max_process = 1;
133 static int got_sighup = 0;
134 static int got_term = 0;
135 static int got_int = 0;
136 const char *default_res = "default.res";
139 * reread_resources: reread monitor resources. The static variable,
140 * max_process, is updated.
142 static void reread_resources (void)
145 gw_res_close (monitor_res);
146 monitor_res = gw_res_init ();
147 if (gw_res_merge (monitor_res, default_res))
149 gw_log (GW_LOG_WARN, module, "Couldn't read resource file %s",
153 max_process = gw_res_int (monitor_res, "gw.max.process", 10);
157 int id; /* email user-id */
158 int stopped; /* stop flag */
159 pid_t pid; /* pid of email kernel child */
160 GIP gip; /* fifo information */
161 struct str_queue *queue; /* message queue */
162 struct ke_info *next; /* link to next */
165 /* list of email kernel infos */
166 static struct ke_info *ke_info_list = NULL;
169 * ke_info_add: add/lookup of email kernel info.
170 * id: email user-id to search for.
171 * return: pointer to info structure.
173 struct ke_info *ke_info_add (int id)
175 struct ke_info **kip;
177 for (kip = &ke_info_list; *kip; kip= &(*kip)->next)
178 if ((*kip)->id == id)
180 *kip = malloc (sizeof(**kip));
185 (*kip)->queue = NULL;
190 static void ke_info_del (void)
194 assert (ke_info_list);
196 str_queue_rm (&ki->queue);
197 ke_info_list = ki->next;
202 * catch_child: catch SIGCHLD. Set email kernel pid to -1
203 * to indicate that child has exited
205 static void catch_child (int num)
210 while ((pid=waitpid (-1, 0, WNOHANG)) > 0)
212 for (ki = ke_info_list; ki; ki = ki->next)
217 signal (SIGCHLD, catch_child);
221 * catch_int: catch SIGHUP.
223 static void catch_hup (int num)
226 signal (SIGHUP, catch_hup);
230 * catch_int: catch SIGTERM.
232 static void catch_term (int num)
235 signal (SIGTERM, catch_term);
239 * catch_int: catch SIGINT.
241 static void catch_int (int num)
244 signal (SIGINT, catch_int);
248 * pipe_handle: handle SIGPIPE when transferring message to kernel
250 static void pipe_handle (int dummy)
252 longjmp (retry_jmp, 1);
256 * start_kernel: start email kernel.
257 * argc: argc of email kernel
258 * argv: argv of email kernel
260 * return: pid of email kernel child
262 static pid_t start_kernel (int argc, char **argv, int id)
267 char userid_option[20];
269 argv_p = malloc (sizeof(*argv_p)*(argc+2));
272 gw_log (GW_LOG_FATAL|GW_LOG_ERRNO, module, "malloc fail");
275 argv_p[0] = "kernel";
276 for (i = 1; i<argc; i++)
278 sprintf (userid_option, "-i%d", id);
279 argv_p[i++] = userid_option;
282 gw_log (GW_LOG_DEBUG, module, "Starting kernel");
286 gw_log (GW_LOG_FATAL|GW_LOG_ERRNO, module, "fork");
291 execv ("kernel", argv_p);
292 gw_log (GW_LOG_FATAL|GW_LOG_ERRNO, module, "execvp");
299 * deliver: deliver message to child (email kernel).
300 * argc: exec argc to child (if it need to be started)
301 * argv: exec argv to child (if it need to be started)
303 * queue: message queue to be transferred
304 * gip: pointer to FIFO info. if *gip is NULL prior invocation
305 * it will be created (initialized) and the pointer will be
307 * pidp: pointer to pid. Will hold process-id of child (if it need to
309 * dont_exec: if non-zero a child will never be started; otherwise child
310 * will be started if not already running.
311 * return: 0 if message couldn't be transferred, i.e. dont_exec is non-zero
312 * and the child is not already running.
313 * 1 if message was transferred and the child was already running.
314 * 2 if message was transferred and the child was started and
315 * dont_exec was zero.
316 * 3 serious error. Permissions denied or kernel couldn't be
319 static int deliver (int argc, char **argv, int id, struct str_queue *queue,
320 GIP *gip, pid_t *pidp, int dont_exec)
325 char fifo_server_name[128];
326 char fifo_client_name[128];
330 sprintf (fifo_server_name, "fifo.s.%d", id);
331 sprintf (fifo_client_name, "fifo.c.%d", id);
335 *gip = gipc_initialize (fifo_client_name);
337 oldsig = signal (SIGPIPE, pipe_handle);
341 { /* assume child is running */
342 gipc_close (*gip); /* shut down existing FIFOs */
343 r = gipc_open (*gip, fifo_server_name, 0); /* try re-open ... */
346 { /* assume child is NOT running */
350 { /* we aren't allowed to start */
351 signal (SIGPIPE, oldsig);
354 mknod (fifo_server_name, S_IFIFO|0666, 0);
355 pid = start_kernel (argc, argv, id);
356 if (pidp) /* set pid of child */
358 r = gipc_open (*gip, fifo_server_name, 1);
361 { /* message couldn't be transferred */
362 signal (SIGPIPE, oldsig);
363 gw_log (GW_LOG_WARN, module, "Cannot start kernel");
366 if (r < 0) /* gipc_open fail? */
369 gw_log (GW_LOG_DEBUG|GW_LOG_ERRNO, module, "r==-2");
371 gw_log (GW_LOG_DEBUG|GW_LOG_ERRNO, module, "r==-1");
373 gw_log (GW_LOG_WARN|GW_LOG_ERRNO, module, "gipc_open");
374 longjmp (retry_jmp, 1); /* yet another pass */
376 index = 0; /* transfer. may be interrupted */
377 while ((msg = str_queue_get (queue, index++)))
379 gw_log (GW_LOG_DEBUG, module, "deliver: %s", msg);
380 gip_wline (*gip, msg);
382 signal (SIGPIPE, oldsig);
383 return pass; /* successful transfer */
387 * monitor_events: Event loop of monitor
388 * argc: argc of monitor (used in exec of Email kernel children)
389 * argv: argv of monitor (used in exec of Email kernel children)
391 static void monitor_events (int argc, char **argv)
394 int r, gip_m_fd, too_many;
397 char command[128], *cp;
399 mknod (MONITOR_FIFO_C, S_IFIFO|0666, 0);
400 open (MONITOR_FIFO_C, O_RDONLY|O_NONBLOCK);
401 gip_m = gips_initialize (MONITOR_FIFO_S);
402 r = gips_open (gip_m, MONITOR_FIFO_C, 0);
403 gip_m_fd = gip_infileno (gip_m);
405 open (MONITOR_FIFO_S, O_WRONLY);
407 gw_log (GW_LOG_DEBUG, module, "Starting event loop");
417 gw_log (GW_LOG_STAT, module, "Got SIGHUP. Reading resources");
423 gw_log (GW_LOG_STAT, module, "Got SIGTERM. Exiting...");
424 unlink (MONITOR_FIFO_S);
425 unlink (MONITOR_FIFO_C);
430 gw_log (GW_LOG_STAT, module, "Got SIGINT. Exiting...");
431 unlink (MONITOR_FIFO_S);
432 unlink (MONITOR_FIFO_C);
435 /* deliver any unsent messages to Email kernels */
437 for (ki = ke_info_list; ki; ki = ki->next)
439 if (!ki->queue || ki->stopped)
441 gw_log (GW_LOG_DEBUG, module, "Transfer mail to %d", ki->id);
442 r = deliver (argc, argv, ki->id, ki->queue, &ki->gip, &ki->pid,
443 no_process >= max_process);
444 if (r == 2) /* new child was spawned? */
447 gw_log (GW_LOG_DEBUG, module, "Start of %d", ki->id);
449 if (r == 1 || r == 2) /* transfer at all? */
450 str_queue_rm (&ki->queue);
451 if (r == 0) /* too many pending? */
456 gw_log (GW_LOG_DEBUG, module, "%d too many pending",
458 for (ki = ke_info_list; ki; ki = ki->next)
460 if (!ki->queue && ki->pid != -1 && !ki->stopped)
462 if (!(ki->queue = str_queue_mk ()))
464 gw_log (GW_LOG_FATAL|GW_LOG_ERRNO, module,
468 str_queue_enq (ki->queue, "stop\n");
469 str_queue_enq (ki->queue, "\001");
470 r = deliver (argc, argv, ki->id, ki->queue, &ki->gip,
473 gw_log (GW_LOG_DEBUG, module,
474 "Stop not sent: %d", r);
475 str_queue_rm (&ki->queue);
482 FD_SET (gip_m_fd, &set_r);
483 gw_log (GW_LOG_DEBUG, module, "set gip_m_fd %d", gip_m_fd);
486 for (ki = ke_info_list; ki; ki = ki->next)
492 { /* child has exited */
493 gw_log (GW_LOG_DEBUG, module, "Close of %d", ki->id);
494 gipc_close (ki->gip);
495 gipc_destroy (ki->gip);
499 else if ((fd = gip_infileno (ki->gip)) != -1)
500 { /* read select on child FIFO */
501 gw_log (GW_LOG_DEBUG, module, "set fd %d", fd);
508 gw_log (GW_LOG_DEBUG, module, "Cur/Max processes %d/%d",
509 no_process, max_process);
510 gw_log (GW_LOG_DEBUG, module, "IPC select");
511 r = select (fd_max+1, &set_r, NULL, NULL, NULL);
515 { /* select aborted. And it was not due to interrupt */
516 gw_log (GW_LOG_FATAL|GW_LOG_ERRNO, module, "select");
519 /* select was interrupted. Probably child has died */
520 gw_log (GW_LOG_DEBUG|GW_LOG_ERRNO, module, "select");
522 /* go through list of Email kernels. See if any message has arrived */
523 gw_log (GW_LOG_DEBUG, module, "Testing ke_info_list");
524 for (ki = ke_info_list; ki; ki = ki->next)
527 if (ki->gip && (fd = gip_infileno (ki->gip)) != -1)
529 gw_log (GW_LOG_DEBUG, module, "Test of %d", fd);
530 if (FD_ISSET (fd, &set_r))
532 if (lgets (line_buf, sizeof(line_buf)-1, fd))
534 gw_log (GW_LOG_DEBUG, module, "IPC: %s", line_buf);
538 gw_log (GW_LOG_DEBUG, module, "Close of %d", ki->id);
539 gipc_close (ki->gip);
540 gipc_destroy (ki->gip);
547 /* see if any message from eti has arrived */
548 gw_log (GW_LOG_DEBUG, module, "Testing gip_m_fd %d", gip_m_fd);
549 if (FD_ISSET (gip_m_fd, &set_r))
551 gw_log (GW_LOG_DEBUG, module, "Reading from %d", gip_m_fd);
552 if (!(lgets (command, sizeof(command)-1, gip_m_fd)))
554 gw_log (GW_LOG_FATAL, module, "Unexpected close");
557 gw_log (GW_LOG_DEBUG, module, "Done");
558 if ((cp = strchr (command, '\n')))
560 gw_log (GW_LOG_DEBUG, module, "IPC: %s", command);
561 if (!memcmp (command, "eti ", 4))
563 int id = atoi (command+4);
564 struct ke_info *new_k;
566 new_k = ke_info_add (id);
567 gw_log (GW_LOG_DEBUG, module, "Incoming mail %d", id);
571 if (!(new_k->queue = str_queue_mk ()))
573 gw_log (GW_LOG_FATAL|GW_LOG_ERRNO, module,
578 str_queue_enq (new_k->queue, "mail\n");
579 while (lgets (line_buf, sizeof(line_buf)-1, gip_m_fd))
580 str_queue_enq (new_k->queue, line_buf);
581 str_queue_enq (new_k->queue, "\001");
588 * main: main of monitor
590 int main (int argc, char **argv)
595 while (++argno < argc)
597 if (argv[argno][0] == '-')
599 if (argv[argno][1] == '-')
601 switch (argv[argno][1])
605 fprintf (stderr, "monitor [options] [resourceFile]"
606 " -- [kernelOptions]\n");
607 fprintf (stderr, "If no resource file is specified");
608 fprintf (stderr, " default.res is used\n");
609 fprintf (stderr, "Options:\n");
610 fprintf (stderr, " -l log Set Log file\n");
611 fprintf (stderr, " -d Enable debugging log\n");
612 fprintf (stderr, " -D Enable more debugging log\n");
613 fprintf (stderr, " -- Precedes kernel options\n");
614 fprintf (stderr, "Kernel options are transferred to kernel\n");
618 gw_log_file (GW_LOG_ALL, argv[argno]+2);
619 else if (++argno < argc)
620 gw_log_file (GW_LOG_ALL, argv[argno]);
623 fprintf (stderr, "%s: missing log filename\n", *argv);
628 gw_log_level (GW_LOG_ALL & ~RES_DEBUG);
631 gw_log_level (GW_LOG_ALL);
634 fprintf (stderr, "%s: unknown option `%s'; use -H for help\n",
640 default_res = argv[argno];
643 signal (SIGCHLD, catch_child);
644 signal (SIGHUP, catch_hup);
645 signal (SIGTERM, catch_term);
646 signal (SIGINT, catch_int);
647 monitor_events (argc-argno, argv+argno);