#include <getopt.h>
-#include "conf.h"
#include "protocol.h"
#include "xalloc.h"
/* If nonzero, generate public/private keypair for this host/net. */
int generate_keys = 0;
-char *identname = NULL; /* program name for syslog */
-char *pidfilename = NULL; /* pid file location */
-char *controlfilename = NULL; /* pid file location */
-char *confbase = NULL;
+static char *identname = NULL; /* program name for syslog */
+static char *controlsocketname = NULL; /* pid file location */
char *netname = NULL;
-
-static int status;
+char *confbase = NULL;
static struct option const long_options[] = {
{"config", required_argument, NULL, 'c'},
{"net", required_argument, NULL, 'n'},
{"help", no_argument, NULL, 1},
{"version", no_argument, NULL, 2},
- {"pidfile", required_argument, NULL, 5},
+ {"controlsocket", required_argument, NULL, 5},
{NULL, 0, NULL, 0}
};
else {
printf(_("Usage: %s [options] command\n\n"), program_name);
printf(_("Valid options are:\n"
- " -c, --config=DIR Read configuration options from DIR.\n"
- " -n, --net=NETNAME Connect to net NETNAME.\n"
- " --pidfile=FILENAME Write PID to FILENAME.\n"
- " --help Display this help and exit.\n"
- " --version Output version information and exit.\n"
+ " -c, --config=DIR Read configuration options from DIR.\n"
+ " -n, --net=NETNAME Connect to net NETNAME.\n"
+ " --controlsocket=FILENAME Open control socket at FILENAME.\n"
+ " --help Display this help and exit.\n"
+ " --version Output version information and exit.\n"
+ "\n"
"Valid commands are:\n"
" start Start tincd.\n"
" stop Stop tincd.\n"
" restart Restart tincd.\n"
" reload Reload configuration of running tincd.\n"
- " genkey [bits] Generate a new public/private keypair.\n"
+ " pid Show PID of currently running tincd.\n"
+ " generate-keys [bits] Generate a new public/private keypair.\n"
" dump Dump a list of one of the following things:\n"
" nodes - all known nodes in the VPN\n"
" edges - all known connections in the VPN\n"
show_version = true;
break;
- case 5: /* write PID to a file */
- pidfilename = xstrdup(optarg);
+ case 5: /* open control socket here */
+ controlsocketname = xstrdup(optarg);
break;
case '?':
return true;
}
+FILE *ask_and_open(const char *filename, const char *what, const char *mode) {
+ FILE *r;
+ char *directory;
+ char buf[PATH_MAX];
+ char buf2[PATH_MAX];
+ size_t len;
+
+ /* Check stdin and stdout */
+ if(isatty(0) && isatty(1)) {
+ /* Ask for a file and/or directory name. */
+ fprintf(stdout, _("Please enter a file to save %s to [%s]: "),
+ what, filename);
+ fflush(stdout);
+
+ if(fgets(buf, sizeof buf, stdin) < 0) {
+ fprintf(stderr, _("Error while reading stdin: %s\n"),
+ strerror(errno));
+ return NULL;
+ }
+
+ len = strlen(buf);
+ if(len)
+ buf[--len] = 0;
+
+ if(len)
+ filename = buf;
+ }
+
+#ifdef HAVE_MINGW
+ if(filename[0] != '\\' && filename[0] != '/' && !strchr(filename, ':')) {
+#else
+ if(filename[0] != '/') {
+#endif
+ /* The directory is a relative path or a filename. */
+ directory = get_current_dir_name();
+ snprintf(buf2, sizeof buf2, "%s/%s", directory, filename);
+ filename = buf2;
+ }
+
+ umask(0077); /* Disallow everything for group and other */
+
+ /* Open it first to keep the inode busy */
+
+ r = fopen(filename, mode);
+
+ if(!r) {
+ fprintf(stderr, _("Error opening file `%s': %s\n"), filename, strerror(errno));
+ return NULL;
+ }
+
+ return r;
+}
+
/* This function prettyprints the key generation process */
static void indicator(int a, int b, void *p) {
}
#endif
- if(!pidfilename)
- asprintf(&pidfilename, LOCALSTATEDIR "/run/%s.pid", identname);
-
- asprintf(&controlfilename, LOCALSTATEDIR "/run/%s.control", identname);
+ if(!controlsocketname)
+ asprintf(&controlsocketname, LOCALSTATEDIR "/run/%s.control", identname);
if(netname) {
if(!confbase)
}
}
-int main(int argc, char **argv) {
+int main(int argc, char *argv[], char *envp[]) {
int fd;
struct sockaddr_un addr;
program_name = argv[0];
return 0;
}
- if(strlen(controlfilename) >= sizeof addr.sun_path) {
+ if(optind >= argc) {
+ fprintf(stderr, _("Not enough arguments.\n"));
+ usage(true);
+ return 1;
+ }
+
+ // First handle commands that don't involve connecting to a running tinc daemon.
+
+ if(!strcasecmp(argv[optind], "generate-keys")) {
+ return !keygen(optind > argc ? atoi(argv[optind + 1]) : 1024);
+ }
+
+ if(!strcasecmp(argv[optind], "start")) {
+ argv[optind] = NULL;
+ execve("tincd", argv, envp);
+ fprintf(stderr, _("Could not start tincd: %s"), strerror(errno));
+ return 1;
+ }
+
+ // Now handle commands that do involve connecting to a running tinc daemon.
+
+ if(strlen(controlsocketname) >= sizeof addr.sun_path) {
fprintf(stderr, _("Control socket filename too long!\n"));
return 1;
}
memset(&addr, 0, sizeof addr);
addr.sun_family = AF_UNIX;
- strncpy(addr.sun_path, controlfilename, sizeof addr.sun_path - 1);
+ strncpy(addr.sun_path, controlsocketname, sizeof addr.sun_path - 1);
if(connect(fd, (struct sockaddr *)&addr, sizeof addr) < 0) {
- fprintf(stderr, _("Cannot connect to %s: %s\n"), controlfilename, strerror(errno));
+ fprintf(stderr, _("Cannot connect to %s: %s\n"), controlsocketname, strerror(errno));
return 1;
}
- printf("Connected to %s.\n", controlfilename);
+ struct ucred cred;
+ socklen_t credlen = sizeof cred;
+
+ if(getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &cred, &credlen) < 0) {
+ fprintf(stderr, _("Could not obtain PID: %s\n"), strerror(errno));
+ return 1;
+ }
+ if(!strcasecmp(argv[optind], "pid")) {
+ printf("%d\n", cred.pid);
+ return 0;
+ }
+
+ if(!strcasecmp(argv[optind], "stop")) {
+ write(fd, "stop\n", 5);
+ return 0;
+ }
+
+ if(!strcasecmp(argv[optind], "reload")) {
+ write(fd, "reload\n", 7);
+ return 0;
+ }
+
+ if(!strcasecmp(argv[optind], "restart")) {
+ write(fd, "restart\n", 8);
+ return 0;
+ }
+
+ fprintf(stderr, _("Unknown command `%s'.\n"), argv[optind]);
+ usage(true);
+
close(fd);
return 0;