From 79bcedd4bc09f0c0b43330a1f386948ef96a0b02 Mon Sep 17 00:00:00 2001 From: Unrud Date: Sun, 7 May 2017 21:55:57 +0200 Subject: [PATCH 1/7] Refactor: Extract daemonize function --- radicale/__main__.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/radicale/__main__.py b/radicale/__main__.py index 4c49c69..e3afe3c 100644 --- a/radicale/__main__.py +++ b/radicale/__main__.py @@ -114,9 +114,8 @@ def run(): exit(1) -def serve(configuration, logger): - """Serve radicale from configuration.""" - # Fork if Radicale is launched as daemon +def daemonize(configuration, logger): + """Fork and decouple if Radicale is configured as daemon.""" if configuration.getboolean("server", "daemon"): # Check and create PID file in a race-free manner if configuration.get("server", "pid"): @@ -153,6 +152,11 @@ def serve(configuration, logger): os.unlink(configuration.get("server", "pid")) atexit.register(cleanup) + + +def serve(configuration, logger): + """Serve radicale from configuration.""" + daemonize(configuration, logger) logger.info("Starting Radicale") # Create collection servers From 65c53df5b3af5bb5bfd925fa333dc7e627964bd0 Mon Sep 17 00:00:00 2001 From: Unrud Date: Sun, 7 May 2017 21:55:59 +0200 Subject: [PATCH 2/7] Keep original exception when PID file creation fails --- radicale/__main__.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/radicale/__main__.py b/radicale/__main__.py index e3afe3c..70bf762 100644 --- a/radicale/__main__.py +++ b/radicale/__main__.py @@ -123,9 +123,9 @@ def daemonize(configuration, logger): pid_fd = os.open( configuration.get("server", "pid"), os.O_CREAT | os.O_EXCL | os.O_WRONLY) - except: - raise OSError( - "PID file exists: %s" % configuration.get("server", "pid")) + except OSError as e: + raise OSError("PID file exists: %s" % + configuration.get("server", "pid")) from e pid = os.fork() if pid: sys.exit() From 6ade44c773c6c8955f96ce15755db6f6f8e39c3b Mon Sep 17 00:00:00 2001 From: Unrud Date: Sun, 7 May 2017 21:56:02 +0200 Subject: [PATCH 3/7] Make relative PID path absolute The daemon changes the current directory to root. --- radicale/__main__.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/radicale/__main__.py b/radicale/__main__.py index 70bf762..35c2361 100644 --- a/radicale/__main__.py +++ b/radicale/__main__.py @@ -120,9 +120,9 @@ def daemonize(configuration, logger): # Check and create PID file in a race-free manner if configuration.get("server", "pid"): try: + pid_path = os.path.abspath(configuration.get("server", "pid")) pid_fd = os.open( - configuration.get("server", "pid"), - os.O_CREAT | os.O_EXCL | os.O_WRONLY) + pid_path, os.O_CREAT | os.O_EXCL | os.O_WRONLY) except OSError as e: raise OSError("PID file exists: %s" % configuration.get("server", "pid")) from e @@ -149,7 +149,7 @@ def daemonize(configuration, logger): # Remove PID file if (configuration.get("server", "pid") and configuration.getboolean("server", "daemon")): - os.unlink(configuration.get("server", "pid")) + os.unlink(pid_path) atexit.register(cleanup) From 360e88f350d52e112f544100cd408d5d54afd43d Mon Sep 17 00:00:00 2001 From: Unrud Date: Sun, 7 May 2017 21:56:05 +0200 Subject: [PATCH 4/7] Write PID file in original process This ensures that the PID is written, when the process exists. --- radicale/__main__.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/radicale/__main__.py b/radicale/__main__.py index 35c2361..4cdd6fe 100644 --- a/radicale/__main__.py +++ b/radicale/__main__.py @@ -128,11 +128,13 @@ def daemonize(configuration, logger): configuration.get("server", "pid")) from e pid = os.fork() if pid: + # Write PID + if configuration.get("server", "pid"): + with os.fdopen(pid_fd, "w") as pid_file: + pid_file.write(str(pid)) sys.exit() - # Write PID if configuration.get("server", "pid"): - with os.fdopen(pid_fd, "w") as pid_file: - pid_file.write(str(os.getpid())) + os.close(pid_fd) # Decouple environment os.chdir("/") os.setsid() From fcccb3f7af7498569d8019becbdee92a52049319 Mon Sep 17 00:00:00 2001 From: Unrud Date: Sun, 7 May 2017 21:56:07 +0200 Subject: [PATCH 5/7] Daemonize after creation of network sockets The original process should exit after the server is ready. See also https://www.freedesktop.org/software/systemd/man/daemon.html#SysV%20Daemons --- radicale/__main__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/radicale/__main__.py b/radicale/__main__.py index 4cdd6fe..66dddef 100644 --- a/radicale/__main__.py +++ b/radicale/__main__.py @@ -158,7 +158,6 @@ def daemonize(configuration, logger): def serve(configuration, logger): """Serve radicale from configuration.""" - daemonize(configuration, logger) logger.info("Starting Radicale") # Create collection servers @@ -234,6 +233,7 @@ def serve(configuration, logger): else: # Fallback to busy waiting select_timeout = 1.0 + daemonize(configuration, logger) logger.debug("Radicale server ready") while not shutdown_program: try: From 6762fc1cadb24b34d0888e36305ee84c2f50ad07 Mon Sep 17 00:00:00 2001 From: Unrud Date: Fri, 12 May 2017 11:35:40 +0200 Subject: [PATCH 6/7] Expand user on PID path --- radicale/__main__.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/radicale/__main__.py b/radicale/__main__.py index 66dddef..90c342a 100644 --- a/radicale/__main__.py +++ b/radicale/__main__.py @@ -120,7 +120,8 @@ def daemonize(configuration, logger): # Check and create PID file in a race-free manner if configuration.get("server", "pid"): try: - pid_path = os.path.abspath(configuration.get("server", "pid")) + pid_path = os.path.abspath(os.path.expanduser( + configuration.get("server", "pid"))) pid_fd = os.open( pid_path, os.O_CREAT | os.O_EXCL | os.O_WRONLY) except OSError as e: From 9cd9ad9a027be5719bc18d9f7c064e9ce30d9038 Mon Sep 17 00:00:00 2001 From: Unrud Date: Tue, 30 May 2017 07:41:50 +0200 Subject: [PATCH 7/7] Only register exit function if a PID file was created The function only deletes the PID file. --- radicale/__main__.py | 71 +++++++++++++++++++++----------------------- 1 file changed, 34 insertions(+), 37 deletions(-) diff --git a/radicale/__main__.py b/radicale/__main__.py index 90c342a..2e3fcdf 100644 --- a/radicale/__main__.py +++ b/radicale/__main__.py @@ -116,45 +116,41 @@ def run(): def daemonize(configuration, logger): """Fork and decouple if Radicale is configured as daemon.""" - if configuration.getboolean("server", "daemon"): - # Check and create PID file in a race-free manner + # Check and create PID file in a race-free manner + if configuration.get("server", "pid"): + try: + pid_path = os.path.abspath(os.path.expanduser( + configuration.get("server", "pid"))) + pid_fd = os.open( + pid_path, os.O_CREAT | os.O_EXCL | os.O_WRONLY) + except OSError as e: + raise OSError("PID file exists: %s" % + configuration.get("server", "pid")) from e + pid = os.fork() + if pid: + # Write PID if configuration.get("server", "pid"): - try: - pid_path = os.path.abspath(os.path.expanduser( - configuration.get("server", "pid"))) - pid_fd = os.open( - pid_path, os.O_CREAT | os.O_EXCL | os.O_WRONLY) - except OSError as e: - raise OSError("PID file exists: %s" % - configuration.get("server", "pid")) from e - pid = os.fork() - if pid: - # Write PID - if configuration.get("server", "pid"): - with os.fdopen(pid_fd, "w") as pid_file: - pid_file.write(str(pid)) - sys.exit() - if configuration.get("server", "pid"): - os.close(pid_fd) - # Decouple environment - os.chdir("/") - os.setsid() - with open(os.devnull, "r") as null_in: - os.dup2(null_in.fileno(), sys.stdin.fileno()) - with open(os.devnull, "w") as null_out: - os.dup2(null_out.fileno(), sys.stdout.fileno()) - os.dup2(null_out.fileno(), sys.stderr.fileno()) + with os.fdopen(pid_fd, "w") as pid_file: + pid_file.write(str(pid)) + sys.exit() + if configuration.get("server", "pid"): + os.close(pid_fd) - # Register exit function - def cleanup(): - """Remove the PID files.""" - logger.debug("Cleaning up") - # Remove PID file - if (configuration.get("server", "pid") and - configuration.getboolean("server", "daemon")): + # Register exit function + def cleanup(): + """Remove the PID files.""" + logger.debug("Cleaning up") + # Remove PID file os.unlink(pid_path) - - atexit.register(cleanup) + atexit.register(cleanup) + # Decouple environment + os.chdir("/") + os.setsid() + with open(os.devnull, "r") as null_in: + os.dup2(null_in.fileno(), sys.stdin.fileno()) + with open(os.devnull, "w") as null_out: + os.dup2(null_out.fileno(), sys.stdout.fileno()) + os.dup2(null_out.fileno(), sys.stderr.fileno()) def serve(configuration, logger): @@ -234,7 +230,8 @@ def serve(configuration, logger): else: # Fallback to busy waiting select_timeout = 1.0 - daemonize(configuration, logger) + if configuration.getboolean("server", "daemon"): + daemonize(configuration, logger) logger.debug("Radicale server ready") while not shutdown_program: try: