CCL commands stop/continue implemented. New functions gw_res_{int,bool}
[egate.git] / kernel / monitor.c
1 /* Gateway Resource Monitor
2  * Europagate, 1995
3  *
4  * $Log: monitor.c,v $
5  * Revision 1.5  1995/05/03 07:37:42  adam
6  * CCL commands stop/continue implemented. New functions gw_res_{int,bool}
7  * are used when possible.
8  *
9  * Revision 1.4  1995/05/02  15:26:00  adam
10  * Monitor observes death of child (email kernel). The number
11  * of simultanous processes is controlled now. Email requests are
12  * queued if necessary. This scheme should only be forced if no kernels
13  * are idle.
14  *
15  * Revision 1.3  1995/05/02  07:20:10  adam
16  * Use pid of exited child to close fifos.
17  *
18  * Revision 1.2  1995/05/01  16:26:57  adam
19  * More work on resource monitor.
20  *
21  * Revision 1.1  1995/05/01  12:43:36  adam
22  * First work on resource monitor program.
23  *
24  */
25
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <assert.h>
29 #include <ctype.h>
30 #include <string.h>
31 #include <unistd.h>
32 #include <fcntl.h>
33 #include <setjmp.h>
34 #include <signal.h>
35
36 #include <sys/file.h>
37 #include <sys/stat.h>
38 #include <sys/types.h>
39 #include <sys/time.h>
40 #include <sys/wait.h>
41
42 #include <gw-log.h>
43 #include <gw-res.h>
44 #include <gip.h>
45 #include <strqueue.h>
46 #include <lgets.h>
47
48 #define LINE_MAX 1024
49
50 #define MONITOR_FIFO_S "fifo.s.m"
51 #define MONITOR_FIFO_C "fifo.c.m"
52
53 static char *module = "monitor";
54 static jmp_buf retry_jmp;
55
56 static GwRes monitor_res = NULL;
57 static int no_process = 0;
58 static int max_process = 1;
59 static int got_sighup = 0;
60 static int got_term = 0;
61 const char *default_res = "default.res";
62
63 static void reread_resources (void)
64 {
65     if (monitor_res)
66         gw_res_close (monitor_res);
67     monitor_res = gw_res_init ();
68     if (gw_res_merge (monitor_res, default_res))
69     {
70         gw_log (GW_LOG_WARN, module, "Couldn't read resource file %s",
71                 default_res);
72         exit (1);
73     }
74     max_process = gw_res_int (monitor_res, "gw.max.process", 10);
75 }
76
77 struct ke_info {
78     pid_t pid;
79     int id;
80     GIP gip;
81     struct str_queue *queue;
82     struct ke_info *next;
83 };
84
85 struct ke_info *ke_info_list = NULL;
86
87 struct ke_info *ke_info_add (int id)
88 {
89     struct ke_info **kip;
90
91     for (kip = &ke_info_list; *kip; kip= &(*kip)->next)
92         if ((*kip)->id == id)
93             return *kip;
94     *kip = malloc (sizeof(**kip));
95     assert (*kip);
96     (*kip)->next = NULL;
97     (*kip)->id = id;
98     (*kip)->gip = NULL;
99     (*kip)->queue = NULL;
100     return *kip;
101 }
102
103 static void ke_info_del (void)
104 {
105     struct ke_info *ki;
106
107     assert (ke_info_list);
108     ki = ke_info_list;
109     str_queue_rm (&ki->queue);
110     ke_info_list = ki->next;
111     free (ki);
112 }
113
114 static void catch_child (int num)
115 {
116     pid_t pid;
117     struct ke_info *ki;
118
119     while ((pid=waitpid (-1, 0, WNOHANG)) > 0)
120     {
121         for (ki = ke_info_list; ki; ki = ki->next)
122             if (ki->pid == pid)
123                 ki->pid = -1;
124         --no_process;
125     }
126     signal (SIGCHLD, catch_child);
127 }
128
129 static void catch_hup (int num)
130 {
131     got_sighup = 1;
132     signal (SIGHUP, catch_hup);
133 }
134
135 static void catch_term (int num)
136 {
137     got_term = 1;
138     signal (SIGTERM, catch_term);
139 }
140
141 static void pipe_handle (int dummy)
142 {
143     longjmp (retry_jmp, 1);
144 }
145
146 static pid_t start_kernel (int argc, char **argv, int id)
147 {
148     pid_t pid;
149     int i;
150     char **argv_p;
151     char userid_option[20];
152
153     argv_p = malloc (sizeof(*argv_p)*(argc+2));
154     if (!argv_p)
155     {
156         gw_log (GW_LOG_FATAL|GW_LOG_ERRNO, module, "malloc fail");
157         exit (1);
158     }
159     argv_p[0] = "kernel";
160     for (i = 1; i<argc; i++)
161         argv_p[i] = argv[i];
162     sprintf (userid_option, "-i%d", id);
163     argv_p[i++] = userid_option;
164     argv_p[i++] = NULL;
165
166     gw_log (GW_LOG_DEBUG, module, "Starting kernel");
167     pid = fork ();
168     if (pid == -1)
169     {
170         gw_log (GW_LOG_FATAL|GW_LOG_ERRNO, module, "fork");
171         exit (1);
172     }
173     if (!pid)
174     {
175         execv ("kernel", argv_p);
176         gw_log (GW_LOG_FATAL|GW_LOG_ERRNO, module, "execvp");
177         exit (1);
178     }
179     return pid;
180 }
181
182 static int deliver (int argc, char **argv, int id, struct str_queue *queue,
183                     GIP *gip, pid_t *pidp, int dont_exec)
184 {
185     int pass = 0;
186     int r;
187     int index;
188     char fifo_server_name[128];
189     char fifo_client_name[128];
190     void (*oldsig)();
191     const char *msg;
192
193     sprintf (fifo_server_name, "fifo.s.%d", id);
194     sprintf (fifo_client_name, "fifo.c.%d", id);
195
196     assert (gip);
197     if (!*gip)
198         *gip = gipc_initialize (fifo_client_name);
199
200     oldsig = signal (SIGPIPE, pipe_handle);
201     setjmp (retry_jmp);
202     ++pass;
203     if (pass == 1)
204     {
205         gipc_close (*gip);
206         r = gipc_open (*gip, fifo_server_name, 0);
207     }
208     else if (pass == 2)
209     {
210         pid_t pid;
211
212         if (dont_exec)
213         {
214             signal (SIGPIPE, oldsig);
215             return 0;
216         }
217         mknod (fifo_server_name, S_IFIFO|0666, 0);
218         pid = start_kernel (argc, argv, id);
219         if (pidp)
220             *pidp = pid;
221         r = gipc_open (*gip, fifo_server_name, 1);
222     }
223     else
224     {
225         signal (SIGPIPE, oldsig);
226         gw_log (GW_LOG_WARN, module, "Cannot start kernel");
227         return 0;
228     }
229     if (r < 0)
230     {
231         if (r == -2)
232         {
233             gw_log (GW_LOG_DEBUG|GW_LOG_ERRNO, module, "r==-2");
234             longjmp (retry_jmp, 1);
235         }
236         else if (r == -1)
237         {
238             gw_log (GW_LOG_DEBUG|GW_LOG_ERRNO, module, "r==-1");
239             longjmp (retry_jmp, 1);
240         }
241         else
242         {
243             gw_log (GW_LOG_WARN|GW_LOG_ERRNO, module, "gipc_open");
244         }
245     }
246     index = 0;
247     while ((msg = str_queue_get (queue, index++)))
248         gip_wline (*gip, msg);
249     signal (SIGPIPE, oldsig);
250     return pass;
251 }
252
253 static void monitor_events (int argc, char **argv)
254 {
255     GIP gip_m;
256     int r, gip_m_fd;
257     char line_buf[1024];
258     fd_set set_r;
259     char command[128], *cp;
260
261     gip_m = gips_initialize (MONITOR_FIFO_S);
262     r = gips_open (gip_m, MONITOR_FIFO_C);
263     gip_m_fd = gip_infileno (gip_m);
264     open (MONITOR_FIFO_S, O_WRONLY);
265
266     while (1)
267     {
268         int fd_max;
269         struct ke_info *ki;
270
271         while (1)
272         {
273             if (got_sighup)
274             {
275                 gw_log (GW_LOG_STAT, module, "Got SIGHUP. Reading resources");
276                 reread_resources ();
277                 got_sighup = 0;
278             }
279             if (got_term)
280             {
281                 gw_log (GW_LOG_STAT, module, "Got SIGTERM. Exiting...");
282                 unlink (MONITOR_FIFO_S);
283                 unlink (MONITOR_FIFO_C);
284                 exit (0);
285             }
286             for (ki = ke_info_list; ki; ki = ki->next)
287             {
288                 if (!ki->queue)
289                     continue;
290                 gw_log (GW_LOG_DEBUG, module, "Transfer mail to %d", ki->id);
291                 r = deliver (argc, argv, ki->id, ki->queue, &ki->gip, &ki->pid,
292                              no_process >= max_process);
293                 if (r == 2)
294                     ++no_process;
295                 if (r == 1 || r == 2)
296                     str_queue_rm (&ki->queue);
297             }
298             FD_ZERO (&set_r);
299             FD_SET (gip_m_fd, &set_r);
300             gw_log (GW_LOG_DEBUG, module, "set gip_m_fd %d", gip_m_fd);
301             fd_max = gip_m_fd;
302             
303             for (ki = ke_info_list; ki; ki = ki->next)
304             {
305                 int fd;
306                 if (ki->gip)
307                 {
308                     if (ki->pid == -1)
309                     {
310                         gw_log (GW_LOG_DEBUG, module, "Close of %d", ki->id);
311                         gipc_close (ki->gip);
312                         gipc_destroy (ki->gip);
313                         ki->gip = NULL;
314                     }
315                     else if ((fd = gip_infileno (ki->gip)) != -1)
316                     {
317                         gw_log (GW_LOG_DEBUG, module, "set fd %d", fd);
318                         FD_SET (fd, &set_r);
319                         if (fd > fd_max)
320                             fd_max = fd;
321                     }
322                 }
323             }
324             gw_log (GW_LOG_DEBUG, module, "Cur/Max processes %d/%d",
325                     no_process, max_process);
326             gw_log (GW_LOG_DEBUG, module, "IPC select");
327             r = select (fd_max+1, &set_r, NULL, NULL, NULL);
328             if (r != -1)
329                 break;
330             if (errno != EINTR)
331             {
332                 gw_log (GW_LOG_FATAL|GW_LOG_ERRNO, module, "select");
333                 exit (1);
334             }
335             gw_log (GW_LOG_DEBUG|GW_LOG_ERRNO, module, "select");
336         }
337         gw_log (GW_LOG_DEBUG, module, "Testing ke_info_list");
338         for (ki = ke_info_list; ki; ki = ki->next)
339         {
340             int fd;
341             if (ki->gip && (fd = gip_infileno (ki->gip)) != -1)
342             {
343                 gw_log (GW_LOG_DEBUG, module, "Test of %d", fd);
344                 if (FD_ISSET (fd, &set_r))
345                 {
346                     if (lgets (line_buf, sizeof(line_buf)-1, fd))
347                     {
348                         gw_log (GW_LOG_DEBUG, module, "IPC: %s", line_buf);
349                     }
350                     else
351                     {
352                         gw_log (GW_LOG_DEBUG, module, "Close of %d", ki->id);
353                         gipc_close (ki->gip);
354                         gipc_destroy (ki->gip);
355                         ki->gip = NULL;
356                     }
357                 }
358             }
359         }
360         gw_log (GW_LOG_DEBUG, module, "Testing gip_m_fd %d", gip_m_fd);
361         if (FD_ISSET (gip_m_fd, &set_r))
362         {
363             gw_log (GW_LOG_DEBUG, module, "Reading from %d", gip_m_fd);
364             if (!(lgets (command, sizeof(command)-1, gip_m_fd)))
365             {
366                 gw_log (GW_LOG_FATAL, module, "Unexpected close");
367                 exit (1);
368             }
369             gw_log (GW_LOG_DEBUG, module, "Done");
370             if ((cp = strchr (command, '\n')))
371                 *cp = '\0';
372             gw_log (GW_LOG_DEBUG, module, "IPC: %s", command);
373             if (!memcmp (command, "eti ", 4))
374             {
375                 int id = atoi (command+4);
376                 struct ke_info *new_k;
377                 
378                 new_k = ke_info_add (id);
379                 gw_log (GW_LOG_DEBUG, module, "Incoming mail %d", id);
380                 
381                 if (!new_k->queue)
382                 {
383                     if (!(new_k->queue = str_queue_mk ()))
384                     {
385                         gw_log (GW_LOG_FATAL|GW_LOG_ERRNO, module,
386                                 "str_queue_mk");
387                         exit (1);
388                     }
389                 }
390                 str_queue_enq (new_k->queue, "mail\n");
391                 while (lgets (line_buf, sizeof(line_buf)-1, gip_m_fd))
392                     str_queue_enq (new_k->queue, line_buf);
393                 str_queue_enq (new_k->queue, "\001");
394             }
395         }
396     }
397 }
398
399 int main (int argc, char **argv)
400 {
401     int argno = 0;
402
403     gw_log_init (*argv);
404     while (++argno < argc)
405     {
406         if (argv[argno][0] == '-')
407         {
408             switch (argv[argno][1])
409             {
410             case 'H':
411                 fprintf (stderr, "monitor [option..] [resource]\n");
412                 fprintf (stderr, "If no resource file is given");
413                 fprintf (stderr, " default.res is used\n");
414                 fprintf (stderr, "Options are transferred to kernel\n");
415                 exit (1);
416             case 'd':
417                 gw_log_level (GW_LOG_ALL & ~RES_DEBUG);
418                 break;
419             case 'D':
420                 gw_log_level (GW_LOG_ALL);
421                 break;
422             }
423         }
424         else
425             default_res = argv[argno];
426     }
427     reread_resources ();
428     signal (SIGCHLD, catch_child);
429     signal (SIGHUP, catch_hup);
430     signal (SIGTERM, catch_term);
431 #if 0
432     gw_log_file (GW_LOG_ALL, "monitor.log");
433 #endif
434     monitor_events (argc, argv);
435     exit (0);
436 }