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 citadel.org
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 3 of the License, or
13 * (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 * (Note: a useful future enhancement might be to support "-h" on both sides)
34 #include <sys/types.h>
36 #include <sys/utsname.h>
44 #include <libcitadel.h>
49 #include "citadel_dirs.h"
57 * Replacement for gets() that doesn't throw a compiler warning.
58 * We're only using it for some simple prompts, so we don't need
59 * to worry about attackers exploiting it.
61 void getz(char *buf) {
64 ptr = fgets(buf, 32767, stdin);
70 ptr = strchr(buf, '\n');
78 int main(int argc, char *argv[])
82 char relhome[PATH_MAX]="";
83 char ctdldir[PATH_MAX]=CTDLDIR;
85 char sendcommand[PATH_MAX];
89 char socket_path[PATH_MAX];
90 char remote_user[256];
91 char remote_host[256];
92 char remote_sendcommand[PATH_MAX];
93 FILE *sourcefp = NULL;
94 FILE *targetfp = NULL;
96 char spinning[4] = "-\\|/" ;
99 calc_dirs_n_files(relh, home, relhome, ctdldir, 0);
100 CtdlMakeTempFileName(socket_path, sizeof socket_path);
102 cmdexit = system("clear");
103 printf( "-------------------------------------------\n"
104 "Over-the-wire migration utility for Citadel\n"
105 "-------------------------------------------\n"
107 "This utility is designed to migrate your Citadel installation\n"
108 "to a new host system via a network connection, without disturbing\n"
109 "the source system. The target may be a different CPU architecture\n"
110 "and/or operating system. The source system should be running\n"
111 "Citadel %d.%02d or newer, and the target system should be running\n"
112 "either the same version or a newer version. You will also need\n"
113 "the 'rsync' utility, and OpenSSH v4 or newer.\n"
115 "You must run this utility on the TARGET SYSTEM. Any existing data\n"
116 "on this system will be ERASED.\n"
118 "Do you wish to continue? "
120 EXPORT_REV_MIN / 100,
124 if ((fgets(yesno, sizeof yesno, stdin) == NULL) || (tolower(yesno[0]) != 'y')) {
128 printf("\n\nGreat! First we will check some things out here on our target\n"
129 "system to make sure it is ready to receive data.\n\n");
131 printf("Locating 'sendcommand' and checking connectivity to Citadel...\n");
132 snprintf(sendcommand, sizeof sendcommand, "%s/sendcommand", ctdl_utilbin_dir);
133 snprintf(cmd, sizeof cmd, "%s 'NOOP'", sendcommand);
134 cmdexit = system(cmd);
136 printf("\nctdlmigrate was unable to attach to the Citadel server\n"
137 "here on the target system. Is Citadel running?\n\n");
140 printf("\nOK, this side is ready to go. Now we must connect to the source system.\n\n");
142 printf("Enter the host name or IP address of the source system\n"
143 "(example: ctdl.foo.org)\n"
146 printf("\nEnter the name of a user on %s who has full access to Citadel files\n"
147 "(usually root)\n--> ",
151 printf("\nEstablishing an SSH connection to the source system...\n\n");
153 snprintf(cmd, sizeof cmd, "ssh -MNf -S %s %s@%s", socket_path, remote_user, remote_host);
154 cmdexit = system(cmd);
157 printf("This program was unable to establish an SSH session to the source system.\n\n");
162 printf("\nTesting a command over the connection...\n\n");
163 snprintf(cmd, sizeof cmd, "ssh -S %s %s@%s 'echo Remote commands are executing successfully.'",
164 socket_path, remote_user, remote_host);
165 cmdexit = system(cmd);
168 printf("Remote commands are not succeeding.\n\n");
173 printf("\nLocating the remote 'sendcommand' and Citadel installation...\n");
174 snprintf(remote_sendcommand, sizeof remote_sendcommand, "/usr/local/citadel/sendcommand");
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);
179 snprintf(remote_sendcommand, sizeof remote_sendcommand, "/usr/sbin/sendcommand");
180 snprintf(cmd, sizeof cmd, "ssh -S %s %s@%s %s NOOP",
181 socket_path, remote_user, remote_host, remote_sendcommand);
182 cmdexit = system(cmd);
185 printf("\nUnable to locate Citadel programs on the remote system. Please enter\n"
186 "the name of the directory on %s which contains the 'sendcommand' program.\n"
187 "(example: /opt/foo/citadel)\n"
188 "--> ", remote_host);
190 snprintf(remote_sendcommand, sizeof remote_sendcommand, "%s/sendcommand", buf);
191 snprintf(cmd, sizeof cmd, "ssh -S %s %s@%s %s NOOP",
192 socket_path, remote_user, remote_host, remote_sendcommand);
193 cmdexit = system(cmd);
197 printf("ctdlmigrate was unable to attach to the remote Citadel system.\n\n");
202 printf("ctdlmigrate will now begin a database migration...\n");
204 snprintf(cmd, sizeof cmd, "ssh -S %s %s@%s %s -w3600 MIGR export",
205 socket_path, remote_user, remote_host, remote_sendcommand);
206 sourcefp = popen(cmd, "r");
208 printf("\n%s\n\n", strerror(errno));
213 snprintf(cmd, sizeof cmd, "%s -w3600 MIGR import", sendcommand);
214 targetfp = popen(cmd, "w");
216 printf("\n%s\n\n", strerror(errno));
221 while (fgets(buf, sizeof buf, sourcefp) != NULL) {
222 if (fwrite(buf, strlen(buf), 1, targetfp) < 1) {
224 printf("%s\n", strerror(errno));
228 if ((linecount % 100) == 0) {
229 printf("%c\r", spinning[((linecount / 100) % 4)]);
234 FAIL: if (sourcefp) pclose(sourcefp);
235 if (targetfp) pclose(targetfp);
236 if (exitcode != 0) goto THEEND;
238 /* We need to copy a bunch of other stuff, and will do so using rsync */
240 snprintf(cmd, sizeof cmd, "ssh -S %s %s@%s %s MIGR listdirs",
241 socket_path, remote_user, remote_host, remote_sendcommand);
242 sourcefp = popen(cmd, "r");
244 printf("\n%s\n\n", strerror(errno));
248 while ((fgets(buf, sizeof buf, sourcefp)) && (strcmp(buf, "000"))) {
249 buf[strlen(buf)-1] = 0;
251 if (!strncasecmp(buf, "bio|", 4)) {
252 snprintf(cmd, sizeof cmd, "rsync -va --rsh='ssh -S %s' %s@%s:%s/ %s/",
253 socket_path, remote_user, remote_host, &buf[4], ctdl_bio_dir);
255 else if (!strncasecmp(buf, "files|", 6)) {
256 snprintf(cmd, sizeof cmd, "rsync -va --rsh='ssh -S %s' %s@%s:%s/ %s/",
257 socket_path, remote_user, remote_host, &buf[6], ctdl_file_dir);
259 else if (!strncasecmp(buf, "userpics|", 9)) {
260 snprintf(cmd, sizeof cmd, "rsync -va --rsh='ssh -S %s' %s@%s:%s/ %s/",
261 socket_path, remote_user, remote_host, &buf[9], ctdl_usrpic_dir);
263 else if (!strncasecmp(buf, "messages|", 9)) {
264 snprintf(cmd, sizeof cmd, "rsync -va --rsh='ssh -S %s' %s@%s:%s/ %s/",
265 socket_path, remote_user, remote_host, &buf[9], ctdl_message_dir);
267 else if (!strncasecmp(buf, "netconfigs|", 11)) {
268 snprintf(cmd, sizeof cmd, "rsync -va --rsh='ssh -S %s' %s@%s:%s/ %s/",
269 socket_path, remote_user, remote_host, &buf[11], ctdl_netcfg_dir);
271 else if (!strncasecmp(buf, "keys|", 5)) {
272 snprintf(cmd, sizeof cmd, "rsync -va --rsh='ssh -S %s' %s@%s:%s/ %s/",
273 socket_path, remote_user, remote_host, &buf[5], ctdl_key_dir);
275 else if (!strncasecmp(buf, "images|", 7)) {
276 snprintf(cmd, sizeof cmd, "rsync -va --rsh='ssh -S %s' %s@%s:%s/ %s/",
277 socket_path, remote_user, remote_host, &buf[7], ctdl_image_dir);
279 else if (!strncasecmp(buf, "info|", 5)) {
280 snprintf(cmd, sizeof cmd, "rsync -va --rsh='ssh -S %s' %s@%s:%s/ %s/",
281 socket_path, remote_user, remote_host, &buf[5], ctdl_info_dir);
284 strcpy(cmd, "false"); /* cheap and sleazy way to throw an error */
287 cmdexit = system(cmd);
294 THEEND: if (exitcode == 0) {
295 printf("\n\n *** Citadel migration was successful! *** \n\n");
298 printf("\n\n *** Citadel migration was unsuccessful. *** \n\n");
300 printf("\nShutting down the socket connection...\n\n");
301 snprintf(cmd, sizeof cmd, "ssh -S %s -N -O exit %s@%s",
302 socket_path, remote_user, remote_host);
303 cmdexit = system(cmd);
306 printf("There was an error shutting down the socket.\n\n");