137 lines
4.6 KiB
Diff
137 lines
4.6 KiB
Diff
From b177311aee0b3cf17af0e1760e18dfb60d99e024 Mon Sep 17 00:00:00 2001
|
|
From: Wayne Davison <wayne@opencoder.net>
|
|
Date: Thu, 4 Jun 2020 17:55:20 -0700
|
|
Subject: [PATCH] Use a lock to not fail on a left-over pid file.
|
|
|
|
---
|
|
clientserver.c | 62 ++++++++++++++++++++++++++++++++++++++------------
|
|
rsyncd.conf.yo | 7 +++---
|
|
socket.c | 3 +++
|
|
3 files changed, 55 insertions(+), 17 deletions(-)
|
|
|
|
diff --git a/clientserver.c b/clientserver.c
|
|
index 3af97d84..d56f5d52 100644
|
|
--- a/clientserver.c
|
|
+++ b/clientserver.c
|
|
@@ -66,6 +66,7 @@ extern gid_t our_gid;
|
|
char *auth_user;
|
|
int read_only = 0;
|
|
int module_id = -1;
|
|
+int pid_file_fd = -1;
|
|
struct chmod_mode_struct *daemon_chmod_modes;
|
|
|
|
/* module_dirlen is the length of the module_dir string when in daemon
|
|
@@ -1149,26 +1150,59 @@ int start_daemon(int f_in, int f_out)
|
|
static void create_pid_file(void)
|
|
{
|
|
char *pid_file = lp_pid_file();
|
|
- char pidbuf[16];
|
|
- pid_t pid = getpid();
|
|
- int fd, len;
|
|
+ char pidbuf[32];
|
|
+ STRUCT_STAT st1, st2;
|
|
+ char *fail = NULL;
|
|
|
|
if (!pid_file || !*pid_file)
|
|
return;
|
|
|
|
- cleanup_set_pid(pid);
|
|
- if ((fd = do_open(pid_file, O_WRONLY|O_CREAT|O_EXCL, 0666)) == -1) {
|
|
- failure:
|
|
- cleanup_set_pid(0);
|
|
- fprintf(stderr, "failed to create pid file %s: %s\n", pid_file, strerror(errno));
|
|
- rsyserr(FLOG, errno, "failed to create pid file %s", pid_file);
|
|
+ /* These tests make sure that a temp-style lock dir is handled safely. */
|
|
+ st1.st_mode = 0;
|
|
+ if (do_lstat(pid_file, &st1) == 0 && !S_ISREG(st1.st_mode) && unlink(pid_file) < 0)
|
|
+ fail = "unlink";
|
|
+ else if ((pid_file_fd = do_open(pid_file, O_RDWR|O_CREAT, 0664)) < 0)
|
|
+ fail = S_ISREG(st1.st_mode) ? "open" : "create";
|
|
+ else if (!lock_range(pid_file_fd, 0, 4))
|
|
+ fail = "lock";
|
|
+ else if (do_fstat(pid_file_fd, &st1) < 0)
|
|
+ fail = "fstat opened";
|
|
+ else if (st1.st_size >= (int)sizeof pidbuf)
|
|
+ fail = "find small";
|
|
+ else if (do_lstat(pid_file, &st2) < 0)
|
|
+ fail = "lstat";
|
|
+ else if (!S_ISREG(st1.st_mode))
|
|
+ fail = "avoid file overwrite race for";
|
|
+ else if (st1.st_dev != st2.st_dev || st1.st_ino != st2.st_ino)
|
|
+ fail = "verify stat info for";
|
|
+#ifdef HAVE_FTRUNCATE
|
|
+ else if (do_ftruncate(pid_file_fd, 0) < 0)
|
|
+ fail = "truncate";
|
|
+#endif
|
|
+ else {
|
|
+ pid_t pid = getpid();
|
|
+ int len = snprintf(pidbuf, sizeof pidbuf, "%d\n", (int)pid);
|
|
+#ifndef HAVE_FTRUNCATE
|
|
+ /* What can we do with a too-long file and no truncate? I guess we'll add extra newlines. */
|
|
+ while (len < st1.st_size) /* We already verfified that size+1 chars fits in the buffer. */
|
|
+ pidbuf[len++] = '\n';
|
|
+ /* We don't need the buffer to end in a '\0' (and we may not have room to add it). */
|
|
+#endif
|
|
+ if (write(pid_file_fd, pidbuf, len) != len)
|
|
+ fail = "write";
|
|
+ cleanup_set_pid(pid); /* Mark the file for removal on exit, even if the write failed. */
|
|
+ }
|
|
+
|
|
+ if (fail) {
|
|
+ char msg[1024];
|
|
+ snprintf(msg, sizeof msg, "failed to %s pid file %s: %s\n",
|
|
+ fail, pid_file, strerror(errno));
|
|
+ fputs(msg, stderr);
|
|
+ rprintf(FLOG, "%s", msg);
|
|
exit_cleanup(RERR_FILEIO);
|
|
}
|
|
- snprintf(pidbuf, sizeof pidbuf, "%d\n", (int)pid);
|
|
- len = strlen(pidbuf);
|
|
- if (write(fd, pidbuf, len) != len)
|
|
- goto failure;
|
|
- close(fd);
|
|
+
|
|
+ /* The file is left open so that the lock remains valid. It is closed in our forked child procs. */
|
|
}
|
|
|
|
/* Become a daemon, discarding the controlling terminal. */
|
|
diff --git a/rsyncd.conf.yo b/rsyncd.conf.yo
|
|
index aac4a7f2..c8338664 100644
|
|
--- a/rsyncd.conf.yo
|
|
+++ b/rsyncd.conf.yo
|
|
@@ -103,9 +103,10 @@ This can be overridden by the bf(--dparam=motdfile=FILE)
|
|
command-line option when starting the daemon.
|
|
|
|
dit(bf(pid file)) This parameter tells the rsync daemon to write
|
|
-its process ID to that file. If the file already exists, the rsync
|
|
-daemon will abort rather than overwrite the file.
|
|
-This can be overridden by the bf(--dparam=pidfile=FILE)
|
|
+its process ID to that file. The rsync keeps the file locked so that
|
|
+it can know when it is safe to overwrite an existing file.
|
|
+
|
|
+The filename can be overridden by the bf(--dparam=pidfile=FILE)
|
|
command-line option when starting the daemon.
|
|
|
|
dit(bf(port)) You can override the default port the daemon will listen on
|
|
diff --git a/socket.c b/socket.c
|
|
index 70fb1695..11ab4a83 100644
|
|
--- a/socket.c
|
|
+++ b/socket.c
|
|
@@ -38,6 +38,7 @@ extern char *bind_address;
|
|
extern char *sockopts;
|
|
extern int default_af_hint;
|
|
extern int connect_timeout;
|
|
+extern int pid_file_fd;
|
|
|
|
#ifdef HAVE_SIGACTION
|
|
static struct sigaction sigact;
|
|
@@ -609,6 +610,8 @@ void start_accept_loop(int port, int (*fn)(int, int))
|
|
|
|
if ((pid = fork()) == 0) {
|
|
int ret;
|
|
+ if (pid_file_fd >= 0)
|
|
+ close(pid_file_fd);
|
|
for (i = 0; sp[i] >= 0; i++)
|
|
close(sp[i]);
|
|
/* Re-open log file in child before possibly giving
|