celix-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From pnol...@apache.org
Subject [25/60] [abbrv] [partial] celix git commit: CELIX-424: Cleans up the directory structure. Moves all libraries to the libs subdir and all bundles to the bundles subdir
Date Sun, 27 May 2018 18:52:33 GMT
http://git-wip-us.apache.org/repos/asf/celix/blob/3bce889b/bundles/shell/shell/src/dm_shell_list_command.c
----------------------------------------------------------------------
diff --git a/bundles/shell/shell/src/dm_shell_list_command.c b/bundles/shell/shell/src/dm_shell_list_command.c
new file mode 100644
index 0000000..6b92c17
--- /dev/null
+++ b/bundles/shell/shell/src/dm_shell_list_command.c
@@ -0,0 +1,192 @@
+/**
+ *Licensed to the Apache Software Foundation (ASF) under one
+ *or more contributor license agreements.  See the NOTICE file
+ *distributed with this work for additional information
+ *regarding copyright ownership.  The ASF licenses this file
+ *to you under the Apache License, Version 2.0 (the
+ *"License"); you may not use this file except in compliance
+ *with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *Unless required by applicable law or agreed to in writing,
+ *software distributed under the License is distributed on an
+ *"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ *specific language governing permissions and limitations
+ *under the License.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <shell_constants.h>
+#include "celix_bundle_context.h"
+#include "dm_dependency_manager.h"
+
+
+static const char * const OK_COLOR = "\033[92m";
+static const char * const WARNING_COLOR = "\033[93m";
+static const char * const NOK_COLOR = "\033[91m";
+static const char * const END_COLOR = "\033[m";
+
+static void parseCommandLine(const char*line, array_list_pt *requestedBundleIds, bool *fullInfo, FILE *err) {
+    *fullInfo = false;
+    char *str = strdup(line);
+    // skip first argument since this is the command
+    strtok(str," ");
+    char* tok = strtok(NULL," ");
+    *requestedBundleIds = NULL;
+    arrayList_create(requestedBundleIds);
+    while (tok) {
+        if (tok[0] == 'f') { // f or full argument => show full info
+            *fullInfo = true;
+        } else if ( (tok[0] >= '0') && (tok[0] <= '9')) { // bundle id
+            long *id = malloc(sizeof(*id));
+            *id = strtol(tok, NULL, 10);
+            arrayList_add(*requestedBundleIds, id);
+        } else {
+            fprintf (err, "DM: Skipping unknown argument: %s", tok );
+        }
+        tok = strtok(NULL," ");
+    }
+    free (str);
+}
+
+static void destroyBundleIdList(array_list_pt ids) {
+    unsigned int size = arrayList_size(ids);
+    for (unsigned int i = 0; i < size; i++) {
+        free(arrayList_get(ids, i));
+    }
+    arrayList_destroy(ids);
+}
+
+/*
+ * Check if the ID is in the array list. If  arrayist is empty also true is returned so that all
+ * bundles are shown
+ */
+static bool is_bundleId_in_list(array_list_pt ids, long id) {
+    unsigned int size = arrayList_size(ids);
+    bool result = false;
+    if (size == 0) {
+        result = true;
+    }
+    for(unsigned int i = 0; i < size; ++i) {
+        if (*((long*)arrayList_get(ids, i))  == id) {
+            result = true;
+            break;
+        }
+    }
+    return result;
+}
+
+
+static void printFullInfo(FILE *out, bool colors, dm_component_info_pt compInfo) {
+    const char *startColors = "";
+    const char *endColors = "";
+    if (colors) {
+        startColors = compInfo->active ? OK_COLOR : NOK_COLOR;
+        endColors = END_COLOR;
+    }
+    fprintf(out, "Component: Name=%s\n|- ID=%s, %sActive=%s%s, State=%s\n", compInfo->name, compInfo->id,
+            startColors, compInfo->active ? "true " : "false", endColors, compInfo->state);
+    fprintf(out, "|- Interfaces (%d):\n", arrayList_size(compInfo->interfaces));
+    for (unsigned int interfCnt = 0; interfCnt < arrayList_size(compInfo->interfaces); interfCnt++) {
+        dm_interface_info_pt intfInfo = arrayList_get(compInfo->interfaces, interfCnt);
+        fprintf(out, "   |- Interface: %s\n", intfInfo->name);
+
+        hash_map_iterator_t iter = hashMapIterator_construct((hash_map_pt) intfInfo->properties);
+        char *key = NULL;
+        while ((key = hashMapIterator_nextKey(&iter)) != NULL) {
+            fprintf(out, "      | %15s = %s\n", key, properties_get(intfInfo->properties, key));
+        }
+    }
+
+    fprintf(out, "|- Dependencies (%d):\n", arrayList_size(compInfo->dependency_list));
+    for (unsigned int depCnt = 0; depCnt < arrayList_size(compInfo->dependency_list); depCnt++) {
+        dm_service_dependency_info_pt dependency;
+        dependency = arrayList_get(compInfo->dependency_list, depCnt);
+        const char *depStartColors = "";
+        const char *depEndColors = "";
+        if (colors) {
+            if (dependency->required) {
+                depStartColors = dependency->available ? OK_COLOR : NOK_COLOR;
+            } else {
+                depStartColors = dependency->available ? OK_COLOR : WARNING_COLOR;
+            }
+
+            depEndColors = END_COLOR;
+        }
+        fprintf(out, "   |- Dependency: %sAvailable = %s%s, Required = %s, Filter = %s\n", depStartColors,
+                dependency->available ? "true " : "false", depEndColors,
+                dependency->required ? "true " : "false", dependency->filter);
+    }
+    fprintf(out, "\n");
+
+}
+
+static void printBasicInfo(FILE *out, bool colors, dm_component_info_pt compInfo) {
+    const char *startColors = "";
+    const char *endColors = "";
+    if (colors) {
+        startColors = compInfo->active ? OK_COLOR : NOK_COLOR;
+        endColors = END_COLOR;
+    }
+    fprintf(out, "Component: Name=%s, ID=%s, %sActive=%s%s, State=%s\n", compInfo->name, compInfo->id,
+            startColors, compInfo->active ? "true " : "false", endColors, compInfo->state);
+
+}
+
+celix_status_t dmListCommand_execute(void* handle, char * line, FILE *out, FILE *err) {
+    bundle_context_t *ctx = handle;
+
+    array_list_t *bundles = NULL;
+    bundleContext_getBundles(ctx, &bundles);
+
+    const char *config = NULL;
+    bundleContext_getPropertyWithDefault(ctx, SHELL_USE_ANSI_COLORS, SHELL_USE_ANSI_COLORS_DEFAULT_VALUE, &config);
+    bool useColors = config != NULL && strncmp("true", config, 5) == 0;
+
+    array_list_pt bundleIds = NULL;
+    bool fullInfo = false;
+    parseCommandLine(line, &bundleIds, &fullInfo, err);
+
+    if (bundles != NULL) {
+        unsigned int size = arrayList_size(bundles);
+        for (unsigned int i = 0; i < size; ++i) {
+            long bndId = -1;
+            bundle_t *bnd = arrayList_get(bundles, i);
+            bundle_getBundleId(bnd, &bndId);
+            if (!is_bundleId_in_list(bundleIds, bndId)) {
+                continue;
+            }
+            bundle_context_t *bndCtx = NULL;
+            bundle_getContext(bnd, &bndCtx);
+            if (bndCtx != NULL) {
+                dm_dependency_manager_t *mng = celix_bundleContext_getDependencyManager(bndCtx);
+                dm_dependency_manager_info_t *info = NULL;
+                dependencyManager_getInfo(mng, &info);
+                if (info != NULL) {
+                    size_t size = celix_arrayList_size(info->components);
+                    if (size > 0) {
+                        fprintf(out, "[Bundle: %ld]\n", bndId);
+                        for (unsigned int cmpCnt = 0; cmpCnt < size; cmpCnt++) {
+                            dm_component_info_pt compInfo = celix_arrayList_get(info->components, cmpCnt);
+                            if (fullInfo) {
+                                printFullInfo(out, useColors, compInfo);
+                            } else {
+                                printBasicInfo(out, useColors, compInfo);
+                            }
+                        }
+                        fprintf(out, "\n");
+                    }
+                    dependencyManager_destroyInfo(mng, info);
+                }
+
+            }
+        }
+    }
+
+    destroyBundleIdList(bundleIds);
+
+    return CELIX_SUCCESS;
+}

http://git-wip-us.apache.org/repos/asf/celix/blob/3bce889b/bundles/shell/shell/src/help_command.c
----------------------------------------------------------------------
diff --git a/bundles/shell/shell/src/help_command.c b/bundles/shell/shell/src/help_command.c
new file mode 100644
index 0000000..48615ae
--- /dev/null
+++ b/bundles/shell/shell/src/help_command.c
@@ -0,0 +1,112 @@
+/**
+ *Licensed to the Apache Software Foundation (ASF) under one
+ *or more contributor license agreements.  See the NOTICE file
+ *distributed with this work for additional information
+ *regarding copyright ownership.  The ASF licenses this file
+ *to you under the Apache License, Version 2.0 (the
+ *"License"); you may not use this file except in compliance
+ *with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *Unless required by applicable law or agreed to in writing,
+ *software distributed under the License is distributed on an
+ *"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ *specific language governing permissions and limitations
+ *under the License.
+ */
+/*
+ * help_command.c
+ *
+ *  \date       Aug 20, 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 <stdint.h>
+
+#include "array_list.h"
+#include "bundle_context.h"
+#include "shell.h"
+#include "std_commands.h"
+
+celix_status_t helpCommand_execute(void *_ptr, char *line_str, FILE *out_ptr, FILE *err_ptr) {
+	celix_status_t status = CELIX_SUCCESS;
+
+	bundle_context_pt context_ptr = _ptr;
+	service_reference_pt shell_service_reference_ptr = NULL;
+	shell_service_pt shell_ptr = NULL;
+
+	if (!context_ptr || !line_str || !out_ptr || !err_ptr) {
+		status = CELIX_ILLEGAL_ARGUMENT;
+	}
+
+	if (status == CELIX_SUCCESS) {
+		status = bundleContext_getServiceReference(context_ptr, (char *) OSGI_SHELL_SERVICE_NAME, &shell_service_reference_ptr);
+	}
+
+	if (status == CELIX_SUCCESS) {
+		status = bundleContext_getService(context_ptr, shell_service_reference_ptr, (void **) &shell_ptr);
+	}
+
+	if (status == CELIX_SUCCESS) {
+        uint32_t out_len = 256;
+        char *sub = NULL;
+        char *save_ptr = NULL;
+        char out_str[out_len];
+
+        memset(out_str, 0, sizeof(out_str));
+
+        strtok_r(line_str, OSGI_SHELL_COMMAND_SEPARATOR, &save_ptr);
+        sub = strtok_r(NULL, OSGI_SHELL_COMMAND_SEPARATOR, &save_ptr);
+
+        if (sub == NULL) {
+            unsigned int i;
+            array_list_pt commands = NULL;
+
+            status = shell_ptr->getCommands(shell_ptr->shell, &commands);
+            for (i = 0; i < arrayList_size(commands); i++) {
+                char *name = arrayList_get(commands, i);
+                fprintf(out_ptr, "%s\n", name);
+            }
+            fprintf(out_ptr, "\nUse 'help <command-name>' for more information.\n");
+            arrayList_destroy(commands);
+        } else {
+            celix_status_t sub_status_desc;
+            celix_status_t sub_status_usage;
+            int i;
+            array_list_pt commands = NULL;
+            shell_ptr->getCommands(shell_ptr->shell, &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_ptr->getCommandDescription(shell_ptr->shell, name, &desc_str);
+                    sub_status_usage = shell_ptr->getCommandUsage(shell_ptr->shell, name, &usage_str);
+
+                    if (sub_status_usage == CELIX_SUCCESS && sub_status_desc == CELIX_SUCCESS) {
+                        fprintf(out_ptr, "Command     : %s\n", name);
+                        fprintf(out_ptr, "Usage       : %s\n", usage_str == NULL ? "" : usage_str);
+                        fprintf(out_ptr, "Description : %s\n", desc_str == NULL ? "" : desc_str);
+                    } else {
+                        fprintf(err_ptr, "Error retreiving help info for command '%s'\n", sub);
+                    }
+
+                    if (sub_status_desc != CELIX_SUCCESS && status == CELIX_SUCCESS) {
+                        status = sub_status_desc;
+                    }
+                    if (sub_status_usage != CELIX_SUCCESS && status == CELIX_SUCCESS) {
+                        status = sub_status_usage;
+                    }
+                }
+            }
+            arrayList_destroy(commands);
+        }
+    }
+
+    return status;
+}

http://git-wip-us.apache.org/repos/asf/celix/blob/3bce889b/bundles/shell/shell/src/inspect_command.c
----------------------------------------------------------------------
diff --git a/bundles/shell/shell/src/inspect_command.c b/bundles/shell/shell/src/inspect_command.c
new file mode 100644
index 0000000..cb93e9c
--- /dev/null
+++ b/bundles/shell/shell/src/inspect_command.c
@@ -0,0 +1,277 @@
+/**
+ *Licensed to the Apache Software Foundation (ASF) under one
+ *or more contributor license agreements.  See the NOTICE file
+ *distributed with this work for additional information
+ *regarding copyright ownership.  The ASF licenses this file
+ *to you under the Apache License, Version 2.0 (the
+ *"License"); you may not use this file except in compliance
+ *with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *Unless required by applicable law or agreed to in writing,
+ *software distributed under the License is distributed on an
+ *"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ *specific language governing permissions and limitations
+ *under the License.
+ */
+/*
+ * inspect_command.c
+ *
+ *  \date       Oct 13, 2011
+ *  \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 "array_list.h"
+#include "bundle_context.h"
+
+#include "std_commands.h"
+
+#define SERVICE_TYPE "service"
+#define CAPABILITY "capability"
+#define REQUIREMENT "requirement"
+
+celix_status_t inspectCommand_printExportedServices(bundle_context_pt context, array_list_pt ids, FILE *outStream, FILE *errStream);
+celix_status_t inspectCommand_printImportedServices(bundle_context_pt context, array_list_pt ids, FILE *outStream, FILE *errStream);
+
+celix_status_t inspectCommand_execute(void *handle, char * commandline, FILE *outStream, FILE *errStream) {
+	celix_status_t status = CELIX_SUCCESS;
+
+	bundle_context_pt context = handle;
+
+	char *token;
+	strtok_r(commandline, " ", &token);
+	char *type = strtok_r(NULL, " ", &token);
+	if (type != NULL) {
+		char *direction = strtok_r(NULL, " ", &token);
+		if (direction != NULL) {
+			array_list_pt ids = NULL;
+			char *id = strtok_r(NULL, " ", &token);
+
+			arrayList_create(&ids);
+			while (id != NULL) {
+				arrayList_add(ids, id);
+				id = strtok_r(NULL, " ", &token);
+			}
+
+			if (strcmp(type, SERVICE_TYPE) == 0) {
+				if (strcmp(direction, CAPABILITY) == 0) {
+					status = inspectCommand_printExportedServices(context, ids, outStream, errStream);
+					if (status != CELIX_SUCCESS) {
+						fprintf(errStream, "INSPECT: Error\n");
+					}
+				} else if (strcmp(direction, REQUIREMENT) == 0) {
+                    status = inspectCommand_printImportedServices(context, ids, outStream, errStream);
+                    if (status != CELIX_SUCCESS) {
+						fprintf(errStream, "INSPECT: Error\n");
+                    }
+				} else {
+					fprintf(errStream, "INSPECT: Invalid argument\n");
+				}
+			} else {
+				fprintf(errStream, "INSPECT: Invalid argument\n");
+			}
+			arrayList_destroy(ids);
+		} else {
+			fprintf(errStream, "INSPECT: Too few arguments\n");
+		}
+	} else {
+		fprintf(errStream, "INSPECT: Too few arguments\n");
+	}
+	return status;
+}
+
+celix_status_t inspectCommand_printExportedServices(bundle_context_pt context, array_list_pt ids, FILE *outStream, FILE *errStream) {
+	celix_status_t status = CELIX_SUCCESS;
+	array_list_pt bundles = NULL;
+
+	if (arrayList_isEmpty(ids)) {
+		status = bundleContext_getBundles(context, &bundles);
+	} else {
+		unsigned int i;
+
+		arrayList_create(&bundles);
+		for (i = 0; i < arrayList_size(ids); i++) {
+			char *idStr = (char *) arrayList_get(ids, i);
+			long id = atol(idStr);
+			bundle_pt b = NULL;
+			celix_status_t st = bundleContext_getBundleById(context, id, &b);
+			if (st == CELIX_SUCCESS) {
+				arrayList_add(bundles, b);
+			} else {
+				fprintf(outStream, "INSPECT: Invalid bundle ID: %ld\n", id);
+			}
+		}
+	}
+
+	if (status == CELIX_SUCCESS) {
+		unsigned int i = 0;
+		for (i = 0; i < arrayList_size(bundles); i++) {
+			bundle_pt bundle = (bundle_pt) arrayList_get(bundles, i);
+
+			if (i > 0) {
+				fprintf(outStream, "\n");
+			}
+
+			if (bundle != NULL) {
+				array_list_pt refs = NULL;
+
+				if (bundle_getRegisteredServices(bundle, &refs) == CELIX_SUCCESS) {
+					module_pt module = NULL;
+					const char * name = NULL;
+					status = bundle_getCurrentModule(bundle, &module);
+					if (status == CELIX_SUCCESS) {
+						status = module_getSymbolicName(module, &name);
+						if (status == CELIX_SUCCESS) {
+							fprintf(outStream, "%s provides services:\n", name);
+							fprintf(outStream, "==============\n");
+
+							if (refs == NULL || arrayList_size(refs) == 0) {
+								fprintf(outStream, "Nothing\n");
+							} else {
+								unsigned int j = 0;
+								for (j = 0; j < arrayList_size(refs); j++) {
+									service_reference_pt ref = (service_reference_pt) arrayList_get(refs, j);
+									unsigned int size = 0;
+									char **keys;
+
+									serviceReference_getPropertyKeys(ref, &keys, &size);
+									for (int k = 0; k < size; k++) {
+									    char *key = keys[k];
+									    const char *value = NULL;
+									    serviceReference_getProperty(ref, key, &value);
+
+										fprintf(outStream, "%s = %s\n", key, value);
+									}
+
+									free(keys);
+
+//									objectClass = properties_get(props, (char *) OSGI_FRAMEWORK_OBJECTCLASS);
+//									sprintf(line, "ObjectClass = %s\n", objectClass);
+									if ((j + 1) < arrayList_size(refs)) {
+										fprintf(outStream, "----\n");
+									}
+								}
+							}
+						}
+					}
+				}
+
+				if(refs!=NULL){
+					arrayList_destroy(refs);
+				}
+			}
+		}
+	}
+
+	if (bundles != NULL) {
+	    arrayList_destroy(bundles);
+	}
+
+	return status;
+}
+
+celix_status_t inspectCommand_printImportedServices(bundle_context_pt context, array_list_pt ids, FILE *outStream, FILE *errStream) {
+    celix_status_t status = CELIX_SUCCESS;
+    array_list_pt bundles = NULL;
+
+    if (arrayList_isEmpty(ids)) {
+        status = bundleContext_getBundles(context, &bundles);
+    } else {
+        unsigned int i;
+
+        arrayList_create(&bundles);
+        for (i = 0; i < arrayList_size(ids); i++) {
+            char *idStr = (char *) arrayList_get(ids, i);
+            long id = atol(idStr);
+            bundle_pt b = NULL;
+            celix_status_t st = bundleContext_getBundleById(context, id, &b);
+            if (st == CELIX_SUCCESS) {
+                arrayList_add(bundles, b);
+            } else {
+				fprintf(outStream, "INSPECT: Invalid bundle ID: %ld\n", id);
+            }
+        }
+    }
+
+    if (status == CELIX_SUCCESS) {
+        unsigned int i = 0;
+        for (i = 0; i < arrayList_size(bundles); i++) {
+            bundle_pt bundle = (bundle_pt) arrayList_get(bundles, i);
+
+            if (i > 0) {
+				fprintf(outStream, "\n");
+            }
+
+            if (bundle != NULL) {
+                array_list_pt refs = NULL;
+
+                if (bundle_getServicesInUse(bundle, &refs) == CELIX_SUCCESS) {
+                    module_pt module = NULL;
+                    const char * name = NULL;
+                    status = bundle_getCurrentModule(bundle, &module);
+                    if (status == CELIX_SUCCESS) {
+                        status = module_getSymbolicName(module, &name);
+                        if (status == CELIX_SUCCESS) {
+							fprintf(outStream, "%s requires services:\n", name);
+							fprintf(outStream, "==============\n");
+
+                            if (refs == NULL || arrayList_size(refs) == 0) {
+								fprintf(outStream, "Nothing\n");
+                            } else {
+                                unsigned int j = 0;
+                                for (j = 0; j < arrayList_size(refs); j++) {
+                                    service_reference_pt ref = (service_reference_pt) arrayList_get(refs, j);
+                                    bundle_pt usedBundle = NULL;
+                                    module_pt usedModule = NULL;
+                                    const char *usedSymbolicName = NULL;
+                                    long usedBundleId;
+
+                                    serviceReference_getBundle(ref, &usedBundle);
+                                    bundle_getBundleId(usedBundle, &usedBundleId);
+                                    bundle_getCurrentModule(usedBundle, &usedModule);
+                                    module_getSymbolicName(usedModule, &usedSymbolicName);
+
+									fprintf(outStream, "%s [%ld]\n", usedSymbolicName, usedBundleId);
+
+                                    unsigned int size = 0;
+                                    char **keys;
+
+                                    serviceReference_getPropertyKeys(ref, &keys, &size);
+                                    for (int k = 0; k < size; k++) {
+                                        char *key = keys[k];
+                                        const char *value = NULL;
+                                        serviceReference_getProperty(ref, key, &value);
+
+										fprintf(outStream, "%s = %s\n", key, value);
+                                    }
+                                    free(keys);
+
+//                                  objectClass = properties_get(props, (char *) OSGI_FRAMEWORK_OBJECTCLASS);
+//                                  sprintf(line, "ObjectClass = %s\n", objectClass);
+                                    if ((j + 1) < arrayList_size(refs)) {
+										fprintf(outStream, "----\n");
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+
+                if(refs!=NULL){
+                	arrayList_destroy(refs);
+                }
+            }
+        }
+    }
+
+    arrayList_destroy(bundles);
+
+
+    return status;
+}

http://git-wip-us.apache.org/repos/asf/celix/blob/3bce889b/bundles/shell/shell/src/install_command.c
----------------------------------------------------------------------
diff --git a/bundles/shell/shell/src/install_command.c b/bundles/shell/shell/src/install_command.c
new file mode 100644
index 0000000..067ba2b
--- /dev/null
+++ b/bundles/shell/shell/src/install_command.c
@@ -0,0 +1,76 @@
+/**
+ *Licensed to the Apache Software Foundation (ASF) under one
+ *or more contributor license agreements.  See the NOTICE file
+ *distributed with this work for additional information
+ *regarding copyright ownership.  The ASF licenses this file
+ *to you under the Apache License, Version 2.0 (the
+ *"License"); you may not use this file except in compliance
+ *with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *Unless required by applicable law or agreed to in writing,
+ *software distributed under the License is distributed on an
+ *"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ *specific language governing permissions and limitations
+ *under the License.
+ */
+/*
+ * install_command.c
+ *
+ *  \date       Apr 4, 2011
+ *  \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 "array_list.h"
+#include "bundle_context.h"
+
+void installCommand_execute(void *handle, char * line, FILE *outStream, FILE *errStream) {
+	bundle_context_pt context = handle;
+
+	char delims[] = " ";
+	char * sub = NULL;
+	char *save_ptr = NULL;
+	char info[256];
+
+	// ignore the command
+	sub = strtok_r(line, delims, &save_ptr);
+	sub = strtok_r(NULL, delims, &save_ptr);
+	
+	if (sub == NULL) {
+		fprintf(errStream, "Incorrect number of arguments.\n");
+	} else {
+		info[0] = '\0';
+		while (sub != NULL) {
+			bundle_pt bundle = NULL;
+			bundleContext_installBundle(context, sub, &bundle);
+			if (bundle != NULL) {
+				long id;
+				bundle_archive_pt archive = NULL;
+				char bundleId[sizeof(id) + 1];
+
+				if (strlen(info) > 0) {
+					strcat(info, ", ");
+				}
+				bundle_getArchive(bundle, &archive);
+				bundleArchive_getId(archive, &id);
+				sprintf(bundleId, "%ld", id);
+				strcat(info, bundleId);
+			}
+			sub = strtok_r(NULL, delims, &save_ptr);
+		}
+		if (strchr(info, ',') != NULL) {
+			fprintf(outStream, "Bundle IDs: ");
+			fprintf(outStream, "%s", info);
+			fprintf(outStream, "\n");
+		} else if (strlen(info) > 0) {
+			fprintf(outStream, "Bundle ID: ");
+			fprintf(outStream, "%s", info);
+			fprintf(outStream, "\n");
+		}
+	}
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/celix/blob/3bce889b/bundles/shell/shell/src/lb_command.c
----------------------------------------------------------------------
diff --git a/bundles/shell/shell/src/lb_command.c b/bundles/shell/shell/src/lb_command.c
new file mode 100644
index 0000000..d0504f6
--- /dev/null
+++ b/bundles/shell/shell/src/lb_command.c
@@ -0,0 +1,205 @@
+/**
+ *Licensed to the Apache Software Foundation (ASF) under one
+ *or more contributor license agreements.  See the NOTICE file
+ *distributed with this work for additional information
+ *regarding copyright ownership.  The ASF licenses this file
+ *to you under the Apache License, Version 2.0 (the
+ *"License"); you may not use this file except in compliance
+ *with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *Unless required by applicable law or agreed to in writing,
+ *software distributed under the License is distributed on an
+ *"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ *specific language governing permissions and limitations
+ *under the License.
+ */
+/*
+ * std_shell_commands.c
+ *
+ *  \date       March 27, 2014
+ *  \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 "array_list.h"
+#include "bundle_context.h"
+#include "std_commands.h"
+#include "shell_constants.h"
+
+static const char * const HEAD_COLOR = "\033[4m"; //underline
+static const char * const EVEN_COLOR = "\033[1m"; //bold
+static const char * const ODD_COLOR = "\033[3m";  //italic
+static const char * const END_COLOR = "\033[0m";
+
+static char * psCommand_stateString(bundle_state_e state); 
+
+celix_status_t psCommand_execute(void *_ptr, char *command_line_str, FILE *out_ptr, FILE *err_ptr) {
+    celix_status_t status = CELIX_SUCCESS;
+    bundle_context_t* ctx = _ptr;
+
+    const char* config = NULL;
+    bundleContext_getPropertyWithDefault(ctx, SHELL_USE_ANSI_COLORS, SHELL_USE_ANSI_COLORS_DEFAULT_VALUE, &config);
+    bool useColors = config != NULL && strncmp("true", config, 5) == 0;
+
+    bundle_context_pt context_ptr = _ptr;
+    array_list_pt bundles_ptr     = NULL;
+
+    bool show_location        = false;
+    bool show_symbolic_name   = false;
+    bool show_update_location = false;
+    char *message_str         = "Name";
+
+    if (!context_ptr || !command_line_str || !out_ptr || !err_ptr) {
+        status = CELIX_ILLEGAL_ARGUMENT;
+    }
+
+    if (status == CELIX_SUCCESS) {
+        status = bundleContext_getBundles(context_ptr, &bundles_ptr);
+    }
+
+    if (status == CELIX_SUCCESS) {
+        char *sub_str = NULL;
+        char *save_ptr = NULL;
+
+        strtok_r(command_line_str, OSGI_SHELL_COMMAND_SEPARATOR, &save_ptr);
+        sub_str = strtok_r(NULL, OSGI_SHELL_COMMAND_SEPARATOR, &save_ptr);
+        while (sub_str != NULL) {
+            if (strcmp(sub_str, "-l") == 0) {
+                show_location = true;
+                message_str = "Location";
+            } else if (strcmp(sub_str, "-s") == 0) {
+                show_symbolic_name = true;
+                message_str = "Symbolic name";
+            } else if (strcmp(sub_str, "-u") == 0) {
+                show_update_location = true;
+                message_str = "Update location";
+            }
+            sub_str = strtok_r(NULL, OSGI_SHELL_COMMAND_SEPARATOR, &save_ptr);
+        }
+
+	const char* startColor = "";
+	const char* endColor = "";
+	if (useColors) {
+		startColor = HEAD_COLOR;
+		endColor = END_COLOR;
+	} 
+        fprintf(out_ptr, "%s  %-5s %-12s %s%s\n", startColor, "ID", "State", message_str, endColor);
+
+        unsigned int size = arrayList_size(bundles_ptr);
+
+        bundle_pt bundles_array_ptr[size];
+
+        for (unsigned int i = 0; i < size; i++) {
+            bundles_array_ptr[i] = arrayList_get(bundles_ptr, i);
+        }
+
+        for (unsigned int i = 0; i < size - 1; i++) {
+            for (unsigned int j = i + 1; j < size; j++) {
+                bundle_pt first_ptr = bundles_array_ptr[i];
+                bundle_pt second_ptr = bundles_array_ptr[j];
+
+                bundle_archive_pt first_archive_ptr = NULL;
+                bundle_archive_pt second_archive_ptr = NULL;
+
+                long first_id;
+                long second_id;
+
+                bundle_getArchive(first_ptr, &first_archive_ptr);
+                bundle_getArchive(second_ptr, &second_archive_ptr);
+
+                bundleArchive_getId(first_archive_ptr, &first_id);
+                bundleArchive_getId(second_archive_ptr, &second_id);
+
+                if (first_id > second_id) {
+                    bundle_pt temp_ptr = bundles_array_ptr[i];
+                    bundles_array_ptr[i] = bundles_array_ptr[j];
+                    bundles_array_ptr[j] = temp_ptr;
+                }
+            }
+        }
+
+        for (unsigned int i = 0; i < size; i++) {
+            celix_status_t sub_status;
+
+            bundle_pt bundle_ptr = bundles_array_ptr[i];
+
+            bundle_archive_pt archive_ptr = NULL;
+            long id = 0;
+            bundle_state_e state = OSGI_FRAMEWORK_BUNDLE_UNKNOWN;
+            const char *state_str = NULL;
+            module_pt module_ptr = NULL;
+            const char *name_str = NULL;
+
+            sub_status = bundle_getArchive(bundle_ptr, &archive_ptr);
+            if (sub_status == CELIX_SUCCESS) {
+                sub_status = bundleArchive_getId(archive_ptr, &id);
+            }
+
+            if (sub_status == CELIX_SUCCESS) {
+                sub_status = bundle_getState(bundle_ptr, &state);
+            }
+
+            if (sub_status == CELIX_SUCCESS) {
+                state_str = psCommand_stateString(state);
+
+                sub_status = bundle_getCurrentModule(bundle_ptr, &module_ptr);
+            }
+
+            if (sub_status == CELIX_SUCCESS) {
+                sub_status = module_getSymbolicName(module_ptr, &name_str);
+            }
+
+            if (sub_status == CELIX_SUCCESS) {
+                if (show_location) {
+                    sub_status = bundleArchive_getLocation(archive_ptr, &name_str);
+                } else if (show_symbolic_name) {
+                    // do nothing
+                } else if (show_update_location) {
+                    sub_status = bundleArchive_getLocation(archive_ptr, &name_str);
+                }
+            }
+
+            if (sub_status == CELIX_SUCCESS) {
+		startColor = "";
+		endColor = "";
+                if (useColors) {
+                    startColor = i % 2 == 0 ? EVEN_COLOR : ODD_COLOR;
+                    endColor = END_COLOR;
+                } 
+		fprintf(out_ptr, "%s  %-5ld %-12s %s%s\n", startColor, id, state_str, name_str, endColor);
+
+            }
+
+            if (sub_status != CELIX_SUCCESS) {
+                status = sub_status;
+                break;
+            }
+        }
+
+        arrayList_destroy(bundles_ptr);
+    }
+
+    return status;
+}
+
+static char * psCommand_stateString(bundle_state_e state) {
+    switch (state) {
+        case OSGI_FRAMEWORK_BUNDLE_ACTIVE:
+            return "Active      ";
+        case OSGI_FRAMEWORK_BUNDLE_INSTALLED:
+            return "Installed   ";
+        case OSGI_FRAMEWORK_BUNDLE_RESOLVED:
+            return "Resolved    ";
+        case OSGI_FRAMEWORK_BUNDLE_STARTING:
+            return "Starting    ";
+        case OSGI_FRAMEWORK_BUNDLE_STOPPING:
+            return "Stopping    ";
+        default:
+            return "Unknown     ";
+    }
+}

http://git-wip-us.apache.org/repos/asf/celix/blob/3bce889b/bundles/shell/shell/src/log_command.c
----------------------------------------------------------------------
diff --git a/bundles/shell/shell/src/log_command.c b/bundles/shell/shell/src/log_command.c
new file mode 100644
index 0000000..8b0244a
--- /dev/null
+++ b/bundles/shell/shell/src/log_command.c
@@ -0,0 +1,94 @@
+/**
+ *Licensed to the Apache Software Foundation (ASF) under one
+ *or more contributor license agreements.  See the NOTICE file
+ *distributed with this work for additional information
+ *regarding copyright ownership.  The ASF licenses this file
+ *to you under the Apache License, Version 2.0 (the
+ *"License"); you may not use this file except in compliance
+ *with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *Unless required by applicable law or agreed to in writing,
+ *software distributed under the License is distributed on an
+ *"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ *specific language governing permissions and limitations
+ *under the License.
+ */
+/*
+ * log_command.c
+ *
+ *  \date       Jun 26, 2011
+ *  \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 "bundle_context.h"
+#include "log_reader_service.h"
+#include "linked_list_iterator.h"
+
+celix_status_t logCommand_levelAsString(bundle_context_pt context, log_level_t level, char **string);
+
+void logCommand_execute(bundle_context_pt context, char *line, FILE *outStream, FILE *errStream) {
+    celix_status_t status;
+    service_reference_pt readerService = NULL;
+
+    status = bundleContext_getServiceReference(context, (char *) OSGI_LOGSERVICE_READER_SERVICE_NAME, &readerService);
+    if (status != CELIX_SUCCESS || readerService != NULL) {
+        linked_list_pt list = NULL;
+        linked_list_iterator_pt iter = NULL;
+        log_reader_service_pt reader = NULL;
+
+
+		bundleContext_getService(context, readerService, (void **) &reader);
+		reader->getLog(reader->reader, &list);
+		iter = linkedListIterator_create(list, 0);
+		while (linkedListIterator_hasNext(iter)) {
+			log_entry_pt entry = linkedListIterator_next(iter);
+			char time[20];
+			char *level = NULL;
+			char errorString[256];
+
+            strftime(time, 20, "%Y-%m-%d %H:%M:%S", localtime(&entry->time));
+            logCommand_levelAsString(context, entry->level, &level);
+
+			if (entry->errorCode > 0) {
+				celix_strerror(entry->errorCode, errorString, 256);
+				fprintf(outStream, "%s - Bundle: %s - %s - %d %s\n", time, entry->bundleSymbolicName, entry->message, entry->errorCode, errorString);
+			} else {
+				fprintf(outStream, "%s - Bundle: %s - %s\n", time, entry->bundleSymbolicName, entry->message);
+			}
+		}
+		linkedListIterator_destroy(iter);
+		linkedList_destroy(list);
+		bool result = true;
+		bundleContext_ungetService(context, readerService, &result);
+        bundleContext_ungetServiceReference(context, readerService);
+    } else {
+		fprintf(outStream, "No log reader available\n");
+    }
+}
+
+celix_status_t logCommand_levelAsString(bundle_context_pt context, log_level_t level, char **string) {
+	switch (level) {
+	case OSGI_LOGSERVICE_ERROR:
+		*string = "ERROR";
+		break;
+	case OSGI_LOGSERVICE_WARNING:
+		*string = "WARNING";
+		break;
+	case OSGI_LOGSERVICE_INFO:
+		*string = "INFO";
+		break;
+	case OSGI_LOGSERVICE_DEBUG:
+	default:
+		*string = "DEBUG";
+		break;
+	}
+
+	return CELIX_SUCCESS;
+}

http://git-wip-us.apache.org/repos/asf/celix/blob/3bce889b/bundles/shell/shell/src/shell.c
----------------------------------------------------------------------
diff --git a/bundles/shell/shell/src/shell.c b/bundles/shell/shell/src/shell.c
new file mode 100644
index 0000000..ac8603e
--- /dev/null
+++ b/bundles/shell/shell/src/shell.c
@@ -0,0 +1,305 @@
+/**
+ *Licensed to the Apache Software Foundation (ASF) under one
+ *or more contributor license agreements.  See the NOTICE file
+ *distributed with this work for additional information
+ *regarding copyright ownership.  The ASF licenses this file
+ *to you under the Apache License, Version 2.0 (the
+ *"License"); you may not use this file except in compliance
+ *with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *Unless required by applicable law or agreed to in writing,
+ *software distributed under the License is distributed on an
+ *"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ *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_errno.h"
+
+#include "shell_private.h"
+
+
+#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;
+
+	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_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;
+
+        status = logHelper_create(context_ptr, &(*shell_service_ptr)->shell->logHelper);
+	}
+
+	if (status != CELIX_SUCCESS) {
+		shell_destroy(shell_service_ptr);
+	}
+
+	return status;
+}
+
+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;
+}
+
+celix_status_t shell_addCommand(shell_pt shell_ptr, service_reference_pt reference_ptr, void *svc) {
+    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;
+        }
+    }
+
+    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);
+    }
+
+    return status;
+}
+
+celix_status_t shell_removeCommand(shell_pt shell_ptr, service_reference_pt reference_ptr, void *svc) {
+    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;
+        }
+    }
+
+    if (status == CELIX_SUCCESS) {
+        hashMap_remove(shell_ptr->command_name_map_ptr, (char *)name_str);
+    }
+
+    return status;
+}
+
+celix_status_t shell_getCommands(shell_pt shell_ptr, array_list_pt *commands_ptr) {
+	celix_status_t status = CELIX_SUCCESS;
+
+	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;
+		}
+	}
+
+	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);
+	}
+
+	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;
+	}
+
+	if (status == CELIX_SUCCESS) {
+		status = shell_getCommandReference(shell_ptr, command_name_str, &reference);
+		if (!reference) {
+			status = CELIX_BUNDLE_EXCEPTION;
+		}
+	}
+
+	if (status == CELIX_SUCCESS) {
+		status = serviceReference_getProperty(reference, "command.usage", (const char**)usage_pstr);
+	}
+
+	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;
+		}
+	}
+
+	if (status == CELIX_SUCCESS) {
+		serviceReference_getProperty(reference, "command.description", (const char**)command_description_pstr);
+	}
+
+	return status;
+}
+
+celix_status_t shell_getCommandReference(shell_pt shell_ptr, char *command_name_str, service_reference_pt *command_reference_ptr) {
+	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;
+	}
+
+	if (status == CELIX_SUCCESS) {
+		size_t pos = strcspn(command_line_str, " ");
+
+		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);
+		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;
+}

http://git-wip-us.apache.org/repos/asf/celix/blob/3bce889b/bundles/shell/shell/src/shell_private.h
----------------------------------------------------------------------
diff --git a/bundles/shell/shell/src/shell_private.h b/bundles/shell/shell/src/shell_private.h
new file mode 100644
index 0000000..7270fa2
--- /dev/null
+++ b/bundles/shell/shell/src/shell_private.h
@@ -0,0 +1,51 @@
+/**
+ *Licensed to the Apache Software Foundation (ASF) under one
+ *or more contributor license agreements.  See the NOTICE file
+ *distributed with this work for additional information
+ *regarding copyright ownership.  The ASF licenses this file
+ *to you under the Apache License, Version 2.0 (the
+ *"License"); you may not use this file except in compliance
+ *with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *Unless required by applicable law or agreed to in writing,
+ *software distributed under the License is distributed on an
+ *"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ *specific language governing permissions and limitations
+ *under the License.
+ */
+/*
+ * shell_private.h
+ *
+ *  \date       Aug 13, 2010
+ *  \author    	<a href="mailto:dev@celix.apache.org">Apache Celix Project Team</a>
+ *  \copyright	Apache License, Version 2.0
+ */
+
+#ifndef SHELL_PRIVATE_H_
+#define SHELL_PRIVATE_H_
+
+#include "bundle_context.h"
+#include "shell.h"
+#include "hash_map.h"
+#include "command.h"
+#include "log_helper.h"
+
+struct shell {
+	bundle_context_pt bundle_context_ptr;
+	hash_map_pt command_reference_map_ptr;
+	hash_map_pt command_name_map_ptr;
+	log_helper_pt logHelper;
+};
+
+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_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);
+
+#endif /* SHELL_PRIVATE_H_ */

http://git-wip-us.apache.org/repos/asf/celix/blob/3bce889b/bundles/shell/shell/src/start_command.c
----------------------------------------------------------------------
diff --git a/bundles/shell/shell/src/start_command.c b/bundles/shell/shell/src/start_command.c
new file mode 100644
index 0000000..f43699a
--- /dev/null
+++ b/bundles/shell/shell/src/start_command.c
@@ -0,0 +1,84 @@
+/**
+ *Licensed to the Apache Software Foundation (ASF) under one
+ *or more contributor license agreements.  See the NOTICE file
+ *distributed with this work for additional information
+ *regarding copyright ownership.  The ASF licenses this file
+ *to you under the Apache License, Version 2.0 (the
+ *"License"); you may not use this file except in compliance
+ *with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *Unless required by applicable law or agreed to in writing,
+ *software distributed under the License is distributed on an
+ *"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ *specific language governing permissions and limitations
+ *under the License.
+ */
+/*
+ * start_command.c
+ *
+ *  \date       Aug 20, 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 <stdio.h>
+
+#include "std_commands.h"
+#include "bundle_context.h"
+
+celix_status_t startCommand_execute(void *_ptr, char *command_line_str, FILE *out_ptr, FILE *err_ptr) {
+    celix_status_t status = CELIX_SUCCESS;
+
+    bundle_context_pt context_ptr = _ptr;
+
+    if (!context_ptr || !command_line_str || !out_ptr || !err_ptr) {
+        status = CELIX_ILLEGAL_ARGUMENT;
+    }
+
+    if (status == CELIX_SUCCESS) {
+        char *sub_str = NULL;
+        char *save_ptr = NULL;
+
+        strtok_r(command_line_str, OSGI_SHELL_COMMAND_SEPARATOR, &save_ptr);
+        sub_str = strtok_r(NULL, OSGI_SHELL_COMMAND_SEPARATOR, &save_ptr);
+
+        if (sub_str == NULL) {
+            fprintf(out_ptr, "Incorrect number of arguments.\n");
+        } else {
+            while (sub_str != NULL) {
+                celix_status_t sub_status = CELIX_SUCCESS;
+
+                bundle_pt bundle_ptr = NULL;
+
+                char *end_str = NULL;
+                long id = strtol(sub_str, &end_str, 10);
+                if (*end_str) {
+                    sub_status = CELIX_ILLEGAL_ARGUMENT;
+                    fprintf(err_ptr, "Bundle id '%s' is invalid, problem at %s\n", sub_str, end_str);
+                }
+
+                if (sub_status == CELIX_SUCCESS) {
+                    sub_status = bundleContext_getBundleById(context_ptr, id, &bundle_ptr);
+                }
+
+                if (sub_status == CELIX_SUCCESS) {
+                    bundle_startWithOptions(bundle_ptr, 0);
+                }
+
+                if (sub_status != CELIX_SUCCESS) {
+                    status = sub_status;
+                    break;
+                }
+
+                sub_str = strtok_r(NULL, OSGI_SHELL_COMMAND_SEPARATOR, &save_ptr);
+            }
+        }
+
+    }
+
+    return status;
+}

http://git-wip-us.apache.org/repos/asf/celix/blob/3bce889b/bundles/shell/shell/src/std_commands.h
----------------------------------------------------------------------
diff --git a/bundles/shell/shell/src/std_commands.h b/bundles/shell/shell/src/std_commands.h
new file mode 100644
index 0000000..2ac0959
--- /dev/null
+++ b/bundles/shell/shell/src/std_commands.h
@@ -0,0 +1,46 @@
+/**
+ *Licensed to the Apache Software Foundation (ASF) under one
+ *or more contributor license agreements.  See the NOTICE file
+ *distributed with this work for additional information
+ *regarding copyright ownership.  The ASF licenses this file
+ *to you under the Apache License, Version 2.0 (the
+ *"License"); you may not use this file except in compliance
+ *with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *Unless required by applicable law or agreed to in writing,
+ *software distributed under the License is distributed on an
+ *"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ *specific language governing permissions and limitations
+ *under the License.
+ */
+/*
+ * std_commands.h
+ *
+ *  \date       March 27, 2014
+ *  \author    	<a href="mailto:dev@celix.apache.org">Apache Celix Project Team</a>
+ *  \copyright	Apache License, Version 2.0
+ */
+
+#ifndef __STD_COMMANDS_H_
+#define __STD_COMMANDS_H_
+
+#include "celix_errno.h"
+
+#define OSGI_SHELL_COMMAND_SEPARATOR " "
+
+celix_status_t psCommand_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);
+celix_status_t uninstallCommand_execute(void *handle, char * commandline, FILE *outStream, FILE *errStream);
+celix_status_t updateCommand_execute(void *handle, char * commandline, FILE *outStream, FILE *errStream);
+celix_status_t logCommand_execute(void *handle, char * commandline, FILE *outStream, FILE *errStream);
+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);
+
+
+#endif

http://git-wip-us.apache.org/repos/asf/celix/blob/3bce889b/bundles/shell/shell/src/stop_command.c
----------------------------------------------------------------------
diff --git a/bundles/shell/shell/src/stop_command.c b/bundles/shell/shell/src/stop_command.c
new file mode 100644
index 0000000..0093c99
--- /dev/null
+++ b/bundles/shell/shell/src/stop_command.c
@@ -0,0 +1,82 @@
+/**
+ *Licensed to the Apache Software Foundation (ASF) under one
+ *or more contributor license agreements.  See the NOTICE file
+ *distributed with this work for additional information
+ *regarding copyright ownership.  The ASF licenses this file
+ *to you under the Apache License, Version 2.0 (the
+ *"License"); you may not use this file except in compliance
+ *with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *Unless required by applicable law or agreed to in writing,
+ *software distributed under the License is distributed on an
+ *"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ *specific language governing permissions and limitations
+ *under the License.
+ */
+/*
+ * stop_command.c
+ *
+ *  \date       Aug 20, 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 "bundle_context.h"
+#include "std_commands.h"
+
+celix_status_t stopCommand_execute(void *_ptr, char *command_line_str, FILE *out_ptr, FILE *err_ptr) {
+    celix_status_t status = CELIX_SUCCESS;
+
+    bundle_context_pt context_ptr = _ptr;
+
+    if (!context_ptr || !command_line_str || !out_ptr || !err_ptr) {
+        status = CELIX_ILLEGAL_ARGUMENT;
+    }
+
+    if (status == CELIX_SUCCESS) {
+        char *sub_str = NULL;
+        char *save_ptr = NULL;
+
+        strtok_r(command_line_str, OSGI_SHELL_COMMAND_SEPARATOR, &save_ptr);
+        sub_str = strtok_r(NULL, OSGI_SHELL_COMMAND_SEPARATOR, &save_ptr);
+
+        if (sub_str == NULL) {
+            fprintf(out_ptr, "Incorrect number of arguments.\n");
+        } else {
+            while (sub_str != NULL) {
+                celix_status_t sub_status = CELIX_SUCCESS;
+
+                bundle_pt bundle_ptr = NULL;
+
+                char *end_str = NULL;
+                long id = strtol(sub_str, &end_str, 10);
+                if (*end_str) {
+                    sub_status = CELIX_ILLEGAL_ARGUMENT;
+                    fprintf(err_ptr, "Bundle id '%s' is invalid, problem at %s\n", sub_str, end_str);
+                }
+
+                if (sub_status == CELIX_SUCCESS) {
+                    sub_status = bundleContext_getBundleById(context_ptr, id, &bundle_ptr);
+                }
+
+                if (sub_status == CELIX_SUCCESS) {
+                    bundle_stopWithOptions(bundle_ptr, 0);
+                }
+
+                if (sub_status != CELIX_SUCCESS) {
+                    status = sub_status;
+                    break;
+                }
+
+                sub_str = strtok_r(NULL, OSGI_SHELL_COMMAND_SEPARATOR, &save_ptr);
+            }
+        }
+    }
+
+    return status;
+}

http://git-wip-us.apache.org/repos/asf/celix/blob/3bce889b/bundles/shell/shell/src/uninstall_command.c
----------------------------------------------------------------------
diff --git a/bundles/shell/shell/src/uninstall_command.c b/bundles/shell/shell/src/uninstall_command.c
new file mode 100644
index 0000000..fb30831
--- /dev/null
+++ b/bundles/shell/shell/src/uninstall_command.c
@@ -0,0 +1,58 @@
+/**
+ *Licensed to the Apache Software Foundation (ASF) under one
+ *or more contributor license agreements.  See the NOTICE file
+ *distributed with this work for additional information
+ *regarding copyright ownership.  The ASF licenses this file
+ *to you under the Apache License, Version 2.0 (the
+ *"License"); you may not use this file except in compliance
+ *with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *Unless required by applicable law or agreed to in writing,
+ *software distributed under the License is distributed on an
+ *"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ *specific language governing permissions and limitations
+ *under the License.
+ */
+/*
+ * uninstall_command.c
+ *
+ *  \date       Aug 20, 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 "array_list.h"
+#include "bundle_context.h"
+
+celix_status_t uninstallCommand_execute(void *handle, char * line, FILE *outStream, FILE *errStream) {
+	bundle_context_pt context = handle;
+	char delims[] = " ";
+	char * sub = NULL;
+	char *save_ptr = NULL;
+	celix_status_t status = CELIX_SUCCESS;
+
+	sub = strtok_r(line, delims, &save_ptr);
+	sub = strtok_r(NULL, delims, &save_ptr);
+
+	if (sub == NULL) {
+		fprintf(errStream, "Incorrect number of arguments.\n");
+	} else {
+		while (sub != NULL) {
+			long id = atol(sub);
+			bundle_pt bundle = NULL;
+			status = bundleContext_getBundleById(context, id, &bundle);
+			if (status==CELIX_SUCCESS && bundle!=NULL) {
+				bundle_uninstall(bundle);
+			} else {
+				fprintf(errStream, "Bundle id is invalid.");
+			}
+			sub = strtok_r(NULL, delims, &save_ptr);
+		}
+	}
+	return status;
+}

http://git-wip-us.apache.org/repos/asf/celix/blob/3bce889b/bundles/shell/shell/src/update_command.c
----------------------------------------------------------------------
diff --git a/bundles/shell/shell/src/update_command.c b/bundles/shell/shell/src/update_command.c
new file mode 100644
index 0000000..64999ac
--- /dev/null
+++ b/bundles/shell/shell/src/update_command.c
@@ -0,0 +1,117 @@
+/**
+ *Licensed to the Apache Software Foundation (ASF) under one
+ *or more contributor license agreements.  See the NOTICE file
+ *distributed with this work for additional information
+ *regarding copyright ownership.  The ASF licenses this file
+ *to you under the Apache License, Version 2.0 (the
+ *"License"); you may not use this file except in compliance
+ *with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *Unless required by applicable law or agreed to in writing,
+ *software distributed under the License is distributed on an
+ *"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ *specific language governing permissions and limitations
+ *under the License.
+ */
+/*
+ * update_command.c
+ *
+ *  \date       Aug 20, 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 <curl/curl.h>
+#include <sys/stat.h>
+
+#include "array_list.h"
+#include "bundle_context.h"
+
+celix_status_t updateCommand_download(bundle_context_pt context, char * url, char **inputFile);
+size_t updateCommand_writeData(void *ptr, size_t size, size_t nmemb, FILE *stream);
+
+void updateCommand_execute(void *handle, char * line, FILE *outStream, FILE *errStream) {
+	bundle_context_pt context = handle;
+    bundle_pt bundle = NULL;
+	char delims[] = " ";
+	char * sub = NULL;
+	char *save_ptr = NULL;
+
+	sub = strtok_r(line, delims, &save_ptr);
+	sub = strtok_r(NULL, delims, &save_ptr);
+
+	if (sub == NULL) {
+		fprintf(errStream, "Incorrect number of arguments.\n");
+	} else {
+		long id = atol(sub);
+		celix_status_t ret = bundleContext_getBundleById(context, id, &bundle);
+		if (ret==CELIX_SUCCESS && bundle!=NULL) {
+			char inputFile[256];
+			sub = strtok_r(NULL, delims, &save_ptr);
+			inputFile[0] = '\0';
+			if (sub != NULL) {
+				char *test = inputFile;
+				printf("URL: %s\n", sub);
+
+				if (updateCommand_download(context, sub, &test) == CELIX_SUCCESS) {
+					printf("Update bundle with stream\n");
+					bundle_update(bundle, inputFile);
+				} else {
+					fprintf(errStream, "Unable to download from %s\n", sub);
+				}
+			} else {
+				bundle_update(bundle, NULL);
+			}
+		} else {
+			fprintf(errStream, "Bundle id is invalid.\n");
+		}
+	}
+}
+
+celix_status_t updateCommand_download(bundle_context_pt context, char * url, char **inputFile) {
+	CURL *curl = NULL;
+	CURLcode res = CURLE_FILE_COULDNT_READ_FILE;
+	curl = curl_easy_init();
+	if (curl) {
+		FILE *fp = NULL;
+		snprintf(*inputFile, 13,"updateXXXXXX");
+		umask(0011);
+		int fd = mkstemp(*inputFile);
+		if (fd) {
+		    fp = fopen(*inputFile, "wb+");
+		    if(fp!=NULL){
+		    	printf("Temp file: %s\n", *inputFile);
+		    	curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);
+		    	curl_easy_setopt(curl, CURLOPT_URL, url);
+		    	curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, updateCommand_writeData);
+		    	curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp);
+		    	//curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0);
+		    	//curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, updateCommand_downloadProgress);
+		    	res = curl_easy_perform(curl);
+		    	fclose(fp);
+		    }
+		    /* always cleanup */
+		    curl_easy_cleanup(curl);
+		    if(fp==NULL){
+		    	return CELIX_FILE_IO_EXCEPTION;
+		    }
+		}
+	}
+	if (res != CURLE_OK) {
+		printf("Error: %d\n", res);
+		*inputFile[0] = '\0';
+		return CELIX_ILLEGAL_STATE;
+	} else {
+		return CELIX_SUCCESS;
+	}
+}
+
+size_t updateCommand_writeData(void *ptr, size_t size, size_t nmemb, FILE *stream) {
+    size_t written = fwrite(ptr, size, nmemb, stream);
+    return written;
+}

http://git-wip-us.apache.org/repos/asf/celix/blob/3bce889b/bundles/shell/shell_bonjour/CMakeLists.txt
----------------------------------------------------------------------
diff --git a/bundles/shell/shell_bonjour/CMakeLists.txt b/bundles/shell/shell_bonjour/CMakeLists.txt
new file mode 100644
index 0000000..034a4f3
--- /dev/null
+++ b/bundles/shell/shell_bonjour/CMakeLists.txt
@@ -0,0 +1,51 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+# 
+#   http://www.apache.org/licenses/LICENSE-2.0
+# 
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+celix_subproject(SHELL_BONJOUR "Option to enable building the Bonjour Shell (shell access by chat clients)" OFF DEPS LAUNCHER shell)
+if (SHELL_BONJOUR)
+	find_package(LibXml2 REQUIRED)
+	
+	#TODO create/add FindDNS_SD.cmake and use it (with required)
+	find_library(DNS_SD_LIB NAMES dns_sd dns_services)
+	
+	set(BUNDLE_SYMBOLICNAME "bonjour_shell")
+	set(BUNDLE_VERSION "0.1.0")
+	set(BUNDLE_NAME "bonjour_shell")
+
+	add_celix_bundle(bonjour_shell
+		VERSION "1.0.0"
+		SOURCES
+		 	private/src/activator.c
+		 	private/src/bonjour_shell.c
+	)
+	add_library(Celix::bonjour_shell ALIAS bonjour_shell)
+
+
+	target_include_directories(bonjour_shell PRIVATE
+			"${PROJECT_SOURCE_DIR}/utils/public/include"
+			"${LIBXML2_INCLUDE_DIR}"
+			private/include
+	)
+	target_link_libraries(bonjour_shell PRIVATE ${LIBXML2_LIBRARIES} ${DNS_SD_LIB} Celix::shell_api)
+
+	add_celix_container("bonjour_shell_deploy" BUNDLES
+		Celix::shell
+		bonjour_shell
+		PROPERTIES "bonjour.shell.id=Apache Celix"
+	)
+endif (SHELL_BONJOUR)
+

http://git-wip-us.apache.org/repos/asf/celix/blob/3bce889b/bundles/shell/shell_bonjour/private/include/bonjour_shell.h
----------------------------------------------------------------------
diff --git a/bundles/shell/shell_bonjour/private/include/bonjour_shell.h b/bundles/shell/shell_bonjour/private/include/bonjour_shell.h
new file mode 100644
index 0000000..1072f0e
--- /dev/null
+++ b/bundles/shell/shell_bonjour/private/include/bonjour_shell.h
@@ -0,0 +1,43 @@
+/**
+ *Licensed to the Apache Software Foundation (ASF) under one
+ *or more contributor license agreements.  See the NOTICE file
+ *distributed with this work for additional information
+ *regarding copyright ownership.  The ASF licenses this file
+ *to you under the Apache License, Version 2.0 (the
+ *"License"); you may not use this file except in compliance
+ *with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *Unless required by applicable law or agreed to in writing,
+ *software distributed under the License is distributed on an
+ *"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ *specific language governing permissions and limitations
+ *under the License.
+ */
+/*
+ * bonjour_shell.h
+ *
+ *  \date       Oct 20, 2014
+ *  \author    	<a href="mailto:dev@celix.apache.org">Apache Celix Project Team</a>
+ *  \copyright	Apache License, Version 2.0
+ */
+
+#ifndef BONJOUR_SHELL_H_
+#define BONJOUR_SHELL_H_
+
+#include <service_reference.h>
+
+#include "celix_errno.h"
+
+typedef struct bonjour_shell *bonjour_shell_pt;
+
+celix_status_t bonjourShell_create(char *id, bonjour_shell_pt *shell);
+celix_status_t bonjourShell_destroy(bonjour_shell_pt shell);
+
+celix_status_t bonjourShell_addShellService(void * handle, service_reference_pt reference, void * service);
+celix_status_t bonjourShell_removeShellService(void * handle, service_reference_pt reference, void * service);
+
+
+#endif /* BONJOUR_SHELL_H_ */

http://git-wip-us.apache.org/repos/asf/celix/blob/3bce889b/bundles/shell/shell_bonjour/private/src/activator.c
----------------------------------------------------------------------
diff --git a/bundles/shell/shell_bonjour/private/src/activator.c b/bundles/shell/shell_bonjour/private/src/activator.c
new file mode 100644
index 0000000..e05211d
--- /dev/null
+++ b/bundles/shell/shell_bonjour/private/src/activator.c
@@ -0,0 +1,110 @@
+/**
+ *Licensed to the Apache Software Foundation (ASF) under one
+ *or more contributor license agreements.  See the NOTICE file
+ *distributed with this work for additional information
+ *regarding copyright ownership.  The ASF licenses this file
+ *to you under the Apache License, Version 2.0 (the
+ *"License"); you may not use this file except in compliance
+ *with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *Unless required by applicable law or agreed to in writing,
+ *software distributed under the License is distributed on an
+ *"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ *specific language governing permissions and limitations
+ *under the License.
+ */
+/*
+ * activator.c
+ *
+ *  \date       Oct 20, 2014
+ *  \author    	<a href="mailto:dev@celix.apache.org">Apache Celix Project Team</a>
+ *  \copyright	Apache License, Version 2.0
+ */
+
+#include <celix_errno.h>
+
+#include <stdlib.h>
+
+#include "bundle_activator.h"
+#include "bundle_context.h"
+#include "constants.h"
+
+#include "bonjour_shell.h"
+
+#include <celix_errno.h>
+#include <service_tracker.h>
+#include <shell.h>
+
+struct bundle_instance {
+        bonjour_shell_pt shell;
+        service_tracker_pt tracker;
+};
+
+typedef struct bundle_instance *bundle_instance_pt;
+
+celix_status_t bundleActivator_create(bundle_context_pt context, void **userData) {
+        celix_status_t status = CELIX_SUCCESS;
+        struct bundle_instance *bi = calloc(1, sizeof(*bi));
+        
+        if (bi) {
+                bi->shell = NULL;
+                bi->tracker = NULL;
+                (*userData) = bi;
+        } else {
+                status = CELIX_ENOMEM;
+                
+        }
+        return status;
+}
+
+celix_status_t bundleActivator_start(void * userData, bundle_context_pt context) {
+        celix_status_t status = CELIX_SUCCESS;
+        bundle_instance_pt bi = (bundle_instance_pt) userData;
+
+        const char *uuid = NULL;
+        bundleContext_getProperty(context, OSGI_FRAMEWORK_FRAMEWORK_UUID, &uuid);
+        const char *hostname = NULL;
+        bundleContext_getProperty(context, "HOSTNAME", &hostname);
+        const char *bonjourShellId = NULL;
+        bundleContext_getProperty(context, "bonjour.shell.id", &bonjourShellId);
+
+        char id[128];
+        if (bonjourShellId != NULL) {
+                snprintf(id, 128, "%s", bonjourShellId);
+        } else if (hostname != NULL) {
+                snprintf(id, 128, "Celix-%.8s@%s", uuid, hostname);
+        } else {
+                snprintf(id, 128, "Celix-%.8s", uuid);
+        }
+        status = bonjourShell_create(id, &bi->shell);
+
+        service_tracker_customizer_pt cust = NULL;
+        serviceTrackerCustomizer_create(bi->shell, NULL, bonjourShell_addShellService, NULL, bonjourShell_removeShellService, &cust);
+        serviceTracker_create(context, (char *) OSGI_SHELL_SERVICE_NAME, cust, &bi->tracker);
+        serviceTracker_open(bi->tracker);
+
+
+        return status;
+}
+
+celix_status_t bundleActivator_stop(void * userData, bundle_context_pt context) {
+        celix_status_t status = CELIX_SUCCESS;
+        bundle_instance_pt bi = (bundle_instance_pt) userData;
+
+        serviceTracker_close(bi->tracker);
+
+        return status;
+}
+
+celix_status_t bundleActivator_destroy(void * userData, bundle_context_pt context) {
+        celix_status_t status = CELIX_SUCCESS;
+        bundle_instance_pt bi = (bundle_instance_pt) userData;
+
+        serviceTracker_destroy(bi->tracker);
+        bonjourShell_destroy(bi->shell);
+
+        return status;
+}

http://git-wip-us.apache.org/repos/asf/celix/blob/3bce889b/bundles/shell/shell_bonjour/private/src/bonjour_shell.c
----------------------------------------------------------------------
diff --git a/bundles/shell/shell_bonjour/private/src/bonjour_shell.c b/bundles/shell/shell_bonjour/private/src/bonjour_shell.c
new file mode 100644
index 0000000..59ee18a
--- /dev/null
+++ b/bundles/shell/shell_bonjour/private/src/bonjour_shell.c
@@ -0,0 +1,445 @@
+/**
+ *Licensed to the Apache Software Foundation (ASF) under one
+ *or more contributor license agreements.  See the NOTICE file
+ *distributed with this work for additional information
+ *regarding copyright ownership.  The ASF licenses this file
+ *to you under the Apache License, Version 2.0 (the
+ *"License"); you may not use this file except in compliance
+ *with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *Unless required by applicable law or agreed to in writing,
+ *software distributed under the License is distributed on an
+ *"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ *specific language governing permissions and limitations
+ *under the License.
+ */
+/*
+ * bonjour_shell.c
+ *
+ *  \date       Oct 20, 2014
+ *  \author    	<a href="mailto:dev@celix.apache.org">Apache Celix Project Team</a>
+ *  \copyright	Apache License, Version 2.0
+ */
+
+#include "bonjour_shell.h"
+
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <pthread.h>
+#include <arpa/inet.h>
+#include <sys/time.h>
+#include <unistd.h>
+
+#include <dns_sd.h>
+#include <libxml/xmlreader.h>
+#include <libxml/xmlwriter.h>
+#include <celixbool.h>
+#include <shell.h>
+
+#if defined(BSD) || defined(__APPLE__)  || defined(ANDROID)
+#include "open_memstream.h"
+#include "fmemopen.h"
+#endif
+
+#define MAX_BUFFER_SIZE 5120
+
+//static xmlBufferPtr buf; //FOR DEBUG
+
+struct bonjour_shell {
+	char *id;
+	volatile bool running;
+
+	//service member and mutex
+	pthread_mutex_t mutex;
+	shell_service_pt service;
+
+	//tcp socket members
+	pthread_t listenThread;
+	int listenSocket;
+	uint16_t portInNetworkByteOrder;
+
+	//dns_sd registration
+	DNSServiceRef sdRef;
+	TXTRecordRef txtRecord;
+
+};
+
+struct connection_context {
+	bool gotCommand;
+	bool running;
+	int sockfd;
+	bonjour_shell_pt shell;
+	xmlTextWriterPtr writer;
+	xmlTextReaderPtr reader;
+	pthread_t sendThread;
+	pthread_mutex_t mutex;
+	pthread_cond_t dataAvailCond;
+	array_list_pt dataList; //protected by mutex
+	struct timeval lastUpdated; //protected by mutex
+};
+
+static struct connection_context *currentContext = NULL; //TODO update shell to accept void * data next to callback
+
+static void bonjourShell_addDataToCurrentContext(const char* out, const char* err);
+static void bonjourShell_sendData(struct connection_context *context);
+
+static celix_status_t bonjourShell_register(bonjour_shell_pt shell);
+static celix_status_t bonjourShell_listen(bonjour_shell_pt shell);
+
+static void bonjourShell_acceptConnection(bonjour_shell_pt shell, int connectionFd);
+
+static void bonjourShell_parse(bonjour_shell_pt shell, struct connection_context *context);
+static void bonjourShell_parseXmlNode(bonjour_shell_pt shell, struct connection_context *context);
+
+static void bonjourShell_parseStream(bonjour_shell_pt shell, struct connection_context *context);
+static void bonjourShell_parseCommand(bonjour_shell_pt shell, struct connection_context *context);
+
+celix_status_t bonjourShell_create(char *id, bonjour_shell_pt *result) {
+	celix_status_t status = CELIX_SUCCESS;
+	bonjour_shell_pt shell = (bonjour_shell_pt) calloc(1, sizeof(*shell));
+	if (shell != NULL) {
+		shell->id = strdup(id);
+		shell->running = true;
+		shell->listenSocket = 0;
+		shell->service = NULL;
+
+		pthread_mutex_init(&shell->mutex, NULL);
+
+		pthread_create(&shell->listenThread, NULL, (void *)bonjourShell_listen, shell);
+
+		(*result) = shell;
+	} else {
+		status = CELIX_ENOMEM;
+	}
+	return status;
+}
+
+static celix_status_t bonjourShell_register(bonjour_shell_pt shell) {
+	celix_status_t status = CELIX_SUCCESS;
+
+	uint16_t portInNetworkByteOrder = shell->portInNetworkByteOrder;
+	char *srvName = shell->id;
+	char portStr[64];
+	sprintf(portStr, "%i", ntohs(portInNetworkByteOrder));
+
+	TXTRecordCreate(&shell->txtRecord, 256, NULL);
+
+	TXTRecordSetValue(&shell->txtRecord, "txtver", 1, "1");
+	TXTRecordSetValue(&shell->txtRecord, "version", 1, "1");;
+	TXTRecordSetValue(&shell->txtRecord, "1st", strlen(shell->id), shell->id);
+	TXTRecordSetValue(&shell->txtRecord, "port.p2pj", 5, portStr);
+	TXTRecordSetValue(&shell->txtRecord, "status", 5, "avail");
+
+	DNSServiceRegister(&shell->sdRef, 0, 0,
+	srvName, /* may be NULL */
+	"_presence._tcp",
+	NULL, /* may be NULL */
+	NULL, /* may be NULL */
+	portInNetworkByteOrder, /* In network byte order */
+	TXTRecordGetLength(&shell->txtRecord), TXTRecordGetBytesPtr(&shell->txtRecord), /* may be NULL */
+	NULL, /* may be NULL */
+	NULL /* may be NULL */
+	);
+
+	//DNSServiceProcessResult(shell->sdRef);
+
+	return status;
+}
+
+static celix_status_t bonjourShell_listen(bonjour_shell_pt shell) {
+	celix_status_t status = CELIX_SUCCESS;
+
+
+	shell->listenSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+	if (shell->listenSocket < 0) {
+		printf("error opening socket\n");
+		return CELIX_START_ERROR;
+	}
+
+	struct sockaddr_in serv_addr;
+	memset(&serv_addr, 0, sizeof(serv_addr));       /* Clear struct */
+	serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);  /* Incoming addr */
+	serv_addr.sin_family = AF_INET;                 /* Internet/IP */
+	serv_addr.sin_port = 0;   			           /* server port, don't specify let os decide */
+
+	if (bind(shell->listenSocket, (struct sockaddr *) &serv_addr, sizeof(struct sockaddr_in)) < 0) {
+		printf("error binding\n");
+		return CELIX_START_ERROR;
+	}
+
+	if (listen(shell->listenSocket, 1) < 0) {
+		printf("error listening");
+		return CELIX_START_ERROR;
+	}
+
+	struct sockaddr_in sin;
+	socklen_t len = sizeof(sin);
+	if (getsockname(shell->listenSocket, (struct sockaddr *)&sin, &len) == -1) {
+	    perror("getsockname");
+		return CELIX_START_ERROR;
+	} else {
+		shell->portInNetworkByteOrder = sin.sin_port;
+	}
+
+	status = bonjourShell_register(shell);
+	if (status != CELIX_SUCCESS) {
+		return status;
+	}
+
+	struct sockaddr_in connect_addr;
+	socklen_t slen = sizeof(struct sockaddr_in);
+	while (shell->running) {
+		int connectionSocket = accept(shell->listenSocket, (struct sockaddr *) &connect_addr, &slen);
+		if (connectionSocket < 0) {
+			printf("error accepting connection\n");
+			return CELIX_START_ERROR;
+		} else {
+			bonjourShell_acceptConnection(shell, connectionSocket);
+		}
+	}
+
+	return status;
+}
+
+static void bonjourShell_acceptConnection(bonjour_shell_pt shell, int connectionFd) {
+	//printf("setting up parser\n");
+
+	struct connection_context context;
+	context.gotCommand = false;
+	context.running = true;
+	context.sockfd = connectionFd;
+	context.shell = shell;
+
+	context.reader = xmlReaderForFd(context.sockfd, NULL, NULL, 0);
+
+	xmlOutputBufferPtr outputBuff = xmlOutputBufferCreateFd(context.sockfd,
+			NULL);
+	context.writer  = xmlNewTextWriter(outputBuff);
+
+	//buf = xmlBufferCreate();
+	//xmlTextWriterPtr writer = xmlNewTextWriterMemory(buf, 0);
+
+	//init send thread and needed data types.
+	arrayList_create(&context.dataList);
+	pthread_cond_init(&context.dataAvailCond, NULL); //TODO destroy
+	pthread_mutex_init(&context.mutex, NULL); //TODO destroy
+	pthread_create(&context.sendThread, NULL, (void *)bonjourShell_sendData, &context);
+
+	int sockStatus = 0;
+	if (context.reader != NULL && context.writer != NULL) {
+		while (sockStatus == 0 && shell->running && context.running) {
+			bonjourShell_parse(shell, &context);
+
+			//check if socket is closed
+			int error = 0;
+			socklen_t len = sizeof(error);
+			sockStatus = getsockopt(context.sockfd, SOL_SOCKET, SO_ERROR,
+					&error, &len);
+			if (sockStatus != 0) {
+				printf("Got error from socket error is %i", error);
+			}
+		}
+
+		if (sockStatus == 0) { //shell stopped still connected
+			usleep(1500); //wait untill all data is send
+			xmlTextWriterEndElement(context.writer); //end stream
+			xmlTextWriterEndDocument(context.writer);
+			close(context.sockfd);
+			xmlFreeTextReader(context.reader);
+			xmlFreeTextWriter(context.writer);
+		}
+		//printf("after close + free of xml parser & socker\n");
+
+		context.running = false;
+		pthread_cond_signal(&context.dataAvailCond);
+
+		pthread_join(context.sendThread, NULL);
+		pthread_mutex_destroy(&context.mutex);
+		pthread_cond_destroy(&context.dataAvailCond);
+	} else {
+		if (context.reader != NULL) {
+			xmlFreeTextReader(context.reader);
+		}
+		if (context.writer != NULL) {
+			xmlFreeTextWriter(context.writer);
+		}
+	}
+        arrayList_destroy(context.dataList);
+
+}
+
+static void bonjourShell_parse(bonjour_shell_pt shell, struct connection_context *context) {
+	xmlTextReaderRead(context->reader);
+	bonjourShell_parseXmlNode(shell, context);
+}
+
+static void bonjourShell_parseXmlNode(bonjour_shell_pt shell, struct connection_context *context) {
+	   xmlChar *name;
+
+	   int nodeType = xmlTextReaderNodeType(context->reader);
+
+	   if (nodeType == XML_READER_TYPE_ELEMENT) {
+		   name = xmlTextReaderLocalName(context->reader);
+		   //printf("found element with name %s\n", name);
+		   if (strcmp((char *)name, "stream") == 0) {
+			   bonjourShell_parseStream(shell, context);
+		   } else if (strcmp((char *)name, "body") == 0 && context->gotCommand == false) {
+			   bonjourShell_parseCommand(shell, context); //assuming first body element is command
+		   } else if (strcmp((char *)name, "message") == 0) {
+			   context->gotCommand = false;
+		   }
+		   xmlFree(name);
+	   } else if (nodeType == XML_READER_TYPE_END_ELEMENT /*end element*/ ) {
+		   name = xmlTextReaderLocalName(context->reader);
+		   //printf("found END element with name %s\n", name);
+		   if (strcmp((char *)name, "stream") == 0) {
+			  context->running = false;
+		   }
+		   xmlFree(name);
+	   } else {
+		   //printf("found node type %i\n", nodeType);
+	   }
+}
+
+static void bonjourShell_parseStream(bonjour_shell_pt shell, struct connection_context *context) {
+	xmlChar *to = xmlTextReaderGetAttribute(context->reader, (xmlChar *)"from");
+	xmlChar *from = xmlTextReaderGetAttribute(context->reader, (xmlChar *)"to");
+
+	xmlTextWriterStartDocument(context->writer, NULL, NULL, NULL);
+	xmlTextWriterStartElementNS(context->writer, (xmlChar *)"stream", (xmlChar *)"stream", (xmlChar *)"http://etherx.jabber.org/streams");
+	xmlTextWriterWriteAttribute(context->writer, (xmlChar *)"xmlns", (xmlChar *)"jabber:client"); //TODO should use namespace method/
+	xmlTextWriterWriteAttribute(context->writer, (xmlChar *)"to", to);
+	xmlTextWriterWriteAttribute(context->writer, (xmlChar *)"from", from);
+	xmlTextWriterWriteAttribute(context->writer, (xmlChar *)"version", (xmlChar *)"1.0");
+
+	xmlTextWriterWriteString(context->writer, (xmlChar *)"\n"); //Needed to flush to writer
+	xmlTextWriterFlush(context->writer);
+	//printf("current context buf: %s\n", (char *)buf->content);
+
+	if (from != NULL) {
+		xmlFree(from);
+	}
+	if (to != NULL) {
+		xmlFree(to);
+	}
+}
+
+static void bonjourShell_parseCommand(bonjour_shell_pt shell, struct connection_context *context)
+{
+        xmlChar *command = xmlTextReaderReadString(context->reader);
+
+        if (command != NULL) {
+                context->gotCommand = true;
+                currentContext = context;
+                pthread_mutex_lock(&shell->mutex);
+                if (shell->service != NULL) {
+                        char *outbuf;
+                        size_t outsize;
+                        char *errbuf;
+                        size_t errsize;
+
+                        FILE *out = open_memstream(&outbuf, &outsize);
+                        FILE *err = open_memstream(&errbuf, &errsize);
+
+                        shell->service->executeCommand(shell->service->shell, (char *) command, out, err);
+
+                        fclose(out);
+                        fclose(err);
+                        bonjourShell_addDataToCurrentContext(outbuf, errbuf);
+                        free(outbuf);
+                        free(errbuf);
+                }
+                pthread_mutex_unlock(&shell->mutex);
+        }
+
+        if (command != NULL) {
+                xmlFree(command);
+        }
+}
+
+static void bonjourShell_addDataToCurrentContext(const char* out, const char* err) {
+	pthread_mutex_lock(&currentContext->mutex);
+    if (out != NULL) {
+	    arrayList_add(currentContext->dataList, strdup(out));
+    }
+	if (err != NULL) {
+        arrayList_add(currentContext->dataList, strdup(err));
+    }
+    gettimeofday(&currentContext->lastUpdated, NULL);
+	pthread_mutex_unlock(&currentContext->mutex);
+	pthread_cond_signal(&currentContext->dataAvailCond);
+}
+
+static void bonjourShell_sendData(struct connection_context *context) {
+	while (context->running == true ) {
+		pthread_mutex_lock(&context->mutex);
+		pthread_cond_wait(&context->dataAvailCond, &context->mutex); //wait till some data is ready.
+
+		struct timeval now;
+		while (context->running) {
+			gettimeofday(&now, NULL);
+			long elapsed = (now.tv_sec * 1000000 + now.tv_usec) - (context->lastUpdated.tv_sec * 1000000 + context->lastUpdated.tv_usec);
+			if (elapsed > 1000) { //usec passed without update of data.
+				break;
+			}
+			pthread_mutex_unlock(&context->mutex);
+			usleep(1000);
+			pthread_mutex_lock(&context->mutex);
+		}
+
+		if (context->running) {
+			xmlTextWriterStartElement(currentContext->writer, (xmlChar *)"message");
+			xmlTextWriterWriteAttribute(currentContext->writer, (xmlChar *)"type", (xmlChar *)"chat");
+			xmlTextWriterStartElement(currentContext->writer, (xmlChar *)"body");
+			xmlTextWriterWriteString(currentContext->writer, (xmlChar *)"\n");
+			int i;
+			int size = arrayList_size(context->dataList);
+			for ( i = 0 ; i < size ; i += 1) {
+				char *entry = arrayList_get(context->dataList, i);
+				xmlTextWriterWriteString(currentContext->writer, (xmlChar *)entry);
+				//xmlTextWriterWriteString(currentContext->writer, (xmlChar *)"\r"); //needed for adium to create new line in UI
+				free(entry);
+			}
+			arrayList_clear(context->dataList);
+			xmlTextWriterEndElement(currentContext->writer); //end body
+			xmlTextWriterEndElement(currentContext->writer); //end message
+			xmlTextWriterWriteString(currentContext->writer, (xmlChar *)"\n"); //flush
+			xmlTextWriterFlush(currentContext->writer);
+		}
+		pthread_mutex_unlock(&context->mutex);
+	}
+}
+
+
+celix_status_t bonjourShell_destroy(bonjour_shell_pt shell) {
+	DNSServiceRefDeallocate(shell->sdRef);
+	TXTRecordDeallocate(&shell->txtRecord);
+
+	close(shell->listenSocket);
+	pthread_join(shell->listenThread, NULL);
+        free(shell->id);
+	free(shell);
+	return CELIX_SUCCESS;
+}
+
+celix_status_t bonjourShell_addShellService(void * handle, service_reference_pt reference, void * service) {
+	bonjour_shell_pt shell = handle;
+	pthread_mutex_lock(&shell->mutex);
+	shell->service = service;
+	pthread_mutex_unlock(&shell->mutex);
+	return CELIX_SUCCESS;
+}
+
+celix_status_t bonjourShell_removeShellService(void * handle, service_reference_pt reference, void * service) {
+	bonjour_shell_pt shell = handle;
+	pthread_mutex_lock(&shell->mutex);
+	if (shell->service == service) {
+		shell->service = NULL;
+	}
+	pthread_mutex_unlock(&shell->mutex);
+	return CELIX_SUCCESS;
+}


Mime
View raw message