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 licensed to you under the terms of the GNU General Public License v3
14 * FIXME handle -h on both sides
24 #include <sys/types.h>
26 #include <sys/utsname.h>
34 #include <libcitadel.h>
39 #include "citadel_dirs.h"
47 * Replacement for gets() that doesn't throw a compiler warning.
48 * We're only using it for some simple prompts, so we don't need
49 * to worry about attackers exploiting it.
51 void getz(char *buf) {
54 ptr = fgets(buf, 32767, stdin);
60 ptr = strchr(buf, '\n');
68 int main(int argc, char *argv[])
72 char relhome[PATH_MAX]="";
73 char ctdldir[PATH_MAX]=CTDLDIR;
75 char sendcommand[PATH_MAX];
79 char socket_path[PATH_MAX];
80 char remote_user[256];
81 char remote_host[256];
82 char remote_sendcommand[PATH_MAX];
83 FILE *sourcefp = NULL;
84 FILE *targetfp = NULL;
86 char spinning[4] = "-\\|/" ;
89 calc_dirs_n_files(relh, home, relhome, ctdldir, 0);
90 CtdlMakeTempFileName(socket_path, sizeof socket_path);
92 cmdexit = system("clear");
93 printf( "-------------------------------------------\n"
94 "Over-the-wire migration utility for Citadel\n"
95 "-------------------------------------------\n"
97 "This utility is designed to migrate your Citadel installation\n"
98 "to a new host system via a network connection, without disturbing\n"
99 "the source system. The target may be a different CPU architecture\n"
100 "and/or operating system. The source system should be running\n"
101 "Citadel %d.%02d or newer, and the target system should be running\n"
102 "either the same version or a newer version. You will also need\n"
103 "the 'rsync' utility, and OpenSSH v4 or newer.\n"
105 "You must run this utility on the TARGET SYSTEM. Any existing data\n"
106 "on this system will be ERASED.\n"
108 "Do you wish to continue? "
110 EXPORT_REV_MIN / 100,
114 if ((fgets(yesno, sizeof yesno, stdin) == NULL) || (tolower(yesno[0]) != 'y')) {
118 printf("\n\nGreat! First we will check some things out here on our target\n"
119 "system to make sure it is ready to receive data.\n\n");
121 printf("Locating 'sendcommand' and checking connectivity to Citadel...\n");
122 snprintf(sendcommand, sizeof sendcommand, "%s/sendcommand", ctdl_utilbin_dir);
123 snprintf(cmd, sizeof cmd, "%s 'NOOP'", sendcommand);
124 cmdexit = system(cmd);
126 printf("\nctdlmigrate was unable to attach to the Citadel server\n"
127 "here on the target system. Is Citadel running?\n\n");
130 printf("\nOK, this side is ready to go. Now we must connect to the source system.\n\n");
132 printf("Enter the host name or IP address of the source system\n"
133 "(example: ctdl.foo.org)\n"
136 printf("\nEnter the name of a user on %s who has full access to Citadel files\n"
137 "(usually root)\n--> ",
141 printf("\nEstablishing an SSH connection to the source system...\n\n");
143 snprintf(cmd, sizeof cmd, "ssh -MNf -S %s %s@%s", socket_path, remote_user, remote_host);
144 cmdexit = system(cmd);
147 printf("This program was unable to establish an SSH session to the source system.\n\n");
152 printf("\nTesting a command over the connection...\n\n");
153 snprintf(cmd, sizeof cmd, "ssh -S %s %s@%s 'echo Remote commands are executing successfully.'",
154 socket_path, remote_user, remote_host);
155 cmdexit = system(cmd);
158 printf("Remote commands are not succeeding.\n\n");
163 printf("\nLocating the remote 'sendcommand' and Citadel installation...\n");
164 snprintf(remote_sendcommand, sizeof remote_sendcommand, "/usr/local/citadel/sendcommand");
165 snprintf(cmd, sizeof cmd, "ssh -S %s %s@%s %s NOOP",
166 socket_path, remote_user, remote_host, remote_sendcommand);
167 cmdexit = system(cmd);
169 snprintf(remote_sendcommand, sizeof remote_sendcommand, "/usr/sbin/sendcommand");
170 snprintf(cmd, sizeof cmd, "ssh -S %s %s@%s %s NOOP",
171 socket_path, remote_user, remote_host, remote_sendcommand);
172 cmdexit = system(cmd);
175 printf("\nUnable to locate Citadel programs on the remote system. Please enter\n"
176 "the name of the directory on %s which contains the 'sendcommand' program.\n"
177 "(example: /opt/foo/citadel)\n"
178 "--> ", remote_host);
180 snprintf(remote_sendcommand, sizeof remote_sendcommand, "%s/sendcommand", buf);
181 snprintf(cmd, sizeof cmd, "ssh -S %s %s@%s %s NOOP",
182 socket_path, remote_user, remote_host, remote_sendcommand);
183 cmdexit = system(cmd);
187 printf("ctdlmigrate was unable to attach to the remote Citadel system.\n\n");
192 printf("ctdlmigrate will now begin a database migration...\n");
194 snprintf(cmd, sizeof cmd, "ssh -S %s %s@%s %s -w3600 MIGR export",
195 socket_path, remote_user, remote_host, remote_sendcommand);
196 sourcefp = popen(cmd, "r");
198 printf("\n%s\n\n", strerror(errno));
203 snprintf(cmd, sizeof cmd, "%s -w3600 MIGR import", sendcommand);
204 targetfp = popen(cmd, "w");
206 printf("\n%s\n\n", strerror(errno));
211 while (fgets(buf, sizeof buf, sourcefp) != NULL) {
212 if (fwrite(buf, strlen(buf), 1, targetfp) < 1) {
214 printf("%s\n", strerror(errno));
218 if ((linecount % 100) == 0) {
219 printf("%c\r", spinning[((linecount / 100) % 4)]);
224 FAIL: if (sourcefp) pclose(sourcefp);
225 if (targetfp) pclose(targetfp);
226 if (exitcode != 0) goto THEEND;
228 /* We need to copy a bunch of other stuff, and will do so using rsync */
230 snprintf(cmd, sizeof cmd, "ssh -S %s %s@%s %s MIGR listdirs",
231 socket_path, remote_user, remote_host, remote_sendcommand);
232 sourcefp = popen(cmd, "r");
234 printf("\n%s\n\n", strerror(errno));
238 while ((fgets(buf, sizeof buf, sourcefp)) && (strcmp(buf, "000"))) {
239 buf[strlen(buf)-1] = 0;
241 if (!strncasecmp(buf, "bio|", 4)) {
242 snprintf(cmd, sizeof cmd, "rsync -va --rsh='ssh -S %s' %s@%s:%s/ %s/",
243 socket_path, remote_user, remote_host, &buf[4], ctdl_bio_dir);
245 else if (!strncasecmp(buf, "files|", 6)) {
246 snprintf(cmd, sizeof cmd, "rsync -va --rsh='ssh -S %s' %s@%s:%s/ %s/",
247 socket_path, remote_user, remote_host, &buf[6], ctdl_file_dir);
249 else if (!strncasecmp(buf, "userpics|", 9)) {
250 snprintf(cmd, sizeof cmd, "rsync -va --rsh='ssh -S %s' %s@%s:%s/ %s/",
251 socket_path, remote_user, remote_host, &buf[9], ctdl_usrpic_dir);
253 else if (!strncasecmp(buf, "messages|", 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_message_dir);
257 else if (!strncasecmp(buf, "netconfigs|", 11)) {
258 snprintf(cmd, sizeof cmd, "rsync -va --rsh='ssh -S %s' %s@%s:%s/ %s/",
259 socket_path, remote_user, remote_host, &buf[11], ctdl_netcfg_dir);
261 else if (!strncasecmp(buf, "keys|", 5)) {
262 snprintf(cmd, sizeof cmd, "rsync -va --rsh='ssh -S %s' %s@%s:%s/ %s/",
263 socket_path, remote_user, remote_host, &buf[5], ctdl_key_dir);
265 else if (!strncasecmp(buf, "images|", 7)) {
266 snprintf(cmd, sizeof cmd, "rsync -va --rsh='ssh -S %s' %s@%s:%s/ %s/",
267 socket_path, remote_user, remote_host, &buf[7], ctdl_image_dir);
269 else if (!strncasecmp(buf, "info|", 5)) {
270 snprintf(cmd, sizeof cmd, "rsync -va --rsh='ssh -S %s' %s@%s:%s/ %s/",
271 socket_path, remote_user, remote_host, &buf[5], ctdl_info_dir);
274 strcpy(cmd, "false"); /* cheap and sleazy way to throw an error */
277 cmdexit = system(cmd);
284 THEEND: if (exitcode == 0) {
285 printf("\n\n *** Citadel migration was successful! *** \n\n");
288 printf("\n\n *** Citadel migration was unsuccessful. *** \n\n");
290 printf("\nShutting down the socket connection...\n\n");
291 snprintf(cmd, sizeof cmd, "ssh -S %s -N -O exit %s@%s",
292 socket_path, remote_user, remote_host);
293 cmdexit = system(cmd);
296 printf("There was an error shutting down the socket.\n\n");