summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSergey Poznyakoff <gray@gnu.org>2020-11-30 18:49:48 +0200
committerSergey Poznyakoff <gray@gnu.org>2020-11-30 18:49:48 +0200
commit23404fbf27b67bd1767998918548aee3235f201c (patch)
treed3557c6336910a53f9f8dc83568195e383d10b4d
parent42e0f735b48ab0f05e80eeec335684b25a93c4a3 (diff)
downloadmailutils-23404fbf27b67bd1767998918548aee3235f201c.tar.gz
mailutils-23404fbf27b67bd1767998918548aee3235f201c.tar.bz2
New functions for closing all fds and daemonizing
* configure.ac: Select a suitable interface for closing all file descriptors greater than or equal to a chosen one. * include/mailutils/daemon.h (mu_daemon): New proto. * include/mailutils/util.h (mu_close_fds): New proto. * lib/daemon.c: Removed. * lib/Makefile.am: Remove daemon.c * libmailutils/base/Makefile.am: Add closefds.c * libmailutils/base/closefds.c: New file. * libmailutils/base/daemon.c: Rename to libmailutils/base/pidfile.c * libmailutils/base/daemon.c: New file. * libmailutils/diag/bt.c: Use mu_close_fds. * libmailutils/server/acl.c: Likewise. * mh/mh_whatnow.c: Likewise. * libmailutils/stream/prog_stream.c: Likewise. * libmailutils/server/msrv.c: Use mu_daemon. * mh/send.c: Likewise. * mda/lib/util.c (mda_close_fds): Remove.
-rw-r--r--configure.ac29
-rw-r--r--include/mailutils/daemon.h5
-rw-r--r--include/mailutils/util.h3
-rw-r--r--lib/Makefile.am1
-rw-r--r--lib/daemon.c198
-rw-r--r--libmailutils/base/Makefile.am2
-rw-r--r--libmailutils/base/closefds.c129
-rw-r--r--libmailutils/base/daemon.c220
-rw-r--r--libmailutils/base/pidfile.c187
-rw-r--r--libmailutils/diag/bt.c4
-rw-r--r--libmailutils/server/acl.c7
-rw-r--r--libmailutils/server/msrv.c4
-rw-r--r--libmailutils/stream/prog_stream.c3
-rw-r--r--mda/lib/util.c9
-rw-r--r--mh/mh_whatnow.c4
-rw-r--r--mh/send.c4
16 files changed, 423 insertions, 386 deletions
diff --git a/configure.ac b/configure.ac
index ae80fd9c2..3e3625db8 100644
--- a/configure.ac
+++ b/configure.ac
@@ -329,6 +329,35 @@ AH_BOTTOM(
# define MU_PATH_MAILDIR PATH_MAILDIR
#endif])
+##################################
+# Select interface used to close file descriptors greater than or
+# equal to the given one.
+#
+# Variants:
+# 1. closefrom call (FreeBSD)
+# 2. F_CLOSEM fcntl (NetBSD, AIX, IRIX)
+# 3. proc_pidinfo call (Darwin)
+# 4. /proc/self/fd filesystem (Linux)
+# 5. Brute force
+#
+# The defines created here direct conditionalal compilation in
+# libmailutils/base/closefds.c
+
+AC_CHECK_FUNCS([closefrom])
+AC_CHECK_DECL([F_CLOSEM],
+ AC_DEFINE([HAVE_FCNTL_CLOSEM], [1],
+ [Use F_CLOSEM fcntl for mu_close_fds]),
+ [],
+ [#include <limits.h>
+ #include <fcntl.h>
+])
+
+AC_CHECK_HEADERS([libproc.h])
+AC_CHECK_FUNCS([proc_pidinfo])
+
+if test -d "/proc/self/fd" ; then
+ AC_DEFINE([HAVE_PROC_SELF_FD], [1], [Define if you have /proc/self/fd])
+fi
##################################
# DBM Support
##################################
diff --git a/include/mailutils/daemon.h b/include/mailutils/daemon.h
index cd368e157..c9c1d2424 100644
--- a/include/mailutils/daemon.h
+++ b/include/mailutils/daemon.h
@@ -28,8 +28,9 @@ extern "C" {
#define MODE_INTERACTIVE 0
#define MODE_DAEMON 1
-extern int mu_daemon_create_pidfile (const char *);
-extern void mu_daemon_remove_pidfile (void);
+int mu_daemon_create_pidfile (const char *);
+void mu_daemon_remove_pidfile (void);
+int mu_daemon (void);
#ifdef __cplusplus
}
diff --git a/include/mailutils/util.h b/include/mailutils/util.h
index 7c12aadec..e0aa0d167 100644
--- a/include/mailutils/util.h
+++ b/include/mailutils/util.h
@@ -248,6 +248,9 @@ int mu_remove_file (const char *path);
int mu_file_name_is_safe (char const *str);
int mu_getmaxfd (void);
+void mu_close_fds (int minfd);
+int mu_daemon (void);
+
/* Get the host name, doing a gethostbyname() if possible. */
int mu_get_host_name (char **host);
int mu_spawnvp (const char *prog, char *av[], int *stat);
diff --git a/lib/Makefile.am b/lib/Makefile.am
index a1d8ea79c..12a7c2b0b 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -27,7 +27,6 @@ lib_LTLIBRARIES = libmuaux.la
noinst_LIBRARIES = libmuscript.a libmutcpwrap.a
libmuaux_la_SOURCES = \
- daemon.c\
mailcap.c\
manlock.c\
mdecode.c\
diff --git a/lib/daemon.c b/lib/daemon.c
deleted file mode 100644
index 3709f4dcb..000000000
--- a/lib/daemon.c
+++ /dev/null
@@ -1,198 +0,0 @@
-/*-
- * Copyright (c) 1990, 1993
- * The Regents of the University of California. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 4. Neither the name of the University nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#ifdef HAVE_CONFIG_H
-# include <config.h>
-#endif
-#include <stdio.h>
-#include <fcntl.h>
-#include <unistd.h>
-#include <stdlib.h>
-#include <signal.h>
-#include <sys/types.h>
-#include <sys/wait.h>
-
-#include <confpaths.h>
-
-#ifndef PATH_DEVNULL
-# define PATH_DEVNULL "/dev/null"
-#endif
-
-/*
- According to Unix-FAQ maintained by Andrew Gierth:
-
- 1.fork() so the parent can exit, this returns control to the command
- line or shell invoking your program. This step is required so that the
- new process is guaranteed not to be a process group leader. The next
- step, setsid(), fails if you're a process group leader.
-
- 2.setsid() to become a process group and session group leader. Since a
- controlling terminal is associated with a session, and this new session
- has not yet acquired a controlling terminal our process now has no
- controlling terminal, which is a Good Thing for daemons.
-
- 3.fork() again so the parent, (the session group leader), can exit. This
- means that we, as a non-session group leader, can never regain a
- controlling terminal.
-
- 4.chdir("/") to ensure that our process doesn't keep any directory in use.
- Failure to do this could make it so that an administrator couldn't unmount
- a filesystem, because it was our current directory.
- [Equivalently, we could change to any directory containing files important
- to the daemon's operation.]
-
- 5.umask(0) so that we have complete control over the permissions of
- anything we write. We don't know what umask we may have inherited.
- [This step is optional]
-
- 6.close() fds 0, 1, and 2. This releases the standard in, out, and error
- we inherited from our parent process. We have no way of knowing where
- these fds might have been redirected to. Note that many daemons use
- sysconf() to determine the limit _SC_OPEN_MAX. _SC_OPEN_MAX tells you the
- maximun open files/process. Then in a loop, the daemon can close all
- possible file descriptors. You have to decide if you need to do this or not.
- If you think that there might be file-descriptors open you should close
- them, since there's a limit on number of concurrent file descriptors.
-
- 7.Establish new open descriptors for stdin, stdout and stderr. Even if
- you don't plan to use them, it is still a good idea to have them open.
- The precise handling of these is a matter of taste; if you have a logfile,
- for example, you might wish to open it as stdout or stderr, and open
- `/dev/null' as stdin; alternatively, you could open `/dev/console' as
- stderr and/or stdout, and `/dev/null' as stdin, or any other combination
- that makes sense for your particular daemon. */
-
-#define MAXFD 64
-
-#if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 7)
-# define __attribute__(x)
-#endif
-
-void
-waitdaemon_timeout (int signo __attribute__ ((__unused__)))
-{
- int left;
-
- left = alarm (0);
- signal (SIGALRM, SIG_DFL);
- if (left == 0)
- {
- fprintf (stderr, "timed out waiting for child\n");
- exit (1);
- }
-}
-
-/* waitdaemon is like daemon, but optionally the parent pause up
- until maxwait before exiting. Return -1, on error, otherwise
- waitdaemon will return the pid of the parent. */
-
-int
-waitdaemon (int nochdir, int noclose, int maxwait)
-{
- int fd;
- pid_t childpid;
- pid_t ppid;
-
- ppid = getpid ();
-
- switch (childpid = fork ())
- {
- case -1: /* Something went wrong. */
- return (-1);
-
- case 0: /* In the child. */
- break;
-
- default: /* In the parent. */
- if (maxwait > 0)
- {
- signal (SIGALRM, waitdaemon_timeout);
- alarm (maxwait);
- pause ();
- }
- _exit(0);
- }
-
- if (setsid () == -1)
- return -1;
-
- /* SIGHUP is ignore because when the session leader terminates
- all process in the session (the second child) are sent the SIGHUP. */
- signal (SIGHUP, SIG_IGN);
-
- switch (fork ())
- {
- case 0:
- break;
-
- case -1:
- return -1;
-
- default:
- _exit (0);
- }
-
- if (!nochdir)
- chdir ("/");
-
- if (!noclose)
- {
- int i;
- long fdlimit = -1;
-
-#if defined (HAVE_SYSCONF) && defined (_SC_OPEN_MAX)
- fdlimit = sysconf (_SC_OPEN_MAX);
-#elif defined (HAVE_GETDTABLESIZE)
- fdlimit = getdtablesize ();
-#endif
-
- if (fdlimit == -1)
- fdlimit = MAXFD;
-
- for (i = 0; i < fdlimit; i++)
- close (i);
-
- fd = open (PATH_DEVNULL, O_RDWR, 0);
- if (fd != -1)
- {
- dup2 (fd, STDIN_FILENO);
- dup2 (fd, STDOUT_FILENO);
- dup2 (fd, STDERR_FILENO);
- if (fd > 2)
- close (fd);
- }
- }
- return ppid;
-}
-
-int
-daemon (int nochdir, int noclose)
-{
- return (waitdaemon (nochdir, noclose, 0) == -1) ? -1 : 0;
-}
diff --git a/libmailutils/base/Makefile.am b/libmailutils/base/Makefile.am
index 3d6820c77..d56938751 100644
--- a/libmailutils/base/Makefile.am
+++ b/libmailutils/base/Makefile.am
@@ -24,6 +24,7 @@ libbase_la_SOURCES = \
argcvjoin.c\
argcvrem.c\
assoc.c\
+ closefds.c\
coord.c\
copyfile.c\
ctparse.c\
@@ -55,6 +56,7 @@ libbase_la_SOURCES = \
onexit.c\
opool.c\
permstr.c\
+ pidfile.c\
registrar.c\
refcount.c\
renamefile.c\
diff --git a/libmailutils/base/closefds.c b/libmailutils/base/closefds.c
new file mode 100644
index 000000000..49c024fb1
--- /dev/null
+++ b/libmailutils/base/closefds.c
@@ -0,0 +1,129 @@
+/* GNU Mailutils -- a suite of utilities for electronic mail
+ Copyright (C) 2019-2020 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, 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 GNU Mailutils. If not, see <http://www.gnu.org/licenses/>. */
+#include <config.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <mailutils/util.h>
+
+#if defined (HAVE_FUNC_CLOSEFROM)
+# include <unistd.h>
+
+static int
+close_fds_sys (int minfd)
+{
+ closefrom (minfd);
+ return 0;
+}
+
+#elif defined (HAVE_FCNTL_CLOSEM)
+# include <fcntl.h>
+
+static int
+close_fds_sys (int minfd)
+{
+ fcntl (minfd, F_CLOSEM, 0);
+ return 0;
+}
+
+#elif defined (HAVE_LIBPROC_H) && defined (HAVE_FUNC_PROC_PIDINFO)
+#include <libproc.h>
+
+static int
+close_fds_sys (int minfd)
+{
+ pid_t pid = getpid ();
+ struct proc_fdinfo *fdinfo;
+ int i, n, size;
+
+ size = proc_pidinfo (pid, PROC_PIDLISTFDS, 0, NULL, 0);
+ if (size == 0)
+ return 0;
+ else if (size < 0)
+ return -1;
+
+ fdinfo = calloc (size, sizeof (fdinfo[0]));
+ if (!fdinfo)
+ return -1;
+
+ n = proc_pidinfo (pid, PROC_PIDLISTFDS, 0, fdinfo, size);
+ if (n <= 0)
+ {
+ free (fdinfo);
+ return -1;
+ }
+
+ n /= PROC_PIDLISTFD_SIZE;
+
+ for (i = minfd; i < n; i++)
+ {
+ close (fdinfo_buf[i].proc_fd);
+ }
+
+ free (fdinfo);
+ return 0;
+}
+
+#elif defined (HAVE_PROC_SELF_FD)
+# include <sys/types.h>
+# include <dirent.h>
+# include <limits.h>
+
+static int
+close_fds_sys (int minfd)
+{
+ DIR *dir;
+ struct dirent *ent;
+
+ dir = opendir ("/proc/self/fd");
+ if (!dir)
+ return -1;
+ while ((ent = readdir (dir)) != NULL)
+ {
+ long n;
+ char *p;
+
+ if (ent->d_name[0] == '.')
+ continue;
+
+ n = strtol (ent->d_name, &p, 10);
+ if (n >= minfd && n < INT_MAX && *p == 0)
+ close ((int) n);
+ }
+ closedir (dir);
+ return 0;
+}
+
+#else
+# define close_fds_sys(fd) (-1)
+#endif
+
+static int
+close_fds_bruteforce (int minfd)
+{
+ int i, n = mu_getmaxfd ();
+
+ for (i = minfd; i < n; i++)
+ close (i);
+
+ return 0;
+}
+
+void
+mu_close_fds (int minfd)
+{
+ if (close_fds_sys (minfd))
+ close_fds_bruteforce (minfd);
+}
diff --git a/libmailutils/base/daemon.c b/libmailutils/base/daemon.c
index a2a3252ca..d7ca0c6cd 100644
--- a/libmailutils/base/daemon.c
+++ b/libmailutils/base/daemon.c
@@ -1,187 +1,87 @@
/* GNU Mailutils -- a suite of utilities for electronic mail
- Copyright (C) 2004-2020 Free Software Foundation, Inc.
+ Copyright (C) 2019-2020 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 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, 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.
+ 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, see
- <http://www.gnu.org/licenses/>. */
-
-#ifdef HAVE_CONFIG_H
-# include <config.h>
-#endif
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
+ You should have received a copy of the GNU Lesser General Public License
+ along with GNU Mailutils. If not, see <http://www.gnu.org/licenses/>. */
+#include <config.h>
+#include <confpaths.h>
#include <fcntl.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <string.h>
-
+#include <unistd.h>
+#include <unistd.h>
+#include <errno.h>
+#include <signal.h>
+#include <mailutils/util.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)
+mu_daemon (void)
{
- char *p;
int fd;
- uid_t uid = 0;
- int rc;
- if (filename[0] != '/')
- return EINVAL;
+ switch (fork ())
+ {
+ case 0:
+ break;
- if (pidfile)
- free (pidfile);
- pidfile = strdup (filename);
- if (!pidfile)
- return ENOMEM;
+ case -1:
+ return errno;
- /* 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;
+ default:
+ _exit (0);
}
- /* Restore directory separator */
- *p = '/';
-
- unlink (pidfile);
- current_pid = getpid ();
+ if (setsid () == (pid_t) -1)
+ return errno;
+
+ signal (SIGHUP, SIG_IGN);
- if ((fd = open (pidfile, O_WRONLY | O_CREAT | O_TRUNC | O_EXCL, 0644)) != -1)
+ switch (fork ())
{
- 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;
- }
+ case 0:
+ break;
- /* Restore previous EUID value. */
- if (uid)
- seteuid (uid);
-
- return rc;
-}
+ case -1:
+ return errno;
-void
-mu_daemon_remove_pidfile (void)
-{
- if (getpid () == current_pid)
- {
- int rc;
- uid_t uid = 0;
+ default:
+ _exit (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));
+ chdir ("/");
+ mu_close_fds (0);
- free (pidfile);
- pidfile = NULL;
+ fd = open (PATH_DEVNULL, O_RDWR);
+ if (fd == 0)
+ {
+ dup2 (fd, 1);
+ dup2 (fd, 2);
+ }
+ else if (fd > 0)
+ {
+ /* This means that mu_close_fds failed to close stdin.
+ Shouldn't happen, but just in case ... */
+ dup2 (fd, 0);
+ dup2 (fd, 1);
+ dup2 (fd, 2);
+ close (fd);
}
+
+ return 0;
}
-
+
+
+
+
+
diff --git a/libmailutils/base/pidfile.c b/libmailutils/base/pidfile.c
new file mode 100644
index 000000000..a2a3252ca
--- /dev/null
+++ b/libmailutils/base/pidfile.c
@@ -0,0 +1,187 @@
+/* GNU Mailutils -- a suite of utilities for electronic mail
+ Copyright (C) 2004-2020 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, see
+ <http://www.gnu.org/licenses/>. */
+
+#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;
+ }
+}
+
+
+
diff --git a/libmailutils/diag/bt.c b/libmailutils/diag/bt.c
index c27634ef2..3b43dcd21 100644
--- a/libmailutils/diag/bt.c
+++ b/libmailutils/diag/bt.c
@@ -36,7 +36,6 @@
void
mu_gdb_bt ()
{
- int i;
pid_t master_pid = getpid ();
pid_t pid;
static char buf[1024];
@@ -58,8 +57,7 @@ mu_gdb_bt ()
abort ();
}
- for (i = mu_getmaxfd (); i >= 0; i--)
- close (i);
+ mu_close_fds (0);
fd = open (fname, O_WRONLY|O_CREAT, 0600);
if (fd == -1)
diff --git a/libmailutils/server/acl.c b/libmailutils/server/acl.c
index f209d6929..5f71d2bce 100644
--- a/libmailutils/server/acl.c
+++ b/libmailutils/server/acl.c
@@ -410,7 +410,6 @@ spawn_prog (const char *cmdline, int *pstatus, struct run_closure *rp)
pid = fork ();
if (pid == 0)
{
- int i;
struct mu_wordsplit ws;
if (mu_wordsplit (s, &ws, MU_WRDSF_DEFFLAGS))
@@ -419,9 +418,9 @@ spawn_prog (const char *cmdline, int *pstatus, struct run_closure *rp)
mu_wordsplit_strerror (&ws));
_exit (127);
}
-
- for (i = mu_getmaxfd (); i > 2; i--)
- close (i);
+
+ mu_close_fds (3);
+
execvp (ws.ws_wordv[0], ws.ws_wordv);
_exit (127);
}
diff --git a/libmailutils/server/msrv.c b/libmailutils/server/msrv.c
index 088f4d8b3..6203ae71c 100644
--- a/libmailutils/server/msrv.c
+++ b/libmailutils/server/msrv.c
@@ -522,9 +522,9 @@ mu_m_server_begin (mu_m_server_t msrv)
{
/* Become a daemon. Take care to close inherited fds and to hold
first three one, in, out, err */
- if (daemon (0, 0) < 0)
+ if ((rc = mu_daemon ()) != 0)
{
- mu_error (_("failed to become a daemon: %s"), mu_strerror (errno));
+ mu_error (_("failed to become a daemon: %s"), mu_strerror (rc));
exit (EXIT_FAILURE);
}
mu_onexit_reset ();
diff --git a/libmailutils/stream/prog_stream.c b/libmailutils/stream/prog_stream.c
index 08a19af2a..61ccfd39a 100644
--- a/libmailutils/stream/prog_stream.c
+++ b/libmailutils/stream/prog_stream.c
@@ -252,8 +252,7 @@ start_program_filter (int *p, struct _mu_prog_stream *fs, int flags)
}
/* Close unneded descripitors */
- for (i = mu_getmaxfd (); i > 2; i--)
- close (i);
+ mu_close_fds (3);
/*FIXME: Switch to other uid/gid if desired */
execvp (fs->progname, fs->argv);
diff --git a/mda/lib/util.c b/mda/lib/util.c
index 5e7fff4ab..ac20a405c 100644
--- a/mda/lib/util.c
+++ b/mda/lib/util.c
@@ -18,15 +18,6 @@
int exit_code;
-void
-mda_close_fds (void)
-{
- int i;
- long fdlimit = sysconf (_SC_OPEN_MAX);
- for (i = 3; i < fdlimit; i++)
- close (i);
-}
-
int
mda_switch_user_id (struct mu_auth_data *auth, int user)
{
diff --git a/mh/mh_whatnow.c b/mh/mh_whatnow.c
index 09ed83031..547980fe9 100644
--- a/mh/mh_whatnow.c
+++ b/mh/mh_whatnow.c
@@ -688,7 +688,6 @@ mh_whatnowproc (struct mh_whatnow_env *wh, int initial_edit, const char *prog)
if (pid == 0)
{
struct mu_wordsplit ws;
- int i;
if (mu_wordsplit (prog, &ws,
MU_WRDSF_DEFFLAGS & ~MU_WRDSF_CESCAPES))
@@ -700,8 +699,7 @@ mh_whatnowproc (struct mh_whatnow_env *wh, int initial_edit, const char *prog)
set_default_editor (wh);
mh_whatnow_env_to_environ (wh);
- for (i = mu_getmaxfd (); i > 2; i--)
- close (i);
+ mu_close_fds (3);
execvp (ws.ws_wordv[0], ws.ws_wordv);
mu_diag_funcall (MU_DIAG_ERROR, "execvp", prog, errno);
_exit (127);
diff --git a/mh/send.c b/mh/send.c
index f9837c3d1..9a00951c8 100644
--- a/mh/send.c
+++ b/