]> andersk Git - moira.git/blob - server/mr_main.c
added support for server going dormant
[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  *      SMS 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_sms_main_c = "$Header$";
20
21 #include <mit-copyright.h>
22 #include <strings.h>
23 #include <sys/types.h>
24 #include <sys/errno.h>
25 #include <sys/signal.h>
26 #include <sys/wait.h>
27 #include <sys/stat.h>
28 #include "sms_server.h"
29 #include <krb_et.h>
30
31 extern CONNECTION newconn, listencon;
32
33 extern int nclients;
34 extern client **clients, *cur_client;
35
36 extern OPERATION listenop;
37 extern LIST_OF_OPERATIONS op_list;
38
39 extern struct sockaddr_in client_addr;
40 extern int client_addrlen;
41 extern TUPLE client_tuple;
42
43 extern char *whoami;
44 extern char buf1[BUFSIZ];
45 extern char *takedown;
46 extern int errno;
47 extern FILE *journal;
48 #define JOURNAL "/u1/sms/journal"
49
50 extern char *malloc();
51 extern int free();
52 extern char *inet_ntoa();
53 extern void sms_com_err();
54 extern void do_client();
55
56 extern int sigshut();
57 void clist_append();
58 void oplist_append();
59 void reapchild(), godormant(), gowakeup();
60
61 extern time_t now;
62
63 /*
64  * Main SMS server loop.
65  *
66  * Initialize the world, then start accepting connections and
67  * making progress on current connections.
68  */
69
70 /*ARGSUSED*/
71 int
72 main(argc, argv)
73         int argc;
74         char **argv;
75 {
76         int status;
77         time_t tardy;
78         struct stat stbuf;
79         
80         whoami = argv[0];
81         /*
82          * Error handler init.
83          */
84         initialize_sms_error_table();
85         initialize_krb_error_table();
86         set_com_err_hook(sms_com_err);
87         setlinebuf(stderr);
88         
89         if (argc != 1) {
90                 com_err(whoami, 0, "Usage: smsd");
91                 exit(1);
92         }               
93
94         /* Profiling implies that getting rid of one level of call
95          * indirection here wins us maybe 1% on the VAX.
96          */
97         gdb_amv = malloc;
98         gdb_fmv = free;
99         
100         /*
101          * GDB initialization.
102          */
103         if(gdb_init() != 0) {
104                 com_err(whoami, 0, "GDB initialization failed.");
105                 exit(1);
106         }
107         gdb_debug(0); /* this can be patched, if necessary, to enable */
108                       /* GDB level debugging .. */
109         krb_realm = malloc(REALM_SZ);
110         get_krbrlm(krb_realm, 1);
111         
112         /*
113          * Database initialization.
114          */
115
116         if ((status = sms_open_database()) != 0) {
117                 com_err(whoami, status, " when trying to open database.");
118                 exit(1);
119         }
120         
121         sanity_check_queries();
122         sanity_check_database();
123
124         /*
125          * Set up client array handler.
126          */
127         nclients = 0;
128         clients = (client **) malloc(0);
129         
130         /*
131          * Signal handlers
132          *      There should probably be a few more of these. This is
133          *      duplicated on the next page, be sure to also add any
134          *      additional handlers there.
135          */
136         
137         if ((((int)signal (SIGTERM, sigshut)) < 0) ||
138             (((int)signal (SIGCHLD, reapchild)) < 0) ||
139             (((int)signal (SIGUSR1, godormant)) < 0) ||
140             (((int)signal (SIGUSR2, gowakeup)) < 0) ||
141             (((int)signal (SIGHUP, sigshut)) < 0)) {
142                 com_err(whoami, errno, " Unable to establish signal handlers.");
143                 exit(1);
144         }
145         
146         journal = fopen(JOURNAL, "a");
147         if (journal == NULL) {
148             com_err(whoami, errno, " while opening journal file");
149             exit(1);
150         }
151
152         /*
153          * Establish template connection.
154          */
155         if ((status = do_listen()) != 0) {
156                 com_err(whoami, status,
157                         " while trying to create listening connection");
158                 exit(1);
159         }
160         
161         op_list = create_list_of_operations(1, listenop);
162         
163         com_err(whoami, 0, "started (pid %d)", getpid());
164         com_err(whoami, 0, rcsid_sms_main_c);
165         send_zgram("SMS", "server started");
166
167         /*
168          * Run until shut down.
169          */
170         while(!takedown) {              
171                 register int i;
172                 /*
173                  * Block until something happens.
174                  */
175 #ifdef notdef
176                 com_err(whoami, 0, "tick");
177 #endif notdef
178                 if (dormant == SLEEPY) {
179                     sms_close_database();
180                     com_err(whoami, 0, "database closed");
181                     send_zgram("SMS", "database closed");
182                     dormant = ASLEEP;
183                 } else if (dormant == GROGGY) {
184                     sms_open_database();
185                     com_err(whoami, 0, "database open");
186                     if ((((int)signal (SIGTERM, sigshut)) < 0) ||
187                         (((int)signal (SIGCHLD, reapchild)) < 0) ||
188                         (((int)signal (SIGUSR1, godormant)) < 0) ||
189                         (((int)signal (SIGUSR2, gowakeup)) < 0) ||
190                         (((int)signal (SIGHUP, sigshut)) < 0)) {
191                         com_err(whoami, errno,
192                                 " Unable to reestablish signal handlers.");
193                         exit(1);
194                     }
195                     send_zgram("SMS", "database open again");
196                     dormant = AWAKE;
197                 }
198
199                 errno = 0;
200                 status = op_select_any(op_list, 0,
201                                        (fd_set *)NULL, (fd_set *)NULL,
202                                        (fd_set *)NULL, (struct timeval *)NULL);
203
204                 if (status == -1) {
205                         com_err(whoami, errno, " error from op_select");
206                         continue;
207                 } else if (status != -2) {
208                         com_err(whoami, 0, " wrong return from op_select_any");
209                         continue;
210                 }
211                 if (takedown) break;
212                 time(&now);
213 #ifdef notdef
214                 fprintf(stderr, "    tick\n");
215 #endif notdef
216                 /*
217                  * Handle any new connections; this comes first so
218                  * errno isn't tromped on.
219                  */
220                 if (OP_DONE(listenop)) {
221                         if (OP_STATUS(listenop) == OP_CANCELLED) {
222                                 if (errno == EWOULDBLOCK) {
223                                         do_reset_listen();
224                                 } else {
225                                         com_err(whoami, errno,
226                                                 " error on listen");
227                                         exit(1);
228                                 }
229                         } else if ((status = new_connection()) != 0) {
230                                 com_err(whoami, errno,
231                                         " Error on listening operation.");
232                                 /*
233                                  * Sleep here to prevent hosing?
234                                  */
235                         }
236                         /* if the new connection is our only connection,
237                          * and the server is supposed to be down, then go
238                          * down now.
239                          */
240                         if ((dormant == AWAKE) && (nclients == 1) &&
241                             (stat(SMS_MOTD_FILE, &stbuf) == 0)) {
242                             com_err(whoami, 0, "motd file exists, slumbertime");
243                             dormant = SLEEPY;
244                         }
245                         /* on new connection, if we are no longer supposed
246                          * to be down, then wake up.
247                          */
248                         if ((dormant == ASLEEP) &&
249                             (stat(SMS_MOTD_FILE, &stbuf) == -1) &&
250                             (errno == ENOENT)) {
251                             com_err(whoami, 0, "motd file no longer exists, waking up");
252                             dormant = GROGGY;
253                         }
254                           
255                 }
256                 /*
257                  * Handle any existing connections.
258                  */
259                 tardy = now - 30*60;
260                 
261                 for (i=0; i<nclients; i++) {
262                         cur_client = clients[i];
263                         if (OP_DONE(clients[i]->pending_op)) {
264                                 cur_client->last_time_used = now;
265                                 do_client(cur_client);
266                         } else if (clients[i]->last_time_used < tardy) {
267                                 com_err(whoami, 0, "Shutting down connection due to inactivity");
268                                 shutdown(cur_client->con->in.fd, 0);
269                         }
270                         cur_client = NULL;
271                         if (takedown) break;
272                 }
273         }
274         com_err(whoami, 0, "%s", takedown);
275         sms_close_database();
276         send_zgram("SMS", takedown);
277         return 0;
278 }
279
280 /*
281  * Set up the template connection and queue the first accept.
282  */
283
284 int
285 do_listen()
286 {
287         char *service = index(SMS_GDB_SERV, ':') + 1;
288
289         listencon = create_listening_connection(service);
290
291         if (listencon == NULL)
292                 return errno;
293
294         listenop = create_operation();
295         client_addrlen = sizeof(client_addr);
296
297         start_accepting_client(listencon, listenop, &newconn,
298                                (char *)&client_addr,
299                                &client_addrlen, &client_tuple);
300         return 0;
301 }
302
303
304 do_reset_listen()
305 {
306         client_addrlen = sizeof(client_addr);
307         start_accepting_client(listencon, listenop, &newconn,
308                                (char *)&client_addr,
309                                &client_addrlen, &client_tuple);
310 }
311
312 /*
313  * This routine is called when a new connection comes in.
314  *
315  * It sets up a new client and adds it to the list of currently active clients.
316  */
317 int
318 new_connection()
319 {
320         register client *cp;
321         static counter = 0;
322         
323         /*
324          * Make sure there's been no error
325          */
326         if(OP_STATUS(listenop) != OP_COMPLETE) {
327                 return errno;
328         }
329         
330         if (newconn == NULL) {
331                 return SMS_NOT_CONNECTED;
332         }
333
334         /*
335          * Set up the new connection and reply to the client
336          */
337         cp = (client *)malloc(sizeof *cp);
338         bzero(cp, sizeof(*cp));
339         cp->action = CL_ACCEPT;
340         cp->con = newconn;
341         cp->id = counter++;
342         cp->args = NULL;
343         cp->clname = NULL;
344         cp->reply.sms_argv = NULL;
345         cp->first = NULL;
346         cp->last = NULL;
347         cp->last_time_used = now;
348         newconn = NULL;
349         
350         cp->pending_op = create_operation();
351         reset_operation(cp->pending_op);
352         oplist_append(&op_list, cp->pending_op);
353         cur_client = cp;
354         
355         /*
356          * Add a new client to the array..
357          */
358         clist_append(cp);
359         
360         /*
361          * Let him know we heard him.
362          */
363         start_replying_to_client(cp->pending_op, cp->con, GDB_ACCEPTED,
364                                  "", "");
365
366         cp->haddr = client_addr;
367         
368         /*
369          * Log new connection.
370          */
371         
372         com_err(whoami, 0, "New connection from %s port %d (now %d client%s)",
373                 inet_ntoa(cp->haddr.sin_addr),
374                 (int)ntohs(cp->haddr.sin_port),
375                 nclients,
376                 nclients!=1?"s":"");
377         
378         /*
379          * Get ready to accept the next connection.
380          */
381         reset_operation(listenop);
382         client_addrlen = sizeof(client_addr);
383
384         start_accepting_client(listencon, listenop, &newconn,
385                                (char *)&client_addr,
386                                &client_addrlen, &client_tuple);
387         return 0;
388 }
389
390 /*
391  * Add a new client to the known clients.
392  */
393 void
394 clist_append(cp)
395         client *cp;
396 {               
397         client **clients_n;
398         
399         nclients++;
400         clients_n = (client **)malloc
401                 ((unsigned)(nclients * sizeof(client *)));
402         bcopy((char *)clients, (char *)clients_n, (nclients-1)*sizeof(cp));
403         clients_n[nclients-1] = cp;
404         free((char *)clients);
405         clients = clients_n;
406         clients_n = NULL;
407 }
408
409                 
410 void
411 clist_delete(cp)
412         client *cp;
413 {
414         client **clients_n, **scpp, **dcpp; /* source and dest client */
415                                             /* ptr ptr */
416         
417         int found_it = 0;
418         
419         clients_n = (client **)malloc
420                 ((unsigned)((nclients - 1)* sizeof(client *)));
421         for (scpp = clients, dcpp = clients_n; scpp < clients+nclients; ) {
422                 if (*scpp != cp) {
423                         *dcpp++ = *scpp++;
424                 } else {
425                         scpp++;
426                         if (found_it) abort();
427                         found_it = 1;
428                 }                       
429         }
430         --nclients;     
431         free((char *)clients);
432         clients = clients_n;
433         clients_n = NULL;
434         oplist_delete(op_list, cp->pending_op);
435         reset_operation(cp->pending_op);
436         delete_operation(cp->pending_op);
437         sever_connection(cp->con);
438         free((char *)cp);
439 }
440
441 /*
442  * Add a new operation to a list of operations.
443  *
444  * This should be rewritten to use realloc instead, since in most
445  * cases it won't have to copy the array.
446  */
447
448 void
449 oplist_append(oplp, op)
450         LIST_OF_OPERATIONS *oplp;
451         OPERATION op;
452 {
453         int count = (*oplp)->count+1;
454         LIST_OF_OPERATIONS newlist = (LIST_OF_OPERATIONS)
455                 db_alloc(size_of_list_of_operations(count));
456         bcopy((char *)(*oplp), (char *)newlist,
457               size_of_list_of_operations((*oplp)->count));
458         newlist->count++;
459         newlist->op[count-1] = op;
460         db_free((char *)(*oplp), size_of_list_of_operations(count-1));
461         (*oplp) = newlist;
462 }
463
464
465 oplist_delete(oplp, op)
466         LIST_OF_OPERATIONS oplp;
467         register OPERATION op;
468 {
469         register OPERATION *s;
470         register int c;
471         
472         for (s = oplp->op, c=oplp->count; c; --c, ++s) {
473                 if (*s == op) {
474                         while (c > 0) {
475                                 *s = *(s+1);
476                                 ++s;
477                                 --c;
478                         }
479                         oplp->count--;
480                         return;
481                 }
482         }
483         abort();
484 }
485
486
487 void reapchild()
488 {
489     union wait status;
490     int pid;
491
492     if (takedown || dormant == ASLEEP)
493       return;
494     while ((pid = wait3(&status, WNOHANG, (struct rusage *)0)) > 0) {
495         if  (status.w_termsig == 0 && status.w_retcode == 0)
496           com_err(whoami, 0, "child exited successfully");
497         else
498           com_err(whoami, 0, "%d: child exits with signal %d status %d",
499                   pid, status.w_termsig, status.w_retcode);
500     }
501 }
502
503
504 void godormant()
505 {
506     switch (dormant) {
507     case AWAKE:
508     case GROGGY:
509         com_err(whoami, 0, "requested to go dormant");
510         break;
511     case ASLEEP:
512         com_err(whoami, 0, "already asleep");
513         break;
514     case SLEEPY:
515         break;
516     }
517     dormant = SLEEPY;
518 }
519
520
521 void gowakeup()
522 {
523     switch (dormant) {
524     case ASLEEP:
525     case SLEEPY:
526         com_err(whoami, 0, "Good morning");
527         break;
528     case AWAKE:
529         com_err(whoami, 0, "already awake");
530         break;
531     case GROGGY:
532         break;
533     }
534     dormant = GROGGY;
535 }
This page took 0.364159 seconds and 5 git commands to generate.