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