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