2 * Across-the-wire migration utility for Citadel
4 * Yes, we used goto, and gets(), and committed all sorts of other heinous sins here.
5 * The scope of this program isn't wide enough to make a difference. If you don't like
6 * it you can rewrite it.
8 * Copyright (c) 2009-2016 citadel.org
10 * This program is open source software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License version 3.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * (Note: a useful future enhancement might be to support "-h" on both sides)
28 #include <sys/types.h>
30 #include <sys/utsname.h>
38 #include <libcitadel.h>
43 #include "citadel_dirs.h"
51 * Replacement for gets() that doesn't throw a compiler warning.
52 * We're only using it for some simple prompts, so we don't need
53 * to worry about attackers exploiting it.
55 void getz(char *buf) {
58 ptr = fgets(buf, SIZ, stdin);
64 ptr = strchr(buf, '\n');
72 int main(int argc, char *argv[])
76 char relhome[PATH_MAX]="";
77 char ctdldir[PATH_MAX]=CTDLDIR;
79 char sendcommand[PATH_MAX];
83 char socket_path[PATH_MAX];
84 char remote_user[SIZ];
85 char remote_host[SIZ];
86 char remote_sendcommand[PATH_MAX];
87 FILE *sourcefp = NULL;
88 FILE *targetfp = NULL;
90 char spinning[4] = "-\\|/" ;
93 calc_dirs_n_files(relh, home, relhome, ctdldir, 0);
94 CtdlMakeTempFileName(socket_path, sizeof socket_path);
96 cmdexit = system("clear");
97 printf( "-------------------------------------------\n"
98 "Over-the-wire migration utility for Citadel\n"
99 "-------------------------------------------\n"
101 "This utility is designed to migrate your Citadel installation\n"
102 "to a new host system via a network connection, without disturbing\n"
103 "the source system. The target may be a different CPU architecture\n"
104 "and/or operating system. The source system should be running\n"
105 "Citadel version %d or newer, and the target system should be running\n"
106 "either the same version or a newer version. You will also need\n"
107 "the 'rsync' utility, and OpenSSH v4 or newer.\n"
109 "You must run this utility on the TARGET SYSTEM. Any existing data\n"
110 "on this system will be ERASED.\n"
112 "Do you wish to continue? "
117 if ((fgets(yesno, sizeof yesno, stdin) == NULL) || (tolower(yesno[0]) != 'y')) {
121 printf("\n\nGreat! First we will check some things out here on our target\n"
122 "system to make sure it is ready to receive data.\n\n");
124 printf("Locating 'sendcommand' and checking connectivity to Citadel...\n");
125 snprintf(sendcommand, sizeof sendcommand, "%s/sendcommand", ctdl_utilbin_dir);
126 snprintf(cmd, sizeof cmd, "%s 'NOOP'", sendcommand);
127 cmdexit = system(cmd);
129 printf("\nctdlmigrate was unable to attach to the Citadel server\n"
130 "here on the target system. Is Citadel running?\n\n");
133 printf("\nOK, this side is ready to go. Now we must connect to the source system.\n\n");
135 printf("Enter the host name or IP address of the source system\n"
136 "(example: ctdl.foo.org)\n"
141 printf("\nEnter the name of a user on %s who has full access to Citadel files\n"
142 "(usually root)\n--> ",
145 if (IsEmptyStr(remote_user))
146 goto get_remote_user;
148 printf("\nEstablishing an SSH connection to the source system...\n\n");
150 snprintf(cmd, sizeof cmd, "ssh -MNf -S %s %s@%s", socket_path, remote_user, remote_host);
151 cmdexit = system(cmd);
154 printf("This program was unable to establish an SSH session to the source system.\n\n");
159 printf("\nTesting a command over the connection...\n\n");
160 snprintf(cmd, sizeof cmd, "ssh -S %s %s@%s 'echo Remote commands are executing successfully.'",
161 socket_path, remote_user, remote_host);
162 cmdexit = system(cmd);
165 printf("Remote commands are not succeeding.\n\n");
170 printf("\nLocating the remote 'sendcommand' and Citadel installation...\n");
171 snprintf(remote_sendcommand, sizeof remote_sendcommand, "/usr/local/citadel/sendcommand");
172 snprintf(cmd, sizeof cmd, "ssh -S %s %s@%s %s NOOP",
173 socket_path, remote_user, remote_host, remote_sendcommand);
174 cmdexit = system(cmd);
176 snprintf(remote_sendcommand, sizeof remote_sendcommand, "/usr/sbin/sendcommand");
177 snprintf(cmd, sizeof cmd, "ssh -S %s %s@%s %s NOOP",
178 socket_path, remote_user, remote_host, remote_sendcommand);
179 cmdexit = system(cmd);
182 printf("\nUnable to locate Citadel programs on the remote system. Please enter\n"
183 "the name of the directory on %s which contains the 'sendcommand' program.\n"
184 "(example: /opt/foo/citadel)\n"
185 "--> ", remote_host);
187 snprintf(remote_sendcommand, sizeof remote_sendcommand, "%s/sendcommand", buf);
188 snprintf(cmd, sizeof cmd, "ssh -S %s %s@%s %s NOOP",
189 socket_path, remote_user, remote_host, remote_sendcommand);
190 cmdexit = system(cmd);
194 printf("ctdlmigrate was unable to attach to the remote Citadel system.\n\n");
199 printf("ctdlmigrate will now begin a database migration...\n");
200 printf(" if the system doesn't start working, \n");
201 printf(" have a look at the syslog for pending jobs needing to be terminated.\n");
203 snprintf(cmd, sizeof cmd, "ssh -S %s %s@%s %s -w3600 MIGR export",
204 socket_path, remote_user, remote_host, remote_sendcommand);
205 sourcefp = popen(cmd, "r");
207 printf("\n%s\n\n", strerror(errno));
212 snprintf(cmd, sizeof cmd, "%s -w3600 MIGR import", sendcommand);
213 targetfp = popen(cmd, "w");
215 printf("\n%s\n\n", strerror(errno));
220 while (fgets(buf, sizeof buf, sourcefp) != NULL) {
221 if (fwrite(buf, strlen(buf), 1, targetfp) < 1) {
223 printf("%s\n", strerror(errno));
227 if ((linecount % 100) == 0) {
228 printf("%c\r", spinning[((linecount / 100) % 4)]);
233 FAIL: if (sourcefp) pclose(sourcefp);
234 if (targetfp) pclose(targetfp);
235 if (exitcode != 0) goto THEEND;
237 /* We need to copy a bunch of other stuff, and will do so using rsync */
239 snprintf(cmd, sizeof cmd, "ssh -S %s %s@%s %s MIGR listdirs",
240 socket_path, remote_user, remote_host, remote_sendcommand);
241 sourcefp = popen(cmd, "r");
243 printf("\n%s\n\n", strerror(errno));
247 while ((fgets(buf, sizeof buf, sourcefp)) && (strcmp(buf, "000"))) {
248 buf[strlen(buf)-1] = 0;
250 if (!strncasecmp(buf, "files|", 6)) {
251 snprintf(cmd, sizeof cmd, "rsync -va --rsh='ssh -S %s' %s@%s:%s/ %s/",
252 socket_path, remote_user, remote_host, &buf[6], ctdl_file_dir);
254 else if (!strncasecmp(buf, "messages|", 9)) {
255 snprintf(cmd, sizeof cmd, "rsync -va --rsh='ssh -S %s' %s@%s:%s/ %s/",
256 socket_path, remote_user, remote_host, &buf[9], ctdl_message_dir);
258 else if (!strncasecmp(buf, "keys|", 5)) {
259 snprintf(cmd, sizeof cmd, "rsync -va --rsh='ssh -S %s' %s@%s:%s/ %s/",
260 socket_path, remote_user, remote_host, &buf[5], ctdl_key_dir);
262 else if (!strncasecmp(buf, "images|", 7)) {
263 snprintf(cmd, sizeof cmd, "rsync -va --rsh='ssh -S %s' %s@%s:%s/ %s/",
264 socket_path, remote_user, remote_host, &buf[7], ctdl_image_dir);
267 strcpy(cmd, "false"); /* cheap and sleazy way to throw an error */
270 cmdexit = system(cmd);
277 THEEND: if (exitcode == 0) {
278 printf("\n\n *** Citadel migration was successful! *** \n\n");
281 printf("\n\n *** Citadel migration was unsuccessful. *** \n\n");
283 printf("\nShutting down the socket connection...\n\n");
284 snprintf(cmd, sizeof cmd, "ssh -S %s -N -O exit %s@%s",
285 socket_path, remote_user, remote_host);
286 cmdexit = system(cmd);
289 printf("There was an error shutting down the socket.\n\n");