Author: pnoltes Date: Wed Oct 22 09:07:10 2014 New Revision: 1633566 URL: http://svn.apache.org/r1633566 Log: CELIX-172: Initial commit for the bonjour shell.
Added: celix/trunk/shell_bonjour/ celix/trunk/shell_bonjour/CMakeLists.txt celix/trunk/shell_bonjour/deploy.cmake celix/trunk/shell_bonjour/private/ celix/trunk/shell_bonjour/private/include/ celix/trunk/shell_bonjour/private/include/bonjour_shell.h celix/trunk/shell_bonjour/private/src/ celix/trunk/shell_bonjour/private/src/activator.c celix/trunk/shell_bonjour/private/src/bonjour_shell.c Modified: celix/trunk/CMakeLists.txt Modified: celix/trunk/CMakeLists.txt URL: http://svn.apache.org/viewvc/celix/trunk/CMakeLists.txt?rev=1633566&r1=1633565&r2=1633566&view=diff ============================================================================== --- celix/trunk/CMakeLists.txt (original) +++ celix/trunk/CMakeLists.txt Wed Oct 22 09:07:10 2014 @@ -58,6 +58,7 @@ add_subdirectory(device_access) add_subdirectory(deployment_admin) add_subdirectory(remote_services) add_subdirectory(remote_shell) +add_subdirectory(shell_bonjour) add_subdirectory(shell_tui) add_subdirectory(shell) add_subdirectory(log_writer) Added: celix/trunk/shell_bonjour/CMakeLists.txt URL: http://svn.apache.org/viewvc/celix/trunk/shell_bonjour/CMakeLists.txt?rev=1633566&view=auto ============================================================================== --- celix/trunk/shell_bonjour/CMakeLists.txt (added) +++ celix/trunk/shell_bonjour/CMakeLists.txt Wed Oct 22 09:07:10 2014 @@ -0,0 +1,44 @@ +# 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) + + set(BUNDLE_SYMBOLICNAME "bonjour_shell") + set(BUNDLE_VERSION "0.1.0") + set(BUNDLE_NAME "bonjour_shell") + + include_directories("${PROJECT_SOURCE_DIR}/utils/public/include") + include_directories("${PROJECT_SOURCE_DIR}/shell/public/include") + include_directories("${LIBXML2_INCLUDE_DIR}") + include_directories("private/include") + + bundle(bonjour_shell + SOURCES + private/src/activator.c + private/src/bonjour_shell.c + ) + + target_link_libraries(bonjour_shell ${CELIX_LIBRARIES} ${APR_LIBRARIES} ${LIBXML2_LIBRARIES} ${DNS_SD_LIB}) + +endif (SHELL_BONJOUR) + Added: celix/trunk/shell_bonjour/deploy.cmake URL: http://svn.apache.org/viewvc/celix/trunk/shell_bonjour/deploy.cmake?rev=1633566&view=auto ============================================================================== --- celix/trunk/shell_bonjour/deploy.cmake (added) +++ celix/trunk/shell_bonjour/deploy.cmake Wed Oct 22 09:07:10 2014 @@ -0,0 +1,25 @@ +# 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. + +is_enabled(SHELL_BONJOUR) +if (SHELL_BONJOUR) + deploy("bonjour_shell" BUNDLES + shell + bonjour_shell + PROPERTIES "bonjour.shell.id=Apache Celix" + ) +endif (SHELL_BONJOUR) Added: celix/trunk/shell_bonjour/private/include/bonjour_shell.h URL: http://svn.apache.org/viewvc/celix/trunk/shell_bonjour/private/include/bonjour_shell.h?rev=1633566&view=auto ============================================================================== --- celix/trunk/shell_bonjour/private/include/bonjour_shell.h (added) +++ celix/trunk/shell_bonjour/private/include/bonjour_shell.h Wed Oct 22 09:07:10 2014 @@ -0,0 +1,45 @@ +/** + *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:celix-...@incubator.apache.org">Apache Celix Project Team</a> + * \copyright Apache License, Version 2.0 + */ + +#ifndef BONJOUR_SHELL_H_ +#define BONJOUR_SHELL_H_ + +#include <apr_pools.h> + +#include <service_reference.h> + +#include "celix_errno.h" + +typedef struct bonjour_shell *bonjour_shell_pt; + +celix_status_t bonjourShell_create(apr_pool_t *pool, 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_ */ Added: celix/trunk/shell_bonjour/private/src/activator.c URL: http://svn.apache.org/viewvc/celix/trunk/shell_bonjour/private/src/activator.c?rev=1633566&view=auto ============================================================================== --- celix/trunk/shell_bonjour/private/src/activator.c (added) +++ celix/trunk/shell_bonjour/private/src/activator.c Wed Oct 22 09:07:10 2014 @@ -0,0 +1,119 @@ +/** + *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:celix-...@incubator.apache.org">Apache Celix Project Team</a> + * \copyright Apache License, Version 2.0 + */ + +#include <celix_errno.h> + +#include <stdlib.h> +#include <apr_strings.h> +#include <apr_pools.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 { + apr_pool_t *pool; + 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; + apr_pool_t *ctxpool; + apr_pool_t *pool; + + status = bundleContext_getMemoryPool(context, &ctxpool); + apr_pool_create(&pool, ctxpool); + if (status == CELIX_SUCCESS) { + bundle_instance_pt bi = (bundle_instance_pt) apr_palloc(pool, sizeof(struct bundle_instance)); + if (userData != NULL) { + bi->pool = pool; + 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; + + char *uuid = NULL; + bundleContext_getProperty(context, OSGI_FRAMEWORK_FRAMEWORK_UUID, &uuid); + char *hostname = NULL; + bundleContext_getProperty(context, "HOSTNAME", &hostname); + char *bonjourShellId = NULL; + bundleContext_getProperty(context, "bonjour.shell.id", &bonjourShellId); + + char id[128]; + if (bonjourShellId != NULL) { + snprintf(id, 128, bonjourShellId); + } else if (hostname != NULL) { + snprintf(id, 128, "Celix-%.8s@%s", uuid, hostname); + } else { + snprintf(id, 128, "Celix-%.8s", uuid); + } + status = bonjourShell_create(bi->pool, 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; +} Added: celix/trunk/shell_bonjour/private/src/bonjour_shell.c URL: http://svn.apache.org/viewvc/celix/trunk/shell_bonjour/private/src/bonjour_shell.c?rev=1633566&view=auto ============================================================================== --- celix/trunk/shell_bonjour/private/src/bonjour_shell.c (added) +++ celix/trunk/shell_bonjour/private/src/bonjour_shell.c Wed Oct 22 09:07:10 2014 @@ -0,0 +1,420 @@ +/** + *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:celix-...@incubator.apache.org">Apache Celix Project Team</a> + * \copyright Apache License, Version 2.0 + */ + +#include "bonjour_shell.h" + +#include <stdint.h> +#include <stdio.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> + +#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(char *buff); +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(apr_pool_t *parentPool, char *id, bonjour_shell_pt *result) { + celix_status_t status = CELIX_SUCCESS; + bonjour_shell_pt shell = (bonjour_shell_pt) malloc(sizeof(struct bonjour_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); + } + } + + +} + +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) { + shell->service->executeCommand(shell->service->shell, (char *)command, bonjourShell_addDataToCurrentContext, bonjourShell_addDataToCurrentContext); + } + pthread_mutex_unlock(&shell->mutex); + } + + if (command != NULL) { + xmlFree(command); + } +} + +static void bonjourShell_addDataToCurrentContext(char *buff) { + pthread_mutex_lock(¤tContext->mutex); + arrayList_add(currentContext->dataList, strdup(buff)); + gettimeofday(¤tContext->lastUpdated, NULL); + pthread_mutex_unlock(¤tContext->mutex); + pthread_cond_signal(¤tContext->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); + 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; +}