int get_user_creds_clean( const char **username, uid_t *uid, gid_t *gid, const char **home, const char **shell) { int r; /* Like get_user_creds(), but resets home/shell to NULL if they don't contain anything relevant. */ r = get_user_creds(username, uid, gid, home, shell); if (r < 0) return r; if (shell && (isempty(*shell) || PATH_IN_SET(*shell, "/bin/nologin", "/sbin/nologin", "/usr/bin/nologin", "/usr/sbin/nologin"))) *shell = NULL; if (home && (isempty(*home) || path_equal(*home, "/"))) *home = NULL; return 0; }
static char *specifier_user_name(char specifier, void *data, void *userdata) { InstallInfo *i = userdata; const char *username; _cleanup_free_ char *tmp = NULL; char *printed = NULL; assert(i); if (i->user) username = i->user; else /* get USER env from env or our own uid */ username = tmp = getusername_malloc(); switch (specifier) { case 'u': printed = strdup(username); break; case 'U': { /* fish username from passwd */ uid_t uid; int r; r = get_user_creds(&username, &uid, NULL, NULL, NULL); if (r < 0) return NULL; if (asprintf(&printed, "%d", uid) < 0) return NULL; break; }} return printed; }
static char *specifier_user_home(char specifier, void *data, void *userdata) { Unit *u = userdata; ExecContext *c; int r; const char *username, *home; assert(u); c = unit_get_exec_context(u); /* return HOME if set, otherwise from passwd */ if (!c || !c->user) { char *h; r = get_home_dir(&h); if (r < 0) return NULL; return h; } username = c->user; r = get_user_creds(&username, NULL, NULL, &home, NULL); if (r < 0) return NULL; return strdup(home); }
static int netdev_tuntap_add(NetDev *netdev, struct ifreq *ifr) { _cleanup_close_ int fd; TunTap *t = NULL; const char *user; const char *group; uid_t uid; gid_t gid; int r; assert(netdev); assert(ifr); fd = open(TUN_DEV, O_RDWR); if (fd < 0) return log_netdev_error_errno(netdev, -errno, "Failed to open tun dev: %m"); r = ioctl(fd, TUNSETIFF, ifr); if (r < 0) return log_netdev_error_errno(netdev, -errno, "TUNSETIFF failed on tun dev: %m"); if (netdev->kind == NETDEV_KIND_TAP) t = TAP(netdev); else t = TUN(netdev); assert(t); if(t->user_name) { user = t->user_name; r = get_user_creds(&user, &uid, NULL, NULL, NULL); if (r < 0) return log_netdev_error_errno(netdev, r, "Cannot resolve user name %s: %m", t->user_name); r = ioctl(fd, TUNSETOWNER, uid); if (r < 0) return log_netdev_error_errno(netdev, -errno, "TUNSETOWNER failed on tun dev: %m"); } if (t->group_name) { group = t->group_name; r = get_group_creds(&group, &gid); if (r < 0) return log_netdev_error_errno(netdev, r, "Cannot resolve group name %s: %m", t->group_name); r = ioctl(fd, TUNSETGROUP, gid); if (r < 0) return log_netdev_error_errno(netdev, -errno, "TUNSETGROUP failed on tun dev: %m"); } r = ioctl(fd, TUNSETPERSIST, 1); if (r < 0) return log_netdev_error_errno(netdev, -errno, "TUNSETPERSIST failed on tun dev: %m"); return 0; }
static int condition_test_user(Condition *c) { uid_t id; int r; _cleanup_free_ char *username = NULL; const char *u; assert(c); assert(c->parameter); assert(c->type == CONDITION_USER); r = parse_uid(c->parameter, &id); if (r >= 0) return id == getuid() || id == geteuid(); if (streq("@system", c->parameter)) return uid_is_system(getuid()) || uid_is_system(geteuid()); username = getusername_malloc(); if (!username) return -ENOMEM; if (streq(username, c->parameter)) return 1; if (getpid_cached() == 1) return streq(c->parameter, "root"); u = c->parameter; r = get_user_creds(&u, &id, NULL, NULL, NULL); if (r < 0) return 0; return id == getuid() || id == geteuid(); }
static char *specifier_user_shell(char specifier, void *data, void *userdata) { Unit *u = userdata; ExecContext *c; int r; const char *username, *shell; assert(u); c = unit_get_exec_context(u); /* return HOME if set, otherwise from passwd */ if (!c || !c->user) { char *sh; r = get_shell(&sh); if (r < 0) return strdup("/bin/sh"); return sh; } username = c->user; r = get_user_creds(&username, NULL, NULL, NULL, &shell); if (r < 0) return strdup("/bin/sh"); return strdup(shell); }
int main(int argc, char *argv[]) { int r, accept_fd; uid_t uid, bus_uid; gid_t gid; log_set_target(LOG_TARGET_JOURNAL_OR_KMSG); log_parse_environment(); log_open(); bus_uid = getuid(); if (geteuid() == 0) { const char *user = "******"; r = get_user_creds(&user, &uid, &gid, NULL, NULL); if (r < 0) { log_error_errno(r, "Cannot resolve user name %s: %m", user); goto finish; } r = drop_privileges(uid, gid, 1ULL << CAP_IPC_OWNER); if (r < 0) { log_error_errno(r, "Cannot drop privileges: %m"); goto finish; } } r = parse_argv(argc, argv); if (r <= 0) goto finish; r = sd_listen_fds(0); if (r != 1) { log_error("Illegal number of file descriptors passed"); goto finish; } accept_fd = SD_LISTEN_FDS_START; r = fd_nonblock(accept_fd, false); if (r < 0) { log_error_errno(r, "Cannot mark accept-fd non-blocking: %m"); goto finish; } r = loop_clients(accept_fd, bus_uid); finish: sd_notify(false, "STOPPING=1\n" "STATUS=Shutting down."); strv_free(arg_configuration); free(arg_address); return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; }
static void test_add_acls_for_user(void) { char fn[] = "/tmp/test-empty.XXXXXX"; _cleanup_close_ int fd = -1; char *cmd; uid_t uid; int r; fd = mkostemp_safe(fn, O_RDWR|O_CLOEXEC); assert_se(fd >= 0); /* Use the mode that user journal files use */ assert_se(fchmod(fd, 0640) == 0); cmd = strjoina("ls -l ", fn); assert_se(system(cmd) == 0); cmd = strjoina("getfacl -p ", fn); assert_se(system(cmd) == 0); if (getuid() == 0) { const char *nobody = "nobody"; r = get_user_creds(&nobody, &uid, NULL, NULL, NULL); if (r < 0) uid = 0; } else uid = getuid(); r = add_acls_for_user(fd, uid); assert_se(r >= 0); cmd = strjoina("ls -l ", fn); assert_se(system(cmd) == 0); cmd = strjoina("getfacl -p ", fn); assert_se(system(cmd) == 0); /* set the acls again */ r = add_acls_for_user(fd, uid); assert_se(r >= 0); cmd = strjoina("ls -l ", fn); assert_se(system(cmd) == 0); cmd = strjoina("getfacl -p ", fn); assert_se(system(cmd) == 0); unlink(fn); }
int main(int argc, char *argv[]) { uid_t uid; int r; const char* name = argv[1] ?: "nfsnobody"; r = get_user_creds(&name, &uid, NULL, NULL, NULL); if (r < 0) { log_full_errno(r == -ESRCH ? LOG_NOTICE : LOG_ERR, r, "Failed to resolve \"%s\": %m", name); return r == -ESRCH ? EXIT_TEST_SKIP : EXIT_FAILURE; } r = clean_ipc_by_uid(uid); return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; }
static char *specifier_user_shell(char specifier, void *data, void *userdata) { Unit *u = userdata; ExecContext *c; int r; const char *username, *shell; char *ret; assert(u); c = unit_get_exec_context(u); if (c && c->user) username = c->user; else username = "******"; /* return /bin/sh for root, otherwise the value from passwd */ r = get_user_creds(&username, NULL, NULL, NULL, &shell); if (r < 0) { log_warning_unit(u->id, "Failed to determine shell: %s", strerror(-r)); return NULL; } if (!path_is_absolute(shell)) { log_warning_unit(u->id, "Shell %s is not absolute, ignoring.", shell); } ret = strdup(shell); if (!ret) log_oom(); return ret; }
static char *specifier_user_name(char specifier, void *data, void *userdata) { Unit *u = userdata; ExecContext *c; int r; const char *username; _cleanup_free_ char *tmp = NULL; uid_t uid; char *printed = NULL; assert(u); c = unit_get_exec_context(u); if (c && c->user) username = c->user; else /* get USER env from env or our own uid */ username = tmp = getusername_malloc(); /* fish username from passwd */ r = get_user_creds(&username, &uid, NULL, NULL, NULL); if (r < 0) return NULL; switch (specifier) { case 'U': if (asprintf(&printed, "%d", uid) < 0) return NULL; break; case 'u': printed = strdup(username); break; } return printed; }
static void test_get_user_creds_one(const char *id, const char *name, uid_t uid, gid_t gid, const char *home, const char *shell) { const char *rhome = NULL; const char *rshell = NULL; uid_t ruid = UID_INVALID; gid_t rgid = GID_INVALID; int r; log_info("/* %s(\"%s\", \"%s\", "UID_FMT", "GID_FMT", \"%s\", \"%s\") */", __func__, id, name, uid, gid, home, shell); r = get_user_creds(&id, &ruid, &rgid, &rhome, &rshell, 0); log_info_errno(r, "got \"%s\", "UID_FMT", "GID_FMT", \"%s\", \"%s\": %m", id, ruid, rgid, strnull(rhome), strnull(rshell)); if (!synthesize_nobody() && streq(name, NOBODY_USER_NAME)) { log_info("(skipping detailed tests because nobody is not synthesized)"); return; } assert_se(r == 0); assert_se(streq_ptr(id, name)); assert_se(ruid == uid); assert_se(rgid == gid); assert_se(path_equal(rhome, home)); assert_se(path_equal(rshell, shell)); }
int main(int argc, char *argv[]) { _cleanup_manager_free_ Manager *m = NULL; const char *user = "******"; uid_t uid; gid_t gid; int r; log_set_target(LOG_TARGET_AUTO); log_parse_environment(); log_open(); umask(0022); if (argc != 1) { log_error("This program takes no arguments."); r = -EINVAL; goto out; } r = get_user_creds(&user, &uid, &gid, NULL, NULL); if (r < 0) { log_error("Cannot resolve user name %s: %s", user, strerror(-r)); goto out; } /* Always create the directories people can create inotify * watches in. */ r = mkdir_safe_label("/run/systemd/netif", 0755, uid, gid); if (r < 0) log_error("Could not create runtime directory: %s", strerror(-r)); r = mkdir_safe_label("/run/systemd/netif/links", 0755, uid, gid); if (r < 0) log_error("Could not create runtime directory 'links': %s", strerror(-r)); r = mkdir_safe_label("/run/systemd/netif/leases", 0755, uid, gid); if (r < 0) log_error("Could not create runtime directory 'leases': %s", strerror(-r)); r = drop_privileges(uid, gid, (1ULL << CAP_NET_ADMIN) | (1ULL << CAP_NET_BIND_SERVICE) | (1ULL << CAP_NET_BROADCAST) | (1ULL << CAP_NET_RAW)); if (r < 0) goto out; assert_se(sigprocmask_many(SIG_BLOCK, SIGTERM, SIGINT, -1) == 0); r = manager_new(&m); if (r < 0) { log_error("Could not create manager: %s", strerror(-r)); goto out; } r = manager_udev_listen(m); if (r < 0) { log_error("Could not connect to udev: %s", strerror(-r)); goto out; } r = manager_rtnl_listen(m); if (r < 0) { log_error("Could not connect to rtnl: %s", strerror(-r)); goto out; } r = manager_bus_listen(m); if (r < 0) { log_error("Could not connect to system bus: %s", strerror(-r)); goto out; } r = manager_load_config(m); if (r < 0) { log_error("Could not load configuration files: %s", strerror(-r)); goto out; } r = manager_rtnl_enumerate_links(m); if (r < 0) { log_error("Could not enumerate links: %s", strerror(-r)); goto out; } sd_notify(false, "READY=1\n" "STATUS=Processing requests..."); r = sd_event_loop(m->event); if (r < 0) { log_error("Event loop failed: %s", strerror(-r)); goto out; } out: sd_notify(false, "STOPPING=1\n" "STATUS=Shutting down..."); return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; }
static int parse_line(const char *fname, unsigned line, const char *buffer) { Item *i, *existing; char *mode = NULL, *user = NULL, *group = NULL, *age = NULL; char type; Hashmap *h; int r, n = -1; assert(fname); assert(line >= 1); assert(buffer); i = new0(Item, 1); if (!i) { log_error("Out of memory"); return -ENOMEM; } if (sscanf(buffer, "%c " "%ms " "%ms " "%ms " "%ms " "%ms " "%n", &type, &i->path, &mode, &user, &group, &age, &n) < 2) { log_error("[%s:%u] Syntax error.", fname, line); r = -EIO; goto finish; } if (n >= 0) { n += strspn(buffer+n, WHITESPACE); if (buffer[n] != 0 && (buffer[n] != '-' || buffer[n+1] != 0)) { i->argument = unquote(buffer+n, "\""); if (!i->argument) { log_error("Out of memory"); return -ENOMEM; } } } switch(type) { case CREATE_FILE: case TRUNCATE_FILE: case CREATE_DIRECTORY: case TRUNCATE_DIRECTORY: case CREATE_FIFO: case IGNORE_PATH: case REMOVE_PATH: case RECURSIVE_REMOVE_PATH: case RELABEL_PATH: case RECURSIVE_RELABEL_PATH: break; case CREATE_SYMLINK: if (!i->argument) { log_error("[%s:%u] Symlink file requires argument.", fname, line); r = -EBADMSG; goto finish; } break; case WRITE_FILE: if (!i->argument) { log_error("[%s:%u] Write file requires argument.", fname, line); r = -EBADMSG; goto finish; } break; case CREATE_CHAR_DEVICE: case CREATE_BLOCK_DEVICE: { unsigned major, minor; if (!i->argument) { log_error("[%s:%u] Device file requires argument.", fname, line); r = -EBADMSG; goto finish; } if (sscanf(i->argument, "%u:%u", &major, &minor) != 2) { log_error("[%s:%u] Can't parse device file major/minor '%s'.", fname, line, i->argument); r = -EBADMSG; goto finish; } i->major_minor = makedev(major, minor); break; } default: log_error("[%s:%u] Unknown file type '%c'.", fname, line, type); r = -EBADMSG; goto finish; } i->type = type; if (!path_is_absolute(i->path)) { log_error("[%s:%u] Path '%s' not absolute.", fname, line, i->path); r = -EBADMSG; goto finish; } path_kill_slashes(i->path); if (arg_prefix && !path_startswith(i->path, arg_prefix)) { r = 0; goto finish; } if (user && !streq(user, "-")) { const char *u = user; r = get_user_creds(&u, &i->uid, NULL, NULL, NULL); if (r < 0) { log_error("[%s:%u] Unknown user '%s'.", fname, line, user); goto finish; } i->uid_set = true; } if (group && !streq(group, "-")) { const char *g = group; r = get_group_creds(&g, &i->gid); if (r < 0) { log_error("[%s:%u] Unknown group '%s'.", fname, line, group); goto finish; } i->gid_set = true; } if (mode && !streq(mode, "-")) { unsigned m; if (sscanf(mode, "%o", &m) != 1) { log_error("[%s:%u] Invalid mode '%s'.", fname, line, mode); r = -ENOENT; goto finish; } i->mode = m; i->mode_set = true; } else i->mode = i->type == CREATE_DIRECTORY || i->type == TRUNCATE_DIRECTORY ? 0755 : 0644; if (age && !streq(age, "-")) { const char *a = age; if (*a == '~') { i->keep_first_level = true; a++; } if (parse_usec(a, &i->age) < 0) { log_error("[%s:%u] Invalid age '%s'.", fname, line, age); r = -EBADMSG; goto finish; } i->age_set = true; } h = needs_glob(i->type) ? globs : items; existing = hashmap_get(h, i->path); if (existing) { /* Two identical items are fine */ if (!item_equal(existing, i)) log_warning("Two or more conflicting lines for %s configured, ignoring.", i->path); r = 0; goto finish; } r = hashmap_put(h, i->path, i); if (r < 0) { log_error("Failed to insert item %s: %s", i->path, strerror(-r)); goto finish; } i = NULL; r = 0; finish: free(user); free(group); free(mode); free(age); if (i) item_free(i); return r; }
int main(int argc, char *argv[]) { _cleanup_(manager_freep) Manager *m = NULL; const char *user = "******"; uid_t uid; gid_t gid; int r; log_set_target(LOG_TARGET_AUTO); log_parse_environment(); log_open(); if (argc != 1) { log_error("This program takes no arguments."); r = -EINVAL; goto finish; } umask(0022); r = mac_selinux_init(); if (r < 0) { log_error_errno(r, "SELinux setup failed: %m"); goto finish; } r = get_user_creds(&user, &uid, &gid, NULL, NULL); if (r < 0) { log_error_errno(r, "Cannot resolve user name %s: %m", user); goto finish; } /* Always create the directory where resolv.conf will live */ r = mkdir_safe_label("/run/systemd/resolve", 0755, uid, gid, false); if (r < 0) { log_error_errno(r, "Could not create runtime directory: %m"); goto finish; } /* Drop privileges, but only if we have been started as root. If we are not running as root we assume all * privileges are already dropped. */ if (getuid() == 0) { /* Drop privileges, but keep three caps. Note that we drop those too, later on (see below) */ r = drop_privileges(uid, gid, (UINT64_C(1) << CAP_NET_RAW)| /* needed for SO_BINDTODEVICE */ (UINT64_C(1) << CAP_NET_BIND_SERVICE)| /* needed to bind on port 53 */ (UINT64_C(1) << CAP_SETPCAP) /* needed in order to drop the caps later */); if (r < 0) goto finish; } assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGTERM, SIGINT, SIGUSR1, SIGUSR2, SIGRTMIN+1, -1) >= 0); r = manager_new(&m); if (r < 0) { log_error_errno(r, "Could not create manager: %m"); goto finish; } r = manager_start(m); if (r < 0) { log_error_errno(r, "Failed to start manager: %m"); goto finish; } /* Write finish default resolv.conf to avoid a dangling symlink */ (void) manager_write_resolv_conf(m); /* Let's drop the remaining caps now */ r = capability_bounding_set_drop(0, true); if (r < 0) { log_error_errno(r, "Failed to drop remaining caps: %m"); goto finish; } sd_notify(false, "READY=1\n" "STATUS=Processing requests..."); r = sd_event_loop(m->event); if (r < 0) { log_error_errno(r, "Event loop failed: %m"); goto finish; } sd_event_get_exit_code(m->event, &r); finish: sd_notify(false, "STOPPING=1\n" "STATUS=Shutting down..."); return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; }
static int netdev_tuntap_add(NetDev *netdev, struct ifreq *ifr) { _cleanup_close_ int fd; const char *user; const char *group; uid_t uid; gid_t gid; int r = 0; fd = open(TUN_DEV, O_RDWR); if (fd < 0) { log_error_netdev(netdev, "Failed to open tun dev: %s", strerror(-r)); return r; } r = ioctl(fd, TUNSETIFF, ifr); if (r < 0) { log_error_netdev(netdev, "TUNSETIFF failed on tun dev: %s", strerror(-r)); return r; } if(netdev->user_name) { user = netdev->user_name; r = get_user_creds(&user, &uid, NULL, NULL, NULL); if (r < 0) { log_error("Cannot resolve user name %s: %s", netdev->user_name, strerror(-r)); return 0; } r = ioctl(fd, TUNSETOWNER, uid); if ( r < 0) { log_error_netdev(netdev, "TUNSETOWNER failed on tun dev: %s", strerror(-r)); } } if(netdev->group_name) { group = netdev->group_name; r = get_group_creds(&group, &gid); if (r < 0) { log_error("Cannot resolve group name %s: %s", netdev->group_name, strerror(-r)); return 0; } r = ioctl(fd, TUNSETGROUP, gid); if( r < 0) { log_error_netdev(netdev, "TUNSETGROUP failed on tun dev: %s", strerror(-r)); return r; } } r = ioctl(fd, TUNSETPERSIST, 1); if (r < 0) { log_error_netdev(netdev, "TUNSETPERSIST failed on tun dev: %s", strerror(-r)); return r; } return r; }
int main(int argc, char *argv[]) { _cleanup_(manager_freep) Manager *m = NULL; const char *user = "******"; uid_t uid; gid_t gid; int r; log_set_target(LOG_TARGET_AUTO); log_parse_environment(); log_open(); if (argc != 1) { log_error("This program takes no arguments."); r = -EINVAL; goto finish; } umask(0022); r = mac_selinux_init(NULL); if (r < 0) { log_error_errno(r, "SELinux setup failed: %m"); goto finish; } r = get_user_creds(&user, &uid, &gid, NULL, NULL); if (r < 0) { log_error_errno(r, "Cannot resolve user name %s: %m", user); goto finish; } /* Always create the directory where resolv.conf will live */ r = mkdir_safe_label("/run/systemd/resolve", 0755, uid, gid); if (r < 0) { log_error_errno(r, "Could not create runtime directory: %m"); goto finish; } r = drop_privileges(uid, gid, 0); if (r < 0) goto finish; assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGTERM, SIGINT, SIGUSR1, -1) >= 0); r = manager_new(&m); if (r < 0) { log_error_errno(r, "Could not create manager: %m"); goto finish; } r = manager_start(m); if (r < 0) { log_error_errno(r, "Failed to start manager: %m"); goto finish; } /* Write finish default resolv.conf to avoid a dangling * symlink */ r = manager_write_resolv_conf(m); if (r < 0) log_warning_errno(r, "Could not create resolv.conf: %m"); sd_notify(false, "READY=1\n" "STATUS=Processing requests..."); r = sd_event_loop(m->event); if (r < 0) { log_error_errno(r, "Event loop failed: %m"); goto finish; } sd_event_get_exit_code(m->event, &r); finish: sd_notify(false, "STOPPING=1\n" "STATUS=Shutting down..."); return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; }
int main(int argc, char *argv[]) { _cleanup_(manager_freep) Manager *m = NULL; const char *user = "******"; uid_t uid; gid_t gid; int r; log_set_target(LOG_TARGET_AUTO); log_set_facility(LOG_CRON); log_parse_environment(); log_open(); umask(0022); if (argc != 1) { log_error("This program does not take arguments."); r = -EINVAL; goto finish; } r = get_user_creds(&user, &uid, &gid, NULL, NULL); if (r < 0) { log_error_errno(r, "Cannot resolve user name %s: %m", user); goto finish; } r = load_clock_timestamp(uid, gid); if (r < 0) goto finish; r = drop_privileges(uid, gid, (1ULL << CAP_SYS_TIME)); if (r < 0) goto finish; /* We need one process for ourselves, plus one thread for the asynchronous resolver */ if (setrlimit(RLIMIT_NPROC, &RLIMIT_MAKE_CONST(2)) < 0) log_warning_errno(errno, "Failed to lower RLIMIT_NPROC to 2: %m"); assert_se(sigprocmask_many(SIG_BLOCK, SIGTERM, SIGINT, -1) == 0); r = manager_new(&m); if (r < 0) { log_error_errno(r, "Failed to allocate manager: %m"); goto finish; } if (clock_is_localtime() > 0) { log_info("The system is configured to read the RTC time in the local time zone. " "This mode can not be fully supported. All system time to RTC updates are disabled."); m->rtc_local_time = true; } r = manager_parse_config_file(m); if (r < 0) log_warning_errno(r, "Failed to parse configuration file: %m"); log_debug("systemd-timesyncd running as pid %lu", (unsigned long) getpid()); sd_notify(false, "READY=1\n" "STATUS=Daemon is running"); if (network_is_online()) { r = manager_connect(m); if (r < 0) goto finish; } r = sd_event_loop(m->event); if (r < 0) { log_error_errno(r, "Failed to run event loop: %m"); goto finish; } /* if we got an authoritative time, store it in the file system */ if (m->sync) touch("/var/lib/systemd/clock"); sd_event_get_exit_code(m->event, &r); finish: sd_notify(false, "STOPPING=1\n" "STATUS=Shutting down..."); return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; }
static int start_transient_scope( sd_bus *bus, char **argv) { _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL; _cleanup_(bus_wait_for_jobs_freep) BusWaitForJobs *w = NULL; _cleanup_strv_free_ char **env = NULL, **user_env = NULL; _cleanup_free_ char *scope = NULL; const char *object = NULL; int r; assert(bus); assert(argv); r = bus_wait_for_jobs_new(bus, &w); if (r < 0) return log_oom(); if (arg_unit) { r = unit_name_mangle_with_suffix(arg_unit, UNIT_NAME_NOGLOB, ".scope", &scope); if (r < 0) return log_error_errno(r, "Failed to mangle scope name: %m"); } else { r = make_unit_name(bus, UNIT_SCOPE, &scope); if (r < 0) return r; } r = sd_bus_message_new_method_call( bus, &m, "org.freedesktop.systemd1", "/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", "StartTransientUnit"); if (r < 0) return bus_log_create_error(r); r = sd_bus_message_set_allow_interactive_authorization(m, arg_ask_password); if (r < 0) return bus_log_create_error(r); /* Name and Mode */ r = sd_bus_message_append(m, "ss", scope, "fail"); if (r < 0) return bus_log_create_error(r); /* Properties */ r = sd_bus_message_open_container(m, 'a', "(sv)"); if (r < 0) return bus_log_create_error(r); r = transient_scope_set_properties(m); if (r < 0) return bus_log_create_error(r); r = sd_bus_message_close_container(m); if (r < 0) return bus_log_create_error(r); /* Auxiliary units */ r = sd_bus_message_append(m, "a(sa(sv))", 0); if (r < 0) return bus_log_create_error(r); polkit_agent_open_if_enabled(); r = sd_bus_call(bus, m, 0, &error, &reply); if (r < 0) { log_error("Failed to start transient scope unit: %s", bus_error_message(&error, -r)); return r; } if (arg_nice_set) { if (setpriority(PRIO_PROCESS, 0, arg_nice) < 0) return log_error_errno(errno, "Failed to set nice level: %m"); } if (arg_exec_group) { gid_t gid; r = get_group_creds(&arg_exec_group, &gid); if (r < 0) return log_error_errno(r, "Failed to resolve group %s: %m", arg_exec_group); if (setresgid(gid, gid, gid) < 0) return log_error_errno(errno, "Failed to change GID to " GID_FMT ": %m", gid); } if (arg_exec_user) { const char *home, *shell; uid_t uid; gid_t gid; r = get_user_creds(&arg_exec_user, &uid, &gid, &home, &shell); if (r < 0) return log_error_errno(r, "Failed to resolve user %s: %m", arg_exec_user); r = strv_extendf(&user_env, "HOME=%s", home); if (r < 0) return log_oom(); r = strv_extendf(&user_env, "SHELL=%s", shell); if (r < 0) return log_oom(); r = strv_extendf(&user_env, "USER=%s", arg_exec_user); if (r < 0) return log_oom(); r = strv_extendf(&user_env, "LOGNAME=%s", arg_exec_user); if (r < 0) return log_oom(); if (!arg_exec_group) { if (setresgid(gid, gid, gid) < 0) return log_error_errno(errno, "Failed to change GID to " GID_FMT ": %m", gid); } if (setresuid(uid, uid, uid) < 0) return log_error_errno(errno, "Failed to change UID to " UID_FMT ": %m", uid); } env = strv_env_merge(3, environ, user_env, arg_environment); if (!env) return log_oom(); r = sd_bus_message_read(reply, "o", &object); if (r < 0) return bus_log_parse_error(r); r = bus_wait_for_jobs_one(w, object, arg_quiet); if (r < 0) return r; if (!arg_quiet) log_info("Running scope as unit: %s", scope); execvpe(argv[0], argv, env); return log_error_errno(errno, "Failed to execute: %m"); }