mirror of
https://github.com/systemd/systemd.git
synced 2025-01-10 05:18:17 +03:00
service: optionally call into PAM when dropping priviliges
This commit is contained in:
parent
04aa0cb9c4
commit
5b6319dcee
@ -290,7 +290,8 @@ systemd_LDADD = \
|
||||
$(DBUS_LIBS) \
|
||||
$(UDEV_LIBS) \
|
||||
$(CGROUP_LIBS) \
|
||||
$(LIBWRAP_LIBS)
|
||||
$(LIBWRAP_LIBS) \
|
||||
$(PAM_LIBS)
|
||||
|
||||
test_engine_SOURCES = \
|
||||
$(COMMON_SOURCES) \
|
||||
|
55
configure.ac
55
configure.ac
@ -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}
|
||||
|
10
fixme
10
fixme
@ -51,14 +51,16 @@
|
||||
|
||||
* write utmp record a la upstart for processes
|
||||
|
||||
* run PAM session stuff
|
||||
|
||||
* use setproctitle() when forking, before exec() (waiting for (PR_SET_PROCTITLE_AREA to enter the kernel)
|
||||
|
||||
* follow property change dbus spec
|
||||
|
||||
* pam module
|
||||
|
||||
* 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
|
||||
|
@ -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);
|
||||
|
@ -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)
|
||||
|
205
src/execute.c
205
src/execute.c
@ -37,6 +37,10 @@
|
||||
#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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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" },
|
||||
|
@ -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");
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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)
|
||||
|
41
src/strv.c
41
src/strv.c
@ -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);
|
||||
|
||||
|
@ -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)++)
|
||||
|
@ -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;
|
||||
}
|
||||
|
21
src/util.c
21
src/util.c
@ -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",
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user