Index: uspace/Makefile
===================================================================
--- uspace/Makefile	(revision 2df7d82445fd27fa3d7738624e0591a3d59ae237)
+++ uspace/Makefile	(revision b55f62aec16b8536367105bb57fa30532845c202)
@@ -72,4 +72,5 @@
 	app/sportdmp \
 	app/stats \
+	app/sysctl \
 	app/taskdump \
 	app/tester \
Index: uspace/app/sysctl/Makefile
===================================================================
--- uspace/app/sysctl/Makefile	(revision b55f62aec16b8536367105bb57fa30532845c202)
+++ uspace/app/sysctl/Makefile	(revision b55f62aec16b8536367105bb57fa30532845c202)
@@ -0,0 +1,37 @@
+#
+# Copyright (c) 2015 Michal Koutny
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# - Redistributions of source code must retain the above copyright
+#   notice, this list of conditions and the following disclaimer.
+# - Redistributions in binary form must reproduce the above copyright
+#   notice, this list of conditions and the following disclaimer in the
+#   documentation and/or other materials provided with the distribution.
+# - The name of the author may not be used to endorse or promote products
+#   derived from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+
+USPACE_PREFIX = ../..
+LIBS = $(LIBSYSMAN_PREFIX)/libsysman.a
+EXTRA_CFLAGS += -I$(LIBSYSMAN_PREFIX)/include
+BINARY = sysctl
+
+SOURCES = \
+	main.c
+
+include $(USPACE_PREFIX)/Makefile.common
Index: uspace/app/sysctl/main.c
===================================================================
--- uspace/app/sysctl/main.c	(revision b55f62aec16b8536367105bb57fa30532845c202)
+++ uspace/app/sysctl/main.c	(revision b55f62aec16b8536367105bb57fa30532845c202)
@@ -0,0 +1,141 @@
+/*
+ * Copyright (c) 2015 Michal Koutny
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup sysctl
+ * @{
+ */
+/** @file Control system manager.
+ */
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <str.h>
+#include <sysman/ctl.h>
+
+#define NAME "sysctl"
+#define NAME_BUFFER 256
+
+typedef struct {
+	const char *name;
+	int args;
+	int (* handler)(int, char **);
+} command_t;
+
+static const char *unit_state(unit_state_t s)
+{
+	switch (s) {
+	case STATE_EMBRYO:
+		return "embryo";
+	case STATE_STARTING:
+		return "starting";
+	case STATE_STARTED:
+		return "started";
+	case STATE_STOPPED:
+		return "stopped";
+	case STATE_FAILED:
+		return "failed";
+	default:
+		return "<unknown>";
+	}
+}
+
+static int list_units(int argc, char *argv[])
+{
+	unit_handle_t *units;
+	size_t unit_cnt;
+
+	int rc = sysman_get_units(&units, &unit_cnt);
+	if (rc != EOK) {
+		return rc;
+	}
+
+	for (unit_handle_t *it = units; it - units < (int)unit_cnt; ++it) {
+		char name[NAME_BUFFER];
+		unit_state_t state;
+
+		rc = sysman_unit_get_name(*it, name, NAME_BUFFER);
+		if (rc != EOK)
+			goto fail;
+
+		rc = sysman_unit_get_state(*it, &state);
+		if (rc != EOK)
+			goto fail;
+
+		printf("%-25s\t%s\n", name, unit_state(state));
+		continue;
+fail:
+		printf(" -- unit skipped due to IPC error (%i) --\n", rc);
+	}
+
+	return 0;
+}
+
+command_t commands[] = {
+	{ "list-units", 0, &list_units },
+	{ 0 }
+};
+
+static void print_syntax(void)
+{
+	printf("syntax:\n");
+	printf("\t%s\n", NAME);
+	// TODO update as functionality grows
+}
+
+
+int main(int argc, char *argv[])
+{
+	if (argc == 1) {
+		print_syntax();
+		return 0;
+	}
+
+	for (command_t *it = commands; it->name != NULL; ++it) {
+		if (str_cmp(it->name, argv[1])) {
+			continue;
+		}
+
+		int real_args = argc - 2;
+		if (it->args < real_args) {
+			printf("%s %s: too many arguments\n", NAME, it->name);
+			return 1;
+		} else if (it->args > real_args) {
+			printf("%s %s: too few arguments\n", NAME, it->name);
+			return 1;
+		} else {
+			return it->handler(real_args + 1, argv + 1);
+		}
+	}
+
+	printf("%s: unknown command '%s'\n", NAME, argv[1]);
+	return 1;
+}
+
+/** @}
+ */
Index: uspace/lib/c/include/ipc/sysman.h
===================================================================
--- uspace/lib/c/include/ipc/sysman.h	(revision 2df7d82445fd27fa3d7738624e0591a3d59ae237)
+++ uspace/lib/c/include/ipc/sysman.h	(revision b55f62aec16b8536367105bb57fa30532845c202)
@@ -44,5 +44,8 @@
 	SYSMAN_BROKER_EXP_ADDED,
 	SYSMAN_BROKER_EXP_REMOVED,
-	SYSMAN_CTL_UNIT_START
+	SYSMAN_CTL_UNIT_START,
+	SYSMAN_CTL_GET_UNITS,
+	SYSMAN_CTL_UNIT_GET_NAME,
+	SYSMAN_CTL_UNIT_GET_STATE
 } sysman_ipc_method_t;
 
@@ -53,4 +56,22 @@
 } sysman_interface_t;
 
+typedef sysarg_t unit_handle_t;
+
+typedef enum {
+	UNIT_TYPE_INVALID = -1,
+	UNIT_CONFIGURATION = 0,
+	UNIT_MOUNT,
+	UNIT_SERVICE,
+	UNIT_TARGET
+} unit_type_t;
+
+typedef enum {
+	STATE_EMBRYO = 0,
+	STATE_STARTING,
+	STATE_STARTED,
+	STATE_STOPPED,
+	STATE_FAILED
+} unit_state_t;
+
 #endif
 
Index: uspace/lib/sysman/include/sysman/ctl.h
===================================================================
--- uspace/lib/sysman/include/sysman/ctl.h	(revision 2df7d82445fd27fa3d7738624e0591a3d59ae237)
+++ uspace/lib/sysman/include/sysman/ctl.h	(revision b55f62aec16b8536367105bb57fa30532845c202)
@@ -30,7 +30,12 @@
 #define _SYSMAN_CTL_H
 
+#include <ipc/sysman.h>
 #include <sysman/unit.h>
 
 int sysman_unit_start(const char *, int);
 
+int sysman_get_units(unit_handle_t **, size_t *);
+
+int sysman_unit_get_name(unit_handle_t, char *, size_t);
+int sysman_unit_get_state(unit_handle_t, unit_state_t *);
 #endif
Index: uspace/lib/sysman/src/ctl.c
===================================================================
--- uspace/lib/sysman/src/ctl.c	(revision 2df7d82445fd27fa3d7738624e0591a3d59ae237)
+++ uspace/lib/sysman/src/ctl.c	(revision b55f62aec16b8536367105bb57fa30532845c202)
@@ -29,4 +29,5 @@
 #include <async.h>
 #include <errno.h>
+#include <stdlib.h>
 #include <str.h>
 #include <sysman/ctl.h>
@@ -57,2 +58,94 @@
 	return rc;
 }
+
+static int sysman_get_units_once(sysarg_t *buf, size_t buf_size,
+    size_t *act_size)
+{
+	async_exch_t *exch = sysman_exchange_begin(SYSMAN_PORT_CTL);
+
+	ipc_call_t answer;
+	aid_t req = async_send_0(exch, SYSMAN_CTL_GET_UNITS, &answer);
+	int rc = async_data_read_start(exch, buf, buf_size);
+
+	sysman_exchange_end(exch);
+
+	if (rc != EOK) {
+		async_forget(req);
+		return rc;
+	}
+
+	sysarg_t retval;
+	async_wait_for(req, &retval);
+
+	if (retval != EOK) {
+		return retval;
+	}
+
+	*act_size = IPC_GET_ARG1(answer);
+	return EOK;
+}
+
+int sysman_get_units(unit_handle_t **units_ptr, size_t *cnt_ptr)
+{
+	*units_ptr = NULL;
+	*cnt_ptr = 0;
+
+	unit_handle_t *units = NULL;
+	size_t alloc_size = 0;
+	size_t act_size = 0;
+
+	while (true) {
+		int rc = sysman_get_units_once(units, alloc_size, &act_size);
+		if (rc != EOK) {
+			return rc;
+		}
+
+		if (act_size <= alloc_size) {
+			break;
+		}
+
+		alloc_size = act_size;
+		units = realloc(units, alloc_size);
+		if (units == NULL) {
+			return ENOMEM;
+		}
+	}
+
+	*units_ptr = units;
+	*cnt_ptr = act_size / sizeof(unit_handle_t);
+	return EOK;
+}
+
+int sysman_unit_get_name(unit_handle_t handle, char *buf, size_t buf_size)
+{
+	async_exch_t *exch = sysman_exchange_begin(SYSMAN_PORT_CTL);
+
+	ipc_call_t answer;
+	aid_t req = async_send_1(exch, SYSMAN_CTL_UNIT_GET_NAME, handle, &answer);
+	int rc = async_data_read_start(exch, buf, buf_size);
+
+	sysman_exchange_end(exch);
+
+	if (rc != EOK) {
+		async_forget(req);
+		return rc;
+	}
+
+	sysarg_t retval;
+	async_wait_for(req, &retval);
+
+	if (retval != EOK) {
+		return retval;
+	}
+
+	return EOK;
+}
+
+int sysman_unit_get_state(unit_handle_t handle, unit_state_t *state)
+{
+	async_exch_t *exch = sysman_exchange_begin(SYSMAN_PORT_CTL);
+	int rc = async_req_1_1(exch, SYSMAN_CTL_UNIT_GET_STATE, handle, state);
+	sysman_exchange_end(exch);
+
+	return rc;
+}
Index: uspace/srv/sysman/configuration.c
===================================================================
--- uspace/srv/sysman/configuration.c	(revision 2df7d82445fd27fa3d7738624e0591a3d59ae237)
+++ uspace/srv/sysman/configuration.c	(revision b55f62aec16b8536367105bb57fa30532845c202)
@@ -43,6 +43,40 @@
 
 static hash_table_t units_by_name;
+static hash_table_t units_by_handle;
 
 /* Hash table functions */
+static size_t units_by_handle_ht_hash(const ht_link_t *item)
+{
+	unit_t *unit =
+	    hash_table_get_inst(item, unit_t, units_by_handle);
+	return unit->handle;
+}
+
+static size_t units_by_handle_ht_key_hash(void *key)
+{
+	return *(unit_handle_t *)key;
+}
+
+static bool units_by_handle_ht_equal(const ht_link_t *item1, const ht_link_t *item2)
+{
+	return
+	    hash_table_get_inst(item1, unit_t, units_by_handle) ==
+	    hash_table_get_inst(item2, unit_t, units_by_handle);
+}
+
+static bool units_by_handle_ht_key_equal(void *key, const ht_link_t *item)
+{
+	return *(unit_handle_t *)key ==
+	    hash_table_get_inst(item, unit_t, units_by_handle)->handle;
+}
+
+static hash_table_ops_t units_by_handle_ht_ops = {
+	.hash            = &units_by_handle_ht_hash,
+	.key_hash        = &units_by_handle_ht_key_hash,
+	.equal           = &units_by_handle_ht_equal,
+	.key_equal       = &units_by_handle_ht_key_equal,
+	.remove_callback = NULL // TODO realy unneeded?
+};
+
 static size_t units_by_name_ht_hash(const ht_link_t *item)
 {
@@ -59,7 +93,7 @@
 static bool units_by_name_ht_equal(const ht_link_t *item1, const ht_link_t *item2)
 {
-	return str_cmp(
-	    hash_table_get_inst(item1, unit_t, units_by_name)->name,
-	    hash_table_get_inst(item2, unit_t, units_by_name)->name) == 0;
+	return
+	    hash_table_get_inst(item1, unit_t, units_by_handle) ==
+	    hash_table_get_inst(item2, unit_t, units_by_handle);
 }
 
@@ -84,4 +118,5 @@
 {
 	hash_table_create(&units_by_name, 0, 0, &units_by_name_ht_ops);
+	hash_table_create(&units_by_handle, 0, 0, &units_by_handle_ht_ops);
 }
 
@@ -90,8 +125,13 @@
 	assert(unit);
 	assert(unit->state == STATE_EMBRYO);
+	assert(unit->handle == 0);
 	assert(unit->name != NULL);
 	sysman_log(LVL_DEBUG2, "%s('%s')", __func__, unit_name(unit));
 
 	if (hash_table_insert_unique(&units_by_name, &unit->units_by_name)) {
+		/* Pointers are same size as unit_handle_t both on 32b and 64b */
+		unit->handle = (unit_handle_t)unit;
+
+		hash_table_insert(&units_by_handle, &unit->units_by_handle);
 		list_append(&unit->units, &units);
 		return EOK;
@@ -218,2 +258,12 @@
 }
 
+unit_t *configuration_find_unit_by_handle(unit_handle_t handle)
+{
+	ht_link_t *ht_link = hash_table_find(&units_by_handle, &handle);
+	if (ht_link != NULL) {
+		return hash_table_get_inst(ht_link, unit_t, units_by_handle);
+	} else {
+		return NULL;
+	}
+}
+
Index: uspace/srv/sysman/configuration.h
===================================================================
--- uspace/srv/sysman/configuration.h	(revision 2df7d82445fd27fa3d7738624e0591a3d59ae237)
+++ uspace/srv/sysman/configuration.h	(revision b55f62aec16b8536367105bb57fa30532845c202)
@@ -31,4 +31,5 @@
 
 #include <adt/list.h>
+#include <ipc/sysman.h>
 
 #include "unit.h"
@@ -49,4 +50,5 @@
 
 extern unit_t *configuration_find_unit_by_name(const char *);
+extern unit_t *configuration_find_unit_by_handle(unit_handle_t);
 
 
Index: uspace/srv/sysman/connection_ctl.c
===================================================================
--- uspace/srv/sysman/connection_ctl.c	(revision 2df7d82445fd27fa3d7738624e0591a3d59ae237)
+++ uspace/srv/sysman/connection_ctl.c	(revision b55f62aec16b8536367105bb57fa30532845c202)
@@ -29,5 +29,7 @@
 #include <errno.h>
 #include <ipc/sysman.h>
+#include <macros.h>
 #include <stdlib.h>
+#include <str.h>
 
 #include "configuration.h"
@@ -61,5 +63,4 @@
 	job_del_ref(&job);
 }
-
 static void sysman_unit_start(ipc_callid_t iid, ipc_call_t *icall)
 {
@@ -77,4 +78,5 @@
 	sysman_log(LVL_DEBUG2, "%s(%s, %x)", __func__, unit_name, flags);
 
+	// TODO this is connection fibril, UNSYNCHRONIZED access to units!
 	unit_t *unit = configuration_find_unit_by_name(unit_name);
 	if (unit == NULL) {
@@ -109,4 +111,97 @@
 }
 
+static int fill_handles_buffer(unit_handle_t *buffer, size_t size,
+    size_t *act_size)
+{
+	if (size % sizeof(unit_handle_t) != 0) {
+		return EINVAL;
+	}
+
+	size_t filled = 0;
+	size_t to_fill = size / sizeof(unit_handle_t);
+	size_t total = 0;
+	list_foreach(units, units, unit_t, u) {
+		if (filled < to_fill) {
+			buffer[filled++] = u->handle;
+		}
+		++total;
+	}
+	*act_size = total * sizeof(unit_handle_t);
+	return EOK;
+}
+
+static void sysman_get_units(ipc_callid_t iid, ipc_call_t *icall)
+{
+	ipc_callid_t callid;
+	size_t size;
+	size_t act_size;
+	int rc;
+	
+	if (!async_data_read_receive(&callid, &size)) {
+		async_answer_0(callid, EREFUSED);
+		async_answer_0(iid, EREFUSED);
+		return;
+	}
+	
+	
+	unit_handle_t *handles = malloc(size);
+	if (handles == NULL && size > 0) {
+		async_answer_0(callid, ENOMEM);
+		async_answer_0(iid, ENOMEM);
+		return;
+	}
+	
+	
+	// TODO UNSYNCHRONIZED access to units!
+	rc = fill_handles_buffer(handles, size, &act_size);
+	if (rc != EOK) {
+		async_answer_0(callid, rc);
+		async_answer_0(iid, rc);
+		return;
+	}
+	
+	size_t real_size = min(act_size, size);
+	sysarg_t retval = async_data_read_finalize(callid, handles, real_size);
+	free(handles);
+	
+	async_answer_1(iid, retval, act_size);
+}
+
+static void sysman_unit_get_name(ipc_callid_t iid, ipc_call_t *icall)
+{
+	ipc_callid_t callid;
+	size_t size;
+	
+	if (!async_data_read_receive(&callid, &size)) {
+		async_answer_0(callid, EREFUSED);
+		async_answer_0(iid, EREFUSED);
+		return;
+	}
+	
+	// TODO UNSYNCHRONIZED access to units!
+	unit_t *u = configuration_find_unit_by_handle(IPC_GET_ARG1(*icall));
+	if (u == NULL) {
+		async_answer_0(callid, ENOENT);
+		async_answer_0(iid, ENOENT);
+		return;
+	}
+	
+	size_t real_size = min(str_size(u->name) + 1, size);
+	sysarg_t retval = async_data_read_finalize(callid, u->name, real_size);
+	
+	async_answer_0(iid, retval);
+}
+
+static void sysman_unit_get_state(ipc_callid_t iid, ipc_call_t *icall)
+{
+	// TODO UNSYNCHRONIZED access to units!
+	unit_t *u = configuration_find_unit_by_handle(IPC_GET_ARG1(*icall));
+	if (u == NULL) {
+		async_answer_0(iid, ENOENT);
+	} else {
+		async_answer_1(iid, EOK, u->state);
+	}
+}
+
 void sysman_connection_ctl(ipc_callid_t iid, ipc_call_t *icall)
 {
@@ -128,4 +223,13 @@
 			sysman_unit_start(callid, &call);
 			break;
+		case SYSMAN_CTL_GET_UNITS:
+			sysman_get_units(callid, &call);
+			break;
+		case SYSMAN_CTL_UNIT_GET_NAME:
+			sysman_unit_get_name(callid, &call);
+			break;
+		case SYSMAN_CTL_UNIT_GET_STATE:
+			sysman_unit_get_state(callid, &call);
+			break;
 		default:
 			async_answer_0(callid, ENOENT);
Index: uspace/srv/sysman/sm_task.c
===================================================================
--- uspace/srv/sysman/sm_task.c	(revision 2df7d82445fd27fa3d7738624e0591a3d59ae237)
+++ uspace/srv/sysman/sm_task.c	(revision b55f62aec16b8536367105bb57fa30532845c202)
@@ -89,4 +89,6 @@
 	sm_task_event_t *tev = data;
 
+	sysman_log(LVL_DEBUG2, "%s, %" PRIu64 " %i",
+	    __func__, tev->task_id, tev->flags);
 	unit_svc_t *u_svc = sm_task_find_service(tev->task_id);
 	if (u_svc == NULL) {
Index: uspace/srv/sysman/unit.h
===================================================================
--- uspace/srv/sysman/unit.h	(revision 2df7d82445fd27fa3d7738624e0591a3d59ae237)
+++ uspace/srv/sysman/unit.h	(revision b55f62aec16b8536367105bb57fa30532845c202)
@@ -39,20 +39,5 @@
 #include <conf/text_parse.h>
 #include <fibril_synch.h>
-
-typedef enum {
-	UNIT_TYPE_INVALID = -1,
-	UNIT_CONFIGURATION = 0,
-	UNIT_MOUNT,
-	UNIT_SERVICE,
-	UNIT_TARGET
-} unit_type_t;
-
-typedef enum {
-	STATE_EMBRYO = 0,
-	STATE_STARTING,
-	STATE_STARTED,
-	STATE_STOPPED,
-	STATE_FAILED
-} unit_state_t;
+#include <ipc/sysman.h>
 
 /* Forward declarations */
@@ -66,4 +51,7 @@
 	/** Link to name-to-unit hash table */
 	ht_link_t units_by_name;
+
+	/** Link to handle-to-unit hash table */
+	ht_link_t units_by_handle;
 
 	/** Link to list of all units */
@@ -80,4 +68,5 @@
 	job_t *job;
 
+	unit_handle_t handle;
 	unit_type_t type;
 	char *name;
