Back to index

citadel  8.12
ctdlmigrate.c
Go to the documentation of this file.
00001 /*
00002  * Across-the-wire migration utility for Citadel
00003  *
00004  * Yes, we used goto, and gets(), and committed all sorts of other heinous sins here.
00005  * The scope of this program isn't wide enough to make a difference.  If you don't like
00006  * it you can rewrite it.
00007  *
00008  * Copyright (c) 2009-2012 citadel.org
00009  *
00010  * This program is open source software; you can redistribute it and/or modify
00011  * it under the terms of the GNU General Public License version 3.
00012  *
00013  * This program is distributed in the hope that it will be useful,
00014  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00015  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00016  * GNU General Public License for more details.
00017  *
00018  * (Note: a useful future enhancement might be to support "-h" on both sides)
00019  *
00020  */
00021 
00022 #include <stdlib.h>
00023 #include <unistd.h>
00024 #include <stdio.h>
00025 #include <string.h>
00026 #include <ctype.h>
00027 #include <fcntl.h>
00028 #include <sys/types.h>
00029 #include <sys/stat.h>
00030 #include <sys/utsname.h>
00031 #include <sys/wait.h>
00032 #include <signal.h>
00033 #include <netdb.h>
00034 #include <errno.h>
00035 #include <limits.h>
00036 #include <pwd.h>
00037 #include <time.h>
00038 #include <libcitadel.h>
00039 #include "citadel.h"
00040 #include "axdefs.h"
00041 #include "sysdep.h"
00042 #include "config.h"
00043 #include "citadel_dirs.h"
00044 #if HAVE_BACKTRACE
00045 #include <execinfo.h>
00046 #endif
00047 
00048 
00049 
00050 /*
00051  * Replacement for gets() that doesn't throw a compiler warning.
00052  * We're only using it for some simple prompts, so we don't need
00053  * to worry about attackers exploiting it.
00054  */
00055 void getz(char *buf) {
00056        char *ptr;
00057 
00058        ptr = fgets(buf, 32767, stdin);
00059        if (!ptr) {
00060               buf[0] = 0;
00061               return;
00062        }
00063 
00064        ptr = strchr(buf, '\n');
00065        if (ptr) *ptr = 0;
00066 }
00067 
00068 
00069 
00070 
00071 
00072 int main(int argc, char *argv[])
00073 {
00074        int relh=0;
00075        int home=0;
00076        char relhome[PATH_MAX]="";
00077        char ctdldir[PATH_MAX]=CTDLDIR;
00078        char yesno[5];
00079        char sendcommand[PATH_MAX];
00080        int cmdexit;
00081        char cmd[PATH_MAX];
00082        char buf[PATH_MAX];
00083        char socket_path[PATH_MAX];
00084        char remote_user[256];
00085        char remote_host[256];
00086        char remote_sendcommand[PATH_MAX];
00087        FILE *sourcefp = NULL;
00088        FILE *targetfp = NULL;
00089        int linecount = 0;
00090        char spinning[4] = "-\\|/" ;
00091        int exitcode = 0;
00092        
00093        calc_dirs_n_files(relh, home, relhome, ctdldir, 0);
00094        CtdlMakeTempFileName(socket_path, sizeof socket_path);
00095 
00096        cmdexit = system("clear");
00097        printf(       "-------------------------------------------\n"
00098               "Over-the-wire migration utility for Citadel\n"
00099               "-------------------------------------------\n"
00100               "\n"
00101               "This utility is designed to migrate your Citadel installation\n"
00102               "to a new host system via a network connection, without disturbing\n"
00103               "the source system.  The target may be a different CPU architecture\n"
00104               "and/or operating system.  The source system should be running\n"
00105               "Citadel %d.%02d or newer, and the target system should be running\n"
00106               "either the same version or a newer version.  You will also need\n"
00107               "the 'rsync' utility, and OpenSSH v4 or newer.\n"
00108               "\n"
00109               "You must run this utility on the TARGET SYSTEM.  Any existing data\n"
00110               "on this system will be ERASED.\n"
00111               "\n"
00112               "Do you wish to continue? "
00113               ,
00114               EXPORT_REV_MIN / 100,
00115               EXPORT_REV_MIN % 100
00116        );
00117 
00118        if ((fgets(yesno, sizeof yesno, stdin) == NULL) || (tolower(yesno[0]) != 'y')) {
00119               exit(0);
00120        }
00121 
00122        printf("\n\nGreat!  First we will check some things out here on our target\n"
00123               "system to make sure it is ready to receive data.\n\n");
00124 
00125        printf("Locating 'sendcommand' and checking connectivity to Citadel...\n");
00126        snprintf(sendcommand, sizeof sendcommand, "%s/sendcommand", ctdl_utilbin_dir);
00127        snprintf(cmd, sizeof cmd, "%s 'NOOP'", sendcommand);
00128        cmdexit = system(cmd);
00129        if (cmdexit != 0) {
00130               printf("\nctdlmigrate was unable to attach to the Citadel server\n"
00131                      "here on the target system.  Is Citadel running?\n\n");
00132               exit(1);
00133        }
00134        printf("\nOK, this side is ready to go.  Now we must connect to the source system.\n\n");
00135 
00136        printf("Enter the host name or IP address of the source system\n"
00137               "(example: ctdl.foo.org)\n"
00138               "--> ");
00139        getz(remote_host);
00140        printf("\nEnter the name of a user on %s who has full access to Citadel files\n"
00141               "(usually root)\n--> ",
00142               remote_host);
00143        getz(remote_user);
00144 
00145        printf("\nEstablishing an SSH connection to the source system...\n\n");
00146        unlink(socket_path);
00147        snprintf(cmd, sizeof cmd, "ssh -MNf -S %s %s@%s", socket_path, remote_user, remote_host);
00148        cmdexit = system(cmd);
00149        printf("\n");
00150        if (cmdexit != 0) {
00151               printf("This program was unable to establish an SSH session to the source system.\n\n");
00152               exitcode = cmdexit;
00153               goto THEEND;
00154        }
00155 
00156        printf("\nTesting a command over the connection...\n\n");
00157        snprintf(cmd, sizeof cmd, "ssh -S %s %s@%s 'echo Remote commands are executing successfully.'",
00158               socket_path, remote_user, remote_host);
00159        cmdexit = system(cmd);
00160        printf("\n");
00161        if (cmdexit != 0) {
00162               printf("Remote commands are not succeeding.\n\n");
00163               exitcode = cmdexit;
00164               goto THEEND;
00165        }
00166 
00167        printf("\nLocating the remote 'sendcommand' and Citadel installation...\n");
00168        snprintf(remote_sendcommand, sizeof remote_sendcommand, "/usr/local/citadel/sendcommand");
00169        snprintf(cmd, sizeof cmd, "ssh -S %s %s@%s %s NOOP",
00170               socket_path, remote_user, remote_host, remote_sendcommand);
00171        cmdexit = system(cmd);
00172        if (cmdexit != 0) {
00173               snprintf(remote_sendcommand, sizeof remote_sendcommand, "/usr/sbin/sendcommand");
00174               snprintf(cmd, sizeof cmd, "ssh -S %s %s@%s %s NOOP",
00175                      socket_path, remote_user, remote_host, remote_sendcommand);
00176               cmdexit = system(cmd);
00177        }
00178        if (cmdexit != 0) {
00179               printf("\nUnable to locate Citadel programs on the remote system.  Please enter\n"
00180                      "the name of the directory on %s which contains the 'sendcommand' program.\n"
00181                      "(example: /opt/foo/citadel)\n"
00182                      "--> ", remote_host);
00183               getz(buf);
00184               snprintf(remote_sendcommand, sizeof remote_sendcommand, "%s/sendcommand", buf);
00185               snprintf(cmd, sizeof cmd, "ssh -S %s %s@%s %s NOOP",
00186                      socket_path, remote_user, remote_host, remote_sendcommand);
00187               cmdexit = system(cmd);
00188        }
00189        printf("\n");
00190        if (cmdexit != 0) {
00191               printf("ctdlmigrate was unable to attach to the remote Citadel system.\n\n");
00192               exitcode = cmdexit;
00193               goto THEEND;
00194        }
00195 
00196        printf("ctdlmigrate will now begin a database migration...\n");
00197 
00198        snprintf(cmd, sizeof cmd, "ssh -S %s %s@%s %s -w3600 MIGR export",
00199               socket_path, remote_user, remote_host, remote_sendcommand);
00200        sourcefp = popen(cmd, "r");
00201        if (!sourcefp) {
00202               printf("\n%s\n\n", strerror(errno));
00203               exitcode = 2;
00204               goto THEEND;
00205        }
00206 
00207        snprintf(cmd, sizeof cmd, "%s -w3600 MIGR import", sendcommand);
00208        targetfp = popen(cmd, "w");
00209        if (!targetfp) {
00210               printf("\n%s\n\n", strerror(errno));
00211               exitcode = 3;
00212               goto THEEND;
00213        }
00214 
00215        while (fgets(buf, sizeof buf, sourcefp) != NULL) {
00216               if (fwrite(buf, strlen(buf), 1, targetfp) < 1) {
00217                      exitcode = 4;
00218                      printf("%s\n", strerror(errno));
00219                      goto FAIL;
00220               }
00221               ++linecount;
00222               if ((linecount % 100) == 0) {
00223                      printf("%c\r", spinning[((linecount / 100) % 4)]);
00224                      fflush(stdout);
00225               }
00226        }
00227 
00228 FAIL:  if (sourcefp) pclose(sourcefp);
00229        if (targetfp) pclose(targetfp);
00230        if (exitcode != 0) goto THEEND;
00231 
00232        /* We need to copy a bunch of other stuff, and will do so using rsync */
00233 
00234        snprintf(cmd, sizeof cmd, "ssh -S %s %s@%s %s MIGR listdirs",
00235               socket_path, remote_user, remote_host, remote_sendcommand);
00236        sourcefp = popen(cmd, "r");
00237        if (!sourcefp) {
00238               printf("\n%s\n\n", strerror(errno));
00239               exitcode = 2;
00240               goto THEEND;
00241        }
00242        while ((fgets(buf, sizeof buf, sourcefp)) && (strcmp(buf, "000"))) {
00243               buf[strlen(buf)-1] = 0;
00244 
00245               if (!strncasecmp(buf, "bio|", 4)) {
00246                      snprintf(cmd, sizeof cmd, "rsync -va --rsh='ssh -S %s' %s@%s:%s/ %s/",
00247                             socket_path, remote_user, remote_host, &buf[4], ctdl_bio_dir);
00248               }
00249               else if (!strncasecmp(buf, "files|", 6)) {
00250                      snprintf(cmd, sizeof cmd, "rsync -va --rsh='ssh -S %s' %s@%s:%s/ %s/",
00251                             socket_path, remote_user, remote_host, &buf[6], ctdl_file_dir);
00252               }
00253               else if (!strncasecmp(buf, "userpics|", 9)) {
00254                      snprintf(cmd, sizeof cmd, "rsync -va --rsh='ssh -S %s' %s@%s:%s/ %s/",
00255                             socket_path, remote_user, remote_host, &buf[9], ctdl_usrpic_dir);
00256               }
00257               else if (!strncasecmp(buf, "messages|", 9)) {
00258                      snprintf(cmd, sizeof cmd, "rsync -va --rsh='ssh -S %s' %s@%s:%s/ %s/",
00259                             socket_path, remote_user, remote_host, &buf[9], ctdl_message_dir);
00260               }
00261               else if (!strncasecmp(buf, "netconfigs|", 11)) {
00262                      snprintf(cmd, sizeof cmd, "rsync -va --rsh='ssh -S %s' %s@%s:%s/ %s/",
00263                             socket_path, remote_user, remote_host, &buf[11], ctdl_netcfg_dir);
00264               }
00265               else if (!strncasecmp(buf, "keys|", 5)) {
00266                      snprintf(cmd, sizeof cmd, "rsync -va --rsh='ssh -S %s' %s@%s:%s/ %s/",
00267                             socket_path, remote_user, remote_host, &buf[5], ctdl_key_dir);
00268               }
00269               else if (!strncasecmp(buf, "images|", 7)) {
00270                      snprintf(cmd, sizeof cmd, "rsync -va --rsh='ssh -S %s' %s@%s:%s/ %s/",
00271                             socket_path, remote_user, remote_host, &buf[7], ctdl_image_dir);
00272               }
00273               else if (!strncasecmp(buf, "info|", 5)) {
00274                      snprintf(cmd, sizeof cmd, "rsync -va --rsh='ssh -S %s' %s@%s:%s/ %s/",
00275                             socket_path, remote_user, remote_host, &buf[5], ctdl_info_dir);
00276               }
00277               else {
00278                      strcpy(cmd, "false");       /* cheap and sleazy way to throw an error */
00279               }
00280               printf("%s\n", cmd);
00281               cmdexit = system(cmd);
00282               if (cmdexit != 0) {
00283                      exitcode += cmdexit;
00284               }
00285        }
00286        pclose(sourcefp);
00287 
00288 THEEND:       if (exitcode == 0) {
00289               printf("\n\n *** Citadel migration was successful! *** \n\n");
00290        }
00291        else {
00292               printf("\n\n *** Citadel migration was unsuccessful. *** \n\n");
00293        }
00294        printf("\nShutting down the socket connection...\n\n");
00295        snprintf(cmd, sizeof cmd, "ssh -S %s -N -O exit %s@%s",
00296               socket_path, remote_user, remote_host);
00297        cmdexit = system(cmd);
00298        printf("\n");
00299        if (cmdexit != 0) {
00300               printf("There was an error shutting down the socket.\n\n");
00301               exitcode = cmdexit;
00302        }
00303 
00304        unlink(socket_path);
00305        exit(exitcode);
00306 }