コード例 #1
0
ファイル: device.c プロジェクト: felipec/udev-fc
static int device_update_unit(Manager *m, struct udev_device *dev, const char *path, bool main) {
        const char *sysfs, *model;
        Unit *u = NULL;
        int r;
        bool delete;

        assert(m);

        if (!(sysfs = udev_device_get_syspath(dev)))
                return -ENOMEM;

        if ((r = device_find_escape_name(m, path, &u)) < 0)
                return r;

        if (u && DEVICE(u)->sysfs && !path_equal(DEVICE(u)->sysfs, sysfs))
                return -EEXIST;

        if (!u) {
                delete = true;

                u = unit_new(m, sizeof(Device));
                if (!u)
                        return -ENOMEM;

                r = device_add_escaped_name(u, path);
                if (r < 0)
                        goto fail;

                unit_add_to_load_queue(u);
        } else
コード例 #2
0
ファイル: init.c プロジェクト: bysin/unitjs
int main(int argc, char **argv, char **envp) {
	unit_t n;
	int32_t rc, i;
	
	m_argc = argc;
	m_argv = argv;
	m_envp = envp;
	
	if ((rc = opt_parse(argc, argv)) < 0)
		return 1;
	
	if (opt_fastcgi)
		fcgi_start(opt_fastcgi);
	else if (opt_webserver)
		ws_start(opt_webserver);
	else {
		if (!(n = unit_new())) {
			printf("Unable to start UnitJS\n");
			return 1;
		}
		for (i = rc; i < argc; i++) {
			if (!unit_script(n, argv[i], NULL))
				break;
		}
		unit_free(n);
		unit_shutdown();
	}
	return m_exitstatus;
}
コード例 #3
0
ファイル: configd.c プロジェクト: rmoorman/ServiceManager
void install_configd_svc ()
{
    svc_t * new_svc = s16_svc_new ();
    char * fmri;

    new_svc->id = 1;
    new_svc->name = strdup ("s16/configd");
    svc_object_set_property_string (new_svc, "S16.Name", new_svc->name);
    asprintf (&fmri, "svc:/%s", new_svc->name);
    svc_object_set_property_string (new_svc, "S16.FMRI", fmri);
    free (fmri);

    svc_object_set_property_string (new_svc, "Unit.Strategy", "exec");
    svc_object_set_property_string (
        new_svc, "Unit.Description",
        "The S16 Service Configuration Repository.");

    /* how shall we identify the location of s16 binaries? */
    svc_object_set_property_string (new_svc, "Method.Prestart",
                                    "/bin/echo PreStart");
    svc_object_set_property_string (new_svc, "Method.Start", "/bin/sleep 7");
    svc_object_set_property_string (new_svc, "Method.Poststart",
                                    "/bin/echo Execed poststart");
    /* "out/freebsd.amd64/debug/stage/bin/s16.repositoryd");*/
    s16_inst_new_default (new_svc);
    inst_list_lget (new_svc->instances)->id = 1;

    unit_list_add (Manager.units,
                   unit_new (new_svc, inst_list_lget (new_svc->instances)));
}
コード例 #4
0
ファイル: slice.c プロジェクト: ajeddeloh/systemd
static int slice_enumerate(Manager *m) {
        Unit *u;
        int r;

        assert(m);

        u = manager_get_unit(m, SPECIAL_ROOT_SLICE);
        if (!u) {
                u = unit_new(m, sizeof(Slice));
                if (!u)
                        return log_oom();

                r = unit_add_name(u, SPECIAL_ROOT_SLICE);
                if (r < 0) {
                        unit_free(u);
                        return log_error_errno(r, "Failed to add -.slice name");
                }
        }

        u->default_dependencies = false;
        u->no_gc = true;
        SLICE(u)->deserialized_state = SLICE_ACTIVE;

        if (!u->description)
                u->description = strdup("Root Slice");
        if (!u->documentation)
                (void) strv_extend(&u->documentation, "man:systemd.special(7)");

        unit_add_to_load_queue(u);
        unit_add_to_dbus_queue(u);

        return 0;
}
コード例 #5
0
ファイル: scope.c プロジェクト: arthur-c/systemd
static void scope_enumerate(Manager *m) {
        Unit *u;
        int r;

        assert(m);

        /* Let's unconditionally add the "init.scope" special unit
         * that encapsulates PID 1. Note that PID 1 already is in the
         * cgroup for this, we hence just need to allocate the object
         * for it and that's it. */

        u = manager_get_unit(m, SPECIAL_INIT_SCOPE);
        if (!u) {
                u = unit_new(m, sizeof(Scope));
                if (!u) {
                        log_oom();
                        return;
                }

                r = unit_add_name(u, SPECIAL_INIT_SCOPE);
                if (r < 0)  {
                        unit_free(u);
                        log_error_errno(r, "Failed to add init.scope name");
                        return;
                }
        }

        u->transient = true;
        u->default_dependencies = false;
        u->no_gc = true;
        u->ignore_on_isolate = true;
        u->refuse_manual_start = true;
        u->refuse_manual_stop = true;
        SCOPE(u)->deserialized_state = SCOPE_RUNNING;
        SCOPE(u)->kill_context.kill_signal = SIGRTMIN+14;

        /* Prettify things, if we can. */
        if (!u->description)
                u->description = strdup("System and Service Manager");
        if (!u->documentation)
                (void) strv_extend(&u->documentation, "man:systemd(1)");

        unit_add_to_load_queue(u);
        unit_add_to_dbus_queue(u);
}
コード例 #6
0
ファイル: test-unit-file.c プロジェクト: OpenDZ/systemd
static void test_config_parse_exec(void) {
        /* int config_parse_exec(
                 const char *unit,
                 const char *filename,
                 unsigned line,
                 const char *section,
                 unsigned section_line,
                 const char *lvalue,
                 int ltype,
                 const char *rvalue,
                 void *data,
                 void *userdata) */
        int r;

        ExecCommand *c = NULL, *c1;
        const char *ccc;
        Manager *m = NULL;
        Unit *u = NULL;

        r = manager_new(UNIT_FILE_USER, MANAGER_TEST_RUN_MINIMAL, &m);
        if (MANAGER_SKIP_TEST(r)) {
                log_notice_errno(r, "Skipping test: manager_new: %m");
                return;
        }

        assert_se(r >= 0);
        assert_se(manager_startup(m, NULL, NULL) >= 0);

        assert_se(u = unit_new(m, sizeof(Service)));

        log_info("/* basic test */");
        r = config_parse_exec(NULL, "fake", 1, "section", 1,
                              "LValue", 0, "/RValue r1",
                              &c, u);
        assert_se(r >= 0);
        check_execcommand(c, "/RValue", "/RValue", "r1", NULL, false);

        r = config_parse_exec(NULL, "fake", 2, "section", 1,
                              "LValue", 0, "/RValue///slashes r1///",
                              &c, u);

        log_info("/* test slashes */");
        assert_se(r >= 0);
        c1 = c->command_next;
        check_execcommand(c1, "/RValue/slashes", "/RValue///slashes", "r1///", NULL, false);

        log_info("/* trailing slash */");
        r = config_parse_exec(NULL, "fake", 4, "section", 1,
                              "LValue", 0, "/RValue/ argv0 r1",
                              &c, u);
        assert_se(r == -ENOEXEC);
        assert_se(c1->command_next == NULL);

        log_info("/* honour_argv0 */");
        r = config_parse_exec(NULL, "fake", 3, "section", 1,
                              "LValue", 0, "@/RValue///slashes2 ///argv0 r1",
                              &c, u);
        assert_se(r >= 0);
        c1 = c1->command_next;
        check_execcommand(c1, "/RValue/slashes2", "///argv0", "r1", NULL, false);

        log_info("/* honour_argv0, no args */");
        r = config_parse_exec(NULL, "fake", 3, "section", 1,
                              "LValue", 0, "@/RValue",
                              &c, u);
        assert_se(r == -ENOEXEC);
        assert_se(c1->command_next == NULL);

        log_info("/* no command, whitespace only, reset */");
        r = config_parse_exec(NULL, "fake", 3, "section", 1,
                              "LValue", 0, "    ",
                              &c, u);
        assert_se(r == 0);
        assert_se(c == NULL);

        log_info("/* ignore && honour_argv0 */");
        r = config_parse_exec(NULL, "fake", 4, "section", 1,
                              "LValue", 0, "-@/RValue///slashes3 argv0a r1",
                              &c, u);
        assert_se(r >= 0);
        c1 = c;
        check_execcommand(c1, "/RValue/slashes3", "argv0a", "r1", NULL, true);

        log_info("/* ignore && honour_argv0 */");
        r = config_parse_exec(NULL, "fake", 4, "section", 1,
                              "LValue", 0, "@-/RValue///slashes4 argv0b r1",
                              &c, u);
        assert_se(r >= 0);
        c1 = c1->command_next;
        check_execcommand(c1, "/RValue/slashes4", "argv0b", "r1", NULL, true);

        log_info("/* ignore && ignore */");
        r = config_parse_exec(NULL, "fake", 4, "section", 1,
                              "LValue", 0, "--/RValue argv0 r1",
                              &c, u);
        assert_se(r == 0);
        assert_se(c1->command_next == NULL);

        log_info("/* ignore && ignore (2) */");
        r = config_parse_exec(NULL, "fake", 4, "section", 1,
                              "LValue", 0, "-@-/RValue argv0 r1",
                              &c, u);
        assert_se(r == 0);
        assert_se(c1->command_next == NULL);

        log_info("/* semicolon */");
        r = config_parse_exec(NULL, "fake", 5, "section", 1,
                              "LValue", 0,
                              "-@/RValue argv0 r1 ; "
                              "/goo/goo boo",
                              &c, u);
        assert_se(r >= 0);
        c1 = c1->command_next;
        check_execcommand(c1, "/RValue", "argv0", "r1", NULL, true);

        c1 = c1->command_next;
        check_execcommand(c1, "/goo/goo", NULL, "boo", NULL, false);

        log_info("/* two semicolons in a row */");
        r = config_parse_exec(NULL, "fake", 5, "section", 1,
                              "LValue", 0,
                              "-@/RValue argv0 r1 ; ; "
                              "/goo/goo boo",
                              &c, u);
        assert_se(r == -ENOEXEC);
        c1 = c1->command_next;
        check_execcommand(c1, "/RValue", "argv0", "r1", NULL, true);

        /* second command fails because the executable name is ";" */
        assert_se(c1->command_next == NULL);

        log_info("/* trailing semicolon */");
        r = config_parse_exec(NULL, "fake", 5, "section", 1,
                              "LValue", 0,
                              "-@/RValue argv0 r1 ; ",
                              &c, u);
        assert_se(r >= 0);
        c1 = c1->command_next;
        check_execcommand(c1, "/RValue", "argv0", "r1", NULL, true);

        assert_se(c1->command_next == NULL);

        log_info("/* trailing semicolon, no whitespace */");
        r = config_parse_exec(NULL, "fake", 5, "section", 1,
                              "LValue", 0,
                              "-@/RValue argv0 r1 ;",
                              &c, u);
        assert_se(r >= 0);
        c1 = c1->command_next;
        check_execcommand(c1, "/RValue", "argv0", "r1", NULL, true);

        assert_se(c1->command_next == NULL);

        log_info("/* trailing semicolon in single quotes */");
        r = config_parse_exec(NULL, "fake", 5, "section", 1,
                              "LValue", 0,
                              "-@/RValue argv0 r1 ';'",
                              &c, u);
        assert_se(r >= 0);
        c1 = c1->command_next;
        check_execcommand(c1, "/RValue", "argv0", "r1", ";", true);

        log_info("/* escaped semicolon */");
        r = config_parse_exec(NULL, "fake", 5, "section", 1,
                              "LValue", 0,
                              "/bin/find \\;",
                              &c, u);
        assert_se(r >= 0);
        c1 = c1->command_next;
        check_execcommand(c1, "/bin/find", NULL, ";", NULL, false);

        log_info("/* escaped semicolon with following arg */");
        r = config_parse_exec(NULL, "fake", 5, "section", 1,
                              "LValue", 0,
                              "/sbin/find \\; /x",
                              &c, u);
        assert_se(r >= 0);
        c1 = c1->command_next;
        check_execcommand(c1,
                          "/sbin/find", NULL, ";", "/x", false);

        log_info("/* escaped semicolon as part of an expression */");
        r = config_parse_exec(NULL, "fake", 5, "section", 1,
                              "LValue", 0,
                              "/sbin/find \\;x",
                              &c, u);
        assert_se(r >= 0);
        c1 = c1->command_next;
        check_execcommand(c1,
                          "/sbin/find", NULL, "\\;x", NULL, false);

        log_info("/* encoded semicolon */");
        r = config_parse_exec(NULL, "fake", 5, "section", 1,
                              "LValue", 0,
                              "/bin/find \\073",
                              &c, u);
        assert_se(r >= 0);
        c1 = c1->command_next;
        check_execcommand(c1, "/bin/find", NULL, ";", NULL, false);

        log_info("/* quoted semicolon */");
        r = config_parse_exec(NULL, "fake", 5, "section", 1,
                              "LValue", 0,
                              "/bin/find \";\"",
                              &c, u);
        assert_se(r >= 0);
        c1 = c1->command_next;
        check_execcommand(c1, "/bin/find", NULL, ";", NULL, false);

        log_info("/* quoted semicolon with following arg */");
        r = config_parse_exec(NULL, "fake", 5, "section", 1,
                              "LValue", 0,
                              "/sbin/find \";\" /x",
                              &c, u);
        assert_se(r >= 0);
        c1 = c1->command_next;
        check_execcommand(c1,
                          "/sbin/find", NULL, ";", "/x", false);

        log_info("/* spaces in the filename */");
        r = config_parse_exec(NULL, "fake", 5, "section", 1,
                              "LValue", 0,
                              "\"/PATH WITH SPACES/daemon\" -1 -2",
                              &c, u);
        assert_se(r >= 0);
        c1 = c1->command_next;
        check_execcommand(c1,
                          "/PATH WITH SPACES/daemon", NULL, "-1", "-2", false);

        log_info("/* spaces in the filename, no args */");
        r = config_parse_exec(NULL, "fake", 5, "section", 1,
                              "LValue", 0,
                              "\"/PATH WITH SPACES/daemon -1 -2\"",
                              &c, u);
        assert_se(r >= 0);
        c1 = c1->command_next;
        check_execcommand(c1,
                          "/PATH WITH SPACES/daemon -1 -2", NULL, NULL, NULL, false);

        log_info("/* spaces in the filename, everything quoted */");
        r = config_parse_exec(NULL, "fake", 5, "section", 1,
                              "LValue", 0,
                              "\"/PATH WITH SPACES/daemon\" \"-1\" '-2'",
                              &c, u);
        assert_se(r >= 0);
        c1 = c1->command_next;
        check_execcommand(c1,
                          "/PATH WITH SPACES/daemon", NULL, "-1", "-2", false);

        log_info("/* escaped spaces in the filename */");
        r = config_parse_exec(NULL, "fake", 5, "section", 1,
                              "LValue", 0,
                              "\"/PATH\\sWITH\\sSPACES/daemon\" '-1 -2'",
                              &c, u);
        assert_se(r >= 0);
        c1 = c1->command_next;
        check_execcommand(c1,
                          "/PATH WITH SPACES/daemon", NULL, "-1 -2", NULL, false);

        log_info("/* escaped spaces in the filename (2) */");
        r = config_parse_exec(NULL, "fake", 5, "section", 1,
                              "LValue", 0,
                              "\"/PATH\\x20WITH\\x20SPACES/daemon\" \"-1 -2\"",
                              &c, u);
        assert_se(r >= 0);
        c1 = c1->command_next;
        check_execcommand(c1,
                          "/PATH WITH SPACES/daemon", NULL, "-1 -2", NULL, false);

        for (ccc = "abfnrtv\\\'\"x"; *ccc; ccc++) {
                /* \\x is an incomplete hexadecimal sequence, invalid because of the slash */
                char path[] = "/path\\X";
                path[sizeof(path) - 2] = *ccc;

                log_info("/* invalid character: \\%c */", *ccc);
                r = config_parse_exec(NULL, "fake", 4, "section", 1,
                                      "LValue", 0, path,
                                      &c, u);
                assert_se(r == -ENOEXEC);
                assert_se(c1->command_next == NULL);
        }

        log_info("/* valid character: \\s */");
        r = config_parse_exec(NULL, "fake", 4, "section", 1,
                              "LValue", 0, "/path\\s",
                              &c, u);
        assert_se(r >= 0);
        c1 = c1->command_next;
        check_execcommand(c1, "/path ", NULL, NULL, NULL, false);

        log_info("/* quoted backslashes */");
        r = config_parse_exec(NULL, "fake", 5, "section", 1,
                              "LValue", 0,
                              "/bin/grep '\\w+\\K'",
                              &c, u);
        assert_se(r >= 0);
        c1 = c1->command_next;
        check_execcommand(c1, "/bin/grep", NULL, "\\w+\\K", NULL, false);


        log_info("/* trailing backslash: \\ */");
        /* backslash is invalid */
        r = config_parse_exec(NULL, "fake", 4, "section", 1,
                              "LValue", 0, "/path\\",
                              &c, u);
        assert_se(r == -ENOEXEC);
        assert_se(c1->command_next == NULL);

        log_info("/* missing ending ' */");
        r = config_parse_exec(NULL, "fake", 4, "section", 1,
                              "LValue", 0, "/path 'foo",
                              &c, u);
        assert_se(r == -ENOEXEC);
        assert_se(c1->command_next == NULL);

        log_info("/* missing ending ' with trailing backslash */");
        r = config_parse_exec(NULL, "fake", 4, "section", 1,
                              "LValue", 0, "/path 'foo\\",
                              &c, u);
        assert_se(r == -ENOEXEC);
        assert_se(c1->command_next == NULL);

        log_info("/* invalid space between modifiers */");
        r = config_parse_exec(NULL, "fake", 4, "section", 1,
                              "LValue", 0, "- /path",
                              &c, u);
        assert_se(r == 0);
        assert_se(c1->command_next == NULL);

        log_info("/* only modifiers, no path */");
        r = config_parse_exec(NULL, "fake", 4, "section", 1,
                              "LValue", 0, "-",
                              &c, u);
        assert_se(r == 0);
        assert_se(c1->command_next == NULL);

        log_info("/* empty argument, reset */");
        r = config_parse_exec(NULL, "fake", 4, "section", 1,
                              "LValue", 0, "",
                              &c, u);
        assert_se(r == 0);
        assert_se(c == NULL);

        exec_command_free_list(c);

        unit_free(u);
        manager_free(m);
}
コード例 #7
0
ファイル: test-unit-name.c プロジェクト: chuanchang/systemd
static int test_unit_printf(void) {
        Manager *m;
        Unit *u, *u2;
        int r;

        _cleanup_free_ char *mid, *bid, *host, *root_uid;
        struct passwd *root;

        assert_se(specifier_machine_id('m', NULL, NULL, &mid) >= 0 && mid);
        assert_se(specifier_boot_id('b', NULL, NULL, &bid) >= 0 && bid);
        assert_se((host = gethostname_malloc()));

        assert_se((root = getpwnam("root")));
        assert_se(asprintf(&root_uid, "%d", (int) root->pw_uid) > 0);

        r = manager_new(SYSTEMD_USER, &m);
        if (r == -EPERM || r == -EACCES || r == -EADDRINUSE) {
                puts("manager_new: Permission denied. Skipping test.");
                return EXIT_TEST_SKIP;
        }
        assert(r == 0);

#define expect(unit, pattern, expected)                                 \
        {                                                               \
                char *e;                                                \
                _cleanup_free_ char *t;                                 \
                assert_se(unit_full_printf(unit, pattern, &t) >= 0);    \
                printf("result: %s\nexpect: %s\n", t, expected);        \
                if ((e = endswith(expected, "*")))                      \
                        assert(strncmp(t, e, e-expected));              \
                else                                                    \
                        assert(streq(t, expected));                     \
        }

        assert_se(setenv("USER", "root", 1) == 0);
        assert_se(setenv("HOME", "/root", 1) == 0);

        assert_se(u = unit_new(m, sizeof(Service)));
        assert_se(unit_add_name(u, "blah.service") == 0);
        assert_se(unit_add_name(u, "blah.service") == 0);

        /* general tests */
        expect(u, "%%", "%");
        expect(u, "%%s", "%s");
        expect(u, "%", "");    // REALLY?

        /* normal unit */
        expect(u, "%n", "blah.service");
        expect(u, "%N", "blah");
        expect(u, "%p", "blah");
        expect(u, "%P", "blah");
        expect(u, "%i", "");
        expect(u, "%I", "");
        expect(u, "%u", root->pw_name);
        expect(u, "%U", root_uid);
        expect(u, "%h", root->pw_dir);
        expect(u, "%s", "/bin/sh");
        expect(u, "%m", mid);
        expect(u, "%b", bid);
        expect(u, "%H", host);
        expect(u, "%t", "/run/user/*");

        /* templated */
        assert_se(u2 = unit_new(m, sizeof(Service)));
        assert_se(unit_add_name(u2, "*****@*****.**") == 0);
        assert_se(unit_add_name(u2, "*****@*****.**") == 0);

        expect(u2, "%n", "*****@*****.**");
        expect(u2, "%N", "blah@foo-foo");
        expect(u2, "%p", "blah");
        expect(u2, "%P", "blah");
        expect(u2, "%i", "foo-foo");
        expect(u2, "%I", "foo/foo");
        expect(u2, "%u", root->pw_name);
        expect(u2, "%U", root_uid);
        expect(u2, "%h", root->pw_dir);
        expect(u2, "%s", "/bin/sh");
        expect(u2, "%m", mid);
        expect(u2, "%b", bid);
        expect(u2, "%H", host);
        expect(u2, "%t", "/run/user/*");

        manager_free(m);

        return 0;
}
コード例 #8
0
ファイル: test-unit-file.c プロジェクト: karelzak/systemd
static void test_config_parse_log_extra_fields(void) {
        /* int config_parse_log_extra_fields(
                const char *unit,
                const char *filename,
                unsigned line,
                const char *section,
                unsigned section_line,
                const char *lvalue,
                int ltype,
                const char *rvalue,
                void *data,
                void *userdata) */

        int r;

        _cleanup_(manager_freep) Manager *m = NULL;
        _cleanup_(unit_freep) Unit *u = NULL;
        ExecContext c = {};

        r = manager_new(UNIT_FILE_USER, MANAGER_TEST_RUN_MINIMAL, &m);
        if (MANAGER_SKIP_TEST(r)) {
                log_notice_errno(r, "Skipping test: manager_new: %m");
                return;
        }

        assert_se(r >= 0);
        assert_se(manager_startup(m, NULL, NULL) >= 0);

        assert_se(u = unit_new(m, sizeof(Service)));

        log_info("/* %s – basic test */", __func__);
        r = config_parse_log_extra_fields(NULL, "fake", 1, "section", 1,
                                          "LValue", 0, "FOO=BAR \"QOOF=quux '  ' \"",
                                          &c, u);
        assert_se(r >= 0);
        assert_se(c.n_log_extra_fields == 2);
        assert_se(strneq(c.log_extra_fields[0].iov_base, "FOO=BAR", c.log_extra_fields[0].iov_len));
        assert_se(strneq(c.log_extra_fields[1].iov_base, "QOOF=quux '  ' ", c.log_extra_fields[1].iov_len));

        log_info("/* %s – add some */", __func__);
        r = config_parse_log_extra_fields(NULL, "fake", 1, "section", 1,
                                          "LValue", 0, "FOO2=BAR2 QOOF2=quux '  '",
                                          &c, u);
        assert_se(r >= 0);
        assert_se(c.n_log_extra_fields == 4);
        assert_se(strneq(c.log_extra_fields[0].iov_base, "FOO=BAR", c.log_extra_fields[0].iov_len));
        assert_se(strneq(c.log_extra_fields[1].iov_base, "QOOF=quux '  ' ", c.log_extra_fields[1].iov_len));
        assert_se(strneq(c.log_extra_fields[2].iov_base, "FOO2=BAR2", c.log_extra_fields[2].iov_len));
        assert_se(strneq(c.log_extra_fields[3].iov_base, "QOOF2=quux", c.log_extra_fields[3].iov_len));

        exec_context_dump(&c, stdout, "    --> ");

        log_info("/* %s – reset */", __func__);
        r = config_parse_log_extra_fields(NULL, "fake", 1, "section", 1,
                                          "LValue", 0, "",
                                          &c, u);
        assert_se(r >= 0);
        assert_se(c.n_log_extra_fields == 0);

        exec_context_free_log_extra_fields(&c);

        log_info("/* %s – bye */", __func__);
}
コード例 #9
0
static int test_unit_printf(void) {
        Manager *m = NULL;
        Unit *u, *u2;
        int r;

        _cleanup_free_ char *mid = NULL, *bid = NULL, *host = NULL, *uid = NULL, *user = NULL, *shell = NULL, *home = NULL;

        assert_se(specifier_machine_id('m', NULL, NULL, &mid) >= 0 && mid);
        assert_se(specifier_boot_id('b', NULL, NULL, &bid) >= 0 && bid);
        assert_se(host = gethostname_malloc());
        assert_se(user = getusername_malloc());
        assert_se(asprintf(&uid, UID_FMT, getuid()));
        assert_se(get_home_dir(&home) >= 0);
        assert_se(get_shell(&shell) >= 0);

        r = manager_new(UNIT_FILE_USER, true, &m);
        if (r == -EPERM || r == -EACCES || r == -EADDRINUSE) {
                puts("manager_new: Permission denied. Skipping test.");
                return EXIT_TEST_SKIP;
        }
        assert_se(r == 0);

#define expect(unit, pattern, expected)                                 \
        {                                                               \
                char *e;                                                \
                _cleanup_free_ char *t = NULL;                          \
                assert_se(unit_full_printf(unit, pattern, &t) >= 0);    \
                printf("result: %s\nexpect: %s\n", t, expected);        \
                if ((e = endswith(expected, "*")))                      \
                        assert_se(strncmp(t, e, e-expected));              \
                else                                                    \
                        assert_se(streq(t, expected));                     \
        }

        assert_se(setenv("XDG_RUNTIME_DIR", "/run/user/1/", 1) == 0);

        assert_se(u = unit_new(m, sizeof(Service)));
        assert_se(unit_add_name(u, "blah.service") == 0);
        assert_se(unit_add_name(u, "blah.service") == 0);

        /* general tests */
        expect(u, "%%", "%");
        expect(u, "%%s", "%s");
        expect(u, "%", "");    // REALLY?

        /* normal unit */
        expect(u, "%n", "blah.service");
        expect(u, "%f", "/blah");
        expect(u, "%N", "blah");
        expect(u, "%p", "blah");
        expect(u, "%P", "blah");
        expect(u, "%i", "");
        expect(u, "%u", user);
        expect(u, "%U", uid);
        expect(u, "%h", home);
        expect(u, "%m", mid);
        expect(u, "%b", bid);
        expect(u, "%H", host);
        expect(u, "%t", "/run/user/*");

        /* templated */
        assert_se(u2 = unit_new(m, sizeof(Service)));
        assert_se(unit_add_name(u2, "*****@*****.**") == 0);
        assert_se(unit_add_name(u2, "*****@*****.**") == 0);

        expect(u2, "%n", "*****@*****.**");
        expect(u2, "%N", "blah@foo-foo");
        expect(u2, "%f", "/foo/foo");
        expect(u2, "%p", "blah");
        expect(u2, "%P", "blah");
        expect(u2, "%i", "foo-foo");
        expect(u2, "%I", "foo/foo");
        expect(u2, "%u", user);
        expect(u2, "%U", uid);
        expect(u2, "%h", home);
        expect(u2, "%m", mid);
        expect(u2, "%b", bid);
        expect(u2, "%H", host);
        expect(u2, "%t", "/run/user/*");

        manager_free(m);
#undef expect

        return 0;
}