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 22:58:43 UTC

[celix] branch feature/query_command updated: gh-144: Adds missing files.

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


The following commit(s) were added to refs/heads/feature/query_command by this push:
     new 436c737  gh-144: Adds missing files.
436c737 is described below

commit 436c737a40ee8499fad2d8e3c061b5e1b4a78a3a
Author: Pepijn Noltes <pe...@gmail.com>
AuthorDate: Sun Feb 2 23:58:28 2020 +0100

    gh-144: Adds missing files.
    
    Also add celix_utils_strdup function, which does a limited length strdup.
---
 bundles/shell/shell/src/activator.c                |   7 +-
 bundles/shell/shell/src/q_command.c                |  27 +++
 bundles/shell/shell/src/query_command.c            | 230 +++++++++++++++++++++
 bundles/shell/shell_tui/private/src/activator.c    |  40 ++--
 libs/framework/include/celix_bundle.h              |   3 +-
 libs/framework/src/bundle.c                        |  23 ++-
 libs/framework/src/service_registration.c          |   2 +-
 libs/framework/src/service_registry.c              |   3 +-
 .../include/{celix_utils_api.h => celix_utils.h}   |  34 +--
 libs/utils/include/celix_utils_api.h               |   1 +
 libs/utils/src/filter.c                            |  28 +--
 libs/utils/src/utils.c                             |  13 +-
 12 files changed, 337 insertions(+), 74 deletions(-)

diff --git a/bundles/shell/shell/src/activator.c b/bundles/shell/shell/src/activator.c
index 291e802..486cccd 100644
--- a/bundles/shell/shell/src/activator.c
+++ b/bundles/shell/shell/src/activator.c
@@ -154,12 +154,13 @@ celix_status_t bundleActivator_create(celix_bundle_context_t* ctx, void **_pptr)
                     .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."
+                    "\nIf a query is provided (or multiple), only service with a service name matching the query will be displayed." \
+                    "\nIf a query is a (LDAP) filter, filter matching will be used."
+                    "\nIf no query is provided all provided and requested services will be listed."
                     "\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 ...]"
+                    .usage = "query [bundleId ...] [-v] [-p] [-r] [query ...]"
                 };
         activator->std_commands[11] =
                 (struct command) {
diff --git a/bundles/shell/shell/src/q_command.c b/bundles/shell/shell/src/q_command.c
new file mode 100644
index 0000000..db60e61
--- /dev/null
+++ b/bundles/shell/shell/src/q_command.c
@@ -0,0 +1,27 @@
+/**
+ *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 "celix_bundle_context.h"
+
+celix_status_t qCommand_execute(void *_ptr, char *command_line_str __attribute__((unused)), FILE *sout, FILE *serr __attribute__((unused))) {
+    bundle_context_t* ctx = _ptr;
+    fprintf(sout, "Quitting framework\n");
+    celix_bundleContext_stopBundle(ctx, 0L);
+    return CELIX_SUCCESS;
+}
diff --git a/bundles/shell/shell/src/query_command.c b/bundles/shell/shell/src/query_command.c
new file mode 100644
index 0000000..9d06049
--- /dev/null
+++ b/bundles/shell/shell/src/query_command.c
@@ -0,0 +1,230 @@
+/**
+ *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 <string.h>
+#include <stdlib.h>
+#include <celix_api.h>
+
+#include "shell_constants.h"
+#include "celix_bundle_context.h"
+#include "std_commands.h"
+#include "celix_bundle.h"
+
+
+
+struct query_options {
+    bool useColors;
+    bool verbose;
+    bool queryProvided;
+    bool queryRequested;
+    long bndId; // -1L if no bundle is selected
+    celix_array_list_t *nameQueries; //entry is char*
+    celix_array_list_t *filterQueries; //enry if celix_filter_t
+};
+
+struct bundle_callback_data {
+    const struct query_options *opts;
+    FILE *sout;
+};
+
+static bool queryCommand_printProvidedService(const struct query_options *opts, celix_bundle_service_list_entry_t *entry) {
+    bool print = celix_arrayList_size(opts->nameQueries) == 0 && celix_arrayList_size(opts->filterQueries) == 0; //no queries, always print
+    for (int i = 0; i < celix_arrayList_size(opts->nameQueries) && !print; ++i) {
+        const char *qry = celix_arrayList_get(opts->nameQueries, i);
+        print = strcasestr(entry->serviceName, qry) != NULL;
+    }
+    for (int i = 0; i < celix_arrayList_size(opts->filterQueries) && !print; ++i) {
+        celix_filter_t *filter = celix_arrayList_get(opts->filterQueries, i);
+        print = celix_filter_match(filter, entry->serviceProperties);
+    }
+    return print;
+}
+
+static bool queryCommand_printRequestedService(const struct query_options *opts, celix_bundle_service_tracker_list_entry_t *entry) {
+    bool print = celix_arrayList_size(opts->nameQueries) == 0 && celix_arrayList_size(opts->filterQueries) == 0; //no queries, always print
+    for (int i = 0; i < celix_arrayList_size(opts->nameQueries) && !print; ++i) {
+        const char *qry = celix_arrayList_get(opts->nameQueries, i);
+        print = strcasestr(entry->serviceName, qry) != NULL;
+    }
+    for (int i = 0; i < celix_arrayList_size(opts->filterQueries) && !print; ++i) {
+        //in case of filter try to match with literal filter string
+        celix_filter_t *filter = celix_arrayList_get(opts->filterQueries, i);
+        const char *f = celix_filter_getFilterString(filter);
+        print = strstr(entry->filter, f) != NULL;
+    }
+    return print;
+}
+
+/**
+ * print bundle header (only for first time)
+ */
+static void queryCommand_printBundleHeader(FILE *sout, const celix_bundle_t *bnd, bool *called) {
+    if (called != NULL && !(*called)) {
+        fprintf(sout, "Bundle %li [%s]:\n", celix_bundle_getId(bnd), celix_bundle_getSymbolicName(bnd));
+        *called = true;
+    }
+}
+
+static void queryCommand_callback(void *handle, const celix_bundle_t *bnd) {
+    struct bundle_callback_data *data = handle;
+    bool printBundleCalled = false;
+    if (data->opts->queryProvided) {
+        celix_array_list_t *services = celix_bundle_listRegisteredServices(bnd);
+        for (int i = 0; i < celix_arrayList_size(services); ++i) {
+            celix_bundle_service_list_entry_t *entry = celix_arrayList_get(services, i);
+            if (queryCommand_printProvidedService(data->opts, entry)) {
+                queryCommand_printBundleHeader(data->sout, bnd, &printBundleCalled);
+                fprintf(data->sout, "|- Provided service '%s' [id = %li]\n", entry->serviceName, entry->serviceId); //TODO underline if color enabled
+                if (data->opts->verbose) {
+                    fprintf(data->sout, "   |- Is factory: %s\n", entry->factory ? "true" : "false");
+                    fprintf(data->sout, "   |- Properties:\n");
+                    const char *key;
+                    CELIX_PROPERTIES_FOR_EACH(entry->serviceProperties, key) {
+                        const char *val = celix_properties_get(entry->serviceProperties, key, "!ERROR!");
+                        fprintf(data->sout, "      |- %20s = %s\n", key, val);
+                    }
+                }
+            }
+        }
+        celix_bundle_destroyRegisteredServicesList(services);
+    }
+    if (data->opts->queryRequested) {
+        celix_array_list_t *trackers = celix_bundle_listServiceTrackers(bnd);
+        for (int i = 0; i < celix_arrayList_size(trackers); ++i) {
+            celix_bundle_service_tracker_list_entry_t *entry = celix_arrayList_get(trackers, i);
+            if (queryCommand_printRequestedService(data->opts, entry)) {
+                queryCommand_printBundleHeader(data->sout, bnd, &printBundleCalled);
+                fprintf(data->sout, "|- Service tracker '%s'\n", entry->filter);
+            }
+        }
+        celix_bundle_destroyServiceTrackerList(trackers);
+    }
+
+    if (printBundleCalled) {
+        fprintf(data->sout, "\n");
+    }
+}
+
+
+static void queryCommand_listServicesForBundle(celix_bundle_context_t *ctx, long bndId, const struct query_options *opts, FILE *sout, FILE *serr) {
+    struct bundle_callback_data data;
+    data.opts = opts;
+    data.sout = sout;
+    bool called = celix_bundleContext_useBundle(ctx, bndId, &data, queryCommand_callback);
+    if (!called) {
+        fprintf(serr, "Bundle %li not installed!", bndId);
+    }
+}
+
+static void queryCommand_listServices(celix_bundle_context_t *ctx, const struct query_options *opts, FILE *sout, FILE *serr) {
+    if (opts->bndId >= 0L) {
+        queryCommand_listServicesForBundle(ctx, opts->bndId, opts, sout, serr);
+    } else {
+        celix_array_list_t *bundleIds = celix_bundleContext_listBundles(ctx);
+        for (int i = 0; i < celix_arrayList_size(bundleIds); ++i) {
+            long bndId = celix_arrayList_getLong(bundleIds, i);
+            queryCommand_listServicesForBundle(ctx, bndId, opts, sout, serr);
+        }
+        celix_arrayList_destroy(bundleIds);
+    }
+}
+
+
+celix_status_t queryCommand_execute(void *_ptr, char *command_line_str, FILE *sout, FILE *serr __attribute__((unused))) {
+    celix_status_t status = CELIX_SUCCESS;
+    bundle_context_t* ctx = _ptr;
+
+    struct query_options opts;
+    memset(&opts, 0, sizeof(opts));
+    opts.bndId = -1L;
+    opts.queryProvided = true;
+    opts.queryRequested = true;
+
+    opts.nameQueries = celix_arrayList_create();
+    opts.filterQueries = celix_arrayList_create();
+
+    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;
+
+    if (!ctx || !command_line_str || !sout || !serr) {
+        status = CELIX_ILLEGAL_ARGUMENT;
+    }
+
+    bool validCommand = true;
+    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, "-v") == 0) {
+                opts.verbose = true;
+            } else if (strcmp(sub_str, "-p") == 0) {
+                opts.queryProvided = true;
+                opts.queryRequested = false;
+            } else if (strcmp(sub_str, "-r") == 0) {
+                opts.queryProvided = false;
+                opts.queryRequested = true;
+            } else {
+                //check if its a number (bundle id)
+                errno = 0;
+                long bndId = strtol(sub_str, NULL, 10);
+                if (bndId > 0 && errno == 0 /*not EINVAL*/) {
+                    opts.bndId = bndId;
+                } else {
+                    //not option and not a bundle id -> query
+                    if (strnlen(sub_str, 16) > 1 && sub_str[0] == '(') {
+                        //assume this is a filter.
+                        celix_filter_t *filter = celix_filter_create(sub_str);
+                        if (filter != NULL) {
+                            celix_arrayList_add(opts.filterQueries, filter);
+                        } else {
+                            validCommand = false;
+                            fprintf(serr, "Cannot parse provided filter '%s'!\n", sub_str);
+                            break;
+                        }
+                    } else {
+                        celix_arrayList_add(opts.nameQueries, celix_utils_strdup(sub_str));
+                    }
+
+                }
+            }
+            sub_str = strtok_r(NULL, OSGI_SHELL_COMMAND_SEPARATOR, &save_ptr);
+        }
+    }
+
+    if (validCommand) {
+        queryCommand_listServices(ctx, &opts, sout, serr);
+    }
+
+    for (int i = 0; i < arrayList_size(opts.nameQueries); ++i) {
+        char *name = celix_arrayList_get(opts.nameQueries, i);
+        free(name);
+    }
+    celix_arrayList_destroy(opts.nameQueries);
+
+    for (int i = 0; i < arrayList_size(opts.filterQueries); ++i) {
+        celix_filter_t *filter = celix_arrayList_get(opts.filterQueries, i);
+        celix_filter_destroy(filter);
+    }
+    celix_arrayList_destroy(opts.filterQueries);
+
+    return status;
+}
diff --git a/bundles/shell/shell_tui/private/src/activator.c b/bundles/shell/shell_tui/private/src/activator.c
index e2abd28..fb422a2 100644
--- a/bundles/shell/shell_tui/private/src/activator.c
+++ b/bundles/shell/shell_tui/private/src/activator.c
@@ -34,28 +34,12 @@
 
 typedef struct shell_tui_activator {
     shell_tui_t* shellTui;
-    service_tracker_pt tracker;
+    long trackerId;
     shell_service_t* currentSvc;
     bool useAnsiControlSequences;
 } shell_tui_activator_t;
 
 
-static celix_status_t activator_addShellService(void *handle, service_reference_pt ref, void *svc) {
-    shell_tui_activator_t* act = (shell_tui_activator_t*) handle;
-    act->currentSvc = svc;
-    shellTui_setShell(act->shellTui, svc);
-    return CELIX_SUCCESS;
-}
-
-static celix_status_t activator_removeShellService(void *handle, service_reference_pt ref, void *svc) {
-    shell_tui_activator_t* act = (shell_tui_activator_t*) handle;
-    if (act->currentSvc == svc) {
-        act->currentSvc = NULL;
-        shellTui_setShell(act->shellTui, NULL);
-    }
-    return CELIX_SUCCESS;
-}
-
 celix_status_t bundleActivator_create(bundle_context_pt context, void **userData) {
 	celix_status_t status = CELIX_SUCCESS;
 
@@ -68,6 +52,7 @@ celix_status_t bundleActivator_create(bundle_context_pt context, void **userData
     shell_tui_activator_t* activator = calloc(1, sizeof(*activator));
 
 	if (activator != NULL) {
+        activator->trackerId = -1L;
         bool useCommands;
         const char* config = NULL;
         bundleContext_getProperty(context, SHELL_USE_ANSI_CONTROL_SEQUENCES, &config);
@@ -80,9 +65,14 @@ celix_status_t bundleActivator_create(bundle_context_pt context, void **userData
 
         activator->shellTui = shellTui_create(useCommands);
 
-        service_tracker_customizer_t* cust = NULL;
-        serviceTrackerCustomizer_create(activator, NULL, activator_addShellService, NULL, activator_removeShellService, &cust);
-        serviceTracker_create(context, OSGI_SHELL_SERVICE_NAME, cust, &activator->tracker);
+        {
+            celix_service_tracking_options_t opts = CELIX_EMPTY_SERVICE_TRACKING_OPTIONS;
+            opts.filter.serviceName = OSGI_SHELL_SERVICE_NAME;
+            opts.callbackHandle = activator->shellTui;
+            opts.set = (void*)shellTui_setShell;
+
+            activator->trackerId = celix_bundleContext_trackServicesWithOptions(context, &opts);
+        }
 	}
 
     if (activator != NULL && activator->shellTui != NULL) {
@@ -90,9 +80,8 @@ celix_status_t bundleActivator_create(bundle_context_pt context, void **userData
     } else {
         if (activator != NULL) {
             shellTui_destroy(activator->shellTui);
-            if (activator->tracker != NULL) {
-                serviceTracker_destroy(activator->tracker);
-            }
+            celix_bundleContext_stopTracker(context, activator->trackerId);
+            activator->trackerId = -1L;
         }
         free(activator);
 		status = CELIX_ENOMEM;
@@ -106,8 +95,6 @@ celix_status_t bundleActivator_start(void * userData, bundle_context_pt context)
     shell_tui_activator_t* act = (shell_tui_activator_t*) userData;
 
     if (act != NULL) {
-        act->currentSvc = NULL;
-        serviceTracker_open(act->tracker);
         shellTui_start(act->shellTui);
     }
 
@@ -119,7 +106,7 @@ celix_status_t bundleActivator_stop(void * userData, bundle_context_pt context)
     shell_tui_activator_t* act = (shell_tui_activator_t*) userData;
 
     if (act != NULL) {
-        serviceTracker_close(act->tracker);
+        celix_bundleContext_stopTracker(context, act->trackerId);
         shellTui_stop(act->shellTui);
     }
 
@@ -131,7 +118,6 @@ celix_status_t bundleActivator_destroy(void * userData, bundle_context_pt contex
 
     if (act != NULL) {
         shellTui_destroy(act->shellTui);
-        serviceTracker_destroy(act->tracker);
         free(act);
     }
 
diff --git a/libs/framework/include/celix_bundle.h b/libs/framework/include/celix_bundle.h
index 0b0dac4..c6a2ab0 100644
--- a/libs/framework/include/celix_bundle.h
+++ b/libs/framework/include/celix_bundle.h
@@ -94,7 +94,8 @@ 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;;
+    char *filter;
+    char *serviceName;
     long bundleOwner;
 } celix_bundle_service_tracker_list_entry_t;
 
diff --git a/libs/framework/src/bundle.c b/libs/framework/src/bundle.c
index 89ac70d..9b1d4bf 100644
--- a/libs/framework/src/bundle.c
+++ b/libs/framework/src/bundle.c
@@ -20,6 +20,8 @@
 #include <stdlib.h>
 #include <string.h>
 #include <service_tracker.h>
+#include <celix_constants.h>
+#include <celix_api.h>
 
 #include "framework_private.h"
 #include "bundle_private.h"
@@ -403,7 +405,7 @@ celix_status_t bundle_addModule(bundle_pt bundle, module_pt module) {
 		const char *sn = NULL;
 		module_getSymbolicName(module, &sn);
 		if (sn != NULL) {
-            bundle->symbolicName = strndup(sn, 1024 * 1024);
+            bundle->symbolicName = celix_utils_strdup(sn);
         }
 	}
 
@@ -678,9 +680,23 @@ celix_array_list_t* celix_bundle_listServiceTrackers(const celix_bundle_t *bnd)
     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->filter = celix_utils_strdup(tracker->filter);
+        celix_filter_t *f = celix_filter_create(entry->filter);
+        if (f != NULL) {
+            const char *sn = celix_filter_findAttribute(f, OSGI_FRAMEWORK_OBJECTCLASS);
+            if (sn != NULL) {
+                entry->serviceName = celix_utils_strdup(sn);
+            }
+        }
         entry->bundleOwner = celix_bundle_getId(bnd);
-        celix_arrayList_add(result, entry);
+
+        if (entry->serviceName != NULL) {
+            celix_arrayList_add(result, entry);
+        } else {
+            framework_logIfError(logger, CELIX_BUNDLE_EXCEPTION, NULL, "Failed to get service name from tracker. filter is %s", entry->filter);
+            free(entry->filter);
+            free(entry);
+        }
     }
     celixThreadMutex_unlock(&bnd->context->mutex);
     return result;
@@ -692,6 +708,7 @@ void celix_bundle_destroyServiceTrackerList(celix_array_list_t* list) {
         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->serviceName);
             free(entry);
         }
     }
diff --git a/libs/framework/src/service_registration.c b/libs/framework/src/service_registration.c
index 49f60c8..3400134 100644
--- a/libs/framework/src/service_registration.c
+++ b/libs/framework/src/service_registration.c
@@ -311,7 +311,7 @@ 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;
+        isFactory = registration->svcType == CELIX_FACTORY_SERVICE || registration->svcType == CELIX_DEPRECATED_FACTORY_SERVICE;
         celixThreadRwlock_unlock(&registration->lock);
     }
     return isFactory;
diff --git a/libs/framework/src/service_registry.c b/libs/framework/src/service_registry.c
index bf2a68b..dc38920 100644
--- a/libs/framework/src/service_registry.c
+++ b/libs/framework/src/service_registry.c
@@ -21,6 +21,7 @@
 #include <stdio.h>
 #include <string.h>
 #include <assert.h>
+#include <celix_api.h>
 
 #include "service_registry_private.h"
 #include "service_registration_private.h"
@@ -954,7 +955,7 @@ bool celix_serviceRegistry_getServiceInfo(
                 if (outServiceName != NULL) {
                     const char *s = NULL;
                     serviceRegistration_getServiceName(reg, &s);
-                    *outServiceName = strndup(s, 1024 * 1024 * 10);
+                    *outServiceName = celix_utils_strdup(s);
                 }
                 if (outServiceProperties != NULL) {
                     celix_properties_t *p = NULL;
diff --git a/libs/utils/include/celix_utils_api.h b/libs/utils/include/celix_utils.h
similarity index 63%
copy from libs/utils/include/celix_utils_api.h
copy to libs/utils/include/celix_utils.h
index 0d9b576..1b96cdf 100644
--- a/libs/utils/include/celix_utils_api.h
+++ b/libs/utils/include/celix_utils.h
@@ -17,23 +17,23 @@
  * under the License.
  */
 
-#ifndef CELIX_CELIX_UTILS_API_H_
-#define CELIX_CELIX_UTILS_API_H_
+#ifndef CELIX_UTILS_H_
+#define CELIX_UTILS_H_
 
-#include "celix_errno.h"
-#include "celixbool.h"
-#include "celix_threads.h"
-#include "array_list.h"
-#include "hash_map.h"
-#include "properties.h"
-#include "utils.h"
-#include "version.h"
-#include "version_range.h"
-#include "thpool.h"
-
-#if defined(BSD) || defined(__APPLE__) || defined(__ANDROID__)
-#include "memstream/open_memstream.h"
-#include "memstream/fmemopen.h"
+#ifdef __cplusplus
+extern "C" {
 #endif
 
-#endif //CELIX_CELIX_UTILS_API_H_
+#define CELIX_UTILS_MAX_STRLEN      1024*1024*10
+
+/**
+ * Creates a copy of a provided string.
+ * The strdup is limited to the CELIX_UTILS_MAX_STRLEN and uses strndup to achieve this.
+ * @return a copy of the string (including null terminator).
+ */
+char* celix_utils_strdup(const char *str);
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* CELIX_UTILS_H_ */
diff --git a/libs/utils/include/celix_utils_api.h b/libs/utils/include/celix_utils_api.h
index 0d9b576..23192a2 100644
--- a/libs/utils/include/celix_utils_api.h
+++ b/libs/utils/include/celix_utils_api.h
@@ -27,6 +27,7 @@
 #include "hash_map.h"
 #include "properties.h"
 #include "utils.h"
+#include "celix_utils.h"
 #include "version.h"
 #include "version_range.h"
 #include "thpool.h"
diff --git a/libs/utils/src/filter.c b/libs/utils/src/filter.c
index 45564c6..c07b54e 100644
--- a/libs/utils/src/filter.c
+++ b/libs/utils/src/filter.c
@@ -59,7 +59,7 @@ static celix_filter_t * filter_parseFilter(char * filterString, int * pos) {
     celix_filter_t * filter;
     filter_skipWhiteSpace(filterString, pos);
     if (filterString[*pos] != '(') {
-        fprintf(stderr, "Filter Error: Missing '(' in filter string '%s'.", filterString);
+        fprintf(stderr, "Filter Error: Missing '(' in filter string '%s'.\n", filterString);
         return NULL;
     }
     (*pos)++;
@@ -69,7 +69,7 @@ static celix_filter_t * filter_parseFilter(char * filterString, int * pos) {
     filter_skipWhiteSpace(filterString, pos);
 
     if (filterString[*pos] != ')') {
-        fprintf(stderr, "Filter Error: Missing ')' in filter string '%s'.", filterString);
+        fprintf(stderr, "Filter Error: Missing ')' in filter string '%s'.\n", filterString);
         if(filter!=NULL){
             filter_destroy(filter);
         }
@@ -110,7 +110,7 @@ static celix_filter_t * filter_parseAndOrOr(char * filterString, celix_filter_op
     bool failure = false;
 
     if (filterString[*pos] != '(') {
-        fprintf(stderr, "Filter Error: Missing '('.");
+        fprintf(stderr, "Filter Error: Missing '('.\n");
         return NULL;
     }
 
@@ -146,7 +146,7 @@ static celix_filter_t * filter_parseNot(char * filterString, int * pos) {
     filter_skipWhiteSpace(filterString, pos);
 
     if (filterString[*pos] != '(') {
-        fprintf(stderr, "Filter Error: Missing '('.");
+        fprintf(stderr, "Filter Error: Missing '('.\n");
         return NULL;
     }
 
@@ -259,7 +259,7 @@ static celix_filter_t * filter_parseItem(char * filterString, int * pos) {
             return filter;
         }
     }
-    fprintf(stderr, "Filter Error: Invalid operator.");
+    fprintf(stderr, "Filter Error: Invalid operator.\n");
     free(attr);
     return NULL;
 }
@@ -286,7 +286,7 @@ static char * filter_parseAttr(char * filterString, int * pos) {
     length = end - begin;
 
     if (length == 0) {
-        fprintf(stderr, "Filter Error: Missing attr.");
+        fprintf(stderr, "Filter Error: Missing attr.\n");
         return NULL;
     } else {
         char * attr = (char *) calloc(1, length+1);
@@ -309,12 +309,12 @@ static char * filter_parseValue(char * filterString, int * pos) {
                 break;
             }
             case '(': {
-                fprintf(stderr, "Filter Error: Invalid value.");
+                fprintf(stderr, "Filter Error: Invalid value.\n");
                 free(value);
                 return NULL;
             }
             case '\0':{
-                fprintf(stderr, "Filter Error: Unclosed bracket.");
+                fprintf(stderr, "Filter Error: Unclosed bracket.\n");
                 free(value);
                 return NULL;
             }
@@ -335,7 +335,7 @@ static char * filter_parseValue(char * filterString, int * pos) {
     }
 
     if (strlen(value) == 0) {
-        fprintf(stderr, "Filter Error: Missing value.");
+        fprintf(stderr, "Filter Error: Missing value.\n");
         free(value);
         return NULL;
     }
@@ -360,12 +360,12 @@ static celix_array_list_t* filter_parseSubstring(char * filterString, int * pos)
                 break;
             }
             case '\0':{
-                fprintf(stderr, "Filter Error: Unclosed bracket.");
+                fprintf(stderr, "Filter Error: Unclosed bracket.\n");
                 keepRunning = false;
                 break;
             }
             case '(': {
-                fprintf(stderr, "Filter Error: Invalid value.");
+                fprintf(stderr, "Filter Error: Invalid value.\n");
                 keepRunning = false;
                 break;
             }
@@ -396,7 +396,7 @@ static celix_array_list_t* filter_parseSubstring(char * filterString, int * pos)
     free(sub);
 
     if (celix_arrayList_size(operands) == 0) {
-        fprintf(stderr, "Filter Error: Missing value.");
+        fprintf(stderr, "Filter Error: Missing value.\n");
         celix_arrayList_destroy(operands);
         return NULL;
     }
@@ -534,7 +534,7 @@ celix_filter_t* celix_filter_create(const char *filterString) {
     int pos = 0;
     filter = filter_parseFilter(filterStr, &pos);
     if (filter != NULL && pos != strlen(filterStr)) {
-        fprintf(stderr, "Filter Error: Extraneous trailing characters.");
+        fprintf(stderr, "Filter Error: Extraneous trailing characters.\n");
         filter_destroy(filter);
         filter = NULL;
     } else if (filter != NULL) {
@@ -579,7 +579,7 @@ void celix_filter_destroy(celix_filter_t *filter) {
                 celix_arrayList_destroy(filter->children);
                 filter->children = NULL;
             } else {
-                fprintf(stderr, "Filter Error: Corrupt filter. children has a value, but not an expected operand");
+                fprintf(stderr, "Filter Error: Corrupt filter. children has a value, but not an expected operand\n");
             }
         }
         free((char*)filter->value);
diff --git a/libs/utils/src/utils.c b/libs/utils/src/utils.c
index fdd653b..e848422 100644
--- a/libs/utils/src/utils.c
+++ b/libs/utils/src/utils.c
@@ -16,18 +16,13 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-/**
- * utils.c
- *
- *  \date       Jul 27, 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 "utils.h"
+#include "celix_utils.h"
 
 unsigned int utils_stringHash(const void* strPtr) {
     const char* string = strPtr;
@@ -146,3 +141,7 @@ double celix_difftime(const struct timespec *tBegin, const struct timespec *tEnd
     float diff_ns = tEnd->tv_nsec - tBegin->tv_nsec;
     return diff_s + (diff_ns / 1000000000.0);
 }
+
+char* celix_utils_strdup(const char *str) {
+    return strndup(str, CELIX_UTILS_MAX_STRLEN);
+}
\ No newline at end of file