source: trunk/third/moira/server/mr_main.c @ 23740

Revision 23740, 10.5 KB checked in by broder, 16 years ago (diff)
In moira: * New CVS snapshot (Trac: #195) * Drop patches that have been incorporated upstream. * Update to build without krb4 on systems that no longer have it. This doesn't build yet on squeeze, which lacks a krb4 library, but I'm committing now before I start hacking away at a patch to fix that.
Line 
1/* $Id: mr_main.c,v 1.54 2006-08-22 17:36:26 zacheiss Exp $
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
34RCSID("$Header: /afs/athena.mit.edu/astaff/project/moiradev/repository/moira/server/mr_main.c,v 1.54 2006-08-22 17:36:26 zacheiss Exp $");
35
36client *cur_client;
37
38char *whoami;
39char *takedown;
40FILE *journal;
41
42time_t now;
43
44char *host;
45char krb_realm[REALM_SZ];
46krb5_context context = NULL;
47
48/* Client array and associated data. This needs to be global for _list_users */
49client **clients;
50int nclients, clientssize;
51
52int dormant;
53int child_exited_abnormally = 0;
54int child_pid, child_signal, child_status;
55
56void reapchild(int x);
57void godormant(int x);
58void gowakeup(int x);
59void clist_append(client *cp);
60void 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
69int 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
383void 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
402void 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
420void 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
438void 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}
Note: See TracBrowser for help on using the repository browser.