From dd3258133e54bd2c3355b09f43059f56fab5ee14 Mon Sep 17 00:00:00 2001
From: Hector Martin
Date: Wed, 19 Aug 2009 03:25:34 +0200
Subject: Fix daemonization and lockfile madness

---
 usbmuxd/main.c | 201 ++++++++++++++++++++++++++++++++++++++++-----------------
 1 file changed, 140 insertions(+), 61 deletions(-)

diff --git a/usbmuxd/main.c b/usbmuxd/main.c
index be7451f..6b3c881 100644
--- a/usbmuxd/main.c
+++ b/usbmuxd/main.c
@@ -33,7 +33,8 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 #include <sys/socket.h>
 #include <sys/un.h>
 #include <sys/stat.h>
-#include <sys/fcntl.h>
+#include <sys/types.h>
+#include <fcntl.h>
 #include <getopt.h>
 #include <pwd.h>
 #include <grp.h>
@@ -44,7 +45,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 #include "client.h"
 
 static const char *socket_path = "/var/run/usbmuxd";
-static const char *lockfile = "/var/run/usbmuxd.lock";
+static const char *lockfile = "/var/run/usbmuxd.pid";
 
 int should_exit;
 
@@ -55,6 +56,9 @@ static const char *drop_user = "usbmux";
 static int opt_udev = 0;
 static int opt_exit = 0;
 static int exit_signal = 0;
+static int daemon_pipe;
+
+static int report_to_parent = 0;
 
 int create_socket(void) {
 	struct sockaddr_un bind_addr;
@@ -191,25 +195,47 @@ int main_loop(int listenfd)
 /**
  * make this program run detached from the current console
  */
-static int daemonize()
+static int daemonize(void)
 {
 	pid_t pid;
 	pid_t sid;
+	int pfd[2];
+	int res;
 
 	// already a daemon
 	if (getppid() == 1)
 		return 0;
 
+	if((res = pipe(pfd)) < 0) {
+		usbmuxd_log(LL_FATAL, "pipe() failed.");
+		return res;
+	}
+
 	pid = fork();
 	if (pid < 0) {
-		exit(EXIT_FAILURE);
+		usbmuxd_log(LL_FATAL, "fork() failed.");
+		return pid;
 	}
 
 	if (pid > 0) {
 		// exit parent process
-		exit(EXIT_SUCCESS);
+		int status;
+		close(pfd[1]);
+
+		if((res = read(pfd[0],&status,sizeof(int))) != sizeof(int)) {
+			fprintf(stderr, "usbmuxd: ERROR: Failed to get init status from child, check syslog for messages.\n");
+			exit(1);
+		}
+		if(status != 0)
+			fprintf(stderr, "usbmuxd: ERROR: Child process exited with error %d, check syslog for messages.\n", status);
+		exit(status);
 	}
 	// At this point we are executing as the child process
+	// but we need to do one more fork
+
+	daemon_pipe = pfd[1];
+	close(pfd[0]);
+	report_to_parent = 1;
 
 	// Change the file mode mask
 	umask(0);
@@ -217,23 +243,57 @@ static int daemonize()
 	// Create a new SID for the child process
 	sid = setsid();
 	if (sid < 0) {
+		usbmuxd_log(LL_FATAL, "setsid() failed.");
 		return -1;
 	}
+
+	pid = fork();
+	if (pid < 0) {
+		usbmuxd_log(LL_FATAL, "fork() failed (second).");
+		return pid;
+	}
+
+	if (pid > 0) {
+		// exit parent process
+		close(daemon_pipe);
+		exit(0);
+	}
+
 	// Change the current working directory.
 	if ((chdir("/")) < 0) {
+		usbmuxd_log(LL_FATAL, "chdir() failed");
 		return -2;
 	}
 	// Redirect standard files to /dev/null
 	if (!freopen("/dev/null", "r", stdin)) {
-		usbmuxd_log(LL_ERROR, "ERROR: redirection of stdin failed.");
+		usbmuxd_log(LL_FATAL, "Redirection of stdin failed.");
+		return -3;
 	}
 	if (!freopen("/dev/null", "w", stdout)) {
-		usbmuxd_log(LL_ERROR, "ERROR: redirection of stdout failed.");
+		usbmuxd_log(LL_FATAL, "Redirection of stdout failed.");
+		return -3;
+	}
+
+	return 0;
+}
+
+static int notify_parent(int status)
+{
+	int res;
+
+	report_to_parent = 0;
+	if ((res = write(daemon_pipe, &status, sizeof(int))) != sizeof(int)) {
+		usbmuxd_log(LL_FATAL, "Could not notify parent!");
+		if(res >= 0)
+			return -2;
+		else
+			return res;
 	}
+	close(daemon_pipe);
 	if (!freopen("/dev/null", "w", stderr)) {
-		usbmuxd_log(LL_ERROR, "ERROR: redirection of stderr failed.");
+		usbmuxd_log(LL_FATAL, "Redirection of stderr failed.");
+		return -1;
 	}
-
 	return 0;
 }
 
@@ -310,8 +370,9 @@ int main(int argc, char *argv[])
 {
 	int listenfd;
 	int res = 0;
-	FILE *lfd = NULL;
+	int lfd;
 	struct flock lock;
+	char pids[10];
 
 	parse_opts(argc, argv);
 
@@ -333,60 +394,77 @@ int main(int argc, char *argv[])
 
 	set_signal_handlers();
 
-	lfd = fopen(lockfile, "r");
-	if (lfd) {
-		lock.l_type = 0;
-		lock.l_whence = SEEK_SET;
-		lock.l_start = 0;
-		lock.l_len = 0;
-		fcntl(fileno(lfd), F_GETLK, &lock);
-		fclose(lfd);
-		if (lock.l_type != F_UNLCK) {
-			if (opt_exit) {
-				if (lock.l_pid && !kill(lock.l_pid, 0)) {
-					usbmuxd_log(LL_NOTICE, "sending signal %d to instance with pid %d", exit_signal, lock.l_pid);
-					if (kill(lock.l_pid, exit_signal) < 0) {
-						usbmuxd_log(LL_FATAL, "Error: could not deliver signal %d to pid %d", exit_signal, lock.l_pid);
-					}
-					res = 0;
-					goto terminate;
-				} else {
-					usbmuxd_log(LL_ERROR, "Could not determine pid of the other running instance!");
+	res = lfd = open(lockfile, O_WRONLY|O_CREAT, 0644);
+	if(res == -1) {
+		usbmuxd_log(LL_FATAL, "Could not open lockfile");
+		goto terminate;
+	}
+	lock.l_type = F_WRLCK;
+	lock.l_whence = SEEK_SET;
+	lock.l_start = 0;
+	lock.l_len = 0;
+	fcntl(lfd, F_GETLK, &lock);
+	close(lfd);
+	if (lock.l_type != F_UNLCK) {
+		if (opt_exit) {
+			if (lock.l_pid && !kill(lock.l_pid, 0)) {
+				usbmuxd_log(LL_NOTICE, "Sending signal %d to instance with pid %d", exit_signal, lock.l_pid);
+				res = 0;
+				if (kill(lock.l_pid, exit_signal) < 0) {
+					usbmuxd_log(LL_FATAL, "Could not deliver signal %d to pid %d", exit_signal, lock.l_pid);
 					res = -1;
-					goto terminate;
 				}
+				goto terminate;
 			} else {
-				if (!opt_udev) {
-					usbmuxd_log(LL_ERROR, "Another instance is already running (pid %d). exiting.", lock.l_pid);
-					res = -1;
-				} else {
-					usbmuxd_log(LL_NOTICE, "Another instance is already running (pid %d). exiting.", lock.l_pid);
-					res = 0;
-				}
+				usbmuxd_log(LL_ERROR, "Could not determine pid of the other running instance!");
+				res = -1;
 				goto terminate;
 			}
+		} else {
+			if (!opt_udev) {
+				usbmuxd_log(LL_ERROR, "Another instance is already running (pid %d). exiting.", lock.l_pid);
+				res = -1;
+			} else {
+				usbmuxd_log(LL_NOTICE, "Another instance is already running (pid %d). exiting.", lock.l_pid);
+				res = 0;
+			}
+			goto terminate;
 		}
 	}
+	unlink(lockfile);
 
 	if (opt_exit) {
 		usbmuxd_log(LL_NOTICE, "No running instance found, none killed. exiting.");
 		goto terminate;
 	}
 
-	// now open the lockfile and place the lock
-	lfd = fopen(lockfile, "w");
-	if (lfd) {
-		lock.l_type = F_WRLCK;
-		lock.l_whence = SEEK_SET;
-		lock.l_start = 0;
-		lock.l_len = 0;
-		if ((res = fcntl(fileno(lfd), F_SETLK, &lock)) < 0) {
-			usbmuxd_log(LL_FATAL, "Lockfile locking failed!");
+	if (!foreground) {
+		if ((res = daemonize()) < 0) {
+			fprintf(stderr, "usbmuxd: FATAL: Could not daemonize!\n");
+			usbmuxd_log(LL_FATAL, "Could not daemonize!");
 			goto terminate;
 		}
-	} else {
-		usbmuxd_log(LL_FATAL, "Lockfile open failed!");
-		res = -1;
+	}
+
+	// now open the lockfile and place the lock
+	res = lfd = open(lockfile, O_WRONLY|O_CREAT|O_TRUNC|O_EXCL, 0644);
+	if(res < 0) {
+		usbmuxd_log(LL_FATAL, "Could not open lockfile");
+		goto terminate;
+	}
+	lock.l_type = F_WRLCK;
+	lock.l_whence = SEEK_SET;
+	lock.l_start = 0;
+	lock.l_len = 0;
+	if ((res = fcntl(lfd, F_SETLK, &lock)) < 0) {
+		usbmuxd_log(LL_FATAL, "Lockfile locking failed!");
+		goto terminate;
+	}
+	sprintf(pids, "%d", getpid());
+	if ((res = write(lfd, pids, strlen(pids))) != strlen(pids)) {
+		usbmuxd_log(LL_FATAL, "Could not write pidfile!");
+		if(res >= 0)
+			res = -2;
 		goto terminate;
 	}
 
@@ -400,7 +478,7 @@ int main(int argc, char *argv[])
 		struct passwd *pw = getpwnam(drop_user);
 		if (!pw) {
 			usbmuxd_log(LL_FATAL, "Dropping privileges failed, check if user '%s' exists!", drop_user);
-			res = 1;
+			res = -1;
 			goto terminate;
 		}
 
@@ -420,12 +498,12 @@ int main(int argc, char *argv[])
 		// security check
 		if (setuid(0) != -1) {
 			usbmuxd_log(LL_FATAL, "Failed to drop privileges properly!");
-			res = 1;
+			res = -1;
 			goto terminate;
 		}
 		if (getuid() != pw->pw_uid || getgid() != pw->pw_gid) {
 			usbmuxd_log(LL_FATAL, "Failed to drop privileges properly!");
-			res = 1;
+			res = -1;
 			goto terminate;
 		}
 		usbmuxd_log(LL_NOTICE, "Successfully dropped privileges to '%s'", drop_user);
@@ -441,13 +519,9 @@ int main(int argc, char *argv[])
 
 	usbmuxd_log(LL_NOTICE, "Initialization complete");
 
-	if (!foreground) {
-		if ((res = daemonize()) < 0) {
-			fprintf(stderr, "usbmuxd: FATAL: Could not daemonize!\n");
-			usbmuxd_log(LL_FATAL, "Could not daemonize!");
+	if (report_to_parent)
+		if((res = notify_parent(0)) < 0)
 			goto terminate;
-		}
-	}
 
 	res = main_loop(listenfd);
 	if(res < 0)
@@ -463,7 +537,12 @@ int main(int argc, char *argv[])
 terminate:
 	log_disable_syslog();
 
-	if(res < 0)
-		return -res;
-	return 0;
+	if (res < 0)
+		res = -res;
+	else
+		res = 0;
+	if (report_to_parent)
+		notify_parent(res);
+
+	return res;
 }
-- 
cgit v1.1-32-gdbae