* Delete socket file after ctdlmigrate runs
[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  * Replacement for gets() that doesn't throw a compiler warning.
42  * We're only using it for some simple prompts, so we don't need
43  * to worry about attackers exploiting it.
44  */
45 void getz(char *buf) {
46         char *ptr;
47
48         ptr = fgets(buf, 32767, stdin);
49         if (!ptr) {
50                 buf[0] = 0;
51                 return;
52         }
53
54         ptr = strchr(buf, '\n');
55         if (ptr) *ptr = 0;
56 }
57
58
59
60
61
62 int main(int argc, char *argv[])
63 {
64         int relh=0;
65         int home=0;
66         char relhome[PATH_MAX]="";
67         char ctdldir[PATH_MAX]=CTDLDIR;
68         char yesno[5];
69         char sendcommand[PATH_MAX];
70         int cmdexit;
71         char cmd[PATH_MAX];
72         char buf[PATH_MAX];
73         char socket_path[PATH_MAX];
74         char remote_user[256];
75         char remote_host[256];
76         char remote_sendcommand[PATH_MAX];
77         FILE *source_artv;
78         FILE *target_artv;
79         int linecount = 0;
80         char spinning[4] = "-\\|/" ;
81         int exitcode = 0;
82         
83         calc_dirs_n_files(relh, home, relhome, ctdldir, 0);
84         CtdlMakeTempFileName(socket_path, sizeof socket_path);
85
86         system("clear");
87         printf( "-------------------------------------------\n"
88                 "Over-the-wire migration utility for Citadel\n"
89                 "-------------------------------------------\n"
90                 "\n"
91                 "This utility is designed to migrate your Citadel installation\n"
92                 "to a new host system via a network connection, without disturbing\n"
93                 "the source system.  The target may be a different CPU architecture\n"
94                 "and/or operating system.  The source system should be running\n"
95                 "Citadel %d.%02d or newer, and the target system should be running\n"
96                 "either the same version or a newer version.  You will also need\n"
97                 "the 'rsync' utility, and OpenSSH v4 or newer.\n"
98                 "\n"
99                 "You must run this utility on the TARGET SYSTEM.  Any existing data\n"
100                 "on this system will be ERASED.\n"
101                 "\n"
102                 "Do you wish to continue? "
103                 ,
104                 EXPORT_REV_MIN / 100,
105                 EXPORT_REV_MIN % 100
106         );
107
108         if ((fgets(yesno, sizeof yesno, stdin) == NULL) || (tolower(yesno[0]) != 'y')) {
109                 exit(0);
110         }
111
112         printf("\n\nGreat!  First we will check some things out here on our target\n"
113                 "system to make sure it is ready to receive data.\n\n");
114
115         printf("Locating 'sendcommand' and checking connectivity to Citadel...\n");
116         snprintf(sendcommand, sizeof sendcommand, "%s/sendcommand", ctdl_utilbin_dir);
117         snprintf(cmd, sizeof cmd, "%s 'NOOP'", sendcommand);
118         cmdexit = system(cmd);
119         if (cmdexit != 0) {
120                 printf("\nctdlmigrate was unable to attach to the Citadel server\n"
121                         "here on the target system.  Is Citadel running?\n\n");
122                 exit(1);
123         }
124         printf("\nOK, this side is ready to go.  Now we must connect to the source system.\n\n");
125
126         printf("Enter the host name or IP address of the source system\n"
127                 "(example: ctdl.foo.org)\n"
128                 "--> ");
129         getz(remote_host);
130         printf("\nEnter the name of a user on %s who has full access to Citadel files\n"
131                 "(usually root)\n--> ",
132                 remote_host);
133         getz(remote_user);
134
135         printf("\nEstablishing an SSH connection to the source system...\n\n");
136         unlink(socket_path);
137         snprintf(cmd, sizeof cmd, "ssh -MNf -S %s %s@%s", socket_path, remote_user, remote_host);
138         cmdexit = system(cmd);
139         if (cmdexit != 0) {
140                 printf("\nctdlmigrate was unable to establish an SSH connection to the\n"
141                         "source system, and cannot continue.\n\n");
142                 exitcode = cmdexit;
143                 goto THEEND;
144         }
145
146         printf("\nTesting a command over the connection...\n\n");
147         snprintf(cmd, sizeof cmd, "ssh -S %s %s@%s 'echo Remote commands are executing successfully.'",
148                 socket_path, remote_user, remote_host);
149         cmdexit = system(cmd);
150         printf("\n");
151         if (cmdexit != 0) {
152                 printf("Remote commands are not succeeding.\n\n");
153                 exitcode = cmdexit;
154                 goto THEEND;
155         }
156
157         printf("\nLocating the remote 'sendcommand' and Citadel installation...\n");
158         snprintf(remote_sendcommand, sizeof remote_sendcommand, "/usr/local/citadel/sendcommand");
159         snprintf(cmd, sizeof cmd, "ssh -S %s %s@%s %s NOOP",
160                 socket_path, remote_user, remote_host, remote_sendcommand);
161         cmdexit = system(cmd);
162         if (cmdexit != 0) {
163                 snprintf(remote_sendcommand, sizeof remote_sendcommand, "/usr/sbin/sendcommand");
164                 snprintf(cmd, sizeof cmd, "ssh -S %s %s@%s %s NOOP",
165                         socket_path, remote_user, remote_host, remote_sendcommand);
166                 cmdexit = system(cmd);
167         }
168         if (cmdexit != 0) {
169                 printf("\nUnable to locate Citadel programs on the remote system.  Please enter\n"
170                         "the name of the directory on %s which contains the 'sendcommand' program.\n"
171                         "(example: /opt/foo/citadel)\n"
172                         "--> ", remote_host);
173                 getz(buf);
174                 snprintf(remote_sendcommand, sizeof remote_sendcommand, "%s/sendcommand", buf);
175                 snprintf(cmd, sizeof cmd, "ssh -S %s %s@%s %s NOOP",
176                         socket_path, remote_user, remote_host, remote_sendcommand);
177                 cmdexit = system(cmd);
178         }
179         printf("\n");
180         if (cmdexit != 0) {
181                 printf("ctdlmigrate was unable to attach to the remote Citadel system.\n\n");
182                 exitcode = cmdexit;
183                 goto THEEND;
184         }
185
186         printf("ctdlmigrate will now begin a database migration...\n");
187
188         snprintf(cmd, sizeof cmd, "ssh -S %s %s@%s %s -w3600 MIGR export",
189                 socket_path, remote_user, remote_host, remote_sendcommand);
190         source_artv = popen(cmd, "r");
191         if (!source_artv) {
192                 printf("\n%s\n\n", strerror(errno));
193                 exitcode = 2;
194                 goto THEEND;
195         }
196
197         snprintf(cmd, sizeof cmd, "%s -w3600 MIGR import", sendcommand);
198         target_artv = popen(cmd, "w");
199         if (!target_artv) {
200                 printf("\n%s\n\n", strerror(errno));
201                 exitcode = 3;
202                 goto THEEND;
203         }
204
205         while (fgets(buf, sizeof buf, source_artv) != NULL) {
206                 if (fwrite(buf, strlen(buf), 1, target_artv) < 1) {
207                         exitcode = 4;
208                         printf("%s\n", strerror(errno));
209                         goto FAIL;
210                 }
211                 ++linecount;
212                 if ((linecount % 100) == 0) {
213                         printf("%c\r", spinning[((linecount / 100) % 4)]);
214                         fflush(stdout);
215                 }
216         }
217
218 FAIL:   pclose(source_artv);
219         pclose(target_artv);
220
221         // FIXME handle -h on both sides
222         // FIXME kill the master ssh session
223         printf("If this program was finished we would do more.  FIXME\n");
224
225 THEEND: unlink(socket_path);
226         exit(exitcode);
227 }