LICENSE. Setting of CCL token names (and/or/not/set) in read_kernel_res.
[egate.git] / kernel / monitor.c
1 /*
2  * Copyright (c) 1995, the EUROPAGATE consortium (see below).
3  *
4  * The EUROPAGATE consortium members are:
5  *
6  *    University College Dublin
7  *    Danmarks Teknologiske Videnscenter
8  *    An Chomhairle Leabharlanna
9  *    Consejo Superior de Investigaciones Cientificas
10  *
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,
13  * provided that:
14  *
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.
18  *
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.
22  *
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.
28  * 
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.
32  *
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.
42  *
43  */
44 /* Gateway Resource Monitor
45  * Europagate, 1995
46  *
47  * $Log: monitor.c,v $
48  * Revision 1.8  1995/05/16 09:40:42  adam
49  * LICENSE. Setting of CCL token names (and/or/not/set) in read_kernel_res.
50  *
51  * Revision 1.7  1995/05/03  12:18:46  adam
52  * This code ran on dtbsun. Minor changes.
53  *
54  * Revision 1.6  1995/05/03  09:16:17  adam
55  * Minor changes.
56  *
57  * Revision 1.5  1995/05/03  07:37:42  adam
58  * CCL commands stop/continue implemented. New functions gw_res_{int,bool}
59  * are used when possible.
60  *
61  * Revision 1.4  1995/05/02  15:26:00  adam
62  * Monitor observes death of child (email kernel). The number
63  * of simultanous processes is controlled now. Email requests are
64  * queued if necessary. This scheme should only be forced if no kernels
65  * are idle.
66  *
67  * Revision 1.3  1995/05/02  07:20:10  adam
68  * Use pid of exited child to close fifos.
69  *
70  * Revision 1.2  1995/05/01  16:26:57  adam
71  * More work on resource monitor.
72  *
73  * Revision 1.1  1995/05/01  12:43:36  adam
74  * First work on resource monitor program.
75  *
76  */
77
78 #include <stdio.h>
79 #include <stdlib.h>
80 #include <assert.h>
81 #include <ctype.h>
82 #include <string.h>
83 #include <unistd.h>
84 #include <fcntl.h>
85 #include <setjmp.h>
86 #include <signal.h>
87 #include <errno.h>
88
89 #include <sys/file.h>
90 #include <sys/stat.h>
91 #include <sys/types.h>
92 #include <sys/time.h>
93 #include <sys/wait.h>
94
95 #include <gw-log.h>
96 #include <gw-res.h>
97 #include <gip.h>
98 #include <strqueue.h>
99 #include <lgets.h>
100
101 #define LINE_MAX 1024
102
103 #define MONITOR_FIFO_S "fifo.s.m"
104 #define MONITOR_FIFO_C "fifo.c.m"
105
106 static char *module = "monitor";
107 static jmp_buf retry_jmp;
108
109 static GwRes monitor_res = NULL;
110 static int no_process = 0;
111 static int max_process = 1;
112 static int got_sighup = 0;
113 static int got_term = 0;
114 static int got_int = 0;
115 const char *default_res = "default.res";
116
117 /*
118  * reread_resources: reread monitor resources. The static variable, 
119  *      max_process, is updated.
120  */
121 static void reread_resources (void)
122 {
123     if (monitor_res)
124         gw_res_close (monitor_res);
125     monitor_res = gw_res_init ();
126     if (gw_res_merge (monitor_res, default_res))
127     {
128         gw_log (GW_LOG_WARN, module, "Couldn't read resource file %s",
129                 default_res);
130         exit (1);
131     }
132     max_process = gw_res_int (monitor_res, "gw.max.process", 10);
133 }
134
135 struct ke_info {
136     int id;                     /* email user-id */
137     pid_t pid;                  /* pid of email kernel child */
138     GIP gip;                    /* fifo information */
139     struct str_queue *queue;    /* message queue */
140     struct ke_info *next;       /* link to next */
141 };
142
143 /* list of email kernel infos */
144 static struct ke_info *ke_info_list = NULL;
145
146 /*
147  * ke_info_add: add/lookup of email kernel info.
148  * id:      email user-id to search for.
149  * return:  pointer to info structure.
150  */
151 struct ke_info *ke_info_add (int id)
152 {
153     struct ke_info **kip;
154
155     for (kip = &ke_info_list; *kip; kip= &(*kip)->next)
156         if ((*kip)->id == id)
157             return *kip;
158     *kip = malloc (sizeof(**kip));
159     assert (*kip);
160     (*kip)->next = NULL;
161     (*kip)->id = id;
162     (*kip)->gip = NULL;
163     (*kip)->queue = NULL;
164     return *kip;
165 }
166
167 static void ke_info_del (void)
168 {
169     struct ke_info *ki;
170
171     assert (ke_info_list);
172     ki = ke_info_list;
173     str_queue_rm (&ki->queue);
174     ke_info_list = ki->next;
175     free (ki);
176 }
177
178 /*
179  * catch_child: catch SIGCHLD. Set email kernel pid to -1
180  *              to indicate that child has exited
181  */
182 static void catch_child (int num)
183 {
184     pid_t pid;
185     struct ke_info *ki;
186
187     while ((pid=waitpid (-1, 0, WNOHANG)) > 0)
188     {
189         for (ki = ke_info_list; ki; ki = ki->next)
190             if (ki->pid == pid)
191                 ki->pid = -1;
192         --no_process;
193     }
194     signal (SIGCHLD, catch_child);
195 }
196
197 /* 
198  * catch_int: catch SIGHUP.
199  */
200 static void catch_hup (int num)
201 {
202     got_sighup = 1;
203     signal (SIGHUP, catch_hup);
204 }
205
206 /* 
207  * catch_int: catch SIGTERM.
208  */
209 static void catch_term (int num)
210 {
211     got_term = 1;
212     signal (SIGTERM, catch_term);
213 }
214
215 /* 
216  * catch_int: catch SIGINT.
217  */
218 static void catch_int (int num)
219 {
220     got_int = 1;
221     signal (SIGINT, catch_int);
222 }
223
224 /*
225  * pipe_handle: handle SIGPIPE when transferring message to kernel
226  */
227 static void pipe_handle (int dummy)
228 {
229     longjmp (retry_jmp, 1);
230 }
231
232 /*
233  * start_kernel: start email kernel.
234  * argc:    argc of email kernel
235  * argv:    argv of email kernel
236  * id:      email user-id
237  * return:  pid of email kernel child
238  */
239 static pid_t start_kernel (int argc, char **argv, int id)
240 {
241     pid_t pid;
242     int i;
243     char **argv_p;
244     char userid_option[20];
245
246     argv_p = malloc (sizeof(*argv_p)*(argc+2));
247     if (!argv_p)
248     {
249         gw_log (GW_LOG_FATAL|GW_LOG_ERRNO, module, "malloc fail");
250         exit (1);
251     }
252     argv_p[0] = "kernel";
253     for (i = 1; i<argc; i++)
254         argv_p[i] = argv[i];
255     sprintf (userid_option, "-i%d", id);
256     argv_p[i++] = userid_option;
257     argv_p[i++] = NULL;
258
259     gw_log (GW_LOG_DEBUG, module, "Starting kernel");
260     pid = fork ();
261     if (pid == -1)
262     {
263         gw_log (GW_LOG_FATAL|GW_LOG_ERRNO, module, "fork");
264         exit (1);
265     }
266     if (!pid)
267     {
268         execv ("kernel", argv_p);
269         gw_log (GW_LOG_FATAL|GW_LOG_ERRNO, module, "execvp");
270         exit (1);
271     }
272     return pid;
273 }
274
275 /*
276  * deliver: deliver message to child (email kernel).
277  * argc:      exec argc to child (if it need to be started)
278  * argv:      exec argv to child (if it need to be started)
279  * id:        email userid
280  * queue:     message queue to be transferred
281  * gip:       pointer to FIFO info. if *gip is NULL prior invocation
282  *            it will be created (initialized) and the pointer will be
283  *            updated.
284  * pidp:      pointer to pid. Will hold process-id of child (if it need to
285  *            be started)
286  * dont_exec: if non-zero a child will never be started; otherwise child
287  *            will be started if not already running.
288  * return:    0 if message couldn't be transferred, i.e. dont_exec is non-zero
289  *              and the child is not already running. 
290  *            1 if message was transferred and the child was already running.
291  *            2 if message was transferred and the child was started and
292  *              dont_exec was zero.
293  *            3 serious error. Permissions denied or kernel couldn't be
294  *              started at all.
295  */
296 static int deliver (int argc, char **argv, int id, struct str_queue *queue,
297                     GIP *gip, pid_t *pidp, int dont_exec)
298 {
299     int pass = 0;
300     int r;
301     int index;
302     char fifo_server_name[128];
303     char fifo_client_name[128];
304     void (*oldsig)();
305     const char *msg;
306
307     sprintf (fifo_server_name, "fifo.s.%d", id);
308     sprintf (fifo_client_name, "fifo.c.%d", id);
309
310     assert (gip);
311     if (!*gip)
312         *gip = gipc_initialize (fifo_client_name);
313
314     oldsig = signal (SIGPIPE, pipe_handle);
315     setjmp (retry_jmp);
316     ++pass;
317     if (pass == 1)
318     {                                  /* assume child is running */
319         gipc_close (*gip);             /* shut down existing FIFOs */
320         r = gipc_open (*gip, fifo_server_name, 0);  /* try re-open ... */
321     }
322     else if (pass == 2)
323     {                                  /* assume child is NOT running */
324         pid_t pid;
325
326         if (dont_exec)
327         {                              /* we aren't allowed to start */
328             signal (SIGPIPE, oldsig);
329             return 0;
330         }
331         mknod (fifo_server_name, S_IFIFO|0666, 0);
332         pid = start_kernel (argc, argv, id);
333         if (pidp)                      /* set pid of child */
334             *pidp = pid;
335         r = gipc_open (*gip, fifo_server_name, 1);
336     }
337     else
338     {                                  /* message couldn't be transferred */
339         signal (SIGPIPE, oldsig);
340         gw_log (GW_LOG_WARN, module, "Cannot start kernel");
341         return 3;
342     }
343     if (r < 0)                         /* gipc_open fail? */
344     {
345         if (r == -2)
346             gw_log (GW_LOG_DEBUG|GW_LOG_ERRNO, module, "r==-2");
347         else if (r == -1)
348             gw_log (GW_LOG_DEBUG|GW_LOG_ERRNO, module, "r==-1");
349         else
350             gw_log (GW_LOG_WARN|GW_LOG_ERRNO, module, "gipc_open");
351         longjmp (retry_jmp, 1);        /* yet another pass */
352     }
353     index = 0;                         /* transfer. may be interrupted */
354     while ((msg = str_queue_get (queue, index++)))
355         gip_wline (*gip, msg);
356     signal (SIGPIPE, oldsig);
357     return pass;                       /* successful transfer */
358 }
359
360 /* 
361  * monitor_events: Event loop of monitor
362  * argc:    argc of monitor (used in exec of Email kernel children)
363  * argv:    argv of monitor (used in exec of Email kernel children)
364  */
365 static void monitor_events (int argc, char **argv)
366 {
367     GIP gip_m;
368     int r, gip_m_fd;
369     char line_buf[1024];
370     fd_set set_r;
371     char command[128], *cp;
372
373     gip_m = gips_initialize (MONITOR_FIFO_S);
374     r = gips_open (gip_m, MONITOR_FIFO_C);
375     gip_m_fd = gip_infileno (gip_m);
376     open (MONITOR_FIFO_S, O_WRONLY);
377
378     while (1)
379     {
380         int fd_max;
381         struct ke_info *ki;
382
383         while (1)
384         {
385             if (got_sighup)
386             {
387                 gw_log (GW_LOG_STAT, module, "Got SIGHUP. Reading resources");
388                 reread_resources ();
389                 got_sighup = 0;
390             }
391             if (got_term)
392             {
393                 gw_log (GW_LOG_STAT, module, "Got SIGTERM. Exiting...");
394                 unlink (MONITOR_FIFO_S);
395                 unlink (MONITOR_FIFO_C);
396                 exit (0);
397             }
398             if (got_int)
399             {
400                 gw_log (GW_LOG_STAT, module, "Got SIGINT. Exiting...");
401                 unlink (MONITOR_FIFO_S);
402                 unlink (MONITOR_FIFO_C);
403                 exit (0);
404             }
405             /* deliver any unsent messages to Email kernels */
406             for (ki = ke_info_list; ki; ki = ki->next)
407             {
408                 if (!ki->queue)
409                     continue;
410                 gw_log (GW_LOG_DEBUG, module, "Transfer mail to %d", ki->id);
411                 r = deliver (argc, argv, ki->id, ki->queue, &ki->gip, &ki->pid,
412                              no_process >= max_process);
413                 if (r == 2)             /* new child was spawned? */
414                     ++no_process;
415                 if (r == 1 || r == 2)   /* transfer at all? */
416                     str_queue_rm (&ki->queue);
417             }
418             FD_ZERO (&set_r);
419             FD_SET (gip_m_fd, &set_r);
420             gw_log (GW_LOG_DEBUG, module, "set gip_m_fd %d", gip_m_fd);
421             fd_max = gip_m_fd;
422             
423             for (ki = ke_info_list; ki; ki = ki->next)
424             {
425                 int fd;
426                 if (ki->gip)
427                 {
428                     if (ki->pid == -1)
429                     {                    /* child has exited */
430                         gw_log (GW_LOG_DEBUG, module, "Close of %d", ki->id);
431                         gipc_close (ki->gip);
432                         gipc_destroy (ki->gip);
433                         ki->gip = NULL;
434                     }
435                     else if ((fd = gip_infileno (ki->gip)) != -1)
436                     {                    /* read select on child FIFO */
437                         gw_log (GW_LOG_DEBUG, module, "set fd %d", fd);
438                         FD_SET (fd, &set_r);
439                         if (fd > fd_max)
440                             fd_max = fd;
441                     }
442                 }
443             }
444             gw_log (GW_LOG_DEBUG, module, "Cur/Max processes %d/%d",
445                     no_process, max_process);
446             gw_log (GW_LOG_DEBUG, module, "IPC select");
447             r = select (fd_max+1, &set_r, NULL, NULL, NULL);
448             if (r != -1)
449                 break;
450             if (errno != EINTR)
451             {   /* select aborted. And it was not due to interrupt */
452                 gw_log (GW_LOG_FATAL|GW_LOG_ERRNO, module, "select");
453                 exit (1);
454             }
455             /* select was interrupted. Probably child has died */
456             gw_log (GW_LOG_DEBUG|GW_LOG_ERRNO, module, "select");
457         }
458         /* go through list of Email kernels. See if any message has arrived */
459         gw_log (GW_LOG_DEBUG, module, "Testing ke_info_list");
460         for (ki = ke_info_list; ki; ki = ki->next)
461         {
462             int fd;
463             if (ki->gip && (fd = gip_infileno (ki->gip)) != -1)
464             {
465                 gw_log (GW_LOG_DEBUG, module, "Test of %d", fd);
466                 if (FD_ISSET (fd, &set_r))
467                 {
468                     if (lgets (line_buf, sizeof(line_buf)-1, fd))
469                     {
470                         gw_log (GW_LOG_DEBUG, module, "IPC: %s", line_buf);
471                     }
472                     else
473                     {
474                         gw_log (GW_LOG_DEBUG, module, "Close of %d", ki->id);
475                         gipc_close (ki->gip);
476                         gipc_destroy (ki->gip);
477                         ki->gip = NULL;
478                     }
479                 }
480             }
481         }
482         /* see if any message from eti has arrived */
483         gw_log (GW_LOG_DEBUG, module, "Testing gip_m_fd %d", gip_m_fd);
484         if (FD_ISSET (gip_m_fd, &set_r))
485         {
486             gw_log (GW_LOG_DEBUG, module, "Reading from %d", gip_m_fd);
487             if (!(lgets (command, sizeof(command)-1, gip_m_fd)))
488             {
489                 gw_log (GW_LOG_FATAL, module, "Unexpected close");
490                 exit (1);
491             }
492             gw_log (GW_LOG_DEBUG, module, "Done");
493             if ((cp = strchr (command, '\n')))
494                 *cp = '\0';
495             gw_log (GW_LOG_DEBUG, module, "IPC: %s", command);
496             if (!memcmp (command, "eti ", 4))
497             {
498                 int id = atoi (command+4);
499                 struct ke_info *new_k;
500                 
501                 new_k = ke_info_add (id);
502                 gw_log (GW_LOG_DEBUG, module, "Incoming mail %d", id);
503                 
504                 if (!new_k->queue)
505                 {
506                     if (!(new_k->queue = str_queue_mk ()))
507                     {
508                         gw_log (GW_LOG_FATAL|GW_LOG_ERRNO, module,
509                                 "str_queue_mk");
510                         exit (1);
511                     }
512                 }
513                 str_queue_enq (new_k->queue, "mail\n");
514                 while (lgets (line_buf, sizeof(line_buf)-1, gip_m_fd))
515                     str_queue_enq (new_k->queue, line_buf);
516                 str_queue_enq (new_k->queue, "\001");
517             }
518         }
519     }
520 }
521
522 /*
523  * main: main of monitor
524  */
525 int main (int argc, char **argv)
526 {
527     int argno = 0;
528
529     gw_log_init (*argv);
530     while (++argno < argc)
531     {
532         if (argv[argno][0] == '-')
533         {
534             switch (argv[argno][1])
535             {
536             case 'H':
537                 fprintf (stderr, "monitor [option..] [resource]\n");
538                 fprintf (stderr, "If no resource file is given");
539                 fprintf (stderr, " default.res is used\n");
540                 fprintf (stderr, "Options are transferred to kernel\n");
541                 exit (1);
542             case 'd':
543                 gw_log_level (GW_LOG_ALL & ~RES_DEBUG);
544                 break;
545             case 'D':
546                 gw_log_level (GW_LOG_ALL);
547                 break;
548             }
549         }
550         else
551             default_res = argv[argno];
552     }
553     reread_resources ();
554     signal (SIGCHLD, catch_child);
555     signal (SIGHUP, catch_hup);
556     signal (SIGTERM, catch_term);
557     signal (SIGINT, catch_int);
558 #if 0
559     gw_log_file (GW_LOG_ALL, "monitor.log");
560 #endif
561     monitor_events (argc, argv);
562     exit (0);
563 }