chiark / gitweb /
Import release 0.1.4
[secnet.git] / secnet.c
1 /* $Log: secnet.c,v $
2  * Revision 1.1  1996/03/13 22:27:41  sde1000
3  * Initial revision
4  *
5  */
6
7 extern char version[];
8
9 #include "secnet.h"
10 #include <stdio.h>
11 #include <string.h>
12 #include <getopt.h>
13 #include <errno.h>
14 #include <unistd.h>
15 #include <sys/socket.h>
16 #include <arpa/inet.h>
17 #include <pwd.h>
18
19 #include "util.h"
20 #include "conffile.h"
21
22 /* Command-line options (possibly config-file options too) */
23 static char *configfile="/etc/secnet/secnet.conf";
24 bool_t just_check_config=False;
25 static char *userid=NULL;
26 static uid_t uid=0;
27 static bool_t background=True;
28 static char *pidfile=NULL;
29 bool_t require_root_privileges=False;
30 string_t require_root_privileges_explanation=NULL;
31
32 /* Structures dealing with poll() call */
33 struct poll_interest {
34     beforepoll_fn *before;
35     afterpoll_fn *after;
36     void *state;
37     uint32_t max_nfds;
38     uint32_t nfds;
39     string_t desc;
40     struct poll_interest *next;
41 };
42 static struct poll_interest *reg=NULL;
43 static uint32_t total_nfds=10;
44
45 static bool_t finished=False;
46
47 /* Parse the command line options */
48 static void parse_options(int argc, char **argv)
49 {
50     int c;
51
52     while (True) {
53         int option_index = 0;
54         static struct option long_options[] = {
55             {"verbose", 0, 0, 'v'},
56             {"nowarnings", 0, 0, 'w'},
57             {"help", 0, 0, 2},
58             {"version", 0, 0, 1},
59             {"nodetach", 0, 0, 'n'},
60             {"silent", 0, 0, 'f'},
61             {"quiet", 0, 0, 'f'},
62             {"debug", 1, 0, 'd'},
63             {"config", 1, 0, 'c'},
64             {"just-check-config", 0, 0, 'j'},
65             {0,0,0,0}
66         };
67
68         c=getopt_long(argc, argv, "vwdnjc:ft:",
69                       long_options, &option_index);
70         if (c==-1)
71             break;
72
73         switch(c) {
74         case 2:
75             /* Help */
76             fprintf(stderr,
77                     "Usage: secnet [OPTION]...\n\n"
78                     "  -f, --silent, --quiet   suppress error messages\n"
79                     "  -w, --nowarnings        suppress warnings\n"
80                     "  -v, --verbose           output extra diagnostics\n"
81                     "  -c, --config=filename   specify a configuration file\n"
82                     "  -j, --just-check-config stop after reading configfile\n"
83                     "  -n, --nodetach          do not run in background\n"
84                     "  -d, --debug=item,...    set debug options\n"
85                     "      --help              display this help and exit\n"
86                     "      --version           output version information and exit\n"
87                 );
88             exit(0);
89             break;
90       
91         case 1:
92             /* Version */
93             fprintf(stderr,"%s\n",version);
94             exit(0);
95             break;
96
97         case 'v':
98             message_level|=M_INFO|M_WARNING|M_ERROR|M_FATAL;
99             break;
100
101         case 'n':
102             background=False;
103             break;
104
105         case 'd':
106             message_level|=M_DEBUG_CONFIG|M_DEBUG_PHASE;
107             break;
108
109         case 'f':
110             message_level=M_FATAL;
111             break;
112
113         case 'c':
114             if (optarg)
115                 configfile=safe_strdup(optarg,"config_filename");
116             else
117                 fatal("secnet: no config filename specified");
118             break;
119
120         case 'j':
121             just_check_config=True;
122             break;
123
124         case '?':
125             break;
126
127         default:
128             Message(M_WARNING,"secnet: Unknown getopt code %c\n",c);
129         }
130     }
131
132     if (argc-optind != 0) {
133         Message(M_WARNING,"secnet: You gave extra command line parameters, "
134                 "which were ignored.\n");
135     }
136 }
137
138 static void setup(dict_t *config)
139 {
140     list_t *l;
141     item_t *site;
142     dict_t *system;
143     struct log_if *log;
144     struct passwd *pw;
145     struct cloc loc;
146     int i;
147
148     l=dict_lookup(config,"system");
149
150     if (!l || list_elem(l,0)->type!=t_dict) {
151         fatal("configuration does not include a \"system\" dictionary\n");
152     }
153     system=list_elem(l,0)->data.dict;
154     loc=list_elem(l,0)->loc;
155
156     /* Arrange systemwide log facility */
157     l=dict_lookup(system,"log");
158     if (!l) {
159         fatal("configuration does not include a system/log facility\n");
160     }
161     log=init_log(l);
162     log->log(log->st,LOG_DEBUG,"%s: logging started",version);
163
164     /* Who are we supposed to run as? */
165     userid=dict_read_string(system,"userid",False,"system",loc);
166     if (userid) {
167         do {
168             pw=getpwent();
169             if (pw && strcmp(pw->pw_name,userid)==0) {
170                 uid=pw->pw_uid;
171                 break;
172             }
173         } while(pw);
174         endpwent();
175         if (uid==0) {
176             fatal("userid \"%s\" not found\n",userid);
177         }
178     }
179
180     /* Pidfile name */
181     pidfile=dict_read_string(system,"pidfile",False,"system",loc);
182
183     /* Check whether we need root privileges */
184     if (require_root_privileges && uid!=0) {
185         fatal("the following configured feature (\"%s\") requires "
186               "that secnet retain root privileges while running.\n",
187               require_root_privileges_explanation);
188     }
189
190     /* Go along site list, starting sites */
191     l=dict_lookup(config,"sites");
192     if (!l) {
193         Message(M_WARNING,"secnet: configuration did not define any "
194                 "remote sites\n");
195     } else {
196         i=0;
197         while ((site=list_elem(l, i++))) {
198             struct site_if *s;
199             if (site->type!=t_closure) {
200                 cfgfatal(site->loc,"system","non-closure in site list");
201             }
202             if (site->data.closure->type!=CL_SITE) {
203                 cfgfatal(site->loc,"system","non-site closure in site list");
204             }
205             s=site->data.closure->interface;
206             s->control(s->st,True);
207         }
208     }
209 }
210
211 void register_for_poll(void *st, beforepoll_fn *before,
212                        afterpoll_fn *after, uint32_t max_nfds, string_t desc)
213 {
214     struct poll_interest *i;
215
216     i=safe_malloc(sizeof(*i),"register_for_poll");
217     i->before=before;
218     i->after=after;
219     i->state=st;
220     i->max_nfds=max_nfds;
221     i->nfds=0;
222     i->desc=desc;
223     total_nfds+=max_nfds;
224     i->next=reg;
225     reg=i;
226     return;
227 }
228
229 static void system_phase_hook(void *sst, uint32_t newphase)
230 {
231     if (newphase==PHASE_SHUTDOWN && pidfile) {
232         /* Try to unlink the pidfile; don't care if it fails */
233         unlink(pidfile);
234     }
235 }
236
237 static void run(void)
238 {
239     struct timeval tv_now;
240     uint64_t now;
241     struct poll_interest *i;
242     int rv, nfds, remain, idx;
243     int timeout;
244     struct pollfd *fds;
245
246     fds=alloca(sizeof(*fds)*total_nfds);
247     if (!fds) {
248         fatal("run: couldn't alloca\n");
249     }
250
251     while (!finished) {
252         if (gettimeofday(&tv_now, NULL)!=0) {
253             fatal_perror("main loop: gettimeofday");
254         }
255         now=(tv_now.tv_sec*1000)+(tv_now.tv_usec/1000);
256         idx=0;
257         for (i=reg; i; i=i->next) {
258             i->after(i->state, fds+idx, i->nfds, &tv_now, &now);
259             idx+=i->nfds;
260         }
261         remain=total_nfds;
262         idx=0;
263         timeout=-1;
264         for (i=reg; i; i=i->next) {
265             nfds=remain;
266             rv=i->before(i->state, fds+idx, &nfds, &timeout, &tv_now, &now);
267             if (rv!=0) {
268                 /* XXX we need to handle this properly: increase the
269                    nfds available */
270                 fatal("run: beforepoll_fn (%s) returns %d\n",i->desc,rv);
271             }
272             if (timeout<-1) {
273                 fatal("run: beforepoll_fn (%s) set timeout to %d\n",timeout);
274             }
275             idx+=nfds;
276             remain-=nfds;
277             i->nfds=nfds;
278         }
279         do {
280             rv=poll(fds, idx, timeout);
281             if (rv<0) {
282                 if (errno!=EINTR) {
283                     fatal_perror("run: poll");
284                 }
285             }
286         } while (rv<0);
287     }
288 }
289
290 static void droppriv(void)
291 {
292     FILE *pf=NULL;
293     pid_t p;
294
295     add_hook(PHASE_SHUTDOWN,system_phase_hook,NULL);
296
297     /* Background now, if we're supposed to: we may be unable to write the
298        pidfile if we don't. */
299     if (background) {
300         /* Open the pidfile before forking - that way the parent can tell
301            whether it succeeds */
302         if (pidfile) {
303             pf=fopen(pidfile,"w");
304             if (!pf) {
305                 fatal_perror("cannot open pidfile \"%s\"",pidfile);
306             }
307         } else {
308             Message(M_WARNING,"secnet: no pidfile configured, but "
309                     "backgrounding anyway\n");
310         }
311         p=fork();
312         if (p>0) {
313             if (pf) {
314                 /* Parent process - write pidfile, exit */
315                 fprintf(pf,"%d\n",p);
316                 fclose(pf);
317             }
318             exit(0);
319         } else if (p==0) {
320             /* Child process - all done, just carry on */
321             if (pf) fclose(pf);
322         } else {
323             /* Error */
324             fatal_perror("cannot fork");
325             exit(1);
326         }
327     } else {
328         if (pidfile) {
329             pf=fopen(pidfile,"w");
330             if (!pf) {
331                 fatal_perror("cannot open pidfile \"%s\"",pidfile);
332             }
333             fprintf(pf,"%d\n",getpid());
334             fclose(pf);
335         }
336     }
337
338     /* Drop privilege now, if configured to do so */
339     if (uid!=0) {
340         if (setuid(uid)!=0) {
341             fatal_perror("can't set uid to \"%s\"",userid);
342         }
343     }
344 }
345
346 int main(int argc, char **argv)
347 {
348     dict_t *config;
349
350     enter_phase(PHASE_GETOPTS);
351     parse_options(argc,argv);
352
353     enter_phase(PHASE_READCONFIG);
354     config=read_conffile(configfile);
355
356     enter_phase(PHASE_SETUP);
357     setup(config);
358
359     if (just_check_config) {
360         Message(M_INFO,"configuration file check complete\n");
361         exit(0);
362     }
363
364     enter_phase(PHASE_GETRESOURCES);
365     /* Appropriate phase hooks will have been run */
366     
367     enter_phase(PHASE_DROPPRIV);
368     droppriv();
369
370     enter_phase(PHASE_RUN);
371     run();
372
373     enter_phase(PHASE_SHUTDOWN);
374
375     return 0;
376 }
377