| /** |
| *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. |
| */ |
| /* |
| * remote_shell.c |
| * |
| * \date Nov 4, 2012 |
| * \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 <unistd.h> |
| #include <utils.h> |
| #include <array_list.h> |
| #include <sys/socket.h> |
| |
| #include "log_helper.h" |
| |
| #include "log_service.h" |
| #include "remote_shell.h" |
| |
| #define COMMAND_BUFF_SIZE (256) |
| |
| #define RS_PROMPT ("-> ") |
| #define RS_WELCOME ("\n---- Apache Celix Remote Shell ----\n---- Type exit to disconnect ----\n\n-> ") |
| #define RS_GOODBYE ("Goobye!\n") |
| #define RS_ERROR ("Error executing command!\n") |
| #define RS_MAXIMUM_CONNECTIONS_REACHED ("Maximum number of connections reached. Disconnecting ...\n") |
| |
| #define CONNECTION_LISTENER_TIMEOUT_SEC 5 |
| |
| |
| |
| struct connection { |
| remote_shell_pt parent; |
| FILE *socketStream; |
| fd_set pollset; |
| bool threadRunning; |
| }; |
| |
| typedef struct connection *connection_pt; |
| |
| static celix_status_t remoteShell_connection_print(connection_pt connection, char * text); |
| static celix_status_t remoteShell_connection_execute(connection_pt connection, char *command); |
| static void* remoteShell_connection_run(void *data); |
| |
| celix_status_t remoteShell_create(shell_mediator_pt mediator, int maximumConnections, remote_shell_pt *instance) { |
| celix_status_t status = CELIX_SUCCESS; |
| (*instance) = calloc(1, sizeof(**instance)); |
| if ((*instance) != NULL) { |
| (*instance)->mediator = mediator; |
| (*instance)->maximumConnections = maximumConnections; |
| (*instance)->connections = NULL; |
| (*instance)->loghelper = &mediator->loghelper; |
| |
| status = celixThreadMutex_create(&(*instance)->mutex, NULL); |
| |
| if (status == CELIX_SUCCESS) { |
| status = arrayList_create(&(*instance)->connections); |
| } |
| } else { |
| status = CELIX_ENOMEM; |
| } |
| return status; |
| } |
| |
| celix_status_t remoteShell_destroy(remote_shell_pt instance) { |
| celix_status_t status = CELIX_SUCCESS; |
| |
| remoteShell_stopConnections(instance); |
| |
| celixThreadMutex_lock(&instance->mutex); |
| arrayList_destroy(instance->connections); |
| celixThreadMutex_unlock(&instance->mutex); |
| |
| return status; |
| } |
| |
| celix_status_t remoteShell_addConnection(remote_shell_pt instance, int socket) { |
| celix_status_t status = CELIX_SUCCESS; |
| connection_pt connection = calloc(1, sizeof(struct connection)); |
| |
| if (connection != NULL) { |
| connection->parent = instance; |
| connection->threadRunning = false; |
| connection->socketStream = fdopen(socket, "w"); |
| |
| if (connection->socketStream != NULL) { |
| |
| celixThreadMutex_lock(&instance->mutex); |
| |
| if (arrayList_size(instance->connections) < instance->maximumConnections) { |
| celix_thread_t connectionRunThread = celix_thread_default; |
| arrayList_add(instance->connections, connection); |
| status = celixThread_create(&connectionRunThread, NULL, &remoteShell_connection_run, connection); |
| } else { |
| status = CELIX_BUNDLE_EXCEPTION; |
| remoteShell_connection_print(connection, RS_MAXIMUM_CONNECTIONS_REACHED); |
| } |
| celixThreadMutex_unlock(&instance->mutex); |
| |
| } else { |
| status = CELIX_BUNDLE_EXCEPTION; |
| } |
| } else { |
| status = CELIX_ENOMEM; |
| } |
| |
| if (status != CELIX_SUCCESS) { |
| if (connection->socketStream != NULL) { |
| fclose(connection->socketStream); |
| } |
| free(connection); |
| } |
| |
| return status; |
| } |
| |
| celix_status_t remoteShell_stopConnections(remote_shell_pt instance) { |
| celix_status_t status = CELIX_SUCCESS; |
| int length = 0; |
| int i = 0; |
| |
| celixThreadMutex_lock(&instance->mutex); |
| length = arrayList_size(instance->connections); |
| |
| for (i = 0; i < length; i += 1) { |
| connection_pt connection = arrayList_get(instance->connections, i); |
| connection->threadRunning = false; |
| } |
| |
| celixThreadMutex_unlock(&instance->mutex); |
| |
| return status; |
| } |
| |
| void *remoteShell_connection_run(void *data) { |
| celix_status_t status = CELIX_SUCCESS; |
| connection_pt connection = data; |
| size_t len; |
| int result; |
| struct timeval timeout; /* Timeout for select */ |
| |
| int fd = fileno(connection->socketStream); |
| |
| connection->threadRunning = true; |
| status = remoteShell_connection_print(connection, RS_WELCOME); |
| |
| while (status == CELIX_SUCCESS && connection->threadRunning == true) { |
| do { |
| timeout.tv_sec = CONNECTION_LISTENER_TIMEOUT_SEC; |
| timeout.tv_usec = 0; |
| |
| FD_ZERO(&connection->pollset); |
| FD_SET(fd, &connection->pollset); |
| result = select(fd + 1, &connection->pollset, NULL, NULL, &timeout); |
| } while (result == -1 && errno == EINTR && connection->threadRunning == true); |
| |
| /* The socket_fd has data available to be read */ |
| if (result > 0 && FD_ISSET(fd, &connection->pollset)) { |
| char buff[COMMAND_BUFF_SIZE]; |
| |
| len = recv(fd, buff, COMMAND_BUFF_SIZE - 1, 0); |
| if (len < COMMAND_BUFF_SIZE) { |
| celix_status_t commandStatus = CELIX_SUCCESS; |
| buff[len] = '\0'; |
| |
| commandStatus = remoteShell_connection_execute(connection, buff); |
| |
| if (commandStatus == CELIX_SUCCESS) { |
| remoteShell_connection_print(connection, RS_PROMPT); |
| } else if (commandStatus == CELIX_FILE_IO_EXCEPTION) { |
| //exit command |
| break; |
| } else { //error |
| remoteShell_connection_print(connection, RS_ERROR); |
| remoteShell_connection_print(connection, RS_PROMPT); |
| } |
| |
| } else { |
| logHelper_log(*connection->parent->loghelper, OSGI_LOGSERVICE_ERROR, "REMOTE_SHELL: Error while retrieving data"); |
| } |
| } |
| } |
| |
| remoteShell_connection_print(connection, RS_GOODBYE); |
| |
| logHelper_log(*connection->parent->loghelper, OSGI_LOGSERVICE_INFO, "REMOTE_SHELL: Closing socket"); |
| celixThreadMutex_lock(&connection->parent->mutex); |
| arrayList_removeElement(connection->parent->connections, connection); |
| celixThreadMutex_unlock(&connection->parent->mutex); |
| |
| fclose(connection->socketStream); |
| |
| return NULL; |
| } |
| |
| static celix_status_t remoteShell_connection_execute(connection_pt connection, char *command) { |
| celix_status_t status = CELIX_SUCCESS; |
| |
| if (status == CELIX_SUCCESS) { |
| char *dline = strdup(command); |
| char *line = utils_stringTrim(dline); |
| int len = strlen(line); |
| |
| if (len == 0) { |
| //ignore |
| } else if (len == 4 && strncmp("exit", line, 4) == 0) { |
| status = CELIX_FILE_IO_EXCEPTION; |
| } else { |
| status = shellMediator_executeCommand(connection->parent->mediator, line, connection->socketStream, connection->socketStream); |
| fflush(connection->socketStream); |
| } |
| |
| free(dline); |
| } |
| |
| return status; |
| } |
| |
| celix_status_t remoteShell_connection_print(connection_pt connection, char *text) { |
| size_t len = strlen(text); |
| int fd = fileno(connection->socketStream); |
| return (send(fd, text, len, 0) > 0) ? CELIX_SUCCESS : CELIX_FILE_IO_EXCEPTION; |
| } |