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