summaryrefslogtreecommitdiff
path: root/libmailutils/daemon.c
diff options
context:
space:
mode:
Diffstat (limited to 'libmailutils/daemon.c')
-rw-r--r--libmailutils/daemon.c189
1 files changed, 189 insertions, 0 deletions
diff --git a/libmailutils/daemon.c b/libmailutils/daemon.c
new file mode 100644
index 000000000..b7fca61a5
--- /dev/null
+++ b/libmailutils/daemon.c
@@ -0,0 +1,189 @@
+/* GNU Mailutils -- a suite of utilities for electronic mail
+ Copyright (C) 2004, 2005, 2007, 2008, 2009, 2010 Free Software
+ Foundation, Inc.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General
+ Public License along with this library; if not, write to the
+ Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301 USA */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <string.h>
+
+#include <mailutils/daemon.h>
+#include <mailutils/errno.h>
+#include <mailutils/error.h>
+#include <mailutils/nls.h>
+
+static char *pidfile;
+static pid_t current_pid;
+
+/* Return 0 if DIR is writable for EUID/EGID.
+ Otherwise, return error code. */
+static int
+ewraccess (const char *dir)
+{
+ struct stat st;
+ if (stat (dir, &st))
+ return errno;
+ if ((st.st_mode & S_IWOTH)
+ || (st.st_gid == getegid () && (st.st_mode & S_IWGRP))
+ || (st.st_uid == geteuid () && (st.st_mode & S_IWUSR)))
+ return 0;
+ else
+ return EACCES;
+}
+
+/* Return 0 if DIR is writable. If necessary and possible, raise to
+ EUID 0, in that case return prior EUID in the memory location pointed to
+ by PUID. */
+static int
+access_dir (const char *dir, uid_t *puid)
+{
+ int ec = ewraccess (dir);
+ if (ec)
+ {
+ if (ec == EACCES && access (dir, W_OK) == 0)
+ {
+ uid_t uid = geteuid ();
+ /* See if we can become root */
+ if (uid && getuid () == 0 && seteuid (0) == 0)
+ {
+ *puid = uid;
+ return 0;
+ }
+ }
+ }
+ return ec;
+}
+
+int
+mu_daemon_create_pidfile (const char *filename)
+{
+ char *p;
+ int fd;
+ uid_t uid = 0;
+ int rc;
+
+ if (filename[0] != '/')
+ return EINVAL;
+
+ if (pidfile)
+ free (pidfile);
+ pidfile = strdup (filename);
+ if (!pidfile)
+ return ENOMEM;
+
+ /* Determine the hosting directory name */
+ p = strrchr (pidfile, '/');
+ if (pidfile == p)
+ {
+ free (pidfile);
+ pidfile = NULL;
+ /* Sorry, pidfiles in root dir are not allowed */
+ return EINVAL;
+ }
+ /* Check if we have write access to the directory */
+ *p = 0;
+ rc = access_dir (pidfile, &uid);
+ if (rc)
+ {
+ /* Nope, clean up and return */
+ free (pidfile);
+ pidfile = NULL;
+ return rc;
+ }
+
+ /* Restore directory separator */
+ *p = '/';
+
+ unlink (pidfile);
+ current_pid = getpid ();
+
+ if ((fd = open (pidfile, O_WRONLY | O_CREAT | O_TRUNC | O_EXCL, 0644)) != -1)
+ {
+ FILE *fp = fdopen (fd, "w");
+ if (!fp)
+ {
+ rc = errno;
+ free (pidfile);
+ close (fd);
+ }
+ else
+ {
+ fprintf (fp, "%lu", (unsigned long) current_pid);
+ fclose (fp);
+ atexit (mu_daemon_remove_pidfile);
+ }
+ }
+ else
+ {
+ rc = errno;
+ free (pidfile);
+ pidfile = NULL;
+ }
+
+ /* Restore previous EUID value. */
+ if (uid)
+ seteuid (uid);
+
+ return rc;
+}
+
+void
+mu_daemon_remove_pidfile (void)
+{
+ if (getpid () == current_pid)
+ {
+ int rc;
+ uid_t uid = 0;
+
+ /* Determine the hosting directory name */
+ char *p = strrchr (pidfile, '/');
+ if (pidfile == p)
+ {
+ /* Should not happen */
+ abort ();
+ }
+ /* Check if we have write access to the directory */
+ *p = 0;
+ rc = access_dir (pidfile, &uid);
+ *p = '/';
+ if (rc == 0)
+ {
+ if (unlink (pidfile) && errno != ENOENT)
+ rc = errno;
+ else
+ rc = 0;
+ }
+
+ if (rc)
+ mu_error (_("cannot remove pidfile %s: %s"),
+ pidfile, mu_strerror (rc));
+
+ free (pidfile);
+ pidfile = NULL;
+ }
+}
+
+
+

Return to:

Send suggestions and report system problems to the System administrator.