You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@celix.apache.org by pn...@apache.org on 2020/02/02 21:17:25 UTC

[celix] 01/01: gh-144: Adds initial impl for the query command.

This is an automated email from the ASF dual-hosted git repository.

pnoltes pushed a commit to branch feature/query_command
in repository https://gitbox.apache.org/repos/asf/celix.git

commit 59f578393047e4531afd7b53eafa624b24c6617a
Author: Pepijn Noltes <pe...@gmail.com>
AuthorDate: Sun Feb 2 22:16:29 2020 +0100

    gh-144: Adds initial impl for the query command.
    
    Also refactors the shell impl. Adds a much needed mutex.
---
 bundles/shell/remote_shell/src/shell_mediator.c    |   2 +-
 bundles/shell/shell/CMakeLists.txt                 |   2 +
 bundles/shell/shell/include/command.h              |   2 +-
 bundles/shell/shell/include/shell.h                |  31 +-
 bundles/shell/shell/src/activator.c                | 183 +++++++-----
 bundles/shell/shell/src/help_command.c             |  12 +-
 bundles/shell/shell/src/lb_command.c               |   3 +-
 bundles/shell/shell/src/shell.c                    | 332 +++++++--------------
 bundles/shell/shell/src/shell_private.h            |  30 +-
 bundles/shell/shell/src/std_commands.h             |   3 +
 bundles/shell/shell_tui/private/src/shell_tui.c    |   8 +-
 .../shell_wui/src/shell_wui_bundle_activator.c     |   2 +-
 libs/framework/CMakeLists.txt                      |   1 +
 libs/framework/include/celix_bundle.h              |  55 ++++
 libs/framework/include/service_registration.h      |   4 +
 libs/framework/include/service_registry.h          |  31 +-
 libs/framework/private/mock/bundle_mock.c          |  20 ++
 .../private/mock/service_registration_mock.c       |   5 +
 .../framework/private/mock/service_registry_mock.c |  25 ++
 libs/framework/src/bundle.c                        |  59 ++++
 libs/framework/src/bundle_context.c                |  12 +-
 libs/framework/src/bundle_context_private.h        |   4 +-
 libs/framework/src/service_registration.c          |  17 +-
 libs/framework/src/service_registry.c              |  55 ++++
 libs/framework/src/service_tracker.c               |  35 ++-
 25 files changed, 554 insertions(+), 379 deletions(-)

diff --git a/bundles/shell/remote_shell/src/shell_mediator.c b/bundles/shell/remote_shell/src/shell_mediator.c
index 0839be2..e12abb2 100644
--- a/bundles/shell/remote_shell/src/shell_mediator.c
+++ b/bundles/shell/remote_shell/src/shell_mediator.c
@@ -110,7 +110,7 @@ celix_status_t shellMediator_executeCommand(shell_mediator_pt instance, char *co
 
 
 	if (instance->shellService != NULL) {
-		instance->shellService->executeCommand(instance->shellService->shell, command, out, err);
+		instance->shellService->executeCommand(instance->shellService->handle, command, out, err);
 	}
 
 	celixThreadMutex_unlock(&instance->mutex);
diff --git a/bundles/shell/shell/CMakeLists.txt b/bundles/shell/shell/CMakeLists.txt
index 4d59345..c1037b7 100644
--- a/bundles/shell/shell/CMakeLists.txt
+++ b/bundles/shell/shell/CMakeLists.txt
@@ -44,6 +44,8 @@ if (SHELL)
           src/inspect_command
           src/help_command
 		  src/dm_shell_list_command
+		  src/query_command.c
+		  src/q_command.c
 	)
 	target_include_directories(shell PRIVATE src)
 	target_link_libraries(shell PRIVATE Celix::shell_api CURL::libcurl Celix::log_service_api Celix::log_helper)
diff --git a/bundles/shell/shell/include/command.h b/bundles/shell/shell/include/command.h
index 3ab3f2a..df1310b 100644
--- a/bundles/shell/shell/include/command.h
+++ b/bundles/shell/shell/include/command.h
@@ -49,7 +49,7 @@ typedef command_service_t * command_service_pt;
  */
 struct commandService {
     void *handle;
-    celix_status_t (*executeCommand)(void *handle, char * commandLine, FILE *outStream, FILE *errorStream);
+    celix_status_t (*executeCommand)(void *handle, char* commandLine, FILE *outStream, FILE *errorStream);
 };
 
 
diff --git a/bundles/shell/shell/include/shell.h b/bundles/shell/shell/include/shell.h
index 54bc6e8..306c881 100644
--- a/bundles/shell/shell/include/shell.h
+++ b/bundles/shell/shell/include/shell.h
@@ -31,18 +31,31 @@
 #include "service_reference.h"
 
 static const char * const OSGI_SHELL_SERVICE_NAME = "shellService";
-
-typedef struct shell shell_t;
-typedef shell_t* shell_pt;
+static const char * const OSGI_SHELL_SERVICE_VERSION = "2.0.0";
 
 struct shellService {
-	shell_pt shell;
+	void *handle;
+
+	/**
+	 * List the registered command names. Caller is owner of the commands.
+	 * @return A celix array list with char*.
+	 */
+	celix_status_t (*getCommands)(void *handle, celix_array_list_t **commands);
+
+	/**
+	 * Gets the usage info for the provided command str. Caller is owner.
+	 */
+	celix_status_t (*getCommandUsage)(void *handle, const char *commandName, char **UsageStr);
+
+    /**
+     * Gets the usage info for the provided command str. Caller is owner.
+     */
+	celix_status_t (*getCommandDescription)(void *handle, const char *commandName, char **commandDescription);
 
-	celix_status_t (*getCommands)(shell_pt shell_ptr, celix_array_list_t **commands_ptr);
-	celix_status_t (*getCommandUsage)(shell_pt shell_ptr, char *command_name_str, char **usage_str);
-	celix_status_t (*getCommandDescription)(shell_pt shell_ptr, char *command_name_str, char **command_description_str);
-	celix_status_t (*getCommandReference)(shell_pt shell_ptr, char *command_name_str, service_reference_pt *command_reference_ptr);
-	celix_status_t (*executeCommand)(shell_pt shell_ptr, char * command_line_str, FILE *out, FILE *err);
+	/**
+	 * Try to execute a commmand using the provided command line.
+	 */
+	celix_status_t (*executeCommand)(void *handle, const char *commandLine, FILE *out, FILE *err);
 };
 
 typedef struct shellService shell_service_t;
diff --git a/bundles/shell/shell/src/activator.c b/bundles/shell/shell/src/activator.c
index 67c06e6..291e802 100644
--- a/bundles/shell/shell/src/activator.c
+++ b/bundles/shell/shell/src/activator.c
@@ -33,50 +33,51 @@
 #include "service_tracker.h"
 #include "celix_constants.h"
 
-#define NUMBER_OF_COMMANDS 11
+#define NUMBER_OF_COMMANDS 13
 
 struct command {
     celix_status_t (*exec)(void *handle, char *commandLine, FILE *out, FILE *err);
     char *name;
     char *description;
     char *usage;
-    command_service_pt service;
-    properties_pt props;
+    command_service_t service;
+    celix_properties_t *props;
     long svcId; //used for service (un)registration
 };
 
-struct bundle_instance {
-	shell_service_pt shellService;
-	service_registration_pt registration;
-    service_tracker_pt tracker;
+struct shell_bundle_activator {
+    shell_t *shell;
+    shell_service_t shellService;
+	long shellSvcId;
+	long trackerId;
 
     struct command std_commands[NUMBER_OF_COMMANDS];
 };
 
-typedef struct bundle_instance *bundle_instance_pt;
+typedef struct shell_bundle_activator shell_bundle_activator_t;
 
-celix_status_t bundleActivator_create(bundle_context_pt context_ptr, void **_pptr) {
+celix_status_t bundleActivator_create(celix_bundle_context_t* ctx, void **_pptr) {
 	celix_status_t status = CELIX_SUCCESS;
 
-    bundle_instance_pt instance_ptr = NULL;
+    shell_bundle_activator_t* activator = NULL;
 
-    if (!_pptr || !context_ptr) {
+    if (!_pptr || !ctx) {
         status = CELIX_ENOMEM;
     }
 
     if (status == CELIX_SUCCESS) {
-        instance_ptr = (bundle_instance_pt) calloc(1, sizeof(struct bundle_instance));
-        if (!instance_ptr) {
+        activator = calloc(1, sizeof(*activator));
+        if (!activator) {
             status = CELIX_ENOMEM;
         }
     }
 
     if (status == CELIX_SUCCESS) {
-        status = shell_create(context_ptr, &instance_ptr->shellService);
+        activator->shell = shell_create(ctx);
     }
 
     if (status == CELIX_SUCCESS) {
-        instance_ptr->std_commands[0] =
+        activator->std_commands[0] =
                 (struct command) {
                         .exec = lbCommand_execute,
                         .name = "lb",
@@ -85,162 +86,191 @@ celix_status_t bundleActivator_create(bundle_context_pt context_ptr, void **_ppt
                             "\nUse -l to print the bundle locations.\nUse -s to print the bundle symbolic names\nUse -u to print the bundle update location.",
                         .usage = "lb [-l | -s | -u | -a] [group]"
                 };
-        instance_ptr->std_commands[1] =
+        activator->std_commands[1] =
                 (struct command) {
                         .exec = startCommand_execute,
                         .name = "start",
                         .description = "start bundle(s).",
                         .usage = "start <id> [<id> ...]"
                 };
-        instance_ptr->std_commands[2] =
+        activator->std_commands[2] =
                 (struct command) {
                         .exec = stopCommand_execute,
                         .name = "stop",
                         .description = "stop bundle(s).",
                         .usage = "stop <id> [<id> ...]"
                 };
-        instance_ptr->std_commands[3] =
+        activator->std_commands[3] =
                 (struct command) {
                         .exec = installCommand_execute,
                         .name = "install",
                         .description = "install bundle(s).",
                         .usage = "install <file> [<file> ...]"
                 };
-        instance_ptr->std_commands[4] =
+        activator->std_commands[4] =
                 (struct command) {
                         .exec = uninstallCommand_execute,
                         .name = "uninstall",
                         .description = "uninstall bundle(s).",
                         .usage = "uninstall <file> [<file> ...]"
                 };
-        instance_ptr->std_commands[5] =
+        activator->std_commands[5] =
                 (struct command) {
                         .exec = updateCommand_execute,
                         .name = "update",
                         .description = "update bundle(s).",
                         .usage = "update <id> [<URL>]"
                 };
-        instance_ptr->std_commands[6] =
+        activator->std_commands[6] =
                 (struct command) {
                         .exec = helpCommand_execute,
                         .name = "help",
                         .description = "display available commands and description.",
                         .usage = "help <command>]"
                 };
-        instance_ptr->std_commands[7] =
+        activator->std_commands[7] =
                 (struct command) {
                         .exec = logCommand_execute,
                         .name = "log",
                         .description = "print log.",
                         .usage = "log"
                 };
-        instance_ptr->std_commands[8] =
+        activator->std_commands[8] =
                 (struct command) {
                         .exec = inspectCommand_execute,
                         .name = "inspect",
                         .description = "inspect services and components.",
                         .usage = "inspect (service) (capability|requirement) [<id> ...]"
                 };
-        instance_ptr->std_commands[9] =
+        activator->std_commands[9] =
                 (struct command) {
                         .exec = dmListCommand_execute,
                         .name = "dm",
                         .description = "Gives an overview of the component managed by a dependency manager.",
                         .usage = "dm [wtf] [f|full] [<Bundle ID> [<Bundle ID> [...]]]"
                 };
-        instance_ptr->std_commands[10] =
-                (struct command) { NULL, NULL, NULL, NULL, NULL, NULL, -1L }; /*marker for last element*/
+        activator->std_commands[10] =
+                (struct command) {
+                    .exec = queryCommand_execute,
+                    .name = "query",
+                    .description = "Query services. Query for registered and requested services" \
+                    "\nIf a query is provided, only service with a service name containing the query will be displayed." \
+                    "\nOr if the query is a filter. the filter will be used. If a filter is used, the optional bundle id will be ignored."
+                    "\n\tIf the -v option is provided, also list the service properties." \
+                    "\n\tIf the -r option is provided, only query for requested services." \
+                    "\n\tIf the -p option is provided, only query for provided services.",
+                    .usage = "ls [bundleId] [-v] [-p] [-r] [query_name ...]"
+                };
+        activator->std_commands[11] =
+                (struct command) {
+                        .exec = qCommand_execute,
+                        .name = "q",
+                        .description = "Quit (exit) framework.",
+                        .usage = "q"
+                };
+        activator->std_commands[12] =
+                (struct command) { NULL, NULL, NULL, NULL, {NULL,NULL}, NULL, -1L }; /*marker for last element*/
 
         unsigned int i = 0;
-        while (instance_ptr->std_commands[i].exec != NULL) {
-            instance_ptr->std_commands[i].props = properties_create();
-            if (!instance_ptr->std_commands[i].props) {
+        while (activator->std_commands[i].exec != NULL) {
+            activator->std_commands[i].props = properties_create();
+            if (!activator->std_commands[i].props) {
                 status = CELIX_BUNDLE_EXCEPTION;
                 break;
             }
 
-            properties_set(instance_ptr->std_commands[i].props, OSGI_SHELL_COMMAND_NAME, instance_ptr->std_commands[i].name);
-            properties_set(instance_ptr->std_commands[i].props, OSGI_SHELL_COMMAND_USAGE, instance_ptr->std_commands[i].usage);
-            properties_set(instance_ptr->std_commands[i].props, OSGI_SHELL_COMMAND_DESCRIPTION, instance_ptr->std_commands[i].description);
-            properties_set(instance_ptr->std_commands[i].props, CELIX_FRAMEWORK_SERVICE_LANGUAGE, CELIX_FRAMEWORK_SERVICE_C_LANGUAGE);
+            celix_properties_set(activator->std_commands[i].props, OSGI_SHELL_COMMAND_NAME, activator->std_commands[i].name);
+            celix_properties_set(activator->std_commands[i].props, OSGI_SHELL_COMMAND_USAGE, activator->std_commands[i].usage);
+            celix_properties_set(activator->std_commands[i].props, OSGI_SHELL_COMMAND_DESCRIPTION, activator->std_commands[i].description);
+            celix_properties_set(activator->std_commands[i].props, CELIX_FRAMEWORK_SERVICE_LANGUAGE, CELIX_FRAMEWORK_SERVICE_C_LANGUAGE);
 
-            instance_ptr->std_commands[i].service = calloc(1, sizeof(*instance_ptr->std_commands[i].service));
-            if (!instance_ptr->std_commands[i].service) {
-                status = CELIX_ENOMEM;
-                break;
-            }
-
-            instance_ptr->std_commands[i].service->handle = context_ptr;
-            instance_ptr->std_commands[i].service->executeCommand = instance_ptr->std_commands[i].exec;
+            activator->std_commands[i].service.handle = ctx;
+            activator->std_commands[i].service.executeCommand = activator->std_commands[i].exec;
 
             i += 1;
         }
     }
 
     if (status == CELIX_SUCCESS) {
-        *_pptr = instance_ptr;
+        *_pptr = activator;
     }
 
 
     if (status != CELIX_SUCCESS) {
-        bundleActivator_destroy(instance_ptr, context_ptr);
+        bundleActivator_destroy(activator, ctx);
     }
 
 	return status;
 }
 
-celix_status_t bundleActivator_start(void *_ptr, bundle_context_pt context_ptr) {
+celix_status_t bundleActivator_start(void *activatorData, celix_bundle_context_t* ctx) {
 	celix_status_t status = CELIX_SUCCESS;
 
-	bundle_instance_pt instance_ptr  = (bundle_instance_pt) _ptr;
+    shell_bundle_activator_t* activator  = (shell_bundle_activator_t*) activatorData;
 
-    if (!instance_ptr || !context_ptr) {
+    if (!activator || !ctx) {
         status = CELIX_ILLEGAL_ARGUMENT;
     }
 
     if (status == CELIX_SUCCESS) {
-        properties_pt props = properties_create();
-        properties_set(props, CELIX_FRAMEWORK_SERVICE_LANGUAGE, CELIX_FRAMEWORK_SERVICE_C_LANGUAGE);
-        status = bundleContext_registerService(context_ptr, (char *) OSGI_SHELL_SERVICE_NAME, instance_ptr->shellService, props, &instance_ptr->registration);
+        activator->shellService.handle = activator->shell;
+        activator->shellService.executeCommand = (void*)shell_executeCommand;
+        activator->shellService.getCommandDescription = (void*)shell_getCommandDescription;
+        activator->shellService.getCommandUsage = (void*)shell_getCommandUsage;
+        activator->shellService.getCommands = (void*)shell_getCommands;
+
+        celix_service_registration_options_t opts = CELIX_EMPTY_SERVICE_REGISTRATION_OPTIONS;
+        opts.serviceName = OSGI_SHELL_SERVICE_NAME;
+        opts.serviceVersion = OSGI_SHELL_SERVICE_VERSION;
+        opts.svc = &activator->shellService;
+
+        activator->shellSvcId = celix_bundleContext_registerServiceWithOptions(ctx, &opts);
     }
 
 	if (status == CELIX_SUCCESS) {
-        service_tracker_customizer_pt cust = NULL;
-        serviceTrackerCustomizer_create(instance_ptr->shellService->shell, NULL, (void *)shell_addCommand, NULL, (void *)shell_removeCommand, &cust);
-        serviceTracker_create(context_ptr, (char *)OSGI_SHELL_COMMAND_SERVICE_NAME, cust, &instance_ptr->tracker);
-        serviceTracker_open(instance_ptr->tracker);
+	    celix_service_tracking_options_t opts = CELIX_EMPTY_SERVICE_TRACKING_OPTIONS;
+	    opts.callbackHandle = activator->shell;
+	    opts.addWithProperties = (void*) shell_addCommand;
+	    opts.removeWithProperties = (void*) shell_removeCommand;
+	    opts.filter.ignoreServiceLanguage = true;
+	    opts.filter.serviceName = OSGI_SHELL_COMMAND_SERVICE_NAME;
+	    activator->trackerId = celix_bundleContext_trackServicesWithOptions(ctx, &opts);
     }
 
 
     if (status == CELIX_SUCCESS) {
-        for (unsigned int i = 0; instance_ptr->std_commands[i].exec != NULL; i++) {
+        for (unsigned int i = 0; activator->std_commands[i].exec != NULL; i++) {
             celix_service_registration_options_t opts = CELIX_EMPTY_SERVICE_REGISTRATION_OPTIONS;
-            opts.svc = instance_ptr->std_commands[i].service;
+            opts.svc = &activator->std_commands[i].service;
             opts.serviceName = OSGI_SHELL_COMMAND_SERVICE_NAME;
             opts.serviceVersion = OSGI_SHELL_COMMAND_SERVICE_VERSION;
-            opts.properties = instance_ptr->std_commands[i].props;
-            instance_ptr->std_commands[i].svcId = celix_bundleContext_registerServiceWithOptions(context_ptr, &opts);
+            opts.properties = activator->std_commands[i].props;
+            activator->std_commands[i].svcId = celix_bundleContext_registerServiceWithOptions(ctx, &opts);
         }
 	}
 
 	return status;
 }
 
-celix_status_t bundleActivator_stop(void *_ptr, bundle_context_pt context_ptr) {
+celix_status_t bundleActivator_stop(void *activatorData, celix_bundle_context_t* ctx) {
     celix_status_t status = CELIX_SUCCESS;
 
-    bundle_instance_pt instance_ptr = (bundle_instance_pt) _ptr;
+    shell_bundle_activator_t* activator = activatorData;
 
-    if (instance_ptr) {
-        for (unsigned int i = 0; instance_ptr->std_commands[i].exec != NULL; i++) {
-            if (instance_ptr->std_commands[i].svcId >= 0) {
-                celix_bundleContext_unregisterService(context_ptr, instance_ptr->std_commands[i].svcId);
-                instance_ptr->std_commands[i].props = NULL;
+    if (activator) {
+        for (unsigned int i = 0; activator->std_commands[i].exec != NULL; i++) {
+            if (activator->std_commands[i].svcId >= 0) {
+                celix_bundleContext_unregisterService(ctx, activator->std_commands[i].svcId);
+                activator->std_commands[i].props = NULL;
             }
         }
 
-        if (instance_ptr->tracker != NULL) {
-            serviceTracker_close(instance_ptr->tracker);
+        if (activator->shellSvcId >= 0L) {
+            celix_bundleContext_unregisterService(ctx, activator->shellSvcId);
+        }
+
+        if (activator->trackerId >= 0L) {
+            celix_bundleContext_stopTracker(ctx, activator->trackerId);
         }
     } else {
         status = CELIX_ILLEGAL_ARGUMENT;
@@ -249,25 +279,14 @@ celix_status_t bundleActivator_stop(void *_ptr, bundle_context_pt context_ptr) {
     return status;
 }
 
-celix_status_t bundleActivator_destroy(void *_ptr, bundle_context_pt __attribute__((__unused__)) context_ptr) {
+celix_status_t bundleActivator_destroy(void *activatorData, celix_bundle_context_t* __attribute__((__unused__)) ctx) {
     celix_status_t status = CELIX_SUCCESS;
+    shell_bundle_activator_t* activator = activatorData;
 
-    bundle_instance_pt instance_ptr = (bundle_instance_pt) _ptr;
-
-    if (instance_ptr) {
-        serviceRegistration_unregister(instance_ptr->registration);
-
-        for (unsigned int i = 0; instance_ptr->std_commands[i].exec != NULL; i++) {
-            free(instance_ptr->std_commands[i].service);
-        }
-
-        shell_destroy(&instance_ptr->shellService);
-
-        if (instance_ptr->tracker != NULL) {
-            serviceTracker_destroy(instance_ptr->tracker);
-        }
 
-        free(instance_ptr);
+    if (activator) {
+        shell_destroy(activator->shell);
+        free(activator);
     } else {
         status = CELIX_ILLEGAL_ARGUMENT;
     }
diff --git a/bundles/shell/shell/src/help_command.c b/bundles/shell/shell/src/help_command.c
index 1c29ab2..91550cc 100644
--- a/bundles/shell/shell/src/help_command.c
+++ b/bundles/shell/shell/src/help_command.c
@@ -52,7 +52,7 @@ static void printHelp(void *handle, void *svc) {
         unsigned int i;
         array_list_pt commands = NULL;
 
-        shell->getCommands(shell->shell, &commands);
+        shell->getCommands(shell->handle, &commands);
         for (i = 0; i < arrayList_size(commands); i++) {
             char *name = arrayList_get(commands, i);
             fprintf(out, "%s\n", name);
@@ -64,15 +64,15 @@ static void printHelp(void *handle, void *svc) {
         celix_status_t sub_status_usage;
         int i;
         celix_array_list_t *commands = NULL;
-        shell->getCommands(shell->shell, &commands);
+        shell->getCommands(shell->handle, &commands);
         for (i = 0; i < arrayList_size(commands); i++) {
             char *name = arrayList_get(commands, i);
             if (strcmp(sub, name) == 0) {
                 char *usage_str = NULL;
                 char *desc_str = NULL;
 
-                sub_status_desc = shell->getCommandDescription(shell->shell, name, &desc_str);
-                sub_status_usage = shell->getCommandUsage(shell->shell, name, &usage_str);
+                sub_status_desc = shell->getCommandDescription(shell->handle, name, &desc_str);
+                sub_status_usage = shell->getCommandUsage(shell->handle, name, &usage_str);
 
                 if (sub_status_usage == CELIX_SUCCESS && sub_status_desc == CELIX_SUCCESS) {
                     fprintf(out, "Command     : %s\n", name);
@@ -81,7 +81,11 @@ static void printHelp(void *handle, void *svc) {
                 } else {
                     fprintf(err, "Error retrieving help info for command '%s'\n", sub);
                 }
+
+                free(usage_str);
+                free(desc_str);
             }
+            free(name);
         }
         celix_arrayList_destroy(commands);
     }
diff --git a/bundles/shell/shell/src/lb_command.c b/bundles/shell/shell/src/lb_command.c
index ffeb9ce..8734a6c 100644
--- a/bundles/shell/shell/src/lb_command.c
+++ b/bundles/shell/shell/src/lb_command.c
@@ -264,8 +264,7 @@ celix_status_t lbCommand_execute(void *_ptr, char *command_line_str, FILE *out_p
     lb_options_t opts;
     memset(&opts, 0, sizeof(opts));
 
-    const char* config = NULL;
-    bundleContext_getPropertyWithDefault(ctx, SHELL_USE_ANSI_COLORS, SHELL_USE_ANSI_COLORS_DEFAULT_VALUE, &config);
+    const char* config = celix_bundleContext_getProperty(ctx, SHELL_USE_ANSI_COLORS, SHELL_USE_ANSI_COLORS_DEFAULT_VALUE);
     opts.useColors = config != NULL && strncmp("true", config, 5) == 0;
 
 
diff --git a/bundles/shell/shell/src/shell.c b/bundles/shell/shell/src/shell.c
index ce90108..bc67700 100644
--- a/bundles/shell/shell/src/shell.c
+++ b/bundles/shell/shell/src/shell.c
@@ -16,17 +16,12 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-/**
- * shell.c
- *
- *  \date       Aug 13, 2010
- *  \author    	<a href="mailto:dev@celix.apache.org">Apache Celix Project Team</a>
- *  \copyright	Apache License, Version 2.0
- */
+
 
 #include <stdlib.h>
 #include <string.h>
 #include <log_helper.h>
+#include <celix_constants.h>
 
 #include "celix_errno.h"
 
@@ -35,271 +30,154 @@
 
 #include "utils.h"
 
-celix_status_t shell_getCommands(shell_pt shell_ptr, array_list_pt *commands_ptr);
-celix_status_t shell_getCommandUsage(shell_pt shell_ptr, char *command_name_str, char **usage_pstr);
-celix_status_t shell_getCommandDescription(shell_pt shell_ptr, char *command_name_str, char **command_description_pstr);
 
-celix_status_t shell_create(bundle_context_pt context_ptr, shell_service_pt *shell_service_ptr) {
-	celix_status_t status = CELIX_SUCCESS;
+shell_t* shell_create(celix_bundle_context_t *ctx) {
+    shell_t *shell = calloc(1, sizeof(*shell));
 
-	if (!context_ptr || !shell_service_ptr) {
-		status = CELIX_ILLEGAL_ARGUMENT;
-	}
-
-	if (status == CELIX_SUCCESS) {
-		*shell_service_ptr =  calloc(1, sizeof(**shell_service_ptr));
-		if (!*shell_service_ptr) {
-			status = CELIX_ENOMEM;
-		}
-	}
-
-	if (status == CELIX_SUCCESS) {
-		(*shell_service_ptr)->shell = calloc(1, sizeof(*(*shell_service_ptr)->shell));
-		if (!(*shell_service_ptr)->shell) {
-			status = CELIX_ENOMEM;
-		}
-	}
-
-	if (status == CELIX_SUCCESS) {
-		(*shell_service_ptr)->shell->bundle_context_ptr = context_ptr;
-		(*shell_service_ptr)->shell->command_name_map_ptr = hashMap_create(utils_stringHash, NULL, utils_stringEquals, NULL);
-		(*shell_service_ptr)->shell->command_reference_map_ptr = hashMap_create(NULL, NULL, NULL, NULL);
+    shell->ctx = ctx;
+    logHelper_create(ctx, &shell->logHelper);
 
-		(*shell_service_ptr)->getCommands = shell_getCommands;
-		(*shell_service_ptr)->getCommandDescription = shell_getCommandDescription;
-		(*shell_service_ptr)->getCommandUsage = shell_getCommandUsage;
-		(*shell_service_ptr)->getCommandReference = shell_getCommandReference;
-		(*shell_service_ptr)->executeCommand = shell_executeCommand;
+    celix_thread_mutexattr_t attr;
+    celixThreadMutexAttr_create(&attr);
+    celixThreadMutexAttr_settype(&attr, CELIX_THREAD_MUTEX_RECURSIVE); //NOTE recursive, because command can also use the shell service
+    celixThreadMutex_create(&shell->mutex, &attr);
+    shell->commandServices = hashMap_create(utils_stringHash, NULL, utils_stringEquals, NULL);
 
-        status = logHelper_create(context_ptr, &(*shell_service_ptr)->shell->logHelper);
-	}
-
-	if (status != CELIX_SUCCESS) {
-		shell_destroy(shell_service_ptr);
-	}
-
-	return status;
+    return shell;
 }
 
-celix_status_t shell_destroy(shell_service_pt *shell_service_ptr) {
-	celix_status_t status = CELIX_SUCCESS;
-
-	if (!shell_service_ptr || !*shell_service_ptr) {
-		status = CELIX_ILLEGAL_ARGUMENT;
-	}
-
-	if (status == CELIX_SUCCESS) {
-		if ((*shell_service_ptr)->shell) {
-			if ((*shell_service_ptr)->shell->command_name_map_ptr) {
-				hashMap_destroy((*shell_service_ptr)->shell->command_name_map_ptr, false, false);
-			}
-			if ((*shell_service_ptr)->shell->command_reference_map_ptr) {
-				hashMap_destroy((*shell_service_ptr)->shell->command_reference_map_ptr, false, false);
-			}
-			if ((*shell_service_ptr)->shell->logHelper) {
-				logHelper_destroy(&((*shell_service_ptr)->shell->logHelper));
-			}
-			free((*shell_service_ptr)->shell);
-			(*shell_service_ptr)->shell = NULL;
-		}
-		free(*shell_service_ptr);
-		*shell_service_ptr = NULL;
-	}
-
-	return status;
+void shell_destroy(shell_t *shell) {
+    if (shell != NULL) {
+        celixThreadMutex_destroy(&shell->mutex);
+        hashMap_destroy(shell->commandServices, false, false);
+        logHelper_destroy(&shell->logHelper);
+        free(shell);
+    }
 }
 
-celix_status_t shell_addCommand(shell_pt shell_ptr, service_reference_pt reference_ptr, void *svc) {
+celix_status_t shell_addCommand(shell_t *shell, command_service_t *svc, const celix_properties_t *props) {
     celix_status_t status = CELIX_SUCCESS;
-    command_service_pt command_ptr = NULL;
-    const char *name_str = NULL;
-
-    if (!shell_ptr || !reference_ptr) {
-        return CELIX_ILLEGAL_ARGUMENT;
-    }
-
-    if (status == CELIX_SUCCESS) {
-        command_ptr = svc;
-    }
-
-    if (status == CELIX_SUCCESS) {
-        status = serviceReference_getProperty(reference_ptr, "command.name", &name_str);
-        if (!name_str) {
-            logHelper_log(shell_ptr->logHelper, OSGI_LOGSERVICE_ERROR, "Command service must contain a 'command.name' property!");
-            status = CELIX_BUNDLE_EXCEPTION;
+    const char *name = celix_properties_get(props, "command.name", NULL);
+
+    if (name == NULL) {
+        logHelper_log(shell->logHelper, OSGI_LOGSERVICE_WARNING, "Command service must contain a 'command.name' property!");
+        status = CELIX_BUNDLE_EXCEPTION;
+    } else {
+        long svcId = celix_properties_getAsLong(props, OSGI_FRAMEWORK_SERVICE_ID, -1L);
+        celixThreadMutex_lock(&shell->mutex);
+        if (hashMap_containsKey(shell->commandServices, name)) {
+            logHelper_log(shell->logHelper, OSGI_LOGSERVICE_WARNING, "Command with name %s already registered!", name);
+        } else {
+            celix_shell_command_entry_t *entry = calloc(1, sizeof(*entry));
+            entry->svcId = svcId;
+            entry->svc = svc;
+            entry->props = props;
+            hashMap_put(shell->commandServices, (void*)name, entry);
         }
-    }
-
-    if (status == CELIX_SUCCESS) {
-        hashMap_put(shell_ptr->command_name_map_ptr, (char *)name_str, command_ptr);
-        hashMap_put(shell_ptr->command_reference_map_ptr, reference_ptr, command_ptr);
-    }
-
-    if (status != CELIX_SUCCESS) {
-        shell_removeCommand(shell_ptr, reference_ptr, svc);
-        char err[32];
-        celix_strerror(status, err, 32);
-        logHelper_log(shell_ptr->logHelper, OSGI_LOGSERVICE_ERROR, "Could not add command, got error %s\n", err);
+        celixThreadMutex_unlock(&shell->mutex);
     }
 
     return status;
 }
 
-celix_status_t shell_removeCommand(shell_pt shell_ptr, service_reference_pt reference_ptr, void *svc) {
+celix_status_t shell_removeCommand(shell_t *shell, command_service_t *svc, const celix_properties_t *props) {
     celix_status_t status = CELIX_SUCCESS;
-
-    command_service_pt command_ptr = NULL;
-    const char *name_str = NULL;
-
-    if (!shell_ptr || !reference_ptr) {
-        status = CELIX_ILLEGAL_ARGUMENT;
-    }
-
-    if (status == CELIX_SUCCESS) {
-        command_ptr = hashMap_remove(shell_ptr->command_reference_map_ptr, reference_ptr);
-        if (!command_ptr) {
-            status = CELIX_ILLEGAL_ARGUMENT;
-        }
-    }
-
-    if (status == CELIX_SUCCESS) {
-        status = serviceReference_getProperty(reference_ptr, "command.name", &name_str);
-        if (!name_str) {
-            status = CELIX_BUNDLE_EXCEPTION;
+    const char *name = celix_properties_get(props, "command.name", NULL);
+
+    if (name == NULL) {
+        logHelper_log(shell->logHelper, OSGI_LOGSERVICE_WARNING, "Command service must contain a 'command.name' property!");
+        status = CELIX_BUNDLE_EXCEPTION;
+    } else {
+        long svcId = celix_properties_getAsLong(props, OSGI_FRAMEWORK_SERVICE_ID, -1L);
+        celixThreadMutex_lock(&shell->mutex);
+        if (hashMap_containsKey(shell->commandServices, name)) {
+            celix_shell_command_entry_t *entry = hashMap_get(shell->commandServices, name);
+            if (entry->svcId == svcId) {
+                hashMap_remove(shell->commandServices, name);
+                free(entry);
+            } else {
+                logHelper_log(shell->logHelper, OSGI_LOGSERVICE_WARNING, "svc id for command with name %s does not match (%li == %li)!", name, svcId, entry->svcId);
+            }
+        } else {
+            logHelper_log(shell->logHelper, OSGI_LOGSERVICE_WARNING, "Cannot find shell command with name %s!", name);
         }
-    }
-
-    if (status == CELIX_SUCCESS) {
-        hashMap_remove(shell_ptr->command_name_map_ptr, (char *)name_str);
+        celixThreadMutex_unlock(&shell->mutex);
     }
 
     return status;
 }
 
-celix_status_t shell_getCommands(shell_pt shell_ptr, array_list_pt *commands_ptr) {
+celix_status_t shell_getCommands(shell_t *shell, celix_array_list_t **outCommands) {
 	celix_status_t status = CELIX_SUCCESS;
+	celix_array_list_t *result = celix_arrayList_create();
 
-	hash_map_iterator_pt iter = NULL;
-
-	if (!shell_ptr || !commands_ptr) {
-		status = CELIX_ILLEGAL_ARGUMENT;
-	}
-
-	if (status == CELIX_SUCCESS) {
-		iter = hashMapIterator_create(shell_ptr->command_name_map_ptr);
-		if (!iter) {
-			status = CELIX_BUNDLE_EXCEPTION;
-		}
-	}
+    celixThreadMutex_lock(&shell->mutex);
+    hash_map_iterator_t iter = hashMapIterator_construct(shell->commandServices);
+    while (hashMapIterator_hasNext(&iter)) {
+        const char *name = hashMapIterator_nextKey(&iter);
+        celix_arrayList_add(result, strndup(name, 1024*1024*10));
+    }
+    celixThreadMutex_unlock(&shell->mutex);
 
-	if (status == CELIX_SUCCESS) {
-		arrayList_create(commands_ptr);
-		while (hashMapIterator_hasNext(iter)) {
-			char *name_str = hashMapIterator_nextKey(iter);
-			arrayList_add(*commands_ptr, name_str);
-		}
-		hashMapIterator_destroy(iter);
-	}
+    *outCommands = result;
 
-	return status;
+    return status;
 }
 
 
-celix_status_t shell_getCommandUsage(shell_pt shell_ptr, char *command_name_str, char **usage_pstr) {
-	celix_status_t status = CELIX_SUCCESS;
-
-	service_reference_pt reference = NULL;
-
-	if (!shell_ptr || !command_name_str || !usage_pstr) {
-		status = CELIX_ILLEGAL_ARGUMENT;
-	}
+celix_status_t shell_getCommandUsage(shell_t *shell, const char *commandName, char **outUsage) {
+    celix_status_t status = CELIX_SUCCESS;
 
-	if (status == CELIX_SUCCESS) {
-		status = shell_getCommandReference(shell_ptr, command_name_str, &reference);
-		if (!reference) {
-			status = CELIX_BUNDLE_EXCEPTION;
-		}
-	}
+    celixThreadMutex_lock(&shell->mutex);
+    celix_shell_command_entry_t *entry = hashMap_get(shell->commandServices, commandName);
+    if (entry != NULL) {
+        const char *usage = celix_properties_get(entry->props, OSGI_SHELL_COMMAND_USAGE, "N/A");
+        *outUsage = strndup(usage, 1024*1024*10);
+    } else {
+        *outUsage = NULL;
+    }
+    celixThreadMutex_unlock(&shell->mutex);
 
-	if (status == CELIX_SUCCESS) {
-		status = serviceReference_getProperty(reference, "command.usage", (const char**)usage_pstr);
-	}
 
-	return status;
+    return status;
 }
 
-celix_status_t shell_getCommandDescription(shell_pt shell_ptr, char *command_name_str, char **command_description_pstr) {
-	celix_status_t status = CELIX_SUCCESS;
-
-	service_reference_pt reference = NULL;
-
-	if (!shell_ptr || !command_name_str || !command_description_pstr) {
-		status = CELIX_ILLEGAL_ARGUMENT;
-	}
-
-	if (status == CELIX_SUCCESS) {
-		status = shell_getCommandReference(shell_ptr, command_name_str, &reference);
-		if (!reference) {
-			status = CELIX_BUNDLE_EXCEPTION;
-		}
-	}
+celix_status_t shell_getCommandDescription(shell_t *shell, const char *commandName, char **outDescription) {
+    celix_status_t status = CELIX_SUCCESS;
 
-	if (status == CELIX_SUCCESS) {
-		serviceReference_getProperty(reference, "command.description", (const char**)command_description_pstr);
-	}
+    celixThreadMutex_lock(&shell->mutex);
+    celix_shell_command_entry_t *entry = hashMap_get(shell->commandServices, commandName);
+    if (entry != NULL) {
+        const char *desc = celix_properties_get(entry->props, OSGI_SHELL_COMMAND_DESCRIPTION, "N/A");
+        *outDescription = strndup(desc, 1024*1024*10);
+    } else {
+        *outDescription = NULL;
+    }
+    celixThreadMutex_unlock(&shell->mutex);
 
-	return status;
+    return status;
 }
 
-celix_status_t shell_getCommandReference(shell_pt shell_ptr, char *command_name_str, service_reference_pt *command_reference_ptr) {
+celix_status_t shell_executeCommand(shell_t *shell, const char *commandLine, FILE *out, FILE *err) {
 	celix_status_t status = CELIX_SUCCESS;
 
-	if (!shell_ptr || !command_name_str || !command_reference_ptr) {
-		status = CELIX_ILLEGAL_ARGUMENT;
-	}
-
 	if (status == CELIX_SUCCESS) {
-		*command_reference_ptr = NULL;
-		hash_map_iterator_pt iter = hashMapIterator_create(shell_ptr->command_reference_map_ptr);
-		while (hashMapIterator_hasNext(iter)) {
-			hash_map_entry_pt entry = hashMapIterator_nextEntry(iter);
-			service_reference_pt reference = hashMapEntry_getKey(entry);
-			const char *name_str = NULL;
-			serviceReference_getProperty(reference, "command.name", &name_str);
-			if (strcmp(name_str, command_name_str) == 0) {
-				*command_reference_ptr = (service_reference_pt) hashMapEntry_getKey(entry);
-				break;
-			}
-		}
-		hashMapIterator_destroy(iter);
-	}
-
-	return status;
-}
-
-celix_status_t shell_executeCommand(shell_pt shell_ptr, char *command_line_str, FILE *out, FILE *err) {
-	celix_status_t status = CELIX_SUCCESS;
-
-	command_service_pt command_ptr = NULL;
-
-	if (!shell_ptr || !command_line_str || !out || !err) {
-		status = CELIX_ILLEGAL_ARGUMENT;
-	}
+		size_t pos = strcspn(commandLine, " ");
 
-	if (status == CELIX_SUCCESS) {
-		size_t pos = strcspn(command_line_str, " ");
+		char *command_name_str = (pos != strlen(commandLine)) ? strndup(commandLine, pos) : strdup(commandLine);
 
-		char *command_name_str = (pos != strlen(command_line_str)) ? strndup(command_line_str, pos) : strdup(command_line_str);
-		command_ptr = hashMap_get(shell_ptr->command_name_map_ptr, command_name_str);
+        celixThreadMutex_lock(&shell->mutex);
+        celix_shell_command_entry_t *entry = hashMap_get(shell->commandServices, command_name_str);
+        if (entry == NULL) {
+            fprintf(err, "No such command\n");
+            status = CELIX_BUNDLE_EXCEPTION;
+        } else {
+            char * cl = (void*)commandLine; //NOTE needed because the shell command was written with char* instead of const char*
+            entry->svc->executeCommand(entry->svc->handle, cl, out, err);
+        }
+        celixThreadMutex_unlock(&shell->mutex);
 		free(command_name_str);
-		if (!command_ptr) {
-			fprintf(err, "No such command\n");
-			status = CELIX_BUNDLE_EXCEPTION;
-		}
-	}
 
-	if (status == CELIX_SUCCESS) {
-		status = command_ptr->executeCommand(command_ptr->handle, command_line_str, out, err);
 	}
 
 	return status;
diff --git a/bundles/shell/shell/src/shell_private.h b/bundles/shell/shell/src/shell_private.h
index c056bbd..b845ef1 100644
--- a/bundles/shell/shell/src/shell_private.h
+++ b/bundles/shell/shell/src/shell_private.h
@@ -33,19 +33,29 @@
 #include "command.h"
 #include "log_helper.h"
 
+typedef struct celix_shell_command_entry {
+    long svcId;
+    command_service_t *svc;
+    const celix_properties_t *props;
+} celix_shell_command_entry_t;
+
 struct shell {
-	bundle_context_pt bundle_context_ptr;
-	hash_map_pt command_reference_map_ptr;
-	hash_map_pt command_name_map_ptr;
-	log_helper_t *logHelper;
+	celix_bundle_context_t *ctx;
+    log_helper_t *logHelper;
+    celix_thread_mutex_t mutex; //protects below
+    hash_map_t *commandServices; //key = char* (command name), value = celix_shell_command_entry_t*
 };
+typedef struct shell shell_t;
+
+shell_t* shell_create(celix_bundle_context_t *ctx);
+void shell_destroy(shell_t *shell);
+celix_status_t shell_addCommand(shell_t *shell, command_service_t *svc, const celix_properties_t *props);
+celix_status_t shell_removeCommand(shell_t *shell, command_service_t *svc, const celix_properties_t *props);
 
-celix_status_t shell_create(bundle_context_pt context_ptr, shell_service_pt *shell_service_ptr);
-celix_status_t shell_destroy(shell_service_pt *shell_service_ptr);
-celix_status_t shell_addCommand(shell_pt shell_ptr, service_reference_pt reference_ptr, void *svc);
-celix_status_t shell_removeCommand(shell_pt shell_ptr, service_reference_pt reference_ptr, void *svc);
+celix_status_t shell_executeCommand(shell_t *shell, const char *commandLine, FILE *out, FILE *err);
 
-celix_status_t shell_getCommandReference(shell_pt shell_ptr, char *command_name_str, service_reference_pt *command_reference_ptr);
-celix_status_t shell_executeCommand(shell_pt shell_ptr, char *command_line_str, FILE *out, FILE *err);
+celix_status_t shell_getCommands(shell_t *shell, celix_array_list_t **commands);
+celix_status_t shell_getCommandUsage(shell_t *shell, const char *commandName, char **outUsage);
+celix_status_t shell_getCommandDescription(shell_t *shell, const char *commandName, char **outDescription);
 
 #endif /* SHELL_PRIVATE_H_ */
diff --git a/bundles/shell/shell/src/std_commands.h b/bundles/shell/shell/src/std_commands.h
index a0c66b4..ef15f93 100644
--- a/bundles/shell/shell/src/std_commands.h
+++ b/bundles/shell/shell/src/std_commands.h
@@ -31,7 +31,9 @@
 
 #define OSGI_SHELL_COMMAND_SEPARATOR " "
 
+//TODO update all char *command_line_str to const char *command_line_str
 celix_status_t lbCommand_execute(void *_ptr, char *command_line_str, FILE *out_ptr, FILE *err_ptr);
+celix_status_t queryCommand_execute(void *_ptr, char *command_line_str, FILE *out_ptr, FILE *err_ptr);
 celix_status_t startCommand_execute(void *_ptr, char* command_line_str, FILE *out_ptr, FILE *err_ptr);
 celix_status_t stopCommand_execute(void *handle, char* commandline, FILE *outStream, FILE *errStream);
 celix_status_t installCommand_execute(void *handle, char * commandline, FILE *outStream, FILE *errStream);
@@ -41,6 +43,7 @@ celix_status_t logCommand_execute(void *handle, char * commandline, FILE *outStr
 celix_status_t inspectCommand_execute(void *handle, char * commandline, FILE *outStream, FILE *errStream);
 celix_status_t helpCommand_execute(void *handle, char * commandline, FILE *outStream, FILE *errStream);
 celix_status_t dmListCommand_execute(void* handle, char * line, FILE *out, FILE *err);
+celix_status_t qCommand_execute(void *_ptr, char *command_line_str, FILE *sout, FILE *serr);
 
 
 #endif
diff --git a/bundles/shell/shell_tui/private/src/shell_tui.c b/bundles/shell/shell_tui/private/src/shell_tui.c
index bbb443d..57bd6d8 100644
--- a/bundles/shell/shell_tui/private/src/shell_tui.c
+++ b/bundles/shell/shell_tui/private/src/shell_tui.c
@@ -256,7 +256,7 @@ static void shellTui_parseInput(shell_tui_t* shellTui, shell_context_t* ctx) {
             celixThreadMutex_lock(&shellTui->mutex);
             if (shellTui->shell != NULL) {
                 printf("Providing command '%s' from in '%s'\n", line, in);
-                shellTui->shell->executeCommand(shellTui->shell->shell, line, stdout, stderr);
+                shellTui->shell->executeCommand(shellTui->shell->handle, line, stdout, stderr);
             } else {
                 fprintf(stderr, "Shell service not available\n");
             }
@@ -379,7 +379,7 @@ static void shellTui_parseInputForControl(shell_tui_t* shellTui, shell_context_t
         historyLineReset(hist);
         celixThreadMutex_lock(&shellTui->mutex);
         if (shellTui->shell != NULL) {
-            shellTui->shell->executeCommand(shellTui->shell->shell, line, stdout, stderr);
+            shellTui->shell->executeCommand(shellTui->shell->handle, line, stdout, stderr);
             pos = 0;
             nr_chars = 0;
         } else {
@@ -426,7 +426,7 @@ static void writeLine(const char* line, int pos) {
 static int autoComplete(shell_service_t* shellSvc, char *in, int cursorPos, size_t maxLen) {
 	array_list_pt commandList = NULL;
 	array_list_pt possibleCmdList = NULL;
-	shellSvc->getCommands(shellSvc->shell, &commandList);
+	shellSvc->getCommands(shellSvc->handle, &commandList);
 	int nrCmds = arrayList_size(commandList);
 	arrayList_create(&possibleCmdList);
 
@@ -446,7 +446,7 @@ static int autoComplete(shell_service_t* shellSvc, char *in, int cursorPos, size
 				if (strncmp(in, cmd, strlen(cmd)) == 0) {
 					clearLine();
 					char* usage = NULL;
-					shellSvc->getCommandUsage(shellSvc->shell, cmd, &usage);
+					shellSvc->getCommandUsage(shellSvc->handle, cmd, &usage);
 					printf("Usage:\n %s\n", usage);
 				}
 			}
diff --git a/bundles/shell/shell_wui/src/shell_wui_bundle_activator.c b/bundles/shell/shell_wui/src/shell_wui_bundle_activator.c
index 932509b..72d0f90 100644
--- a/bundles/shell/shell_wui/src/shell_wui_bundle_activator.c
+++ b/bundles/shell/shell_wui/src/shell_wui_bundle_activator.c
@@ -44,7 +44,7 @@ static void useShell(void *handle, void *svc) {
     char *buf = NULL;
     size_t size;
     FILE *out = open_memstream(&buf, &size);
-    shell->executeCommand(shell->shell, arg->command, out, out);
+    shell->executeCommand(shell->handle, arg->command, out, out);
     fclose(out);
     mg_websocket_write(arg->conn, MG_WEBSOCKET_OPCODE_TEXT, buf, size);
 };
diff --git a/libs/framework/CMakeLists.txt b/libs/framework/CMakeLists.txt
index 1946eb8..0ad8364 100644
--- a/libs/framework/CMakeLists.txt
+++ b/libs/framework/CMakeLists.txt
@@ -131,6 +131,7 @@ if (ENABLE_TESTING AND FRAMEWORK_TESTS)
         private/mock/bundle_revision_mock.c
         private/mock/resolver_mock.c
         private/mock/version_mock.c
+        private/mock/service_registry_mock.c
         src/bundle.c
         src/celix_errorcodes.c
         private/mock/celix_log_mock.c)
diff --git a/libs/framework/include/celix_bundle.h b/libs/framework/include/celix_bundle.h
index bd12693..0b0dac4 100644
--- a/libs/framework/include/celix_bundle.h
+++ b/libs/framework/include/celix_bundle.h
@@ -54,11 +54,66 @@ celix_bundle_state_e celix_bundle_getState(const celix_bundle_t *bnd);
  */
 char* celix_bundle_getEntry(const celix_bundle_t* bnd, const char *path);
 
+/**
+ * Returns the group of the bundle. Groups are used to order bundles.
+ * Note the return value is valid as long as the bundle is installed.
+ */
 const char* celix_bundle_getGroup(const celix_bundle_t *bnd);
 
+/**
+ * Returns the symbolic name of the bundle.
+ * Note the return value is valid as long as the bundle is installed.
+ */
 const char* celix_bundle_getSymbolicName(const celix_bundle_t *bnd);
 
 
+typedef struct celix_bundle_service_list_entry {
+    long serviceId;
+    long bundleOwner;
+    char *serviceName;
+    celix_properties_t *serviceProperties;
+    bool factory;
+} celix_bundle_service_list_entry_t;
+
+/**
+ * Returns a array list of registered service info entries for this bundle.
+ *
+ * @param ctx       The bundle context
+ * @param bndId     The bundle id for which the services should be listed
+ * @return          A celix array list with celix_bundle_service_list_entry_t*. Caller is owner of the celix array.
+ */
+celix_array_list_t* celix_bundle_listRegisteredServices(const celix_bundle_t *bnd);
+
+/**
+ * Utils function to free memory for the return of a celix_bundle_listRegisteredServices call.
+ */
+void celix_bundle_destroyRegisteredServicesList(celix_array_list_t* list);
+
+
+/**
+ * Service Tracker Info provided to the service tracker tracker callbacks.
+ */
+typedef struct celix_bundle_service_tracker_list_entry {
+    char* filter;;
+    long bundleOwner;
+} celix_bundle_service_tracker_list_entry_t;
+
+
+/**
+ * Returns a array list of service tracker info entries for this bundle.
+ *
+ * @param ctx       The bundle context
+ * @param bndId     The bundle id for which the services should be listed
+ * @return          A celix array list with celix_bundle_service_tracker_list_entry_t*. Caller is owner of the celix array.
+ */
+celix_array_list_t* celix_bundle_listServiceTrackers(const celix_bundle_t *bnd);
+
+/**
+ * Utils function to free memory for the return of a celix_bundle_listServiceTrackers call.
+ */
+void celix_bundle_destroyServiceTrackerList(celix_array_list_t* list);
+
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/libs/framework/include/service_registration.h b/libs/framework/include/service_registration.h
index 097001f..4a93ae9 100644
--- a/libs/framework/include/service_registration.h
+++ b/libs/framework/include/service_registration.h
@@ -48,6 +48,10 @@ serviceRegistration_getServiceName(service_registration_t *registration, const c
 FRAMEWORK_EXPORT long
 serviceRegistration_getServiceId(service_registration_t *registration);
 
+FRAMEWORK_EXPORT bool
+serviceRegistration_isFactoryService(service_registration_t *registration);
+
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/libs/framework/include/service_registry.h b/libs/framework/include/service_registry.h
index 3373328..9f06f8a 100644
--- a/libs/framework/include/service_registry.h
+++ b/libs/framework/include/service_registry.h
@@ -16,13 +16,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-/**
- * service_registry.h
- *
- *  \date       Aug 6, 2010
- *  \author    	<a href="mailto:dev@celix.apache.org">Apache Celix Project Team</a>
- *  \copyright	Apache License, Version 2.0
- */
+
 
 #ifndef SERVICE_REGISTRY_H_
 #define SERVICE_REGISTRY_H_
@@ -110,6 +104,29 @@ celix_serviceRegistry_registerServiceFactory(
         celix_properties_t* props,
         service_registration_t **registration);
 
+/**
+ * List the registered service for the provided bundle.
+ * @return A list of service ids. Caller is owner of the array list.
+ */
+celix_array_list_t* celix_serviceRegistry_listServiceIdsForOwner(celix_service_registry_t* registry, long bndId);
+
+/**
+ * Get service information for the provided svc id and bnd id.
+ *
+ * If the output pointers for serviceName and/or serviceProperties are provided these will get a copy of the registry
+ * value. The caller is owner of the serviceName/serviceProperties.
+ *
+ * Returns true if the bundle is found.
+ */
+bool celix_serviceRegistry_getServiceInfo(
+        celix_service_registry_t* registry,
+        long svcId,
+        long bndId,
+        char **serviceName,
+        celix_properties_t **serviceProperties,
+        bool *factory);
+
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/libs/framework/private/mock/bundle_mock.c b/libs/framework/private/mock/bundle_mock.c
index 22bd18b..86382a4 100644
--- a/libs/framework/private/mock/bundle_mock.c
+++ b/libs/framework/private/mock/bundle_mock.c
@@ -297,5 +297,25 @@ celix_bundle_state_e celix_bundle_getState(const bundle_t *bnd) {
 	return (celix_bundle_state_e)mock_c()->returnValue().value.intValue;
 }
 
+celix_array_list_t* celix_bundle_listRegisteredServices(const celix_bundle_t *bnd) {
+    mock_c()->actualCall("celix_bundle_listRegisteredServices")
+            ->withConstPointerParameters("bnd", bnd);
+    return mock_c()->returnValue().value.pointerValue;
+}
+
+void celix_bundle_destroyRegisteredServicesList(celix_array_list_t* list) {
+    mock_c()->actualCall("celix_bundle_destroyRegisteredServicesList")
+            ->withPointerParameters("list)", list);
+}
 
+celix_array_list_t* celix_bundle_listServiceTrackers(const celix_bundle_t *bnd) {
+    mock_c()->actualCall("celix_bundle_listServiceTrackers")
+            ->withConstPointerParameters("bnd", bnd);
+    return mock_c()->returnValue().value.pointerValue;
+}
+
+void celix_bundle_destroyServiceTrackerList(celix_array_list_t* list) {
+    mock_c()->actualCall("celix_bundle_destroyServiceTrackerList")
+            ->withPointerParameters("list", list);
+}
 
diff --git a/libs/framework/private/mock/service_registration_mock.c b/libs/framework/private/mock/service_registration_mock.c
index 8f141eb..02715ab 100644
--- a/libs/framework/private/mock/service_registration_mock.c
+++ b/libs/framework/private/mock/service_registration_mock.c
@@ -157,3 +157,8 @@ service_registration_t* celix_serviceRegistration_createServiceFactory(
 	return mock_c()->returnValue().value.pointerValue;
 }
 
+bool serviceRegistration_isFactoryService(service_registration_t *registration) {
+    mock_c()->actualCall("serviceRegistration_isFactoryService")
+            ->withPointerParameters("registration", registration);
+    return mock_c()->returnValue().value.boolValue;
+}
diff --git a/libs/framework/private/mock/service_registry_mock.c b/libs/framework/private/mock/service_registry_mock.c
index feb7f4a..767e507 100644
--- a/libs/framework/private/mock/service_registry_mock.c
+++ b/libs/framework/private/mock/service_registry_mock.c
@@ -24,6 +24,7 @@
  *  \copyright  Apache License, Version 2.0
  */
 
+#include <CppUTestExt/MockSupport_c.h>
 #include "CppUTestExt/MockSupport_c.h"
 
 #include "service_registry.h"
@@ -195,3 +196,27 @@ void serviceRegistry_callHooksForListenerFilter(
             ->withStringParameters("filter", filter)
             ->withBoolParameters("removed", removed);
 }
+
+bool celix_serviceRegistry_getServiceInfo(
+        celix_service_registry_t* registry,
+        long svcId,
+        long bndId,
+        char **outServiceName,
+        celix_properties_t **outServiceProperties,
+        bool *outIsFactory) {
+    mock_c()->actualCall("celix_serviceRegistry_getServiceInfo")
+        ->withPointerParameters("registry", registry)
+        ->withLongIntParameters("svcId", svcId)
+        ->withLongIntParameters("bndId", bndId)
+        ->withOutputParameter("outServiceName", outServiceName)
+        ->withOutputParameter("outServiceProperties", outServiceProperties)
+        ->withOutputParameter("outIsFactory", outIsFactory);
+    return mock_c()->returnValue().value.boolValue;
+}
+
+celix_array_list_t* celix_serviceRegistry_listServiceIdsForOwner(celix_service_registry_t* registry, long bndId) {
+    mock_c()->actualCall("celix_serviceRegistry_listServiceIdsForOwner")
+        ->withPointerParameters("registry", registry)
+        ->withLongIntParameters("bndId", bndId);
+    return mock_c()->returnValue().value.pointerValue;
+}
\ No newline at end of file
diff --git a/libs/framework/src/bundle.c b/libs/framework/src/bundle.c
index 251941e..89ac70d 100644
--- a/libs/framework/src/bundle.c
+++ b/libs/framework/src/bundle.c
@@ -19,12 +19,16 @@
 
 #include <stdlib.h>
 #include <string.h>
+#include <service_tracker.h>
 
 #include "framework_private.h"
 #include "bundle_private.h"
 #include "resolver.h"
 #include "utils.h"
 
+#include "bundle_context_private.h"
+#include "service_tracker_private.h"
+
 celix_status_t bundle_createModule(bundle_pt bundle, module_pt *module);
 celix_status_t bundle_closeRevisions(const_bundle_pt bundle);
 
@@ -637,3 +641,58 @@ const char* celix_bundle_getSymbolicName(const celix_bundle_t *bnd) {
 	}
 	return result;
 }
+
+celix_array_list_t* celix_bundle_listRegisteredServices(const celix_bundle_t *bnd) {
+    long bndId = celix_bundle_getId(bnd);
+    celix_array_list_t* result = celix_arrayList_create();
+    celix_array_list_t *svcIds = celix_serviceRegistry_listServiceIdsForOwner(bnd->framework->registry, bndId);
+    for (int i = 0; i < celix_arrayList_size(svcIds); ++i) {
+        long svcId = celix_arrayList_getLong(svcIds, i);
+        celix_bundle_service_list_entry_t* entry = calloc(1, sizeof(*entry));
+        entry->serviceId = svcId;
+        entry->bundleOwner = bndId;
+        celix_serviceRegistry_getServiceInfo(bnd->framework->registry, svcId, bndId, &entry->serviceName, &entry->serviceProperties, &entry->factory);
+        celix_arrayList_add(result, entry);
+    }
+    celix_arrayList_destroy(svcIds);
+    return result;
+}
+
+void celix_bundle_destroyRegisteredServicesList(celix_array_list_t* list) {
+    if (list != NULL) {
+        for (int i = 0; i < celix_arrayList_size(list); ++i) {
+            celix_bundle_service_list_entry_t *entry = celix_arrayList_get(list, i);
+            free(entry->serviceName);
+            celix_properties_destroy(entry->serviceProperties);
+            free(entry);
+        }
+    }
+}
+
+celix_array_list_t* celix_bundle_listServiceTrackers(const celix_bundle_t *bnd) {
+    celix_array_list_t* result = celix_arrayList_create();
+    //FIXME: should not fall back to bundle context, but for now that is were the trackers are stored.
+    celixThreadMutex_lock(&bnd->context->mutex);
+    //hash_map_t *serviceTrackers; //key = trackerId, value = celix_service_tracker_t*
+    hash_map_iterator_t iter = hashMapIterator_construct(bnd->context->serviceTrackers);
+    while (hashMapIterator_hasNext(&iter)) {
+        celix_service_tracker_t *tracker = hashMapIterator_nextValue(&iter);
+        celix_bundle_service_tracker_list_entry_t *entry = calloc(1, sizeof(*entry));
+        entry->filter = strndup(tracker->filter, 1024*1024*10);
+        entry->bundleOwner = celix_bundle_getId(bnd);
+        celix_arrayList_add(result, entry);
+    }
+    celixThreadMutex_unlock(&bnd->context->mutex);
+    return result;
+}
+
+
+void celix_bundle_destroyServiceTrackerList(celix_array_list_t* list) {
+    if (list != NULL) {
+        for (int i = 0; i < celix_arrayList_size(list); ++i) {
+            celix_bundle_service_tracker_list_entry_t *entry = celix_arrayList_get(list, i);
+            free(entry->filter);
+            free(entry);
+        }
+    }
+}
\ No newline at end of file
diff --git a/libs/framework/src/bundle_context.c b/libs/framework/src/bundle_context.c
index 430781c..00683c0 100644
--- a/libs/framework/src/bundle_context.c
+++ b/libs/framework/src/bundle_context.c
@@ -59,7 +59,7 @@ celix_status_t bundleContext_create(framework_pt framework, framework_logger_pt
             arrayList_create(&context->svcRegistrations);
             context->bundleTrackers = hashMap_create(NULL,NULL,NULL,NULL);
             context->serviceTrackers = hashMap_create(NULL,NULL,NULL,NULL);
-            context->serviceTrackerTrackers =  hashMap_create(NULL,NULL,NULL,NULL);
+            context->metaTrackers =  hashMap_create(NULL,NULL,NULL,NULL);
             context->nextTrackerId = 1L;
 
             *bundle_context = context;
@@ -632,13 +632,13 @@ static void bundleContext_cleanupServiceTrackers(bundle_context_t *ctx) {
 }
 
 static void bundleContext_cleanupServiceTrackerTrackers(bundle_context_t *ctx) {
-    hash_map_iterator_t iter = hashMapIterator_construct(ctx->serviceTrackerTrackers);
+    hash_map_iterator_t iter = hashMapIterator_construct(ctx->metaTrackers);
     while (hashMapIterator_hasNext(&iter)) {
         celix_bundle_context_service_tracker_tracker_entry_t *entry = hashMapIterator_nextValue(&iter);
         serviceRegistration_unregister(entry->hookReg);
         free(entry);
     }
-    hashMap_destroy(ctx->serviceTrackerTrackers, false, false);
+    hashMap_destroy(ctx->metaTrackers, false, false);
 }
 
 
@@ -656,9 +656,9 @@ void celix_bundleContext_stopTracker(bundle_context_t *ctx, long trackerId) {
         } else if (hashMap_containsKey(ctx->serviceTrackers, (void*)trackerId)) {
             found = true;
             serviceTracker = hashMap_remove(ctx->serviceTrackers, (void*)trackerId);
-        } else if (hashMap_containsKey(ctx->serviceTrackerTrackers, (void*)trackerId)) {
+        } else if (hashMap_containsKey(ctx->metaTrackers, (void*)trackerId)) {
             found = true;
-            svcTrackerTracker = hashMap_remove(ctx->serviceTrackerTrackers, (void*)trackerId);
+            svcTrackerTracker = hashMap_remove(ctx->metaTrackers, (void*)trackerId);
         }
         celixThreadMutex_unlock(&ctx->mutex);
 
@@ -1016,7 +1016,7 @@ long celix_bundleContext_trackServiceTrackers(
     if (entry->hookReg != NULL) {
         celixThreadMutex_lock(&ctx->mutex);
         entry->trackerId = ctx->nextTrackerId++;
-        hashMap_put(ctx->serviceTrackerTrackers, (void*)entry->trackerId, entry);
+        hashMap_put(ctx->metaTrackers, (void*)entry->trackerId, entry);
         trackerId = entry->trackerId;
         celixThreadMutex_unlock(&ctx->mutex);
     } else {
diff --git a/libs/framework/src/bundle_context_private.h b/libs/framework/src/bundle_context_private.h
index d9e8d9d..e3b7619 100644
--- a/libs/framework/src/bundle_context_private.h
+++ b/libs/framework/src/bundle_context_private.h
@@ -49,13 +49,13 @@ struct celix_bundle_context {
 	celix_framework_t *framework;
 	celix_bundle_t *bundle;
 
-	celix_thread_mutex_t mutex; //protects fields below
+	celix_thread_mutex_t mutex; //protects fields below (NOTE/FIXME also used by bundle.c for listing service tracker usage)
 	array_list_t *svcRegistrations;
 	celix_dependency_manager_t *mng;
 	long nextTrackerId;
 	hash_map_t *bundleTrackers; //key = trackerId, value = celix_bundle_context_bundle_tracker_entry_t*
 	hash_map_t *serviceTrackers; //key = trackerId, value = celix_service_tracker_t*
-	hash_map_t *serviceTrackerTrackers; //key = trackerId, value = celix_bundle_context_service_tracker_tracker_entry_t*
+	hash_map_t *metaTrackers; //key = trackerId, value = celix_bundle_context_service_tracker_tracker_entry_t*
 };
 
 
diff --git a/libs/framework/src/service_registration.c b/libs/framework/src/service_registration.c
index 967f107..49f60c8 100644
--- a/libs/framework/src/service_registration.c
+++ b/libs/framework/src/service_registration.c
@@ -16,13 +16,6 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-/**
- * service_registration.c
- *
- *  \date       Aug 6, 2010
- *  \author    	<a href="mailto:dev@celix.apache.org">Apache Celix Project Team</a>
- *  \copyright	Apache License, Version 2.0
- */
 
 #include <stdlib.h>
 #include <stdio.h>
@@ -312,4 +305,14 @@ service_registration_t* celix_serviceRegistration_createServiceFactory(
     service_registration_pt registration = NULL;
     serviceRegistration_createInternal(callback, (celix_bundle_t*)bnd, serviceName, svcId, factory, props, CELIX_FACTORY_SERVICE, &registration);
     return registration;
+}
+
+bool serviceRegistration_isFactoryService(service_registration_t *registration) {
+    bool isFactory = false;
+    if (registration != NULL) {
+        celixThreadRwlock_readLock(&registration->lock);
+        isFactory = registration->svcType = CELIX_FACTORY_SERVICE || registration->svcType == CELIX_DEPRECATED_FACTORY_SERVICE;
+        celixThreadRwlock_unlock(&registration->lock);
+    }
+    return isFactory;
 }
\ No newline at end of file
diff --git a/libs/framework/src/service_registry.c b/libs/framework/src/service_registry.c
index accd7b6..bf2a68b 100644
--- a/libs/framework/src/service_registry.c
+++ b/libs/framework/src/service_registry.c
@@ -916,4 +916,59 @@ static void celix_decreaseCountHook(celix_service_registry_listener_hook_entry_t
         celixThreadCondition_broadcast(&entry->cond);
         celixThreadMutex_unlock(&entry->mutex);
     }
+}
+
+celix_array_list_t* celix_serviceRegistry_listServiceIdsForOwner(celix_service_registry_t* registry, long bndId) {
+    celix_array_list_t *result = celix_arrayList_create();
+    celixThreadRwlock_readLock(&registry->lock);
+    celix_bundle_t *bundle = framework_getBundleById(registry->framework, bndId);
+    celix_array_list_t *registrations = bundle != NULL ? hashMap_get(registry->serviceRegistrations, bundle) : NULL;
+    if (registrations != NULL) {
+        for (int i = 0; i < celix_arrayList_size(registrations); ++i) {
+            service_registration_t *reg = celix_arrayList_get(registrations, i);
+            long svcId = serviceRegistration_getServiceId(reg);
+            celix_arrayList_addLong(result, svcId);
+        }
+    }
+    celixThreadRwlock_unlock(&registry->lock);
+    return result;
+}
+
+bool celix_serviceRegistry_getServiceInfo(
+        celix_service_registry_t* registry,
+        long svcId,
+        long bndId,
+        char **outServiceName,
+        celix_properties_t **outServiceProperties,
+        bool *outIsFactory) {
+    bool found = false;
+
+    celixThreadRwlock_readLock(&registry->lock);
+    celix_bundle_t *bundle = framework_getBundleById(registry->framework, bndId);
+    celix_array_list_t *registrations = bundle != NULL ? hashMap_get(registry->serviceRegistrations, bundle) : NULL;
+    if (registrations != NULL) {
+        for (int i = 0; i < celix_arrayList_size(registrations); ++i) {
+            service_registration_t *reg = celix_arrayList_get(registrations, i);
+            if (svcId == serviceRegistration_getServiceId(reg)) {
+                found = true;
+                if (outServiceName != NULL) {
+                    const char *s = NULL;
+                    serviceRegistration_getServiceName(reg, &s);
+                    *outServiceName = strndup(s, 1024 * 1024 * 10);
+                }
+                if (outServiceProperties != NULL) {
+                    celix_properties_t *p = NULL;
+                    serviceRegistration_getProperties(reg, &p);
+                    *outServiceProperties = celix_properties_copy(p);
+                }
+                if (outIsFactory != NULL) {
+                    *outIsFactory = serviceRegistration_isFactoryService(reg);
+                }
+                break;
+            }
+        }
+    }
+    celixThreadRwlock_unlock(&registry->lock);
+
+    return found;
 }
\ No newline at end of file
diff --git a/libs/framework/src/service_tracker.c b/libs/framework/src/service_tracker.c
index 2dc0bba..9b24de8 100644
--- a/libs/framework/src/service_tracker.c
+++ b/libs/framework/src/service_tracker.c
@@ -52,14 +52,17 @@ static bool serviceTracker_useHighestRankingServiceInternal(celix_service_tracke
 static void serviceTracker_addInstanceFromShutdownList(celix_service_tracker_instance_t *instance);
 static void serviceTracker_remInstanceFromShutdownList(celix_service_tracker_instance_t *instance);
 
-static celix_thread_once_t g_once = CELIX_THREAD_ONCE_INIT;
-static celix_thread_mutex_t g_mutex;
-static celix_thread_cond_t g_cond;
+static celix_thread_once_t g_once = CELIX_THREAD_ONCE_INIT; //once for g_shutdownMutex, g_shutdownCond
+
+
+static celix_thread_mutex_t g_shutdownMutex;
+static celix_thread_cond_t g_shutdownCond;
 static celix_array_list_t *g_shutdownInstances = NULL; //value = celix_service_tracker_instance -> used for syncing with shutdown threads
 
+
 static void serviceTracker_once(void) {
-    celixThreadMutex_create(&g_mutex, NULL);
-    celixThreadCondition_init(&g_cond, NULL);
+    celixThreadMutex_create(&g_shutdownMutex, NULL);
+    celixThreadCondition_init(&g_shutdownCond, NULL);
 }
 
 static inline celix_tracked_entry_t* tracked_create(service_reference_pt ref, void *svc, celix_properties_t *props, celix_bundle_t *bnd) {
@@ -933,7 +936,7 @@ void celix_serviceTracker_useServices(
 
 void celix_serviceTracker_syncForFramework(void *fw) {
     celixThread_once(&g_once, serviceTracker_once);
-    celixThreadMutex_lock(&g_mutex);
+    celixThreadMutex_lock(&g_shutdownMutex);
     size_t count = 0;
     do {
         count = 0;
@@ -946,7 +949,7 @@ void celix_serviceTracker_syncForFramework(void *fw) {
             }
         }
         if (count > 0) {
-            pthread_cond_wait(&g_cond, &g_mutex);
+            pthread_cond_wait(&g_shutdownCond, &g_shutdownMutex);
         }
     } while (count > 0);
 
@@ -954,12 +957,12 @@ void celix_serviceTracker_syncForFramework(void *fw) {
         celix_arrayList_destroy(g_shutdownInstances);
         g_shutdownInstances = NULL;
     }
-    celixThreadMutex_unlock(&g_mutex);
+    celixThreadMutex_unlock(&g_shutdownMutex);
 }
 
 void celix_serviceTracker_syncForContext(void *ctx) {
     celixThread_once(&g_once, serviceTracker_once);
-    celixThreadMutex_lock(&g_mutex);
+    celixThreadMutex_lock(&g_shutdownMutex);
     size_t count;
     do {
         count = 0;
@@ -972,7 +975,7 @@ void celix_serviceTracker_syncForContext(void *ctx) {
             }
         }
         if (count > 0) {
-            pthread_cond_wait(&g_cond, &g_mutex);
+            pthread_cond_wait(&g_shutdownCond, &g_shutdownMutex);
         }
     } while (count > 0);
 
@@ -980,22 +983,22 @@ void celix_serviceTracker_syncForContext(void *ctx) {
         celix_arrayList_destroy(g_shutdownInstances);
         g_shutdownInstances = NULL;
     }
-    celixThreadMutex_unlock(&g_mutex);
+    celixThreadMutex_unlock(&g_shutdownMutex);
 }
 
 static void serviceTracker_addInstanceFromShutdownList(celix_service_tracker_instance_t *instance) {
     celixThread_once(&g_once, serviceTracker_once);
-    celixThreadMutex_lock(&g_mutex);
+    celixThreadMutex_lock(&g_shutdownMutex);
     if (g_shutdownInstances == NULL) {
         g_shutdownInstances = celix_arrayList_create();
     }
     celix_arrayList_add(g_shutdownInstances, instance);
-    celixThreadMutex_unlock(&g_mutex);
+    celixThreadMutex_unlock(&g_shutdownMutex);
 }
 
 static void serviceTracker_remInstanceFromShutdownList(celix_service_tracker_instance_t *instance) {
     celixThread_once(&g_once, serviceTracker_once);
-    celixThreadMutex_lock(&g_mutex);
+    celixThreadMutex_lock(&g_shutdownMutex);
     if (g_shutdownInstances != NULL) {
         size_t size = celix_arrayList_size(g_shutdownInstances);
         for (size_t i = 0; i < size; ++i) {
@@ -1008,7 +1011,7 @@ static void serviceTracker_remInstanceFromShutdownList(celix_service_tracker_ins
             celix_arrayList_destroy(g_shutdownInstances);
             g_shutdownInstances = NULL;
         }
-        celixThreadCondition_broadcast(&g_cond);
+        celixThreadCondition_broadcast(&g_shutdownCond);
     }
-    celixThreadMutex_unlock(&g_mutex);
+    celixThreadMutex_unlock(&g_shutdownMutex);
 }