Commit | Line | Data |
---|---|---|
d3372da9 | 1 | /* |
2 | * Dolda Connect - Modular multiuser Direct Connect-style client | |
302a2600 | 3 | * Copyright (C) 2004 Fredrik Tolf <fredrik@dolda2000.com> |
d3372da9 | 4 | * |
5 | * This program is free software; you can redistribute it and/or modify | |
6 | * it under the terms of the GNU General Public License as published by | |
7 | * the Free Software Foundation; either version 2 of the License, or | |
8 | * (at your option) any later version. | |
9 | * | |
10 | * This program is distributed in the hope that it will be useful, | |
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
13 | * GNU General Public License for more details. | |
14 | * | |
15 | * You should have received a copy of the GNU General Public License | |
16 | * along with this program; if not, write to the Free Software | |
17 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
18 | */ | |
19 | #include <stdio.h> | |
20 | #include <unistd.h> | |
21 | #include <stdlib.h> | |
22 | #include <string.h> | |
23 | #include <errno.h> | |
24 | #include <locale.h> | |
25 | #include <signal.h> | |
26 | #include <getopt.h> | |
27 | #include <time.h> | |
28 | #include <pwd.h> | |
29 | #include <grp.h> | |
30 | #include <sys/wait.h> | |
31 | #include <stdarg.h> | |
32 | #include <fcntl.h> | |
e304af88 | 33 | #include <sys/select.h> |
d3372da9 | 34 | |
35 | #ifdef HAVE_CONFIG_H | |
36 | #include <config.h> | |
37 | #endif | |
38 | #include "utils.h" | |
39 | #include "log.h" | |
40 | #include "conf.h" | |
41 | #include "module.h" | |
42 | #include "net.h" | |
43 | #include "client.h" | |
44 | #include "sysevents.h" | |
45 | #include "auth.h" | |
46 | ||
5feb6be7 | 47 | #ifdef HAVE_KEYUTILS |
48 | #include <keyutils.h> | |
49 | #endif | |
50 | ||
d3372da9 | 51 | struct module *modchain = NULL; |
52 | static struct timer *timers = NULL; | |
53 | static struct child *children = NULL; | |
54 | volatile int running; | |
55 | volatile int reinit; | |
56 | static volatile int childrendone = 0; | |
57 | ||
58 | struct timer *timercallback(double at, void (*func)(int, void *), void *data) | |
59 | { | |
60 | struct timer *new; | |
61 | ||
62 | new = smalloc(sizeof(*new)); | |
63 | new->at = at; | |
64 | new->func = func; | |
65 | new->data = data; | |
66 | new->next = timers; | |
67 | new->prev = NULL; | |
68 | if(timers != NULL) | |
69 | timers->prev = new; | |
70 | timers = new; | |
71 | return(new); | |
72 | } | |
73 | ||
74 | void canceltimer(struct timer *timer) | |
75 | { | |
76 | if(timer->next != NULL) | |
77 | timer->next->prev = timer->prev; | |
78 | if(timer->prev != NULL) | |
79 | timer->prev->next = timer->next; | |
80 | if(timer == timers) | |
81 | timers = timer->next; | |
82 | timer->func(1, timer->data); | |
83 | free(timer); | |
84 | } | |
85 | ||
86 | void childcallback(pid_t pid, void (*func)(pid_t, int, void *), void *data) | |
87 | { | |
88 | struct child *new; | |
89 | ||
90 | new = smalloc(sizeof(*new)); | |
91 | new->pid = pid; | |
92 | new->callback = func; | |
93 | new->data = data; | |
d3372da9 | 94 | new->prev = NULL; |
95 | new->next = children; | |
96 | if(children != NULL) | |
97 | children->prev = new; | |
98 | children = new; | |
99 | } | |
100 | ||
101 | static void preinit(int hup) | |
102 | { | |
103 | struct module *mod; | |
104 | ||
105 | for(mod = modchain; mod != NULL; mod = mod->next) | |
106 | { | |
107 | if(mod->preinit) | |
108 | mod->preinit(hup); | |
109 | if(!hup && ((mod->conf.vars != NULL) || (mod->conf.cmds != NULL))) | |
110 | confregmod(&mod->conf); | |
111 | } | |
112 | } | |
113 | ||
114 | static void init(int hup) | |
115 | { | |
116 | struct module *mod; | |
117 | ||
118 | for(mod = modchain; mod != NULL; mod = mod->next) | |
119 | { | |
120 | if(mod->init && mod->init(hup)) | |
121 | { | |
122 | flog(LOG_CRIT, "initialization of \"%s\" failed", mod->name); | |
123 | exit(1); | |
124 | } | |
125 | } | |
126 | } | |
127 | ||
128 | static void terminate(void) | |
129 | { | |
130 | struct module *mod; | |
131 | ||
132 | for(mod = modchain; mod != NULL; mod = mod->next) | |
133 | { | |
134 | if(mod->terminate) | |
135 | mod->terminate(); | |
136 | } | |
137 | } | |
138 | ||
139 | static void handler(int signum) | |
140 | { | |
d3372da9 | 141 | FILE *dumpfile; |
142 | extern int numfnetnodes, numtransfers, numdcpeers; | |
143 | ||
144 | switch(signum) | |
145 | { | |
146 | case SIGHUP: | |
147 | reinit = 1; | |
148 | break; | |
149 | case SIGINT: | |
150 | case SIGTERM: | |
151 | running = 0; | |
152 | break; | |
153 | case SIGCHLD: | |
bc222c94 | 154 | childrendone = 1; |
d3372da9 | 155 | break; |
156 | case SIGUSR1: | |
157 | flog(LOG_NOTICE, "forking and dumping core upon SIGUSR1"); | |
158 | if(fork() == 0) | |
159 | abort(); | |
160 | break; | |
161 | case SIGUSR2: | |
162 | flog(LOG_NOTICE, "dumping memstats to /tmp/dc-mem upon SIGUSR2"); | |
163 | if((dumpfile = fopen("/tmp/dc-mem", "w")) == NULL) { | |
164 | flog(LOG_ERR, "could not dump stats: %s", strerror(errno)); | |
165 | break; | |
166 | } | |
167 | fprintf(dumpfile, "%i %i %i\n", numfnetnodes, numtransfers, numdcpeers); | |
168 | fclose(dumpfile); | |
169 | break; | |
170 | } | |
171 | } | |
172 | ||
bc222c94 FT |
173 | static void checkchildren(void) |
174 | { | |
175 | pid_t pid; | |
176 | int status; | |
177 | struct child *child; | |
178 | ||
179 | while((pid = waitpid(-1, &status, WNOHANG)) > 0) | |
180 | { | |
181 | for(child = children; child != NULL; child = child->next) | |
182 | { | |
183 | if(child->pid == pid) | |
184 | { | |
185 | child->callback(pid, status, child->data); | |
186 | if(child == children) | |
187 | children = child->next; | |
188 | if(child->prev != NULL) | |
189 | child->prev->next = child->next; | |
190 | if(child->next != NULL) | |
191 | child->next->prev = child->prev; | |
192 | free(child); | |
193 | break; | |
194 | } | |
195 | } | |
196 | } | |
197 | } | |
198 | ||
d3372da9 | 199 | pid_t forksess(uid_t user, struct authhandle *auth, void (*ccbfunc)(pid_t, int, void *), void *data, ...) |
200 | { | |
201 | int i, o; | |
202 | int cpipe[2]; | |
203 | struct | |
204 | { | |
205 | int tfd; | |
206 | int fd; | |
207 | } *files; | |
208 | int maxfd, type, numfiles; | |
209 | int acc; | |
210 | int *ibuf; | |
211 | struct passwd *pwent; | |
212 | pid_t pid; | |
213 | char *buf; | |
214 | va_list args; | |
215 | sigset_t sigset; | |
216 | int ret, status; | |
217 | ||
218 | if((pwent = getpwuid(user)) == NULL) | |
219 | { | |
220 | flog(LOG_WARNING, "no passwd entry for uid %i, cannot fork session", user); | |
221 | errno = EACCES; | |
222 | return(-1); | |
223 | } | |
224 | if((geteuid() != 0) && (user != geteuid())) | |
225 | { | |
226 | flog(LOG_WARNING, "cannot fork non-owning session when not running as root (EUID is %i, target UID is %i)", geteuid(), user); | |
227 | errno = EPERM; | |
228 | return(-1); | |
229 | } | |
230 | va_start(args, data); | |
231 | numfiles = 0; | |
232 | files = NULL; | |
233 | maxfd = 0; | |
234 | while((type = va_arg(args, int)) != FD_END) | |
235 | { | |
236 | files = srealloc(files, sizeof(*files) * (numfiles + 1)); | |
237 | files[numfiles].fd = va_arg(args, int); | |
238 | if(files[numfiles].fd > maxfd) | |
239 | maxfd = files[numfiles].fd; | |
240 | acc = va_arg(args, int); | |
241 | if(type == FD_PIPE) | |
242 | { | |
243 | if(pipe(cpipe) < 0) | |
244 | { | |
245 | flog(LOG_CRIT, "could not create pipe(!): %s", strerror(errno)); | |
246 | for(i = 0; i < numfiles; i++) | |
247 | close(files[i].tfd); | |
248 | return(-1); | |
249 | } | |
250 | ibuf = va_arg(args, int *); | |
251 | if(acc == O_WRONLY) | |
252 | { | |
253 | *ibuf = cpipe[1]; | |
254 | files[numfiles].tfd = cpipe[0]; | |
255 | } else { | |
256 | *ibuf = cpipe[0]; | |
257 | files[numfiles].tfd = cpipe[1]; | |
258 | } | |
259 | } else if(type == FD_FILE) { | |
260 | buf = va_arg(args, char *); | |
261 | if((files[numfiles].tfd = open(buf, acc)) < 0) | |
262 | { | |
263 | flog(LOG_CRIT, "could not open file \"%s\": %s", buf, strerror(errno)); | |
264 | for(i = 0; i < numfiles; i++) | |
265 | close(files[i].tfd); | |
266 | return(-1); | |
267 | } | |
268 | } | |
269 | if(files[numfiles].tfd > maxfd) | |
270 | maxfd = files[numfiles].tfd; | |
271 | numfiles++; | |
272 | } | |
273 | va_end(args); | |
274 | sigemptyset(&sigset); | |
275 | sigaddset(&sigset, SIGCHLD); | |
276 | sigprocmask(SIG_BLOCK, &sigset, NULL); | |
277 | if((pid = fork()) < 0) | |
278 | { | |
279 | flog(LOG_WARNING, "could not fork(!) in forksess(): %s", strerror(errno)); | |
280 | for(i = 0; i < numfiles; i++) | |
281 | close(files[i].tfd); | |
282 | sigprocmask(SIG_UNBLOCK, &sigset, NULL); | |
283 | } | |
284 | if(pid == 0) | |
285 | { | |
286 | sigprocmask(SIG_UNBLOCK, &sigset, NULL); | |
287 | signal(SIGPIPE, SIG_DFL); | |
288 | signal(SIGCHLD, SIG_DFL); | |
289 | signal(SIGINT, SIG_DFL); | |
290 | signal(SIGTERM, SIG_DFL); | |
291 | signal(SIGHUP, SIG_DFL); | |
292 | for(i = 0; i < numfiles; i++) | |
293 | { | |
294 | if(dup2(files[i].tfd, maxfd + i + 1) < 0) | |
295 | exit(127); | |
296 | files[i].tfd = maxfd + i + 1; | |
297 | } | |
298 | for(i = 0; i < numfiles; i++) | |
299 | { | |
300 | if(dup2(files[i].tfd, files[i].fd) < 0) | |
301 | exit(127); | |
302 | } | |
303 | initlog(); | |
304 | for(i = 0; i < FD_SETSIZE; i++) | |
305 | { | |
306 | if(i <= maxfd) | |
307 | { | |
308 | for(o = 0; o < numfiles; o++) | |
309 | { | |
310 | if(i == files[o].fd) | |
311 | break; | |
312 | } | |
313 | if(o == numfiles) | |
314 | close(i); | |
315 | } else { | |
316 | close(i); | |
317 | } | |
318 | } | |
1001e996 | 319 | setpgid(0, 0); |
d3372da9 | 320 | signal(SIGHUP, SIG_IGN); |
321 | errno = 0; | |
5feb6be7 | 322 | #ifdef HAVE_KEYUTILS |
323 | keyctl_join_session_keyring(NULL); | |
4a68468f | 324 | keyctl_chown(KEY_SPEC_SESSION_KEYRING, pwent->pw_uid, pwent->pw_gid); |
5feb6be7 | 325 | #endif |
d3372da9 | 326 | if((authopensess(auth)) != AUTH_SUCCESS) |
327 | { | |
328 | flog(LOG_WARNING, "could not open session for user %s: %s", pwent->pw_name, (errno == 0)?"Unknown error - should be logged above":strerror(errno)); | |
329 | exit(127); | |
330 | } | |
331 | if((pid = fork()) < 0) | |
332 | { | |
333 | authclosesess(auth); | |
334 | exit(127); | |
335 | } | |
336 | if(pid == 0) | |
337 | { | |
338 | if(geteuid() == 0) | |
339 | { | |
340 | if(initgroups(pwent->pw_name, pwent->pw_gid)) | |
341 | { | |
342 | flog(LOG_WARNING, "could not initgroups: %s", strerror(errno)); | |
343 | exit(127); | |
344 | } | |
345 | if(setgid(pwent->pw_gid)) | |
346 | { | |
347 | flog(LOG_WARNING, "could not setgid: %s", strerror(errno)); | |
348 | exit(127); | |
349 | } | |
350 | if(setuid(pwent->pw_uid)) | |
351 | { | |
352 | flog(LOG_WARNING, "could not setuid: %s", strerror(errno)); | |
353 | exit(127); | |
354 | } | |
5b50bfcd FT |
355 | putenv(sprintf2("HOME=%s", pwent->pw_dir)); |
356 | putenv(sprintf2("SHELL=%s", pwent->pw_shell)); | |
357 | putenv(sprintf2("PATH=%s/bin:/usr/local/bin:/bin:/usr/bin", pwent->pw_dir)); | |
d3372da9 | 358 | } |
d3372da9 | 359 | putenv(sprintf2("USER=%s", pwent->pw_name)); |
360 | putenv(sprintf2("LOGNAME=%s", pwent->pw_name)); | |
d3372da9 | 361 | chdir(pwent->pw_dir); |
362 | return(0); | |
363 | } | |
364 | for(i = 0; i < numfiles; i++) | |
365 | close(files[i].fd); | |
366 | while(((ret = waitpid(pid, &status, 0)) != pid) && (ret >= 0)); | |
367 | authclosesess(auth); | |
368 | if(ret < 0) | |
369 | { | |
370 | flog(LOG_WARNING, "waitpid(%i) said \"%s\"", pid, strerror(errno)); | |
371 | exit(127); | |
372 | } | |
373 | if(!WIFEXITED(status)) | |
374 | exit(127); | |
375 | exit(WEXITSTATUS(status)); | |
376 | } | |
377 | for(i = 0; i < numfiles; i++) | |
378 | close(files[i].tfd); | |
379 | if(files != NULL) | |
380 | free(files); | |
381 | if(ccbfunc != NULL) | |
382 | childcallback(pid, ccbfunc, data); | |
383 | sigprocmask(SIG_UNBLOCK, &sigset, NULL); | |
384 | return(pid); | |
385 | } | |
386 | ||
387 | int main(int argc, char **argv) | |
388 | { | |
389 | int c; | |
390 | int nofork; | |
391 | char *configfile; | |
392 | char *pidfile; | |
393 | FILE *pfstream, *confstream; | |
5cccc2d5 | 394 | int delay, immsyslog; |
d3372da9 | 395 | struct module *mod; |
dae3c8b4 | 396 | struct timer *timer; |
d3372da9 | 397 | double now; |
398 | ||
2559e077 | 399 | now = ntime(); |
5cccc2d5 | 400 | immsyslog = nofork = 0; |
d3372da9 | 401 | syslogfac = LOG_DAEMON; |
402 | configfile = NULL; | |
403 | pidfile = NULL; | |
8af979f3 | 404 | while((c = getopt(argc, argv, "p:C:f:hnsV")) != -1) |
d3372da9 | 405 | { |
406 | switch(c) | |
407 | { | |
408 | case 'p': | |
409 | pidfile = optarg; | |
410 | break; | |
411 | case 'C': | |
412 | configfile = optarg; | |
413 | break; | |
414 | case 'f': | |
415 | if(!strcmp(optarg, "auth")) | |
416 | syslogfac = LOG_AUTH; | |
417 | else if(!strcmp(optarg, "authpriv")) | |
418 | syslogfac = LOG_AUTHPRIV; | |
419 | else if(!strcmp(optarg, "cron")) | |
420 | syslogfac = LOG_CRON; | |
421 | else if(!strcmp(optarg, "daemon")) | |
422 | syslogfac = LOG_DAEMON; | |
423 | else if(!strcmp(optarg, "ftp")) | |
424 | syslogfac = LOG_FTP; | |
425 | else if(!strcmp(optarg, "kern")) | |
426 | syslogfac = LOG_KERN; | |
427 | else if(!strcmp(optarg, "lpr")) | |
428 | syslogfac = LOG_LPR; | |
429 | else if(!strcmp(optarg, "mail")) | |
430 | syslogfac = LOG_MAIL; | |
431 | else if(!strcmp(optarg, "news")) | |
432 | syslogfac = LOG_NEWS; | |
433 | else if(!strcmp(optarg, "syslog")) | |
434 | syslogfac = LOG_SYSLOG; | |
435 | else if(!strcmp(optarg, "user")) | |
436 | syslogfac = LOG_USER; | |
437 | else if(!strcmp(optarg, "uucp")) | |
438 | syslogfac = LOG_UUCP; | |
439 | else if(!strncmp(optarg, "local", 5) && (strlen(optarg) == 6)) | |
440 | syslogfac = LOG_LOCAL0 + (optarg[5] - '0'); | |
441 | else | |
442 | fprintf(stderr, "unknown syslog facility %s, using daemon\n", optarg); | |
443 | break; | |
444 | case 'n': | |
445 | nofork = 1; | |
446 | break; | |
9a0b3ef8 | 447 | case 's': |
5cccc2d5 | 448 | immsyslog = 1; |
58b3dc7b | 449 | break; |
8af979f3 FT |
450 | case 'V': |
451 | printf("%s", RELEASEINFO); | |
452 | exit(0); | |
d3372da9 | 453 | case 'h': |
454 | case ':': | |
455 | case '?': | |
456 | default: | |
8af979f3 | 457 | printf("usage: doldacond [-hnsV] [-C configfile] [-p pidfile] [-f facility]\n"); |
d3372da9 | 458 | exit(c != 'h'); |
459 | } | |
460 | } | |
461 | setlocale(LC_ALL, ""); | |
462 | initlog(); | |
5cccc2d5 | 463 | if(immsyslog) |
464 | { | |
465 | logtosyslog = 1; | |
466 | logtostderr = 0; | |
467 | } | |
d3372da9 | 468 | signal(SIGPIPE, SIG_IGN); |
469 | signal(SIGHUP, handler); | |
470 | signal(SIGINT, handler); | |
471 | signal(SIGTERM, handler); | |
472 | signal(SIGCHLD, handler); | |
473 | signal(SIGUSR1, handler); | |
474 | signal(SIGUSR2, handler); | |
475 | preinit(0); | |
476 | if(configfile == NULL) | |
477 | { | |
c5d23632 | 478 | if((configfile = findfile("doldacond.conf", NULL, 0)) == NULL) |
d3372da9 | 479 | { |
480 | flog(LOG_CRIT, "could not find a configuration file"); | |
481 | exit(1); | |
482 | } | |
483 | } | |
484 | pfstream = NULL; | |
485 | if(pidfile != NULL) | |
486 | { | |
487 | if((pfstream = fopen(pidfile, "w")) == NULL) | |
488 | { | |
489 | flog(LOG_CRIT, "could not open specified PID file %s: %s", pidfile, strerror(errno)); | |
490 | exit(1); | |
491 | } | |
492 | } | |
493 | if((confstream = fopen(configfile, "r")) == NULL) | |
494 | { | |
495 | flog(LOG_CRIT, "could not open configuration file %s: %s", configfile, strerror(errno)); | |
496 | exit(1); | |
497 | } | |
498 | readconfig(confstream); | |
499 | fclose(confstream); | |
500 | init(0); | |
501 | if(!nofork) | |
502 | { | |
503 | logtosyslog = 1; | |
504 | daemon(0, 0); | |
505 | flog(LOG_INFO, "daemonized"); | |
506 | logtostderr = 0; | |
507 | } | |
508 | if(pfstream != NULL) { | |
509 | fprintf(pfstream, "%i\n", getpid()); | |
510 | fclose(pfstream); | |
511 | } | |
2559e077 | 512 | flog(LOG_INFO, "startup took %f seconds", ntime() - now); |
d3372da9 | 513 | running = 1; |
514 | reinit = 0; | |
515 | while(running) | |
516 | { | |
517 | if(reinit) | |
518 | { | |
519 | if((confstream = fopen(configfile, "r")) == NULL) | |
520 | { | |
521 | flog(LOG_ERR, "could not open configuration file %s: %s (ignoring HUP)", configfile, strerror(errno)); | |
522 | } else { | |
523 | preinit(1); | |
524 | readconfig(confstream); | |
525 | fclose(confstream); | |
526 | init(1); | |
527 | } | |
528 | reinit = 0; | |
529 | } | |
530 | delay = 1000; /* -1; */ | |
531 | for(mod = modchain; mod != NULL; mod = mod->next) | |
532 | { | |
533 | if(mod->run && mod->run()) | |
534 | delay = 0; | |
535 | } | |
536 | if(!running) | |
537 | delay = 0; | |
538 | if(delay != 0) | |
539 | { | |
540 | now = ntime(); | |
541 | for(timer = timers; timer != NULL; timer = timer->next) | |
542 | { | |
543 | if((delay == -1) || ((int)((timer->at - now) * 1000.0) < delay)) | |
544 | delay = (int)((timer->at - now) * 1000.0); | |
545 | } | |
546 | } | |
bc222c94 FT |
547 | /* Of course, there's a race condition here that should be |
548 | * solved with pselect, but it doesn't matter a lot. */ | |
d3372da9 | 549 | if(childrendone) |
d3372da9 | 550 | delay = 0; |
d3372da9 | 551 | pollsocks(delay); |
552 | now = ntime(); | |
dae3c8b4 | 553 | do |
d3372da9 | 554 | { |
dae3c8b4 | 555 | for(timer = timers; timer != NULL; timer = timer->next) |
556 | { | |
557 | if(now < timer->at) | |
558 | continue; | |
559 | if(timer->prev != NULL) | |
560 | timer->prev->next = timer->next; | |
561 | if(timer->next != NULL) | |
562 | timer->next->prev = timer->prev; | |
563 | if(timer == timers) | |
564 | timers = timer->next; | |
565 | timer->func(0, timer->data); | |
566 | free(timer); | |
567 | break; | |
568 | } | |
569 | } while(timer != NULL); | |
bc222c94 | 570 | if(childrendone) |
d3372da9 | 571 | { |
bc222c94 FT |
572 | childrendone = 0; |
573 | checkchildren(); | |
574 | } | |
d3372da9 | 575 | } |
576 | flog(LOG_INFO, "terminating..."); | |
577 | terminate(); | |
267c4a93 | 578 | if(pidfile != NULL) |
579 | unlink(pidfile); | |
d3372da9 | 580 | return(0); |
581 | } |