From 2d7ecfc7640be82d537439b97acf51adf08bdcbb Mon Sep 17 00:00:00 2001 From: eeff <33566201+eeff@users.noreply.github.com> Date: Fri, 8 Apr 2022 10:23:06 +0800 Subject: [PATCH] Refactor neuron command line argument parser into separate module --- CMakeLists.txt | 1 + src/argparse.c | 133 +++++++++++++++++++++++++++++++++++++++++++++++++ src/argparse.h | 46 +++++++++++++++++ src/main.c | 98 ++++-------------------------------- 4 files changed, 189 insertions(+), 89 deletions(-) create mode 100644 src/argparse.c create mode 100644 src/argparse.h diff --git a/CMakeLists.txt b/CMakeLists.txt index dd708500..36ee5f7a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -94,6 +94,7 @@ set(PERSIST_SOURCES src/persist/json/persist_json_subs.c) set(NEURON_SOURCES src/main.c + src/argparse.c src/config.c src/core/message.c src/core/databuf.c diff --git a/src/argparse.c b/src/argparse.c new file mode 100644 index 00000000..870b4359 --- /dev/null +++ b/src/argparse.c @@ -0,0 +1,133 @@ +/** + * NEURON IIoT System for Industry 4.0 + * Copyright (C) 2020-2022 EMQ Technologies Co., Ltd All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + **/ + +#include +#include +#include +#include +#include + +#include "argparse.h" + +#define OPTIONAL_ARGUMENT_IS_PRESENT \ + ((optarg == NULL && optind < argc && argv[optind][0] != '-') \ + ? (bool) (optarg = argv[optind++]) \ + : (optarg != NULL)) + +#define STRDUP(var, str) \ + do { \ + (var) = strdup(str); \ + if (NULL == (var)) { \ + fprintf( \ + stderr, \ + "Neuron argument parser fail strdup string: %s, reason: %s\n", \ + (str), strerror(errno)); \ + goto quit; \ + } \ + } while (0) + +// clang-format off +const char *usage_text = +"USAGE:\n" +" neuron [OPTIONS]\n\n" +"OPTIONS:\n" +" -c, --config neuron configuration file\n" +" without this option, default from ./config/neuron.yaml\n" +" -d, --daemon run as daemon process\n" +" -h, --help show this help message\n" +" --log [FILE] log to the given file, or stdout if no FILE argument\n" +" without this option, default log to ./logs/neuron.log\n" +"\n"; +// clang-format on + +static inline void usage() +{ + fprintf(stderr, "%s", usage_text); +} + +void neu_cli_args_init(neu_cli_args_t *args, int argc, char *argv[]) +{ + char * opts = "c:dh"; + struct option long_options[] = { + { "help", no_argument, NULL, 1 }, + { "config", required_argument, NULL, 'c' }, + { "daemon", no_argument, NULL, 'd' }, + { "log", optional_argument, NULL, 'l' }, + { NULL, 0, NULL, 0 }, + }; + + memset(args, 0, sizeof(*args)); + + int c = 0; + while ((c = getopt_long(argc, argv, opts, long_options, NULL)) != -1) { + switch (c) { + case 'h': + usage(); + exit(0); + case 'd': + args->daemonized = true; + break; + case ':': + fprintf(stderr, "%s: option '-%c' requires an argument\n", argv[0], + optopt); + usage(); + goto quit; + case 'c': + STRDUP(args->conf_file, optarg); + break; + case 'l': + if (OPTIONAL_ARGUMENT_IS_PRESENT) { + // log to file + STRDUP(args->log_file, optarg); + } else { + // log to stdout + STRDUP(args->log_file, NEU_LOG_STDOUT_FNAME); + } + break; + case '?': + default: + fprintf(stderr, "%s: option '-%c' is invalid: ignored\n", argv[0], + optopt); + usage(); + goto quit; + } + } + + if (NULL == args->conf_file) { + STRDUP(args->conf_file, "./config/neuron.yaml"); + } + + if (NULL == args->log_file) { + STRDUP(args->log_file, "./logs/neuron.log"); + } + + return; + +quit: + neu_cli_args_fini(args); + exit(1); +} + +void neu_cli_args_fini(neu_cli_args_t *args) +{ + free(args->conf_file); + args->conf_file = NULL; + free(args->log_file); + args->log_file = NULL; +} diff --git a/src/argparse.h b/src/argparse.h new file mode 100644 index 00000000..5bc96c37 --- /dev/null +++ b/src/argparse.h @@ -0,0 +1,46 @@ +/** + * NEURON IIoT System for Industry 4.0 + * Copyright (C) 2020-2022 EMQ Technologies Co., Ltd All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + **/ + +#define NEU_LOG_STDOUT_FNAME "/dev/stdout" + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +/** Neuron command line arguments. + */ +typedef struct { + bool daemonized; // flag indicating whether to run as daemon process + char *conf_file; // configuration file path + char *log_file; // log file path +} neu_cli_args_t; + +/** Parse command line arguments. + */ +void neu_cli_args_init(neu_cli_args_t *args, int argc, char *argv[]); + +/** Clean up the command line arguments. + */ +void neu_cli_args_fini(neu_cli_args_t *args); + +#ifdef __cplusplus +} +#endif diff --git a/src/main.c b/src/main.c index 0010e65e..a40e6e33 100644 --- a/src/main.c +++ b/src/main.c @@ -28,6 +28,7 @@ #include #include +#include "argparse.h" #include "config.h" #include "core/message.h" #include "core/neu_manager.h" @@ -36,33 +37,12 @@ #include "neu_panic.h" #include "restful/rest.h" -#define STDOUT_FILE "/dev/stdout" - -#define OPTIONAL_ARGUMENT_IS_PRESENT \ - ((optarg == NULL && optind < argc && argv[optind][0] != '-') \ - ? (bool) (optarg = argv[optind++]) \ - : (optarg != NULL)) - static neu_manager_t *manager = NULL; static nng_socket main_sock = { 0 }; static nng_mtx *log_mtx; FILE * g_logfile; -// clang-format off -const char *usage_text = -"USAGE:\n" -" neuron [OPTIONS]\n\n" -"OPTIONS:\n" -" -c, --config neuron configuration file\n" -" without this option, default from ./config/neuron.yaml\n" -" -d, --daemon run as daemon process\n" -" -h, --help show this help message\n" -" --log [FILE] log to the given file, or stdout if no FILE argument\n" -" without this option, default log to ./logs/neuron.log\n" -"\n"; -// clang-format on - static void log_lock(bool lock, void *udata) { nng_mtx *mutex = (nng_mtx *) (udata); @@ -78,7 +58,7 @@ static void init(const char *log_file) nng_mtx_alloc(&log_mtx); log_set_lock(log_lock, log_mtx); log_set_level(NEU_LOG_DEBUG); - if (0 == strcmp(log_file, STDOUT_FILE)) { + if (0 == strcmp(log_file, NEU_LOG_STDOUT_FNAME)) { g_logfile = stdout; } else { g_logfile = fopen(log_file, "a"); @@ -99,11 +79,6 @@ static void uninit() nng_mtx_free(log_mtx); } -static void usage() -{ - fprintf(stderr, "%s", usage_text); -} - static int read_neuron_config(const char *config) { return neu_config_init(config); @@ -165,68 +140,14 @@ static void unbind_main_adapter(nng_pipe p, nng_pipe_ev ev, void *arg) int main(int argc, char *argv[]) { + int rv = 0; + neu_cli_args_t args = { 0 }; - int rv = 0; - bool is_daemon = false; - char *config_file = NULL; - char *log_file = NULL; + neu_cli_args_init(&args, argc, argv); - char * opts = "c:dh"; - struct option long_options[] = { - { "help", no_argument, NULL, 1 }, - { "config", required_argument, NULL, 'c' }, - { "daemon", no_argument, NULL, 'd' }, - { "log", optional_argument, NULL, 'l' }, - { NULL, 0, NULL, 0 }, - }; + init(args.log_file); - int c; - - while ((c = getopt_long(argc, argv, opts, long_options, NULL)) != -1) { - switch (c) { - case 'h': - usage(); - exit(0); - case 'd': - is_daemon = true; - break; - case ':': - fprintf(stderr, "%s: option '-%c' requires an argument\n", argv[0], - optopt); - usage(); - exit(0); - case 'c': - config_file = strdup(optarg); - break; - case 'l': - if (OPTIONAL_ARGUMENT_IS_PRESENT) { - // log to file - log_file = strdup(optarg); - } else { - // log to stdout - log_file = strdup(STDOUT_FILE); - } - break; - case '?': - default: - fprintf(stderr, "%s: option '-%c' is invalid: ignored\n", argv[0], - optopt); - usage(); - exit(0); - } - } - - if (config_file == NULL) { - config_file = strdup("./config/neuron.yaml"); - } - - if (log_file == NULL) { - log_file = strdup("./logs/neuron.log"); - } - - init(log_file); - - rv = read_neuron_config(config_file); + rv = read_neuron_config(args.conf_file); if (rv < 0) { log_error("Failed to get neuron configuration."); goto main_end; @@ -236,7 +157,7 @@ int main(int argc, char *argv[]) signal(SIGTERM, sig_handler); signal(SIGABRT, sig_handler); - log_info("running neuron main process, daemon: %d", is_daemon); + log_info("running neuron main process, daemon: %d", args.daemonized); manager = neu_manager_create(); if (manager == NULL) { log_error("Failed to create neuron manager, exit!"); @@ -271,7 +192,6 @@ int main(int argc, char *argv[]) main_end: uninit(); - free(config_file); - free(log_file); + neu_cli_args_fini(&args); return rv; }