X-Git-Url: https://code.citadel.org/?a=blobdiff_plain;f=citadel%2Fctdlmigrate.c;h=78af852bd6bdf80349daae4a4b30af0910a80c23;hb=7ca5dde7f307874234674829d6bdd22a7d774b6c;hp=7a53a8fb98d0df1b55940355e952480a1ec3f35a;hpb=5353be73570cffd8d53a4eb86d0f73669fd68283;p=citadel.git diff --git a/citadel/ctdlmigrate.c b/citadel/ctdlmigrate.c index 7a53a8fb9..78af852bd 100644 --- a/citadel/ctdlmigrate.c +++ b/citadel/ctdlmigrate.c @@ -1,12 +1,15 @@ /* - * $Id: $ - * * Across-the-wire migration utility for Citadel * - * Copyright (c) 2009 citadel.org + * Copyright (c) 2009-2021 citadel.org * - * This program is licensed to you under the terms of the GNU General Public License v3 + * 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 @@ -25,38 +28,165 @@ #include #include #include +#include +#include #include #include "citadel.h" #include "axdefs.h" #include "sysdep.h" #include "config.h" #include "citadel_dirs.h" -#if HAVE_BACKTRACE -#include -#endif +/* + * 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. + */ +void getz(char *buf) { + char *ptr; + + ptr = fgets(buf, SIZ, stdin); + if (!ptr) { + buf[0] = 0; + return; + } + + ptr = strchr(buf, '\n'); + if (ptr) *ptr = 0; +} + + +int uds_connectsock(char *sockpath) { + int s; + struct sockaddr_un addr; + + memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_UNIX; + strcpy(addr.sun_path, sockpath); + + s = socket(AF_UNIX, SOCK_STREAM, 0); + if (s < 0) { + fprintf(stderr, "sendcommand: Can't create socket: %s\n", strerror(errno)); + exit(3); + } + + if (connect(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + fprintf(stderr, "sendcommand: can't connect: %s\n", strerror(errno)); + close(s); + exit(3); + } + + return s; +} + + +/* + * input binary data from socket + */ +void serv_read(int serv_sock, char *buf, int bytes) { + int len, rlen; + + len = 0; + while (len < bytes) { + rlen = read(serv_sock, &buf[len], bytes - len); + if (rlen < 1) { + return; + } + len = len + rlen; + } +} + + +/* + * send binary to server + */ +void serv_write(int serv_sock, char *buf, int nbytes) { + int bytes_written = 0; + int retval; + while (bytes_written < nbytes) { + retval = write(serv_sock, &buf[bytes_written], nbytes - bytes_written); + if (retval < 1) { + return; + } + bytes_written = bytes_written + retval; + } +} + + +/* + * input string from socket - implemented in terms of serv_read() + */ +void serv_gets(int serv_sock, char *buf) { + int i; + + /* 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; + } -int main(int argc, char *argv[]) -{ - int relh=0; - int home=0; - char relhome[PATH_MAX]=""; + /* 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); + } + } + + /* Strip all trailing nonprintables (crlf) + */ + buf[i] = 0; +} + + +/* + * 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); +} + + +int main(int argc, char *argv[]) { char ctdldir[PATH_MAX]=CTDLDIR; char yesno[5]; - char sendcommand[PATH_MAX]; - int exitcode; + int cmdexit = 0; // when something fails, set cmdexit to nonzero, and skip to the end char cmd[PATH_MAX]; + char buf[PATH_MAX]; char socket_path[PATH_MAX]; - char remote_user[256]; - char remote_host[256]; - - calc_dirs_n_files(relh, home, relhome, ctdldir, 0); - CtdlMakeTempFileName(socket_path, sizeof socket_path); + char remote_user[SIZ]; + char remote_host[SIZ]; + char remote_sendcommand[PATH_MAX]; + FILE *sourcefp = NULL; + int linecount = 0; + int a; + int local_admin_socket = (-1); + pid_t sshpid = (-1); + + /* Parse command line */ + while ((a = getopt(argc, argv, "h:")) != EOF) { + switch (a) { + case 'h': + strcpy(ctdldir, optarg); + break; + default: + fprintf(stderr, "sendcommand: usage: ctdlmigrate [-h server_dir]\n"); + return(1); + } + } - system("clear"); - printf( "-------------------------------------------\n" + if (chdir(ctdldir) != 0) { + fprintf(stderr, "sendcommand: %s: %s\n", ctdldir, strerror(errno)); + exit(errno); + } + + printf( "\033[2J\033[H\n" + "-------------------------------------------\n" "Over-the-wire migration utility for Citadel\n" "-------------------------------------------\n" "\n" @@ -64,69 +194,163 @@ int main(int argc, char *argv[]) "to a new host system via a network connection, without disturbing\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 %d.%02d or newer, and the target system should be running\n" + "Citadel version %d or newer, and the target system should be running\n" "either the same version or a newer version. You will also need\n" "the 'rsync' utility, and OpenSSH v4 or newer.\n" "\n" "You must run this utility on the TARGET SYSTEM. Any existing data\n" - "on this system will be ERASED.\n" + "on this system will be ERASED. Your target system will be on this\n" + "host in %s.\n" "\n" "Do you wish to continue? " , - EXPORT_REV_MIN / 100, - EXPORT_REV_MIN % 100 + EXPORT_REV_MIN, ctdldir ); if ((fgets(yesno, sizeof yesno, stdin) == NULL) || (tolower(yesno[0]) != 'y')) { - exit(0); + cmdexit = 1; + } + + if (!cmdexit) { + printf("\n\nGreat! First we will check some things out here on our target\n" + "system to make sure it is ready to receive data.\n\n"); + + printf("Checking connectivity to Citadel in %s...\n", ctdldir); + local_admin_socket = uds_connectsock("citadel-admin.socket"); + + serv_gets(local_admin_socket, buf); + puts(buf); + if (buf[0] != '2') { + cmdexit = 1; + } } - printf("\n\nGreat! First we will check some things out here on our target\n" - "system to make sure it is ready to receive data.\n\n"); + if (!cmdexit) { + serv_puts(local_admin_socket, "ECHO Connection to Citadel Server succeeded."); + serv_gets(local_admin_socket, buf); + puts(buf); + if (buf[0] != '2') { + cmdexit = 1; + } + } - printf("Locating 'sendcommand' and checking connectivity to Citadel...\n"); - snprintf(sendcommand, sizeof sendcommand, "%s/sendcommand", ctdl_utilbin_dir); - snprintf(cmd, sizeof cmd, "%s 'NOOP'", sendcommand); - exitcode = system(cmd); - if (exitcode != 0) { - printf("\nctdlmigrate was unable to attach to the Citadel server\n" - "here on the target system. Is Citadel running?\n\n"); - exit(1); + if (!cmdexit) { + printf("\nOK, 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" + "(example: ctdl.foo.org)\n" + "--> "); + getz(remote_host); + + while (IsEmptyStr(remote_user)) { + printf("\nEnter the name of a user on %s who has full access to Citadel files\n" + "(usually root)\n--> ", + remote_host); + getz(remote_user); + } + + printf("\nEstablishing an SSH connection to the source system...\n\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, NULL, 0); + } } - printf("\nOK, 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" - "(example: ctdl.foo.org)\n" - "--> "); - gets(remote_host); - printf("\nEnter the name of a user on %s who has full access to Citadel files\n" - "(usually root)\n--> ", - remote_host); - gets(remote_user); + 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("Remote commands are not succeeding.\n\n"); + } + } - printf("\nEstablishing an SSH connection to the source system...\n\n"); - unlink(socket_path); - snprintf(cmd, sizeof cmd, "ssh -MNf -S %s %s@%s", socket_path, remote_user, remote_host); - exitcode = system(cmd); - if (exitcode != 0) { - printf("\nctdlmigrate was unable to establish an SSH connection to the\n" - "source system, and cannot continue.\n\n"); - exit(exitcode); + 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); + } + + if (cmdexit) { + printf("\nUnable 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(buf); + 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) { + printf("\033[2J\n"); + printf("\033[2;0H\033[33mMigrating from %s\033[0m\n\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)); + } } - 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); - exitcode = system(cmd); - printf("\n"); - if (exitcode != 0) { - printf("Remote commands are not succeeding.\n\n"); - exit(exitcode); + if (!cmdexit) { + serv_puts(local_admin_socket, "MIGR import"); + serv_gets(local_admin_socket, buf); + if (buf[0] != '4') { + printf("\n%s\n", buf); + cmdexit = 3; + } } + if (!cmdexit) { + char *ptr; + time_t last_update = time(NULL); + while (ptr = fgets(buf, SIZ, sourcefp), (ptr != NULL)) { + ptr = strchr(buf, '\n'); + if (ptr) *ptr = 0; // remove the newline character + ++linecount; + if (!strncasecmp(buf, "", 10)) { + printf("\033[11;0HPercent complete: \033[32m%d\033[0m\n", atoi(&buf[10])); + } + if (time(NULL) != last_update) { + last_update = time(NULL); + printf("\033[10;0H Lines received: \033[32m%d\033[0m\n", linecount); + } + serv_puts(local_admin_socket, buf); + } + + serv_puts(local_admin_socket, "000"); + } + // FIXME restart the local server now - // FIXME kill the master ssh session - printf("If this program was finished we would do more. FIXME\n"); - exit(0); + pclose(sourcefp); + printf("\nShutting down the socket connection...\n\n"); + unlink(socket_path); + kill(sshpid, SIGKILL); + exit(cmdexit); }