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