espeakup release coming soon

Samuel Thibault samuel.thibault at ens-lyon.org
Wed Jul 20 19:35:31 EDT 2016


Hello,

currently espeakup uses daemon() to do the daemonizing stuff.
Unfortunately, daemon() does things not very appropriately, and there
is notably a delay between the parent exit()ing and the child writing
the pid file. The attached patch reimplements it properly, espeakup then
notably plays much more nicely with systemd.

Samuel
-------------- next part --------------
--- a/espeakup.c
+++ b/espeakup.c
@@ -24,6 +24,8 @@
 #include <string.h>
 #include <unistd.h>
 #include <pthread.h>
+#include <fcntl.h>
+#include <sys/file.h>
 
 #include "espeakup.h"
 
@@ -42,21 +44,94 @@ const int defaultVolume = 5;
 char *defaultVoice = NULL;
 int debug = 0;
 
+int espeakup_start_daemon(void)
+{
+	int fds[2];
+	pid_t pid;
+	char c;
+
+	if (pipe(fds) < 0) {
+		perror("pipe");
+		exit(1);
+	}
+	pid = fork();
+
+	if (pid < 0) {
+		perror("fork");
+		exit(1);
+	}
+	if (pid) {
+		/* Parent, just wait for daemon */
+		if (read(fds[0], &c, 1) < 0) {
+			printf("Espeakup is already running!\n");
+			exit(1);
+		}
+		exit(c);
+	}
+
+
+	/* Child, create new session */
+	setsid();
+	pid = fork();
+	if (pid)
+		/* Intermediate child, just exit */
+		exit(0);
+
+	/* Child */
+	if (chdir("/") < 0) {
+		c = 1;
+		(void) write(fds[1], &c, 1);
+		exit(1);
+	}
+	return fds[1];
+}
+
 int espeakup_is_running(void)
 {
-	int rc;
-	FILE *pidFile;
+	int pidFile;
+	int n;
+	char s[16];
 	pid_t pid;
 
-	rc = 0;
-	pidFile = fopen(pidPath, "r");
-	if (pidFile) {
-		fscanf(pidFile, "%d", &pid);
-		fclose(pidFile);
-		if (!kill(pid, 0) || errno != ESRCH)
-			rc = 1;
+	pidFile = open(pidPath, O_RDWR|O_CREAT, 0666);
+	if (pidFile < 0) {
+		printf("Can not work with the pid file %s: %s\n", pidPath, strerror(errno));
+		return -1;
+	}
+
+	if (flock(pidFile, LOCK_EX) < 0) {
+		printf("Can not lock the pid file %s: %s\n", pidPath, strerror(errno));
+		goto error;
+	}
+	n = read(pidFile, s, sizeof(s)-1);
+	if (n < 0) {
+		printf("Can not read the pid file %s: %s\n", pidPath, strerror(errno));
+		goto error;
+	}
+	s[n] = 0;
+	n = sscanf(s, "%d", &pid);
+	if (n == 1 && (!kill(pid, 0) || errno != ESRCH))
+	{
+		/* Already running */
+		close(pidFile);
+		return 1;
 	}
-	return rc;
+	if (ftruncate(pidFile, 0) < 0) {
+		printf("Could not truncate the pid file %s: %s\n", pidPath, strerror(errno));
+		goto error;
+	}
+	lseek(pidFile, 0, SEEK_SET);
+	n = snprintf(s, sizeof(s), "%d", getpid());
+	if (write(pidFile, s, n) < 0) {
+		printf("Could not write to the pid file %s: %s\n", pidPath, strerror(errno));
+		goto error;
+	}
+	close(pidFile);
+	return 0;
+
+error:
+	close(pidFile);
+	return -1;
 }
 
 int create_pid_file(void)
@@ -91,6 +166,8 @@ void espeakup_sighandler(int sig)
 
 int main(int argc, char **argv)
 {
+	int fd, null;
+	char ret;
 	pthread_t queue_thread_id;
 	struct synth_t s = {
 		.voice = "",
@@ -99,27 +176,37 @@ int main(int argc, char **argv)
 	/* process command line options */
 	process_cli(argc, argv);
 
+	if (!debug)
+		fd = espeakup_start_daemon();
+
 	/* Is the espeakup daemon running? */
 	if (espeakup_is_running()) {
 		printf("Espeakup is already running!\n");
-		return 1;
+		ret = 1;
+		goto out;
 	}
 
 	/* open the softsynth. */
 	if (! open_softsynth()) {
 		perror("Unable to open the softsynth device");
-		return 3;
+		ret = 3;
+		goto out;
+	}
+
+	if (!debug) {
+		/* Shut down stdout/stderr */
+		null = open("/dev/null", O_RDWR);
+		dup2(null, STDIN_FILENO);
+		dup2(null, STDOUT_FILENO);
+		dup2(null, STDERR_FILENO);
+		if (null > 2)
+			close(null);
 	}
 
 	/* register signal handler */
 	signal(SIGINT, espeakup_sighandler);
 	signal(SIGTERM, espeakup_sighandler);
 
-	if (!debug) {
-		/* become a daemon */
-		daemon(0, 1);
-	}
-
 	/* initialize espeak */
 	espeak_Initialize(AUDIO_OUTPUT_PLAYBACK, 0, NULL, 0);
 
@@ -138,19 +225,23 @@ int main(int argc, char **argv)
 	/* Spawn our queue-processing thread. */
 	int err = pthread_create(&queue_thread_id, NULL, &queue_runner, &s);
 	if (err != 0) {
-		return 4;
+		ret = 4;
+		goto out;
 	}
 
 	if (!debug) {
-		/* We are now ready, write our pid file. */
-		if (create_pid_file() < 0) {
-			perror("Unable to create pid file");
-			return 2;
-		}
+		/* We are now ready, notify parent */
+		ret = 0;
+		(void) write(fd, &ret, 1);
+		close(fd);
 	}
 
 	/* run the main loop */
 	main_loop(&s);
-
 	return 0;
+
+out:
+	if (!debug)
+		(void) write(fd, &ret, 1);
+	return ret;
 }


More information about the Speakup mailing list