-/* $Id: process.c,v 1.1 2007-06-08 13:57:19 adam Exp $
+/* $Id: process.c,v 1.4 2007-06-18 11:44:43 adam Exp $
Copyright (c) 2006-2007, Index Data.
This file is part of Pazpar2.
#endif
#include <signal.h>
+#include <errno.h>
#include <unistd.h>
#include <assert.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/wait.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <pwd.h>
#include <yaz/log.h>
-
+#include <yaz/snprintf.h>
#include "pazpar2.h"
-static void write_pidfile(const char *pidfile)
+static void write_pidfile(int pid_fd)
{
- if (pidfile)
+ if (pid_fd != -1)
{
- FILE *f = fopen(pidfile, "w");
- if (!f)
+ char buf[40];
+ yaz_snprintf(buf, sizeof(buf), "%ld", (long) getpid());
+ if (ftruncate(pid_fd, 0))
{
- yaz_log(YLOG_ERRNO|YLOG_FATAL, "Couldn't create %s", pidfile);
- exit(0);
+ yaz_log(YLOG_FATAL|YLOG_ERRNO, "ftruncate");
+ exit(1);
}
- fprintf(f, "%ld", (long) getpid());
- fclose(f);
+ if (write(pid_fd, buf, strlen(buf)) != strlen(buf))
+ {
+ yaz_log(YLOG_FATAL|YLOG_ERRNO, "write");
+ exit(1);
+ }
+ close(pid_fd);
}
}
kill(child_pid, num);
}
-int pazpar2_process(int debug, int flags,
+int pazpar2_process(int debug, int daemon,
void (*work)(void *data), void *data,
const char *pidfile, const char *uid /* not yet used */)
{
int cont = 1;
void (*old_sighup)(int);
void (*old_sigterm)(int);
+ int pid_fd = -1;
+ /* open pidfile .. defer write until in child and after setuid */
+ if (pidfile)
+ {
+ pid_fd = open(pidfile, O_CREAT|O_RDWR, 0666);
+ if (pid_fd == -1)
+ {
+ yaz_log(YLOG_FATAL|YLOG_ERRNO, "open %s", pidfile);
+ exit(1);
+ }
+ }
if (debug)
{
/* in debug mode.. it's quite simple */
- write_pidfile(pidfile);
+ write_pidfile(pid_fd);
work(data);
exit(0);
}
+
/* running in production mode. */
+ if (uid)
+ {
+ /* OK to use the non-thread version here */
+ struct passwd *pw = getpwnam(uid);
+ if (!pw)
+ {
+ yaz_log(YLOG_FATAL, "%s: Unknown user", uid);
+ exit(1);
+ }
+ if (setuid(pw->pw_uid) < 0)
+ {
+ yaz_log(YLOG_FATAL|YLOG_ERRNO, "setuid");
+ exit(1);
+ }
+ }
+
+ if (daemon)
+ {
+ /* create pipe so that parent waits until child has created
+ PID (or failed) */
+ static int hand[2]; /* hand shake for child */
+ if (pipe(hand) < 0)
+ {
+ yaz_log(YLOG_FATAL|YLOG_ERRNO, "pipe");
+ return 1;
+ }
+ switch (fork())
+ {
+ case 0:
+ break;
+ case -1:
+ return 1;
+ default:
+ close(hand[1]);
+ while(1)
+ {
+ char dummy[1];
+ int res = read(hand[0], dummy, 1);
+ if (res < 0 && errno != EINTR)
+ {
+ yaz_log(YLOG_FATAL|YLOG_ERRNO, "read fork handshake");
+ break;
+ }
+ else if (res >= 0)
+ break;
+ }
+ close(hand[0]);
+ _exit(0);
+ }
+ /* child */
+ close(hand[0]);
+ if (setsid() < 0)
+ return 1;
+
+ close(0);
+ close(1);
+ close(2);
+ open("/dev/null", O_RDWR);
+ dup(0); dup(0);
+ close(hand[1]);
+ }
+
+ write_pidfile(pid_fd);
/* keep signals in their original state and make sure that some signals
- to parent process also gets sent to the child.. Normally this
- should not happen. We want the _child_ process to be terminated
- normally. However, if the parent process is terminated, we
- kill the child too */
+ to parent process also gets sent to the child..
+ */
old_sighup = signal(SIGHUP, kill_child_handler);
old_sigterm = signal(SIGTERM, kill_child_handler);
while (cont)
signal(SIGHUP, old_sighup); /* restore */
signal(SIGTERM, old_sigterm);/* restore */
- write_pidfile(pidfile);
work(data);
exit(0);
}
child_pid = p;
p1 = wait(&status);
- yaz_log_reopen();
/* disable signalling in kill_child_handler */
child_pid = 0;
else if (status == 0)
cont = 0; /* child exited normally */
else
- { /* child exited in an abnormal way */
+ { /* child exited with error */
yaz_log(YLOG_LOG, "Exit %d from child %ld", status, (long) p);
- cont = 1;
+ cont = 0;
}
if (cont) /* respawn slower as we get more errors */
sleep(1 + run/5);