From 3995cbdac4db5a01608eb70a7562c30ff852dcd1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Knecht?= Date: Thu, 13 Jun 2013 23:23:21 +0200 Subject: [PATCH] Limit the number of simultanious children Make sure minidlna cannot fork more than 5 children, to avoid becoming a fork bomb. Reported-by: Rinat Ibragimov --- Makefile.am | 2 +- minidlna.c | 26 +++++------ daemonize.c => process.c | 96 +++++++++++++++++++++++++--------------- daemonize.h => process.h | 55 +++++++++++++++-------- upnphttp.c | 13 +++--- 5 files changed, 117 insertions(+), 75 deletions(-) rename daemonize.c => process.c (68%) rename daemonize.h => process.h (54%) diff --git a/Makefile.am b/Makefile.am index c3aa4c9..42b44f7 100644 --- a/Makefile.am +++ b/Makefile.am @@ -6,7 +6,7 @@ sbin_PROGRAMS = minidlnad check_PROGRAMS = testupnpdescgen minidlnad_SOURCES = minidlna.c upnphttp.c upnpdescgen.c upnpsoap.c \ upnpreplyparse.c minixml.c clients.c \ - getifaddr.c daemonize.c upnpglobalvars.c \ + getifaddr.c process.c upnpglobalvars.c \ options.c minissdp.c uuid.c upnpevents.c \ sql.c utils.c metadata.c scanner.c inotify.c \ tivo_utils.c tivo_beacon.c tivo_commands.c \ diff --git a/minidlna.c b/minidlna.c index d5c1ecb..3d88348 100644 --- a/minidlna.c +++ b/minidlna.c @@ -86,7 +86,7 @@ #include "utils.h" #include "minissdp.h" #include "minidlnatypes.h" -#include "daemonize.h" +#include "process.h" #include "upnpevents.h" #include "scanner.h" #include "inotify.h" @@ -155,14 +155,6 @@ sigterm(int sig) quitting = 1; } -static void -sigchld(int sig) -{ - if (!scanning) - signal(SIGCHLD, SIG_IGN); - waitpid(-1, NULL, WNOHANG); -} - static void sighup(int sig) { @@ -369,9 +361,9 @@ rescan: #if USE_FORK scanning = 1; sqlite3_close(db); - *scanner_pid = fork(); + *scanner_pid = process_fork(); open_db(&db); - if (!(*scanner_pid)) /* child (scanner) process */ + if (*scanner_pid == 0) /* child (scanner) process */ { start_scanner(); sqlite3_close(db); @@ -379,6 +371,10 @@ rescan: freeoptions(); exit(EXIT_SUCCESS); } + else if (*scanner_pid < 0) + { + start_scanner(); + } #else start_scanner(); #endif @@ -884,7 +880,7 @@ init(int argc, char **argv) } else { - pid = daemonize(); + pid = process_daemonize(); #ifdef READYNAS unlink("/ramfs/.upnp-av_scan"); path = "/var/log/upnp-av.log"; @@ -897,7 +893,7 @@ init(int argc, char **argv) } log_init(path, log_level); - if (checkforrunning(pidfilename) < 0) + if (process_check_if_running(pidfilename) < 0) { DPRINTF(E_ERROR, L_GENERAL, "MiniDLNA is already running. EXITING.\n"); return 1; @@ -923,6 +919,9 @@ init(int argc, char **argv) DPRINTF(E_FATAL, L_GENERAL, "Failed to set %s handler. EXITING.\n", "SIGPIPE"); if (signal(SIGHUP, &sighup) == SIG_ERR) DPRINTF(E_FATAL, L_GENERAL, "Failed to set %s handler. EXITING.\n", "SIGHUP"); + sa.sa_handler = process_handle_child_termination; + if (sigaction(SIGCHLD, &sa, NULL)) + DPRINTF(E_FATAL, L_GENERAL, "Failed to set %s handler. EXITING.\n", "SIGCHLD"); if (writepidfile(pidfilename, pid, uid) != 0) pidfilename = NULL; @@ -989,7 +988,6 @@ main(int argc, char **argv) ret = -1; } check_db(db, ret, &scanner_pid); - signal(SIGCHLD, &sigchld); #ifdef HAVE_INOTIFY if( GETFLAG(INOTIFY_MASK) ) { diff --git a/daemonize.c b/process.c similarity index 68% rename from daemonize.c rename to process.c index 1fddf80..22f5207 100644 --- a/daemonize.c +++ b/process.c @@ -1,7 +1,8 @@ -/* MiniUPnP project - * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ +/* Process handling + * + * Copyright © 2006, Thomas Bernard + * Copyright © 2013, Benoît Knecht * - * Copyright (c) 2006, Thomas Bernard * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -35,48 +36,74 @@ #include #include #include +#include -#include "daemonize.h" +#include "process.h" #include "config.h" #include "log.h" +static const int max_number_of_children = 5; +static int number_of_children = 0; + +pid_t +process_fork(void) +{ + if (number_of_children >= max_number_of_children) + { + errno = EAGAIN; + return -1; + } + + pid_t pid = fork(); + if (pid > 0) + ++number_of_children; + return pid; +} + +void +process_handle_child_termination(int signal) +{ + waitpid(-1, NULL, WNOHANG); + --number_of_children; +} + int -daemonize(void) +process_daemonize(void) { int pid; #ifndef USE_DAEMON int i; - switch(fork()) + switch(process_fork()) { - /* fork error */ - case -1: - perror("fork()"); - exit(1); - - /* child process */ - case 0: - /* obtain a new process group */ - if( (pid = setsid()) < 0) - { - perror("setsid()"); + /* fork error */ + case -1: + perror("fork()"); exit(1); - } - /* close all descriptors */ - for (i=getdtablesize();i>=0;--i) close(i); + /* child process */ + case 0: + /* obtain a new process group */ + if( (pid = setsid()) < 0) + { + perror("setsid()"); + exit(1); + } - i = open("/dev/null",O_RDWR); /* open stdin */ - dup(i); /* stdout */ - dup(i); /* stderr */ + /* close all descriptors */ + for (i=getdtablesize();i>=0;--i) close(i); - umask(027); - chdir("/"); + i = open("/dev/null",O_RDWR); /* open stdin */ + dup(i); /* stdout */ + dup(i); /* stderr */ - break; - /* parent process */ - default: - exit(0); + umask(027); + chdir("/"); + + break; + /* parent process */ + default: + exit(0); } #else if( daemon(0, 0) < 0 ) @@ -87,7 +114,7 @@ daemonize(void) } int -checkforrunning(const char * fname) +process_check_if_running(const char *fname) { char buffer[64]; int pidfile; @@ -98,9 +125,9 @@ checkforrunning(const char * fname) if( (pidfile = open(fname, O_RDONLY)) < 0) return 0; - + memset(buffer, 0, 64); - + if(read(pidfile, buffer, 63)) { if( (pid = atol(buffer)) > 0) @@ -112,9 +139,8 @@ checkforrunning(const char * fname) } } } - + close(pidfile); - + return 0; } - diff --git a/daemonize.h b/process.h similarity index 54% rename from daemonize.h rename to process.h index ec8732a..7fa7682 100644 --- a/daemonize.h +++ b/process.h @@ -1,7 +1,7 @@ -/* MiniUPnP project - * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ +/* Process handling + * + * Copyright © 2013, Benoît Knecht * - * Copyright (c) 2006, Thomas Bernard * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -26,24 +26,41 @@ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ -#ifndef __DAEMONIZE_H__ -#define __DAEMONIZE_H__ +#ifndef __PROCESS_H__ +#define __PROCESS_H__ -#include "config.h" +#include -/* daemonize() - * "fork" to background, detach from terminal, etc... - * returns: pid of the daemon, exits upon failure */ -int -daemonize(void); +/** + * Fork a new child (just like fork()) but keep track of how many childs are + * already running, and refuse fo fork if there are too many. + * @return -1 if it couldn't fork, 0 in the child process, the pid of the + * child process in the parent process. + */ +pid_t process_fork(void); -/* checkforrunning() - * check for another instance running - * returns: 0 only instance - * -1 invalid filename - * -2 another instance running */ -int -checkforrunning(const char * fname); +/** + * Handler to be called upon receiving SIGCHLD. This signal is received by the + * parent process when a child terminates, and this handler updates the number + * of running childs accordingly. + * @param signal The signal number. + */ +void process_handle_child_termination(int signal); -#endif +/** + * Daemonize the current process by forking itself and redirecting standard + * input, standard output and standard error to /dev/null. + * @return The pid of the process. + */ +int process_daemonize(void); +/** + * Check if the process corresponding to the pid found in the pid file is + * running. + * @param fname The path to the pid file. + * @return 0 if no other instance is running, -1 if the file name is invalid, + * -2 if another instance is running. + */ +int process_check_if_running(const char *fname); + +#endif // __PROCESS_H__ diff --git a/upnphttp.c b/upnphttp.c index 7de4e7a..8434748 100644 --- a/upnphttp.c +++ b/upnphttp.c @@ -78,6 +78,7 @@ #include "tivo_utils.h" #include "tivo_commands.h" #include "clients.h" +#include "process.h" #include "sendfile.h" @@ -1574,8 +1575,8 @@ SendResp_resizedimg(struct upnphttp * h, char * object) #if USE_FORK pid_t newpid = 0; - newpid = fork(); - if( newpid ) + newpid = process_fork(); + if( newpid > 0 ) { CloseSocket_upnphttp(h); goto resized_error; @@ -1716,7 +1717,7 @@ SendResp_resizedimg(struct upnphttp * h, char * object) resized_error: sqlite3_free_table(result); #if USE_FORK - if( !newpid ) + if( newpid == 0 ) _exit(0); #endif } @@ -1809,8 +1810,8 @@ SendResp_dlnafile(struct upnphttp *h, char *object) sqlite3_free_table(result); } #if USE_FORK - newpid = fork(); - if( newpid ) + newpid = process_fork(); + if( newpid > 0 ) { CloseSocket_upnphttp(h); goto error; @@ -1951,7 +1952,7 @@ SendResp_dlnafile(struct upnphttp *h, char *object) CloseSocket_upnphttp(h); error: #if USE_FORK - if( !newpid ) + if( newpid == 0 ) _exit(0); #endif return;