/** *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 Apache Celix Project Team * \copyright Apache License, Version 2.0 */ #include #include #include #include #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; int socket; 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->socket = socket; connection->threadRunning = false; 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_ENOMEM; } if (status != CELIX_SUCCESS) { close(connection->socket); 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 */ 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(connection->socket, &connection->pollset); result = select(connection->socket + 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(connection->socket, &connection->pollset)) { char buff[COMMAND_BUFF_SIZE]; len = recv(connection->socket, 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); close(connection->socket); 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->socket); } free(dline); } return status; } celix_status_t remoteShell_connection_print(connection_pt connection, char *text) { size_t len = strlen(text); return (send(connection->socket, text, len, 0) > 0) ? CELIX_SUCCESS : CELIX_FILE_IO_EXCEPTION; }