4 * Across-the-wire migration utility for Citadel
6 * Copyright (c) 2009 citadel.org
8 * This program is licensed to you under the terms of the GNU General Public License v3
18 #include <sys/types.h>
20 #include <sys/utsname.h>
28 #include <libcitadel.h>
33 #include "citadel_dirs.h"
41 * Replacement for gets() that doesn't throw a compiler warning.
42 * We're only using it for some simple prompts, so we don't need
43 * to worry about attackers exploiting it.
45 void getz(char *buf) {
48 ptr = fgets(buf, 32767, stdin);
54 ptr = strchr(buf, '\n');
62 int main(int argc, char *argv[])
66 char relhome[PATH_MAX]="";
67 char ctdldir[PATH_MAX]=CTDLDIR;
69 char sendcommand[PATH_MAX];
73 char socket_path[PATH_MAX];
74 char remote_user[256];
75 char remote_host[256];
76 char remote_sendcommand[PATH_MAX];
80 char spinning[4] = "-\\|/" ;
84 calc_dirs_n_files(relh, home, relhome, ctdldir, 0);
85 CtdlMakeTempFileName(socket_path, sizeof socket_path);
88 printf( "-------------------------------------------\n"
89 "Over-the-wire migration utility for Citadel\n"
90 "-------------------------------------------\n"
92 "This utility is designed to migrate your Citadel installation\n"
93 "to a new host system via a network connection, without disturbing\n"
94 "the source system. The target may be a different CPU architecture\n"
95 "and/or operating system. The source system should be running\n"
96 "Citadel %d.%02d or newer, and the target system should be running\n"
97 "either the same version or a newer version. You will also need\n"
98 "the 'rsync' utility, and OpenSSH v4 or newer.\n"
100 "You must run this utility on the TARGET SYSTEM. Any existing data\n"
101 "on this system will be ERASED.\n"
103 "Do you wish to continue? "
105 EXPORT_REV_MIN / 100,
109 if ((fgets(yesno, sizeof yesno, stdin) == NULL) || (tolower(yesno[0]) != 'y')) {
113 printf("\n\nGreat! First we will check some things out here on our target\n"
114 "system to make sure it is ready to receive data.\n\n");
116 printf("Locating 'sendcommand' and checking connectivity to Citadel...\n");
117 snprintf(sendcommand, sizeof sendcommand, "%s/sendcommand", ctdl_utilbin_dir);
118 snprintf(cmd, sizeof cmd, "%s 'NOOP'", sendcommand);
119 cmdexit = system(cmd);
121 printf("\nctdlmigrate was unable to attach to the Citadel server\n"
122 "here on the target system. Is Citadel running?\n\n");
125 printf("\nOK, this side is ready to go. Now we must connect to the source system.\n\n");
127 printf("Enter the host name or IP address of the source system\n"
128 "(example: ctdl.foo.org)\n"
131 printf("\nEnter the name of a user on %s who has full access to Citadel files\n"
132 "(usually root)\n--> ",
139 printf("\n%s\n", strerror(errno));
143 else if (sshpid == 0)
145 printf("\nEstablishing an SSH connection to the source system...\n\n");
147 snprintf(cmd, sizeof cmd, "%s@%s", remote_user, remote_host);
148 execlp("ssh", "ssh", "-MNf", "-S", socket_path, cmd, NULL);
150 printf("\n%s\n", strerror(cmdexit));
151 exit(cmdexit); /* child process exits */
154 /* If we get here we are the parent process */
155 if (waitpid(sshpid, &cmdexit, 0) <= 0) {
157 printf("\n%s\n", strerror(errno));
161 if (WIFSIGNALED(cmdexit)) {
163 printf("\n%s\n", strerror(errno));
167 if ((WIFEXITED(cmdexit)) && (WEXITSTATUS(cmdexit) != 0)) {
168 exitcode = WEXITSTATUS(cmdexit);
169 printf("\n%s\n", strerror(errno));
173 printf("\nTesting a command over the connection...\n\n");
174 snprintf(cmd, sizeof cmd, "ssh -S %s %s@%s 'echo Remote commands are executing successfully.'",
175 socket_path, remote_user, remote_host);
176 cmdexit = system(cmd);
179 printf("Remote commands are not succeeding.\n\n");
184 printf("\nLocating the remote 'sendcommand' and Citadel installation...\n");
185 snprintf(remote_sendcommand, sizeof remote_sendcommand, "/usr/local/citadel/sendcommand");
186 snprintf(cmd, sizeof cmd, "ssh -S %s %s@%s %s NOOP",
187 socket_path, remote_user, remote_host, remote_sendcommand);
188 cmdexit = system(cmd);
190 snprintf(remote_sendcommand, sizeof remote_sendcommand, "/usr/sbin/sendcommand");
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);
196 printf("\nUnable to locate Citadel programs on the remote system. Please enter\n"
197 "the name of the directory on %s which contains the 'sendcommand' program.\n"
198 "(example: /opt/foo/citadel)\n"
199 "--> ", remote_host);
201 snprintf(remote_sendcommand, sizeof remote_sendcommand, "%s/sendcommand", buf);
202 snprintf(cmd, sizeof cmd, "ssh -S %s %s@%s %s NOOP",
203 socket_path, remote_user, remote_host, remote_sendcommand);
204 cmdexit = system(cmd);
208 printf("ctdlmigrate was unable to attach to the remote Citadel system.\n\n");
213 printf("ctdlmigrate will now begin a database migration...\n");
215 snprintf(cmd, sizeof cmd, "ssh -S %s %s@%s %s -w3600 MIGR export",
216 socket_path, remote_user, remote_host, remote_sendcommand);
217 source_artv = popen(cmd, "r");
219 printf("\n%s\n\n", strerror(errno));
224 snprintf(cmd, sizeof cmd, "%s -w3600 MIGR import", sendcommand);
225 target_artv = popen(cmd, "w");
227 printf("\n%s\n\n", strerror(errno));
232 while (fgets(buf, sizeof buf, source_artv) != NULL) {
233 if (fwrite(buf, strlen(buf), 1, target_artv) < 1) {
235 printf("%s\n", strerror(errno));
239 if ((linecount % 100) == 0) {
240 printf("%c\r", spinning[((linecount / 100) % 4)]);
245 FAIL: pclose(source_artv);
248 // FIXME handle -h on both sides
249 printf("If this program was finished we would do more. FIXME\n");
251 THEEND: kill(sshpid, SIGKILL);