diff options
Diffstat (limited to 'hicn-light/src/hicn/cli')
-rw-r--r-- | hicn-light/src/hicn/cli/CMakeLists.txt | 93 | ||||
-rw-r--r-- | hicn-light/src/hicn/cli/color.c | 71 | ||||
-rw-r--r-- | hicn-light/src/hicn/cli/color.h | 42 | ||||
-rw-r--r-- | hicn-light/src/hicn/cli/hicnc.c | 237 | ||||
-rw-r--r-- | hicn-light/src/hicn/cli/hicnd.c | 311 | ||||
-rw-r--r-- | hicn-light/src/hicn/cli/hicns.c | 206 | ||||
-rw-r--r-- | hicn-light/src/hicn/cli/logo.h | 37 |
7 files changed, 997 insertions, 0 deletions
diff --git a/hicn-light/src/hicn/cli/CMakeLists.txt b/hicn-light/src/hicn/cli/CMakeLists.txt new file mode 100644 index 000000000..39581e6ad --- /dev/null +++ b/hicn-light/src/hicn/cli/CMakeLists.txt @@ -0,0 +1,93 @@ +# Copyright (c) 2021-2022 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. + + +if (NOT DISABLE_EXECUTABLES) +############################################################## +# Force libhicn library to whole archive mode +############################################################## + if (APPLE) + set( + LIBHICN_LIGHT_WHOLE_ARCHIVE + "-Wl,-force_load" + "${LIBHICN_LIGHT_STATIC}" + ) + else() + set( + LIBHICN_LIGHT_WHOLE_ARCHIVE + "-Wl,--whole-archive" + "${LIBHICN_LIGHT_STATIC}" + "-Wl,--no-whole-archive" + ) + endif() + +############################################################## +# Sources +############################################################## + list(APPEND CONTROLLER_SRC + color.c + hicnc.c + ) + + +############################################################## +# Build hicn-light-control +############################################################## + build_executable(${HICN_LIGHT_CONTROL} + SOURCES ${CONTROLLER_SRC} + LINK_LIBRARIES ${LIBHICN_LIGHT_WHOLE_ARCHIVE} + DEPENDS ${LIBHICN_LIGHT_STATIC} + COMPONENT ${HICN_LIGHT} + DEFINITIONS ${COMPILER_DEFINITIONS} + COMPILE_OPTIONS ${COMPILER_OPTIONS} + ) + + +############################################################## +# Build hicn-light-shell +############################################################## + list(APPEND SHELL_SRC + color.c + hicns.c + ) + + build_executable(${HICN_LIGHT_SHELL} + SOURCES ${SHELL_SRC} + LINK_LIBRARIES ${LIBHICN_LIGHT_WHOLE_ARCHIVE} + DEPENDS ${LIBHICN_LIGHT_STATIC} + COMPONENT ${HICN_LIGHT} + DEFINITIONS ${COMPILER_DEFINITIONS} + COMPILE_OPTIONS ${COMPILER_OPTIONS} + ) + + +############################################################## +# Build hicn-light-shell +############################################################## + list(APPEND DAEMON_SRC + color.c + hicnd.c + ) + + # hicn-light-daemon does not compile under Android due to bindSocket + if (NOT ${CMAKE_SYSTEM_NAME} STREQUAL "Android") + build_executable(${HICN_LIGHT_DAEMON} + SOURCES ${DAEMON_SRC} + LINK_LIBRARIES ${LIBHICN_LIGHT_WHOLE_ARCHIVE} + DEPENDS ${LIBHICN_LIGHT_STATIC} + COMPONENT ${HICN_LIGHT} + DEFINITIONS ${COMPILER_DEFINITIONS} + COMPILE_OPTIONS ${COMPILER_OPTIONS} + ) + endif () +endif () diff --git a/hicn-light/src/hicn/cli/color.c b/hicn-light/src/hicn/cli/color.c new file mode 100644 index 000000000..aed22e302 --- /dev/null +++ b/hicn-light/src/hicn/cli/color.c @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2021 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 <stdio.h> +#include "color.h" + +#ifndef _WIN32 + +void vprintfc(color_t color, const char* fmt, va_list ap) { + char* color_s; + switch (color) { +#define _(x, y, z) \ + case COLOR_##x: \ + color_s = y; \ + break; + foreach_color +#undef _ + + case COLOR_UNDEFINED : case COLOR_N : default : // XXX + color_s = ""; + break; + } + printf("%s", color_s); + vprintf(fmt, ap); +} +#else +void vprintfc(color_t color, const char* fmt, va_list ap) { + int color_id; + switch (color) { +#define _(x, y, z) \ + case COLOR_##x: \ + color_id = z; \ + break; + foreach_color +#undef _ + + case COLOR_UNDEFINED : case COLOR_N : color_id = 0; + break; + } + HANDLE hConsole = NULL; + WORD currentConsoleAttr; + CONSOLE_SCREEN_BUFFER_INFO csbi; + hConsole = GetStdHandle(STD_OUTPUT_HANDLE); + if (GetConsoleScreenBufferInfo(hConsole, &csbi)) + currentConsoleAttr = csbi.wAttributes; + if (color_id != 0) SetConsoleTextAttribute(hConsole, color_id); + fprintf("%s", color); + vfprintf(fmt, ap); + SetConsoleTextAttribute(hConsole, currentConsoleAttr); +} +#endif + +void printfc(color_t color, const char* fmt, ...) { + va_list ap; + + va_start(ap, fmt); + vprintfc(color, fmt, ap); + va_end(ap); +} diff --git a/hicn-light/src/hicn/cli/color.h b/hicn-light/src/hicn/cli/color.h new file mode 100644 index 000000000..a9b71c047 --- /dev/null +++ b/hicn-light/src/hicn/cli/color.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2021 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. + */ + +#ifndef HICNLIGHT_COLOR +#define HICNLIGHT_COLOR + +#include <stdarg.h> + +/* + * Format : color_name, escape sequence, windows id + */ +#define foreach_color \ + _(RED, "\033[0;31m", 4) \ + _(WHITE, "\033[0m", 7) + +typedef enum { + COLOR_UNDEFINED, +#define _(x, y, z) COLOR_##x, + foreach_color +#undef _ + COLOR_N, +} color_t; + +#define IS_VALID_COLOR(color) ((color != COLOR_UNDEFINED) && (color != COLOR_N)) + +void vprintfc(color_t color, const char* fmt, va_list ap); + +void printfc(color_t color, const char* fmt, ...); + +#endif /* HICNLIGHT_COLOR */ diff --git a/hicn-light/src/hicn/cli/hicnc.c b/hicn-light/src/hicn/cli/hicnc.c new file mode 100644 index 000000000..ed8d4ed97 --- /dev/null +++ b/hicn-light/src/hicn/cli/hicnc.c @@ -0,0 +1,237 @@ +/* + * Copyright (c) 2021 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 <limits.h> // LONG_MAX, LONG_MIN +#include <signal.h> +#include <hicn/ctrl.h> + +#ifndef _WIN32 +#include <getopt.h> +#endif + +#include "color.h" +#include <hicn/ctrl/parse.h> +#include <hicn/ctrl/hicn-light.h> +#include <hicn/util/log.h> +#include <hicn/util/sstrncpy.h> + +#define PORT 9695 + +/* + * Duplicated from hicn_light_ng_api.c while is only available as a module in + * libhicnctrl + */ +const char *command_type_str[] = { +#define _(l, u) [COMMAND_TYPE_##u] = STRINGIZE(u), + foreach_command_type +#undef _ +}; + +static struct option longFormOptions[] = {{"help", no_argument, 0, 'h'}, + {"server", required_argument, 0, 'S'}, + {"port", required_argument, 0, 'P'}, + {0, 0, 0, 0}}; + +static void usage(char *prog) { + printf("%s: portable hICN forwarder\n", prog); + printf("\n"); + printf("Usage: %s COMMAND [PARAMETERS]\n", prog); + printf("\n"); + printf(" %s -h This help screen.\n", prog); + printf(" %s help Obtain a list of available commands.\n", prog); + printf("\n"); +} + +static bool stop = false; +void signal_handler(int sig) { + fprintf(stderr, "Received ^C... quitting !\n"); + stop = true; +} + +int main(int argc, char *const *argv) { + log_conf.log_level = LOG_INFO; + + // Handle termination signal + struct sigaction sa; + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = signal_handler; + sigaction(SIGINT, &sa, NULL); + + /* Parse commandline */ + char *server_ip = NULL; + uint16_t server_port = 0; + + for (;;) { + // getopt_long stores the option index here. + int optind = 0; + + int c = getopt_long(argc, argv, "dhS:P:", longFormOptions, &optind); + if (c == -1) break; + + switch (c) { + case 'd': + log_conf.log_level = LOG_DEBUG; + break; + case 'S': + server_ip = optarg; + break; + + case 'P': { + char *endptr; + long val = strtol(optarg, &endptr, 10); + + errno = 0; /* To distinguish success/failure after call */ + + if ((errno == ERANGE && (val == LONG_MAX || val == LONG_MIN)) || + (errno != 0 && val == 0)) { + perror("strtol"); + exit(EXIT_FAILURE); + } + + if (endptr == optarg) { + fprintf(stderr, "No digits were found.\n"); + exit(EXIT_FAILURE); + } + + if (*endptr != '\0') { + fprintf(stderr, "Spurious characters after number: %s.\n", endptr); + exit(EXIT_FAILURE); + } + + if ((val < 1) || (val > 65535)) { + fprintf(stderr, "Invalid port number: %ld.\n", val); + exit(EXIT_FAILURE); + } + + server_port = val; + break; + } + + case 'h': + usage(argv[0]); + exit(EXIT_SUCCESS); + + default: + fprintf(stderr, "Invalid argument.\n"); + usage(argv[0]); + exit(EXIT_FAILURE); + } + } + + /* Parse */ + char *param = argv[optind]; + for (; optind < argc - 1; optind++) { + char *arg = argv[optind]; + arg[strlen(arg)] = ' '; + } + + if (!param) { + usage(argv[0]); + goto ERR_PARAM; + } + + if (strncmp(param, "help", 4) == 0) { + if (help(param) < 0) { + usage(argv[0]); + exit(EXIT_FAILURE); + } + exit(EXIT_SUCCESS); + } + + hc_command_t command = {}; + if (parse(param, &command) < 0) { + fprintf(stderr, "Error parsing command : '%s'\n", param); + goto ERR_PARSE; + } + + hc_sock_t *s; + if (server_ip) { + if (server_port == 0) server_port = PORT; +#define BUFSIZE 255 + char url[BUFSIZE]; + snprintf(url, BUFSIZE, "tcp://%s:%d/", server_ip, server_port); + s = hc_sock_create(FORWARDER_TYPE_HICNLIGHT, url); + } else { + s = hc_sock_create(FORWARDER_TYPE_HICNLIGHT, NULL); + } + if (!s) { + fprintf(stderr, "Could not create socket.\n"); + goto ERR_SOCKET; + } + + if (hc_sock_connect(s) < 0) { + fprintf(stderr, "Could not establish connection to forwarder.\n"); + goto ERR_CONNECT; + } + + if (!IS_VALID_OBJECT_TYPE(command.object_type) || + !IS_VALID_ACTION(command.action)) { + fprintf(stderr, "Unsupported command"); + goto ERR_PARAM; + } + + int rc = UNSUPPORTED_CMD_ERROR; + hc_data_t *data = NULL; + + rc = hc_execute(s, command.action, command.object_type, &command.object, + &data); + + if (rc < 0) { + switch (rc) { + case INPUT_ERROR: + ERROR("Wrong input parameters"); + break; + case UNSUPPORTED_CMD_ERROR: + ERROR("Unsupported command"); + break; + default: + ERROR("Error executing command"); + break; + } + goto ERR_COMMAND; + } + + if (!data) goto ERR_QUERY; + if (!hc_data_get_result(data)) goto ERR_DATA; + + if (command.action == ACTION_LIST) { + char buf[MAXSZ_HC_OBJECT]; + hc_data_foreach(data, obj, { + rc = hc_object_snprintf(buf, MAXSZ_HC_OBJECT, command.object_type, obj); + if (rc < 0) + WARN("Display error"); + else if (rc >= MAXSZ_HC_OBJECT) + WARN("Output truncated"); + else + printf("%s\n", buf); + }); + } + + hc_data_free(data); + hc_sock_free(s); + return EXIT_SUCCESS; + +ERR_DATA: + hc_data_free(data); +ERR_QUERY: +ERR_COMMAND: +ERR_CONNECT: + hc_sock_free(s); +ERR_SOCKET: +ERR_PARSE: +ERR_PARAM: + ERROR("Error"); + return EXIT_FAILURE; +} diff --git a/hicn-light/src/hicn/cli/hicnd.c b/hicn-light/src/hicn/cli/hicnd.c new file mode 100644 index 000000000..fa1b1c024 --- /dev/null +++ b/hicn-light/src/hicn/cli/hicnd.c @@ -0,0 +1,311 @@ +/* + * Copyright (c) 2021 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. + */ + +#ifndef _WIN32 +#include <unistd.h> +#endif +#include <errno.h> +#include <fcntl.h> +#include <stdbool.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> + +#include <hicn/util/log.h> +#include <hicn/base/loop.h> + +#include "logo.h" +#include "../core/forwarder.h" +#include "../config/configuration.h" // XXX needed ? +#include "../config/configuration_file.h" + +static void usage(const char *prog) { + printf( + "Usage: %s [--port port]" +#ifndef _WIN32 + " [--daemon]" +#endif + " [--capacity objectStoreSize] [--log level]" + "[--log-file filename] [--config file]\n", + prog); + 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("%-30s = tcp port for in-bound connections\n", "--port <tcp_port>"); +#ifndef _WIN32 + printf("%-30s = start as daemon process\n", "--daemon"); +#endif + printf( + "%-30s = maximum number of content objects to cache. To disable the " + "cache objectStoreSize must be 0.\n", + "--capacity <objectStoreSize>"); + printf("%-30s Default vaule for objectStoreSize is 100000\n", ""); + printf( + "%-30s = sets the log level. Available levels: trace, debug, info, warn, " + "error, fatal\n", + "--log <level>"); + printf("%-30s = file to write log messages to (required in daemon mode)\n", + "--log-file <output_logfile>"); + printf("%-30s = configuration filename\n", "--config <config_path>"); + printf("\n"); +} + +#ifndef _WIN32 +static int daemonize(int logfile_fd) { + /* Check whether we already are a daemon */ + if (getppid() == 1) return 0; + + int rc = fork(); + if (rc < 0) { + ERROR("Fork error"); + goto ERR_FORK; + } else if (rc > 0) { + /* Parent exits successfully */ + exit(EXIT_SUCCESS); + } + + /* Child daemon detaches */ + DEBUG("child continuing, pid = %u\n", getpid()); + + /* get a new process group independent from old parent */ + setsid(); + + /* close all descriptors (apart from the logfile) */ +#ifdef __ANDROID__ + for (int i = sysconf(_SC_OPEN_MAX); i >= 0; --i) close(i); +#else + for (int i = getdtablesize(); i >= 0; --i) { + if (i != logfile_fd) close(i); + } +#endif + + /* + * 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); + if (nullfile < 0) { + ERROR("Error opening file '%s': (%d) %s", devnull, errno, strerror(errno)); + goto ERR_DEVNULL; + } + + /* Redirect stdout and stderr to the logfile */ + rc = dup2(logfile_fd, STDOUT_FILENO); + if (rc != STDOUT_FILENO) { + ERROR("Error duping fd 1 got %d file: (%d) %s", rc, errno, strerror(errno)); + goto ERR_DUP1; + } + rc = dup2(logfile_fd, STDERR_FILENO); + if (rc != STDERR_FILENO) { + ERROR("Error duping fd 2 got %d file: (%d) %s", rc, errno, strerror(errno)); + goto ERR_DUP2; + } + + /* Forwarder will capture signals */ + return 0; + +ERR_DUP2: +ERR_DUP1: +ERR_DEVNULL: +ERR_FORK: + return -1; +} +#endif + +static void signal_cb(int sig) { + switch (sig) { + case SIGTERM: + case SIGINT: + INFO("caught an interrupt signal, exiting cleanly"); + break; +#ifndef _WIN32 + case SIGUSR1: + // dump stats + break; +#endif + default: + break; + } + + if (loop_break(MAIN_LOOP) < 0) { + ERROR("Failed to terminate main loop"); + _exit(1); + } +} + +static void signal_setup() { +#ifndef _WIN32 + signal(SIGUSR1, signal_cb); + + /* ignore child */ + signal(SIGCHLD, SIG_IGN); + + /* ignore tty signals */ + signal(SIGTSTP, SIG_IGN); + signal(SIGTTOU, SIG_IGN); + signal(SIGTTIN, SIG_IGN); +#endif + signal(SIGINT, signal_cb); + signal(SIGTERM, signal_cb); +} + +configuration_t *parse_commandline(int argc, const char *argv[]) { + if (argc == 2 && strcasecmp(argv[1], "-h") == 0) { + usage(argv[0]); + exit(EXIT_SUCCESS); // XXX redundant + } + + configuration_t *configuration = configuration_create(); + + // XXX use getoptlong ???? + for (int i = 0; i < argc; i++) { + if (argv[i][0] == '-') { + if (strcmp(argv[i], "--config") == 0) { + const char *fn_config = argv[i + 1]; + configuration_set_fn_config(configuration, fn_config); + i++; + } else if (strcmp(argv[i], "--port") == 0) { + uint16_t port = atoi(argv[i + 1]); + configuration_set_port(configuration, port); + i++; +#ifndef _WIN32 + } else if (strcmp(argv[i], "--daemon") == 0) { + configuration_set_daemon(configuration, true); +#endif + } else if (strcmp(argv[i], "--capacity") == 0 || + strcmp(argv[i], "-c") == 0) { + int capacity = atoi(argv[i + 1]); + configuration_set_cs_size(configuration, capacity); + i++; + } else if (strcmp(argv[i], "--log") == 0) { + int loglevel = loglevel_from_str(argv[i + 1]); + configuration_set_loglevel(configuration, loglevel); + i++; + } else if (strcmp(argv[i], "--log-file") == 0) { + if (configuration_get_logfile(configuration)) { + fprintf(stderr, "Cannot specify --log-file more than once\n"); + usage(argv[0]); + exit(EXIT_FAILURE); + } + + const char *logfile = argv[i + 1]; + configuration_set_logfile(configuration, logfile); + i++; + } else { + usage(argv[0]); + exit(EXIT_FAILURE); + } + } + } + + return configuration; +} + +int main(int argc, const char *argv[]) { + signal_setup(); + logo(); + + configuration_t *configuration = parse_commandline(argc, argv); + const char *logfile = configuration_get_logfile(configuration); + bool daemon = configuration_get_daemon(configuration); + + // set restrictive umask, in case we create any files + umask(027); + +#ifndef _WIN32 + if (daemon && (logfile == NULL)) { + fprintf(stderr, "Must specify a logfile when running in daemon mode\n"); + usage(argv[0]); + exit(EXIT_FAILURE); + } + + /* In daemon mode, parent will exit and child will continue */ + if (daemon && daemonize(configuration_get_logfile_fd(configuration)) < 0) { + ERROR("Could not daemonize process"); + exit(EXIT_FAILURE); + } +#endif + + /* + * The loop should be created before the forwarder instance as it is needed + * for timers + */ + MAIN_LOOP = loop_create(); + + forwarder_t *forwarder = forwarder_create(configuration); + if (!forwarder) { + ERROR( + "Forwarder initialization failed. Are you running it with sudo " + "privileges?"); + return -1; + } + + forwarder_setup_local_listeners(forwarder, + configuration_get_port(configuration)); + + /* If specified, process the configuration file */ + const char *fn_config = configuration_get_fn_config(configuration); + if (fn_config) configuration_file_process(forwarder, fn_config); + INFO("%s running port %d configuration-port %d", argv[0], + configuration_get_port(configuration), + configuration_get_configuration_port(configuration)); + + /* Main loop */ + if (loop_dispatch(MAIN_LOOP) < 0) { + ERROR("Failed to run main loop"); + return EXIT_FAILURE; + } + + INFO("loop stopped"); + forwarder_free(forwarder); + loop_free(MAIN_LOOP); + MAIN_LOOP = NULL; + +#ifdef _WIN32 + WSACleanup(); // XXX why is this needed here ? +#endif + + configuration_flush_log(); + return 0; +} diff --git a/hicn-light/src/hicn/cli/hicns.c b/hicn-light/src/hicn/cli/hicns.c new file mode 100644 index 000000000..c03fa8599 --- /dev/null +++ b/hicn-light/src/hicn/cli/hicns.c @@ -0,0 +1,206 @@ +/* + * Copyright (c) 2021 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 <limits.h> // LONG_MAX, LONG_MIN +#include <hicn/ctrl.h> + +#ifndef _WIN32 +#include <getopt.h> +#endif + +#include "logo.h" +#include <hicn/ctrl/parse.h> +#include <hicn/util/sstrncpy.h> + +#define PORT 9695 + +static struct option longFormOptions[] = {{"help", no_argument, 0, 'h'}, + {"server", required_argument, 0, 'S'}, + {"port", required_argument, 0, 'P'}, + {0, 0, 0, 0}}; + +static void usage(char *prog) { + printf("%s: interactive shell for hicn-light\n", prog); + printf("\n"); + printf("Usage: %s", prog); + printf("\n"); + printf(" %s -h This help screen.\n", prog); + printf("\n"); +} + +void prompt(void) { + fputs("hicn> ", stdout); + fflush(stdout); +} + +int shell(hc_sock_t *s) { + char *line = NULL; + size_t len = 0; + hc_data_t *data = NULL; + ssize_t nread; + + hc_data_t *connections; + + prompt(); + while ((nread = getline(&line, &len, stdin)) != -1) { + hc_command_t command = {0}; + + char *pos; + if (line != NULL && (pos = strchr(line, '\n')) != NULL) { + *pos = '\0'; + } else { + fprintf(stderr, "Error while reading command.\n"); + goto CONTINUE; + } + + if (strlen(line) == 0) goto CONTINUE; + + if (strncmp(line, "exit", 4) == 0) break; + if (strncmp(line, "quit", 4) == 0) break; + + if (parse(line, &command) < 0) { + fprintf(stderr, "Unknown command '%s'\n", line); + goto CONTINUE; + } + + /* XXX connection list */ + if (hc_connection_list(s, &connections) < 0) { + fprintf(stderr, "Error running command.\n"); + goto CONTINUE; + } + // data = command.object.data; + + char buf[MAXSZ_HC_CONNECTION]; // XXX + foreach_connection(c, data) { + /* XXX connection print */ + int rc = hc_connection_snprintf(buf, MAXSZ_HC_CONNECTION, c); + if (rc < 0) { + strcpy_s(buf, sizeof(buf), "(Error)"); + } else if (rc >= MAXSZ_HC_CONNECTION) { + buf[MAXSZ_HC_CONNECTION - 1] = '\0'; + buf[MAXSZ_HC_CONNECTION - 2] = '.'; + buf[MAXSZ_HC_CONNECTION - 3] = '.'; + buf[MAXSZ_HC_CONNECTION - 4] = '.'; + } + printf("%s\n", buf); + } + + hc_data_free(data); + CONTINUE: + prompt(); + } + + return 0; +} + +int main(int argc, char *const *argv) { + logo(); + printf("Type 'help' for a list of available commands\n"); + printf("\n"); + printf("\n"); + + /* Parse commandline */ + char *server_ip = NULL; + uint16_t server_port = 0; + + for (;;) { + // getopt_long stores the option index here. + int optind = 0; + + int c = getopt_long(argc, argv, "hS:P:", longFormOptions, &optind); + if (c == -1) break; + + switch (c) { + case 'S': + server_ip = optarg; + break; + + case 'P': { + char *endptr; + long val = strtol(optarg, &endptr, 10); + + errno = 0; /* To distinguish success/failure after call */ + + if ((errno == ERANGE && (val == LONG_MAX || val == LONG_MIN)) || + (errno != 0 && val == 0)) { + perror("strtol"); + exit(EXIT_FAILURE); + } + + if (endptr == optarg) { + fprintf(stderr, "No digits were found.\n"); + exit(EXIT_FAILURE); + } + + if (*endptr != '\0') { + fprintf(stderr, "Spurious characters after number: %s.\n", endptr); + exit(EXIT_FAILURE); + } + + if ((val < 1) || (val > 65535)) { + fprintf(stderr, "Invalid port number: %ld.\n", val); + exit(EXIT_FAILURE); + } + + server_port = (uint16_t)val; + break; + } + + case 'h': + usage(argv[0]); + exit(EXIT_SUCCESS); + + default: + fprintf(stderr, "Invalid argument.\n"); + usage(argv[0]); + exit(EXIT_FAILURE); + } + } + + if (optind != argc) { + fprintf(stderr, "Invalid parameters.\n"); + usage(argv[0]); + exit(EXIT_FAILURE); + } + + hc_sock_t *s; + if (server_ip) { + if (server_port == 0) server_port = PORT; +#define BUFSIZE 255 + char url[BUFSIZE]; + snprintf(url, BUFSIZE, "tcp://%s:%d/", server_ip, server_port); + s = hc_sock_create(FORWARDER_TYPE_HICNLIGHT, url); + } else { + s = hc_sock_create(FORWARDER_TYPE_HICNLIGHT, NULL); + } + if (!s) { + fprintf(stderr, "Could not create socket.\n"); + goto ERR_SOCK; + } + + if (hc_sock_connect(s) < 0) { + fprintf(stderr, "Could not establish connection to forwarder.\n"); + goto ERR_CONNECT; + } + + int rc = shell(s); + + exit((rc < 0) ? EXIT_FAILURE : EXIT_SUCCESS); + +ERR_CONNECT: + hc_sock_free(s); +ERR_SOCK: + exit(EXIT_FAILURE); +} diff --git a/hicn-light/src/hicn/cli/logo.h b/hicn-light/src/hicn/cli/logo.h new file mode 100644 index 000000000..a69097f91 --- /dev/null +++ b/hicn-light/src/hicn/cli/logo.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2021 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. + */ + +#ifndef HICNLIGHT_LOGO +#define HICNLIGHT_LOGO + +#include "color.h" + +static void logo(void) { + printfc(COLOR_RED, " ____ ___ _ "); + printfc(COLOR_WHITE, " __ _ __ _ __ __\n"); + printfc(COLOR_RED, " / __// _ \\ (_)___ "); + printfc(COLOR_WHITE, " / / (_)____ ___ ____/ /(_)___ _ / / / /_\n"); + printfc(COLOR_RED, " / _/ / // /_ / // _ \\ "); + printfc(COLOR_WHITE, " / _ \\ / // __// _ \\___/ // // _ `// _ \\/ __/\n"); + printfc(COLOR_RED, "/_/ /____/(_)/_/ \\___/ "); + printfc(COLOR_WHITE, "/_//_//_/ \\__//_//_/ /_//_/ \\_, //_//_/\\__/\n"); + printfc( + COLOR_WHITE, + " /___/ " + "\n"); + printf("\n"); +} + +#endif /* HICNLIGHT_LOGO */ |