#include "config.h" #include "conf.h" #include "err.h" #include "strutil.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef NEED_LIBUTIL #include #endif #ifdef NEED_STRINGS #include #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; }