예제 #1
0
파일: cgmanager.c 프로젝트: MSylvia/koding
/*
 * Use the cgmanager to move a task into a cgroup for a particular
 * hierarchy.
 * All the subsystems in this hierarchy are co-mounted, so we only
 * need to transition the task into one of the cgroups
 *
 * Internal helper, must be called with cgmanager dbus socket open
 */
static bool lxc_cgmanager_enter(pid_t pid, const char *controller,
		const char *cgroup_path, bool abs)
{
	int ret;

	if (abs)
		ret = cgmanager_move_pid_abs_sync(NULL, cgroup_manager,
			controller, cgroup_path, pid);
	else
		ret = cgmanager_move_pid_sync(NULL, cgroup_manager,
			controller, cgroup_path, pid);
	if (ret != 0) {
		NihError *nerr;
		nerr = nih_error_get();
		ERROR("call to cgmanager_move_pid_%ssync failed: %s",
			abs ? "abs_" : "", nerr->message);
		nih_free(nerr);
		return false;
	}
	return true;
}
예제 #2
0
void
test_alloc (void)
{
	void *ptr1;
	void *ptr2;

	TEST_FUNCTION ("nih_alloc");

	/* Check allocation remembers the size, and is possible without
	 * a parent.
	 */
	TEST_FEATURE ("with no parent");
	ptr1 = nih_alloc (NULL, 8096);
	memset (ptr1, 'x', 8096);

	TEST_ALLOC_SIZE (ptr1, 8096);
	TEST_ALLOC_PARENT (ptr1, NULL);


	/* Check that allocation with a parent remembers the parent */
	TEST_FEATURE ("with a parent");
	ptr2 = nih_alloc (ptr1, 10);
	memset (ptr2, 'x', 10);

	TEST_ALLOC_SIZE (ptr2, 10);
	TEST_ALLOC_PARENT (ptr2, ptr1);

	nih_free (ptr1);


	/* Check that nih_alloc returns NULL if allocation fails. */
	TEST_FEATURE ("with failed allocation");
	__nih_malloc = malloc_null;
	ptr1 = nih_new (NULL, int);
	__nih_malloc = malloc;

	TEST_EQ_P (ptr1, NULL);
}
예제 #3
0
파일: cgmanager.c 프로젝트: MSylvia/koding
static char *try_get_abs_cgroup(const char *name, const char *lxcpath,
		const char *controller)
{
	char *cgroup = NULL;

	if (abs_cgroup_supported()) {
		/* get the container init pid and ask for its abs cgroup */
		pid_t pid = lxc_cmd_get_init_pid(name, lxcpath);
		if (pid < 0)
			return NULL;
		if (cgmanager_get_pid_cgroup_abs_sync(NULL, cgroup_manager,
				controller, pid, &cgroup) != 0) {
			cgroup = NULL;
			NihError *nerr;
			nerr = nih_error_get();
			nih_free(nerr);
		}
		return cgroup;
	}

	/* use the command interface to look for the cgroup */
	return lxc_cmd_get_cgroup_path(name, lxcpath, controller);
}
예제 #4
0
void
test_array_new (void)
{
    char **array;

    /* Check that we can allocate a NULL-terminated array of strings using
     * nih_alloc().
     */
    TEST_FUNCTION ("nih_str_array_new");
    TEST_ALLOC_FAIL {
        array = nih_str_array_new (NULL);

        if (test_alloc_failed) {
            TEST_EQ_P (array, NULL);
            continue;
        }

        TEST_ALLOC_SIZE (array, sizeof (char *));
        TEST_EQ_P (array[0], NULL);

        nih_free (array);
    }
}
예제 #5
0
/*
 * Escape to the root cgroup if we are root, so that the container will
 * be in "/lxc/c1" rather than "/user/..../c1"
 * called internally with connection already open
 */
static bool cgm_escape(void)
{
	bool ret = true, cgm_needs_disconnect = false;
	pid_t me = getpid();
	char **slist = subsystems;
	int i;

	if (!cgroup_manager) {
		if (!cgm_dbus_connect()) {
			ERROR("Error connecting to cgroup manager");
			return false;
		}
		cgm_needs_disconnect = true;
	}


	if (cgm_all_controllers_same)
		slist = subsystems_inone;

	for (i = 0; slist[i]; i++) {
		if (cgmanager_move_pid_abs_sync(NULL, cgroup_manager,
					slist[i], "/", me) != 0) {
			NihError *nerr;
			nerr = nih_error_get();
			ERROR("call to cgmanager_move_pid_abs_sync(%s) failed: %s",
					slist[i], nerr->message);
			nih_free(nerr);
			ret = false;
			break;
		}
	}

	if (cgm_needs_disconnect)
		cgm_dbus_disconnect();

	return ret;
}
예제 #6
0
파일: session.c 프로젝트: pexip/os-upstart
/**
 * session_new:
 * @parent: parent,
 * @chroot: full chroot path,
 * @user: user id.
 *
 * Create a new session.
 *
 * Return new Session, or NULL on error.
 **/
Session *
session_new (const void *parent,
	     const char *chroot,
	     uid_t       user)
{
	Session *session;

	nih_assert ((chroot != NULL) || (user != 0));

	session_init ();

	session = nih_new (parent, Session);
	if (! session)
		return NULL;

	nih_list_init (&session->entry);

	if (chroot) {
		session->chroot = nih_strdup (session, chroot);
		if (! session->chroot) {
			nih_free (session);
			return NULL;
		}
	} else {
		session->chroot = NULL;
	}

	session->user = user;

	session->conf_path = NULL;

	nih_alloc_set_destructor (session, nih_list_destroy);

	nih_list_add (sessions, &session->entry);

	return session;
}
예제 #7
0
/**
 * fgets_alloc:
 * @stream: stdio stream to read from.
 *
 * Reads from stream up to EOF or a newline, without any line-length
 * limitations.
 *
 * Returns: static string containing the entire line WITHOUT the
 * terminating newline, or NULL if end of file is reached and nothing
 * was read.
 **/
char *
fgets_alloc (const void *parent,
	     FILE *      stream)
{
        char * buf = NULL;
        size_t buf_sz = 0;
	size_t buf_len = 0;

	nih_assert (stream != NULL);

	for (;;) {
		char *ret;
		char *pos;

		if (buf_sz <= (buf_len + 1)) {
			buf_sz += 4096;
			buf = NIH_MUST (nih_realloc (buf, parent, buf_sz));
		}

		ret = fgets (buf + buf_len, buf_sz - buf_len, stream);
		if ((! ret) && (! buf_len)) {
			nih_free (buf);
			return NULL;
		} else if (! ret) {
			return buf;
		}

		buf_len += strlen (ret);
		pos = strchr (ret, '\n');
		if (pos) {
			*pos = '\0';
			break;
		}
	}

	return buf;
}
예제 #8
0
bool cgm_remove(const char *controller, const char *cg)
{
	/*
	 * tempting to make remove be recursive, but this is a filesystem,
	 * so best to opt for least surprise
	 */
	int32_t r = 0, e;

	if (!cgm_dbus_connect()) {
		return false;
	}

	if ( cgmanager_remove_sync(NULL, cgroup_manager, controller, cg, r, &e) != 0) {
		NihError *nerr;
		nerr = nih_error_get();
		fprintf(stderr, "call to remove (%s:%s) failed: %s\n", controller, cg, nerr->message);
		nih_free(nerr);
		cgm_dbus_disconnect();
		return false;
	}

	cgm_dbus_disconnect();
	return true;
}
예제 #9
0
void
test_new (void)
{
	void *ptr1;
	void *ptr2;

	TEST_FUNCTION ("nih_new");

	/* Check that nih_new works if we don't give it a parent, the block
	 * should be allocated with the size of the type given.
	 */
	TEST_FEATURE ("with no parent");
	ptr1 = nih_new (NULL, int);

	TEST_ALLOC_SIZE (ptr1, sizeof (int));
	TEST_ALLOC_PARENT (ptr1, NULL);


	/* Check that nih_new works if we do give a parent. */
	TEST_FEATURE ("with parent");
	ptr2 = nih_new (ptr1, char);

	TEST_ALLOC_SIZE (ptr2, sizeof (char));
	TEST_ALLOC_PARENT (ptr2, ptr1);

	nih_free (ptr1);


	/* Check that nih_new returns NULL if allocation fails. */
	TEST_FEATURE ("with failed allocation");
	__nih_malloc = malloc_null;
	ptr1 = nih_new (NULL, int);
	__nih_malloc = malloc;

	TEST_EQ_P (ptr1, NULL);
}
예제 #10
0
/* unfreeze is called by the command api after killing a container.  */
static bool cgm_unfreeze(void *hdata)
{
	struct cgm_data *d = hdata;
	bool ret = true;

	if (!d || !d->cgroup_path)
		return false;

	if (!cgm_dbus_connect()) {
		ERROR("Error connecting to cgroup manager");
		return false;
	}
	if (cgmanager_set_value_sync(NULL, cgroup_manager, "freezer", d->cgroup_path,
			"freezer.state", "THAWED") != 0) {
		NihError *nerr;
		nerr = nih_error_get();
		ERROR("call to cgmanager_set_value_sync failed: %s", nerr->message);
		nih_free(nerr);
		ERROR("Error unfreezing %s", d->cgroup_path);
		ret = false;
	}
	cgm_dbus_disconnect();
	return ret;
}
예제 #11
0
static int do_chown_cgroup(const char *controller, const char *cgroup_path,
		uid_t newuid)
{
	int sv[2] = {-1, -1}, optval = 1, ret = -1;
	char buf[1];
	struct pollfd fds;

	if (socketpair(AF_UNIX, SOCK_DGRAM, 0, sv) < 0) {
		SYSERROR("Error creating socketpair");
		goto out;
	}
	if (setsockopt(sv[1], SOL_SOCKET, SO_PASSCRED, &optval, sizeof(optval)) == -1) {
		SYSERROR("setsockopt failed");
		goto out;
	}
	if (setsockopt(sv[0], SOL_SOCKET, SO_PASSCRED, &optval, sizeof(optval)) == -1) {
		SYSERROR("setsockopt failed");
		goto out;
	}
	if ( cgmanager_chown_scm_sync(NULL, cgroup_manager, controller,
				       cgroup_path, sv[1]) != 0) {
		NihError *nerr;
		nerr = nih_error_get();
		ERROR("call to cgmanager_chown_scm_sync failed: %s", nerr->message);
		nih_free(nerr);
		goto out;
	}
	/* now send credentials */

	fds.fd = sv[0];
	fds.events = POLLIN;
	fds.revents = 0;
	if (poll(&fds, 1, -1) <= 0) {
		ERROR("Error getting go-ahead from server: %s", strerror(errno));
		goto out;
	}
	if (read(sv[0], &buf, 1) != 1) {
		ERROR("Error getting reply from server over socketpair");
		goto out;
	}
	if (send_creds(sv[0], getpid(), getuid(), getgid())) {
		SYSERROR("%s: Error sending pid over SCM_CREDENTIAL", __func__);
		goto out;
	}
	fds.fd = sv[0];
	fds.events = POLLIN;
	fds.revents = 0;
	if (poll(&fds, 1, -1) <= 0) {
		ERROR("Error getting go-ahead from server: %s", strerror(errno));
		goto out;
	}
	if (read(sv[0], &buf, 1) != 1) {
		ERROR("Error getting reply from server over socketpair");
		goto out;
	}
	if (send_creds(sv[0], getpid(), newuid, 0)) {
		SYSERROR("%s: Error sending pid over SCM_CREDENTIAL", __func__);
		goto out;
	}
	fds.fd = sv[0];
	fds.events = POLLIN;
	fds.revents = 0;
	if (poll(&fds, 1, -1) <= 0) {
		ERROR("Error getting go-ahead from server: %s", strerror(errno));
		goto out;
	}
	ret = read(sv[0], buf, 1);
out:
	close(sv[0]);
	close(sv[1]);
	if (ret == 1 && *buf == '1')
		return 0;
	return -1;
}
예제 #12
0
int setup_proxy(void)
{
	bool exists_upper = false, exists_lower = false;
	NihError *err;

	/* When running in container, /sys/fs/cgroup will have been
	   already mounted.  But it may be ro */
	if (is_ro_mount(CGDIR))
		turn_mount_rw(CGDIR);

	/*
	 * If /sys/fs/cgroup/cgmanager.lower exists,
	 *    if /sys/fs/cgroup/cgmanager exists, then exit (proxy already running)
	 *    start up, connect to .lower
	 * else
	 *    if /sys/fs/cgroup/cgmanager exists, move it to /sys/fs/cgroup/cgmanager.lower
	 *    start up and connect to .lower
	 */
	server_conn = nih_dbus_connect(CGMANAGER_DBUS_PATH, NULL);
	if (server_conn) {
		exists_upper = true;
		dbus_connection_unref (server_conn);
	} else {
		err = nih_error_get();
		nih_free(err);
	}
	server_conn = nih_dbus_connect(CGPROXY_DBUS_PATH, cgm_dbus_disconnected);
	if (server_conn) {
		exists_lower = true;
	} else {
		err = nih_error_get();
		nih_free(err);
	}
	if (exists_upper && exists_lower) {
		dbus_connection_unref (server_conn);
		nih_fatal("proxy already running");
		return -1;  // proxy already running
	}
	if (exists_lower)
		// we've got the sock we need, all set.
		return 0;
	if (exists_upper) {
		//move /sys/fs/cgroup/cgmanager to /sys/fs/cgroup/cgmanager.lower
		if (mkdir(CGPROXY_DIR, 0755) < 0 && errno != EEXIST) {
			nih_fatal("failed to create lower sock");
			return -1;
		}
		if (mount(CGMANAGER_DIR, CGPROXY_DIR, "none", MS_MOVE, 0) < 0) {
			/* it wasn't a mount, meaning we are at the host
			 * level on an old kernel.  So rename it */
			if (unlink(CGPROXY_SOCK) && errno != ENOENT)
				nih_warn("failed to remove %s: %s", CGPROXY_SOCK,
					strerror(errno));
			if (rmdir(CGPROXY_DIR) && errno != ENOENT)
				nih_warn("failed to remove %s: %s", CGPROXY_DIR,
					strerror(errno));
			if (rename(CGMANAGER_DIR, CGPROXY_DIR) < 0) {
				nih_fatal("unable to rename the socket");
				return -1;
			}
			if (mkdir(CGMANAGER_DIR, 0755) < 0) {
				nih_fatal("unable to create socket dir");
				return -1;
			}
		}
	}
	server_conn = nih_dbus_connect(CGPROXY_DBUS_PATH, cgm_dbus_disconnected);
	if (!server_conn) {
		err = nih_error_get();
		nih_fatal("Failed to open connection to %s: %s",
				CGPROXY_DBUS_PATH, err->message);
		nih_free(err);

		return -1;
	}
	return 0;
}
예제 #13
0
static void do_cgm_set(const char *name, const char *lxcpath, const char *filename, const char *value, int outp)
{
	char *controller, *key, *cgroup = NULL;
	int retval = 0;  // value we are sending to the parent over outp
	int ret;
	char *cglast;

	controller = alloca(strlen(filename)+1);
	strcpy(controller, filename);
	key = strchr(controller, '.');
	if (!key) {
		ret = write(outp, &retval, sizeof(retval));
		if (ret != sizeof(retval))
			WARN("Failed to warn cgm_set of error; parent may hang");
		exit(1);
	}
	*key = '\0';

	if (!cgm_dbus_connect()) {
		ERROR("Error connecting to cgroup manager");
		ret = write(outp, &retval, sizeof(retval));
		if (ret != sizeof(retval))
			WARN("Failed to warn cgm_set of error; parent may hang");
		exit(1);
	}
	cgroup = try_get_abs_cgroup(name, lxcpath, controller);
	if (!cgroup) {
		cgm_dbus_disconnect();
		ret = write(outp, &retval, sizeof(retval));
		if (ret != sizeof(retval))
			WARN("Failed to warn cgm_set of error; parent may hang");
		exit(1);
	}
	cglast = strrchr(cgroup, '/');
	if (!cglast) {
		cgm_dbus_disconnect();
		free_abs_cgroup(cgroup);
		ret = write(outp, &retval, sizeof(retval));
		if (ret != sizeof(retval))
			WARN("Failed to warn cgm_set of error; parent may hang");
		exit(1);
	}
	*cglast = '\0';
	if (!lxc_cgmanager_enter(getpid(), controller, cgroup, abs_cgroup_supported())) {
		ERROR("Failed to enter container cgroup %s:%s", controller, cgroup);
		ret = write(outp, &retval, sizeof(retval));
		if (ret != sizeof(retval))
			WARN("Failed to warn cgm_set of error; parent may hang");
		cgm_dbus_disconnect();
		free_abs_cgroup(cgroup);
		exit(1);
	}
	if (cgmanager_set_value_sync(NULL, cgroup_manager, controller, cglast+1, filename, value) != 0) {
		NihError *nerr;
		nerr = nih_error_get();
		ERROR("Error setting cgroup value %s for %s:%s", filename, controller, cgroup);
		ERROR("call to cgmanager_set_value_sync failed: %s", nerr->message);
		nih_free(nerr);
		free_abs_cgroup(cgroup);
		cgm_dbus_disconnect();
		ret = write(outp, &retval, sizeof(retval));
		if (ret != sizeof(retval))
			WARN("Failed to warn cgm_set of error; parent may hang");
		exit(1);
	}
	free_abs_cgroup(cgroup);
	cgm_dbus_disconnect();
	/* tell parent that we are done */
	retval = 1;
	ret = write(outp, &retval, sizeof(retval));
	if (ret != sizeof(retval)) {
		exit(1);
	}
	exit(0);
}
예제 #14
0
int
main (int   argc,
      char *argv[])
{
	char **args;
	int    runlevel;
	int    ret;

	nih_main_init (argv[0]);

	nih_option_set_usage ("RUNLEVEL");
	nih_option_set_synopsis (_("Change runlevel."));
	nih_option_set_help (
		_("RUNLEVEL should be one of 0123456sS, where s and S are "
		  "considered identical.\n"
		  "\n"
		  "RUNLEVEL may also be Q or q to instruct the init daemon "
		  "to reload its configuration, this is rarely necessary "
		  "since the daemon watches its configuration for changes.\n"
		  "\n"
		  "RUNLEVEL may be U or u to instruct the init daemon to "
		  "re-execute itself, this is not recommended since Upstart "
		  "does not currently preserve its state.\n"));

	args = nih_option_parser (NULL, argc, argv, options, FALSE);
	if (! args)
		exit (1);

	/* First argument must be a single character we know */
	if (! args[0]) {
		fprintf (stderr, _("%s: missing runlevel\n"), program_name);
		nih_main_suggest_help ();
		exit (1);
	}
	if ((! strchr ("0123456SsQqUu", args[0][0])) || args[0][1]) {
		fprintf (stderr, _("%s: illegal runlevel: %s\n"),
			 program_name, args[0]);
		nih_main_suggest_help ();
		exit (1);
	}

	/* Check we're root */
	setuid (geteuid ());
	if (getuid ()) {
		nih_fatal (_("Need to be root"));
		exit (1);
	}

	/* Send the appropriate message */
	runlevel = args[0][0];

	switch (runlevel) {
	case '0':
	case '1':
	case '2':
	case '3':
	case '4':
	case '5':
	case '6':
		ret = sysv_change_runlevel (runlevel, extra_env, NULL, NULL);
		break;
	case 'S':
	case 's':
		ret = sysv_change_runlevel ('S', extra_env, NULL, NULL);
		break;
	case 'Q':
	case 'q':
		ret = kill (1, SIGHUP);
		if (ret < 0)
			nih_error_raise_system ();
		break;
	case 'U':
	case 'u':
		ret = kill (1, SIGTERM);
		if (ret < 0)
			nih_error_raise_system ();
		break;
	default:
		nih_assert_not_reached ();
	}

	if (ret < 0) {
		NihError *err;

		err = nih_error_get ();
		nih_error ("%s", err->message);
		nih_free (err);

		exit (1);
	}

	return 0;
}
예제 #15
0
void
test_cgroup_setting_new (void)
{
	CGroupSetting   *setting;
	nih_local char  *parent= NULL;

	parent = nih_strdup (NULL, "a parent object");
	TEST_NE_P (parent, NULL);

	TEST_FUNCTION ("cgroup_setting_new");

	TEST_FEATURE ("no parent, key, no value");
	TEST_ALLOC_FAIL {
		setting = cgroup_setting_new (NULL, "foo", NULL);

		if (test_alloc_failed) {
			TEST_EQ_P (setting, NULL);
			continue;
		}

		TEST_ALLOC_SIZE (setting, sizeof (CGroupSetting));
		TEST_ALLOC_PARENT (setting, NULL);
		TEST_EQ_STR (setting->key, "foo");
		TEST_ALLOC_SIZE (setting->key, 1+strlen ("foo"));
		TEST_ALLOC_PARENT (setting->key, setting);

		nih_free (setting);
	}

	TEST_FEATURE ("parent, key, no value");
	TEST_ALLOC_FAIL {
		setting = cgroup_setting_new (parent, "hello world", NULL);

		if (test_alloc_failed) {
			TEST_EQ_P (setting, NULL);
			continue;
		}

		TEST_ALLOC_SIZE (setting, sizeof (CGroupSetting));
		TEST_ALLOC_PARENT (setting, parent);
		TEST_EQ_STR (setting->key, "hello world");
		TEST_ALLOC_SIZE (setting->key, 1+strlen ("hello world"));
		TEST_ALLOC_PARENT (setting->key, setting);

		nih_free (setting);
	}

	TEST_FEATURE ("no parent, key, value");
	TEST_ALLOC_FAIL {
		setting = cgroup_setting_new (NULL, "hello world", "a value");

		if (test_alloc_failed) {
			TEST_EQ_P (setting, NULL);
			continue;
		}

		TEST_ALLOC_SIZE (setting, sizeof (CGroupSetting));
		TEST_ALLOC_PARENT (setting, NULL);

		TEST_EQ_STR (setting->key, "hello world");
		TEST_ALLOC_SIZE (setting->key, 1+strlen ("hello world"));
		TEST_ALLOC_PARENT (setting->key, setting);

		TEST_EQ_STR (setting->value, "a value");
		TEST_ALLOC_SIZE (setting->value, 1+strlen ("a value"));
		TEST_ALLOC_PARENT (setting->value, setting);

		nih_free (setting);
	}

	TEST_FEATURE ("parent, key, value");
	TEST_ALLOC_FAIL {
		setting = cgroup_setting_new (parent, "hello world", "a value");

		if (test_alloc_failed) {
			TEST_EQ_P (setting, NULL);
			continue;
		}

		TEST_ALLOC_SIZE (setting, sizeof (CGroupSetting));
		TEST_ALLOC_PARENT (setting, parent);

		TEST_EQ_STR (setting->key, "hello world");
		TEST_ALLOC_SIZE (setting->key, 1+strlen ("hello world"));
		TEST_ALLOC_PARENT (setting->key, setting);

		TEST_EQ_STR (setting->value, "a value");
		TEST_ALLOC_SIZE (setting->value, 1+strlen ("a value"));
		TEST_ALLOC_PARENT (setting->value, setting);

		nih_free (setting);
	}
}
예제 #16
0
void
test_discard (void)
{
	void *ptr1;
	void *ptr2;
	int   ret;

	TEST_FUNCTION ("nih_discard");

	/* Check that nih_discard works if the block has no parent, freeing
	 * the object.  The destructor should get called and nih_discard
	 * should return that return value.
	 */
	TEST_FEATURE ("with no parent");
	ptr1 = nih_alloc (NULL, 10);
	nih_alloc_set_destructor (ptr1, destructor_called);
	destructor_was_called = 0;
	ret = nih_discard (ptr1);

	TEST_TRUE (destructor_was_called);
	TEST_EQ (ret, 2);


	/* Check that nih_discard does nothing it the block has a parent.
	 */
	TEST_FEATURE ("with parent");
	ptr2 = nih_alloc (NULL, 20);

	ptr1 = nih_alloc (ptr2, 10);
	nih_alloc_set_destructor (ptr1, destructor_called);
	destructor_was_called = 0;
	ret = nih_discard (ptr1);

	TEST_FALSE (destructor_was_called);
	TEST_EQ (ret, 0);

	nih_free (ptr2);


	/* Check that the destructor on any children also gets called, which
	 * is as good a indication as any that the children are being freed.
	 */
	TEST_FEATURE ("with destructor on child");
	ptr1 = nih_alloc (NULL, 10);
	ptr2 = nih_alloc (ptr1, 10);
	nih_alloc_set_destructor (ptr2, child_destructor_called);
	child_destructor_was_called = 0;
	ret = nih_discard (ptr1);

	TEST_TRUE (child_destructor_was_called);
	TEST_EQ (ret, 0);


	/* Check that both destructors on parent and children are called,
	 * and that the return value from nih_discard is that of the parent's.
	 */
	TEST_FEATURE ("with child and destructors");
	ptr1 = nih_alloc (NULL, 10);
	ptr2 = nih_alloc (ptr1, 10);
	nih_alloc_set_destructor (ptr1, destructor_called);
	nih_alloc_set_destructor (ptr2, child_destructor_called);
	destructor_was_called = 0;
	child_destructor_was_called = 0;
	ret = nih_discard (ptr1);

	TEST_TRUE (destructor_was_called);
	TEST_TRUE (child_destructor_was_called);
	TEST_EQ (ret, 2);
}
예제 #17
0
void
test_append (void)
{
	char   **env = NULL, **new_env, **ret;
	size_t   len = 0;

	TEST_FUNCTION ("environ_append");

	/* Check that we can append all new entries onto the end of an
	 * existing environment table, without modifying the entries passed.
	 */
	TEST_FEATURE ("with new entries");
	new_env = nih_str_array_new (NULL);
	assert (environ_add (&new_env, NULL, NULL, TRUE, "MILK=white"));
	assert (environ_add (&new_env, NULL, NULL, TRUE, "TEA=green"));

	TEST_ALLOC_FAIL {
		TEST_ALLOC_SAFE {
			len = 0;
			env = nih_str_array_new (NULL);
			assert (environ_add (&env, NULL, &len, TRUE, "FOO=BAR"));
			assert (environ_add (&env, NULL, &len, TRUE, "BAR=BAZ"));
		}

		ret = environ_append (&env, NULL, &len, TRUE, new_env);

		if (test_alloc_failed) {
			TEST_EQ_P (ret, NULL);
			nih_free (env);
			continue;
		}

		TEST_EQ_P (ret, env);
		TEST_EQ (len, 4);

		TEST_EQ_STR (env[0], "FOO=BAR");
		TEST_EQ_STR (env[1], "BAR=BAZ");
		TEST_EQ_STR (env[2], "MILK=white");
		TEST_EQ_STR (env[3], "TEA=green");
		TEST_EQ_P (env[4], NULL);

		nih_free (env);
	}

	nih_free (new_env);


	/* Check that if entries are being replaced, those values from the
	 * new table replace the values in the old table.
	 */
	TEST_FEATURE ("with replacement entries");
	new_env = nih_str_array_new (NULL);
	assert (environ_add (&new_env, NULL, NULL, TRUE, "MILK=white"));
	assert (environ_add (&new_env, NULL, NULL, TRUE, "TEA=green"));
	assert (environ_add (&new_env, NULL, NULL, TRUE, "FOO=apricot"));

	TEST_ALLOC_FAIL {
		TEST_ALLOC_SAFE {
			len = 0;
			env = nih_str_array_new (NULL);
			assert (environ_add (&env, NULL, &len, TRUE, "FOO=BAR"));
			assert (environ_add (&env, NULL, &len, TRUE, "BAR=BAZ"));
		}

		ret = environ_append (&env, NULL, &len, TRUE, new_env);

		if (test_alloc_failed) {
			TEST_EQ_P (ret, NULL);
			nih_free (env);
			continue;
		}

		TEST_EQ_P (ret, env);
		TEST_EQ (len, 4);

		TEST_EQ_STR (env[0], "FOO=apricot");
		TEST_EQ_STR (env[1], "BAR=BAZ");
		TEST_EQ_STR (env[2], "MILK=white");
		TEST_EQ_STR (env[3], "TEA=green");
		TEST_EQ_P (env[4], NULL);

		nih_free (env);
	}

	nih_free (new_env);


	/* Check that if entries are being preserved, those values from the
	 * new table are ignored.
	 */
	TEST_FEATURE ("with preserve existing entries");
	new_env = nih_str_array_new (NULL);
	assert (environ_add (&new_env, NULL, NULL, TRUE, "MILK=white"));
	assert (environ_add (&new_env, NULL, NULL, TRUE, "TEA=green"));
	assert (environ_add (&new_env, NULL, NULL, TRUE, "FOO=apricot"));

	TEST_ALLOC_FAIL {
		TEST_ALLOC_SAFE {
			len = 0;
			env = nih_str_array_new (NULL);
			assert (environ_add (&env, NULL, &len, TRUE, "FOO=BAR"));
			assert (environ_add (&env, NULL, &len, TRUE, "BAR=BAZ"));
		}

		ret = environ_append (&env, NULL, &len, FALSE, new_env);

		if (test_alloc_failed) {
			TEST_EQ_P (ret, NULL);
			nih_free (env);
			continue;
		}

		TEST_EQ_P (ret, env);
		TEST_EQ (len, 4);

		TEST_EQ_STR (env[0], "FOO=BAR");
		TEST_EQ_STR (env[1], "BAR=BAZ");
		TEST_EQ_STR (env[2], "MILK=white");
		TEST_EQ_STR (env[3], "TEA=green");
		TEST_EQ_P (env[4], NULL);

		nih_free (env);
	}

	nih_free (new_env);
}
예제 #18
0
void
test_free (void)
{
	void *  ptr1;
	void *  ptr2;
	Parent *parent;
	int     ret;

	TEST_FUNCTION ("nih_free");

	/* Check that nih_free works if the block has no parent.  The
	 * destructor should get called and nih_free should return that
	 * return value.
	 */
	TEST_FEATURE ("with no parent");
	ptr1 = nih_alloc (NULL, 10);
	nih_alloc_set_destructor (ptr1, destructor_called);
	destructor_was_called = 0;
	ret = nih_free (ptr1);

	TEST_TRUE (destructor_was_called);
	TEST_EQ (ret, 2);


	/* Check that nih_free works if the block has a parent.  The
	 * destructor should get called and nih_free should return that
	 * return value.
	 */
	TEST_FEATURE ("with parent");
	ptr2 = nih_alloc (NULL, 20);

	ptr1 = nih_alloc (ptr2, 10);
	nih_alloc_set_destructor (ptr1, destructor_called);
	destructor_was_called = 0;
	ret = nih_free (ptr1);

	TEST_TRUE (destructor_was_called);
	TEST_EQ (ret, 2);

	nih_free (ptr2);


	/* Check that the destructor on any children also gets called, which
	 * is as good a indication as any that the children are being freed.
	 */
	TEST_FEATURE ("with destructor on child");
	ptr1 = nih_alloc (NULL, 10);
	ptr2 = nih_alloc (ptr1, 10);
	nih_alloc_set_destructor (ptr2, child_destructor_called);
	child_destructor_was_called = 0;
	ret = nih_free (ptr1);

	TEST_TRUE (child_destructor_was_called);
	TEST_EQ (ret, 0);


	/* Check that both destructors on parent and children are called,
	 * and that the return value from nih_free is that of the parent's.
	 */
	TEST_FEATURE ("with child and destructors");
	ptr1 = nih_alloc (NULL, 10);
	ptr2 = nih_alloc (ptr1, 10);
	nih_alloc_set_destructor (ptr1, destructor_called);
	nih_alloc_set_destructor (ptr2, child_destructor_called);
	destructor_was_called = 0;
	child_destructor_was_called = 0;
	ret = nih_free (ptr1);

	TEST_TRUE (destructor_was_called);
	TEST_TRUE (child_destructor_was_called);
	TEST_EQ (ret, 2);


	/* Check that a child of an object may be included in a sibling
	 * linked list allocated earlier.  At the point the child destructor
	 * is called, the sibling must not have been freed otherwise it
	 * cannot cut itself out.
	 */
	TEST_FEATURE ("with child in older sibling list");
	parent = nih_new (NULL, Parent);

	__nih_malloc = my_list_head_malloc;
	parent->list = nih_new (parent, NihList);
	nih_list_init (parent->list);
	__nih_malloc = malloc;

	parent->child = nih_new (parent, Child);
	nih_list_init (&parent->child->entry);

	nih_list_add (parent->list, &parent->child->entry);
	nih_alloc_set_destructor (parent->child, child_destructor_test);

	__nih_free = my_list_head_free;
	nih_free (parent);
	__nih_free = free;


	/* Check that a child of an object may be included in a sibling
	 * linked list allocated later.  At the point the child destructor
	 * is called, the sibling must not have been freed otherwise it
	 * cannot cut itself out.
	 */
	TEST_FEATURE ("with child in younger sibling list");
	parent = nih_new (NULL, Parent);

	parent->child = nih_new (parent, Child);
	nih_list_init (&parent->child->entry);

	__nih_malloc = my_list_head_malloc;
	parent->list = nih_new (parent, NihList);
	nih_list_init (parent->list);
	__nih_malloc = malloc;

	nih_list_add (parent->list, &parent->child->entry);
	nih_alloc_set_destructor (parent->child, child_destructor_test);

	__nih_free = my_list_head_free;
	nih_free (parent);
	__nih_free = free;
}
예제 #19
0
void
test_sentinel (void)
{
	char *str;

	TEST_FUNCTION ("output_sentinel");

	/* Check that a header file sentinel is correctly generated for a
	 * local path.
	 */
	TEST_FEATURE ("with local path");
	TEST_ALLOC_FAIL {
		str = output_sentinel (NULL, "foo.h");

		if (test_alloc_failed) {
			TEST_EQ_P (str, NULL);
			continue;
		}

		TEST_EQ_STR (str, "TEST_FOO_H");

		nih_free (str);
	}


	/* Check that a header file sentinel is correctly generated for a
	 * sub-directory path.
	 */
	TEST_FEATURE ("with sub-directory path");
	TEST_ALLOC_FAIL {
		str = output_sentinel (NULL, "path/to/foo.h");

		if (test_alloc_failed) {
			TEST_EQ_P (str, NULL);
			continue;
		}

		TEST_EQ_STR (str, "TEST_PATH_TO_FOO_H");

		nih_free (str);
	}


	/* Check that a header file sentinel is generated for an absolute
	 * path; we might want to change the format of this later, but it's
	 * ok for now.
	 */
	TEST_FEATURE ("with absolute path");
	TEST_ALLOC_FAIL {
		str = output_sentinel (NULL, "/path/to/foo.h");

		if (test_alloc_failed) {
			TEST_EQ_P (str, NULL);
			continue;
		}

		TEST_EQ_STR (str, "TEST__PATH_TO_FOO_H");

		nih_free (str);
	}
}
예제 #20
0
void
test_realloc (void)
{
	void *ptr1;
	void *ptr2;
	void *ptr3;

	TEST_FUNCTION ("nih_realloc");

	/* Check that nih_realloc behaves like nih_alloc if the pointer is
	 * NULL (it should, in fact, just call it)
	 */
	TEST_FEATURE ("as nih_alloc");
	ptr1 = nih_realloc (NULL, NULL, 4096);
	memset (ptr1, 'x', 4096);

	TEST_ALLOC_SIZE (ptr1, 4096);
	TEST_ALLOC_PARENT (ptr1, NULL);

	nih_free (ptr1);


	/* Check that nih_realloc works if the block doesn't have a parent.
	 */
	TEST_FEATURE ("with no parent");
	ptr1 = nih_alloc (NULL, 4096);
	memset (ptr1, 'x', 4096);

	ptr1 = nih_realloc (ptr1, NULL, 8096);
	memset (ptr1, 'x', 8096);

	TEST_ALLOC_SIZE (ptr1, 8096);
	TEST_ALLOC_PARENT (ptr1, NULL);


	/* Check that nih_realloc works if the block has a parent, the size
	 * should change but the parent should remain the same.
	 */
	TEST_FEATURE ("with a parent");
	ptr2 = nih_alloc (ptr1, 5);
	memset (ptr2, 'x', 5);

	ptr2 = nih_realloc (ptr2, ptr1, 10);
	memset (ptr2, 'x', 10);

	TEST_ALLOC_SIZE (ptr2, 10);
	TEST_ALLOC_PARENT (ptr2, ptr1);

	nih_free (ptr1);


	/* Check that nih_realloc works if the block being reallocated has
	 * a child.  This is fiddly as they need their parent pointers
	 * adjusted.
	 */
	TEST_FEATURE ("with a child");
	ptr1 = nih_alloc (NULL, 128);
	memset (ptr1, 'x', 128);

	ptr2 = nih_alloc (ptr1, 512);
	memset (ptr2, 'x', 512);

	ptr3 = nih_realloc (ptr1, NULL, 1024);
	memset (ptr3, 'x', 1024);

	TEST_ALLOC_PARENT (ptr2, ptr3);

	nih_free (ptr3);


	/* Check that nih_realloc returns NULL and doesn't alter the block
	 * if the allocator fails.
	 */
	TEST_FEATURE ("with failing realloc");
	ptr1 = nih_alloc (NULL, 10);
	assert (ptr1);
	memset (ptr1, 'x', 10);

	__nih_realloc = realloc_null;
	ptr2 = nih_realloc (ptr1, NULL, 200);
	__nih_realloc = realloc;

	TEST_EQ_P (ptr2, NULL);
	TEST_ALLOC_SIZE (ptr1, 10);

	nih_free (ptr1);
}
예제 #21
0
void
test_cgroup_new (void)
{
	nih_local char *parent = NULL;
	CGroup         *cgroup;

	TEST_FUNCTION ("cgroup_new");

	parent = nih_strdup (NULL, "a parent object");
	TEST_NE_P (parent, NULL);

	TEST_FEATURE ("no parent, controller");
	TEST_ALLOC_FAIL {

		cgroup = cgroup_new (NULL, "cpuset");

		if (test_alloc_failed) {
			TEST_EQ_P (cgroup, NULL);
			continue;
		}

		TEST_NE_P (cgroup, NULL);

		TEST_ALLOC_SIZE (cgroup, sizeof (CGroup));

		TEST_ALLOC_PARENT (cgroup, NULL);

		TEST_EQ_STR (cgroup->controller, "cpuset");
		TEST_ALLOC_SIZE (cgroup->controller, 1+strlen ("cpuset"));
		TEST_ALLOC_PARENT (cgroup->controller, cgroup);

		TEST_LIST_EMPTY (&cgroup->names);

		nih_free (cgroup);
	}

	TEST_FEATURE ("parent, controller");
	TEST_ALLOC_FAIL {

		cgroup = cgroup_new (parent, "perf_event");

		if (test_alloc_failed) {
			TEST_EQ_P (cgroup, NULL);
			continue;
		}

		TEST_NE_P (cgroup, NULL);

		TEST_ALLOC_SIZE (cgroup, sizeof (CGroup));

		TEST_ALLOC_PARENT (cgroup, parent);

		TEST_EQ_STR (cgroup->controller, "perf_event");
		TEST_ALLOC_SIZE (cgroup->controller, 1+strlen ("perf_event"));
		TEST_ALLOC_PARENT (cgroup->controller, cgroup);

		TEST_LIST_EMPTY (&cgroup->names);

		nih_free (cgroup);
	}
}
예제 #22
0
void
test_cgroup_job_start (void)
{
	char             confdir[PATH_MAX];
	char             logdir[PATH_MAX];
	char             flagfile[PATH_MAX];
	nih_local char  *cmd = NULL;
	pid_t            dbus_pid = 0;
	pid_t            upstart_pid = 0;
	char           **output;
	size_t           lines;
	size_t           len;
	nih_local char  *logfile = NULL;
	nih_local char  *logfile_name = NULL;
	nih_local char  *contents = NULL;

	if (geteuid ()) {
		printf ("INFO: skipping %s tests as not running as root\n", __func__);
		fflush (NULL);
		return;
	}

	TEST_GROUP ("cgroup manager handling");

        TEST_FILENAME (confdir);
        TEST_EQ (mkdir (confdir, 0755), 0);

        TEST_FILENAME (logdir);
        TEST_EQ (mkdir (logdir, 0755), 0);

        TEST_FILENAME (flagfile);

	/* Use the "secret" interface */
	TEST_EQ (setenv ("UPSTART_CONFDIR", confdir, 1), 0);
	TEST_EQ (setenv ("UPSTART_LOGDIR", logdir, 1), 0);

	TEST_DBUS (dbus_pid);

	/*******************************************************************/
	TEST_FEATURE ("Ensure startup job does not start until cgmanager available");

	contents = nih_sprintf (NULL, 
			"start on startup\n"
			"\n"
			"cgroup memory mem-%s\n"
			"\n"
			"exec echo hello\n",
			__func__);
	TEST_NE_P (contents, NULL);

	CREATE_FILE (confdir, "cgroup.conf", contents);

	logfile_name = NIH_MUST (nih_sprintf (NULL, "%s/%s",
				logdir,
				"cgroup.log"));

	start_upstart_common (&upstart_pid, FALSE, FALSE, confdir, logdir, NULL);

	cmd = nih_sprintf (NULL, "%s status %s 2>&1", get_initctl (), "cgroup");
	TEST_NE_P (cmd, NULL);
	RUN_COMMAND (NULL, cmd, &output, &lines);
	TEST_EQ (lines, 1);

	/* job should *NOT* start on startup */
	TEST_EQ_STR (output[0], "cgroup stop/waiting");
	nih_free (output);

	TEST_FALSE (file_exists (logfile_name));

	cmd = nih_sprintf (NULL, "%s notify-cgroup-manager-address %s 2>&1",
			get_initctl (),
			CGMANAGER_DBUS_SOCK);
	TEST_NE_P (cmd, NULL);
	RUN_COMMAND (NULL, cmd, &output, &lines);
	TEST_EQ (lines, 0);

	WAIT_FOR_FILE (logfile_name);

	logfile = nih_file_read (NULL, logfile_name, &len);
	TEST_NE_P (logfile, NULL);

	TEST_EQ_STR (logfile, "hello\r\n");

	DELETE_FILE (confdir, "cgroup.conf");
	assert0 (unlink (logfile_name));

	/*******************************************************************/
	TEST_FEATURE ("Ensure bogus cgroups don't crash init");

	contents = nih_sprintf (NULL, 
			"cgroup name\n"
			"\n"
			"exec echo hello\n");
	TEST_NE_P (contents, NULL);

	CREATE_FILE (confdir, "cgroup-name.conf", contents);

	logfile_name = NIH_MUST (nih_sprintf (NULL, "%s/%s",
				logdir,
				"cgroup-name.log"));

	cmd = nih_sprintf (NULL, "%s status %s 2>&1", get_initctl (), "cgroup-name");
	TEST_NE_P (cmd, NULL);
	RUN_COMMAND (NULL, cmd, &output, &lines);
	TEST_EQ (lines, 1);

	/* job is not running yet */
	TEST_EQ_STR (output[0], "cgroup-name stop/waiting");
	nih_free (output);

	TEST_FALSE (file_exists (logfile_name));

	cmd = nih_sprintf (NULL, "%s start %s 2>&1", get_initctl (), "cgroup-name");
	TEST_NE_P (cmd, NULL);
	RUN_COMMAND (NULL, cmd, &output, &lines);
	TEST_EQ (lines, 1);

        TEST_EQ_STR (output[0], "initctl: Job failed to start");

	DELETE_FILE (confdir, "cgroup-name.conf");
	/*******************************************************************/

	STOP_UPSTART (upstart_pid);
	TEST_DBUS_END (dbus_pid);

        TEST_EQ (rmdir (confdir), 0);
        TEST_EQ (rmdir (logdir), 0);

	/*******************************************************************/

}
예제 #23
0
static void do_cgm_get(const char *name, const char *lxcpath, const char *filename, int outp, bool sendvalue)
{
	char *controller, *key, *cgroup = NULL, *cglast;
	int len = -1;
	int ret;
	nih_local char *result = NULL;

	controller = alloca(strlen(filename)+1);
	strcpy(controller, filename);
	key = strchr(controller, '.');
	if (!key) {
		ret = write(outp, &len, sizeof(len));
		if (ret != sizeof(len))
			WARN("Failed to warn cgm_get of error; parent may hang");
		exit(1);
	}
	*key = '\0';

	if (!cgm_dbus_connect()) {
		ERROR("Error connecting to cgroup manager");
		ret = write(outp, &len, sizeof(len));
		if (ret != sizeof(len))
			WARN("Failed to warn cgm_get of error; parent may hang");
		exit(1);
	}
	cgroup = try_get_abs_cgroup(name, lxcpath, controller);
	if (!cgroup) {
		cgm_dbus_disconnect();
		ret = write(outp, &len, sizeof(len));
		if (ret != sizeof(len))
			WARN("Failed to warn cgm_get of error; parent may hang");
		exit(1);
	}
	cglast = strrchr(cgroup, '/');
	if (!cglast) {
		cgm_dbus_disconnect();
		free_abs_cgroup(cgroup);
		ret = write(outp, &len, sizeof(len));
		if (ret != sizeof(len))
			WARN("Failed to warn cgm_get of error; parent may hang");
		exit(1);
	}
	*cglast = '\0';
	if (!lxc_cgmanager_enter(getpid(), controller, cgroup, abs_cgroup_supported())) {
		ERROR("Failed to enter container cgroup %s:%s", controller, cgroup);
		ret = write(outp, &len, sizeof(len));
		if (ret != sizeof(len))
			WARN("Failed to warn cgm_get of error; parent may hang");
		cgm_dbus_disconnect();
		free_abs_cgroup(cgroup);
		exit(1);
	}
	if (cgmanager_get_value_sync(NULL, cgroup_manager, controller, cglast+1, filename, &result) != 0) {
		NihError *nerr;
		nerr = nih_error_get();
		nih_free(nerr);
		free_abs_cgroup(cgroup);
		cgm_dbus_disconnect();
		ret = write(outp, &len, sizeof(len));
		if (ret != sizeof(len))
			WARN("Failed to warn cgm_get of error; parent may hang");
		exit(1);
	}
	free_abs_cgroup(cgroup);
	cgm_dbus_disconnect();
	len = strlen(result);
	ret = write(outp, &len, sizeof(len));
	if (ret != sizeof(len)) {
		WARN("Failed to send length to parent");
		exit(1);
	}
	if (!len || !sendvalue) {
		exit(0);
	}
	ret = write(outp, result, len);
	if (ret < 0)
		exit(1);
	exit(0);
}
예제 #24
0
void
test_ref (void)
{
	void *ptr1;
	void *ptr2;
	void *ptr3;

	TEST_FUNCTION ("nih_ref");


	/* Check that we can add a reference to an object that has no
	 * parent, and this does not remove the NULL reference.
	 */
	TEST_FEATURE ("with no parent");
	ptr1 = nih_alloc (NULL, 100);
	memset (ptr1, 'x', 100);

	ptr2 = nih_alloc (NULL, 100);
	memset (ptr2, 'y', 100);

	nih_ref (ptr1, ptr2);

	TEST_ALLOC_PARENT (ptr1, ptr2);
	TEST_ALLOC_PARENT (ptr1, NULL);

	nih_free (ptr1);
	nih_free (ptr2);


	/* Check that we can add a reference to an object that already has
	 * a parent, and that both shall be parents afterwards.
	 */
	TEST_FEATURE ("with existing parent");
	ptr1 = nih_alloc (NULL, 100);
	memset (ptr1, 'x', 100);

	ptr2 = nih_alloc (ptr1, 100);
	memset (ptr2, 'y', 100);

	ptr3 = nih_alloc (NULL, 100);
	memset (ptr2, 'z', 100);

	nih_ref (ptr2, ptr3);

	TEST_ALLOC_PARENT (ptr2, ptr1);
	TEST_ALLOC_PARENT (ptr2, ptr3);

	nih_free (ptr1);
	nih_free (ptr3);


	/* Check that we can add a new NULL reference to an object that
	 * already has a parent, and that both shall be parents afterwards.
	 */
	TEST_FEATURE ("with existing parent and new NULL");
	ptr1 = nih_alloc (NULL, 100);
	memset (ptr1, 'x', 100);

	ptr2 = nih_alloc (ptr1, 100);
	memset (ptr2, 'y', 100);

	nih_ref (ptr2, NULL);

	TEST_ALLOC_PARENT (ptr2, ptr1);
	TEST_ALLOC_PARENT (ptr2, NULL);

	nih_free (ptr1);
	nih_free (ptr2);


	/* Check that we can add a second NULL reference to an object that
	 * already has one.
	 */
	TEST_FEATURE ("with additional NULL parent");
	ptr1 = nih_alloc (NULL, 100);
	memset (ptr1, 'x', 100);

	nih_ref (ptr1, NULL);

	TEST_ALLOC_PARENT (ptr1, NULL);

	nih_free (ptr1);


	/* Check that we can add a second reference to an object that already
	 * has a reference from the same parent.
	 */
	TEST_FEATURE ("with additional existing parent");
	ptr1 = nih_alloc (NULL, 100);
	memset (ptr1, 'x', 100);

	ptr2 = nih_alloc (ptr1, 100);
	memset (ptr2, 'y', 100);

	nih_ref (ptr2, ptr1);

	TEST_ALLOC_PARENT (ptr2, ptr1);

	nih_free (ptr2);
	nih_free (ptr1);
}
예제 #25
0
void
test_add (void)
{
	char   **env = NULL, **ret;
	size_t   len = 0;

	TEST_FUNCTION ("environ_add");

	/* Check that we can add a variable to a new environment table
	 * and that it is appended to the array.
	 */
	TEST_FEATURE ("with empty table");
	TEST_ALLOC_FAIL {
		TEST_ALLOC_SAFE {
			len = 0;
			env = nih_str_array_new (NULL);
		}

		ret = environ_add (&env, NULL, &len, TRUE, "FOO=BAR");

		if (test_alloc_failed) {
			TEST_EQ_P (ret, NULL);

			TEST_EQ (len, 0);
			TEST_EQ_P (env[0], NULL);

			nih_free (env);
			continue;
		}

		TEST_NE_P (ret, NULL);

		TEST_EQ (len, 1);
		TEST_ALLOC_PARENT (env[0], env);
		TEST_ALLOC_SIZE (env[0], 8);
		TEST_EQ_STR (env[0], "FOO=BAR");
		TEST_EQ_P (env[1], NULL);

		nih_free (env);
	}


	/* Check that we can add a variable to an environment table with
	 * existing different entries and that it is appended to the array.
	 */
	TEST_FEATURE ("with new variable");
	TEST_ALLOC_FAIL {
		TEST_ALLOC_SAFE {
			len = 0;
			env = nih_str_array_new (NULL);
			assert (nih_str_array_add (&env, NULL, &len,
						   "FOO=BAR"));
			assert (nih_str_array_add (&env, NULL, &len,
						   "BAR=BAZ"));
		}

		ret = environ_add (&env, NULL, &len, TRUE,
				   "FRODO=BAGGINS");

		if (test_alloc_failed) {
			TEST_EQ_P (ret, NULL);

			TEST_EQ (len, 2);
			TEST_EQ_STR (env[0], "FOO=BAR");
			TEST_EQ_STR (env[1], "BAR=BAZ");
			TEST_EQ_P (env[2], NULL);

			nih_free (env);
			continue;
		}

		TEST_NE_P (ret, NULL);

		TEST_EQ (len, 3);
		TEST_EQ_STR (env[0], "FOO=BAR");
		TEST_EQ_STR (env[1], "BAR=BAZ");
		TEST_ALLOC_PARENT (env[2], env);
		TEST_ALLOC_SIZE (env[2], 14);
		TEST_EQ_STR (env[2], "FRODO=BAGGINS");
		TEST_EQ_P (env[3], NULL);

		nih_free (env);
	}


	/* Check that we can add a variable from the environment to the table
	 * and that it is appended to the array.
	 */
	TEST_FEATURE ("with new variable from environment");
	putenv ("FRODO=BAGGINS");

	TEST_ALLOC_FAIL {
		TEST_ALLOC_SAFE {
			len = 0;
			env = nih_str_array_new (NULL);
			assert (nih_str_array_add (&env, NULL, &len,
						   "FOO=BAR"));
			assert (nih_str_array_add (&env, NULL, &len,
						   "BAR=BAZ"));
		}

		ret = environ_add (&env, NULL, &len, TRUE,
				   "FRODO");

		if (test_alloc_failed) {
			TEST_EQ_P (ret, NULL);

			TEST_EQ (len, 2);
			TEST_EQ_STR (env[0], "FOO=BAR");
			TEST_EQ_STR (env[1], "BAR=BAZ");
			TEST_EQ_P (env[2], NULL);

			nih_free (env);
			continue;
		}

		TEST_NE_P (ret, NULL);

		TEST_EQ (len, 3);
		TEST_EQ_STR (env[0], "FOO=BAR");
		TEST_EQ_STR (env[1], "BAR=BAZ");
		TEST_ALLOC_PARENT (env[2], env);
		TEST_ALLOC_SIZE (env[2], 14);
		TEST_EQ_STR (env[2], "FRODO=BAGGINS");
		TEST_EQ_P (env[3], NULL);

		nih_free (env);
	}

	unsetenv ("FRODO");


	/* Check that when we attempt to add a variable that's not in the
	 * environment, the table is not extended.
	 */
	TEST_FEATURE ("with new variable unset in environment");
	unsetenv ("FRODO");

	TEST_ALLOC_FAIL {
		TEST_ALLOC_SAFE {
			len = 0;
			env = nih_str_array_new (NULL);
			assert (nih_str_array_add (&env, NULL, &len,
						   "FOO=BAR"));
			assert (nih_str_array_add (&env, NULL, &len,
						   "BAR=BAZ"));
		}

		ret = environ_add (&env, NULL, &len, TRUE,
				   "FRODO");

		if (test_alloc_failed) {
			TEST_EQ_P (ret, NULL);

			TEST_EQ (len, 2);
			TEST_EQ_STR (env[0], "FOO=BAR");
			TEST_EQ_STR (env[1], "BAR=BAZ");
			TEST_EQ_P (env[2], NULL);

			nih_free (env);
			continue;
		}

		TEST_NE_P (ret, NULL);

		TEST_EQ (len, 2);
		TEST_EQ_STR (env[0], "FOO=BAR");
		TEST_EQ_STR (env[1], "BAR=BAZ");
		TEST_EQ_P (env[2], NULL);

		nih_free (env);
	}


	/* Check that we can replace a variable in the environment table
	 * when one already exists with the same or different value.
	 */
	TEST_FEATURE ("with replacement variable");
	TEST_ALLOC_FAIL {
		char *old_env;

		TEST_ALLOC_SAFE {
			len = 0;
			env = nih_str_array_new (NULL);
			assert (nih_str_array_add (&env, NULL, &len,
						   "FOO=BAR"));
			assert (nih_str_array_add (&env, NULL, &len,
						   "BAR=BAZ"));
			assert (nih_str_array_add (&env, NULL, &len,
						   "FRODO=BAGGINS"));
		}

		old_env = env[1];
		TEST_FREE_TAG (old_env);

		ret = environ_add (&env, NULL, &len, TRUE,
				   "BAR=WIBBLE");

		if (test_alloc_failed) {
			TEST_EQ_P (ret, NULL);
			TEST_NOT_FREE (old_env);

			TEST_EQ (len, 3);
			TEST_EQ_STR (env[0], "FOO=BAR");
			TEST_EQ_STR (env[1], "BAR=BAZ");
			TEST_EQ_STR (env[2], "FRODO=BAGGINS");
			TEST_EQ_P (env[3], NULL);

			nih_free (env);
			continue;
		}

		TEST_FREE (old_env);

		TEST_NE_P (ret, NULL);

		TEST_EQ (len, 3);
		TEST_EQ_STR (env[0], "FOO=BAR");
		TEST_ALLOC_PARENT (env[1], env);
		TEST_ALLOC_SIZE (env[1], 11);
		TEST_EQ_STR (env[1], "BAR=WIBBLE");
		TEST_EQ_STR (env[2], "FRODO=BAGGINS");
		TEST_EQ_P (env[3], NULL);

		nih_free (env);
	}


	/* Check that we can replace a variable from the environment in the
	 * environment table when one already exists with the same or
	 * different value.
	 */
	TEST_FEATURE ("with replacement variable from environment");
	putenv ("BAR=WIBBLE");

	TEST_ALLOC_FAIL {
		char *old_env;

		TEST_ALLOC_SAFE {
			len = 0;
			env = nih_str_array_new (NULL);
			assert (nih_str_array_add (&env, NULL, &len,
						   "FOO=BAR"));
			assert (nih_str_array_add (&env, NULL, &len,
						   "BAR=BAZ"));
			assert (nih_str_array_add (&env, NULL, &len,
						   "FRODO=BAGGINS"));
			assert (nih_str_array_add (&env, NULL, &len,
						   "BILBO=TOOK"));
		}

		old_env = env[1];
		TEST_FREE_TAG (old_env);

		ret = environ_add (&env, NULL, &len, TRUE, "BAR");

		if (test_alloc_failed) {
			TEST_EQ_P (ret, NULL);
			TEST_NOT_FREE (old_env);

			TEST_EQ (len, 4);
			TEST_EQ_STR (env[0], "FOO=BAR");
			TEST_EQ_STR (env[1], "BAR=BAZ");
			TEST_EQ_STR (env[2], "FRODO=BAGGINS");
			TEST_EQ_STR (env[3], "BILBO=TOOK");
			TEST_EQ_P (env[4], NULL);

			nih_free (env);
			continue;
		}

		TEST_FREE (old_env);

		TEST_NE_P (ret, NULL);

		TEST_EQ (len, 4);
		TEST_EQ_STR (env[0], "FOO=BAR");
		TEST_ALLOC_PARENT (env[1], env);
		TEST_ALLOC_SIZE (env[1], 11);
		TEST_EQ_STR (env[1], "BAR=WIBBLE");
		TEST_EQ_STR (env[2], "FRODO=BAGGINS");
		TEST_EQ_STR (env[3], "BILBO=TOOK");
		TEST_EQ_P (env[4], NULL);

		nih_free (env);
	}

	unsetenv ("BAR");


	/* Check that when we attempt to replace a variable that's unset
	 * in the environment, the existing variable is removed from the
	 * table.
	 */
	TEST_FEATURE ("with replacement variable unset in environment");
	unsetenv ("BAR");

	TEST_ALLOC_FAIL {
		char *old_env;

		TEST_ALLOC_SAFE {
			len = 0;
			env = nih_str_array_new (NULL);
			assert (nih_str_array_add (&env, NULL, &len,
						   "FOO=BAR"));
			assert (nih_str_array_add (&env, NULL, &len,
						   "BAR=BAZ"));
			assert (nih_str_array_add (&env, NULL, &len,
						   "FRODO=BAGGINS"));
			assert (nih_str_array_add (&env, NULL, &len,
						   "BILBO=TOOK"));
		}

		old_env = env[1];
		TEST_FREE_TAG (old_env);

		ret = environ_add (&env, NULL, &len, TRUE, "BAR");

		if (test_alloc_failed) {
			TEST_EQ_P (ret, NULL);
			TEST_NOT_FREE (old_env);

			TEST_EQ (len, 4);
			TEST_EQ_STR (env[0], "FOO=BAR");
			TEST_EQ_STR (env[1], "BAR=BAZ");
			TEST_EQ_STR (env[2], "FRODO=BAGGINS");
			TEST_EQ_STR (env[3], "BILBO=TOOK");
			TEST_EQ_P (env[4], NULL);

			nih_free (env);
			continue;
		}

		TEST_FREE (old_env);

		TEST_NE_P (ret, NULL);

		TEST_EQ (len, 3);
		TEST_EQ_STR (env[0], "FOO=BAR");
		TEST_EQ_STR (env[1], "FRODO=BAGGINS");
		TEST_EQ_STR (env[2], "BILBO=TOOK");
		TEST_EQ_P (env[3], NULL);

		nih_free (env);
	}

	unsetenv ("BAR");


	/* Check that we can add a variable to an environment table with
	 * existing different entries and that it is appended to the array,
	 * even if replace is FALSE.
	 */
	TEST_FEATURE ("with new variable but no replace");
	TEST_ALLOC_FAIL {
		TEST_ALLOC_SAFE {
			len = 0;
			env = nih_str_array_new (NULL);
			assert (nih_str_array_add (&env, NULL, &len,
						   "FOO=BAR"));
			assert (nih_str_array_add (&env, NULL, &len,
						   "BAR=BAZ"));
		}

		ret = environ_add (&env, NULL, &len, FALSE,
				   "FRODO=BAGGINS");

		if (test_alloc_failed) {
			TEST_EQ_P (ret, NULL);

			TEST_EQ (len, 2);
			TEST_EQ_STR (env[0], "FOO=BAR");
			TEST_EQ_STR (env[1], "BAR=BAZ");
			TEST_EQ_P (env[2], NULL);

			nih_free (env);
			continue;
		}

		TEST_NE_P (ret, NULL);

		TEST_EQ (len, 3);
		TEST_EQ_STR (env[0], "FOO=BAR");
		TEST_EQ_STR (env[1], "BAR=BAZ");
		TEST_ALLOC_PARENT (env[2], env);
		TEST_ALLOC_SIZE (env[2], 14);
		TEST_EQ_STR (env[2], "FRODO=BAGGINS");
		TEST_EQ_P (env[3], NULL);

		nih_free (env);
	}


	/* Check that when a variable already exists in the environment
	 * table, and we're not replacing, the original value is left
	 * untouched.
	 */
	TEST_FEATURE ("with existing variable");
	TEST_ALLOC_FAIL {
		TEST_ALLOC_SAFE {
			len = 0;
			env = nih_str_array_new (NULL);
			assert (nih_str_array_add (&env, NULL, &len,
						   "FOO=BAR"));
			assert (nih_str_array_add (&env, NULL, &len,
						   "BAR=BAZ"));
			assert (nih_str_array_add (&env, NULL, &len,
						   "FRODO=BAGGINS"));
		}

		ret = environ_add (&env, NULL, &len, FALSE,
				   "BAR=WIBBLE");

		if (test_alloc_failed) {
			TEST_EQ_P (ret, NULL);

			TEST_EQ (len, 3);
			TEST_EQ_STR (env[0], "FOO=BAR");
			TEST_EQ_STR (env[1], "BAR=BAZ");
			TEST_EQ_STR (env[2], "FRODO=BAGGINS");
			TEST_EQ_P (env[3], NULL);

			nih_free (env);
			continue;
		}

		TEST_NE_P (ret, NULL);

		TEST_EQ (len, 3);
		TEST_EQ_STR (env[0], "FOO=BAR");
		TEST_EQ_STR (env[1], "BAR=BAZ");
		TEST_EQ_STR (env[2], "FRODO=BAGGINS");
		TEST_EQ_P (env[3], NULL);

		nih_free (env);
	}


	/* Check that when a variable from the environment already exists in
	 * the environment table, and we're not replacing, the original value
	 * is left untouched.
	 */
	TEST_FEATURE ("with existing variable from environment");
	putenv ("BAR=WIBBLE");

	TEST_ALLOC_FAIL {
		TEST_ALLOC_SAFE {
			len = 0;
			env = nih_str_array_new (NULL);
			assert (nih_str_array_add (&env, NULL, &len,
						   "FOO=BAR"));
			assert (nih_str_array_add (&env, NULL, &len,
						   "BAR=BAZ"));
			assert (nih_str_array_add (&env, NULL, &len,
						   "FRODO=BAGGINS"));
			assert (nih_str_array_add (&env, NULL, &len,
						   "BILBO=TOOK"));
		}

		ret = environ_add (&env, NULL, &len, FALSE, "BAR");

		if (test_alloc_failed) {
			TEST_EQ_P (ret, NULL);

			TEST_EQ (len, 4);
			TEST_EQ_STR (env[0], "FOO=BAR");
			TEST_EQ_STR (env[1], "BAR=BAZ");
			TEST_EQ_STR (env[2], "FRODO=BAGGINS");
			TEST_EQ_STR (env[3], "BILBO=TOOK");
			TEST_EQ_P (env[4], NULL);

			nih_free (env);
			continue;
		}

		TEST_NE_P (ret, NULL);

		TEST_EQ (len, 4);
		TEST_EQ_STR (env[0], "FOO=BAR");
		TEST_EQ_STR (env[1], "BAR=BAZ");
		TEST_EQ_STR (env[2], "FRODO=BAGGINS");
		TEST_EQ_STR (env[3], "BILBO=TOOK");
		TEST_EQ_P (env[4], NULL);

		nih_free (env);
	}

	unsetenv ("BAR");


	/* Check that when a variable from the environment is unset it
	 * does not remove an existing variable in the environment table
	 * if we're not replacing.
	 */
	TEST_FEATURE ("with existing variable unset in environment");
	unsetenv ("BAR");

	TEST_ALLOC_FAIL {
		TEST_ALLOC_SAFE {
			len = 0;
			env = nih_str_array_new (NULL);
			assert (nih_str_array_add (&env, NULL, &len,
						   "FOO=BAR"));
			assert (nih_str_array_add (&env, NULL, &len,
						   "BAR=BAZ"));
			assert (nih_str_array_add (&env, NULL, &len,
						   "FRODO=BAGGINS"));
			assert (nih_str_array_add (&env, NULL, &len,
						   "BILBO=TOOK"));
		}

		ret = environ_add (&env, NULL, &len, FALSE, "BAR");

		if (test_alloc_failed) {
			TEST_EQ_P (ret, NULL);

			TEST_EQ (len, 4);
			TEST_EQ_STR (env[0], "FOO=BAR");
			TEST_EQ_STR (env[1], "BAR=BAZ");
			TEST_EQ_STR (env[2], "FRODO=BAGGINS");
			TEST_EQ_STR (env[3], "BILBO=TOOK");
			TEST_EQ_P (env[4], NULL);

			nih_free (env);
			continue;
		}

		TEST_NE_P (ret, NULL);

		TEST_EQ (len, 4);
		TEST_EQ_STR (env[0], "FOO=BAR");
		TEST_EQ_STR (env[1], "BAR=BAZ");
		TEST_EQ_STR (env[2], "FRODO=BAGGINS");
		TEST_EQ_STR (env[3], "BILBO=TOOK");
		TEST_EQ_P (env[4], NULL);

		nih_free (env);
	}

	unsetenv ("BAR");
}
예제 #26
0
void
test_unref (void)
{
	void *ptr1;
	void *ptr2;
	void *ptr3;

	TEST_FUNCTION ("nih_unref");


	/* Check that we can remove a reference from an object with multiple
	 * parents, which means the object will not be freed.
	 */
	TEST_FEATURE ("with multiple parents");
	ptr1 = nih_alloc (NULL, 100);
	memset (ptr1, 'x', 100);

	ptr2 = nih_alloc (ptr1, 100);
	memset (ptr2, 'y', 100);

	ptr3 = nih_alloc (NULL, 100);
	memset (ptr2, 'z', 100);

	nih_ref (ptr2, ptr3);

	nih_alloc_set_destructor (ptr2, destructor_called);
	destructor_was_called = 0;

	nih_unref (ptr2, ptr1);

	TEST_FALSE (destructor_was_called);
	TEST_ALLOC_PARENT (ptr2, ptr3);

	nih_free (ptr1);
	nih_free (ptr3);


	/* Check that when we remove the last reference from an object,
	 * the object is freed.
	 */
	TEST_FEATURE ("with last parent");
	ptr1 = nih_alloc (NULL, 100);
	memset (ptr1, 'x', 100);

	ptr2 = nih_alloc (ptr1, 100);
	memset (ptr2, 'y', 100);

	nih_alloc_set_destructor (ptr2, destructor_called);
	destructor_was_called = 0;

	nih_unref (ptr2, ptr1);

	TEST_TRUE (destructor_was_called);

	nih_free (ptr1);


	/* Check that we have to remove the NULL reference from an object
	 * for it to be freed.
	 */
	TEST_FEATURE ("with only NULL parent");
	ptr1 = nih_alloc (NULL, 100);
	memset (ptr1, 'x', 100);

	nih_alloc_set_destructor (ptr1, destructor_called);
	destructor_was_called = 0;

	nih_unref (ptr1, NULL);

	TEST_TRUE (destructor_was_called);


	/* Check that we can remove the NULL reference leaving a reference
	 * to a different object.
	 */
	TEST_FEATURE ("with no parent and other parent");
	ptr1 = nih_alloc (NULL, 100);
	memset (ptr1, 'x', 100);

	ptr2 = nih_alloc (NULL, 100);
	memset (ptr2, 'y', 100);

	nih_ref (ptr2, ptr1);

	nih_alloc_set_destructor (ptr2, destructor_called);
	destructor_was_called = 0;

	nih_unref (ptr2, NULL);

	TEST_FALSE (destructor_was_called);

	TEST_ALLOC_PARENT (ptr2, ptr1);
	TEST_FALSE (nih_alloc_parent (ptr2, NULL));

	nih_free (ptr1);


	/* Check that an object with multiple NULL references must have
	 * them both removed before it will be freed.
	 */
	TEST_FEATURE ("with multiple NULL parents");
	ptr1 = nih_alloc (NULL, 100);
	memset (ptr1, 'x', 100);

	nih_ref (ptr1, NULL);

	nih_alloc_set_destructor (ptr1, destructor_called);
	destructor_was_called = 0;

	nih_unref (ptr1, NULL);

	TEST_FALSE (destructor_was_called);

	nih_unref (ptr1, NULL);

	TEST_TRUE (destructor_was_called);


	/* Check that an object with multiple identical references must have
	 * them both removed before it will be freed.
	 */
	TEST_FEATURE ("with multiple identical parents");
	ptr1 = nih_alloc (NULL, 100);
	memset (ptr1, 'x', 100);

	ptr2 = nih_alloc (ptr1, 100);
	memset (ptr2, 'y', 100);

	nih_ref (ptr2, ptr1);

	nih_alloc_set_destructor (ptr2, destructor_called);
	destructor_was_called = 0;

	nih_unref (ptr2, ptr1);

	TEST_FALSE (destructor_was_called);

	nih_unref (ptr2, ptr1);

	TEST_TRUE (destructor_was_called);

	nih_free (ptr1);
}
예제 #27
0
void
test_expand (void)
{
	NihError *error;
	char     *env[7], *str;

	TEST_FUNCTION ("environ_expand");
	env[0] = "FOO=frodo";
	env[1] = "BAR=bilbo";
	env[2] = "BAZ=xx";
	env[3] = "HOBBIT=FOO";
	env[4] = "NULL=";
	env[5] = "DOH=oops";
	env[6] = NULL;

	nih_error_push_context();
	nih_error_pop_context ();


	/* Check that we can expand a string containing no expansion.
	 */
	TEST_FEATURE ("with no expansion");
	TEST_ALLOC_FAIL {
		str = environ_expand (NULL, "this is a test", env);

		if (test_alloc_failed) {
			TEST_EQ_P (str, NULL);

			error = nih_error_get ();
			TEST_EQ (error->number, ENOMEM);
			nih_free (error);
			continue;
		}

		TEST_EQ_STR (str, "this is a test");

		nih_free (str);
	}


	/* Check that we can expand a simple string containing a reference
	 * from the environment, with the reference replaced by the environment
	 * variable value.
	 */
	TEST_FEATURE ("with simple expansion");
	TEST_ALLOC_FAIL {
		str = environ_expand (NULL, "this is a $FOO test", env);

		if (test_alloc_failed) {
			TEST_EQ_P (str, NULL);

			error = nih_error_get ();
			TEST_EQ (error->number, ENOMEM);
			nih_free (error);
			continue;
		}

		TEST_EQ_STR (str, "this is a frodo test");

		nih_free (str);
	}


	/* Check that we can expand a simple string containing a reference
	 * from the environment that is smaller than the reference, with the
	 * reference replaced by the environment variable value.
	 */
	TEST_FEATURE ("with simple expansion of smaller value");
	TEST_ALLOC_FAIL {
		str = environ_expand (NULL, "this is a $BAZ test", env);

		if (test_alloc_failed) {
			TEST_EQ_P (str, NULL);

			error = nih_error_get ();
			TEST_EQ (error->number, ENOMEM);
			nih_free (error);
			continue;
		}

		TEST_EQ_STR (str, "this is a xx test");

		nih_free (str);
	}


	/* Check that we can expand a simple string containing a reference
	 * from the environment that is exactly the same size as the
	 * reference, with the reference replaced by the environment variable
	 * value.
	 */
	TEST_FEATURE ("with simple expansion of same size value");
	TEST_ALLOC_FAIL {
		str = environ_expand (NULL, "this is a $DOH test", env);

		if (test_alloc_failed) {
			TEST_EQ_P (str, NULL);

			error = nih_error_get ();
			TEST_EQ (error->number, ENOMEM);
			nih_free (error);
			continue;
		}

		TEST_EQ_STR (str, "this is a oops test");

		nih_free (str);
	}


	/* Check that we can expand a string containing multiple simple
	 * references, with each replaced by the variable value.
	 */
	TEST_FEATURE ("with multiple simple expansions");
	TEST_ALLOC_FAIL {
		str = environ_expand (NULL, "test $FOO $BAR$BAZ", env);

		if (test_alloc_failed) {
			TEST_EQ_P (str, NULL);

			error = nih_error_get ();
			TEST_EQ (error->number, ENOMEM);
			nih_free (error);
			continue;
		}

		TEST_EQ_STR (str, "test frodo bilboxx");

		nih_free (str);
	}


	/* Check that we can expand a string containing a bracketed
	 * reference, allowing it to nestle against other alphanumerics.
	 */
	TEST_FEATURE ("with simple bracketed expression");
	TEST_ALLOC_FAIL {
		str = environ_expand (NULL, "${BAR}test", env);

		if (test_alloc_failed) {
			TEST_EQ_P (str, NULL);

			error = nih_error_get ();
			TEST_EQ (error->number, ENOMEM);
			nih_free (error);
			continue;
		}

		TEST_EQ_STR (str, "bilbotest");

		nih_free (str);
	}


	/* Check that we can expand a string containing multiple bracketed
	 * references, allowing it to nestle against other alphanumerics.
	 */
	TEST_FEATURE ("with multiple simple bracketed expression");
	TEST_ALLOC_FAIL {
		str = environ_expand (NULL, "${BAR}${FOO}test${BAZ}", env);

		if (test_alloc_failed) {
			TEST_EQ_P (str, NULL);

			error = nih_error_get ();
			TEST_EQ (error->number, ENOMEM);
			nih_free (error);
			continue;
		}

		TEST_EQ_STR (str, "bilbofrodotestxx");

		nih_free (str);
	}


	/* Check that simple expressions may appear within bracketed
	 * expressions, causing them to be evaluted and the evalution
	 * serving as the reference.
	 */
	TEST_FEATURE ("with simple expression inside bracketed expression");
	TEST_ALLOC_FAIL {
		str = environ_expand (NULL, "${$HOBBIT} baggins", env);

		if (test_alloc_failed) {
			TEST_EQ_P (str, NULL);

			error = nih_error_get ();
			TEST_EQ (error->number, ENOMEM);
			nih_free (error);
			continue;
		}

		TEST_EQ_STR (str, "frodo baggins");

		nih_free (str);
	}


	/* Check that bracketed expressions may appear within bracketed
	 * expressions.
	 */
	TEST_FEATURE ("with bracketed expression inside bracketed expression");
	TEST_ALLOC_FAIL {
		str = environ_expand (NULL, "${${HOBBIT}} baggins", env);

		if (test_alloc_failed) {
			TEST_EQ_P (str, NULL);

			error = nih_error_get ();
			TEST_EQ (error->number, ENOMEM);
			nih_free (error);
			continue;
		}

		TEST_EQ_STR (str, "frodo baggins");

		nih_free (str);
	}


	/* Check that we can substitute a default value if the variable
	 * we were after was unset.
	 */
	TEST_FEATURE ("with bracketed default expression");
	TEST_ALLOC_FAIL {
		str = environ_expand (NULL, "${MEEP-a }test", env);

		if (test_alloc_failed) {
			TEST_EQ_P (str, NULL);

			error = nih_error_get ();
			TEST_EQ (error->number, ENOMEM);
			nih_free (error);
			continue;
		}

		TEST_EQ_STR (str, "a test");

		nih_free (str);
	}


	/* Check that a default expression uses the environment value if
	 * it is actually set.
	 */
	TEST_FEATURE ("with bracketed default expression for set variable");
	TEST_ALLOC_FAIL {
		str = environ_expand (NULL, "${BAZ-a }test", env);

		if (test_alloc_failed) {
			TEST_EQ_P (str, NULL);

			error = nih_error_get ();
			TEST_EQ (error->number, ENOMEM);
			nih_free (error);
			continue;
		}

		TEST_EQ_STR (str, "xxtest");

		nih_free (str);
	}


	/* Check that a default expression uses the environment value if
	 * it is actually set, even if it is NULL.
	 */
	TEST_FEATURE ("with bracketed default expression for null variable");
	TEST_ALLOC_FAIL {
		str = environ_expand (NULL, "${NULL-a }test", env);

		if (test_alloc_failed) {
			TEST_EQ_P (str, NULL);

			error = nih_error_get ();
			TEST_EQ (error->number, ENOMEM);
			nih_free (error);
			continue;
		}

		TEST_EQ_STR (str, "test");

		nih_free (str);
	}


	/* Check that we can substitute a default value if the variable
	 * we were after was unset (or null).
	 */
	TEST_FEATURE ("with bracketed default or null expression");
	TEST_ALLOC_FAIL {
		str = environ_expand (NULL, "${MEEP:-a }test", env);

		if (test_alloc_failed) {
			TEST_EQ_P (str, NULL);

			error = nih_error_get ();
			TEST_EQ (error->number, ENOMEM);
			nih_free (error);
			continue;
		}

		TEST_EQ_STR (str, "a test");

		nih_free (str);
	}


	/* Check that a default or null expression uses the environment value
	 * if it is actually set and not null.
	 */
	TEST_FEATURE ("with bracketed default or null expression for set variable");
	TEST_ALLOC_FAIL {
		str = environ_expand (NULL, "${BAZ:-a }test", env);

		if (test_alloc_failed) {
			TEST_EQ_P (str, NULL);

			error = nih_error_get ();
			TEST_EQ (error->number, ENOMEM);
			nih_free (error);
			continue;
		}

		TEST_EQ_STR (str, "xxtest");

		nih_free (str);
	}


	/* Check that we can substitute a default value if the variable
	 * we were after was null.
	 */
	TEST_FEATURE ("with bracketed default or null expression for null variable");
	TEST_ALLOC_FAIL {
		str = environ_expand (NULL, "${NULL:-a }test", env);

		if (test_alloc_failed) {
			TEST_EQ_P (str, NULL);

			error = nih_error_get ();
			TEST_EQ (error->number, ENOMEM);
			nih_free (error);
			continue;
		}

		TEST_EQ_STR (str, "a test");

		nih_free (str);
	}


	/* Check that we don't substitute an alternate value if the
	 * variable we were after was unset.
	 */
	TEST_FEATURE ("with bracketed alternate expression");
	TEST_ALLOC_FAIL {
		str = environ_expand (NULL, "${MEEP+good }test", env);

		if (test_alloc_failed) {
			TEST_EQ_P (str, NULL);

			error = nih_error_get ();
			TEST_EQ (error->number, ENOMEM);
			nih_free (error);
			continue;
		}

		TEST_EQ_STR (str, "test");

		nih_free (str);
	}


	/* Check that we use the alternate value if the environment variable
	 * is actually set.
	 */
	TEST_FEATURE ("with bracketed alternate expression for set variable");
	TEST_ALLOC_FAIL {
		str = environ_expand (NULL, "${BAZ+good }test", env);

		if (test_alloc_failed) {
			TEST_EQ_P (str, NULL);

			error = nih_error_get ();
			TEST_EQ (error->number, ENOMEM);
			nih_free (error);
			continue;
		}

		TEST_EQ_STR (str, "good test");

		nih_free (str);
	}


	/* Check that we use the alternate value if the environment variable
	 * is set, even if it is NULL.
	 */
	TEST_FEATURE ("with bracketed alternate expression for null variable");
	TEST_ALLOC_FAIL {
		str = environ_expand (NULL, "${NULL+good }test", env);

		if (test_alloc_failed) {
			TEST_EQ_P (str, NULL);

			error = nih_error_get ();
			TEST_EQ (error->number, ENOMEM);
			nih_free (error);
			continue;
		}

		TEST_EQ_STR (str, "good test");

		nih_free (str);
	}


	/* Check that we don't substitute an alternate value if the
	 * variable we were after was unset (or null).
	 */
	TEST_FEATURE ("with bracketed alternate or null expression");
	TEST_ALLOC_FAIL {
		str = environ_expand (NULL, "${MEEP:+good }test", env);

		if (test_alloc_failed) {
			TEST_EQ_P (str, NULL);

			error = nih_error_get ();
			TEST_EQ (error->number, ENOMEM);
			nih_free (error);
			continue;
		}

		TEST_EQ_STR (str, "test");

		nih_free (str);
	}


	/* Check that we use the alternate value if the environment variable
	 * is actually set and not null.
	 */
	TEST_FEATURE ("with bracketed alternate or null expression for set variable");
	TEST_ALLOC_FAIL {
		str = environ_expand (NULL, "${BAZ:+good }test", env);

		if (test_alloc_failed) {
			TEST_EQ_P (str, NULL);

			error = nih_error_get ();
			TEST_EQ (error->number, ENOMEM);
			nih_free (error);
			continue;
		}

		TEST_EQ_STR (str, "good test");

		nih_free (str);
	}


	/* Check that we don't substitute an alternate value if the
	 * variable we were after was set, but was null.
	 */
	TEST_FEATURE ("with bracketed alternate or null expression for null variable");
	TEST_ALLOC_FAIL {
		str = environ_expand (NULL, "${NULL:+good }test", env);

		if (test_alloc_failed) {
			TEST_EQ_P (str, NULL);

			error = nih_error_get ();
			TEST_EQ (error->number, ENOMEM);
			nih_free (error);
			continue;
		}

		TEST_EQ_STR (str, "test");

		nih_free (str);
	}


	/* Check that references on either side of an expression are
	 * expanded before evaluation.
	 */
	TEST_FEATURE ("with references in bracketed expression argument");
	TEST_ALLOC_FAIL {
		str = environ_expand (NULL, "${$BAZ:-${$HOBBIT}}test", env);

		if (test_alloc_failed) {
			TEST_EQ_P (str, NULL);

			error = nih_error_get ();
			TEST_EQ (error->number, ENOMEM);
			nih_free (error);
			continue;
		}

		TEST_EQ_STR (str, "frodotest");

		nih_free (str);
	}


	/* Check that a literal dollar sign with no following text is
	 * treated just as a dollar sign.
	 */
	TEST_FEATURE ("with dollar sign in whitespace");
	TEST_ALLOC_FAIL {
		str = environ_expand (NULL, "this is a $ test", env);

		if (test_alloc_failed) {
			TEST_EQ_P (str, NULL);

			error = nih_error_get ();
			TEST_EQ (error->number, ENOMEM);
			nih_free (error);
			continue;
		}

		TEST_EQ_STR (str, "this is a $ test");

		nih_free (str);
	}


	/* Check that a literal dollar sign in text can be followed by empty
	 * brackets to be just as a dollar sign.
	 */
	TEST_FEATURE ("with bracketed dollar sign");
	TEST_ALLOC_FAIL {
		str = environ_expand (NULL, "${}test", env);

		if (test_alloc_failed) {
			TEST_EQ_P (str, NULL);

			error = nih_error_get ();
			TEST_EQ (error->number, ENOMEM);
			nih_free (error);
			continue;
		}

		TEST_EQ_STR (str, "$test");

		nih_free (str);
	}


	/* Check that attempting to expand an unknown variable results in
	 * an error being raised.
	 */
	TEST_FEATURE ("with simple expansion of unknown variable");
	str = environ_expand (NULL, "this is a $WIBBLE test", env);

	TEST_EQ_P (str, NULL);

	error = nih_error_get ();
	TEST_EQ (error->number, ENVIRON_UNKNOWN_PARAM);
	nih_free (error);


	/* Check that attempting to expand an unknown variable results in
	 * an error being raised.
	 */
	TEST_FEATURE ("with bracketed expansion of unknown variable");
	str = environ_expand (NULL, "this is a ${WIBBLE} test", env);

	TEST_EQ_P (str, NULL);

	error = nih_error_get ();
	TEST_EQ (error->number, ENVIRON_UNKNOWN_PARAM);
	nih_free (error);


	/* Check that attempting to expand an unknown variable results in
	 * an error being raised.
	 */
	TEST_FEATURE ("with expansion of unknown variable within expression name");
	str = environ_expand (NULL, "this is a ${$WIBBLE:-$FOO} test", env);

	TEST_EQ_P (str, NULL);

	error = nih_error_get ();
	TEST_EQ (error->number, ENVIRON_UNKNOWN_PARAM);
	nih_free (error);


	/* Check that attempting to expand an unknown variable results in
	 * an error being raised.
	 */
	TEST_FEATURE ("with expansion of unknown variable within expression argument");
	str = environ_expand (NULL, "this is a ${$FOO:-$WIBBLE} test", env);

	TEST_EQ_P (str, NULL);

	error = nih_error_get ();
	TEST_EQ (error->number, ENVIRON_UNKNOWN_PARAM);
	nih_free (error);


	/* Check that inventing a new operator results in an error
	 * being raised.
	 */
	TEST_FEATURE ("with unknown operator in expression");
	str = environ_expand (NULL, "this is a ${$FOO:!$BAR test", env);

	TEST_EQ_P (str, NULL);

	error = nih_error_get ();
	TEST_EQ (error->number, ENVIRON_EXPECTED_OPERATOR);
	nih_free (error);


	/* Check that forgetting to close a brace results in an error
	 * being raised.
	 */
	TEST_FEATURE ("with missing close brace after expression");
	str = environ_expand (NULL, "this is a ${$FOO:-$BAR test", env);

	TEST_EQ_P (str, NULL);

	error = nih_error_get ();
	TEST_EQ (error->number, ENVIRON_MISMATCHED_BRACES);
	nih_free (error);
}
예제 #28
0
void
test_output (void)
{
	FILE *        source;
	FILE *        header;
	Node *        node = NULL;
	Interface *   interface = NULL;
	Method *      method = NULL;
	Signal *      signal = NULL;
	Argument *    argument = NULL;
	Property *    property = NULL;
	int           ret;
	NihError *    err;

	TEST_FUNCTION ("output");
	source = tmpfile ();
	header = tmpfile ();


	/* Check that we can generate a valid source file and accompanying
	 * header file for a node in proxy mode.
	 */
	TEST_FEATURE ("with proxy");
	TEST_ALLOC_FAIL {
		TEST_ALLOC_SAFE {
			node = node_new (NULL, NULL);

			interface = interface_new (node, "com.netsplit.Nih.Test");
			interface->symbol = "test";
			nih_list_add (&node->interfaces, &interface->entry);

			method = method_new (interface, "Poke");
			method->symbol = "poke";
			nih_list_add (&interface->methods, &method->entry);

			argument = argument_new (method, "address",
						 "u", NIH_DBUS_ARG_IN);
			argument->symbol = "address";
			nih_list_add (&method->arguments, &argument->entry);

			argument = argument_new (method, "value",
						 "s", NIH_DBUS_ARG_IN);
			argument->symbol = "value";
			nih_list_add (&method->arguments, &argument->entry);

			method = method_new (interface, "Peek");
			method->symbol = "peek";
			nih_list_add (&interface->methods, &method->entry);

			argument = argument_new (method, "address",
						 "u", NIH_DBUS_ARG_IN);
			argument->symbol = "address";
			nih_list_add (&method->arguments, &argument->entry);

			argument = argument_new (method, "value",
						 "s", NIH_DBUS_ARG_OUT);
			argument->symbol = "value";
			nih_list_add (&method->arguments, &argument->entry);

			method = method_new (interface, "IsValidAddress");
			method->symbol = "is_valid_address";
			nih_list_add (&interface->methods, &method->entry);

			argument = argument_new (method, "address",
						 "u", NIH_DBUS_ARG_IN);
			argument->symbol = "address";
			nih_list_add (&method->arguments, &argument->entry);


			signal = signal_new (interface, "Bounce");
			signal->symbol = "bounce";
			nih_list_add (&interface->signals, &signal->entry);

			argument = argument_new (signal, "height",
						 "u", NIH_DBUS_ARG_OUT);
			argument->symbol = "height";
			nih_list_add (&signal->arguments, &argument->entry);

			argument = argument_new (signal, "velocity",
						 "i", NIH_DBUS_ARG_OUT);
			argument->symbol = "velocity";
			nih_list_add (&signal->arguments, &argument->entry);

			signal = signal_new (interface, "Exploded");
			signal->symbol = "exploded";
			nih_list_add (&interface->signals, &signal->entry);


			property = property_new (interface, "colour",
						 "s", NIH_DBUS_READWRITE);
			property->symbol = "colour";
			nih_list_add (&interface->properties, &property->entry);

			property = property_new (interface, "size",
						 "u", NIH_DBUS_READ);
			property->symbol = "size";
			nih_list_add (&interface->properties, &property->entry);

			property = property_new (interface, "touch",
						 "b", NIH_DBUS_WRITE);
			property->symbol = "touch";
			nih_list_add (&interface->properties, &property->entry);


			interface = interface_new (node, "com.netsplit.Nih.Foo");
			interface->symbol = "foo";
			nih_list_add (&node->interfaces, &interface->entry);

			method = method_new (interface, "Bing");
			method->symbol = "bing";
			nih_list_add (&interface->methods, &method->entry);

			signal = signal_new (interface, "NewResult");
			signal->symbol = "new_result";
			nih_list_add (&interface->signals, &signal->entry);

			property = property_new (interface, "preferences",
						 "(us)", NIH_DBUS_READWRITE);
			property->symbol = "preferences";
			nih_list_add (&interface->properties, &property->entry);

		}

		ret = output ("test.c", fileno (source),
			      "test.h", fileno (header),
			      "my", node, FALSE);

		rewind (source);
		rewind (header);

		if (test_alloc_failed) {
			TEST_LT (ret, 0);

			err = nih_error_get ();
			TEST_EQ (err->number, ENOMEM);
			nih_free (err);

			TEST_FILE_RESET (source);
			TEST_FILE_RESET (header);

			nih_free (node);
			continue;
		}

		TEST_EQ (ret, 0);

		TEST_EXPECTED_FILE (source, "test_output_proxy_standard.c");
		TEST_EXPECTED_FILE (header, "test_output_proxy_standard.h");

		nih_free (node);
	}


	/* Check that when there are no interfaces, a valid empty source
	 * and header file are generated.
	 */
	TEST_FEATURE ("with proxy but no interfaces");
	TEST_ALLOC_FAIL {
		TEST_ALLOC_SAFE {
			node = node_new (NULL, NULL);
		}

		ret = output ("test.c", fileno (source),
			      "test.h", fileno (header),
			      "my", node, FALSE);

		rewind (source);
		rewind (header);

		if (test_alloc_failed) {
			TEST_LT (ret, 0);

			err = nih_error_get ();
			TEST_EQ (err->number, ENOMEM);
			nih_free (err);

			TEST_FILE_RESET (source);
			TEST_FILE_RESET (header);

			nih_free (node);
			continue;
		}

		TEST_EQ (ret, 0);

		TEST_EXPECTED_FILE (source, "test_output_proxy_no_interfaces.c");
		TEST_EXPECTED_FILE (header, "test_output_proxy_no_interfaces.h");

		nih_free (node);
	}


	/* Check that we can generate a valid source file and accompanying
	 * header file for a node in object mode.
	 */
	TEST_FEATURE ("with object");
	TEST_ALLOC_FAIL {
		TEST_ALLOC_SAFE {
			node = node_new (NULL, NULL);

			interface = interface_new (node, "com.netsplit.Nih.Test");
			interface->symbol = "test";
			nih_list_add (&node->interfaces, &interface->entry);

			method = method_new (interface, "Poke");
			method->symbol = "poke";
			nih_list_add (&interface->methods, &method->entry);

			argument = argument_new (method, "address",
						 "u", NIH_DBUS_ARG_IN);
			argument->symbol = "address";
			nih_list_add (&method->arguments, &argument->entry);

			argument = argument_new (method, "value",
						 "s", NIH_DBUS_ARG_IN);
			argument->symbol = "value";
			nih_list_add (&method->arguments, &argument->entry);

			method = method_new (interface, "Peek");
			method->symbol = "peek";
			method->async = TRUE;
			nih_list_add (&interface->methods, &method->entry);

			argument = argument_new (method, "address",
						 "u", NIH_DBUS_ARG_IN);
			argument->symbol = "address";
			nih_list_add (&method->arguments, &argument->entry);

			argument = argument_new (method, "value",
						 "s", NIH_DBUS_ARG_OUT);
			argument->symbol = "value";
			nih_list_add (&method->arguments, &argument->entry);

			method = method_new (interface, "IsValidAddress");
			method->symbol = "is_valid_address";
			nih_list_add (&interface->methods, &method->entry);

			argument = argument_new (method, "address",
						 "u", NIH_DBUS_ARG_IN);
			argument->symbol = "address";
			nih_list_add (&method->arguments, &argument->entry);

			argument = argument_new (method, "is_valid",
						 "b", NIH_DBUS_ARG_OUT);
			argument->symbol = "is_valid";
			nih_list_add (&method->arguments, &argument->entry);


			signal = signal_new (interface, "Bounce");
			signal->symbol = "bounce";
			nih_list_add (&interface->signals, &signal->entry);

			argument = argument_new (signal, "height",
						 "u", NIH_DBUS_ARG_OUT);
			argument->symbol = "height";
			nih_list_add (&signal->arguments, &argument->entry);

			argument = argument_new (signal, "velocity",
						 "i", NIH_DBUS_ARG_OUT);
			argument->symbol = "velocity";
			nih_list_add (&signal->arguments, &argument->entry);

			signal = signal_new (interface, "Exploded");
			signal->symbol = "exploded";
			nih_list_add (&interface->signals, &signal->entry);


			property = property_new (interface, "colour",
						 "s", NIH_DBUS_READWRITE);
			property->symbol = "colour";
			nih_list_add (&interface->properties, &property->entry);

			property = property_new (interface, "size",
						 "u", NIH_DBUS_READ);
			property->symbol = "size";
			nih_list_add (&interface->properties, &property->entry);

			property = property_new (interface, "touch",
						 "b", NIH_DBUS_WRITE);
			property->symbol = "touch";
			nih_list_add (&interface->properties, &property->entry);


			interface = interface_new (node, "com.netsplit.Nih.Foo");
			interface->symbol = "foo";
			nih_list_add (&node->interfaces, &interface->entry);

			method = method_new (interface, "Bing");
			method->symbol = "bing";
			nih_list_add (&interface->methods, &method->entry);

			signal = signal_new (interface, "NewResult");
			signal->symbol = "new_result";
			nih_list_add (&interface->signals, &signal->entry);

			property = property_new (interface, "preferences",
						 "(us)", NIH_DBUS_READWRITE);
			property->symbol = "preferences";
			nih_list_add (&interface->properties, &property->entry);
		}

		ret = output ("test.c", fileno (source),
			      "test.h", fileno (header),
			      "my", node, TRUE);

		rewind (source);
		rewind (header);

		if (test_alloc_failed) {
			TEST_LT (ret, 0);

			err = nih_error_get ();
			TEST_EQ (err->number, ENOMEM);
			nih_free (err);

			TEST_FILE_RESET (source);
			TEST_FILE_RESET (header);

			nih_free (node);
			continue;
		}

		TEST_EQ (ret, 0);

		TEST_EXPECTED_FILE (source, "test_output_object_standard.c");
		TEST_EXPECTED_FILE (header, "test_output_object_standard.h");

		nih_free (node);
	}


	/* Check that when there are no interfaces, a valid empty source
	 * and header file are generated.
	 */
	TEST_FEATURE ("with object but no interfaces");
	TEST_ALLOC_FAIL {
		TEST_ALLOC_SAFE {
			node = node_new (NULL, NULL);
		}

		ret = output ("test.c", fileno (source),
			      "test.h", fileno (header),
			      "my", node, TRUE);

		rewind (source);
		rewind (header);

		if (test_alloc_failed) {
			TEST_LT (ret, 0);

			err = nih_error_get ();
			TEST_EQ (err->number, ENOMEM);
			nih_free (err);

			TEST_FILE_RESET (source);
			TEST_FILE_RESET (header);

			nih_free (node);
			continue;
		}

		TEST_EQ (ret, 0);

		TEST_EXPECTED_FILE (source, "test_output_object_no_interfaces.c");
		TEST_EXPECTED_FILE (header, "test_output_object_no_interfaces.h");

		nih_free (node);
	}


	fclose (source);
	fclose (header);
}
예제 #29
0
int
main (int argc, char *argv[])
{
	char **		args;
	int		ret;
	DBusServer *	server;
	struct stat sb;
	struct rlimit newrlimit;

	nih_main_init (argv[0]);

	nih_option_set_synopsis (_("Control group manager"));
	nih_option_set_help (_("The cgroup manager daemon"));

	args = nih_option_parser (NULL, argc, argv, options, FALSE);
	if (! args)
		exit (1);

	if (!setup_cgroup_dir()) {
		nih_fatal("Failed to set up cgmanager socket");
		exit(1);
	}

	/* Setup the DBus server */
	server = nih_dbus_server (CGMANAGER_DBUS_PATH, client_connect,
				  client_disconnect);
	nih_assert (server != NULL);

	if (!setup_base_run_path()) {
		nih_fatal("Error setting up base cgroup path");
		return -1;
	}

	if (collect_subsystems(extra_cgroup_mounts) < 0)
	{
		nih_fatal("failed to collect cgroup subsystems");
		exit(1);
	}

	if (!create_agent_symlinks()) {
		nih_fatal("Error creating release agent symlinks");
		exit(1);
	}

	if (setup_cgroup_mounts() < 0) {
		nih_fatal ("Failed to set up cgroup mounts");
		exit(1);
	}

	if (!move_self_to_root()) {
		nih_fatal ("Failed to move self to root cgroup");
		exit(1);
	}

	if (stat("/proc/self/ns/pid", &sb) == 0) {
		mypidns = read_pid_ns_link(getpid());
		setns_pid_supported = true;
	}

	if (stat("/proc/self/ns/user", &sb) == 0) {
		myuserns = read_user_ns_link(getpid());
		setns_user_supported = true;
	}

	newrlimit.rlim_cur = 10000;
	newrlimit.rlim_max = 10000;
	if (setrlimit(RLIMIT_NOFILE, &newrlimit) < 0)
		nih_warn("Failed to increase open file limit: %s",
			strerror(errno));

	/* Become daemon */
	if (daemonise) {
		if (nih_main_daemonise () < 0) {
			NihError *err;

			err = nih_error_get ();
			nih_fatal ("%s: %s", _("Unable to become daemon"),
					err->message);
			nih_free (err);

			exit (1);
		}
	}

	if (sigstop)
		raise(SIGSTOP);

	ret = nih_main_loop ();

	/* Destroy any PID file we may have created */
	if (daemonise) {
		nih_main_unlink_pidfile();
	}

	return ret;
}
int
my_method_sync (const void *        parent,
                NihDBusProxy *      proxy,
                MyMethodStructure **structure)
{
	DBusMessage *      method_call;
	DBusMessageIter    iter;
	DBusError          error;
	DBusMessage *      reply;
	MyMethodStructure *structure_local;
	DBusMessageIter    structure_local_iter;
	const char *       structure_local_item0_dbus;
	char *             structure_local_item0;
	uint32_t           structure_local_item1;

	nih_assert (proxy != NULL);
	nih_assert (structure != NULL);

	/* Construct the method call message. */
	method_call = dbus_message_new_method_call (proxy->name, proxy->path, "com.netsplit.Nih.Test", "Method");
	if (! method_call)
		nih_return_no_memory_error (-1);

	dbus_message_set_auto_start (method_call, proxy->auto_start);

	dbus_message_iter_init_append (method_call, &iter);

	/* Send the message, and wait for the reply. */
	dbus_error_init (&error);

	reply = dbus_connection_send_with_reply_and_block (proxy->connection, method_call, -1, &error);
	if (! reply) {
		dbus_message_unref (method_call);

		if (dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY)) {
			nih_error_raise_no_memory ();
		} else {
			nih_dbus_error_raise (error.name, error.message);
		}

		dbus_error_free (&error);
		return -1;
	}

	dbus_message_unref (method_call);

	/* Iterate the arguments of the reply */
	dbus_message_iter_init (reply, &iter);

	do {
		__label__ enomem;

		/* Demarshal a structure from the message */
		if (dbus_message_iter_get_arg_type (&iter) != DBUS_TYPE_STRUCT) {
			dbus_message_unref (reply);
			nih_return_error (-1, NIH_DBUS_INVALID_ARGS,
			                  _(NIH_DBUS_INVALID_ARGS_STR));
		}

		dbus_message_iter_recurse (&iter, &structure_local_iter);

		structure_local = nih_new (parent, MyMethodStructure);
		if (! structure_local) {
			*structure = NULL;
			goto enomem;
		}

		/* Demarshal a char * from the message */
		if (dbus_message_iter_get_arg_type (&structure_local_iter) != DBUS_TYPE_STRING) {
			nih_free (structure_local);
			dbus_message_unref (reply);
			nih_return_error (-1, NIH_DBUS_INVALID_ARGS,
			                  _(NIH_DBUS_INVALID_ARGS_STR));
		}

		dbus_message_iter_get_basic (&structure_local_iter, &structure_local_item0_dbus);

		structure_local_item0 = nih_strdup (structure_local, structure_local_item0_dbus);
		if (! structure_local_item0) {
			nih_free (structure_local);
			*structure = NULL;
			goto enomem;
		}

		dbus_message_iter_next (&structure_local_iter);

		structure_local->item0 = structure_local_item0;

		/* Demarshal a uint32_t from the message */
		if (dbus_message_iter_get_arg_type (&structure_local_iter) != DBUS_TYPE_UINT32) {
			nih_free (structure_local);
			dbus_message_unref (reply);
			nih_return_error (-1, NIH_DBUS_INVALID_ARGS,
			                  _(NIH_DBUS_INVALID_ARGS_STR));
		}

		dbus_message_iter_get_basic (&structure_local_iter, &structure_local_item1);

		dbus_message_iter_next (&structure_local_iter);

		structure_local->item1 = structure_local_item1;

		if (dbus_message_iter_get_arg_type (&structure_local_iter) != DBUS_TYPE_INVALID) {
			nih_free (structure_local);
			dbus_message_unref (reply);
			nih_return_error (-1, NIH_DBUS_INVALID_ARGS,
			                  _(NIH_DBUS_INVALID_ARGS_STR));
		}

		dbus_message_iter_next (&iter);

		*structure = structure_local;
	enomem: __attribute__ ((unused));
	} while (! *structure);

	if (dbus_message_iter_get_arg_type (&iter) != DBUS_TYPE_INVALID) {
		nih_free (structure_local);
		*structure = NULL;
		dbus_message_unref (reply);
		nih_return_error (-1, NIH_DBUS_INVALID_ARGS,
		                  _(NIH_DBUS_INVALID_ARGS_STR));
	}

	dbus_message_unref (reply);

	return 0;
}