-/*
- * Across-the-wire migration utility for Citadel
- *
- * Copyright (c) 2009-2021 citadel.org
- *
- * This program is open source software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 3.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
+// Across-the-wire migration utility for Citadel
+//
+// Copyright (c) 2009-2021 citadel.org
+//
+// This program is open source software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License version 3.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
#include <stdlib.h>
#include <unistd.h>
#include "citadel_dirs.h"
-
-// yeah, this wouldn't work in a multithreaded program.
+// support for getz() -- (globals would not be appropriate in a multithreaded program)
static int gmaxlen = 0;
static char *gdeftext = NULL;
+
+// support function for getz()
static int limit_rl(FILE *dummy) {
if (rl_end > gmaxlen) {
return '\b';
}
+// support function for getz()
static int getz_deftext(void) {
if (gdeftext) {
rl_insert_text(gdeftext);
}
-/*
- * Replacement for gets() that doesn't throw a compiler warning.
- * We're only using it for some simple prompts, so we don't need
- * to worry about attackers exploiting it.
- */
+// Replacement for gets() that uses libreadline.
void getz(char *buf, int maxlen, char *default_value, char *prompt) {
rl_startup_hook = getz_deftext;
rl_getc_function = limit_rl;
}
+// Exit from the program while displaying an error code
void ctdlmigrate_exit(int cmdexit) {
printf("\n\n\033[3%dmExit code %d\033[0m\n", (cmdexit ? 1 : 2), cmdexit);
exit(cmdexit);
}
-/*
- * Connect to a Citadel on a remote host using a TCP/IP socket
- */
+// Connect to a Citadel on a remote host using a TCP/IP socket
static int tcp_connectsock(char *host, char *service) {
struct in6_addr serveraddr;
struct addrinfo hints;
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
- /*
- * Handle numeric IPv4 and IPv6 addresses
- */
+ // Handle numeric IPv4 and IPv6 addresses
rc = inet_pton(AF_INET, host, &serveraddr);
- if (rc == 1) { /* dotted quad */
+ if (rc == 1) { // dotted quad
hints.ai_family = AF_INET;
hints.ai_flags |= AI_NUMERICHOST;
} else {
rc = inet_pton(AF_INET6, host, &serveraddr);
- if (rc == 1) { /* IPv6 address */
+ if (rc == 1) { // IPv6 address
hints.ai_family = AF_INET6;
hints.ai_flags |= AI_NUMERICHOST;
}
}
- /* Begin the connection process */
-
+ // Begin the connection process
rc = getaddrinfo(host, service, &hints, &res);
if (rc != 0) {
fprintf(stderr, "ctdlmigrate: %s\n", strerror(errno));
return (-1);
}
- /*
- * Try all available addresses until we connect to one or until we run out.
- */
+ // Try all available addresses until we connect to one or until we run out.
for (ai = res; ai != NULL; ai = ai->ai_next) {
sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
if (sock < 0) {
rc = connect(sock, ai->ai_addr, ai->ai_addrlen);
if (rc >= 0) {
- return (sock); /* Connected! */
+ return (sock); // Connected!
}
else {
fprintf(stderr, "ctdlmigrate: %s\n", strerror(errno));
- close(sock); /* Failed. Close the socket to avoid fd leak! */
+ close(sock); // Failed. Close the socket to avoid fd leak!
}
}
return (-1);
}
+// Connect to a Citadel on a remote host using a unix domaion socket
int uds_connectsock(char *sockpath) {
int s;
struct sockaddr_un addr;
}
-/*
- * input binary data from socket
- */
+// input binary data from socket
void serv_read(int serv_sock, char *buf, int bytes) {
- int len, rlen;
+ int len = 0;
+ int rlen = 0;
- len = 0;
while (len < bytes) {
rlen = read(serv_sock, &buf[len], bytes - len);
if (rlen < 1) {
}
-/*
- * send binary to server
- */
+// send binary to server
void serv_write(int serv_sock, char *buf, int nbytes) {
int bytes_written = 0;
int retval;
}
-/*
- * input string from socket - implemented in terms of serv_read()
- */
+// input string from socket - implemented in terms of serv_read()
void serv_gets(int serv_sock, char *buf) {
int i;
- /* Read one character at a time.
- */
+ // Read one character at a time.
for (i = 0;; i++) {
serv_read(serv_sock, &buf[i], 1);
if (buf[i] == '\n' || i == (SIZ-1))
break;
}
- /* If we got a long line, discard characters until the newline.
- */
+ // If we got a long line, discard characters until the newline.
if (i == (SIZ-1)) {
while (buf[i] != '\n') {
serv_read(serv_sock, &buf[i], 1);
}
}
- /* Strip all trailing nonprintables (crlf)
- */
+ // Strip all trailing nonprintables (crlf)
buf[i] = 0;
}
-/*
- * send line to server - implemented in terms of serv_write()
- */
+// send line to server - implemented in terms of serv_write()
void serv_puts(int serv_sock, char *buf) {
serv_write(serv_sock, buf, strlen(buf));
serv_write(serv_sock, "\n", 1);
}
-/*
- * serv_printf() Send formatted printable data to the server
- */
+// send formatted printable data to the server
void serv_printf(int serv_sock, const char *format, ...) {
va_list arg_ptr;
char buf[1024];
}
+// You know what main() does. If you don't, you shouldn't be trying to understand this program.
int main(int argc, char *argv[]) {
char ctdldir[PATH_MAX]=CTDLDIR;
char yesno[2];
int local_admin_socket = (-1);
int progress = 0;
- /* Parse command line */
+ // Parse command line
while ((a = getopt(argc, argv, "h:")) != EOF) {
switch (a) {
case 'h':
"the source system. The target may be a different CPU architecture\n"
"and/or operating system. The source system should be running\n"
"Citadel version \033[33m%d\033[0m or newer, and the target system should be running\n"
- "either the same version or a newer version. You will also need\n"
- "the \033[33mrsync\033[0m utility, and OpenSSH v4 or newer.\n"
+ "either the same version or a newer version.\n"
"\n"
"You must run this utility on the TARGET SYSTEM. Any existing data\n"
"on this system will be ERASED. Your target system will be on this\n"
getz(yesno, 1, NULL, "Do you wish to continue? ");
puts(yesno);
if (tolower(yesno[0]) != 'y') {
- cmdexit = 1;
+ cmdexit = 101;
}
if (!cmdexit) {
printf( "\033[2J\033[H\n"
- " \033[32m╔═══════════════════════════════════════════════╗\n"
- " ║ ║\n"
- " ║ \033[33mValidate source and target systems\033[32m ║\n"
- " ║ ║\n"
- " ╚═══════════════════════════════════════════════╝\033[0m\n"
+ "\033[32m╔═══════════════════════════════════════════════╗\n"
+ "║ ║\n"
+ "║ \033[33mValidate source and target systems\033[32m ║\n"
+ "║ ║\n"
+ "╚═══════════════════════════════════════════════╝\033[0m\n"
"\n\n");
- printf("First we must validate that the local target system is running and ready to receive data.\n");
+ printf("First we must validate that the local target system is running\n");
+ printf("and ready to receive data.\n");
printf("Checking connectivity to Citadel in %s...\n", ctdldir);
local_admin_socket = uds_connectsock("citadel-admin.socket");
if (local_admin_socket < 0) {
- cmdexit = 1;
+ cmdexit = 102;
}
}
serv_gets(local_admin_socket, buf);
puts(buf);
if (buf[0] != '2') {
- cmdexit = 1;
+ cmdexit = 103;
}
}
serv_gets(local_admin_socket, buf);
puts(buf);
if (buf[0] != '2') {
- cmdexit = 1;
+ cmdexit = 104;
}
}
remote_server_socket = tcp_connectsock(remote_host, remote_port);
if (remote_server_socket < 0) {
- cmdexit = 1;
+ cmdexit = 105;
}
}
serv_gets(remote_server_socket, buf);
puts(buf);
if (buf[0] != '2') {
- cmdexit = 1;
+ cmdexit = 106;
}
}
serv_gets(remote_server_socket, buf);
puts(buf);
if (buf[0] != '3') {
- cmdexit = 1;
+ cmdexit = 107;
}
}
serv_gets(remote_server_socket, buf);
puts(buf);
if (buf[0] != '2') {
- cmdexit = 1;
+ cmdexit = 108;
}
}
if (!cmdexit) {
printf( "\033[2J\033[H\n"
- " \033[32m╔═══════════════════════════════════════════════════════════════════╗\n"
- " ║ ║\n"
- " ║ \033[33mMigrating from: %-50s\033[32m║\n"
- " ║ ║\n"
- " ╠═══════════════════════════════════════════════════════════════════╣\n"
- " ║ ║\n"
- " ║ Lines received: 0 Percent complete: 0 ║\n"
- " ║ ║\n"
- " ╚═══════════════════════════════════════════════════════════════════╝\033[0m\n"
+ "\033[32m╔═══════════════════════════════════════════════════════════════════╗\n"
+ "║ ║\n"
+ "║ \033[33mMigrating from: %-50s\033[32m║\n"
+ "║ ║\n"
+ "╠═══════════════════════════════════════════════════════════════════╣\n"
+ "║ ║\n"
+ "║ Lines received: 0 Percent complete: 0 ║\n"
+ "║ ║\n"
+ "╚═══════════════════════════════════════════════════════════════════╝\033[0m\n"
"\n", remote_host
);
}
serv_gets(remote_server_socket, buf);
if (buf[0] != '1') {
printf("\n\033[31m%s\033[0m\n", buf);
- cmdexit = 3;
+ cmdexit = 109;
}
}
serv_gets(local_admin_socket, buf);
if (buf[0] != '4') {
printf("\n\033[31m%s\033[0m\n", buf);
- cmdexit = 3;
+ cmdexit = 110;
}
}
++linecount;
if (!strncasecmp(buf, "<progress>", 10)) {
progress = atoi(&buf[10]);
- printf("\033[8;75H\033[33m%d\033[0m\033[20;0H\n", progress);
+ printf("\033[8;65H\033[33m%d\033[0m\033[12;0H\n", progress);
}
if (time(NULL) != last_update) {
last_update = time(NULL);
- printf("\033[8;29H\033[33m%d\033[0m\033[20;0H\n", linecount);
+ printf("\033[8;19H\033[33m%d\033[0m\033[12;0H\n", linecount);
}
serv_puts(local_admin_socket, buf);
}
if ( (cmdexit == 0) && (progress < 100) ) {
printf("\033[31mERROR: source stream ended before 100 percent of data was received.\033[0m\n");
- ctdlmigrate_exit(86);
+ cmdexit = 111;
}
- // FIXME restart the local server now
+ if (!cmdexit) {
+ printf("\033[36mMigration is complete. Restarting the target server.\033[0m\n");
+ serv_puts(local_admin_socket, "DOWN 1");
+ serv_gets(local_admin_socket, buf);
+ puts(buf);
+ printf("\033[36mIt is recommended that you shut down the source server now.\033[0m\n");
+ printf("\033[36mRemember to copy over your SSL keys and file libraries if appropriate.\033[0m\n");
+ }
+ close(remote_server_socket);
+ close(local_admin_socket);
ctdlmigrate_exit(cmdexit);
}