4 * Across-the-wire migration utility for Citadel
6 * Yes, we used goto, and gets(), and committed all sorts of other heinous sins here.
7 * The scope of this program isn't wide enough to make a difference. If you don't like
8 * it you can rewrite it.
10 * Copyright (c) 2009 citadel.org
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 3 of the License, or
15 * (at your option) any later version.
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
26 * (Note: a useful future enhancement might be to support "-h" on both sides)
36 #include <sys/types.h>
38 #include <sys/utsname.h>
46 #include <libcitadel.h>
51 #include "citadel_dirs.h"
59 * Replacement for gets() that doesn't throw a compiler warning.
60 * We're only using it for some simple prompts, so we don't need
61 * to worry about attackers exploiting it.
63 void getz(char *buf) {
66 ptr = fgets(buf, 32767, stdin);
72 ptr = strchr(buf, '\n');
80 int main(int argc, char *argv[])
84 char relhome[PATH_MAX]="";
85 char ctdldir[PATH_MAX]=CTDLDIR;
87 char sendcommand[PATH_MAX];
91 char socket_path[PATH_MAX];
92 char remote_user[256];
93 char remote_host[256];
94 char remote_sendcommand[PATH_MAX];
95 FILE *sourcefp = NULL;
96 FILE *targetfp = NULL;
98 char spinning[4] = "-\\|/" ;
101 calc_dirs_n_files(relh, home, relhome, ctdldir, 0);
102 CtdlMakeTempFileName(socket_path, sizeof socket_path);
104 cmdexit = system("clear");
105 printf( "-------------------------------------------\n"
106 "Over-the-wire migration utility for Citadel\n"
107 "-------------------------------------------\n"
109 "This utility is designed to migrate your Citadel installation\n"
110 "to a new host system via a network connection, without disturbing\n"
111 "the source system. The target may be a different CPU architecture\n"
112 "and/or operating system. The source system should be running\n"
113 "Citadel %d.%02d or newer, and the target system should be running\n"
114 "either the same version or a newer version. You will also need\n"
115 "the 'rsync' utility, and OpenSSH v4 or newer.\n"
117 "You must run this utility on the TARGET SYSTEM. Any existing data\n"
118 "on this system will be ERASED.\n"
120 "Do you wish to continue? "
122 EXPORT_REV_MIN / 100,
126 if ((fgets(yesno, sizeof yesno, stdin) == NULL) || (tolower(yesno[0]) != 'y')) {
130 printf("\n\nGreat! First we will check some things out here on our target\n"
131 "system to make sure it is ready to receive data.\n\n");
133 printf("Locating 'sendcommand' and checking connectivity to Citadel...\n");
134 snprintf(sendcommand, sizeof sendcommand, "%s/sendcommand", ctdl_utilbin_dir);
135 snprintf(cmd, sizeof cmd, "%s 'NOOP'", sendcommand);
136 cmdexit = system(cmd);
138 printf("\nctdlmigrate was unable to attach to the Citadel server\n"
139 "here on the target system. Is Citadel running?\n\n");
142 printf("\nOK, this side is ready to go. Now we must connect to the source system.\n\n");
144 printf("Enter the host name or IP address of the source system\n"
145 "(example: ctdl.foo.org)\n"
148 printf("\nEnter the name of a user on %s who has full access to Citadel files\n"
149 "(usually root)\n--> ",
153 printf("\nEstablishing an SSH connection to the source system...\n\n");
155 snprintf(cmd, sizeof cmd, "ssh -MNf -S %s %s@%s", socket_path, remote_user, remote_host);
156 cmdexit = system(cmd);
159 printf("This program was unable to establish an SSH session to the source system.\n\n");
164 printf("\nTesting a command over the connection...\n\n");
165 snprintf(cmd, sizeof cmd, "ssh -S %s %s@%s 'echo Remote commands are executing successfully.'",
166 socket_path, remote_user, remote_host);
167 cmdexit = system(cmd);
170 printf("Remote commands are not succeeding.\n\n");
175 printf("\nLocating the remote 'sendcommand' and Citadel installation...\n");
176 snprintf(remote_sendcommand, sizeof remote_sendcommand, "/usr/local/citadel/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);
181 snprintf(remote_sendcommand, sizeof remote_sendcommand, "/usr/sbin/sendcommand");
182 snprintf(cmd, sizeof cmd, "ssh -S %s %s@%s %s NOOP",
183 socket_path, remote_user, remote_host, remote_sendcommand);
184 cmdexit = system(cmd);
187 printf("\nUnable to locate Citadel programs on the remote system. Please enter\n"
188 "the name of the directory on %s which contains the 'sendcommand' program.\n"
189 "(example: /opt/foo/citadel)\n"
190 "--> ", remote_host);
192 snprintf(remote_sendcommand, sizeof remote_sendcommand, "%s/sendcommand", buf);
193 snprintf(cmd, sizeof cmd, "ssh -S %s %s@%s %s NOOP",
194 socket_path, remote_user, remote_host, remote_sendcommand);
195 cmdexit = system(cmd);
199 printf("ctdlmigrate was unable to attach to the remote Citadel system.\n\n");
204 printf("ctdlmigrate will now begin a database migration...\n");
206 snprintf(cmd, sizeof cmd, "ssh -S %s %s@%s %s -w3600 MIGR export",
207 socket_path, remote_user, remote_host, remote_sendcommand);
208 sourcefp = popen(cmd, "r");
210 printf("\n%s\n\n", strerror(errno));
215 snprintf(cmd, sizeof cmd, "%s -w3600 MIGR import", sendcommand);
216 targetfp = popen(cmd, "w");
218 printf("\n%s\n\n", strerror(errno));
223 while (fgets(buf, sizeof buf, sourcefp) != NULL) {
224 if (fwrite(buf, strlen(buf), 1, targetfp) < 1) {
226 printf("%s\n", strerror(errno));
230 if ((linecount % 100) == 0) {
231 printf("%c\r", spinning[((linecount / 100) % 4)]);
236 FAIL: if (sourcefp) pclose(sourcefp);
237 if (targetfp) pclose(targetfp);
238 if (exitcode != 0) goto THEEND;
240 /* We need to copy a bunch of other stuff, and will do so using rsync */
242 snprintf(cmd, sizeof cmd, "ssh -S %s %s@%s %s MIGR listdirs",
243 socket_path, remote_user, remote_host, remote_sendcommand);
244 sourcefp = popen(cmd, "r");
246 printf("\n%s\n\n", strerror(errno));
250 while ((fgets(buf, sizeof buf, sourcefp)) && (strcmp(buf, "000"))) {
251 buf[strlen(buf)-1] = 0;
253 if (!strncasecmp(buf, "bio|", 4)) {
254 snprintf(cmd, sizeof cmd, "rsync -va --rsh='ssh -S %s' %s@%s:%s/ %s/",
255 socket_path, remote_user, remote_host, &buf[4], ctdl_bio_dir);
257 else if (!strncasecmp(buf, "files|", 6)) {
258 snprintf(cmd, sizeof cmd, "rsync -va --rsh='ssh -S %s' %s@%s:%s/ %s/",
259 socket_path, remote_user, remote_host, &buf[6], ctdl_file_dir);
261 else if (!strncasecmp(buf, "userpics|", 9)) {
262 snprintf(cmd, sizeof cmd, "rsync -va --rsh='ssh -S %s' %s@%s:%s/ %s/",
263 socket_path, remote_user, remote_host, &buf[9], ctdl_usrpic_dir);
265 else if (!strncasecmp(buf, "messages|", 9)) {
266 snprintf(cmd, sizeof cmd, "rsync -va --rsh='ssh -S %s' %s@%s:%s/ %s/",
267 socket_path, remote_user, remote_host, &buf[9], ctdl_message_dir);
269 else if (!strncasecmp(buf, "netconfigs|", 11)) {
270 snprintf(cmd, sizeof cmd, "rsync -va --rsh='ssh -S %s' %s@%s:%s/ %s/",
271 socket_path, remote_user, remote_host, &buf[11], ctdl_netcfg_dir);
273 else if (!strncasecmp(buf, "keys|", 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_key_dir);
277 else if (!strncasecmp(buf, "images|", 7)) {
278 snprintf(cmd, sizeof cmd, "rsync -va --rsh='ssh -S %s' %s@%s:%s/ %s/",
279 socket_path, remote_user, remote_host, &buf[7], ctdl_image_dir);
281 else if (!strncasecmp(buf, "info|", 5)) {
282 snprintf(cmd, sizeof cmd, "rsync -va --rsh='ssh -S %s' %s@%s:%s/ %s/",
283 socket_path, remote_user, remote_host, &buf[5], ctdl_info_dir);
286 strcpy(cmd, "false"); /* cheap and sleazy way to throw an error */
289 cmdexit = system(cmd);
296 THEEND: if (exitcode == 0) {
297 printf("\n\n *** Citadel migration was successful! *** \n\n");
300 printf("\n\n *** Citadel migration was unsuccessful. *** \n\n");
302 printf("\nShutting down the socket connection...\n\n");
303 snprintf(cmd, sizeof cmd, "ssh -S %s -N -O exit %s@%s",
304 socket_path, remote_user, remote_host);
305 cmdexit = system(cmd);
308 printf("There was an error shutting down the socket.\n\n");