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-2012 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, 32767, 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[256];
85 char remote_host[256];
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 %d.%02d 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? "
114 EXPORT_REV_MIN / 100,
118 if ((fgets(yesno, sizeof yesno, stdin) == NULL) || (tolower(yesno[0]) != 'y')) {
122 printf("\n\nGreat! First we will check some things out here on our target\n"
123 "system to make sure it is ready to receive data.\n\n");
125 printf("Locating 'sendcommand' and checking connectivity to Citadel...\n");
126 snprintf(sendcommand, sizeof sendcommand, "%s/sendcommand", ctdl_utilbin_dir);
127 snprintf(cmd, sizeof cmd, "%s 'NOOP'", sendcommand);
128 cmdexit = system(cmd);
130 printf("\nctdlmigrate was unable to attach to the Citadel server\n"
131 "here on the target system. Is Citadel running?\n\n");
134 printf("\nOK, this side is ready to go. Now we must connect to the source system.\n\n");
136 printf("Enter the host name or IP address of the source system\n"
137 "(example: ctdl.foo.org)\n"
140 printf("\nEnter the name of a user on %s who has full access to Citadel files\n"
141 "(usually root)\n--> ",
145 printf("\nEstablishing an SSH connection to the source system...\n\n");
147 snprintf(cmd, sizeof cmd, "ssh -MNf -S %s %s@%s", socket_path, remote_user, remote_host);
148 cmdexit = system(cmd);
151 printf("This program was unable to establish an SSH session to the source system.\n\n");
156 printf("\nTesting a command over the connection...\n\n");
157 snprintf(cmd, sizeof cmd, "ssh -S %s %s@%s 'echo Remote commands are executing successfully.'",
158 socket_path, remote_user, remote_host);
159 cmdexit = system(cmd);
162 printf("Remote commands are not succeeding.\n\n");
167 printf("\nLocating the remote 'sendcommand' and Citadel installation...\n");
168 snprintf(remote_sendcommand, sizeof remote_sendcommand, "/usr/local/citadel/sendcommand");
169 snprintf(cmd, sizeof cmd, "ssh -S %s %s@%s %s NOOP",
170 socket_path, remote_user, remote_host, remote_sendcommand);
171 cmdexit = system(cmd);
173 snprintf(remote_sendcommand, sizeof remote_sendcommand, "/usr/sbin/sendcommand");
174 snprintf(cmd, sizeof cmd, "ssh -S %s %s@%s %s NOOP",
175 socket_path, remote_user, remote_host, remote_sendcommand);
176 cmdexit = system(cmd);
179 printf("\nUnable to locate Citadel programs on the remote system. Please enter\n"
180 "the name of the directory on %s which contains the 'sendcommand' program.\n"
181 "(example: /opt/foo/citadel)\n"
182 "--> ", remote_host);
184 snprintf(remote_sendcommand, sizeof remote_sendcommand, "%s/sendcommand", buf);
185 snprintf(cmd, sizeof cmd, "ssh -S %s %s@%s %s NOOP",
186 socket_path, remote_user, remote_host, remote_sendcommand);
187 cmdexit = system(cmd);
191 printf("ctdlmigrate was unable to attach to the remote Citadel system.\n\n");
196 printf("ctdlmigrate will now begin a database migration...\n");
198 snprintf(cmd, sizeof cmd, "ssh -S %s %s@%s %s -w3600 MIGR export",
199 socket_path, remote_user, remote_host, remote_sendcommand);
200 sourcefp = popen(cmd, "r");
202 printf("\n%s\n\n", strerror(errno));
207 snprintf(cmd, sizeof cmd, "%s -w3600 MIGR import", sendcommand);
208 targetfp = popen(cmd, "w");
210 printf("\n%s\n\n", strerror(errno));
215 while (fgets(buf, sizeof buf, sourcefp) != NULL) {
216 if (fwrite(buf, strlen(buf), 1, targetfp) < 1) {
218 printf("%s\n", strerror(errno));
222 if ((linecount % 100) == 0) {
223 printf("%c\r", spinning[((linecount / 100) % 4)]);
228 FAIL: if (sourcefp) pclose(sourcefp);
229 if (targetfp) pclose(targetfp);
230 if (exitcode != 0) goto THEEND;
232 /* We need to copy a bunch of other stuff, and will do so using rsync */
234 snprintf(cmd, sizeof cmd, "ssh -S %s %s@%s %s MIGR listdirs",
235 socket_path, remote_user, remote_host, remote_sendcommand);
236 sourcefp = popen(cmd, "r");
238 printf("\n%s\n\n", strerror(errno));
242 while ((fgets(buf, sizeof buf, sourcefp)) && (strcmp(buf, "000"))) {
243 buf[strlen(buf)-1] = 0;
245 if (!strncasecmp(buf, "bio|", 4)) {
246 snprintf(cmd, sizeof cmd, "rsync -va --rsh='ssh -S %s' %s@%s:%s/ %s/",
247 socket_path, remote_user, remote_host, &buf[4], ctdl_bio_dir);
249 else if (!strncasecmp(buf, "files|", 6)) {
250 snprintf(cmd, sizeof cmd, "rsync -va --rsh='ssh -S %s' %s@%s:%s/ %s/",
251 socket_path, remote_user, remote_host, &buf[6], ctdl_file_dir);
253 else if (!strncasecmp(buf, "userpics|", 9)) {
254 snprintf(cmd, sizeof cmd, "rsync -va --rsh='ssh -S %s' %s@%s:%s/ %s/",
255 socket_path, remote_user, remote_host, &buf[9], ctdl_usrpic_dir);
257 else if (!strncasecmp(buf, "messages|", 9)) {
258 snprintf(cmd, sizeof cmd, "rsync -va --rsh='ssh -S %s' %s@%s:%s/ %s/",
259 socket_path, remote_user, remote_host, &buf[9], ctdl_message_dir);
261 else if (!strncasecmp(buf, "netconfigs|", 11)) {
262 snprintf(cmd, sizeof cmd, "rsync -va --rsh='ssh -S %s' %s@%s:%s/ %s/",
263 socket_path, remote_user, remote_host, &buf[11], ctdl_netcfg_dir);
265 else if (!strncasecmp(buf, "keys|", 5)) {
266 snprintf(cmd, sizeof cmd, "rsync -va --rsh='ssh -S %s' %s@%s:%s/ %s/",
267 socket_path, remote_user, remote_host, &buf[5], ctdl_key_dir);
269 else if (!strncasecmp(buf, "images|", 7)) {
270 snprintf(cmd, sizeof cmd, "rsync -va --rsh='ssh -S %s' %s@%s:%s/ %s/",
271 socket_path, remote_user, remote_host, &buf[7], ctdl_image_dir);
273 else if (!strncasecmp(buf, "info|", 5)) {
274 snprintf(cmd, sizeof cmd, "rsync -va --rsh='ssh -S %s' %s@%s:%s/ %s/",
275 socket_path, remote_user, remote_host, &buf[5], ctdl_info_dir);
278 strcpy(cmd, "false"); /* cheap and sleazy way to throw an error */
281 cmdexit = system(cmd);
288 THEEND: if (exitcode == 0) {
289 printf("\n\n *** Citadel migration was successful! *** \n\n");
292 printf("\n\n *** Citadel migration was unsuccessful. *** \n\n");
294 printf("\nShutting down the socket connection...\n\n");
295 snprintf(cmd, sizeof cmd, "ssh -S %s -N -O exit %s@%s",
296 socket_path, remote_user, remote_host);
297 cmdexit = system(cmd);
300 printf("There was an error shutting down the socket.\n\n");