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