aboutsummaryrefslogtreecommitdiffstats
path: root/metis/ccnx/forwarder/metis/command-line
diff options
context:
space:
mode:
Diffstat (limited to 'metis/ccnx/forwarder/metis/command-line')
-rw-r--r--metis/ccnx/forwarder/metis/command-line/CMakeLists.txt3
-rw-r--r--metis/ccnx/forwarder/metis/command-line/metis_control/CMakeLists.txt4
-rw-r--r--metis/ccnx/forwarder/metis/command-line/metis_control/metisControl_main.c251
-rw-r--r--metis/ccnx/forwarder/metis/command-line/metis_daemon/CMakeLists.txt4
-rw-r--r--metis/ccnx/forwarder/metis/command-line/metis_daemon/metis_daemon.c319
-rw-r--r--metis/ccnx/forwarder/metis/command-line/test/test_metis-control.c68
6 files changed, 649 insertions, 0 deletions
diff --git a/metis/ccnx/forwarder/metis/command-line/CMakeLists.txt b/metis/ccnx/forwarder/metis/command-line/CMakeLists.txt
new file mode 100644
index 00000000..ac383008
--- /dev/null
+++ b/metis/ccnx/forwarder/metis/command-line/CMakeLists.txt
@@ -0,0 +1,3 @@
+add_subdirectory(metis_control)
+add_subdirectory(metis_daemon)
+#add_subdirectory(test)
diff --git a/metis/ccnx/forwarder/metis/command-line/metis_control/CMakeLists.txt b/metis/ccnx/forwarder/metis/command-line/metis_control/CMakeLists.txt
new file mode 100644
index 00000000..e07d5f40
--- /dev/null
+++ b/metis/ccnx/forwarder/metis/command-line/metis_control/CMakeLists.txt
@@ -0,0 +1,4 @@
+add_executable(metis_control metisControl_main.c)
+target_link_libraries(metis_control ${METIS_LINK_LIBRARIES})
+
+install(TARGETS metis_control RUNTIME DESTINATION bin)
diff --git a/metis/ccnx/forwarder/metis/command-line/metis_control/metisControl_main.c b/metis/ccnx/forwarder/metis/command-line/metis_control/metisControl_main.c
new file mode 100644
index 00000000..067ed9c9
--- /dev/null
+++ b/metis/ccnx/forwarder/metis/command-line/metis_control/metisControl_main.c
@@ -0,0 +1,251 @@
+/*
+ * 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 <strings.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <getopt.h>
+
+#include <LongBow/runtime.h>
+#include <string.h>
+
+#include <parc/security/parc_Security.h>
+#include <parc/security/parc_IdentityFile.h>
+
+#include <parc/algol/parc_Memory.h>
+#include <parc/algol/parc_SafeMemory.h>
+#include <parc/algol/parc_List.h>
+#include <parc/algol/parc_ArrayList.h>
+
+#include <ccnx/api/control/cpi_ManageLinks.h>
+
+#include <ccnx/api/control/cpi_Forwarding.h>
+#include <ccnx/forwarder/metis/core/metis_Forwarder.h>
+#include <ccnx/forwarder/metis/core/metis_Dispatcher.h>
+
+#include <ccnx/api/ccnx_Portal/ccnx_Portal.h>
+#include <ccnx/api/ccnx_Portal/ccnx_PortalRTA.h>
+
+#include <ccnx/common/ccnx_KeystoreUtilities.h>
+#include <ccnx/transport/common/transport_MetaMessage.h>
+
+#include <ccnx/forwarder/metis/metis_About.h>
+#include <ccnx/forwarder/metis/config/metis_ControlState.h>
+#include <ccnx/forwarder/metis/config/metisControl_Root.h>
+
+#include <errno.h>
+
+typedef struct metis_control_main_state {
+ KeystoreParams *keystoreParams;
+ CCNxPortal *controlPortal;
+ MetisControlState *controlState;
+} MetisControlMainState;
+
+static void
+_displayForwarderLogo(void)
+{
+ printf("%s\n", metisAbout_About());
+
+ printf(" __ __ _ _\n");
+ printf(" | \\/ | ___ | |_ (_) ___\n");
+ printf(" | |\\/| | / _ \\| __|| |/ __|\n");
+ printf(" | | | || __/| |_ | |\\__ \\\n");
+ printf(" |_| |_| \\___| \\__||_||___/\n");
+
+ printf("\n");
+}
+
+static void
+_displayUsage(char *programName)
+{
+ printf("Usage: %s -h\n", programName);
+ printf(" %s [--k|--keystore <keystore file name>] [--p|--password <keystore password>] [commands]\n", programName);
+ 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_control is the program to configure the forwarder, metis_daemon.\n");
+ printf("\n");
+ printf("Options:\n");
+ printf("-h = This help screen\n");
+ printf("-k | --keystore = Specify the path of the PKCS12 keystore (default ~/.ccnx/.ccnx_keystore.p12)\n");
+ printf("-p | --password = keystore password (default to prompt user)\n");
+ printf("commands = configuration line to send to metis (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;
+}
+
+static CCNxMetaMessage *
+_writeAndReadMessage(void *mainStatePtr, CCNxMetaMessage *msg)
+{
+ assertNotNull(mainStatePtr, "Parameter mainStatePtr must be non-null");
+ assertNotNull(msg, "Parameter msg must be non-null");
+
+ MetisControlMainState *mainState = mainStatePtr;
+
+ if (ccnxPortal_Send(mainState->controlPortal, msg, CCNxStackTimeout_Never)) {
+ CCNxMetaMessage *result = ccnxPortal_Receive(mainState->controlPortal, CCNxStackTimeout_Never);
+ assertTrue(result != NULL, "Error reading response from Portal: (%d) %s\nb", errno, strerror(errno));
+
+ return result;
+ }
+
+ return NULL;
+}
+
+static CCNxPortal *
+_createPortalWithKeystore(const char *keystoreName, const char *keystorePassword)
+{
+ PARCIdentityFile *identityFile = parcIdentityFile_Create(keystoreName, keystorePassword);
+ PARCIdentity *identity = parcIdentity_Create(identityFile, PARCIdentityFileAsPARCIdentity);
+ CCNxPortalFactory *portalFactory = ccnxPortalFactory_Create(identity);
+
+ CCNxPortal *result = ccnxPortalFactory_CreatePortal(portalFactory, ccnxPortalRTA_Message);
+
+ ccnxPortalFactory_Release(&portalFactory);
+ parcIdentity_Release(&identity);
+ parcIdentityFile_Release(&identityFile);
+
+ return result;
+}
+
+static MetisControlMainState
+_openKeyStore(char *keystorePath, char *keystorePassword)
+{
+ MetisControlMainState mainState;
+
+ if (keystorePassword == NULL) {
+ keystorePassword = ccnxKeystoreUtilities_ReadPassword();
+
+ mainState.keystoreParams = ccnxKeystoreUtilities_OpenFile(keystorePath, keystorePassword);
+ parcMemory_Deallocate((void *) &keystorePassword);
+ } else {
+ mainState.keystoreParams = ccnxKeystoreUtilities_OpenFile(keystorePath, keystorePassword);
+ }
+
+ return mainState;
+}
+
+int
+main(int argc, char *argv[])
+{
+ _displayForwarderLogo();
+
+ if (argc == 2 && strcmp("-h", argv[1]) == 0) {
+ _displayUsage(argv[0]);
+ exit(EXIT_SUCCESS);
+ }
+
+ char *keystorePath = NULL;
+ char *keystorePassword = NULL;
+
+ PARCList *commands = parcList(parcArrayList_Create(NULL), PARCArrayListAsPARCList);
+
+ // Extract optional keystore and password, and optional commands.
+ if (!_parseArgs(argc, argv, &keystorePath, &keystorePassword, commands)) {
+ parcList_Release(&commands);
+ exit(EXIT_FAILURE);
+ }
+
+ if (keystorePath == NULL) {
+ printf("No keystore specified. Will try default.\n");
+ } else {
+ printf("Using keystore: %s\n", keystorePath);
+ }
+
+ parcSecurity_Init();
+
+ MetisControlMainState mainState = _openKeyStore(keystorePath, keystorePassword);
+ if (mainState.keystoreParams == NULL) {
+ printf("Could not open keystore '%s'\n", keystorePath == NULL ? "~/.ccnx/.ccnx_keystore.p12" : keystorePath);
+ exit(EXIT_FAILURE);
+ }
+
+ mainState.controlPortal = _createPortalWithKeystore(ccnxKeystoreUtilities_GetFileName(mainState.keystoreParams),
+ ccnxKeystoreUtilities_GetPassword(mainState.keystoreParams));
+ parcSecurity_Fini();
+
+ mainState.controlState = metisControlState_Create(&mainState, _writeAndReadMessage);
+
+ metisControlState_RegisterCommand(mainState.controlState, metisControlRoot_HelpCreate(mainState.controlState));
+ metisControlState_RegisterCommand(mainState.controlState, metisControlRoot_Create(mainState.controlState));
+
+ if (parcList_Size(commands) > 0) {
+ metisControlState_DispatchCommand(mainState.controlState, commands);
+ } else {
+ metisControlState_Interactive(mainState.controlState);
+ }
+
+ parcList_Release(&commands);
+
+ metisControlState_Destroy(&mainState.controlState);
+
+ keystoreParams_Destroy(&mainState.keystoreParams);
+
+ ccnxPortal_Release(&mainState.controlPortal);
+
+ return EXIT_SUCCESS;
+}
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;
+}
diff --git a/metis/ccnx/forwarder/metis/command-line/test/test_metis-control.c b/metis/ccnx/forwarder/metis/command-line/test/test_metis-control.c
new file mode 100644
index 00000000..fd1b4110
--- /dev/null
+++ b/metis/ccnx/forwarder/metis/command-line/test/test_metis-control.c
@@ -0,0 +1,68 @@
+/*
+ * 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 <LongBow/unit-test.h>
+#include <LongBow/debugging.h>
+
+// Include the file(s) containing the functions to be tested.
+// This permits internal static functions to be visible to this Test Framework.
+#include "../metis-control/metisControl_main.c"
+
+LONGBOW_TEST_RUNNER(test_metis_control)
+{
+ // The following Test Fixtures will run their corresponding Test Cases.
+ // Test Fixtures are run in the order specified, but all tests should be idempotent.
+ // Never rely on the execution order of tests or share state between them.
+ LONGBOW_RUN_TEST_FIXTURE(Global);
+}
+
+// The Test Runner calls this function once before any Test Fixtures are run.
+LONGBOW_TEST_RUNNER_SETUP(test_metis_control)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+// The Test Runner calls this function once after all the Test Fixtures are run.
+LONGBOW_TEST_RUNNER_TEARDOWN(test_metis_control)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_FIXTURE(Global)
+{
+ LONGBOW_RUN_TEST_CASE(Global, myTest);
+}
+
+LONGBOW_TEST_FIXTURE_SETUP(Global)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_FIXTURE_TEARDOWN(Global)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_CASE(Global, myTest)
+{
+}
+
+int
+main(int argc, char *argv[])
+{
+ LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(test_metis_control);
+ int exitStatus = longBowMain(argc, argv, testRunner, NULL);
+ longBowTestRunner_Destroy(&testRunner);
+ exit(exitStatus);
+}