]> code.citadel.org Git - citadel.git/blobdiff - citadel/ctdlmigrate.c
formatting
[citadel.git] / citadel / ctdlmigrate.c
index f767148c0a1fc59d9d42d1730c5d0a93ef0d97c0..d18707a27319dece1fac47bf3314214783739100 100644 (file)
@@ -1,16 +1,14 @@
-/*
- * Across-the-wire migration utility for Citadel
- *
- * Copyright (c) 2009-2021 citadel.org
- *
- * This program is open source software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 3.
- *
- * 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 General Public License for more details.
- */
+// Across-the-wire migration utility for Citadel
+//
+// Copyright (c) 2009-2021 citadel.org
+//
+// This program is open source software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License version 3.
+//
+// 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 General Public License for more details.
 
 #include <stdlib.h>
 #include <unistd.h>
 
 #include <stdlib.h>
 #include <unistd.h>
 #include "citadel_dirs.h"
 
 
 #include "citadel_dirs.h"
 
 
-
 // yeah, this wouldn't work in a multithreaded program.
 static int gmaxlen = 0;
 static char *gdeftext = NULL;
 
 // yeah, this wouldn't work in a multithreaded program.
 static int gmaxlen = 0;
 static char *gdeftext = NULL;
 
+
+// support function for getz()
 static int limit_rl(FILE *dummy) {
        if (rl_end > gmaxlen) {
                return '\b';
 static int limit_rl(FILE *dummy) {
        if (rl_end > gmaxlen) {
                return '\b';
@@ -52,6 +51,7 @@ static int limit_rl(FILE *dummy) {
 }
 
 
 }
 
 
+// support function for getz()
 static int getz_deftext(void) {
        if (gdeftext) {
                rl_insert_text(gdeftext);
 static int getz_deftext(void) {
        if (gdeftext) {
                rl_insert_text(gdeftext);
@@ -62,11 +62,7 @@ static int getz_deftext(void) {
 }
 
 
 }
 
 
-/*
- * Replacement for gets() that doesn't throw a compiler warning.
- * We're only using it for some simple prompts, so we don't need
- * to worry about attackers exploiting it.
- */
+// Replacement for gets() that uses libreadline.
 void getz(char *buf, int maxlen, char *default_value, char *prompt) {
        rl_startup_hook = getz_deftext;
        rl_getc_function = limit_rl;
 void getz(char *buf, int maxlen, char *default_value, char *prompt) {
        rl_startup_hook = getz_deftext;
        rl_getc_function = limit_rl;
@@ -76,27 +72,76 @@ void getz(char *buf, int maxlen, char *default_value, char *prompt) {
 }
 
 
 }
 
 
-// These variables are used by both main() and ctdlmigrate_exit()
-// They are global so that ctdlmigrate_exit can be called from a signal handler
-FILE *sourcefp = NULL;
-char socket_path[PATH_MAX];
-pid_t sshpid = (-1);
-
+// Exit from the program while displaying an error code
 void ctdlmigrate_exit(int cmdexit) {
 void ctdlmigrate_exit(int cmdexit) {
-       if (sourcefp) {
-               printf("Closing the data connection from the source system...\n");
-               pclose(sourcefp);
-       }
-       unlink(socket_path);
-       if (sshpid > 0) {
-               printf("Shutting down the SSH session...\n");
-               kill(sshpid, SIGKILL);
-       }
        printf("\n\n\033[3%dmExit code %d\033[0m\n", (cmdexit ? 1 : 2), cmdexit);
        exit(cmdexit);
 }
 
 
        printf("\n\n\033[3%dmExit code %d\033[0m\n", (cmdexit ? 1 : 2), cmdexit);
        exit(cmdexit);
 }
 
 
+// Connect to a Citadel on a remote host using a TCP/IP socket
+static int tcp_connectsock(char *host, char *service) {
+       struct in6_addr serveraddr;
+       struct addrinfo hints;
+       struct addrinfo *res = NULL;
+       struct addrinfo *ai = NULL;
+       int rc = (-1);
+       int sock = (-1);
+
+       if ((host == NULL) || IsEmptyStr(host)) {
+               return(-1);
+       }
+       if ((service == NULL) || IsEmptyStr(service)) {
+               return(-1);
+       }
+
+       memset(&hints, 0x00, sizeof(hints));
+       hints.ai_flags = AI_NUMERICSERV;
+       hints.ai_family = AF_UNSPEC;
+       hints.ai_socktype = SOCK_STREAM;
+
+       // Handle numeric IPv4 and IPv6 addresses
+       rc = inet_pton(AF_INET, host, &serveraddr);
+       if (rc == 1) {                                  // dotted quad
+               hints.ai_family = AF_INET;
+               hints.ai_flags |= AI_NUMERICHOST;
+       } else {
+               rc = inet_pton(AF_INET6, host, &serveraddr);
+               if (rc == 1) {                          // IPv6 address
+                       hints.ai_family = AF_INET6;
+                       hints.ai_flags |= AI_NUMERICHOST;
+               }
+       }
+
+       // Begin the connection process
+       rc = getaddrinfo(host, service, &hints, &res);
+       if (rc != 0) {
+               fprintf(stderr, "ctdlmigrate: %s\n", strerror(errno));
+               return (-1);
+       }
+
+       // Try all available addresses until we connect to one or until we run out.
+       for (ai = res; ai != NULL; ai = ai->ai_next) {
+               sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
+               if (sock < 0) {
+                       fprintf(stderr, "ctdlmigrate: %s\n", strerror(errno));
+                       return (-1);
+               }
+
+               rc = connect(sock, ai->ai_addr, ai->ai_addrlen);
+               if (rc >= 0) {
+                       return (sock);  // Connected!
+               }
+               else {
+                       fprintf(stderr, "ctdlmigrate: %s\n", strerror(errno));
+                       close(sock);    // Failed.  Close the socket to avoid fd leak!
+               }
+       }
+       return (-1);
+}
+
+
+// Connect to a Citadel on a remote host using a unix domaion socket
 int uds_connectsock(char *sockpath) {
        int s;
        struct sockaddr_un addr;
 int uds_connectsock(char *sockpath) {
        int s;
        struct sockaddr_un addr;
@@ -108,26 +153,24 @@ int uds_connectsock(char *sockpath) {
        s = socket(AF_UNIX, SOCK_STREAM, 0);
        if (s < 0) {
                fprintf(stderr, "ctdlmigrate: Can't create socket: %s\n", strerror(errno));
        s = socket(AF_UNIX, SOCK_STREAM, 0);
        if (s < 0) {
                fprintf(stderr, "ctdlmigrate: Can't create socket: %s\n", strerror(errno));
-               ctdlmigrate_exit(3);
+               return(-1);
        }
 
        if (connect(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
                fprintf(stderr, "ctdlmigrate: can't connect: %s\n", strerror(errno));
                close(s);
        }
 
        if (connect(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
                fprintf(stderr, "ctdlmigrate: can't connect: %s\n", strerror(errno));
                close(s);
-               ctdlmigrate_exit(3);
+               return(-1);
        }
 
        return s;
 }
 
 
        }
 
        return s;
 }
 
 
-/*
- * input binary data from socket
- */
+// input binary data from socket
 void serv_read(int serv_sock, char *buf, int bytes) {
 void serv_read(int serv_sock, char *buf, int bytes) {
-       int len, rlen;
+       int len = 0;
+       int rlen = 0;
 
 
-       len = 0;
        while (len < bytes) {
                rlen = read(serv_sock, &buf[len], bytes - len);
                if (rlen < 1) {
        while (len < bytes) {
                rlen = read(serv_sock, &buf[len], bytes - len);
                if (rlen < 1) {
@@ -138,9 +181,7 @@ void serv_read(int serv_sock, char *buf, int bytes) {
 }
 
 
 }
 
 
-/*
- * send binary to server
- */
+// send binary to server
 void serv_write(int serv_sock, char *buf, int nbytes) {
        int bytes_written = 0;
        int retval;
 void serv_write(int serv_sock, char *buf, int nbytes) {
        int bytes_written = 0;
        int retval;
@@ -154,58 +195,68 @@ void serv_write(int serv_sock, char *buf, int nbytes) {
 }
 
 
 }
 
 
-/*
- * input string from socket - implemented in terms of serv_read()
- */
+// input string from socket - implemented in terms of serv_read()
 void serv_gets(int serv_sock, char *buf) {
        int i;
 
 void serv_gets(int serv_sock, char *buf) {
        int i;
 
-       /* Read one character at a time.
-        */
+       // Read one character at a time.
        for (i = 0;; i++) {
                serv_read(serv_sock, &buf[i], 1);
                if (buf[i] == '\n' || i == (SIZ-1))
                        break;
        }
 
        for (i = 0;; i++) {
                serv_read(serv_sock, &buf[i], 1);
                if (buf[i] == '\n' || i == (SIZ-1))
                        break;
        }
 
-       /* If we got a long line, discard characters until the newline.
-        */
+       // If we got a long line, discard characters until the newline.
        if (i == (SIZ-1)) {
                while (buf[i] != '\n') {
                        serv_read(serv_sock, &buf[i], 1);
                }
        }
 
        if (i == (SIZ-1)) {
                while (buf[i] != '\n') {
                        serv_read(serv_sock, &buf[i], 1);
                }
        }
 
-       /* Strip all trailing nonprintables (crlf)
-        */
+       // Strip all trailing nonprintables (crlf)
        buf[i] = 0;
 }
 
 
        buf[i] = 0;
 }
 
 
-/*
- * send line to server - implemented in terms of serv_write()
- */
+// send line to server - implemented in terms of serv_write()
 void serv_puts(int serv_sock, char *buf) {
        serv_write(serv_sock, buf, strlen(buf));
        serv_write(serv_sock, "\n", 1);
 }
 
 
 void serv_puts(int serv_sock, char *buf) {
        serv_write(serv_sock, buf, strlen(buf));
        serv_write(serv_sock, "\n", 1);
 }
 
 
+// send formatted printable data to the server
+void serv_printf(int serv_sock, const char *format, ...) {   
+       va_list arg_ptr;   
+       char buf[1024];
+   
+       va_start(arg_ptr, format);   
+       if (vsnprintf(buf, sizeof buf, format, arg_ptr) == -1)
+               buf[sizeof buf - 2] = '\n';
+       serv_write(serv_sock, buf, strlen(buf)); 
+       va_end(arg_ptr);
+}
+
+
+// You know what main() does.  If you don't, you shouldn't be trying to understand this program.
 int main(int argc, char *argv[]) {
        char ctdldir[PATH_MAX]=CTDLDIR;
 int main(int argc, char *argv[]) {
        char ctdldir[PATH_MAX]=CTDLDIR;
-       char yesno[5];
+       char yesno[2];
        int cmdexit = 0;                                // when something fails, set cmdexit to nonzero, and skip to the end
        int cmdexit = 0;                                // when something fails, set cmdexit to nonzero, and skip to the end
-       char cmd[PATH_MAX];
        char buf[PATH_MAX];
        char buf[PATH_MAX];
-       char remote_user[SIZ];
-       char remote_host[SIZ];
-       char remote_sendcommand[PATH_MAX];
+
+       char remote_user[128];
+       char remote_host[128];
+       char remote_pass[128];
+       char *remote_port = "504";
+
        int linecount = 0;
        int a;
        int linecount = 0;
        int a;
+       int remote_server_socket = (-1);
        int local_admin_socket = (-1);
        int progress = 0;
 
        int local_admin_socket = (-1);
        int progress = 0;
 
-       /* Parse command line */
+       // Parse command line
        while ((a = getopt(argc, argv, "h:")) != EOF) {
                switch (a) {
                case 'h':
        while ((a = getopt(argc, argv, "h:")) != EOF) {
                switch (a) {
                case 'h':
@@ -241,8 +292,7 @@ int main(int argc, char *argv[]) {
                "the source system.  The target may be a different CPU architecture\n"
                "and/or operating system.  The source system should be running\n"
                "Citadel version \033[33m%d\033[0m or newer, and the target system should be running\n"
                "the source system.  The target may be a different CPU architecture\n"
                "and/or operating system.  The source system should be running\n"
                "Citadel version \033[33m%d\033[0m or newer, and the target system should be running\n"
-               "either the same version or a newer version.  You will also need\n"
-               "the \033[33mrsync\033[0m utility, and OpenSSH v4 or newer.\n"
+               "either the same version or a newer version.\n"
                "\n"
                "You must run this utility on the TARGET SYSTEM.  Any existing data\n"
                "on this system will be ERASED.  Your target system will be on this\n"
                "\n"
                "You must run this utility on the TARGET SYSTEM.  Any existing data\n"
                "on this system will be ERASED.  Your target system will be on this\n"
@@ -258,19 +308,24 @@ int main(int argc, char *argv[]) {
        }
 
        if (!cmdexit) {
        }
 
        if (!cmdexit) {
-
                printf( "\033[2J\033[H\n"
                printf( "\033[2J\033[H\n"
-                       "          \033[32m╔═══════════════════════════════════════════════╗\n"
-                       "          ║                                               ║\n"
-                       "          ║       \033[33mValidate source and target systems\033[32m      ║\n"
-                       "          ║                                               ║\n"
-                       "          ╚═══════════════════════════════════════════════╝\033[0m\n"
+                       "\033[32m╔═══════════════════════════════════════════════╗\n"
+                       "║                                               ║\n"
+                       "║       \033[33mValidate source and target systems\033[32m      ║\n"
+                       "║                                               ║\n"
+                       "╚═══════════════════════════════════════════════╝\033[0m\n"
                        "\n\n");
        
                        "\n\n");
        
-               printf("First we must validate that the local target system is running and ready to receive data.\n");
+               printf("First we must validate that the local target system is running\n");
+               printf("and ready to receive data.\n");
                printf("Checking connectivity to Citadel in %s...\n", ctdldir);
                local_admin_socket = uds_connectsock("citadel-admin.socket");
                printf("Checking connectivity to Citadel in %s...\n", ctdldir);
                local_admin_socket = uds_connectsock("citadel-admin.socket");
+               if (local_admin_socket < 0) {
+                       cmdexit = 1;
+               }
+       }
 
 
+       if (!cmdexit) {
                serv_gets(local_admin_socket, buf);
                puts(buf);
                if (buf[0] != '2') {
                serv_gets(local_admin_socket, buf);
                puts(buf);
                if (buf[0] != '2') {
@@ -290,93 +345,64 @@ int main(int argc, char *argv[]) {
        if (!cmdexit) {
                printf("\n");
                printf("OK, this side is ready to go.  Now we must connect to the source system.\n\n");
        if (!cmdexit) {
                printf("\n");
                printf("OK, this side is ready to go.  Now we must connect to the source system.\n\n");
-               printf("Enter the host name or IP address of the source system\n");
-               printf("(example: ctdl.foo.org)\n");
-               getz(remote_host, sizeof remote_host, NULL, "--> ");
-       
-               while (IsEmptyStr(remote_user)) {
-                       printf("\n");
-                       printf("Enter the name of a user on %s who has full access to Citadel files.\n", remote_host);
-                       getz(remote_user, sizeof remote_user, "root", "--> ");
-               }
 
 
-               printf("\n");
-               printf("Establishing an SSH connection to the source system...\n");
-               sprintf(socket_path, "/tmp/ctdlmigrate-socket.%ld.%d", time(NULL), getpid());
-               unlink(socket_path);
-
-               snprintf(cmd, sizeof cmd, "ssh -MNf -S %s -l %s %s", socket_path, remote_user, remote_host);
-               sshpid = fork();
-               if (sshpid < 0) {
-                       printf("%s\n", strerror(errno));
-                       cmdexit = errno;
-               }
-               else if (sshpid == 0) {
-                       execl("/bin/bash", "bash", "-c", cmd, (char *) NULL);
-                       exit(1);
-               }
-               else {                                          // Wait for SSH to go into the background
-                       waitpid(sshpid, &cmdexit, 0);
+               getz(remote_host, sizeof remote_host, NULL,     "Enter the name or IP address of the source system: ");
+               getz(remote_user, sizeof remote_user, "admin",  "  Enter the user name of an administrator account: ");
+               getz(remote_pass, sizeof remote_pass, NULL,     "              Enter the password for this account: ");
+
+               remote_server_socket = tcp_connectsock(remote_host, remote_port);
+               if (remote_server_socket < 0) {
+                       cmdexit = 1;
                }
        }
 
        if (!cmdexit) {
                }
        }
 
        if (!cmdexit) {
-               printf("\nTesting a command over the connection...\n\n");
-               snprintf(cmd, sizeof cmd, "ssh -S %s %s@%s 'echo Remote commands are executing successfully.'",
-                       socket_path, remote_user, remote_host);
-               cmdexit = system(cmd);
-               printf("\n");
-               if (cmdexit != 0) {
-                       printf("\033[31mRemote commands are not succeeding.\033[0m\n\n");
+               serv_gets(remote_server_socket, buf);
+               puts(buf);
+               if (buf[0] != '2') {
+                       cmdexit = 1;
                }
        }
 
        if (!cmdexit) {
                }
        }
 
        if (!cmdexit) {
-               printf("\nLocating the remote 'sendcommand' and Citadel installation...\n");
-               snprintf(remote_sendcommand, sizeof remote_sendcommand, "/usr/local/citadel/sendcommand");
-               snprintf(cmd, sizeof cmd, "ssh -S %s %s@%s %s NOOP", socket_path, remote_user, remote_host, remote_sendcommand);
-               cmdexit = system(cmd);
-
-               if (cmdexit) {
-                       snprintf(remote_sendcommand, sizeof remote_sendcommand, "/usr/sbin/sendcommand");
-                       snprintf(cmd, sizeof cmd, "ssh -S %s %s@%s %s NOOP", socket_path, remote_user, remote_host, remote_sendcommand);
-                       cmdexit = system(cmd);
+               serv_printf(remote_server_socket, "USER %s\n", remote_user);
+               serv_gets(remote_server_socket, buf);
+               puts(buf);
+               if (buf[0] != '3') {
+                       cmdexit = 1;
                }
                }
+       }
 
 
-               if (cmdexit) {
-                       printf("\n");
-                       printf("Unable to locate Citadel programs on the remote system.  Please enter\n"
-                               "the name of the directory on %s which contains the 'sendcommand' program.\n"
-                               "(example: /opt/foo/citadel)\n", remote_host);
-                       getz(remote_host, sizeof remote_host, "/usr/local/citadel", "--> ");
-                       snprintf(remote_sendcommand, sizeof remote_sendcommand, "%s/sendcommand", buf);
-                       snprintf(cmd, sizeof cmd, "ssh -S %s %s@%s %s NOOP", socket_path, remote_user, remote_host, remote_sendcommand);
-                       cmdexit = system(cmd);
-                       if (!cmdexit) {
-                               printf("ctdlmigrate was unable to attach to the remote Citadel system.\n\n");
-                       }
+       if (!cmdexit) {
+               serv_printf(remote_server_socket, "PASS %s\n", remote_pass);
+               serv_gets(remote_server_socket, buf);
+               puts(buf);
+               if (buf[0] != '2') {
+                       cmdexit = 1;
                }
        }
 
        if (!cmdexit) {
                printf( "\033[2J\033[H\n"
                }
        }
 
        if (!cmdexit) {
                printf( "\033[2J\033[H\n"
-                       "          \033[32m╔═══════════════════════════════════════════════════════════════════╗\n"
-                       "          ║                                                                   ║\n"
-                       "          ║ \033[33mMigrating from: %-50s\033[32m║\n"
-                       "          ║                                                                   ║\n"
-                       "          ╟═══════════════════════════════════════════════════════════════════╣\n"
-                       "          ║                                                                   ║\n"
-                       "          ║ Lines received: 0                           Percent complete: 0   ║\n"
-                       "          ║                                                                   ║\n"
-                       "          ╚═══════════════════════════════════════════════════════════════════╝\033[0m\n"
+                       "\033[32m╔═══════════════════════════════════════════════════════════════════╗\n"
+                       "║                                                                   ║\n"
+                       "║ \033[33mMigrating from: %-50s\033[32m║\n"
+                       "║                                                                   ║\n"
+                       "═══════════════════════════════════════════════════════════════════╣\n"
+                       "║                                                                   ║\n"
+                       "║ Lines received: 0                           Percent complete: 0   ║\n"
+                       "║                                                                   ║\n"
+                       "╚═══════════════════════════════════════════════════════════════════╝\033[0m\n"
                        "\n", remote_host
                );
                        "\n", remote_host
                );
+       }
 
 
-               snprintf(cmd, sizeof cmd, "ssh -S %s %s@%s %s -w3600 MIGR export", socket_path, remote_user, remote_host, remote_sendcommand);
-               sourcefp = popen(cmd, "r");
-               if (!sourcefp) {
-                       cmdexit = errno;
-                       printf("\n%s\n\n", strerror(errno));
+       if (!cmdexit) {
+               serv_puts(remote_server_socket, "MIGR export");
+               serv_gets(remote_server_socket, buf);
+               if (buf[0] != '1') {
+                       printf("\n\033[31m%s\033[0m\n", buf);
+                       cmdexit = 3;
                }
        }
 
                }
        }
 
@@ -392,17 +418,18 @@ int main(int argc, char *argv[]) {
        if (!cmdexit) {
                char *ptr;
                time_t last_update = time(NULL);
        if (!cmdexit) {
                char *ptr;
                time_t last_update = time(NULL);
-               while (ptr = fgets(buf, SIZ, sourcefp), (ptr != NULL)) {
+
+               while (serv_gets(remote_server_socket, buf), strcmp(buf, "000")) {
                        ptr = strchr(buf, '\n');
                        if (ptr) *ptr = 0;      // remove the newline character
                        ++linecount;
                        if (!strncasecmp(buf, "<progress>", 10)) {
                                progress = atoi(&buf[10]);
                        ptr = strchr(buf, '\n');
                        if (ptr) *ptr = 0;      // remove the newline character
                        ++linecount;
                        if (!strncasecmp(buf, "<progress>", 10)) {
                                progress = atoi(&buf[10]);
-                               printf("\033[8;75H\033[33m%d\033[0m\033[20;0H\n", progress);
+                               printf("\033[8;65H\033[33m%d\033[0m\033[12;0H\n", progress);
                        }
                        if (time(NULL) != last_update) {
                                last_update = time(NULL);
                        }
                        if (time(NULL) != last_update) {
                                last_update = time(NULL);
-                               printf("\033[8;29H\033[33m%d\033[0m\033[20;0H\n", linecount);
+                               printf("\033[8;19H\033[33m%d\033[0m\033[12;0H\n", linecount);
                        }
                        serv_puts(local_admin_socket, buf);
                }
                        }
                        serv_puts(local_admin_socket, buf);
                }
@@ -415,7 +442,9 @@ int main(int argc, char *argv[]) {
                ctdlmigrate_exit(86);
        }
 
                ctdlmigrate_exit(86);
        }
 
-       // FIXME restart the local server now
+       // FIXME restart the local server here
 
 
+       close(remote_server_socket);
+       close(local_admin_socket);
        ctdlmigrate_exit(cmdexit);
 }
        ctdlmigrate_exit(cmdexit);
 }