"Fossies" - the Fresh Open Source Software Archive

Member "citadel/ctdlmigrate.c" (5 Jun 2021, 13172 Bytes) of package /linux/www/citadel.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "ctdlmigrate.c" see the Fossies "Dox" file reference documentation.

    1 // Across-the-wire migration utility for Citadel
    2 //
    3 // Copyright (c) 2009-2021 citadel.org
    4 //
    5 // This program is open source software; you can redistribute it and/or modify
    6 // it under the terms of the GNU General Public License version 3.
    7 //
    8 // This program is distributed in the hope that it will be useful,
    9 // but WITHOUT ANY WARRANTY; without even the implied warranty of
   10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   11 // GNU General Public License for more details.
   12 
   13 #include <stdlib.h>
   14 #include <unistd.h>
   15 #include <stdio.h>
   16 #include <string.h>
   17 #include <ctype.h>
   18 #include <fcntl.h>
   19 #include <sys/types.h>
   20 #include <sys/stat.h>
   21 #include <sys/utsname.h>
   22 #include <sys/wait.h>
   23 #include <signal.h>
   24 #include <netdb.h>
   25 #include <errno.h>
   26 #include <limits.h>
   27 #include <pwd.h>
   28 #include <time.h>
   29 #include <readline/readline.h>
   30 #include <sys/socket.h>
   31 #include <sys/un.h>
   32 #include <libcitadel.h>
   33 #include "citadel.h"
   34 #include "axdefs.h"
   35 #include "sysdep.h"
   36 #include "config.h"
   37 #include "citadel_dirs.h"
   38 
   39 
   40 // support for getz() -- (globals would not be appropriate in a multithreaded program)
   41 static int gmaxlen = 0;
   42 static char *gdeftext = NULL;
   43 
   44 
   45 // support function for getz()
   46 static int limit_rl(FILE *dummy) {
   47     if (rl_end > gmaxlen) {
   48         return '\b';
   49     }
   50     return rl_getc(dummy);
   51 }
   52 
   53 
   54 // support function for getz()
   55 static int getz_deftext(void) {
   56     if (gdeftext) {
   57         rl_insert_text(gdeftext);
   58         gdeftext = NULL;
   59         rl_startup_hook = NULL;
   60     }
   61     return 0;
   62 }
   63 
   64 
   65 // Replacement for gets() that uses libreadline.
   66 void getz(char *buf, int maxlen, char *default_value, char *prompt) {
   67     rl_startup_hook = getz_deftext;
   68     rl_getc_function = limit_rl;
   69     gmaxlen = maxlen;
   70     gdeftext = default_value;
   71     strcpy(buf, readline(prompt));
   72 }
   73 
   74 
   75 // Exit from the program while displaying an error code
   76 void ctdlmigrate_exit(int cmdexit) {
   77     printf("\n\n\033[3%dmExit code %d\033[0m\n", (cmdexit ? 1 : 2), cmdexit);
   78     exit(cmdexit);
   79 }
   80 
   81 
   82 // Connect to a Citadel on a remote host using a TCP/IP socket
   83 static int tcp_connectsock(char *host, char *service) {
   84     struct in6_addr serveraddr;
   85     struct addrinfo hints;
   86     struct addrinfo *res = NULL;
   87     struct addrinfo *ai = NULL;
   88     int rc = (-1);
   89     int sock = (-1);
   90 
   91     if ((host == NULL) || IsEmptyStr(host)) {
   92         return(-1);
   93     }
   94     if ((service == NULL) || IsEmptyStr(service)) {
   95         return(-1);
   96     }
   97 
   98     memset(&hints, 0x00, sizeof(hints));
   99     hints.ai_flags = AI_NUMERICSERV;
  100     hints.ai_family = AF_UNSPEC;
  101     hints.ai_socktype = SOCK_STREAM;
  102 
  103     // Handle numeric IPv4 and IPv6 addresses
  104     rc = inet_pton(AF_INET, host, &serveraddr);
  105     if (rc == 1) {                  // dotted quad
  106         hints.ai_family = AF_INET;
  107         hints.ai_flags |= AI_NUMERICHOST;
  108     } else {
  109         rc = inet_pton(AF_INET6, host, &serveraddr);
  110         if (rc == 1) {              // IPv6 address
  111             hints.ai_family = AF_INET6;
  112             hints.ai_flags |= AI_NUMERICHOST;
  113         }
  114     }
  115 
  116     // Begin the connection process
  117     rc = getaddrinfo(host, service, &hints, &res);
  118     if (rc != 0) {
  119         fprintf(stderr, "ctdlmigrate: %s\n", strerror(errno));
  120         return (-1);
  121     }
  122 
  123     // Try all available addresses until we connect to one or until we run out.
  124     for (ai = res; ai != NULL; ai = ai->ai_next) {
  125         sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
  126         if (sock < 0) {
  127             fprintf(stderr, "ctdlmigrate: %s\n", strerror(errno));
  128             return (-1);
  129         }
  130 
  131         rc = connect(sock, ai->ai_addr, ai->ai_addrlen);
  132         if (rc >= 0) {
  133             return (sock);  // Connected!
  134         }
  135         else {
  136             fprintf(stderr, "ctdlmigrate: %s\n", strerror(errno));
  137             close(sock);    // Failed.  Close the socket to avoid fd leak!
  138         }
  139     }
  140     return (-1);
  141 }
  142 
  143 
  144 // Connect to a Citadel on a remote host using a unix domaion socket
  145 int uds_connectsock(char *sockpath) {
  146     int s;
  147     struct sockaddr_un addr;
  148 
  149     memset(&addr, 0, sizeof(addr));
  150     addr.sun_family = AF_UNIX;
  151     strcpy(addr.sun_path, sockpath);
  152 
  153     s = socket(AF_UNIX, SOCK_STREAM, 0);
  154     if (s < 0) {
  155         fprintf(stderr, "ctdlmigrate: Can't create socket: %s\n", strerror(errno));
  156         return(-1);
  157     }
  158 
  159     if (connect(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
  160         fprintf(stderr, "ctdlmigrate: can't connect: %s\n", strerror(errno));
  161         close(s);
  162         return(-1);
  163     }
  164 
  165     return s;
  166 }
  167 
  168 
  169 // input binary data from socket
  170 void serv_read(int serv_sock, char *buf, int bytes) {
  171     int len = 0;
  172     int rlen = 0;
  173 
  174     while (len < bytes) {
  175         rlen = read(serv_sock, &buf[len], bytes - len);
  176         if (rlen < 1) {
  177             return;
  178         }
  179         len = len + rlen;
  180     }
  181 }
  182 
  183 
  184 // send binary to server
  185 void serv_write(int serv_sock, char *buf, int nbytes) {
  186     int bytes_written = 0;
  187     int retval;
  188     while (bytes_written < nbytes) {
  189         retval = write(serv_sock, &buf[bytes_written], nbytes - bytes_written);
  190         if (retval < 1) {
  191             return;
  192         }
  193         bytes_written = bytes_written + retval;
  194     }
  195 }
  196 
  197 
  198 // input string from socket - implemented in terms of serv_read()
  199 void serv_gets(int serv_sock, char *buf) {
  200     int i;
  201 
  202     // Read one character at a time.
  203     for (i = 0;; i++) {
  204         serv_read(serv_sock, &buf[i], 1);
  205         if (buf[i] == '\n' || i == (SIZ-1))
  206             break;
  207     }
  208 
  209     // If we got a long line, discard characters until the newline.
  210     if (i == (SIZ-1)) {
  211         while (buf[i] != '\n') {
  212             serv_read(serv_sock, &buf[i], 1);
  213         }
  214     }
  215 
  216     // Strip all trailing nonprintables (crlf)
  217     buf[i] = 0;
  218 }
  219 
  220 
  221 // send line to server - implemented in terms of serv_write()
  222 void serv_puts(int serv_sock, char *buf) {
  223     serv_write(serv_sock, buf, strlen(buf));
  224     serv_write(serv_sock, "\n", 1);
  225 }
  226 
  227 
  228 // send formatted printable data to the server
  229 void serv_printf(int serv_sock, const char *format, ...) {   
  230     va_list arg_ptr;   
  231     char buf[1024];
  232    
  233     va_start(arg_ptr, format);   
  234     if (vsnprintf(buf, sizeof buf, format, arg_ptr) == -1)
  235         buf[sizeof buf - 2] = '\n';
  236     serv_write(serv_sock, buf, strlen(buf)); 
  237     va_end(arg_ptr);
  238 }
  239 
  240 
  241 // You know what main() does.  If you don't, you shouldn't be trying to understand this program.
  242 int main(int argc, char *argv[]) {
  243     char ctdldir[PATH_MAX]=CTDLDIR;
  244     char yesno[2];
  245     int cmdexit = 0;                // when something fails, set cmdexit to nonzero, and skip to the end
  246     char buf[PATH_MAX];
  247 
  248     char remote_user[128];
  249     char remote_host[128];
  250     char remote_pass[128];
  251     char *remote_port = "504";
  252 
  253     int linecount = 0;
  254     int a;
  255     int remote_server_socket = (-1);
  256     int local_admin_socket = (-1);
  257     int progress = 0;
  258 
  259     // Parse command line
  260     while ((a = getopt(argc, argv, "h:")) != EOF) {
  261         switch (a) {
  262         case 'h':
  263             strcpy(ctdldir, optarg);
  264             break;
  265         default:
  266             fprintf(stderr, "ctdlmigrate: usage: ctdlmigrate [-h server_dir]\n");
  267             return(1);
  268         }
  269     }
  270 
  271     if (chdir(ctdldir) != 0) {
  272         fprintf(stderr, "ctdlmigrate: %s: %s\n", ctdldir, strerror(errno));
  273         exit(errno);
  274     }
  275 
  276     signal(SIGINT, ctdlmigrate_exit);
  277     signal(SIGQUIT, ctdlmigrate_exit);
  278     signal(SIGTERM, ctdlmigrate_exit);
  279     signal(SIGSEGV, ctdlmigrate_exit);
  280     signal(SIGHUP, ctdlmigrate_exit);
  281     signal(SIGPIPE, ctdlmigrate_exit);
  282 
  283     printf( "\033[2J\033[H\n"
  284         "          \033[32m╔═══════════════════════════════════════════════╗\n"
  285         "          ║                                               ║\n"
  286         "          ║    \033[33mCitadel over-the-wire migration utility    \033[32m║\n"
  287         "          ║                                               ║\n"
  288         "          ╚═══════════════════════════════════════════════╝\033[0m\n"
  289         "\n"
  290         "This utility is designed to migrate your Citadel installation\n"
  291         "to a new host system via a network connection, without disturbing\n"
  292         "the source system.  The target may be a different CPU architecture\n"
  293         "and/or operating system.  The source system should be running\n"
  294         "Citadel version \033[33m%d\033[0m or newer, and the target system should be running\n"
  295         "either the same version or a newer version.\n"
  296         "\n"
  297         "You must run this utility on the TARGET SYSTEM.  Any existing data\n"
  298         "on this system will be ERASED.  Your target system will be on this\n"
  299         "host in %s.\n"
  300         "\n",
  301         EXPORT_REV_MIN, ctdldir
  302     );
  303 
  304     getz(yesno, 1, NULL, "Do you wish to continue? ");
  305     puts(yesno);
  306     if (tolower(yesno[0]) != 'y') {
  307         cmdexit = 101;
  308     }
  309 
  310     if (!cmdexit) {
  311         printf( "\033[2J\033[H\n"
  312             "\033[32m╔═══════════════════════════════════════════════╗\n"
  313             "║                                               ║\n"
  314             "║       \033[33mValidate source and target systems\033[32m      ║\n"
  315             "║                                               ║\n"
  316             "╚═══════════════════════════════════════════════╝\033[0m\n"
  317             "\n\n");
  318     
  319         printf("First we must validate that the local target system is running\n");
  320         printf("and ready to receive data.\n");
  321         printf("Checking connectivity to Citadel in %s...\n", ctdldir);
  322         local_admin_socket = uds_connectsock("citadel-admin.socket");
  323         if (local_admin_socket < 0) {
  324             cmdexit = 102;
  325         }
  326     }
  327 
  328     if (!cmdexit) {
  329         serv_gets(local_admin_socket, buf);
  330         puts(buf);
  331         if (buf[0] != '2') {
  332             cmdexit = 103;
  333         }
  334     }
  335 
  336     if (!cmdexit) {
  337         serv_puts(local_admin_socket, "ECHO Connection to Citadel Server succeeded.");
  338         serv_gets(local_admin_socket, buf);
  339         puts(buf);
  340         if (buf[0] != '2') {
  341             cmdexit = 104;
  342         }
  343     }
  344 
  345     if (!cmdexit) {
  346         printf("\n");
  347         printf("OK, this side is ready to go.  Now we must connect to the source system.\n\n");
  348 
  349         getz(remote_host, sizeof remote_host, NULL, "Enter the name or IP address of the source system: ");
  350         getz(remote_user, sizeof remote_user, "admin",  "  Enter the user name of an administrator account: ");
  351         getz(remote_pass, sizeof remote_pass, NULL, "              Enter the password for this account: ");
  352 
  353         remote_server_socket = tcp_connectsock(remote_host, remote_port);
  354         if (remote_server_socket < 0) {
  355             cmdexit = 105;
  356         }
  357     }
  358 
  359     if (!cmdexit) {
  360         serv_gets(remote_server_socket, buf);
  361         puts(buf);
  362         if (buf[0] != '2') {
  363             cmdexit = 106;
  364         }
  365     }
  366 
  367     if (!cmdexit) {
  368         serv_printf(remote_server_socket, "USER %s\n", remote_user);
  369         serv_gets(remote_server_socket, buf);
  370         puts(buf);
  371         if (buf[0] != '3') {
  372             cmdexit = 107;
  373         }
  374     }
  375 
  376     if (!cmdexit) {
  377         serv_printf(remote_server_socket, "PASS %s\n", remote_pass);
  378         serv_gets(remote_server_socket, buf);
  379         puts(buf);
  380         if (buf[0] != '2') {
  381             cmdexit = 108;
  382         }
  383     }
  384 
  385     if (!cmdexit) {
  386         printf( "\033[2J\033[H\n"
  387             "\033[32m╔═══════════════════════════════════════════════════════════════════╗\n"
  388             "║                                                                   ║\n"
  389             "║ \033[33mMigrating from: %-50s\033[32m║\n"
  390             "║                                                                   ║\n"
  391             "╠═══════════════════════════════════════════════════════════════════╣\n"
  392             "║                                                                   ║\n"
  393             "║ Lines received: 0                           Percent complete: 0   ║\n"
  394             "║                                                                   ║\n"
  395             "╚═══════════════════════════════════════════════════════════════════╝\033[0m\n"
  396             "\n", remote_host
  397         );
  398     }
  399 
  400     if (!cmdexit) {
  401         serv_puts(remote_server_socket, "MIGR export");
  402         serv_gets(remote_server_socket, buf);
  403         if (buf[0] != '1') {
  404             printf("\n\033[31m%s\033[0m\n", buf);
  405             cmdexit = 109;
  406         }
  407     }
  408 
  409     if (!cmdexit) {
  410         serv_puts(local_admin_socket, "MIGR import");
  411         serv_gets(local_admin_socket, buf);
  412         if (buf[0] != '4') {
  413             printf("\n\033[31m%s\033[0m\n", buf);
  414             cmdexit = 110;
  415         }
  416     }
  417 
  418     if (!cmdexit) {
  419         char *ptr;
  420         time_t last_update = time(NULL);
  421 
  422         while (serv_gets(remote_server_socket, buf), strcmp(buf, "000")) {
  423             ptr = strchr(buf, '\n');
  424             if (ptr) *ptr = 0;  // remove the newline character
  425             ++linecount;
  426             if (!strncasecmp(buf, "<progress>", 10)) {
  427                 progress = atoi(&buf[10]);
  428                 printf("\033[8;65H\033[33m%d\033[0m\033[12;0H\n", progress);
  429             }
  430             if (time(NULL) != last_update) {
  431                 last_update = time(NULL);
  432                 printf("\033[8;19H\033[33m%d\033[0m\033[12;0H\n", linecount);
  433             }
  434             serv_puts(local_admin_socket, buf);
  435         }
  436     
  437         serv_puts(local_admin_socket, "000");
  438     }
  439 
  440     if ( (cmdexit == 0) && (progress < 100) ) {
  441         printf("\033[31mERROR: source stream ended before 100 percent of data was received.\033[0m\n");
  442         cmdexit = 111;
  443     }
  444 
  445     if (!cmdexit) {
  446         printf("\033[36mMigration is complete.  Restarting the target server.\033[0m\n");
  447         serv_puts(local_admin_socket, "DOWN 1");
  448         serv_gets(local_admin_socket, buf);
  449         puts(buf);
  450         printf("\033[36mIt is recommended that you shut down the source server now.\033[0m\n");
  451         printf("\033[36mRemember to copy over your SSL keys and file libraries if appropriate.\033[0m\n");
  452     }
  453 
  454     close(remote_server_socket);
  455     close(local_admin_socket);
  456     ctdlmigrate_exit(cmdexit);
  457 }