diff options
Diffstat (limited to 'metis/ccnx/forwarder/metis/command-line/metis_daemon')
-rw-r--r-- | metis/ccnx/forwarder/metis/command-line/metis_daemon/CMakeLists.txt | 4 | ||||
-rw-r--r-- | metis/ccnx/forwarder/metis/command-line/metis_daemon/metis_daemon.c | 319 |
2 files changed, 323 insertions, 0 deletions
diff --git a/metis/ccnx/forwarder/metis/command-line/metis_daemon/CMakeLists.txt b/metis/ccnx/forwarder/metis/command-line/metis_daemon/CMakeLists.txt new file mode 100644 index 00000000..957cab17 --- /dev/null +++ b/metis/ccnx/forwarder/metis/command-line/metis_daemon/CMakeLists.txt @@ -0,0 +1,4 @@ +add_executable(metis_daemon metis_daemon.c) +target_link_libraries(metis_daemon ${METIS_LINK_LIBRARIES}) + +install(TARGETS metis_daemon RUNTIME DESTINATION bin) diff --git a/metis/ccnx/forwarder/metis/command-line/metis_daemon/metis_daemon.c b/metis/ccnx/forwarder/metis/command-line/metis_daemon/metis_daemon.c new file mode 100644 index 00000000..b6fcfcf7 --- /dev/null +++ b/metis/ccnx/forwarder/metis/command-line/metis_daemon/metis_daemon.c @@ -0,0 +1,319 @@ +/* + * Copyright (c) 2017 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 <config.h> +#include <stdbool.h> +#include <stdint.h> +#include <string.h> +#include <strings.h> +#include <stdlib.h> +#include <fcntl.h> +#include <errno.h> +#include <sys/stat.h> +#include <stdio.h> +#include <unistd.h> + +#include <parc/algol/parc_FileOutputStream.h> +#include <parc/logging/parc_LogLevel.h> +#include <parc/logging/parc_LogReporterTextStdout.h> +#include <parc/logging/parc_LogReporterFile.h> + +#include <LongBow/runtime.h> + +#include <ccnx/forwarder/metis/core/metis_Forwarder.h> +#include <ccnx/forwarder/metis/core/metis_Dispatcher.h> +#include <ccnx/forwarder/metis/metis_About.h> + +static void +header(void) +{ + printf("%s\n", metisAbout_About()); + + printf(" __ __ _ _\n"); + printf(" | \\/ | ___ | |_ (_) ___\n"); + printf(" | |\\/| | / _ \\| __|| |/ __|\n"); + printf(" | | | || __/| |_ | |\\__ \\\n"); + printf(" |_| |_| \\___| \\__||_||___/\n"); + + printf("\n"); +} + +static void +_usage(int exitCode) +{ + printf("Usage: metis_daemon [--port port] [--daemon] [--capacity objectStoreSize] [--log facility=level] [--log-file filename] [--config file]\n"); + printf("\n"); + printf("Metis is the CCNx 1.0 forwarder, which runs on each end system and as a software forwarder\n"); + printf("on intermediate systems. metis_daemon is the program to launch Metis, either as a console program\n"); + printf("or a background daemon (detatched from console). Once running, use the program metis_control to\n"); + printf("configure Metis.\n"); + printf("\n"); + printf("The configuration file contains configuration lines as per metis_control\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, metis_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: metis_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[MetisLoggerFacility_END], MetisLoggerFacility 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[MetisLoggerFacility_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 (MetisLoggerFacility facility = 0; facility < MetisLoggerFacility_END; facility++) { + _setLogLevelToLevel(logLevelArray, facility, levelString); + } + } else { + MetisLoggerFacility facility; + for (facility = 0; facility < MetisLoggerFacility_END; facility++) { + if (strcasecmp(facilityString, metisLogger_FacilityString(facility)) == 0) { + break; + } + } + + if (facility < MetisLoggerFacility_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(); + trapUnexpectedStateIf(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); + assertTrue(nullfile >= 0, "Error opening file '%s': (%d) %s", devnull, errno, strerror(errno)); + + int ret; + ret = dup(nullfile); + assertTrue(ret == 1, "Error duping fd 1 got %d file: (%d) %s", ret, errno, strerror(errno)); + ret = dup(nullfile); + assertTrue(ret == 2, "Error duping fd 2, got %d file: (%d) %s", ret, errno, strerror(errno)); + + // metisForwarder will capture signals +} + +static MetisLogger * +_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); + + MetisLogger *logger = metisLogger_Create(reporter, parcClock_Wallclock()); + + parcOutputStream_Release(&pos); + parcLogReporter_Release(&reporter); + + return logger; +} + +int +main(int argc, const char *argv[]) +{ + header(); + + 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[MetisLoggerFacility_END]; + for (int i = 0; i < MetisLoggerFacility_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(); + } + + MetisLogger *logger = NULL; + if (logfile) { + logger = _createLogfile(logfile); + parcMemory_Deallocate((void **) &logfile); + } else { + PARCLogReporter *stdoutReporter = parcLogReporterTextStdout_Create(); + logger = metisLogger_Create(stdoutReporter, parcClock_Wallclock()); + parcLogReporter_Release(&stdoutReporter); + } + + for (int i = 0; i < MetisLoggerFacility_END; i++) { + if (logLevelArray[i] > -1) { + metisLogger_SetLogLevel(logger, i, logLevelArray[i]); + } + } + + + // this will update the clock to the tick clock + MetisForwarder *metis = metisForwarder_Create(logger); + + MetisConfiguration *configuration = metisForwarder_GetConfiguration(metis); + + if (capacity > -1) { + metisConfiguration_SetObjectStoreSize(configuration, capacity); + } + + metisConfiguration_StartCLI(configuration, configurationPort); + + if (configFileName) { + metisForwarder_SetupFromConfigFile(metis, configFileName); + } else { + // NULL to not setup AF_UNIX + metisForwarder_SetupAllListeners(metis, port, NULL); + } + + MetisDispatcher *dispatcher = metisForwarder_GetDispatcher(metis); + + metisLogger_Log(logger, MetisLoggerFacility_Core, PARCLogLevel_Alert, "daemon", "metis running port %d configuration-port %d", port, configurationPort); + + metisDispatcher_Run(dispatcher); + + metisLogger_Log(logger, MetisLoggerFacility_Core, PARCLogLevel_Alert, "daemon", "metis exiting port %d", port); + + metisForwarder_Destroy(&metis); + + sleep(2); + + metisLogger_Release(&logger); + return 0; +} |