aboutsummaryrefslogtreecommitdiffstats
path: root/run-dump.c
diff options
context:
space:
mode:
Diffstat (limited to 'run-dump.c')
-rw-r--r--run-dump.c490
1 files changed, 490 insertions, 0 deletions
diff --git a/run-dump.c b/run-dump.c
new file mode 100644
index 0000000..a5a34ff
--- /dev/null
+++ b/run-dump.c
@@ -0,0 +1,490 @@
+#include "config.h"
+#include "conf.h"
+#include "err.h"
+#include "strutil.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <netinet/in.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#ifdef NEED_LIBUTIL
+#include <libutil.h>
+#endif
+
+#ifdef NEED_STRINGS
+#include <strings.h>
+#endif
+
+RCSID("$Id: run-dump.c,v 1.1.1.1 1999/02/02 23:29:39 shmit Exp $");
+
+short exit_status = -1;
+
+void
+sig_pipe(int signo)
+{
+ err("SIGPIPE caught.\n");
+ exit_status = -2;
+}
+
+void
+sig_child(int signo)
+{
+ int status;
+
+ if (wait(&status) == -1) {
+ err("Couldn't clean up child: %s.\n", strerror(errno));
+ return;
+ }
+
+ exit_status = WEXITSTATUS(status);
+}
+
+char **
+genargs(const char *expression, int level, const char *vol)
+{
+ /* TODO: generate dynamically. */
+ static char *args[32];
+ char word[MAXLINE];
+ const char *p = expression;
+ char *sub;
+ int i = 0;
+
+ while (i < 31 && (p = getword(p, word))) {
+ sub = index(word, '%');
+ if (!sub) {
+ args[i] = malloc(strlen(word)+1);
+ strcpy(args[i], word);
+ } else {
+ args[i] = malloc(MAXLINE);
+
+ *sub = '\0';
+ switch (*++sub) {
+ case 'l':
+ snprintf(args[i], MAXLINE, "%s%d%s",
+ word, level, ++sub);
+ break;
+ case 'v':
+ snprintf(args[i], MAXLINE, "%s%s%s",
+ word, vol, ++sub);
+ break;
+ case '%':
+ snprintf(args[i], MAXLINE, "%s%%%s",
+ word, ++sub);
+ break;
+ default:
+ fprintf(stderr,
+ "Don't understand substitution"
+ " code `%%%c'\n", *sub);
+ return NULL;
+ }
+ }
+ i++;
+ }
+ sub = index(word, '%');
+ if (!sub) {
+ args[i] = malloc(strlen(word)+1);
+ strcpy(args[i], word);
+ } else {
+ args[i] = malloc(MAXLINE);
+
+ *sub = '\0';
+ switch (*++sub) {
+ case 'l':
+ snprintf(args[i], MAXLINE, "%s%d%s",
+ word, level, ++sub);
+ break;
+ case 'v':
+ snprintf(args[i], MAXLINE, "%s%s%s",
+ word, vol, ++sub);
+ break;
+ case '%':
+ snprintf(args[i], MAXLINE, "%s%%%s",
+ word, ++sub);
+ break;
+ default:
+ fprintf(stderr,
+ "Don't understand substitution"
+ " code `%%%c'\n", *sub);
+ return NULL;
+ }
+ }
+
+ args[++i] = NULL;
+
+ return args;
+}
+
+int
+senddump(int outfd, disk_t *disk, int dumplevel)
+{
+ char buffer[MAXLINE];
+ char path[MAXPATHLEN] = "";
+ int pout[2], perr[2], errfd = -1, error = 0;
+ pid_t pid;
+ ssize_t readb;
+ struct sigaction sa, ocsa, opsa;
+
+ setproctitle("Dumping %s", disk->vol);
+
+ if (pipe(pout) == -1 || pipe(perr) == -1) {
+ err("Couldn't create pipes: %s.\n", strerror(errno));
+ close(outfd);
+ return -1;
+ }
+
+ sa.sa_flags = 0;
+ sigemptyset(&sa.sa_mask);
+ sa.sa_handler = sig_child;
+ sigaction(SIGCHLD, &sa, &ocsa);
+ sa.sa_handler = sig_pipe;
+ sigaction(SIGPIPE, &sa, &opsa);
+
+ pid = fork();
+ if (pid == -1) {
+ err("Couldn't fork: %s.\n", strerror(errno));
+ close(outfd);
+ return -1;
+ } else if (!pid) {
+ char **args;
+
+ sa.sa_flags = 0;
+ sigemptyset(&sa.sa_mask);
+ sa.sa_handler = SIG_DFL;
+ sigaction(SIGCHLD, &sa, NULL);
+ sigaction(SIGPIPE, &sa, NULL);
+
+ close(pout[0]); close(perr[0]);
+ if (dup2(pout[1], STDOUT_FILENO) == -1 ||
+ dup2(perr[1], STDERR_FILENO) == -1) {
+ err("Couldn't set up pipes: %s.\n", strerror(errno));
+ exit(1);
+ }
+
+ args = genargs(disk->type->d_cmdline, dumplevel, disk->vol);
+ if (!args)
+ exit(1);
+
+ if (execvp(args[0], args) == -1) {
+ fprintf(stderr, "Couldn't exec %s: %s.\n",
+ args[0], strerror(errno));
+ exit(1);
+ }
+ }
+
+ close(pout[1]); close(perr[1]);
+ if (fcntl(pout[0], F_SETFL, O_NONBLOCK) == -1 ||
+ fcntl(perr[0], F_SETFL, O_NONBLOCK) == -1) {
+ err("Couldn't set pipes to non-blocking: %s.\n",
+ strerror(errno));
+ close(outfd);
+ return -1;
+ }
+
+ while (exit_status == -1) {
+ fd_set readfds;
+
+ FD_ZERO(&readfds);
+ FD_SET(pout[0], &readfds);
+ FD_SET(perr[0], &readfds);
+
+ readb = select(FD_SETSIZE, &readfds, NULL, NULL, NULL);
+ if (readb == -1) {
+ if (errno == EINTR)
+ continue;
+ fprintf(stderr,
+ "Dumper died abnormally: %s.\n",
+ strerror(errno));
+ error = 1;
+ break;
+ }
+
+ if (FD_ISSET(pout[0], &readfds)) {
+ READO:
+ readb = read(pout[0], buffer, sizeof(buffer));
+ if (readb == -1) {
+ if (errno == EINTR)
+ goto READO;
+ fprintf(stderr, "Read died abnormally: %s.\n",
+ strerror(errno));
+ error = 1;
+ break;
+ }
+
+ write(outfd, buffer, readb);
+ }
+ if (FD_ISSET(perr[0], &readfds)) {
+ if (errfd == -1) {
+ sprintf(path, "%s/ticra-dumperr.%d", TMPPATH,
+ getpid());
+ errfd = open(path,
+ O_WRONLY|O_CREAT|O_TRUNC, 0666);
+ if (errfd == -1) {
+ err("Couldn't open error output file"
+ " `%s': %s.\n", path,
+ strerror(errno));
+ error = 1;
+ break;
+ }
+ }
+ READE:
+ readb = read(perr[0], buffer, sizeof(buffer));
+ if (readb == -1) {
+ if (errno == EINTR)
+ goto READE;
+ fprintf(stderr,
+ "Stderr read died abnormally: %s\n",
+ strerror(errno));
+ error = 1;
+ break;
+ }
+
+ write(errfd, buffer, readb);
+ }
+ }
+ if (errfd != -1)
+ close(errfd);
+ close(outfd);
+
+ sigaction(SIGPIPE, NULL, &opsa);
+ sigaction(SIGCHLD, NULL, &ocsa);
+
+ if (!exit_status) {
+ printf(DUMP_DONE "\n");
+ fflush(stdout);
+ if (*path)
+ (void)unlink(path);
+ return 0;
+ }
+
+
+ switch (exit_status) {
+ case -1:
+ fprintf(stderr, "Exit status unset.\n");
+ break;
+ case -2:
+ /* TODO: handle me. */
+ err("You're probably dead, this shouldn't happen.\n");
+ return -1;
+ default:
+ if (*path) {
+ fprintf(stderr,
+ "Error occured while dumping %s;"
+ " output follows:\n", disk->vol);
+ fflush(stderr);
+
+ errfd = open(path, O_RDONLY);
+ if (errfd != -1) {
+ while ((readb = read(errfd, buffer,
+ sizeof(buffer))) > 0)
+ write(STDERR_FILENO, buffer, readb);
+ close(errfd);
+ break;
+ }
+ }
+ fprintf(stderr, "Dump process returned %d.\n", exit_status);
+ }
+
+ printf(DUMP_DONE "\n");
+ fflush(stdout);
+ return 0;
+}
+
+int
+do_sendme(disklist_t *disklist, char *buffer, int sock)
+{
+ char word[MAXLINE], vol[MAXLINE];
+ disklist_t *disk = disklist;
+ char *p = buffer;
+ auth_t authtype = -1;
+ int dumplevel = 0;
+
+ while ((p = getword(p, word))) {
+ if (!strncmp(word, DISK_NAME, sizeof(word))) {
+ p = getword(p, vol);
+ if (!p) {
+ err("NAME without argument");
+ return -1;
+ }
+ } else if (!strncmp(word, DISK_LEVEL, sizeof(word))) {
+ p = getword(p, word);
+ if (!p) {
+ err("LEVEL command without argument");
+ return -1;
+ }
+ dumplevel = atoi(word);
+ } else if (!strncmp(word, DISK_AUTH, sizeof(word))) {
+ p = getword(p, word);
+ if (!strncmp(word, AUTH_NOAUTH, sizeof(word)))
+ authtype = NOAUTH;
+ else if (!strncmp(word, AUTH_RSH, sizeof(word)))
+ authtype = RSH;
+ else {
+ err("Don't understand auth type `%s'.\n",
+ word);
+ return -1;
+ }
+ } else {
+ err("Don't understand DISK field `%s'.\n", word);
+ return -1;
+ }
+ }
+
+ while (disk && strncmp(disk->disk.vol, vol, sizeof(disk->disk.vol)))
+ disk = disk->next;
+
+ if (!disk) {
+ err("Disk `%s' not found in diskilst.\n", vol);
+ return -1;
+ }
+
+ /* TODO: figure this stuff out. */
+ switch (authtype) {
+ case NOAUTH: {
+ struct sockaddr_in fa;
+ int newsock, fromlen = sizeof(fa);
+
+ exit_status = -1;
+ printf(REQ_ACK "\n");
+ fflush(stdout);
+
+ /*
+ * TODO: for some reason accept doesn't die when the
+ * sshd does.
+ */
+ setproctitle("Waiting for connection");
+ newsock = accept(sock, (struct sockaddr *)&fa, &fromlen);
+ if (newsock == -1) {
+ err("Couldn't accept a new connection: %s.\n",
+ strerror(errno));
+ return -1;
+ }
+
+ if (senddump(newsock, &disk->disk, dumplevel) == -1)
+ return -1;
+ }; break;
+ case RSH:
+ err("RSH auth currently not supported.\n");
+ break;
+ default:
+ err("AUTH field has not been set.\n", authtype);
+ }
+
+ return 0;
+}
+
+int
+main(int argc, char *argv[])
+{
+ char buffer[MAXLINE];
+ disklist_t *disklist;
+ int sock = 0;
+
+ if (read_config(CLIENT_CONFIG_FILE) == -1) {
+ err("Couldn't open config file %s: %s.\n",
+ CLIENT_CONFIG_FILE, strerror(errno));
+ return 1;
+ }
+
+ printf(INIT_REQ "\n");
+ fflush(stdout);
+
+ if (fgets(buffer, sizeof(buffer), stdin) == NULL) {
+ err("NULL input waiting for handshake ACK.\n");
+ return 1;
+ }
+
+ if (strncmp(buffer, INIT_ACK "\n", sizeof(buffer))) {
+ err("Recieved invalid handshake ACK: %s.", buffer);
+ return 1;
+ }
+
+ if (read_dumptypes(DATADIR "/dumptypes") == -1)
+ return 1;
+
+ disklist = read_disklist(DATADIR "/disklist");
+ if (!disklist)
+ return 1;
+
+ setproctitle("Waiting for instructions");
+ while (fgets(buffer, sizeof(buffer), stdin)) {
+ char word[MAXLINE];
+ char *p = buffer;
+
+ p = getword(p, word);
+ if (!strncmp(word, DUMP_SENDME, sizeof(word))) {
+ if (do_sendme(disklist, p, sock) == -1)
+ return 2;
+ } else if (!strncmp(word, DUMP_DONE, sizeof(word)))
+ break;
+ else if (!strncmp(word, PORT_REQ, sizeof(word))) {
+ sockopt_t opt;
+ struct sockaddr_in sa;
+ int port;
+
+ p = getword(p, word);
+ if (!p) {
+ err("PORT command without argument.\n");
+ return 3;
+ }
+ port = atoi(word);
+ if (!p) {
+ err("PORT `%s' isn't a number.\n", word);
+ return 3;
+ }
+
+ if (!port)
+ break;
+
+ sock = socket(PF_INET, SOCK_STREAM, 0);
+ if (!sock) {
+ err("Couldn't initialize socket: %s.\n",
+ strerror(errno));
+ return 3;
+ }
+
+ opt = 1;
+ if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
+ &opt, sizeof(opt)) == -1) {
+ err("Couldn't make socket re-usable: %s.\n",
+ strerror(errno));
+ return 3;
+ }
+
+ sa.sin_family = AF_INET;
+ sa.sin_port = htons(port);
+ sa.sin_addr.s_addr = INADDR_ANY;
+ if (bind(sock, (struct sockaddr *)&sa,
+ sizeof(sa)) == -1) {
+ err("Couldn't bind to port %d: %s.\n",
+ port, strerror(errno));
+ return 3;
+ }
+
+ if (listen(sock, 0) == -1) {
+ err("Couldn't set backlog on socket: %s.\n",
+ strerror(errno));
+ return 3;
+ }
+
+ printf(REQ_ACK "\n");
+ fflush(stdout);
+ } else {
+ err("Don't understand command `%s'.\n", word);
+ return 4;
+ }
+
+ setproctitle("Waiting for instructions");
+ }
+
+ return 0;
+}