int get_user_creds(
                const char **username,
                uid_t *uid, gid_t *gid,
                const char **home,
                const char **shell,
                UserCredsFlags flags) {

        uid_t u = UID_INVALID;
        struct passwd *p;
        int r;

        assert(username);
        assert(*username);

        if (!FLAGS_SET(flags, USER_CREDS_PREFER_NSS) ||
            (!home && !shell)) {

                /* So here's the deal: normally, we'll try to synthesize all records we can synthesize, and override
                 * the user database with that. However, if the user specifies USER_CREDS_PREFER_NSS then the
                 * user database will override the synthetic records instead — except if the user is only interested in
                 * the UID and/or GID (but not the home directory, or the shell), in which case we'll always override
                 * the user database (i.e. the USER_CREDS_PREFER_NSS flag has no effect in this case). Why?
                 * Simply because there are valid usecase where the user might change the home directory or the shell
                 * of the relevant users, but changing the UID/GID mappings for them is something we explicitly don't
                 * support. */

                r = synthesize_user_creds(username, uid, gid, home, shell, flags);
                if (r >= 0)
                        return 0;
                if (r != -ENOMEDIUM) /* not a username we can synthesize */
                        return r;
        }

        if (parse_uid(*username, &u) >= 0) {
                errno = 0;
                p = getpwuid(u);

                /* If there are multiple users with the same id, make sure to leave $USER to the configured value
                 * instead of the first occurrence in the database. However if the uid was configured by a numeric uid,
                 * then let's pick the real username from /etc/passwd. */
                if (p)
                        *username = p->pw_name;
                else if (FLAGS_SET(flags, USER_CREDS_ALLOW_MISSING) && !gid && !home && !shell) {

                        /* If the specified user is a numeric UID and it isn't in the user database, and the caller
                         * passed USER_CREDS_ALLOW_MISSING and was only interested in the UID, then juts return that
                         * and don't complain. */

                        if (uid)
                                *uid = u;

                        return 0;
                }
        } else {
                errno = 0;
                p = getpwnam(*username);
        }
        if (!p) {
                r = errno_or_else(ESRCH);

                /* If the user requested that we only synthesize as fallback, do so now */
                if (FLAGS_SET(flags, USER_CREDS_PREFER_NSS)) {
                        if (synthesize_user_creds(username, uid, gid, home, shell, flags) >= 0)
                                return 0;
                }

                return r;
        }

        if (uid) {
                if (!uid_is_valid(p->pw_uid))
                        return -EBADMSG;

                *uid = p->pw_uid;
        }

        if (gid) {
                if (!gid_is_valid(p->pw_gid))
                        return -EBADMSG;

                *gid = p->pw_gid;
        }

        if (home) {
                if (FLAGS_SET(flags, USER_CREDS_CLEAN) &&
                    (empty_or_root(p->pw_dir) ||
                     !path_is_valid(p->pw_dir) ||
                     !path_is_absolute(p->pw_dir)))
                    *home = NULL; /* Note: we don't insist on normalized paths, since there are setups that have /./ in the path */
                else
                        *home = p->pw_dir;
        }

        if (shell) {
                if (FLAGS_SET(flags, USER_CREDS_CLEAN) &&
                    (isempty(p->pw_shell) ||
                     !path_is_valid(p->pw_dir) ||
                     !path_is_absolute(p->pw_shell) ||
                     is_nologin_shell(p->pw_shell)))
                        *shell = NULL;
                else
                        *shell = p->pw_shell;
        }

        return 0;
}