1 /* This file is part of GNU Dico.
2    Copyright (C) 2008-2020 Sergey Poznyakoff
3 
4    GNU Dico is free software; you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation; either version 3, or (at your option)
7    any later version.
8 
9    GNU Dico is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13 
14    You should have received a copy of the GNU General Public License
15    along with GNU Dico.  If not, see <http://www.gnu.org/licenses/>. */
16 
17 #include <dicod.h>
18 #include <unistd.h>
19 #include <fcntl.h>
20 #include <sys/types.h>
21 #include <sys/wait.h>
22 
23 struct dicod_server {
24     int fd;               /* Socket descriptor */
25     struct sockaddr *addr;
26     socklen_t addrlen;
27 };
28 
29 struct dicod_server *srvtab;
30 size_t srvcount;
31 int fdmax;
32 
33 pid_t *childtab;
34 unsigned long num_children;
35 unsigned long total_forks;
36 
37 struct sockaddr server_addr;
38 socklen_t server_addrlen;
39 
40 static int
address_family_to_domain(int family)41 address_family_to_domain (int family)
42 {
43     switch (family) {
44     case AF_UNIX:
45 	return PF_UNIX;
46 
47     case AF_INET:
48 	return PF_INET;
49 
50     case AF_INET6:
51 	return PF_INET6;
52 
53     default:
54 	abort();
55     }
56 }
57 
58 void
open_sockets(void)59 open_sockets(void)
60 {
61     size_t i;
62     dico_iterator_t itr;
63     struct grecs_sockaddr *sp;
64     struct stat st;
65     int t;
66     char *p;
67 
68     srvcount = dico_list_count(listen_addr);
69     if (srvcount == 0) {
70 	/* Provide defaults */
71 	struct sockaddr_in *s_in = xmalloc(sizeof(*s_in));
72 
73 	sp = xmalloc(sizeof(*sp));
74 	sp->sa = (struct sockaddr*) s_in;
75 	sp->len = sizeof(*s_in);
76 
77 	if (!listen_addr)
78 	    listen_addr = xdico_list_create();
79 	s_in->sin_family = AF_INET;
80 	s_in->sin_addr.s_addr = INADDR_ANY;
81 	s_in->sin_port = htons(DICO_DICT_PORT);
82 	xdico_list_append(listen_addr, sp);
83 	srvcount = 1;
84     }
85     srvtab = xcalloc(srvcount, sizeof srvtab[0]);
86     fdmax = 0;
87     itr = xdico_list_iterator(listen_addr);
88     for (i = 0, sp = dico_iterator_first(itr); sp;
89 	 sp = dico_iterator_next(itr)) {
90 	int fd = socket(address_family_to_domain(sp->sa->sa_family),
91 			SOCK_STREAM, 0);
92 	if (fd == -1) {
93 	    dico_log(L_ERR, errno, "socket");
94 	    continue;
95 	}
96 
97 	switch (sp->sa->sa_family) {
98 	case AF_UNIX: {
99 	    struct sockaddr_un *s_un = (struct sockaddr_un *)sp->sa;
100 	    if (stat(s_un->sun_path, &st)) {
101 		if (errno != ENOENT) {
102 		    dico_log(L_ERR, errno,
103 			   _("file %s exists but cannot be stat'd"),
104 			   s_un->sun_path);
105 		    close(fd);
106 		    continue;
107 		}
108 	    } else if (!S_ISSOCK(st.st_mode)) {
109 		dico_log(L_ERR, 0,
110 		       _("file %s is not a socket"),
111 		       s_un->sun_path);
112 		close(fd);
113 		continue;
114 	    } else if (unlink(s_un->sun_path)) {
115 		dico_log(L_ERR, errno,
116 			 _("cannot unlink file %s"),
117 			 s_un->sun_path);
118 		close(fd);
119 		continue;
120 	    }
121 	    break;
122 	}
123 
124 	case AF_INET:
125 	case AF_INET6:
126 	    t = 1;
127 	    setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &t, sizeof(t));
128 	    break;
129 
130 	default:
131 	    dico_log(L_ERR, 0, _("unsupported address family: %d"),
132 		     sp->sa->sa_family);
133 	    continue;
134 	}
135 
136 	if (bind(fd, sp->sa, sp->len) == -1) {
137 	    p = sockaddr_to_astr(sp->sa, sp->len);
138 	    dico_log(L_ERR, errno, ("cannot bind to %s"), p);
139 	    free(p);
140 	    close(fd);
141 	    continue;
142 	}
143 
144 	if (listen(fd, 8) == -1) {
145 	    p = sockaddr_to_astr(sp->sa, sp->len);
146 	    dico_log(L_ERR, errno, "cannot listen on %s", p);
147 	    free(p);
148 	    close(fd);
149 	    continue;
150 	}
151 
152 	srvtab[i].addr = sp->sa;
153 	srvtab[i].addrlen = sp->len;
154 	srvtab[i].fd = fd;
155 	i++;
156 	if (fd > fdmax)
157 	    fdmax = fd;
158     }
159     dico_iterator_destroy(&itr);
160     srvcount = i;
161 
162     if (srvcount == 0)
163 	dico_die(EX_UNAVAILABLE, L_ERR, 0, _("No sockets opened"));
164 }
165 
166 void
close_sockets(void)167 close_sockets(void)
168 {
169     size_t i;
170 
171     for (i = 0; i < srvcount; i++)
172 	close(srvtab[i].fd);
173     free(srvtab);
174     srvtab = NULL;
175     srvcount = 0;
176     fdmax = 0;
177 }
178 
179 
180 unsigned long
find_pid(pid_t pid)181 find_pid(pid_t pid)
182 {
183     unsigned long i;
184     for (i = 0; i < max_children; i++)
185 	if (childtab[i] == pid)
186 	    return i;
187     return (unsigned long)-1;
188 }
189 
190 void
register_child(pid_t pid)191 register_child(pid_t pid)
192 {
193     unsigned long i = find_pid(0);
194     if (i != (unsigned long)-1)
195 	childtab[i] = pid;
196     ++num_children;
197     ++total_forks;
198 }
199 
200 static void
print_status(pid_t pid,int status,int expect_term)201 print_status(pid_t pid, int status, int expect_term)
202 {
203     if (WIFEXITED(status)) {
204 	switch (WEXITSTATUS(status)) {
205 	case EX_OK:
206 	    dico_log(L_DEBUG, 0, _("%lu exited successfully"),
207 		     (unsigned long) pid);
208 	    break;
209 
210 	case EXIT_TIMEOUT:
211 	    dico_log(L_INFO, 0, _("%lu timed out"),
212 		     (unsigned long) pid);
213 	    break;
214 
215 	default:
216 	    dico_log(L_ERR, 0, _("%lu failed with status %d"),
217 		     (unsigned long) pid,
218 		     WEXITSTATUS(status));
219 	}
220     } else if (WIFSIGNALED(status)) {
221 	int prio;
222 
223 	if (expect_term && WTERMSIG(status) == SIGTERM)
224 	    prio = L_DEBUG;
225 	else
226 	    prio = L_ERR;
227 	dico_log(prio, 0, _("%lu terminated on signal %d"),
228 		 (unsigned long) pid, WTERMSIG(status));
229     } else if (WIFSTOPPED(status))
230 	dico_log(L_ERR, 0, _("%lu stopped on signal %d"),
231 		 (unsigned long) pid, WSTOPSIG(status));
232 #ifdef WCOREDUMP
233     else if (WCOREDUMP(status))
234 	dico_log(L_ERR, 0, _("%lu dumped core"), (unsigned long) pid);
235 #endif
236     else
237 	dico_log(L_ERR, 0, _("%lu terminated with unrecognized status"),
238 		 (unsigned long) pid);
239 }
240 
241 
242 void
cleanup_children(int term)243 cleanup_children(int term)
244 {
245     pid_t pid;
246     int status;
247 
248     while ((pid = waitpid ((pid_t)-1, &status, WNOHANG)) > 0) {
249 	unsigned long i = find_pid(pid);
250 	if (i == (unsigned long)-1) {
251 	    dico_log(L_INFO, 0, "subprocess %lu finished",
252 		   (unsigned long) pid);
253 	} else {
254 	    print_status(pid, status, term);
255 	}
256 	childtab[i] = 0;
257 	--num_children;
258     }
259 
260 
261 }
262 
263 void
stop_all(int sig)264 stop_all(int sig)
265 {
266     unsigned long i;
267 
268     if (!childtab)
269 	return;
270     for (i = 0; i < max_children; i++)
271 	if (childtab[i])
272 	    kill(sig, childtab[i]);
273 }
274 
275 void
stop_children(void)276 stop_children(void)
277 {
278     int i;
279     if (!childtab)
280 	return;
281     stop_all(SIGTERM);
282     for (i = 0; i < shutdown_timeout; i++) {
283 	cleanup_children(1);
284 	if (num_children == 0)
285 	    return;
286 	sleep(1);
287     }
288     stop_all(SIGKILL);
289 }
290 
291 
292 
293 /* Check whether pidfile NAME exists and if so, whether its PID is still
294    active. Exit if it is. */
295 void
check_pidfile(char * name)296 check_pidfile(char *name)
297 {
298     unsigned long pid;
299     FILE *fp = fopen(name, "r");
300 
301     if (!fp) {
302 	if (errno == ENOENT)
303 	    return;
304 	dico_die(EX_NOINPUT, L_ERR, errno,
305 		 _("Cannot open pidfile `%s'"), name);
306     }
307     if (fscanf(fp, "%lu", &pid) != 1) {
308 	dico_log(L_ERR, 0, _("Cannot get pid from pidfile `%s'"), name);
309     } else {
310 	if (kill(pid, 0) == 0) {
311 	    dico_die(EX_UNAVAILABLE,
312 		     L_ERR,
313 		     0,
314 		     _("%s appears to run with pid %lu. "
315 		       "If it does not, remove `%s' and retry."),
316 		     dico_program_name,
317 		     pid,
318 		     name);
319 	}
320     }
321     fclose(fp);
322     if (unlink(name))
323 	dico_die(EX_OSERR, L_ERR, errno,
324 		 _("Cannot unlink pidfile `%s'"), name);
325 }
326 
327 void
create_pidfile(char * name)328 create_pidfile(char *name)
329 {
330     FILE *fp = fopen(name, "w");
331 
332     if (!fp)
333 	dico_die(EX_CANTCREAT, L_ERR, errno,
334 		 _("Cannot create pidfile `%s'"), name);
335     fprintf(fp, "%lu", (unsigned long)getpid());
336     fclose(fp);
337 }
338 
339 void
remove_pidfile(char * name)340 remove_pidfile(char *name)
341 {
342     if (unlink(name))
343 	dico_log(L_ERR, errno, _("Cannot unlink pidfile `%s'"), name);
344 }
345 
346 
347 int restart;
348 int stop;
349 int need_cleanup;
350 
351 static RETSIGTYPE
sig_stop(int sig)352 sig_stop(int sig)
353 {
354     stop = 1;
355 }
356 
357 static RETSIGTYPE
sig_restart(int sig)358 sig_restart(int sig)
359 {
360     restart = 1;
361 }
362 
363 static RETSIGTYPE
sig_child(int sig)364 sig_child(int sig)
365 {
366     need_cleanup = 1;
367 }
368 
369 struct sockaddr_storage client_addr;
370 socklen_t client_addrlen;
371 
372 #define ACCESS_DENIED_MSG "530 Access denied\n"
373 #define TEMP_FAIL_MSG "420 Server temporarily unavailable\n"
374 #define SWRITE(fd, s) write(fd, s, sizeof(s)-1)
375 
376 int
handle_connection(int n)377 handle_connection(int n)
378 {
379     int status = 0;
380     int connfd;
381     int listenfd = srvtab[n].fd;
382 
383     server_addr = *srvtab[n].addr;
384     server_addrlen = srvtab[n].addrlen;
385 
386     client_addrlen = sizeof(client_addr);
387     connfd = accept(listenfd, (struct sockaddr*) &client_addr,
388 		    &client_addrlen);
389 
390     if (connfd == -1) {
391 	if (errno == EINTR)
392 	    return -1;
393 	dico_log(L_ERR, errno, "accept");
394 	return -1;
395 	/*exit (EXIT_FAILURE);*/
396     }
397 
398     if (dicod_acl_check(connect_acl, 1) == 0) {
399 	char *p = sockaddr_to_astr((struct sockaddr *)&client_addr,
400 				   client_addrlen);
401 	dico_log(L_NOTICE, 0,
402 		 _("connection from %s denied"),
403 		 p);
404 	free(p);
405 	SWRITE(connfd, ACCESS_DENIED_MSG);
406 	close(connfd);
407 	return 0;
408     }
409 
410     if (single_process) {
411 	dico_stream_t str = dicod_iostream(connfd, connfd);
412 	if (str) {
413 	    status = dicod_loop(str);
414 	    dico_stream_close(str);
415 	    dico_stream_destroy(&str);
416 	} else
417 	    status = -1;
418     } else {
419 	pid_t pid = fork();
420 	if (pid == -1) {
421 	    dico_log(L_ERR, errno, "fork");
422 	    SWRITE(connfd, TEMP_FAIL_MSG);
423 	} else if (pid == 0) {
424 	    /* Child.  */
425 	    dico_stream_t str;
426 
427 	    close(listenfd);
428 
429 	    signal(SIGTERM, SIG_DFL);
430 	    signal(SIGQUIT, SIG_DFL);
431 	    signal(SIGINT, SIG_DFL);
432 	    signal(SIGCHLD, SIG_DFL);
433 	    signal(SIGHUP, SIG_DFL);
434 
435 	    str = dicod_iostream(connfd, connfd);
436 	    if (!str)
437 		exit(EX_UNAVAILABLE);
438 	    status = dicod_loop(str);
439 	    dico_stream_close(str);
440 	    dico_stream_destroy(&str);
441 	    exit(status);
442 	} else {
443 	    register_child(pid);
444 	}
445     }
446     close(connfd);
447     return status;
448 }
449 
450 static int
pre_restart_lint_internal(void)451 pre_restart_lint_internal(void)
452 {
453     pid_t pid;
454     time_t ts;
455     pid = fork();
456     if (pid == 0)
457 	run_lint();
458     else if (pid == -1) {
459 	dico_log(L_ERR, errno, _("fork failed"));
460 	return 1;
461     }
462     /* Master process */
463     ts = time(NULL);
464     while (1) {
465 	int status;
466 	pid_t n = waitpid(pid, &status, WNOHANG);
467 	if (n == pid) {
468 	    /* Child exited, examine status */
469 	    if (WIFEXITED(status)) {
470 		if (WEXITSTATUS(status) == 0) {
471 		    /* OK to restart */
472 		    return 0;
473 		} else {
474 		    dico_log(L_NOTICE, 0,
475 			     _("refusing to restart due to errors in "
476 			       "configuration file"));
477 		    return 1;
478 		}
479 	    } else {
480 		dico_log(L_NOTICE, 0,
481 			 _("refusing to restart due to unexpected "
482 			   "return status of configuration checker"));
483 		print_status(pid, status, 0);
484 		return 1;
485 	    }
486 	} else if (pid == 0) {
487 	    if (time(NULL) - ts > 5) {
488 		dico_log(L_NOTICE, 0,
489 			 _("refusing to restart: "
490 			   "configuration checker did not exit within "
491 			   "5 seconds"));
492 		kill(pid, SIGKILL);
493 		break;
494 	    }
495 	} else if (errno == EINTR || errno == EAGAIN)
496 	    continue;
497 	else {
498 	    dico_log(L_ERR, errno, _("waitpid failed"));
499 	    dico_log(L_NOTICE, 0, _("refusing to restart"));
500 	    kill(pid, SIGKILL);
501 	    break;
502 	}
503     }
504     return 1;
505 }
506 
507 static int
pre_restart_lint(void)508 pre_restart_lint(void)
509 {
510     int rc;
511     RETSIGTYPE (*sf)(int);
512 
513     sf = signal(SIGCHLD, SIG_DFL);
514     rc = pre_restart_lint_internal();
515     signal(SIGCHLD, sf);
516     return rc;
517 }
518 
519 int
server_loop(void)520 server_loop(void)
521 {
522     size_t i;
523     fd_set fdset;
524 
525     FD_ZERO(&fdset);
526     for (i = 0; i < srvcount; i++)
527 	FD_SET(srvtab[i].fd, &fdset);
528 
529     for (;;) {
530 	int rc;
531 	fd_set rdset;
532 
533 	if (need_cleanup) {
534 	    cleanup_children(0);
535 	    need_cleanup = 1;
536 	}
537 
538 	if (num_children > max_children) {
539 	    dico_log(L_ERR, 0, _("too many children (%lu)"), num_children);
540 	    pause();
541 	    continue;
542 	}
543 
544 	rdset = fdset;
545 	rc = select (fdmax + 1, &rdset, NULL, NULL, NULL);
546 	if (rc == -1 && errno == EINTR) {
547 	    if (stop)
548 		break;
549 	    else if (restart) {
550 		if (pre_restart_lint() == 0)
551 		    break;
552 		else
553 		    restart = 0;
554 	    }
555 	    continue;
556 	} else if (rc < 0) {
557 	    dico_log(L_CRIT, errno, _("select error"));
558 	    return EX_OSERR;
559 	}
560 
561 	for (i = 0; i < srvcount; i++)
562 	    if (FD_ISSET(srvtab[i].fd, &rdset))
563 		handle_connection(i);
564     }
565     return 0;
566 }
567 
568 /* Return the highest-numbered open file descriptor. */
569 static int
getmaxfd(void)570 getmaxfd(void)
571 {
572     int i = open("/dev/null", O_RDONLY);
573     if (i >= 0) {
574 	close(i);
575 	i--;
576     }
577     return i;
578 }
579 
580 void
dicod_server(int argc,char ** argv)581 dicod_server(int argc, char **argv)
582 {
583     int rc;
584 
585     dico_log(L_INFO, 0, _("%s started"), program_version);
586 
587     if (user_id) {
588 	if (getuid())
589 	    dico_log(L_NOTICE, 0,
590 		     _("not running as root: ignoring user/group settings"));
591 	else if (switch_to_privs(user_id, group_id, group_list))
592 	    dico_die(EX_NOUSER, L_CRIT, 0, "exiting");
593     }
594 
595     if (!foreground)
596 	check_pidfile(pidfile_name);
597 
598     signal(SIGTERM, sig_stop);
599     signal(SIGQUIT, sig_stop);
600     signal(SIGINT, sig_stop);
601     signal(SIGCHLD, sig_child);
602     if (argv[0][0] != '/') {
603 	dico_log(L_WARN, 0, _("dicod started without full file name"));
604 	dico_log(L_WARN, 0, _("restart (SIGHUP) will not work"));
605 	signal(SIGHUP, sig_stop);
606     } else if (config_file[0] != '/') {
607 	dico_log(L_WARN, 0, _("configuration file is not given with a full file name"));
608 	dico_log(L_WARN, 0, _("restart (SIGHUP) will not work"));
609 	signal(SIGHUP, sig_stop);
610     } else
611 	signal(SIGHUP, sig_restart);
612 
613     if (!foreground) {
614 	if (daemon(0, 0) == -1)
615 	    dico_die(EX_OSERR, L_CRIT, errno, _("Cannot become a daemon"));
616 
617 	create_pidfile(pidfile_name);
618     }
619 
620     open_sockets();
621     if (!single_process)
622 	childtab = xcalloc(max_children, sizeof(childtab[0]));
623 
624     rc = server_loop();
625 
626     stop_children();
627     free(childtab);
628     dicod_server_cleanup();
629     close_sockets();
630 
631     if (rc)
632 	dico_log(L_NOTICE, errno, _("Exit code = %d, last error status"), rc);
633 
634     if (!foreground)
635 	remove_pidfile(pidfile_name);
636 
637     if (restart) {
638 	int i;
639 
640 	dico_log(L_INFO, 0, _("%s restarting"), program_version);
641 	for (i = getmaxfd(); i > 2; i--)
642 	    close(i);
643 	execv(argv[0], argv);
644 	dico_die(EX_UNAVAILABLE, L_ERR|L_CONS, errno, _("Cannot restart"));
645     } else
646 	dico_log(L_INFO, 0, _("%s terminating"), program_version);
647     exit(rc);
648 }
649 
650 
651 
652 
653 
654 
655 
656 
657 
658