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