#include "version.h"
#include "subnet.h"
#include "keys.h"
+#include "random.h"
+#include "pidfile.h"
+#include "console.h"
#ifndef MSG_NOSIGNAL
#define MSG_NOSIGNAL 0
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) {
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;
}
}
}
-#ifdef HAVE_MINGW
+#ifdef HAVE_WINDOWS
if(filename[0] != '\\' && filename[0] != '/' && !strchr(filename, ':')) {
#else
}
}
-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];
// wait for tincd to close the connection...
}
- close(fd);
+ closesocket(fd);
pid = 0;
fd = -1;
return true;
}
-#ifdef HAVE_MINGW
+#ifdef HAVE_WINDOWS
static bool remove_service(void) {
SC_HANDLE manager = NULL;
SC_HANDLE service = NULL;
if(select(fd + 1, &r, NULL, NULL, &tv)) {
fprintf(stderr, "Previous connection to tincd lost, reconnecting.\n");
- close(fd);
+ closesocket(fd);
fd = -1;
} else {
return true;
}
}
- 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));
}
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_MINGW
+#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);
fprintf(stderr, "Cannot connect to UNIX socket %s: %s\n", unixsocketname, sockstrerror(sockerrno));
}
- close(fd);
+ closesocket(fd);
fd = -1;
return false;
}
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;
}
fprintf(stderr, "Cannot create TCP socket: %s\n", sockstrerror(sockerrno));
}
+ free(pidfile);
return false;
}
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));
}
- close(fd);
+ free(pidfile);
+ closesocket(fd);
fd = -1;
return false;
}
+ free(pidfile);
freeaddrinfo(res);
#endif
fprintf(stderr, "Cannot read greeting from control socket: %s\n", sockstrerror(sockerrno));
}
- close(fd);
+ closesocket(fd);
fd = -1;
return false;
}
fprintf(stderr, "Could not fully establish control socket connection\n");
}
- close(fd);
+ closesocket(fd);
fd = -1;
return false;
}
char *c;
char *slash = strrchr(program_name, '/');
-#ifdef HAVE_MINGW
+#ifdef HAVE_WINDOWS
if((c = strrchr(program_name, '\\')) > slash) {
slash = c;
char **nargv = xzalloc((optind + argc) * sizeof(*nargv));
char *arg0 = c;
-#ifdef HAVE_MINGW
+#ifdef HAVE_WINDOWS
/*
Windows has no real concept of an "argv array". A command line is just one string.
The CRT of the new process will decode the command line string to generate argv before calling main(), and (by convention)
nargv[nargc++] = argv[i];
}
-#ifdef HAVE_MINGW
+#ifdef HAVE_WINDOWS
int status = spawnvp(_P_WAIT, c, nargv);
free(nargv);
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 {
return 1;
}
-#ifdef HAVE_MINGW
+#ifdef HAVE_WINDOWS
return remove_service() ? EXIT_SUCCESS : EXIT_FAILURE;
#else
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);
#endif
- close(fd);
+ closesocket(fd);
fd = -1;
return 0;
}
return NULL;
}
-ecdsa_t *get_pubkey(FILE *f) {
+static ecdsa_t *get_pubkey(FILE *f) ATTR_MALLOC ATTR_DEALLOCATOR(ecdsa_free);
+static ecdsa_t *get_pubkey(FILE *f) {
char buf[4096];
char *value;
{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");
argv--, argc++;
}
- int action = -2;
+ typedef enum { GET, DEL, SET, ADD } action_t;
+ action_t action = GET;
if(!strcasecmp(argv[1], "get")) {
argv++, argc--;
} else if(!strcasecmp(argv[1], "add")) {
- argv++, argc--, action = 1;
+ argv++, argc--, action = ADD;
} else if(!strcasecmp(argv[1], "del")) {
- argv++, argc--, action = -1;
+ argv++, argc--, action = DEL;
} else if(!strcasecmp(argv[1], "replace") || !strcasecmp(argv[1], "set") || !strcasecmp(argv[1], "change")) {
- argv++, argc--, action = 0;
+ argv++, argc--, action = SET;
}
if(argc < 2) {
return 1;
}
- if(action >= 0 && !*value) {
+ if((action == SET || action == ADD) && !*value) {
fprintf(stderr, "No value for variable given.\n");
return 1;
}
- if(action < -1 && *value) {
- action = 0;
+ if(action == GET && *value) {
+ 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. */
/* Discourage use of obsolete variables. */
- if(variables[i].type & VAR_OBSOLETE && action >= 0) {
+ if(variables[i].type & VAR_OBSOLETE && (action == SET || action == ADD)) {
if(force) {
fprintf(stderr, "Warning: %s is an obsolete variable!\n", variable);
} else {
/* Don't put server variables in host config files */
- if(node && !(variables[i].type & VAR_HOST) && action >= 0) {
+ if(node && !(variables[i].type & VAR_HOST) && (action == SET || action == ADD)) {
if(force) {
fprintf(stderr, "Warning: %s is not a host configuration variable!\n", variable);
} else {
/* Change "add" into "set" for variables that do not allow multiple occurrences.
Turn on warnings when it seems variables might be removed unintentionally. */
- if(action == 1 && !(variables[i].type & VAR_MULTIPLE)) {
+ if(action == ADD && !(variables[i].type & VAR_MULTIPLE)) {
warnonremove = true;
- action = 0;
- } else if(action == 0 && (variables[i].type & VAR_MULTIPLE)) {
+ action = SET;
+ } else if(action == SET && (variables[i].type & VAR_MULTIPLE)) {
warnonremove = true;
}
}
if(!found) {
- if(force || action < 0) {
+ if(force || action == GET || action == DEL) {
fprintf(stderr, "Warning: %s is not a known configuration variable!\n", variable);
} else {
fprintf(stderr, "%s: is not a known configuration variable! Use --force to use it anyway.\n", variable);
char tmpfile[PATH_MAX];
FILE *tf = NULL;
- if(action >= -1) {
+ if(action != GET) {
if((size_t)snprintf(tmpfile, sizeof(tmpfile), "%s.config.tmp", filename) >= sizeof(tmpfile)) {
fprintf(stderr, "Filename too long: %s.config.tmp\n", filename);
return 1;
// Did it match?
if(!strcasecmp(buf2, variable)) {
- // Get
- if(action < -1) {
+ if(action == GET) {
found = true;
printf("%s\n", bvalue);
- // Del
- } else if(action == -1) {
+ } else if(action == DEL) {
if(!*value || !strcasecmp(bvalue, value)) {
removed = true;
continue;
}
-
- // Set
- } else if(action == 0) {
+ } else if(action == SET) {
// Warn if "set" was used for variables that can occur multiple times
if(warnonremove && strcasecmp(bvalue, value)) {
fprintf(stderr, "Warning: removing %s = %s\n", variable, bvalue);
set = true;
continue;
- // Add
- } else if(action > 0) {
+ } else if(action == ADD) {
// Check if we've already seen this variable with the same value
if(!strcasecmp(bvalue, value)) {
found = true;
}
}
- if(action >= -1) {
+ if(action != GET) {
// Copy original line...
if(fputs(buf1, tf) < 0) {
fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
}
// Add new variable if necessary.
- if((action > 0 && !found) || (action == 0 && !set)) {
+ if((action == ADD && !found) || (action == SET && !set)) {
if(fprintf(tf, "%s = %s\n", variable, value) < 0) {
fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
return 1;
}
}
- if(action < -1) {
+ if(action == GET) {
if(found) {
return 0;
} else {
}
// Could we find what we had to remove?
- if(action < 0 && !removed) {
+ if((action == GET || action == DEL) && !removed) {
remove(tmpfile);
fprintf(stderr, "No configuration variables deleted.\n");
return 1;
}
// Replace the configuration file with the new one
-#ifdef HAVE_MINGW
+#ifdef HAVE_WINDOWS
if(remove(filename)) {
fprintf(stderr, "Error replacing file %s: %s\n", filename, strerror(errno));
check_port(name);
-#ifndef HAVE_MINGW
+#ifndef HAVE_WINDOWS
char filename[PATH_MAX];
snprintf(filename, sizeof(filename), "%s" SLASH "tinc-up", confbase);
}
char *command;
-#ifndef HAVE_MINGW
+#ifndef HAVE_WINDOWS
const char *editor = getenv("VISUAL");
if(!editor) {
}
if(fd >= 0) {
- close(fd);
+ closesocket(fd);
fd = -1;
}
free_names();
}
+static int run_command(int argc, char *argv[]) {
+ if(optind >= argc) {
+ return cmd_shell(argc, argv);
+ }
+
+ for(int i = 0; commands[i].command; i++) {
+ if(!strcasecmp(argv[optind], commands[i].command)) {
+ return commands[i].function(argc - optind, argv + optind);
+ }
+ }
+
+ fprintf(stderr, "Unknown command `%s'.\n", argv[optind]);
+ usage(true);
+ return 1;
+}
+
int main(int argc, char *argv[]) {
program_name = argv[0];
orig_argv = argv;
return 0;
}
-#ifdef HAVE_MINGW
+#ifdef HAVE_WINDOWS
static struct WSAData wsa_state;
if(WSAStartup(MAKEWORD(2, 2), &wsa_state)) {
#endif
gettimeofday(&now, NULL);
+ random_init();
crypto_init();
prng_init();
- if(optind >= argc) {
- return cmd_shell(argc, argv);
- }
+ int result = run_command(argc, argv);
- for(int i = 0; commands[i].command; i++) {
- if(!strcasecmp(argv[optind], commands[i].command)) {
- return commands[i].function(argc - optind, argv + optind);
- }
- }
+ random_exit();
- fprintf(stderr, "Unknown command `%s'.\n", argv[optind]);
- usage(true);
- return 1;
+ return result;
}