+ }
+
+ if (shutdown(fd, SHUT_RD) < 0) {
+ close_nointr_nofail(fd);
+ return -errno;
+ }
+
+ if ((fd = dup(fd)) < 0) {
+ close_nointr_nofail(fd);
+ return -errno;
+ }
+
+ if (fd != STDERR_FILENO) {
+ close_nointr_nofail(fd);
+ return -EIO;
+ }
+
+ /* We speak a very simple protocol between log server
+ * and client: one line for the log destination (kmsg
+ * or syslog), followed by the priority field,
+ * followed by the process name. Since we replaced
+ * stdin/stderr we simple use stdio to write to
+ * it. Note that we use stderr, to minimize buffer
+ * flushing issues. */
+
+ fprintf(stderr,
+ "%s\n"
+ "%i\n"
+ "%s\n",
+ context->output == EXEC_OUTPUT_KERNEL ? "kmsg" : "syslog",
+ context->syslog_priority,
+ context->syslog_identifier ? context->syslog_identifier : ident);
+
+ return 0;
+ }
+
+ default:
+ assert_not_reached("Unknown output type");
+ }
+}
+
+static int setup_input(const ExecContext *context) {
+ int r;
+
+ assert(context);
+
+ switch (context->input) {
+
+ case EXEC_INPUT_CONSOLE:
+ return 0;
+
+ case EXEC_INPUT_NULL:
+ if ((r = replace_null_fd(STDIN_FILENO, O_RDONLY)) < 0)
+ return r;
+
+ return 0;
+
+ default:
+ assert_not_reached("Unknown input type");
+ }
+}
+
+static int get_group_creds(const char *groupname, gid_t *gid) {
+ struct group *g;
+ unsigned long lu;
+
+ assert(groupname);
+ assert(gid);
+
+ /* We enforce some special rules for gid=0: in order to avoid
+ * NSS lookups for root we hardcode its data. */
+
+ if (streq(groupname, "root") || streq(groupname, "0")) {
+ *gid = 0;
+ return 0;
+ }
+
+ if (safe_atolu(groupname, &lu) >= 0) {
+ errno = 0;
+ g = getgrgid((gid_t) lu);
+ } else {
+ errno = 0;
+ g = getgrnam(groupname);
+ }
+
+ if (!g)
+ return errno != 0 ? -errno : -ESRCH;
+
+ *gid = g->gr_gid;
+ return 0;
+}
+
+static int get_user_creds(const char **username, uid_t *uid, gid_t *gid, const char **home) {
+ struct passwd *p;
+ unsigned long lu;
+
+ assert(username);
+ assert(*username);
+ assert(uid);
+ assert(gid);
+ assert(home);
+
+ /* We enforce some special rules for uid=0: in order to avoid
+ * NSS lookups for root we hardcode its data. */
+
+ if (streq(*username, "root") || streq(*username, "0")) {
+ *username = "root";
+ *uid = 0;
+ *gid = 0;
+ *home = "/root";
+ return 0;
+ }
+
+ if (safe_atolu(*username, &lu) >= 0) {
+ errno = 0;
+ p = getpwuid((uid_t) lu);
+
+ /* If there are multiple users with the same id, make
+ * sure to leave $USER to the configured value instead
+ * of the first occurence in the database. However if
+ * the uid was configured by a numeric uid, then let's
+ * pick the real username from /etc/passwd. */
+ if (*username && p)
+ *username = p->pw_name;
+ } else {
+ errno = 0;
+ p = getpwnam(*username);
+ }
+
+ if (!p)
+ return errno != 0 ? -errno : -ESRCH;
+
+ *uid = p->pw_uid;
+ *gid = p->pw_gid;
+ *home = p->pw_dir;
+ return 0;
+}
+
+static int enforce_groups(const ExecContext *context, const char *username, gid_t gid) {
+ bool keep_groups = false;
+ int r;
+
+ assert(context);
+
+ /* Lookup and ser GID and supplementary group list. Here too
+ * we avoid NSS lookups for gid=0. */
+
+ if (context->group || username) {
+
+ if (context->group)
+ if ((r = get_group_creds(context->group, &gid)) < 0)
+ return r;
+
+ /* First step, initialize groups from /etc/groups */
+ if (username && gid != 0) {
+ if (initgroups(username, gid) < 0)
+ return -errno;
+
+ keep_groups = true;
+ }
+
+ /* Second step, set our gids */
+ if (setresgid(gid, gid, gid) < 0)
+ return -errno;
+ }
+
+ if (context->supplementary_groups) {
+ int ngroups_max, k;
+ gid_t *gids;
+ char **i;
+
+ /* Final step, initialize any manually set supplementary groups */
+ ngroups_max = (int) sysconf(_SC_NGROUPS_MAX);
+
+ if (!(gids = new(gid_t, ngroups_max)))
+ return -ENOMEM;
+
+ if (keep_groups) {
+ if ((k = getgroups(ngroups_max, gids)) < 0) {
+ free(gids);
+ return -errno;
+ }
+ } else
+ k = 0;
+
+ STRV_FOREACH(i, context->supplementary_groups) {
+
+ if (k >= ngroups_max) {
+ free(gids);
+ return -E2BIG;
+ }
+
+ if ((r = get_group_creds(*i, gids+k)) < 0) {
+ free(gids);
+ return r;
+ }