* sendcommand default watchdog timeout set to 60 seconds. 5 seconds is way too low.
[citadel.git] / citadel / ctdlmigrate.c
1 /*
2  * $Id: $
3  *
4  * Across-the-wire migration utility for Citadel
5  *
6  * Copyright (c) 2009 citadel.org
7  *
8  * This program is licensed to you under the terms of the GNU General Public License v3
9  *
10  */
11
12 #include <stdlib.h>
13 #include <unistd.h>
14 #include <stdio.h>
15 #include <string.h>
16 #include <ctype.h>
17 #include <fcntl.h>
18 #include <sys/types.h>
19 #include <sys/stat.h>
20 #include <sys/utsname.h>
21 #include <sys/wait.h>
22 #include <signal.h>
23 #include <netdb.h>
24 #include <errno.h>
25 #include <limits.h>
26 #include <pwd.h>
27 #include <time.h>
28 #include <libcitadel.h>
29 #include "citadel.h"
30 #include "axdefs.h"
31 #include "sysdep.h"
32 #include "config.h"
33 #include "citadel_dirs.h"
34 #if HAVE_BACKTRACE
35 #include <execinfo.h>
36 #endif
37
38
39
40
41 int main(int argc, char *argv[])
42 {
43         int relh=0;
44         int home=0;
45         char relhome[PATH_MAX]="";
46         char ctdldir[PATH_MAX]=CTDLDIR;
47         char yesno[5];
48         char sendcommand[PATH_MAX];
49         int exitcode;
50         char cmd[PATH_MAX];
51         char buf[PATH_MAX];
52         char socket_path[PATH_MAX];
53         char remote_user[256];
54         char remote_host[256];
55         char remote_sendcommand[PATH_MAX];
56         FILE *source_artv;
57         FILE *target_artv;
58         int linecount = 0;
59         char spinning[4] = "-\\|/" ;
60         
61         calc_dirs_n_files(relh, home, relhome, ctdldir, 0);
62         CtdlMakeTempFileName(socket_path, sizeof socket_path);
63
64         system("clear");
65         printf( "-------------------------------------------\n"
66                 "Over-the-wire migration utility for Citadel\n"
67                 "-------------------------------------------\n"
68                 "\n"
69                 "This utility is designed to migrate your Citadel installation\n"
70                 "to a new host system via a network connection, without disturbing\n"
71                 "the source system.  The target may be a different CPU architecture\n"
72                 "and/or operating system.  The source system should be running\n"
73                 "Citadel %d.%02d or newer, and the target system should be running\n"
74                 "either the same version or a newer version.  You will also need\n"
75                 "the 'rsync' utility, and OpenSSH v4 or newer.\n"
76                 "\n"
77                 "You must run this utility on the TARGET SYSTEM.  Any existing data\n"
78                 "on this system will be ERASED.\n"
79                 "\n"
80                 "Do you wish to continue? "
81                 ,
82                 EXPORT_REV_MIN / 100,
83                 EXPORT_REV_MIN % 100
84         );
85
86         if ((fgets(yesno, sizeof yesno, stdin) == NULL) || (tolower(yesno[0]) != 'y')) {
87                 exit(0);
88         }
89
90         printf("\n\nGreat!  First we will check some things out here on our target\n"
91                 "system to make sure it is ready to receive data.\n\n");
92
93         printf("Locating 'sendcommand' and checking connectivity to Citadel...\n");
94         snprintf(sendcommand, sizeof sendcommand, "%s/sendcommand", ctdl_utilbin_dir);
95         snprintf(cmd, sizeof cmd, "%s 'NOOP'", sendcommand);
96         exitcode = system(cmd);
97         if (exitcode != 0) {
98                 printf("\nctdlmigrate was unable to attach to the Citadel server\n"
99                         "here on the target system.  Is Citadel running?\n\n");
100                 exit(1);
101         }
102         printf("\nOK, this side is ready to go.  Now we must connect to the source system.\n\n");
103
104         printf("Enter the host name or IP address of the source system\n"
105                 "(example: ctdl.foo.org)\n"
106                 "--> ");
107         gets(remote_host);
108         printf("\nEnter the name of a user on %s who has full access to Citadel files\n"
109                 "(usually root)\n--> ",
110                 remote_host);
111         gets(remote_user);
112
113         printf("\nEstablishing an SSH connection to the source system...\n\n");
114         unlink(socket_path);
115         snprintf(cmd, sizeof cmd, "ssh -MNf -S %s %s@%s", socket_path, remote_user, remote_host);
116         exitcode = system(cmd);
117         if (exitcode != 0) {
118                 printf("\nctdlmigrate was unable to establish an SSH connection to the\n"
119                         "source system, and cannot continue.\n\n");
120                 exit(exitcode);
121         }
122
123         printf("\nTesting a command over the connection...\n\n");
124         snprintf(cmd, sizeof cmd, "ssh -S %s %s@%s 'echo Remote commands are executing successfully.'",
125                 socket_path, remote_user, remote_host);
126         exitcode = system(cmd);
127         printf("\n");
128         if (exitcode != 0) {
129                 printf("Remote commands are not succeeding.\n\n");
130                 exit(exitcode);
131         }
132
133         printf("\nLocating the remote 'sendcommand' and Citadel installation...\n");
134         snprintf(remote_sendcommand, sizeof remote_sendcommand, "/usr/local/citadel/sendcommand");
135         snprintf(cmd, sizeof cmd, "ssh -S %s %s@%s %s NOOP",
136                 socket_path, remote_user, remote_host, remote_sendcommand);
137         exitcode = system(cmd);
138         if (exitcode != 0) {
139                 snprintf(remote_sendcommand, sizeof remote_sendcommand, "/usr/sbin/sendcommand");
140                 snprintf(cmd, sizeof cmd, "ssh -S %s %s@%s %s NOOP",
141                         socket_path, remote_user, remote_host, remote_sendcommand);
142                 exitcode = system(cmd);
143         }
144         if (exitcode != 0) {
145                 printf("\nUnable to locate Citadel programs on the remote system.  Please enter\n"
146                         "the name of the directory on %s which contains the 'sendcommand' program.\n"
147                         "(example: /opt/foo/citadel)\n"
148                         "--> ", remote_host);
149                 gets(buf);
150                 snprintf(remote_sendcommand, sizeof remote_sendcommand, "%s/sendcommand", buf);
151                 snprintf(cmd, sizeof cmd, "ssh -S %s %s@%s %s NOOP",
152                         socket_path, remote_user, remote_host, remote_sendcommand);
153                 exitcode = system(cmd);
154         }
155         printf("\n");
156         if (exitcode != 0) {
157                 printf("ctdlmigrate was unable to attach to the remote Citadel system.\n\n");
158                 exit(exitcode);
159         }
160
161         printf("ctdlmigrate will now begin a database migration...\n");
162
163         snprintf(cmd, sizeof cmd, "ssh -S %s %s@%s %s -w3600 MIGR export",
164                 socket_path, remote_user, remote_host, remote_sendcommand);
165         source_artv = popen(cmd, "r");
166         if (!source_artv) {
167                 printf("\n%s\n\n", strerror(errno));
168                 exit(2);
169         }
170
171         snprintf(cmd, sizeof cmd, "%s -w3600 MIGR import", sendcommand);
172         target_artv = popen(cmd, "w");
173         if (!target_artv) {
174                 printf("\n%s\n\n", strerror(errno));
175                 exit(3);
176         }
177
178         while (fgets(buf, sizeof buf, source_artv) != NULL) {
179                 fwrite(buf, strlen(buf), 1, target_artv);
180                 ++linecount;
181                 if ((linecount % 100) == 0) {
182                         printf("%c\r", spinning[((linecount / 100) % 4)]);
183                         fflush(stdout);
184                 }
185         }
186
187         pclose(source_artv);
188         pclose(target_artv);
189
190         // FIXME handle -h on both sides
191         // FIXME kill the master ssh session
192         printf("If this program was finished we would do more.  FIXME\n");
193         exit(0);
194 }