static int build_environment(
                const Unit *u,
                const ExecContext *c,
                const ExecParameters *p,
                size_t n_fds,
                const char *home,
                const char *username,
                const char *shell,
                dev_t journal_stream_dev,
                ino_t journal_stream_ino,
                char ***ret) {

        _cleanup_strv_free_ char **our_env = NULL;
        ExecDirectoryType t;
        size_t n_env = 0;
        char *x;

        assert(u);
        assert(c);
        assert(p);
        assert(ret);

        our_env = new0(char*, 14 + _EXEC_DIRECTORY_TYPE_MAX);
        if (!our_env)
                return -ENOMEM;

        if (n_fds > 0) {
                _cleanup_free_ char *joined = NULL;

                if (asprintf(&x, "LISTEN_PID="PID_FMT, getpid_cached()) < 0)
                        return -ENOMEM;
                our_env[n_env++] = x;

                if (asprintf(&x, "LISTEN_FDS=%zu", n_fds) < 0)
                        return -ENOMEM;
                our_env[n_env++] = x;

                joined = strv_join(p->fd_names, ":");
                if (!joined)
                        return -ENOMEM;

                x = strjoin("LISTEN_FDNAMES=", joined);
                if (!x)
                        return -ENOMEM;
                our_env[n_env++] = x;
        }

        if ((p->flags & EXEC_SET_WATCHDOG) && p->watchdog_usec > 0) {
                if (asprintf(&x, "WATCHDOG_PID="PID_FMT, getpid_cached()) < 0)
                        return -ENOMEM;
                our_env[n_env++] = x;

                if (asprintf(&x, "WATCHDOG_USEC="USEC_FMT, p->watchdog_usec) < 0)
                        return -ENOMEM;
                our_env[n_env++] = x;
        }

        /* If this is D-Bus, tell the nss-systemd module, since it relies on being able to use D-Bus look up dynamic
         * users via PID 1, possibly dead-locking the dbus daemon. This way it will not use D-Bus to resolve names, but
         * check the database directly. */
        if (p->flags & EXEC_NSS_BYPASS_BUS) {
                x = strdup("SYSTEMD_NSS_BYPASS_BUS=1");
                if (!x)
                        return -ENOMEM;
                our_env[n_env++] = x;
        }

        if (home) {
                x = strappend("HOME=", home);
                if (!x)
                        return -ENOMEM;

                path_simplify(x + 5, true);
                our_env[n_env++] = x;
        }

        if (username) {
                x = strappend("LOGNAME=", username);
                if (!x)
                        return -ENOMEM;
                our_env[n_env++] = x;

                x = strappend("USER=", username);
                if (!x)
                        return -ENOMEM;
                our_env[n_env++] = x;
        }

        if (shell) {
                x = strappend("SHELL=", shell);
                if (!x)
                        return -ENOMEM;

                path_simplify(x + 6, true);
                our_env[n_env++] = x;
        }

        if (!sd_id128_is_null(u->invocation_id)) {
                if (asprintf(&x, "INVOCATION_ID=" SD_ID128_FORMAT_STR, SD_ID128_FORMAT_VAL(u->invocation_id)) < 0)
                        return -ENOMEM;

                our_env[n_env++] = x;
        }

        if (exec_context_needs_term(c)) {
                const char *tty_path, *term = NULL;

                tty_path = exec_context_tty_path(c);

                /* If we are forked off PID 1 and we are supposed to operate on /dev/console, then let's try to inherit
                 * the $TERM set for PID 1. This is useful for containers so that the $TERM the container manager
                 * passes to PID 1 ends up all the way in the console login shown. */

                if (path_equal(tty_path, "/dev/console") && getppid() == 1)
                        term = getenv("TERM");
                if (!term)
                        term = default_term_for_tty(tty_path);

                x = strappend("TERM=", term);
                if (!x)
                        return -ENOMEM;
                our_env[n_env++] = x;
        }

        if (journal_stream_dev != 0 && journal_stream_ino != 0) {
                if (asprintf(&x, "JOURNAL_STREAM=" DEV_FMT ":" INO_FMT, journal_stream_dev, journal_stream_ino) < 0)
                        return -ENOMEM;

                our_env[n_env++] = x;
        }

        for (t = 0; t < _EXEC_DIRECTORY_TYPE_MAX; t++) {
                _cleanup_free_ char *pre = NULL, *joined = NULL;
                const char *n;

                if (!p->prefix[t])
                        continue;

                if (strv_isempty(c->directories[t].paths))
                        continue;

                n = exec_directory_env_name_to_string(t);
                if (!n)
                        continue;

                pre = strjoin(p->prefix[t], "/");
                if (!pre)
                        return -ENOMEM;

                joined = strv_join_prefix(c->directories[t].paths, ":", pre);
                if (!joined)
                        return -ENOMEM;

                x = strjoin(n, "=", joined);
                if (!x)
                        return -ENOMEM;

                our_env[n_env++] = x;
        }

        our_env[n_env++] = NULL;
        assert(n_env <= 14 + _EXEC_DIRECTORY_TYPE_MAX);

        *ret = TAKE_PTR(our_env);

        return 0;
}