Use enums for command-line options
[tinc] / src / tincctl.c
index 9be7d87..a18f6ce 100644 (file)
@@ -41,6 +41,8 @@
 #include "subnet.h"
 #include "keys.h"
 #include "random.h"
+#include "pidfile.h"
+#include "console.h"
 
 #ifndef MSG_NOSIGNAL
 #define MSG_NOSIGNAL 0
@@ -78,15 +80,31 @@ char *device = NULL;
 char *iface = NULL;
 int debug_level = -1;
 
+typedef enum option_t {
+       OPT_BAD_OPTION  = '?',
+       OPT_LONG_OPTION =  0,
+
+       // Short options
+       OPT_BATCH       = 'b',
+       OPT_CONFIG_FILE = 'c',
+       OPT_NETNAME     = 'n',
+
+       // Long options
+       OPT_HELP        = 255,
+       OPT_VERSION,
+       OPT_PIDFILE,
+       OPT_FORCE,
+} option_t;
+
 static struct option const long_options[] = {
-       {"batch", no_argument, NULL, 'b'},
-       {"config", required_argument, NULL, 'c'},
-       {"net", required_argument, NULL, 'n'},
-       {"help", no_argument, NULL, 1},
-       {"version", no_argument, NULL, 2},
-       {"pidfile", required_argument, NULL, 3},
-       {"force", no_argument, NULL, 4},
-       {NULL, 0, NULL, 0}
+       {"batch",   no_argument,       NULL, OPT_BATCH},
+       {"config",  required_argument, NULL, OPT_CONFIG_FILE},
+       {"net",     required_argument, NULL, OPT_NETNAME},
+       {"help",    no_argument,       NULL, OPT_HELP},
+       {"version", no_argument,       NULL, OPT_VERSION},
+       {"pidfile", required_argument, NULL, OPT_PIDFILE},
+       {"force",   no_argument,       NULL, OPT_FORCE},
+       {NULL,      0,                 NULL, 0},
 };
 
 static void version(void) {
@@ -187,47 +205,47 @@ static bool parse_options(int argc, char **argv) {
        int option_index = 0;
 
        while((r = getopt_long(argc, argv, "+bc:n:", long_options, &option_index)) != EOF) {
-               switch(r) {
-               case 0:   /* long option */
+               switch((option_t) r) {
+               case OPT_LONG_OPTION:
                        break;
 
-               case 'b':
+               case OPT_BAD_OPTION:
+                       usage(true);
+                       free_names();
+                       return false;
+
+               case OPT_BATCH:
                        tty = false;
                        break;
 
-               case 'c': /* config file */
+               case OPT_CONFIG_FILE:
                        free(confbase);
                        confbase = xstrdup(optarg);
                        confbasegiven = true;
                        break;
 
-               case 'n': /* net name given */
+               case OPT_NETNAME:
                        free(netname);
                        netname = xstrdup(optarg);
                        break;
 
-               case 1:   /* show help */
+               case OPT_HELP:
                        show_help = true;
                        break;
 
-               case 2:   /* show version */
+               case OPT_VERSION:
                        show_version = true;
                        break;
 
-               case 3:   /* open control socket here */
+               case OPT_PIDFILE:
                        free(pidfilename);
                        pidfilename = xstrdup(optarg);
                        break;
 
-               case 4:   /* force */
+               case OPT_FORCE:
                        force = true;
                        break;
 
-               case '?': /* wrong options */
-                       usage(true);
-                       free_names();
-                       return false;
-
                default:
                        break;
                }
@@ -611,8 +629,9 @@ static void pcap(int fd, FILE *out, uint32_t snaplen) {
        }
 }
 
-static void logcontrol(int fd, FILE *out, int level) {
-       sendline(fd, "%d %d %d", CONTROL, REQ_LOG, level);
+static void log_control(int fd, FILE *out, int level, bool use_color) {
+       sendline(fd, "%d %d %d %d", CONTROL, REQ_LOG, level, use_color);
+
        char data[1024];
        char line[32];
 
@@ -725,9 +744,9 @@ bool connect_tincd(bool verbose) {
                }
        }
 
-       FILE *f = fopen(pidfilename, "r");
+       pidfile_t *pidfile = read_pidfile();
 
-       if(!f) {
+       if(!pidfile) {
                if(verbose) {
                        fprintf(stderr, "Could not open pid file %s: %s\n", pidfilename, strerror(errno));
                }
@@ -735,21 +754,11 @@ bool connect_tincd(bool verbose) {
                return false;
        }
 
-       char host[129];
-       char port[129];
-
-       if(fscanf(f, "%20d %1024s %128s port %128s", &pid, controlcookie, host, port) != 4) {
-               if(verbose) {
-                       fprintf(stderr, "Could not parse pid file %s\n", pidfilename);
-               }
-
-               fclose(f);
-               return false;
-       }
-
-       fclose(f);
+       pid = pidfile->pid;
+       strcpy(controlcookie, pidfile->cookie);
 
 #ifndef HAVE_WINDOWS
+       free(pidfile);
 
        if((pid == 0) || (kill(pid, 0) && (errno == ESRCH))) {
                fprintf(stderr, "Could not find tincd running at pid %d\n", pid);
@@ -800,11 +809,12 @@ bool connect_tincd(bool verbose) {
 
        struct addrinfo *res = NULL;
 
-       if(getaddrinfo(host, port, &hints, &res) || !res) {
+       if(getaddrinfo(pidfile->host, pidfile->port, &hints, &res) || !res) {
                if(verbose) {
-                       fprintf(stderr, "Cannot resolve %s port %s: %s\n", host, port, sockstrerror(sockerrno));
+                       fprintf(stderr, "Cannot resolve %s port %s: %s\n", pidfile->host, pidfile->port, sockstrerror(sockerrno));
                }
 
+               free(pidfile);
                return false;
        }
 
@@ -815,6 +825,7 @@ bool connect_tincd(bool verbose) {
                        fprintf(stderr, "Cannot create TCP socket: %s\n", sockstrerror(sockerrno));
                }
 
+               free(pidfile);
                return false;
        }
 
@@ -828,14 +839,16 @@ bool connect_tincd(bool verbose) {
 
        if(connect(fd, res->ai_addr, res->ai_addrlen) < 0) {
                if(verbose) {
-                       fprintf(stderr, "Cannot connect to %s port %s: %s\n", host, port, sockstrerror(sockerrno));
+                       fprintf(stderr, "Cannot connect to %s port %s: %s\n", pidfile->host, pidfile->port, sockstrerror(sockerrno));
                }
 
+               free(pidfile);
                closesocket(fd);
                fd = -1;
                return false;
        }
 
+       free(pidfile);
        freeaddrinfo(res);
 #endif
 
@@ -960,7 +973,7 @@ static int cmd_start(int argc, char *argv[]) {
        if(!pid) {
                close(pfd[0]);
                char buf[100];
-               snprintf(buf, sizeof(buf), "%d", pfd[1]);
+               snprintf(buf, sizeof(buf), "%d %d", pfd[1], use_ansi_escapes(stderr));
                setenv("TINC_UMBILICAL", buf, true);
                exit(execvp(c, nargv));
        } else {
@@ -1523,7 +1536,8 @@ static int cmd_log(int argc, char *argv[]) {
        signal(SIGINT, sigint_handler);
 #endif
 
-       logcontrol(fd, stdout, argc > 1 ? atoi(argv[1]) : -1);
+       bool use_color = use_ansi_escapes(stdout);
+       log_control(fd, stdout, argc > 1 ? atoi(argv[1]) : DEBUG_UNSET, use_color);
 
 #ifdef SIGINT
        signal(SIGINT, SIG_DFL);
@@ -1720,6 +1734,20 @@ const var_t variables[] = {
        {NULL, 0}
 };
 
+// Request actual port from tincd
+static bool read_actual_port(void) {
+       pidfile_t *pidfile = read_pidfile();
+
+       if(pidfile) {
+               printf("%s\n", pidfile->port);
+               free(pidfile);
+               return true;
+       } else {
+               fprintf(stderr, "Could not get port from the pidfile.\n");
+               return false;
+       }
+}
+
 static int cmd_config(int argc, char *argv[]) {
        if(argc < 2) {
                fprintf(stderr, "Invalid number of arguments.\n");
@@ -1795,6 +1823,11 @@ static int cmd_config(int argc, char *argv[]) {
                action = SET;
        }
 
+       // If port is requested, try reading it from the pidfile and fall back to configs if that fails
+       if(action == GET && !strcasecmp(variable, "Port") && read_actual_port()) {
+               return 0;
+       }
+
        /* Some simple checks. */
        bool found = false;
        bool warnonremove = false;