From bac3da61644515f05663789b122554dc77549286 Mon Sep 17 00:00:00 2001 From: Luca Muscariello Date: Thu, 17 Jan 2019 13:47:57 +0100 Subject: This is the first commit of the hicn project Change-Id: I6f2544ad9b9f8891c88cc4bcce3cf19bd3cc863f Signed-off-by: Luca Muscariello --- hicn-light/src/command_line/CMakeLists.txt | 2 + .../src/command_line/controller/CMakeLists.txt | 23 ++ .../controller/hicnLightControl_main.c | 284 +++++++++++++++++ hicn-light/src/command_line/daemon/CMakeLists.txt | 23 ++ .../src/command_line/daemon/hicnLightDaemon_main.c | 336 +++++++++++++++++++++ 5 files changed, 668 insertions(+) create mode 100755 hicn-light/src/command_line/CMakeLists.txt create mode 100755 hicn-light/src/command_line/controller/CMakeLists.txt create mode 100755 hicn-light/src/command_line/controller/hicnLightControl_main.c create mode 100755 hicn-light/src/command_line/daemon/CMakeLists.txt create mode 100755 hicn-light/src/command_line/daemon/hicnLightDaemon_main.c (limited to 'hicn-light/src/command_line') diff --git a/hicn-light/src/command_line/CMakeLists.txt b/hicn-light/src/command_line/CMakeLists.txt new file mode 100755 index 000000000..16c23dc5c --- /dev/null +++ b/hicn-light/src/command_line/CMakeLists.txt @@ -0,0 +1,2 @@ +add_subdirectory(controller) +add_subdirectory(daemon) diff --git a/hicn-light/src/command_line/controller/CMakeLists.txt b/hicn-light/src/command_line/controller/CMakeLists.txt new file mode 100755 index 000000000..b53e610a1 --- /dev/null +++ b/hicn-light/src/command_line/controller/CMakeLists.txt @@ -0,0 +1,23 @@ +# Copyright (c) 2017-2019 Cisco and/or its affiliates. +# Licensed 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. + +list(APPEND CONTROLLER_SRC + hicnLightControl_main.c +) + +build_executable(${HICN_LIGHT_CONTROL} + SOURCES ${CONTROLLER_SRC} + LINK_LIBRARIES ${HICN_LIGHT_LINK_LIBRARIES} + DEPENDS hicn-light + COMPONENT hicn-light +) diff --git a/hicn-light/src/command_line/controller/hicnLightControl_main.c b/hicn-light/src/command_line/controller/hicnLightControl_main.c new file mode 100755 index 000000000..4641bddf5 --- /dev/null +++ b/hicn-light/src/command_line/controller/hicnLightControl_main.c @@ -0,0 +1,284 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed 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 + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include + +size_t commandOutputLen = 0; // preserve the number of structs composing + // payload in case on not interactive call. + +// REMINDER: when a new_command is added, the following array has to be updated +// with the sizeof(new_command). It allows to allocate the buffer for receiving +// the payload of the DAEMON RESPONSE after the header has beed read. Each +// command identifier (typedef enum command_id) corresponds to a position in the +// following array. +static int payloadLengthController[LAST_COMMAND_VALUE] = { + sizeof(add_listener_command), + sizeof(add_connection_command), + sizeof(list_connections_command), // needed when get response from FWD + sizeof(add_route_command), + sizeof(list_routes_command), // needed when get response from FWD + sizeof(remove_connection_command), + sizeof(remove_route_command), + sizeof(cache_store_command), + sizeof(cache_serve_command), + 0, // cache clear + sizeof(set_strategy_command), + sizeof(set_wldr_command), + sizeof(add_punting_command), + sizeof(list_listeners_command), // needed when get response from FWD + sizeof(mapme_activator_command), + sizeof(mapme_activator_command), + sizeof(mapme_timing_command), + sizeof(mapme_timing_command)}; + +#include +#include +#include +#include + +typedef struct controller_main_state { + ControlState *controlState; +} ControlMainState; + +static void _displayForwarderLogo(void){ + const char cli_banner [] = + "\033[0;31m ____ ___ _ \033[0m __ _ __ _ __ __\n" + "\033[0;31m / __// _ \\ (_)___ \033[0m / / (_)____ ___ ____/ /(_)___ _ / / / /_\n" + "\033[0;31m / _/ / // /_ / // _ \\ \033[0m / _ \\ / // __// _ \\___/ // // _ `// _ \\/ __/\n" + "\033[0;31m/_/ /____/(_)/_/ \\___/ \033[0m/_//_//_/ \\__//_//_/ /_//_/ \\_, //_//_/\\__/\n" + " /___/ \n"; + printf("%s", cli_banner); + printf("\n"); +} + +static void _displayUsage(char *programName) { + printf("Usage: %s -h\n", programName); + printf( + "hicn-light is the 1.0 source, which runs on each end system and as a " + "software source\n"); + printf( + "on intermediate systems. controller is the program to configure the " + "source, daemon.\n"); + printf("\n"); + printf("Options:\n"); + printf("-h = This help screen\n"); + printf( + "commands = configuration line to send to hicn-light (use 'help' " + "for list)\n"); + printf("\n"); +} + +static int _parseArgs(int argc, char *argv[], char **keystorePath, + char **keystorePassword, PARCList *commandList) { + static struct option longFormOptions[] = { + {"help", no_argument, 0, 'h'}, + {"keystore", required_argument, 0, 'k'}, + {"password", required_argument, 0, 'p'}, + {0, 0, 0, 0}}; + + int c; + + while (1) { + // getopt_long stores the option index here. + int optionIndex = 0; + + c = getopt_long(argc, argv, "hk:p:", longFormOptions, &optionIndex); + + // Detect the end of the options. + if (c == -1) { + break; + } + + switch (c) { + case 'k': + *keystorePath = optarg; + break; + + case 'p': + *keystorePassword = optarg; + break; + + case 'h': + default: + _displayUsage(argv[0]); + return 0; + } + } + + // Any remaining parameters get put in the command list. + if (optind < argc) { + while (optind < argc) { + parcList_Add(commandList, argv[optind]); + optind++; + } + } + + return 1; +} + +struct iovec *_writeAndReadMessage(ControlState *state, struct iovec *msg) { + parcAssertNotNull(msg, "Parameter msg must be non-null"); + int sockfd = controlState_GetSockfd(state); + + // check if request has a payload + if (((header_control_message *)msg[0].iov_base)->length > + 0) { // command with payload + // write header + payload (compatibility issue: two write needed instead of + // the writev) + if (write(sockfd, msg[0].iov_base, msg[0].iov_len) < 0 || + write(sockfd, msg[1].iov_base, msg[1].iov_len) < 0) { + printf("\nError while sending the Message: cannot write on socket \n"); + exit(EXIT_FAILURE); + } + parcMemory_Deallocate(&msg[1].iov_base); + } else { // command without payload, e.g. 'list' + // write header only + if (write(sockfd, msg[0].iov_base, msg[0].iov_len) < 0) { + printf("\nError while sending the Message: cannot write on socket \n"); + exit(EXIT_FAILURE); + } + } + parcMemory_Deallocate(&msg[0].iov_base); + + // ======= RECEIVE ======= + + header_control_message *headerResponse = + (header_control_message *)parcMemory_AllocateAndClear( + sizeof(header_control_message)); + if (recv(sockfd, headerResponse, sizeof(header_control_message), 0) < 0) { + printf("\nError in Receiving the Message \n"); + exit(EXIT_FAILURE); + } + + if (headerResponse->messageType < RESPONSE_LIGHT || + headerResponse->messageType >= LAST_MSG_TYPE_VALUE) { + char *checkFinMsg = parcMemory_Reallocate(headerResponse, 32); + if (recv(sockfd, checkFinMsg, sizeof(checkFinMsg), + MSG_PEEK | MSG_DONTWAIT) == 0) { + // if recv returns zero, that means the connection has been closed: + close(sockfd); + printf("\nConnection terminated by the Daemon. Exiting... \n"); + exit(EXIT_SUCCESS); + } else { + printf("\nError: Unrecognized message type received \n"); + exit(EXIT_FAILURE); + } + } + + void *payloadResponse = NULL; + + if ((commandOutputLen = headerResponse->length) > 0) { + payloadResponse = parcMemory_AllocateAndClear( + payloadLengthController[headerResponse->commandID] * + headerResponse->length); + + if (recv(sockfd, payloadResponse, + payloadLengthController[headerResponse->commandID] * + headerResponse->length, + 0) < 0) { + printf("\nError in Receiving the Message \n"); + exit(EXIT_FAILURE); + } + } + + struct iovec *response = + parcMemory_AllocateAndClear(sizeof(struct iovec) * 2); + + response[0].iov_base = headerResponse; + response[0].iov_len = sizeof(header_control_message); + response[1].iov_base = payloadResponse; + response[1].iov_len = payloadLengthController[headerResponse->commandID] * + headerResponse->length; + + return response; +} + +int main(int argc, char *argv[]) { + _displayForwarderLogo(); + + if (argc == 2 && strcmp("-h", argv[1]) == 0) { + _displayUsage(argv[0]); + exit(EXIT_SUCCESS); + } + + PARCList *commands = + parcList(parcArrayList_Create(NULL), PARCArrayListAsPARCList); + + if (!_parseArgs(argc, argv, NULL, NULL, commands)) { + parcList_Release(&commands); + exit(EXIT_FAILURE); + } + + ControlMainState mainState; + mainState.controlState = + controlState_Create(&mainState, _writeAndReadMessage, true); + + controlState_RegisterCommand(mainState.controlState, + controlRoot_HelpCreate(mainState.controlState)); + controlState_RegisterCommand(mainState.controlState, + controlRoot_Create(mainState.controlState)); + + if (parcList_Size(commands) > 0) { + controlState_SetInteractiveFlag(mainState.controlState, false); + controlState_DispatchCommand(mainState.controlState, commands); + char **commandOutputMain = + controlState_GetCommandOutput(mainState.controlState); + if (commandOutputMain != NULL && commandOutputLen > 0) { + for (size_t j = 0; j < commandOutputLen; j++) { + printf("Output %zu: %s \n", j, commandOutputMain[j]); + } + controlState_ReleaseCommandOutput(mainState.controlState, + commandOutputMain, commandOutputLen); + } + // release + + } else { + controlState_Interactive(mainState.controlState); + } + + parcList_Release(&commands); + + controlState_Destroy(&mainState.controlState); + + return EXIT_SUCCESS; +} diff --git a/hicn-light/src/command_line/daemon/CMakeLists.txt b/hicn-light/src/command_line/daemon/CMakeLists.txt new file mode 100755 index 000000000..fd6cc9310 --- /dev/null +++ b/hicn-light/src/command_line/daemon/CMakeLists.txt @@ -0,0 +1,23 @@ +# Copyright (c) 2017-2019 Cisco and/or its affiliates. +# Licensed 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. + +list(APPEND DAEMON_SRC + hicnLightDaemon_main.c +) + +build_executable(${HICN_LIGHT_DAEMON} + SOURCES ${DAEMON_SRC} + LINK_LIBRARIES ${HICN_LIGHT_LINK_LIBRARIES} + DEPENDS hicn-light + COMPONENT hicn-light +) \ No newline at end of file diff --git a/hicn-light/src/command_line/daemon/hicnLightDaemon_main.c b/hicn-light/src/command_line/daemon/hicnLightDaemon_main.c new file mode 100755 index 000000000..f6d521711 --- /dev/null +++ b/hicn-light/src/command_line/daemon/hicnLightDaemon_main.c @@ -0,0 +1,336 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include +#include + +static void _displayForwarderLogo(void){ + const char cli_banner [] = + "\033[0;31m ____ ___ _ \033[0m __ _ __ _ __ __\n" + "\033[0;31m / __// _ \\ (_)___ \033[0m / / (_)____ ___ ____/ /(_)___ _ / / / /_\n" + "\033[0;31m / _/ / // /_ / // _ \\ \033[0m / _ \\ / // __// _ \\___/ // // _ `// _ \\/ __/\n" + "\033[0;31m/_/ /____/(_)/_/ \\___/ \033[0m/_//_//_/ \\__//_//_/ /_//_/ \\_, //_//_/\\__/\n" + " /___/ \n"; + printf("%s", cli_banner); + printf("\n"); +} + +static void _usage(int exitCode) { + printf( + "Usage: daemon [--port port] [--daemon] [--capacity objectStoreSize] " + "[--log facility=level] [--log-file filename] [--config file]\n"); + printf("\n"); + printf( + "hicn-light run as a daemon is the program to launch the forwarder, " + "either as a console program\n"); + printf( + "or a background daemon (detatched from console). Once running, use the " + "program controller to\n"); + printf("configure hicn-light.\n"); + printf("\n"); + printf( + "The configuration file contains configuration lines as per " + "controller\n"); + printf( + "If logging level or content store capacity is set in the configuraiton " + "file, it overrides the command_line\n"); + printf( + "When a configuration file is specified, no default listeners on 'port' " + "are setup. Only 'add listener' lines\n"); + printf("in the configuration file matter.\n"); + printf("\n"); + printf( + "If no configuration file is specified, daemon will listen on TCP and " + "UDP ports specified by\n"); + printf( + "the --port flag (or default port). It will listen on both IPv4 and " + "IPv6 if available.\n"); + printf("\n"); + printf("Options:\n"); + printf("--port = tcp port for in-bound connections\n"); + printf("--daemon = start as daemon process\n"); + printf("--objectStoreSize = maximum number of content objects to cache\n"); + printf( + "--log = sets a facility to a given log level. You can have " + "multiple of these.\n"); + printf( + " facilities: all, config, core, io, message, " + "processor\n"); + printf( + " levels: debug, info, notice, warning, error, " + "critical, alert, off\n"); + printf(" example: daemon --log io=debug --log core=off\n"); + printf( + "--log-file = file to write log messages to (required in daemon " + "mode)\n"); + printf("--config = configuration filename\n"); + printf("\n"); + exit(exitCode); +} + +static void _setLogLevelToLevel(int logLevelArray[LoggerFacility_END], + LoggerFacility facility, + const char *levelString) { + PARCLogLevel level = parcLogLevel_FromString(levelString); + + if (level < PARCLogLevel_All) { + // we have a good facility and level + logLevelArray[facility] = level; + } else { + printf("Invalid log level string %s\n", levelString); + _usage(EXIT_FAILURE); + } +} + +/** + * string: "facility=level" + * Set the right thing in the logger + */ +static void _setLogLevel(int logLevelArray[LoggerFacility_END], + const char *string) { + char *tofree = parcMemory_StringDuplicate(string, strlen(string)); + char *p = tofree; + + char *facilityString = strsep(&p, "="); + if (facilityString) { + char *levelString = p; + + if (strcasecmp(facilityString, "all") == 0) { + for (LoggerFacility facility = 0; facility < LoggerFacility_END; + facility++) { + _setLogLevelToLevel(logLevelArray, facility, levelString); + } + } else { + LoggerFacility facility; + for (facility = 0; facility < LoggerFacility_END; facility++) { + if (strcasecmp(facilityString, logger_FacilityString(facility)) == 0) { + break; + } + } + + if (facility < LoggerFacility_END) { + _setLogLevelToLevel(logLevelArray, facility, levelString); + } else { + printf("Invalid facility string %s\n", facilityString); + _usage(EXIT_FAILURE); + } + } + } + + parcMemory_Deallocate((void **)&tofree); +} + +static void _daemonize(void) { + if (getppid() == 1) { + // already a daemon + return; + } + + int forkReturn = fork(); + parcTrapUnexpectedStateIf(forkReturn < 0, "Fork error"); + + if (forkReturn > 0) { + // parent exits + exit(EXIT_SUCCESS); + } + + // Child daemon detaches + printf("child continuing, pid = %u\n", getpid()); + + // get a new process group independent from old parent + setsid(); + + /* close all descriptors */ + for (int i = getdtablesize(); i >= 0; --i) { + close(i); + } + + // reset errno because it might be seg to EBADF from the close calls above + errno = 0; + + // Redirect stdin and stdout and stderr to /dev/null + const char *devnull = "/dev/null"; + int nullfile = open(devnull, O_RDWR); + parcAssertTrue(nullfile >= 0, "Error opening file '%s': (%d) %s", devnull, + errno, strerror(errno)); + + int ret; + ret = dup(nullfile); + parcAssertTrue(ret == 1, "Error duping fd 1 got %d file: (%d) %s", ret, errno, + strerror(errno)); + ret = dup(nullfile); + parcAssertTrue(ret == 2, "Error duping fd 2, got %d file: (%d) %s", ret, + errno, strerror(errno)); + + // forwarder will capture signals +} + +static Logger *_createLogfile(const char *logfile) { + int logfd = open(logfile, O_WRONLY | O_APPEND | O_CREAT, S_IWUSR | S_IRUSR); + if (logfd < 0) { + fprintf(stderr, "Error opening %s for writing: (%d) %s\n", logfile, errno, + strerror(errno)); + exit(EXIT_FAILURE); + } + + chmod(logfile, S_IRWXU); + + PARCFileOutputStream *fos = parcFileOutputStream_Create(logfd); + PARCOutputStream *pos = parcFileOutputStream_AsOutputStream(fos); + PARCLogReporter *reporter = parcLogReporterFile_Create(pos); + + Logger *logger = logger_Create(reporter, parcClock_Wallclock()); + + parcOutputStream_Release(&pos); + parcLogReporter_Release(&reporter); + + return logger; +} + +int main(int argc, const char *argv[]) { + _displayForwarderLogo(); + + uint16_t port = PORT_NUMBER; + uint16_t configurationPort = 2001; + bool daemon = false; + int capacity = -1; + const char *configFileName = NULL; + + char *logfile = NULL; + + if (argc == 2 && strcasecmp(argv[1], "-h") == 0) { + _usage(EXIT_SUCCESS); + } + + int logLevelArray[LoggerFacility_END]; + for (int i = 0; i < LoggerFacility_END; i++) { + logLevelArray[i] = -1; + } + + for (int i = 0; i < argc; i++) { + if (argv[i][0] == '-') { + if (strcmp(argv[i], "--config") == 0) { + configFileName = argv[i + 1]; + i++; + } else if (strcmp(argv[i], "--port") == 0) { + port = atoi(argv[i + 1]); + i++; + } else if (strcmp(argv[i], "--daemon") == 0) { + daemon = true; + } else if (strcmp(argv[i], "--capacity") == 0 || + strcmp(argv[i], "-c") == 0) { + capacity = atoi(argv[i + 1]); + i++; + } else if (strcmp(argv[i], "--log") == 0) { + _setLogLevel(logLevelArray, argv[i + 1]); + i++; + } else if (strcmp(argv[i], "--log-file") == 0) { + if (logfile) { + // error cannot repeat + fprintf(stderr, "Cannot specify --log-file more than once\n"); + _usage(EXIT_FAILURE); + } + + logfile = parcMemory_StringDuplicate(argv[i + 1], strlen(argv[i + 1])); + i++; + } else { + _usage(EXIT_FAILURE); + } + } + } + + // set restrictive umask, in case we create any files + umask(027); + + if (daemon && (logfile == NULL)) { + fprintf(stderr, "Must specify a logfile when running in daemon mode\n"); + _usage(EXIT_FAILURE); + } + + if (daemon) { + // inside this call, parent will EXIT_SUCCESS and child will continue + _daemonize(); + } + + Logger *logger = NULL; + if (logfile) { + logger = _createLogfile(logfile); + parcMemory_Deallocate((void **)&logfile); + } else { + PARCLogReporter *stdoutReporter = parcLogReporterTextStdout_Create(); + logger = logger_Create(stdoutReporter, parcClock_Wallclock()); + parcLogReporter_Release(&stdoutReporter); + } + + for (int i = 0; i < LoggerFacility_END; i++) { + if (logLevelArray[i] > -1) { + logger_SetLogLevel(logger, i, logLevelArray[i]); + } + } + + // this will update the clock to the tick clock + Forwarder *forwarder = forwarder_Create(logger); + + Configuration *configuration = forwarder_GetConfiguration(forwarder); + + if (capacity > -1) { + configuration_SetObjectStoreSize(configuration, capacity); + } + + if (configFileName) { + forwarder_SetupAllListeners(forwarder, port, NULL); + forwarder_SetupFromConfigFile(forwarder, configFileName); + } else { + // NULL to not setup AF_UNIX + forwarder_SetupAllListeners(forwarder, port, NULL); + } + + Dispatcher *dispatcher = forwarder_GetDispatcher(forwarder); + + logger_Log(logger, LoggerFacility_Core, PARCLogLevel_Alert, "daemon", + "hicn-light running port %d configuration-port %d", port, + configurationPort); + + dispatcher_Run(dispatcher); + + logger_Log(logger, LoggerFacility_Core, PARCLogLevel_Alert, "daemon", + "hicn-light exiting port %d", port); + + forwarder_Destroy(&forwarder); + + sleep(2); + + logger_Release(&logger); + return 0; +} -- cgit 1.2.3-korg