Command line: Follow IEEE Std 1003.1, 2013 Edition
Standardize command line options: http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap12.html Make command line options maintainable: - Use getopts for parsing. - Define options in a list of structs. - Compute optstring and help message from the struct. Changed command line options are marked deprecated. We can remove them in the next major release.
Simon Nagl committed
May 10, 2016 at 00:27 UTC
a48fb7d4951c2541e713da4b9272db02f1a57abc
6 files changed
+214
-63
netdata-installer.sh
+1
-1
@@ -566,7 +566,7 @@ echo >&2
566
# run netdata
567
568
echo >&2 "Starting netdata..."
569
-run ${NETDATA_PREFIX}/usr/sbin/netdata -pidfile ${NETDATA_RUN_DIR}/netdata.pid "${@}"
569
+run ${NETDATA_PREFIX}/usr/sbin/netdata -P ${NETDATA_RUN_DIR}/netdata.pid "${@}"
570
571
if [ $? -ne 0 ]
572
then
src/main.c
+186
-60
@@ -1,40 +1,38 @@
1
#ifdef HAVE_CONFIG_H
2
#include <config.h>
3
#endif
4
+#include <errno.h>
5
+#include <getopt.h>
6
+#include <pthread.h>
7
+#include <signal.h>
8
#include <stdio.h>
9
#include <stdlib.h>
6
-#include <unistd.h>
7
-#include <signal.h>
8
-#include <syslog.h>
9
-#include <pthread.h>
10
#include <string.h>
11
-#include <errno.h>
12
-#include <sys/types.h>
13
-#include <sys/wait.h>
14
-#include <sys/time.h>
15
-#include <sys/resource.h>
11
#include <sys/mman.h>
12
#include <sys/prctl.h>
13
+#include <sys/wait.h>
14
+#include <syslog.h>
15
+#include <unistd.h>
16
17
+#include "appconfig.h"
18
#include "common.h"
20
-#include "log.h"
19
#include "daemon.h"
22
-#include "web_server.h"
20
+#include "log.h"
21
#include "popen.h"
24
-#include "appconfig.h"
25
-#include "web_client.h"
22
#include "rrd.h"
23
#include "rrd2json.h"
24
+#include "web_client.h"
25
+#include "web_server.h"
26
27
#include "unit_test.h"
28
31
-#include "plugins_d.h"
32
-#include "plugin_idlejitter.h"
33
-#include "plugin_tc.h"
29
#include "plugin_checks.h"
35
-#include "plugin_proc.h"
30
+#include "plugin_idlejitter.h"
31
#include "plugin_nfacct.h"
32
#include "registry.h"
33
+#include "plugin_proc.h"
34
+#include "plugin_tc.h"
35
+#include "plugins_d.h"
36
37
#include "main.h"
38
@@ -192,6 +190,60 @@ void kill_childs()
190
debug(D_EXIT, "All threads/childs stopped.");
191
}
192
193
+struct option_def options[] = {
194
+ // opt description arg name default value
195
+ {'c', "Load alternate configuration file", "config_file", CONFIG_DIR "/" CONFIG_FILENAME},
196
+ {'D', "Disable fork into background", NULL, NULL},
197
+ {'h', "Display help message", NULL, NULL},
198
+ {'P', "File to save a pid while running", "FILE", NULL},
199
+ {'p', "Port to listen. Can be from 1 to 65535.", "port_number", "19999"},
200
+ {'s', "Path to access host /proc and /sys when running in a container.", "PATH", NULL},
201
+ {'t', "The frequency in seconds, for data collection. \
202
+ Same as 'update every' config file option.", "seconds", "1"},
203
+ {'l', "The number of entries the netdata daemon will keep in memory \
204
+ for each chart dimension. Same as 'history config file option.", "lines_to_save", NULL},
205
+ {'u', "System username to run as.", "username", "netdata"},
206
+ {'v', "Version of the program", NULL, NULL},
207
+ {'W', "vendor options.", "stacksize=<size>|unittest|debug_flag", NULL},
208
+};
209
+
210
+void help(int exitcode) {
211
+ FILE *stream;
212
+ if(exitcode == 0)
213
+ stream = stdout;
214
+ else
215
+ stream = stderr;
216
+
217
+ int num_opts = sizeof(options) / sizeof(struct option_def);
218
+ int i;
219
+ int max_len_arg = 0;
220
+
221
+ // Compute maximum argument length
222
+ for( i = 0; i < num_opts; i++ ) {
223
+ if(options[i].arg_name) {
224
+ int len_arg = strlen(options[i].arg_name);
225
+ if(len_arg > max_len_arg) max_len_arg = len_arg;
226
+ }
227
+ }
228
+
229
+ fprintf(stream, "SYNOPSIS: netdata [options]\n");
230
+ fprintf(stream, "\n");
231
+ fprintf(stream, "Options:\n");
232
+
233
+ // Output options description.
234
+ for( i = 0; i < num_opts; i++ ) {
235
+ fprintf(stream, " -%c %-*s %s", options[i].val, max_len_arg, options[i].arg_name ? options[i].arg_name : "", options[i].description);
236
+ if(options[i].default_value) {
237
+ fprintf(stream, " Default: %s\n", options[i].default_value);
238
+ } else {
239
+ fprintf(stream, "\n");
240
+ }
241
+ }
242
+
243
+ fflush(stream);
244
+ exit(exitcode);
245
+}
246
+
247
248
int main(int argc, char **argv)
249
{
@@ -207,53 +259,127 @@ int main(int argc, char **argv)
259
// set the name for logging
260
program_name = "netdata";
261
210
- // parse the arguments
211
- for(i = 1; i < argc ; i++) {
212
- if(strcmp(argv[i], "-c") == 0 && (i+1) < argc) {
213
- if(load_config(argv[i+1], 1) != 1) {
214
- error("Cannot load configuration file %s.", argv[i+1]);
215
- exit(1);
262
+ // parse command line.
263
+
264
+ // parse depercated options
265
+ // TODO: Remove this block with the next major release.
266
+ {
267
+ void remove_option(int opt_index, int *argc, char **argv) {
268
+ int i = opt_index;
269
+ // remove the options.
270
+ do {
271
+ *argc = *argc - 1;
272
+ for(i = opt_index; i < *argc; i++) {
273
+ argv[i] = argv[i+1];
274
+ }
275
+ i = opt_index;
276
+ } while(argv[i][0] != '-' && opt_index >= *argc);
277
+ }
278
+
279
+ i = 1;
280
+ while(i < argc) {
281
+ if(strcmp(argv[i], "-pidfile") == 0 && (i+1) < argc) {
282
+ strncpyz(pidfile, argv[i+1], FILENAME_MAX);
283
+ fprintf(stderr, "%s: deprecate option -- %s -- please use -P instead.\n", argv[0], argv[i]);
284
+ remove_option(i, &argc, argv);
285
}
217
- else {
218
- debug(D_OPTIONS, "Configuration loaded from %s.", argv[i+1]);
219
- config_loaded = 1;
286
+ else if(strcmp(argv[i], "-nodaemon") == 0 || strcmp(argv[i], "-nd") == 0) {
287
+ dont_fork = 1;
288
+ fprintf(stderr, "%s: deprecate option -- %s -- please use -D instead.\n ", argv[0], argv[i]);
289
+ remove_option(i, &argc, argv);
290
}
221
- i++;
222
- }
223
- else if(strcmp(argv[i], "-df") == 0 && (i+1) < argc) { config_set("global", "debug flags", argv[i+1]); debug_flags = strtoull(argv[i+1], NULL, 0); i++; }
224
- else if(strcmp(argv[i], "-p") == 0 && (i+1) < argc) { config_set("global", "port", argv[i+1]); i++; }
225
- else if(strcmp(argv[i], "-u") == 0 && (i+1) < argc) { config_set("global", "run as user", argv[i+1]); i++; }
226
- else if(strcmp(argv[i], "-l") == 0 && (i+1) < argc) { config_set("global", "history", argv[i+1]); i++; }
227
- else if(strcmp(argv[i], "-t") == 0 && (i+1) < argc) { config_set("global", "update every", argv[i+1]); i++; }
228
- else if(strcmp(argv[i], "-ch") == 0 && (i+1) < argc) { config_set("global", "host access prefix", argv[i+1]); i++; }
229
- else if(strcmp(argv[i], "-stacksize") == 0 && (i+1) < argc) { config_set("global", "pthread stack size", argv[i+1]); i++; }
230
- else if(strcmp(argv[i], "-nodaemon") == 0 || strcmp(argv[i], "-nd") == 0) dont_fork = 1;
231
- else if(strcmp(argv[i], "-pidfile") == 0 && (i+1) < argc) {
232
- i++;
233
- strncpyz(pidfile, argv[i], FILENAME_MAX);
234
- }
235
- else if(strcmp(argv[i], "--unittest") == 0) {
236
- rrd_update_every = 1;
237
- if(run_all_mockup_tests()) exit(1);
238
- if(unit_test_storage()) exit(1);
239
- fprintf(stderr, "\n\nALL TESTS PASSED\n\n");
240
- exit(0);
291
+ else if(strcmp(argv[i], "-ch") == 0 && (i+1) < argc) {
292
+ config_set("global", "host access prefix", argv[i+1]);
293
+ fprintf(stderr, "%s: deprecate option -- %s -- please use -s instead.\n", argv[0], argv[i]);
294
+ remove_option(i, &argc, argv);
295
+ }
296
+ else i++;
297
}
242
- else {
243
- fprintf(stderr, "Cannot understand option '%s'.\n", argv[i]);
244
- fprintf(stderr, "\nUSAGE: %s [-d] [-l LINES_TO_SAVE] [-u UPDATE_TIMER] [-p LISTEN_PORT] [-df debug flags].\n\n", argv[0]);
245
- fprintf(stderr, " -c CONFIG FILE the configuration file to load. Default: %s.\n", CONFIG_DIR "/" CONFIG_FILENAME);
246
- fprintf(stderr, " -l LINES_TO_SAVE can be from 5 to %d lines in JSON data. Default: %d.\n", RRD_HISTORY_ENTRIES_MAX, RRD_DEFAULT_HISTORY_ENTRIES);
247
- fprintf(stderr, " -t UPDATE_TIMER can be from 1 to %d seconds. Default: %d.\n", UPDATE_EVERY_MAX, UPDATE_EVERY);
248
- fprintf(stderr, " -p LISTEN_PORT can be from 1 to %d. Default: %d.\n", 65535, LISTEN_PORT);
249
- fprintf(stderr, " -u USERNAME can be any system username to run as. Default: none.\n");
250
- fprintf(stderr, " -ch path to access host /proc and /sys when running in a container. Default: empty.\n");
251
- fprintf(stderr, " -nd or -nodeamon to disable forking in the background. Default: unset.\n");
252
- fprintf(stderr, " -df FLAGS debug options. Default: 0x%08llx.\n", debug_flags);
253
- fprintf(stderr, " -stacksize BYTES to overwrite the pthread stack size.\n");
254
- fprintf(stderr, " -pidfile FILENAME to save a pid while running.\n");
255
- exit(1);
298
+ }
299
+
300
+ // parse options
301
+ {
302
+ int num_opts = sizeof(options) / sizeof(struct option_def);
303
+ char optstring[(num_opts * 2) + 1];
304
+
305
+ int string_i = 0;
306
+ for( i = 0; i < num_opts; i++ ) {
307
+ optstring[string_i] = options[i].val;
308
+ string_i++;
309
+ if(options[i].arg_name) {
310
+ optstring[string_i] = ':';
311
+ string_i++;
312
+ }
313
}
314
+
315
+ int opt;
316
+ while( (opt = getopt(argc, argv, optstring)) != -1 ) {
317
+ switch(opt) {
318
+ case 'c':
319
+ if(load_config(optarg, 1) != 1) {
320
+ error("Cannot load configuration file %s.", optarg);
321
+ exit(1);
322
+ }
323
+ else {
324
+ debug(D_OPTIONS, "Configuration loaded from %s.", optarg);
325
+ config_loaded = 1;
326
+ }
327
+ break;
328
+ case 'D':
329
+ dont_fork = 1;
330
+ break;
331
+ case 'h':
332
+ help(0);
333
+ break;
334
+ case 'P':
335
+ strncpy(pidfile, optarg, FILENAME_MAX);
336
+ pidfile[FILENAME_MAX] = '\0';
337
+ break;
338
+ case 'p':
339
+ config_set("global", "port", optarg);
340
+ break;
341
+ case 's':
342
+ config_set("global", "host access prefix", optarg);
343
+ break;
344
+ case 't':
345
+ config_set("global", "update every", optarg);
346
+ break;
347
+ case 'u':
348
+ config_set("global", "run as user", optarg);
349
+ break;
350
+ case 'v':
351
+ // TODO: Outsource version to makefile which can compute version from git.
352
+ printf("netdata 1.1.0\n");
353
+ return 0;
354
+ break;
355
+ case 'l':
356
+ config_set("global", "history", optarg);
357
+ break;
358
+ case 'W':
359
+ {
360
+ char* stacksize = "stacksize=";
361
+ char* debug_flags_string = "debug_flags=";
362
+ if(strcmp(optarg, "unittest") == 0) {
363
+ rrd_update_every = 1;
364
+ if(run_all_mockup_tests()) exit(1);
365
+ if(unit_test_storage()) exit(1);
366
+ fprintf(stderr, "\n\nALL TESTS PASSED\n\n");
367
+ exit(0);
368
+ } else if(strncmp(optarg, stacksize, strlen(stacksize)) == 0) {
369
+ optarg += strlen(stacksize);
370
+ config_set("global", "pthread stack size", optarg);
371
+ } else if(strncmp(optarg, debug_flags_string, strlen(debug_flags_string)) == 0) {
372
+ optarg += strlen(debug_flags_string);
373
+ config_set("global", "debug flags", optarg);
374
+ debug_flags = strtoull(optarg, NULL, 0);
375
+ }
376
+ }
377
+ break;
378
+ default: /* ? */
379
+ help(1);
380
+ break;
381
+ }
382
+ }
383
}
384
385
if(!config_loaded) load_config(NULL, 0);
src/main.h
+23
@@ -1,3 +1,5 @@
1
+#include <getopt.h>
2
+
3
#ifndef NETDATA_MAIN_H
4
#define NETDATA_MAIN_H 1
5
@@ -5,6 +7,27 @@
7
8
extern volatile sig_atomic_t netdata_exit;
9
10
+/**
11
+ * This struct contains information about command line options.
12
+ */
13
+struct option_def {
14
+ /** The option character */
15
+ const char val;
16
+ /** The name of the long option. */
17
+ const char *description;
18
+ /** Short descripton what the option does */
19
+ /** Name of the argument displayed in SYNOPSIS */
20
+ const char *arg_name;
21
+ /** Default value if not set */
22
+ const char *default_value;
23
+};
24
+
25
+/**
26
+ * List of command line options.
27
+ * This can be used to compute manpage, help messages, ect.
28
+ */
29
+extern struct option_def options[];
30
+
31
extern void kill_childs(void);
32
extern int killpid(pid_t pid, int signal);
33
extern void netdata_cleanup_and_exit(int ret);
src/storage_number.h
+2
@@ -1,3 +1,5 @@
1
+#include <stdint.h>
2
+
3
#ifndef NETDATA_STORAGE_NUMBER_H
4
#define NETDATA_STORAGE_NUMBER_H
5
system/netdata-init-d.in
+1
-1
@@ -14,7 +14,7 @@
14
DAEMON="netdata"
15
DAEMON_PATH=@sbindir_POST@
16
PIDFILE=@localstatedir_POST@/$DAEMON.pid
17
-DAEMONOPTS="-pidfile $PIDFILE"
17
+DAEMONOPTS="-P $PIDFILE"
18
STOP_TIMEOUT="10"
19
20
service_start()
system/netdata-openrc.in
+1
-1
@@ -25,7 +25,7 @@ extra_started_commands="getconf"
25
pidfile="/run/netdata.pid"
26
command="${NETDATA_INSTALL_PATH}/usr/sbin/netdata"
27
command_background="yes"
28
-command_args="-pidfile ${pidfile} ${NETDATA_EXTRA_ARGS}"
28
+command_args="-P ${pidfile} ${NETDATA_EXTRA_ARGS}"
29
# start_stop_daemon_args="-u ${NETDATA_OWNER}"
30
start_stop_daemon_args=""
31