chiark / gitweb /
service: optionally call into PAM when dropping priviliges
authorLennart Poettering <lennart@poettering.net>
Wed, 16 Jun 2010 19:54:17 +0000 (21:54 +0200)
committerLennart Poettering <lennart@poettering.net>
Wed, 16 Jun 2010 19:54:17 +0000 (21:54 +0200)
17 files changed:
Makefile.am
configure.ac
fixme
src/dbus-execute.h
src/dbus-manager.c
src/execute.c
src/execute.h
src/install.c
src/load-fragment.c
src/log.c
src/main.c
src/manager.c
src/strv.c
src/strv.h
src/systemctl.c
src/util.c
src/util.h

index c78968f..b887d26 100644 (file)
@@ -290,7 +290,8 @@ systemd_LDADD = \
        $(DBUS_LIBS) \
        $(UDEV_LIBS) \
        $(CGROUP_LIBS) \
-       $(LIBWRAP_LIBS)
+       $(LIBWRAP_LIBS) \
+       $(PAM_LIBS)
 
 test_engine_SOURCES = \
        $(COMMON_SOURCES) \
index ae25200..c5c5cc0 100644 (file)
@@ -111,27 +111,61 @@ AC_SUBST(CGROUP_LIBS)
 AC_ARG_ENABLE([tcpwrap],
         AS_HELP_STRING([--disable-tcpwrap],[Disable optional TCP wrappers support]),
                 [case "${enableval}" in
-                        yes) tcpwrap=yes ;;
-                        no) tcpwrap=no ;;
+                        yes) have_tcpwrap=yes ;;
+                        no) have_tcpwrap=no ;;
                         *) AC_MSG_ERROR(bad value ${enableval} for --disable-tcpwrap) ;;
                 esac],
-                [tcpwrap=auto])
+                [have_tcpwrap=auto])
 
-if test "x${tcpwrap}" != xno ; then
+if test "x${have_tcpwrap}" != xno ; then
         ACX_LIBWRAP
         if test "x${LIBWRAP_LIBS}" = x ; then
-                if test "x$tcpwrap" = xyes ; then
-                        AC_MSG_ERROR([*** TCP wrappers support not found])
+                if test "x$have_tcpwrap" = xyes ; then
+                        AC_MSG_ERROR([*** TCP wrappers support not found.])
                 fi
         else
-                tcpwrap=yes
+                have_tcpwrap=yes
         fi
 else
-    LIBWRAP_LIBS=
+        LIBWRAP_LIBS=
 fi
-
 AC_SUBST(LIBWRAP_LIBS)
 
+AC_ARG_ENABLE([pam],
+        AS_HELP_STRING([--disable-pam],[Disable optional PAM support]),
+                [case "${enableval}" in
+                        yes) have_pam=yes ;;
+                        no) have_pam=no ;;
+                        *) AC_MSG_ERROR(bad value ${enableval} for --disable-pam) ;;
+                esac],
+                [have_pam=auto])
+
+if test "x${have_pam}" != xno ; then
+        AC_CHECK_HEADERS(
+                [security/pam_modules.h security/pam_modutil.h security/pam_ext.h],
+                [have_pam=yes],
+                [if test "x$have_pam" = xyes ; then
+                        AC_MSG_ERROR([*** PAM headers not found.])
+                fi])
+
+        AC_CHECK_LIB(
+                [pam],
+                [pam_syslog],
+                [have_pam=yes],
+                [if test "x$have_pam" = xyes ; then
+                        AC_MSG_ERROR([*** libpam not found.])
+                fi])
+
+        if test "x$have_pam" = xyes ; then
+                PAM_LIBS="-lpam -lpam_misc"
+                AC_DEFINE(HAVE_PAM, 1, [PAM available])
+        fi
+else
+        PAM_LIBS=
+fi
+AC_SUBST(PAM_LIBS)
+AM_CONDITIONAL([HAVE_PAM], [test "x$have_pam" != xno])
+
 have_gtk=no
 AC_ARG_ENABLE(gtk, AS_HELP_STRING([--disable-gtk], [disable GTK tools]))
 if test "x$enable_gtk" != "xno"; then
@@ -336,7 +370,8 @@ echo "
         Syslog service:          ${SPECIAL_SYSLOG_SERVICE}
         D-Bus service:           ${SPECIAL_DBUS_SERVICE}
         Gtk:                     ${have_gtk}
-        tcpwrap:                 ${tcpwrap}
+        tcpwrap:                 ${have_tcpwrap}
+        PAM:                     ${have_pam}
         prefix:                  ${prefix}
         root dir:                ${with_rootdir}
         udev rules dir:          ${with_udevrulesdir}
diff --git a/fixme b/fixme
index 17a8ef8..7a443f4 100644 (file)
--- a/fixme
+++ b/fixme
 
 * write utmp record a la upstart for processes
 
-* run PAM session stuff
+* follow property change dbus spec
 
-* use setproctitle() when forking, before exec() (waiting for (PR_SET_PROCTITLE_AREA to enter the kernel)
+* pam module
 
-* follow property change dbus spec
+* selinux
 
 Regularly:
 
 * look for close() vs. close_nointr() vs. close_nointr_nofail()
 
 * check for strerror(r) instead of strerror(-r)
+
+* Use PR_SET_PROCTITLE_AREA if it becomes available in the kernel
index 243854f..1e83cac 100644 (file)
@@ -44,7 +44,8 @@
         "  <property name=\"User\" type=\"s\" access=\"read\"/>\n"      \
         "  <property name=\"Group\" type=\"s\" access=\"read\"/>\n"     \
         "  <property name=\"SupplementaryGroups\" type=\"as\" access=\"read\"/>\n" \
-        "  <property name=\"TCPWrapName\" type=\"s\" access=\"read\"/>\n"
+        "  <property name=\"TCPWrapName\" type=\"s\" access=\"read\"/>\n" \
+        "  <property name=\"PAMName\" type=\"s\" access=\"read\"/>\n"
 
 #define BUS_EXEC_CONTEXT_PROPERTIES(interface, context)                 \
         { interface, "Environment",                   bus_property_append_strv,   "as",    (context).environment                   }, \
@@ -73,7 +74,8 @@
         { interface, "User",                          bus_property_append_string, "s",     (context).user                          }, \
         { interface, "Group",                         bus_property_append_string, "s",     (context).group                         }, \
         { interface, "SupplementaryGroups",           bus_property_append_strv,   "as",    (context).supplementary_groups          }, \
-        { interface, "TCPWrapName",                   bus_property_append_string, "s",     (context).tcpwrap_name                  }
+        { interface, "TCPWrapName",                   bus_property_append_string, "s",     (context).tcpwrap_name                  }, \
+        { interface, "PAMName",                       bus_property_append_string, "s",     (context).pam_name                      }
 
 int bus_execute_append_output(Manager *m, DBusMessageIter *i, const char *property, void *data);
 int bus_execute_append_input(Manager *m, DBusMessageIter *i, const char *property, void *data);
index c80b22b..d4c9e8f 100644 (file)
@@ -626,7 +626,7 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection  *connection
                         return bus_send_error_reply(m, message, NULL, r);
                 }
 
-                e = strv_env_merge(m->environment, l, NULL);
+                e = strv_env_merge(2, m->environment, l);
                 strv_free(l);
 
                 if (!e)
@@ -650,7 +650,7 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection  *connection
                         return bus_send_error_reply(m, message, NULL, r);
                 }
 
-                e = strv_env_delete(m->environment, l, NULL);
+                e = strv_env_delete(m->environment, 1, l);
                 strv_free(l);
 
                 if (!e)
index 1a7871b..ed10ea2 100644 (file)
 #include <sys/mount.h>
 #include <linux/fs.h>
 
+#ifdef HAVE_PAM
+#include <security/pam_appl.h>
+#endif
+
 #include "execute.h"
 #include "strv.h"
 #include "macro.h"
@@ -720,6 +724,164 @@ static int enforce_user(const ExecContext *context, uid_t uid) {
         return 0;
 }
 
+#ifdef HAVE_PAM
+
+static int null_conv(
+                int num_msg,
+                const struct pam_message **msg,
+                struct pam_response **resp,
+                void *appdata_ptr) {
+
+        /* We don't support conversations */
+
+        return PAM_CONV_ERR;
+}
+
+static int setup_pam(
+                const char *name,
+                const char *user,
+                const char *tty,
+                char ***pam_env,
+                int fds[], unsigned n_fds) {
+
+        static const struct pam_conv conv = {
+                .conv = null_conv,
+                .appdata_ptr = NULL
+        };
+
+        pam_handle_t *handle = NULL;
+        sigset_t ss, old_ss;
+        int pam_code = PAM_SUCCESS;
+        char **e = NULL;
+        bool close_session = false;
+        pid_t pam_pid = 0, parent_pid;
+
+        assert(name);
+        assert(user);
+        assert(pam_env);
+
+        /* We set up PAM in the parent process, then fork. The child
+         * will then stay around untill killed via PR_GET_PDEATHSIG or
+         * systemd via the cgroup logic. It will then remove the PAM
+         * session again. The parent process will exec() the actual
+         * daemon. We do things this way to ensure that the main PID
+         * of the daemon is the one we initially fork()ed. */
+
+        if ((pam_code = pam_start(name, user, &conv, &handle)) != PAM_SUCCESS) {
+                handle = NULL;
+                goto fail;
+        }
+
+        if (tty)
+                if ((pam_code = pam_set_item(handle, PAM_TTY, tty)) != PAM_SUCCESS)
+                        goto fail;
+
+        if ((pam_code = pam_acct_mgmt(handle, PAM_SILENT)) != PAM_SUCCESS)
+                goto fail;
+
+        if ((pam_code = pam_open_session(handle, PAM_SILENT)) != PAM_SUCCESS)
+                goto fail;
+
+        close_session = true;
+
+        if ((pam_code = pam_setcred(handle, PAM_ESTABLISH_CRED | PAM_SILENT)) != PAM_SUCCESS)
+                goto fail;
+
+        if ((!(e = pam_getenvlist(handle)))) {
+                pam_code = PAM_BUF_ERR;
+                goto fail;
+        }
+
+        /* Block SIGTERM, so that we know that it won't get lost in
+         * the child */
+        if (sigemptyset(&ss) < 0 ||
+            sigaddset(&ss, SIGTERM) < 0 ||
+            sigprocmask(SIG_BLOCK, &ss, &old_ss) < 0)
+                goto fail;
+
+        parent_pid = getpid();
+
+        if ((pam_pid = fork()) < 0)
+                goto fail;
+
+        if (pam_pid == 0) {
+                int sig;
+                int r = EXIT_PAM;
+
+                /* The child's job is to reset the PAM session on
+                 * termination */
+
+                /* This string must fit in 10 chars (i.e. the length
+                 * of "/sbin/init") */
+                rename_process("sd:pam");
+
+                /* Make sure we don't keep open the passed fds in this
+                child. We assume that otherwise only those fds are
+                open here that have been opened by PAM. */
+                close_many(fds, n_fds);
+
+                /* Wait until our parent died. This will most likely
+                 * not work since the kernel does not allow
+                 * unpriviliged paretns kill their priviliged children
+                 * this way. We rely on the control groups kill logic
+                 * to do the rest for us. */
+                if (prctl(PR_SET_PDEATHSIG, SIGTERM) < 0)
+                        goto child_finish;
+
+                /* Check if our parent process might already have
+                 * died? */
+                if (getppid() == parent_pid) {
+                        if (sigwait(&ss, &sig) < 0)
+                                goto child_finish;
+
+                        assert(sig == SIGTERM);
+                }
+
+                /* Only if our parent died we'll end the session */
+                if (getppid() != parent_pid)
+                        if ((pam_code = pam_close_session(handle, PAM_DATA_SILENT)) != PAM_SUCCESS)
+                                goto child_finish;
+
+                r = 0;
+
+        child_finish:
+                pam_end(handle, pam_code | PAM_DATA_SILENT);
+                _exit(r);
+        }
+
+        /* If the child was forked off successfully it will do all the
+         * cleanups, so forget about the handle here. */
+        handle = NULL;
+
+        /* Unblock SIGSUR1 again in the parent */
+        if (sigprocmask(SIG_SETMASK, &old_ss, NULL) < 0)
+                goto fail;
+
+        /* We close the log explicitly here, since the PAM modules
+         * might have opened it, but we don't want this fd around. */
+        closelog();
+
+        return 0;
+
+fail:
+        if (handle) {
+                if (close_session)
+                        pam_code = pam_close_session(handle, PAM_DATA_SILENT);
+
+                pam_end(handle, pam_code | PAM_DATA_SILENT);
+        }
+
+        strv_free(e);
+
+        closelog();
+
+        if (pam_pid > 1)
+                kill(pam_pid, SIGTERM);
+
+        return EXIT_PAM;
+}
+#endif
+
 int exec_spawn(ExecCommand *command,
                char **argv,
                const ExecContext *context,
@@ -777,13 +939,17 @@ int exec_spawn(ExecCommand *command,
                 const char *username = NULL, *home = NULL;
                 uid_t uid = (uid_t) -1;
                 gid_t gid = (gid_t) -1;
-                char **our_env = NULL, **final_env = NULL;
+                char **our_env = NULL, **pam_env = NULL, **final_env = NULL;
                 unsigned n_env = 0;
                 int saved_stdout = -1, saved_stdin = -1;
                 bool keep_stdout = false, keep_stdin = false;
 
                 /* child */
 
+                /* This string must fit in 10 chars (i.e. the length
+                 * of "/sbin/init") */
+                rename_process("sd:exec");
+
                 /* We reset exactly these signals, since they are the
                  * only ones we set to SIG_IGN in the main daemon. All
                  * others we leave untouched because we set them to
@@ -928,6 +1094,25 @@ int exec_spawn(ExecCommand *command,
                                 }
                 }
 
+#ifdef HAVE_PAM
+                if (context->pam_name && username) {
+                        /* Make sure no fds leak into the PAM
+                         * supervisor process. We will call this later
+                         * on again to make sure that any fds leaked
+                         * by the PAM modules get closed before our
+                         * exec(). */
+                        if (close_all_fds(fds, n_fds) < 0) {
+                                r = EXIT_FDS;
+                                goto fail;
+                        }
+
+                        if (setup_pam(context->pam_name, username, context->tty_path, &pam_env, fds, n_fds) < 0) {
+                                r = EXIT_PAM;
+                                goto fail;
+                        }
+                }
+#endif
+
                 if (apply_permissions)
                         if (enforce_groups(context, username, uid) < 0) {
                                 r = EXIT_GROUP;
@@ -1049,7 +1234,13 @@ int exec_spawn(ExecCommand *command,
 
                 assert(n_env <= 6);
 
-                if (!(final_env = strv_env_merge(environment, our_env, context->environment, NULL))) {
+                if (!(final_env = strv_env_merge(
+                                      4,
+                                      environment,
+                                      our_env,
+                                      context->environment,
+                                      pam_env,
+                                      NULL))) {
                         r = EXIT_MEMORY;
                         goto fail;
                 }
@@ -1060,6 +1251,7 @@ int exec_spawn(ExecCommand *command,
         fail:
                 strv_free(our_env);
                 strv_free(final_env);
+                strv_free(pam_env);
 
                 if (saved_stdin >= 0)
                         close_nointr_nofail(saved_stdin);
@@ -1133,6 +1325,9 @@ void exec_context_done(ExecContext *c) {
         strv_free(c->supplementary_groups);
         c->supplementary_groups = NULL;
 
+        free(c->pam_name);
+        c->pam_name = NULL;
+
         if (c->capabilities) {
                 cap_free(c->capabilities);
                 c->capabilities = NULL;
@@ -1332,6 +1527,9 @@ void exec_context_dump(ExecContext *c, FILE* f, const char *prefix) {
                 fputs("\n", f);
         }
 
+        if (c->pam_name)
+                fprintf(f, "%sPAMName: %s", prefix, c->pam_name);
+
         if (strv_length(c->read_write_dirs) > 0) {
                 fprintf(f, "%sReadWriteDirs:", prefix);
                 strv_fprintf(f, c->read_write_dirs);
@@ -1613,6 +1811,9 @@ const char* exit_status_to_string(ExitStatus status) {
         case EXIT_TCPWRAP:
                 return "TCPWRAP";
 
+        case EXIT_PAM:
+                return "PAM";
+
         default:
                 return NULL;
         }
index 1adf41e..e618049 100644 (file)
@@ -116,6 +116,8 @@ struct ExecContext {
         char *group;
         char **supplementary_groups;
 
+        char *pam_name;
+
         char **read_write_dirs, **read_only_dirs, **inaccessible_dirs;
         unsigned long mount_flags;
 
@@ -182,7 +184,8 @@ typedef enum ExitStatus {
         EXIT_SETSID,   /* 220 */
         EXIT_CONFIRM,
         EXIT_STDERR,
-        EXIT_TCPWRAP
+        EXIT_TCPWRAP,
+        EXIT_PAM
 
 } ExitStatus;
 
index e3db637..c3dbe8b 100644 (file)
@@ -69,7 +69,7 @@ static int help(void) {
                "  enable [NAME...]    Enable one or more units\n"
                "  disable [NAME...]   Disable one or more units\n"
                "  test [NAME...]      Test whether any of the specified units are enabled\n",
-               __progname);
+               program_invocation_short_name);
 
         return 0;
 }
index f409776..1f082d5 100644 (file)
@@ -1392,7 +1392,8 @@ static int load_from_path(Unit *u, const char *path) {
                 { "InaccessibleDirectories",config_parse_path_strv,       &(context).inaccessible_dirs,                    section   }, \
                 { "PrivateTmp",             config_parse_bool,            &(context).private_tmp,                          section   }, \
                 { "MountFlags",             config_parse_mount_flags,     &(context),                                      section   }, \
-                { "TCPWrapName",            config_parse_string,          &(context).tcpwrap_name,                         section   }
+                { "TCPWrapName",            config_parse_string,          &(context).tcpwrap_name,                         section   }, \
+                { "PAMName",                config_parse_string,          &(context).pam_name,                             section   }
 
         const ConfigItem items[] = {
                 { "Names",                  config_parse_names,           u,                                               "Unit"    },
index 265a7f1..b7173eb 100644 (file)
--- a/src/log.c
+++ b/src/log.c
@@ -267,7 +267,7 @@ static int write_to_syslog(
         zero(iovec);
         IOVEC_SET_STRING(iovec[0], header_priority);
         IOVEC_SET_STRING(iovec[1], header_time);
-        IOVEC_SET_STRING(iovec[2], __progname);
+        IOVEC_SET_STRING(iovec[2], program_invocation_short_name);
         IOVEC_SET_STRING(iovec[3], header_pid);
         IOVEC_SET_STRING(iovec[4], buffer);
 
@@ -302,7 +302,7 @@ static int write_to_kmsg(
 
         zero(iovec);
         IOVEC_SET_STRING(iovec[0], header_priority);
-        IOVEC_SET_STRING(iovec[1], __progname);
+        IOVEC_SET_STRING(iovec[1], program_invocation_short_name);
         IOVEC_SET_STRING(iovec[2], header_pid);
         IOVEC_SET_STRING(iovec[3], buffer);
         IOVEC_SET_STRING(iovec[4], "\n");
index 9cdbf2e..e5bdf84 100644 (file)
@@ -504,7 +504,7 @@ static int help(void) {
                "     --dump-configuration-items  Dump understood unit configuration items\n"
                "     --confirm-spawn             Ask for confirmation when spawning processes\n"
                "     --introspect[=INTERFACE]    Extract D-Bus interface data\n",
-               __progname);
+               program_invocation_short_name);
 
         return 0;
 }
index 97d05b5..385b371 100644 (file)
@@ -109,7 +109,7 @@ static int manager_setup_notify(Manager *m) {
                 return -ENOMEM;
 
         ne[1] = NULL;
-        t = strv_env_merge(m->environment, ne, NULL);
+        t = strv_env_merge(2, m->environment, ne);
         free(ne[0]);
 
         if (!t)
index 2ebd0ee..85599fe 100644 (file)
@@ -374,7 +374,9 @@ char **strv_remove(char **l, const char *s) {
 static int env_append(char **r, char ***k, char **a) {
         assert(r);
         assert(k);
-        assert(a);
+
+        if (!a)
+                return 0;
 
         /* Add the entries of a to *k unless they already exist in *r
          * in which case they are overriden instead. This assumes
@@ -400,38 +402,33 @@ static int env_append(char **r, char ***k, char **a) {
         return 0;
 }
 
-char **strv_env_merge(char **x, ...) {
+char **strv_env_merge(unsigned n_lists, ...) {
         size_t n = 0;
         char **l, **k, **r;
         va_list ap;
+        unsigned i;
 
         /* Merges an arbitrary number of environment sets */
 
-        if (x) {
-                n += strv_length(x);
-
-                va_start(ap, x);
-                while ((l = va_arg(ap, char**)))
-                        n += strv_length(l);
-                va_end(ap);
+        va_start(ap, n_lists);
+        for (i = 0; i < n_lists; i++) {
+                l = va_arg(ap, char**);
+                n += strv_length(l);
         }
-
+        va_end(ap);
 
         if (!(r = new(char*, n+1)))
                 return NULL;
 
         k = r;
 
-        if (x) {
-                if (env_append(r, &k, x) < 0)
+        va_start(ap, n_lists);
+        for (i = 0; i < n_lists; i++) {
+                l = va_arg(ap, char**);
+                if (env_append(r, &k, l) < 0)
                         goto fail;
-
-                va_start(ap, x);
-                while ((l = va_arg(ap, char**)))
-                        if (env_append(r, &k, l) < 0)
-                                goto fail;
-                va_end(ap);
         }
+        va_end(ap);
 
         *k = NULL;
 
@@ -472,7 +469,7 @@ static bool env_match(const char *t, const char *pattern) {
         return false;
 }
 
-char **strv_env_delete(char **x, ...) {
+char **strv_env_delete(char **x, unsigned n_lists, ...) {
         size_t n = 0, i = 0;
         char **l, **k, **r, **j;
         va_list ap;
@@ -486,12 +483,14 @@ char **strv_env_delete(char **x, ...) {
                 return NULL;
 
         STRV_FOREACH(k, x) {
-                va_start(ap, x);
+                va_start(ap, n_lists);
 
-                while ((l = va_arg(ap, char**)))
+                for (i = 0; i < n_lists; i++) {
+                        l = va_arg(ap, char**);
                         STRV_FOREACH(j, l)
                                 if (env_match(*k, *j))
                                         goto delete;
+                }
 
                 va_end(ap);
 
index 2d24b4e..af13983 100644 (file)
@@ -55,8 +55,8 @@ char **strv_split_quoted(const char *s) _malloc_;
 
 char *strv_join(char **l, const char *separator) _malloc_;
 
-char **strv_env_merge(char **x, ...) _sentinel_;
-char **strv_env_delete(char **x, ...) _sentinel_;
+char **strv_env_merge(unsigned n_lists, ...);
+char **strv_env_delete(char **x, unsigned n_lists, ...);
 
 #define STRV_FOREACH(s, l)                      \
         for ((s) = (l); (s) && *(s); (s)++)
index 663cb83..35ca082 100644 (file)
@@ -1322,7 +1322,7 @@ static int help(void) {
                "  show-environment                Dump environment\n"
                "  set-environment [NAME=VALUE...] Set one or more environment variables\n"
                "  unset-environment [NAME...]     Unset one or more environment variables\n",
-               __progname);
+               program_invocation_short_name);
 
         return 0;
 }
index 7140903..ed0991a 100644 (file)
@@ -43,6 +43,7 @@
 #include <sys/poll.h>
 #include <libgen.h>
 #include <ctype.h>
+#include <sys/prctl.h>
 
 #include "macro.h"
 #include "util.h"
@@ -222,6 +223,13 @@ void close_nointr_nofail(int fd) {
         errno = saved_errno;
 }
 
+void close_many(const int fds[], unsigned n_fd) {
+        unsigned i;
+
+        for (i = 0; i < n_fd; i++)
+                close_nointr_nofail(fds[i]);
+}
+
 int parse_boolean(const char *v) {
         assert(v);
 
@@ -2161,6 +2169,19 @@ fallback:
         return random() * RAND_MAX + random();
 }
 
+void rename_process(const char name[8]) {
+        assert(name);
+
+        prctl(PR_SET_NAME, name);
+
+        /* This is a like a poor man's setproctitle(). The string
+         * passed should fit in 7 chars (i.e. the length of
+         * "systemd") */
+
+        if (program_invocation_name)
+                strncpy(program_invocation_name, name, strlen(program_invocation_name));
+}
+
 static const char *const ioprio_class_table[] = {
         [IOPRIO_CLASS_NONE] = "none",
         [IOPRIO_CLASS_RT] = "realtime",
index 0b397ac..1e5ee28 100644 (file)
@@ -104,6 +104,7 @@ bool first_word(const char *s, const char *word);
 
 int close_nointr(int fd);
 void close_nointr_nofail(int fd);
+void close_many(const int fds[], unsigned n_fd);
 
 int parse_boolean(const char *v);
 int parse_usec(const char *t, usec_t *usec);
@@ -252,7 +253,7 @@ bool is_device_path(const char *path);
 
 int dir_is_empty(const char *path);
 
-extern char * __progname;
+void rename_process(const char name[8]);
 
 const char *ioprio_class_to_string(int i);
 int ioprio_class_from_string(const char *s);