]> andersk Git - moira.git/blob - server/mr_main.c
Change `SMS' to `Moira' where possible.
[moira.git] / server / mr_main.c
1 /*
2  *      $Source$
3  *      $Author$
4  *      $Header$
5  *
6  *      Copyright (C) 1987 by the Massachusetts Institute of Technology
7  *      For copying and distribution information, please see the file
8  *      <mit-copyright.h>.
9  *
10  *      Moira server process.
11  *
12  *      Most of this is stolen from ../gdb/tsr.c
13  *
14  *      You are in a maze of twisty little finite automata, all different.
15  *      Let the reader beware.
16  *
17  */
18
19 static char *rcsid_mr_main_c = "$Header$";
20
21 #include <mit-copyright.h>
22 #include <string.h>
23 #include <stdio.h>
24 #include <sys/types.h>
25 #include <sys/errno.h>
26 #include <sys/signal.h>
27 #include <sys/wait.h>
28 #include <sys/stat.h>
29 #include <unistd.h>
30 #include <signal.h>
31 #include "mr_server.h"
32 #include <krb_et.h>
33 #include <gdss_et.h>
34 #include <arpa/inet.h>
35
36 extern CONNECTION newconn, listencon;
37
38 extern int nclients;
39 extern client **clients, *cur_client;
40
41 extern OPERATION listenop;
42 extern LIST_OF_OPERATIONS op_list;
43
44 extern struct sockaddr_in client_addr;
45 extern int client_addrlen;
46 extern TUPLE client_tuple;
47
48 extern char *whoami;
49 extern char buf1[BUFSIZ];
50 extern char *takedown;
51 extern int errno;
52 extern FILE *journal;
53
54 extern time_t now;
55
56 char hostbuf[BUFSIZ], *host;
57
58 int do_listen(char *port);
59 void do_reset_listen(void);
60 void clist_append(client *cp);
61 void oplist_append(LIST_OF_OPERATIONS *oplp, OPERATION op);
62 void oplist_delete(LIST_OF_OPERATIONS oplp, OPERATION op);
63 void mr_setup_signals(void);
64 int new_connection(void);
65
66 /*
67  * Main Moira server loop.
68  *
69  * Initialize the world, then start accepting connections and
70  * making progress on current connections.
71  */
72
73 int main(int argc, char **argv)
74 {
75   int status, i;
76   time_t tardy;
77   char *port, *p;
78   extern char *database;
79   struct stat stbuf;
80
81   whoami = argv[0];
82   /*
83    * Error handler init.
84    */
85   initialize_sms_error_table();
86   initialize_krb_error_table();
87   initialize_gdss_error_table();
88   set_com_err_hook(mr_com_err);
89   setvbuf(stderr, NULL, _IOLBF, BUFSIZ);
90
91   port = strchr(MOIRA_SERVER, ':') + 1;
92
93   for (i = 1; i < argc; i++)
94     {
95       if (!strcmp(argv[i], "-db") && i + 1 < argc)
96         {
97           database = argv[i + 1];
98           i++;
99         }
100       else if (!strcmp(argv[i], "-p") && i + 1 < argc)
101         {
102           port = argv[i + 1];
103           i++;
104         }
105       else
106         {
107           com_err(whoami, 0, "Usage: moirad [-db database][-p port]");
108           exit(1);
109         }
110     }
111
112   /*
113    * GDB initialization.
114    */
115   if (gdb_init() != 0)
116     {
117       com_err(whoami, 0, "GDB initialization failed.");
118       exit(1);
119     }
120   gdb_debug(0); /* this can be patched, if necessary, to enable */
121                 /* GDB level debugging .. */
122   krb_realm = malloc(REALM_SZ);
123   krb_get_lrealm(krb_realm, 1);
124
125   /*
126    * Database initialization.  Only init if database should be open.
127    */
128
129   if (stat(MOIRA_MOTD_FILE, &stbuf) != 0)
130     {
131       if ((status = mr_open_database()))
132         {
133           com_err(whoami, status, " when trying to open database.");
134           exit(1);
135         }
136       sanity_check_database();
137     }
138   else
139     {
140       dormant = ASLEEP;
141       com_err(whoami, 0, "sleeping, not opening database");
142     }
143
144   sanity_check_queries();
145
146   /*
147    * Get moira server hostname for authentication
148    */
149   if (gethostname(hostbuf, sizeof(hostbuf)) < 0)
150     {
151       com_err(whoami, errno, "Unable to get local hostname");
152       exit(1);
153     }
154   host = canonicalize_hostname(strsave(hostbuf));
155   for (p = host; *p && *p != '.'; p++)
156     {
157       if (isupper(*p))
158         *p = tolower(*p);
159     }
160   *p = '\0';
161
162   /*
163    * Set up client array handler.
164    */
165   nclients = 0;
166   clients = malloc(0);
167
168   mr_setup_signals();
169
170   journal = fopen(JOURNAL, "a");
171   if (!journal)
172     {
173       com_err(whoami, errno, " while opening journal file");
174       exit(1);
175     }
176
177   /*
178    * Establish template connection.
179    */
180   if ((status = do_listen(port)))
181     {
182       com_err(whoami, status, " while trying to create listening connection");
183       exit(1);
184     }
185
186   op_list = create_list_of_operations(1, listenop);
187
188   com_err(whoami, 0, "started (pid %d)", getpid());
189   com_err(whoami, 0, rcsid_mr_main_c);
190   if (dormant != ASLEEP)
191     send_zgram("MOIRA", "server started");
192   else
193     send_zgram("MOIRA", "server started, but database closed");
194
195   /*
196    * Run until shut down.
197    */
198   while (!takedown)
199     {
200       int i;
201       /*
202        * Block until something happens.
203        */
204       if (dormant == SLEEPY)
205         {
206           mr_close_database();
207           com_err(whoami, 0, "database closed");
208           mr_setup_signals();
209           send_zgram("MOIRA", "database closed");
210           dormant = ASLEEP;
211         }
212       else if (dormant == GROGGY)
213         {
214           mr_open_database();
215           com_err(whoami, 0, "database open");
216           mr_setup_signals();
217           send_zgram("MOIRA", "database open again");
218           dormant = AWAKE;
219         }
220
221       errno = 0;
222       status = op_select_any(op_list, 0, NULL, NULL, NULL, NULL);
223       if (status == -1)
224         {
225           if (errno != EINTR)
226             com_err(whoami, errno, " error from op_select");
227           if (!inc_running || now - inc_started > INC_TIMEOUT)
228             next_incremental();
229           continue;
230         }
231       else if (status != -2)
232         {
233           com_err(whoami, 0, " wrong return from op_select_any");
234           continue;
235         }
236       if (takedown)
237         break;
238       time(&now);
239       if (!inc_running || now - inc_started > INC_TIMEOUT)
240         next_incremental();
241
242       /*
243        * Handle any new connections; this comes first so
244        * errno isn't tromped on.
245        */
246       if (OP_DONE(listenop))
247         {
248           if (OP_STATUS(listenop) == OP_CANCELLED)
249             {
250               if (errno == EWOULDBLOCK)
251                 do_reset_listen();
252               else
253                 {
254                   static int count = 0;
255                   com_err(whoami, errno, " error (%d) on listen", count);
256                   if (count++ > 10)
257                     exit(1);
258                 }
259             }
260           else if ((status = new_connection()))
261             {
262               com_err(whoami, errno, " Error on listening operation.");
263               /*
264                * Sleep here to prevent hosing?
265                */
266             }
267           /* if the new connection is our only connection,
268            * and the server is supposed to be down, then go
269            * down now.
270            */
271           if ((dormant == AWAKE) && (nclients == 1) &&
272               (stat(MOIRA_MOTD_FILE, &stbuf) == 0))
273             {
274               com_err(whoami, 0, "motd file exists, slumbertime");
275               dormant = SLEEPY;
276             }
277           /* on new connection, if we are no longer supposed
278            * to be down, then wake up.
279            */
280           if ((dormant == ASLEEP) && (stat(MOIRA_MOTD_FILE, &stbuf) == -1) &&
281               (errno == ENOENT))
282             {
283               com_err(whoami, 0, "motd file no longer exists, waking up");
284               dormant = GROGGY;
285             }
286         }
287       /*
288        * Handle any existing connections.
289        */
290       tardy = now - 30 * 60;
291
292       for (i = 0; i < nclients; i++)
293         {
294           cur_client = clients[i];
295           if (OP_DONE(clients[i]->pending_op))
296             {
297               cur_client->last_time_used = now;
298               do_client(cur_client);
299             }
300           else if (clients[i]->last_time_used < tardy)
301             {
302               com_err(whoami, 0, "Shutting down connection due to inactivity");
303               shutdown(cur_client->con->in.fd, 2);
304             }
305           cur_client = NULL;
306           if (takedown)
307             break;
308         }
309     }
310   com_err(whoami, 0, "%s", takedown);
311   if (dormant != ASLEEP)
312     mr_close_database();
313   send_zgram("MOIRA", takedown);
314   return 0;
315 }
316
317 /*
318  * Set up the template connection and queue the first accept.
319  */
320
321 int do_listen(char *port)
322 {
323   listencon = create_listening_connection(port);
324
325   if (!listencon)
326     return errno;
327
328   listenop = create_operation();
329   client_addrlen = sizeof(client_addr);
330
331   start_accepting_client(listencon, listenop, &newconn, (char *)&client_addr,
332                          &client_addrlen, &client_tuple);
333   return 0;
334 }
335
336
337 void do_reset_listen(void)
338 {
339   client_addrlen = sizeof(client_addr);
340   start_accepting_client(listencon, listenop, &newconn, (char *)&client_addr,
341                          &client_addrlen, &client_tuple);
342 }
343
344 /*
345  * This routine is called when a new connection comes in.
346  *
347  * It sets up a new client and adds it to the list of currently active clients.
348  */
349 int new_connection(void)
350 {
351   client *cp;
352   static counter = 0;
353
354   /*
355    * Make sure there's been no error
356    */
357   if (OP_STATUS(listenop) != OP_COMPLETE)
358     return errno;
359
360   if (!newconn)
361     return MR_NOT_CONNECTED;
362
363   /*
364    * Set up the new connection and reply to the client
365    */
366   cp = malloc(sizeof(client));
367   memset(cp, 0, sizeof(*cp));
368   cp->action = CL_ACCEPT;
369   cp->con = newconn;
370   cp->id = counter++;
371   cp->args = NULL;
372   cp->clname[0] = NULL;
373   cp->reply.mr_argv = NULL;
374   cp->first = NULL;
375   cp->last = NULL;
376   cp->last_time_used = now;
377   newconn = NULL;
378
379   cp->pending_op = create_operation();
380   reset_operation(cp->pending_op);
381   oplist_append(&op_list, cp->pending_op);
382   cur_client = cp;
383
384   /*
385    * Add a new client to the array..
386    */
387   clist_append(cp);
388
389   /*
390    * Let him know we heard him.
391    */
392   start_replying_to_client(cp->pending_op, cp->con, GDB_ACCEPTED, "", "");
393
394   cp->haddr = client_addr;
395
396   /*
397    * Log new connection.
398    */
399   com_err(whoami, 0, "New connection from %s port %d (now %d client%s)",
400           inet_ntoa(cp->haddr.sin_addr), (int)ntohs(cp->haddr.sin_port),
401           nclients, nclients != 1 ? "s" : "");
402
403   /*
404    * Get ready to accept the next connection.
405    */
406   reset_operation(listenop);
407   client_addrlen = sizeof(client_addr);
408
409   start_accepting_client(listencon, listenop, &newconn, (char *)&client_addr,
410                          &client_addrlen, &client_tuple);
411   return 0;
412 }
413
414 /*
415  * Add a new client to the known clients.
416  */
417 void clist_append(client *cp)
418 {
419   client **clients_n;
420
421   nclients++;
422   clients_n = malloc(nclients * sizeof(client *));
423   memcpy(clients_n, clients, (nclients - 1) * sizeof(cp));
424   clients_n[nclients - 1] = cp;
425   free(clients);
426   clients = clients_n;
427   clients_n = NULL;
428 }
429
430
431 void clist_delete(client *cp)
432 {
433   client **clients_n, **scpp, **dcpp; /* source and dest client ptr ptr */
434
435   int found_it = 0;
436
437   clients_n = malloc((nclients - 1) * sizeof(client *));
438   for (scpp = clients, dcpp = clients_n; scpp < clients + nclients; )
439     {
440       if (*scpp != cp)
441         *dcpp++ = *scpp++;
442       else
443         {
444           scpp++;
445           if (found_it)
446             abort();
447           found_it = 1;
448         }
449     }
450   --nclients;
451   free(clients);
452   clients = clients_n;
453   clients_n = NULL;
454   oplist_delete(op_list, cp->pending_op);
455   reset_operation(cp->pending_op);
456   delete_operation(cp->pending_op);
457   sever_connection(cp->con);
458   free(cp);
459 }
460
461 /*
462  * Add a new operation to a list of operations.
463  *
464  * This should be rewritten to use realloc instead, since in most
465  * cases it won't have to copy the array.
466  */
467
468 void oplist_append(LIST_OF_OPERATIONS *oplp, OPERATION op)
469 {
470   int count = (*oplp)->count + 1;
471   LIST_OF_OPERATIONS newlist = (LIST_OF_OPERATIONS)
472     db_alloc(size_of_list_of_operations(count));
473   memcpy(newlist, *oplp, size_of_list_of_operations((*oplp)->count));
474   newlist->count++;
475   newlist->op[count - 1] = op;
476   db_free(*oplp, size_of_list_of_operations(count - 1));
477   *oplp = newlist;
478 }
479
480 void oplist_delete(LIST_OF_OPERATIONS oplp, OPERATION op)
481 {
482   OPERATION *s;
483   int c;
484
485   for (s = oplp->op, c = oplp->count; c; --c, ++s)
486     {
487       if (*s == op)
488         {
489           while (c > 0)
490             {
491               *s = *(s + 1);
492               ++s;
493               --c;
494             }
495           oplp->count--;
496           return;
497         }
498     }
499   abort();
500 }
501
502
503 void reapchild(int x)
504 {
505   int status, pid;
506
507   while ((pid = waitpid(-1, &status, WNOHANG)) > 0)
508     {
509       if (pid == inc_pid)
510         inc_running = 0;
511       if (!takedown && (WTERMSIG(status) != 0 || WEXITSTATUS(status) != 0))
512         com_err(whoami, 0, "%d: child exits with signal %d status %d",
513                 pid, WTERMSIG(status), WEXITSTATUS(status));
514     }
515 }
516
517
518 void godormant(int x)
519 {
520   switch (dormant)
521     {
522     case AWAKE:
523     case GROGGY:
524       com_err(whoami, 0, "requested to go dormant");
525       break;
526     case ASLEEP:
527       com_err(whoami, 0, "already asleep");
528       break;
529     case SLEEPY:
530       break;
531     }
532   dormant = SLEEPY;
533 }
534
535
536 void gowakeup(int x)
537 {
538   switch (dormant)
539     {
540     case ASLEEP:
541     case SLEEPY:
542       com_err(whoami, 0, "Good morning");
543       break;
544     case AWAKE:
545       com_err(whoami, 0, "already awake");
546       break;
547     case GROGGY:
548       break;
549     }
550   dormant = GROGGY;
551 }
552
553
554 void mr_setup_signals(void)
555 {
556   struct sigaction action;
557
558   action.sa_flags = 0;
559   sigemptyset(&action.sa_mask);
560
561   /* There should probably be a few more of these. */
562
563   action.sa_handler = sigshut;
564   if ((sigaction(SIGTERM, &action, NULL) < 0) ||
565       (sigaction(SIGINT, &action, NULL) < 0) ||
566       (sigaction(SIGHUP, &action, NULL) < 0))
567     {
568       com_err(whoami, errno, "Unable to establish signal handlers.");
569       exit(1);
570     }
571
572   action.sa_handler = godormant;
573   if (sigaction(SIGUSR1, &action, NULL) < 0)
574     {
575       com_err(whoami, errno, "Unable to establish signal handlers.");
576       exit(1);
577     }
578
579   action.sa_handler = gowakeup;
580   if (sigaction(SIGUSR2, &action, NULL) < 0)
581     {
582       com_err(whoami, errno, "Unable to establish signal handlers.");
583       exit(1);
584     }
585
586   action.sa_handler = reapchild;
587   sigaddset(&action.sa_mask, SIGCHLD);
588   if (sigaction(SIGCHLD, &action, NULL) < 0)
589     {
590       com_err(whoami, errno, "Unable to establish signal handlers.");
591       exit(1);
592     }
593 }
This page took 0.081212 seconds and 5 git commands to generate.