]> andersk Git - moira.git/blob - server/mr_main.c
7337f4551f385cd0d728686be164cc9106d1ca4b
[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 #include <krb.h>
32 #include <krb5.h>
33
34 RCSID("$Header$");
35
36 client *cur_client;
37
38 char *whoami;
39 char *takedown;
40 FILE *journal;
41
42 time_t now;
43
44 char *host;
45 char krb_realm[REALM_SZ];
46 krb5_context context = NULL;
47
48 /* Client array and associated data. This needs to be global for _list_users */
49 client **clients;
50 int nclients, clientssize;
51
52 int dormant;
53 int child_exited_abnormally = 0;
54 int child_pid, child_signal, child_status;
55
56 void reapchild(int x);
57 void godormant(int x);
58 void gowakeup(int x);
59 void clist_append(client *cp);
60 void mr_setup_signals(void);
61
62 /*
63  * Main Moira server loop.
64  *
65  * Initialize the world, then start accepting connections and
66  * making progress on current connections.
67  */
68
69 int main(int argc, char **argv)
70 {
71   int status, i, listener;
72   time_t tardy;
73   char *port, *p;
74   extern char *database;
75   struct stat stbuf;
76   struct utsname uts;
77   fd_set readfds, writefds, xreadfds, xwritefds;
78   int nfds, counter = 0;
79
80   whoami = argv[0];
81   /*
82    * Error handler init.
83    */
84   mr_init();
85   set_com_err_hook(mr_com_err);
86   setvbuf(stderr, NULL, _IOLBF, BUFSIZ);
87
88   port = strchr(MOIRA_SERVER, ':') + 1;
89
90   for (i = 1; i < argc; i++)
91     {
92       if (!strcmp(argv[i], "-db") && i + 1 < argc)
93         {
94           database = argv[i + 1];
95           i++;
96         }
97       else if (!strcmp(argv[i], "-p") && i + 1 < argc)
98         {
99           port = argv[i + 1];
100           i++;
101         }
102       else
103         {
104           com_err(whoami, 0, "Usage: moirad [-db database][-p port]");
105           exit(1);
106         }
107     }
108
109   krb_get_lrealm(krb_realm, 1);
110
111   status = krb5_init_context(&context);
112   if (status)
113     {
114       com_err(whoami, status, "Initializing krb5 context.");
115       exit(1);
116     }
117
118   /*
119    * Database initialization.  Only init if database should be open.
120    */
121
122   if (stat(MOIRA_MOTD_FILE, &stbuf) != 0)
123     {
124       if ((status = mr_open_database()))
125         {
126           com_err(whoami, status, "trying to open database.");
127           exit(1);
128         }
129       sanity_check_database();
130     }
131   else
132     {
133       dormant = ASLEEP;
134       com_err(whoami, 0, "sleeping, not opening database");
135     }
136
137   sanity_check_queries();
138
139   /*
140    * Get moira server hostname for authentication
141    */
142   if (uname(&uts) < 0)
143     {
144       com_err(whoami, errno, "Unable to get local hostname");
145       exit(1);
146     }
147   host = canonicalize_hostname(xstrdup(uts.nodename));
148   for (p = host; *p && *p != '.'; p++)
149     {
150       if (isupper(*p))
151         *p = tolower(*p);
152     }
153   *p = '\0';
154
155   /*
156    * Set up client array handler.
157    */
158   nclients = 0;
159   clientssize = 10;
160   clients = xmalloc(clientssize * sizeof(client *));
161
162   mr_setup_signals();
163
164   journal = fopen(JOURNAL, "a");
165   if (!journal)
166     {
167       com_err(whoami, errno, "opening journal file");
168       exit(1);
169     }
170
171   /*
172    * Establish template connection.
173    */
174   listener = mr_listen(port);
175   if (listener == -1)
176     {
177       com_err(whoami, MR_ABORTED, "trying to create listening connection");
178       exit(1);
179     }
180   FD_ZERO(&xreadfds);
181   FD_ZERO(&xwritefds);
182   FD_SET(listener, &xreadfds);
183   nfds = listener + 1;
184
185   com_err(whoami, 0, "started (pid %d)", getpid());
186   com_err(whoami, 0, rcsid);
187   if (dormant != ASLEEP)
188     send_zgram("MOIRA", "server started");
189   else
190     send_zgram("MOIRA", "server started, but database closed");
191
192   /*
193    * Run until shut down.
194    */
195   while (!takedown)
196     {
197       int i;
198       struct timeval timeout = {60, 0}; /* 1 minute */
199
200       /* If we're supposed to go down and we can, do it */
201       if (((dormant == AWAKE) && (nclients == 0) &&
202            (stat(MOIRA_MOTD_FILE, &stbuf) == 0)) ||
203           (dormant == SLEEPY))
204         {
205           mr_close_database();
206           com_err(whoami, 0, "database closed");
207           mr_setup_signals();
208           send_zgram("MOIRA", "database closed");
209           dormant = ASLEEP;
210         }
211
212       /* Block until something happens. */
213       memcpy(&readfds, &xreadfds, sizeof(readfds));
214       memcpy(&writefds, &xwritefds, sizeof(writefds));
215       if (select(nfds, &readfds, &writefds, NULL, &timeout) == -1)
216         {
217           if (errno != EINTR)
218             com_err(whoami, errno, "in select");
219           if (!inc_running || now - inc_started > INC_TIMEOUT)
220             next_incremental();
221           continue;
222         }
223
224       if (takedown)
225         break;
226
227       if (child_exited_abnormally)
228         {
229           critical_alert("moirad", "%d: child exits with signal %d status %d",
230                          child_pid, child_signal, child_status);
231           child_exited_abnormally = 0;
232         }
233
234       time(&now);
235       if (!inc_running || now - inc_started > INC_TIMEOUT)
236         next_incremental();
237       tardy = now - 30 * 60;
238
239       /* If we're asleep and we should wake up, do it */
240       if ((dormant == ASLEEP) && (stat(MOIRA_MOTD_FILE, &stbuf) == -1) &&
241           (errno == ENOENT))
242         {
243           mr_open_database();
244           com_err(whoami, 0, "database open");
245           mr_setup_signals();
246           send_zgram("MOIRA", "database open again");
247           dormant = AWAKE;
248         }
249
250       /* Handle any new connections */
251       if (FD_ISSET(listener, &readfds))
252         {
253           int newconn, addrlen = sizeof(struct sockaddr_in);
254           struct sockaddr_in addr;
255           client *cp;
256
257           newconn = accept(listener, (struct sockaddr *)&addr, &addrlen);
258           if (newconn == -1)
259             com_err(whoami, errno, "accepting new connection");
260           else if (newconn > 0)
261             {
262               if (newconn + 1 > nfds)
263                 nfds = newconn + 1;
264               FD_SET(newconn, &xreadfds);
265
266               /* Add a new client to the array */
267               nclients++;
268               if (nclients > clientssize)
269                 {
270                   clientssize = 2 * clientssize;
271                   clients = xrealloc(clients, clientssize * sizeof(client *));
272                 }
273
274               clients[nclients - 1] = cp = xmalloc(sizeof(client));
275               memset(cp, 0, sizeof(client));
276               cp->con = newconn;
277               cp->id = counter++;
278               cp->last_time_used = now;
279               cp->haddr = addr;
280               cp->tuplessize = 1;
281               cp->tuples = xmalloc(sizeof(mr_params));
282               memset(cp->tuples, 0, sizeof(mr_params));
283               cp->state = CL_ACCEPTING;
284               cp->version = 2;
285
286               cur_client = cp;
287               com_err(whoami, 0,
288                       "New connection from %s port %d (now %d client%s)",
289                       inet_ntoa(cp->haddr.sin_addr),
290                       (int)ntohs(cp->haddr.sin_port),
291                       nclients, nclients != 1 ? "s" : "");
292             }
293         }
294
295       /* Handle any existing connections. */
296       for (i = 0; i < nclients; i++)
297         {
298           cur_client = clients[i];
299
300           if (FD_ISSET(clients[i]->con, &writefds))
301             {
302               client_write(clients[i]);
303               if (!clients[i]->ntuples)
304                 {
305                   FD_CLR(clients[i]->con, &xwritefds);
306                   FD_SET(clients[i]->con, &xreadfds);
307                 }
308               clients[i]->last_time_used = now;
309             }
310
311           if (FD_ISSET(clients[i]->con, &readfds))
312             {
313               if (clients[i]->state == CL_ACCEPTING)
314                 {
315                   switch(mr_cont_accept(clients[i]->con,
316                                         &clients[i]->hsbuf,
317                                         &clients[i]->hslen))
318                     {
319                     case -1:
320                       break;
321
322                     case 0:
323                       clients[i]->state = CL_CLOSING;
324                       break;
325
326                     default:
327                       clients[i]->state = CL_ACTIVE;
328                       clients[i]->hsbuf = NULL;
329                       break;
330                     }
331                 }
332               else
333                 {
334                   client_read(clients[i]);
335                   if (clients[i]->ntuples)
336                     {
337                       FD_CLR(clients[i]->con, &xreadfds);
338                       FD_SET(clients[i]->con, &xwritefds);
339                     }
340                   clients[i]->last_time_used = now;
341                 }
342             }
343
344           if (clients[i]->last_time_used < tardy)
345             {
346               com_err(whoami, 0, "Shutting down connection due to inactivity");
347               clients[i]->state = CL_CLOSING;
348             }
349
350           if (clients[i]->state == CL_CLOSING)
351             {
352               client *old;
353
354               com_err(whoami, 0, "Closed connection (now %d client%s, "
355                       "%d queries)", nclients - 1, nclients != 2 ? "s" : "",
356                       newqueries);
357
358               shutdown(clients[i]->con, 2);
359               close(clients[i]->con);
360               FD_CLR(clients[i]->con, &xreadfds);
361               FD_CLR(clients[i]->con, &xwritefds);
362               free_rtn_tuples(clients[i]);
363               free(clients[i]->tuples);
364               free(clients[i]->hsbuf);
365               old = clients[i];
366               clients[i] = clients[--nclients];
367               free(old);
368             }
369
370           cur_client = NULL;
371           if (takedown)
372             break;
373         }
374     }
375
376   com_err(whoami, 0, "%s", takedown);
377   if (dormant != ASLEEP)
378     mr_close_database();
379   send_zgram("MOIRA", takedown);
380   return 0;
381 }
382
383 void reapchild(int x)
384 {
385   int status, pid;
386
387   while ((pid = waitpid(-1, &status, WNOHANG)) > 0)
388     {
389       if (pid == inc_pid)
390         inc_running = 0;
391       if (!takedown && (WTERMSIG(status) != 0 || WEXITSTATUS(status) != 0))
392         {
393           child_exited_abnormally = 1;
394           child_pid = pid;
395           child_signal = WTERMSIG(status);
396           child_status = WEXITSTATUS(status);
397         }
398     }
399 }
400
401
402 void godormant(int x)
403 {
404   switch (dormant)
405     {
406     case AWAKE:
407     case GROGGY:
408       com_err(whoami, 0, "requested to go dormant");
409       break;
410     case ASLEEP:
411       com_err(whoami, 0, "already asleep");
412       break;
413     case SLEEPY:
414       break;
415     }
416   dormant = SLEEPY;
417 }
418
419
420 void gowakeup(int x)
421 {
422   switch (dormant)
423     {
424     case ASLEEP:
425     case SLEEPY:
426       com_err(whoami, 0, "Good morning");
427       break;
428     case AWAKE:
429       com_err(whoami, 0, "already awake");
430       break;
431     case GROGGY:
432       break;
433     }
434   dormant = GROGGY;
435 }
436
437
438 void mr_setup_signals(void)
439 {
440   struct sigaction action;
441
442   action.sa_flags = 0;
443   sigemptyset(&action.sa_mask);
444
445   /* There should probably be a few more of these. */
446
447   action.sa_handler = sigshut;
448   if ((sigaction(SIGTERM, &action, NULL) < 0) ||
449       (sigaction(SIGINT, &action, NULL) < 0) ||
450       (sigaction(SIGHUP, &action, NULL) < 0))
451     {
452       com_err(whoami, errno, "Unable to establish signal handlers.");
453       exit(1);
454     }
455
456   action.sa_handler = godormant;
457   if (sigaction(SIGUSR1, &action, NULL) < 0)
458     {
459       com_err(whoami, errno, "Unable to establish signal handlers.");
460       exit(1);
461     }
462
463   action.sa_handler = gowakeup;
464   if (sigaction(SIGUSR2, &action, NULL) < 0)
465     {
466       com_err(whoami, errno, "Unable to establish signal handlers.");
467       exit(1);
468     }
469
470   action.sa_handler = SIG_IGN;
471   if (sigaction(SIGPIPE, &action, NULL) < 0)
472     {
473       com_err(whoami, errno, "Unable to establish signal handlers.");
474       exit(1);
475     }
476
477   action.sa_handler = reapchild;
478   sigaddset(&action.sa_mask, SIGCHLD);
479   if (sigaction(SIGCHLD, &action, NULL) < 0)
480     {
481       com_err(whoami, errno, "Unable to establish signal handlers.");
482       exit(1);
483     }
484 }
This page took 0.084787 seconds and 3 git commands to generate.