diff options
-rw-r--r-- | qarsh/Makefile | 3 | ||||
-rw-r--r-- | qarsh/qacp.c | 334 | ||||
-rw-r--r-- | qarsh/qarsh_packet.c | 147 | ||||
-rw-r--r-- | qarsh/qarsh_packet.h | 24 | ||||
-rw-r--r-- | qarsh/qarshd.c | 90 |
5 files changed, 595 insertions, 3 deletions
diff --git a/qarsh/Makefile b/qarsh/Makefile index 0402db7..a742e12 100644 --- a/qarsh/Makefile +++ b/qarsh/Makefile @@ -7,10 +7,11 @@ LOADLIBES := -lxml2 COMMON := qarsh_packet.c sockutil.c -all: qarshd qarsh +all: qarshd qarsh qacp qarshd: qarshd.c $(COMMON) qarsh: qarsh.c $(COMMON) +qacp: qacp.c $(COMMON) install: qarsh qarshd @echo Installing qarsh daemon and client diff --git a/qarsh/qacp.c b/qarsh/qacp.c new file mode 100644 index 0000000..b404482 --- /dev/null +++ b/qarsh/qacp.c @@ -0,0 +1,334 @@ +/* + * QA Remote Copy - client side + * + * Copy a file to, or from, server. + * + * + */ + +#define _GNU_SOURCE +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <unistd.h> +#include <fcntl.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <sys/socket.h> +#include <sys/select.h> +#include <netinet/in.h> +#include <netdb.h> +#include <pwd.h> +#include <sys/sendfile.h> +#include <libgen.h> + + +#include "sockutil.h" +#include "qarsh_packet.h" + +#define QARSHD_CONTROL_PORT 5008 +#define QARSH_MINPORT 5010 + +/* Globals */ +int qacp_fd = -1; /* The control connection to qacpd */ + +void +usage() +{ +/* printf("usage: qacp: [-r] [[user@]host1:]file1 [...] " */ + /* "[[user@]host2:]file2\n"); */ + + /* For now, only simple cmdlines */ + fprintf(stderr, "usage:\n\t%s\n\t%s\n", + "qacp <localpath> [<loacalpath> ...] [user@]host:<rmtpath>", + "qacp [user@]host:<rmtpath> [[user@]host:<rmtpath> ...] <localpath> "); +} + +char * +copyargs(char **argv) +{ + int cc; + char **ap, *p; + char *args; + + cc = 0; + for (ap = argv; *ap; ++ap) + cc += strlen(*ap) + 1; + if (cc == 0) return NULL; + args = malloc(cc); + if (!args) { + perror("qacp: malloc failed in copyargs"); + exit(1); + } + for (p = args, ap = argv; *ap; ++ap) { + for (p = strcpy(p, *ap); *p; ++p); + if (ap[1]) + *p++ = ' '; + } + return args; +} + +void +set_remote_user(char *user, char *group) +{ + struct qa_packet *qp; + + qp = make_qp_setuser(user, group); + qp->qp_seq = 1; + send_packet(qacp_fd, qp); + qpfree(qp); + qp = recv_packet(qacp_fd); + if (qp && qp->qp_type == QP_RETURNCODE + && qp->qp_returncode.qp_rc == -1) { + + fprintf(stderr, "Remote side failed, %s\n", + qp->qp_returncode.qp_strerror); + close(qacp_fd); + exit(125); + } +} + +void +qacp_sendonefile(const char *host, const char *srcfile, const char *destfile) +{ + struct qa_packet *qp; + struct sockaddr_in daddr; + socklen_t dlen; + int fd; + int sd; + int outsd; + int port; + ssize_t nbytes; + off_t offset = 0; + struct stat sb; + + /* Recall that the packet types are qarshd-centric, so if we want + * to send a file to the host running qarshd we have to tell + * qarshd to recv a file. */ + + if ((fd = open(srcfile, O_RDONLY)) <0) { + fprintf(stderr, "Could not open %s: %s\n", srcfile, strerror(errno)); + close(qacp_fd); + exit(errno); + } + + if (fstat(fd, &sb) < 0) { + fprintf(stderr, "Could not stat %s: %s\n", srcfile, strerror(errno)); + close(qacp_fd); + exit(errno); + } + + sd = bind_any(QARSH_MINPORT); + port = getsockport(sd); + + qp = make_qp_recvfile(destfile, port, sb.st_size, sb.st_mode); + qp->qp_seq = 1; + send_packet(qacp_fd, qp); + qpfree(qp); + + dlen = sizeof daddr; + outsd = accept(sd, (struct sockaddr *) &daddr, &dlen); + + nbytes = sendfile(outsd, fd, &offset, sb.st_size); + + close(sd); + close(outsd); + close(fd); + + /* Await our return code from qarshd */ + qp = recv_packet(qacp_fd); + if (qp && qp->qp_type == QP_RETURNCODE + && qp->qp_returncode.qp_rc == -1) { + fprintf(stderr, "Remote side failed, %s\n", + qp->qp_returncode.qp_strerror); + close(qacp_fd); + exit(125); + } + + return; +} + +void +qacp_recvonefile(const char *host, const char *srcfile, const char *destfile) +{ + struct qa_packet *qp; + + /* Recall that the packet types are qarshd-centric, so if we want + * to recv a file from the host running qarshd we have to tell + * qarshd to send a file. */ + + qp = make_qp_sendfile(srcfile, 88, 8888); + qp->qp_seq = 1; + send_packet(qacp_fd, qp); + qpfree(qp); + + return; +} + +int +recvfiles(char **argv, int argc, short recursive, short quiet) +{ + puts("recvfiles() NA"); + return 0; +} + + +int +sendfiles(char **argv, int argc, short recursive, short quiet) +{ + char *cp; + char *rhost = NULL; + char *ruser = NULL; + char *rgrp = NULL; + struct passwd *pw; + int file; + char *rmtpath; + char *destpath; + char *tmpstr; + char *lbnp; /* local file basename */ + char *ldnp; /* local file dirname */ + char *rbnp; /* remote file basename */ + char *rdnp; /* remote file dirname */ + + if (strchr(argv[argc-1], ':') == NULL) { + fprintf(stderr, "%s is not a valid remote host:file string\n", + argv[argc-1]); + return -1; + } + + rhost = strdup(argv[argc-1]); + cp = strchr(rhost, ':'); + *cp = '\0'; + + /* Grab any user/group info */ + if ((cp = strchr(rhost, '@'))) { + ruser = rhost; + rhost = cp+1; + *cp = '\0'; + } + + if (ruser && (cp = strchr(ruser, '.'))) { + rgrp = cp+1; + *cp = '\0'; + } + + if (!(pw = getpwuid(getuid()))) { + fprintf(stderr, "qacp: unknown user id.\n"); + return -1; + } + + if (ruser == NULL) { + ruser = strdup(pw->pw_name); + } + + qacp_fd = connect_to_host(rhost, QARSHD_CONTROL_PORT); + if (qacp_fd == -1) { + if (errno == 0) { + fprintf(stderr, "Could not connect to %s:%d, %d: %s\n", + rhost, QARSHD_CONTROL_PORT, h_errno, hstrerror(h_errno)); + } else { + fprintf(stderr, "Could not connect to %s:%d, %d: %s\n", + rhost, QARSHD_CONTROL_PORT, errno, strerror(errno)); + } + return -1; + } + + set_remote_user(ruser, rgrp); + + cp = strdup(argv[argc-1]); + cp = strchr(argv[argc-1], ':'); + cp++; + rmtpath = strdup(cp); + + tmpstr = strdup(rmtpath); + rdnp = strdup(dirname(tmpstr)); + free(tmpstr); + + tmpstr = strdup(rmtpath); + rbnp = strdup(basename(tmpstr)); + free(tmpstr); + + + for (file = optind; file < (argc - 1); file++) { + tmpstr = strdup(argv[file]); + lbnp = strdup(basename(tmpstr)); + free(tmpstr); + + tmpstr = strdup(argv[file]); + ldnp = strdup(dirname(tmpstr)); + free(tmpstr); + + if (strcmp(lbnp, rbnp)) { + /* Tack on our lbnp to rmtpath */ + destpath = malloc(strlen(rmtpath) + strlen(lbnp) + 2); + memset(destpath, 0, strlen(destpath)); + strcpy(destpath, rmtpath); + strcat(destpath, "/"); + strcat(destpath, lbnp); + } else { + destpath = strdup(rmtpath); + } + + if (!quiet) { + printf("%-20.20s -> %s:%s\n", file[argv], rhost, destpath); + } + + qacp_sendonefile(rhost, file[argv], destpath); + + free(destpath); + } + + return 0; +} + + +int +main(int argc, char *argv[]) +{ + int c; + short recursive=0; + short quiet=0; + int eflag; + + while ((c = getopt(argc, argv, "+rq")) != -1) { + switch (c) { + case 'r': + recursive = 1; + break; + case 'q': + quiet = 1; + break; + case '?': + default: + printf("Unknown option %c\n", (char)optopt); + usage(); + exit(1); + } + } + + if (argv[optind + 1] == NULL) { + usage(); + exit(2); + } + + eflag = 0; + /* If last arg has a ':' then we are sending files */ + if (strchr(argv[argc-1], ':') != NULL) { + if (sendfiles(argv, argc, recursive, quiet) < 0) { + eflag++; + } + } else { + if (recvfiles(argv, argc, recursive, quiet) < 0) { + eflag++; + } + } + + close(qacp_fd); + + if (eflag) { + exit(1); + } + + exit(0); +} diff --git a/qarsh/qarsh_packet.c b/qarsh/qarsh_packet.c index bad614f..f3f5559 100644 --- a/qarsh/qarsh_packet.c +++ b/qarsh/qarsh_packet.c @@ -18,6 +18,8 @@ int parse_qp_runcmd(xmlXPathContextPtr ctxt, struct qa_packet *qp); int parse_qp_cmdexit(xmlXPathContextPtr ctxt, struct qa_packet *qp); int parse_qp_setuser(xmlXPathContextPtr ctxt, struct qa_packet *qp); int parse_qp_kill(xmlXPathContextPtr ctxt, struct qa_packet *qp); +int parse_qp_recvfile(xmlXPathContextPtr ctxt, struct qa_packet *qp); +int parse_qp_sendfile(xmlXPathContextPtr ctxt, struct qa_packet *qp); void string_qp_hello(xmlNodePtr node, struct qa_packet *qp); void string_qp_returncode(xmlNodePtr node, struct qa_packet *qp); @@ -26,11 +28,15 @@ void string_qp_ack(xmlNodePtr node, struct qa_packet *qp); void string_qp_cmdexit(xmlNodePtr node, struct qa_packet *qp); void string_qp_setuser(xmlNodePtr node, struct qa_packet *qp); void string_qp_kill(xmlNodePtr node, struct qa_packet *qp); +void string_qp_recvfile(xmlNodePtr node, struct qa_packet *qp); +void string_qp_sendfile(xmlNodePtr node, struct qa_packet *qp); void free_qp_hello(struct qa_packet *qp); void free_qp_returncode(struct qa_packet *qp); void free_qp_runcmd(struct qa_packet *qp); void free_qp_setuser(struct qa_packet *qp); +void free_qp_recvfile(struct qa_packet *qp); +void free_qp_sendfile(struct qa_packet *qp); void dump_qp_ack(struct qa_packet *qp); void dump_qp_runcmd(struct qa_packet *qp); @@ -38,6 +44,8 @@ void dump_qp_returncode(struct qa_packet *qp); void dump_qp_cmdexit(struct qa_packet *qp); void dump_qp_setuser(struct qa_packet *qp); void dump_qp_kill(struct qa_packet *qp); +void dump_qp_recvfile(struct qa_packet *qp); +void dump_qp_sendfile(struct qa_packet *qp); struct packet_internals { @@ -95,6 +103,18 @@ struct packet_internals { .pi_string = string_qp_kill, .pi_free = NULL, .pi_dump = dump_qp_kill + }, { + .pi_name = "recvfile", + .pi_parse = parse_qp_recvfile, + .pi_string = string_qp_recvfile, + .pi_free = free_qp_recvfile, + .pi_dump = dump_qp_recvfile + }, { + .pi_name = "sendfile", + .pi_parse = parse_qp_sendfile, + .pi_string = string_qp_sendfile, + .pi_free = free_qp_sendfile, + .pi_dump = dump_qp_sendfile } }; @@ -216,6 +236,44 @@ parse_qp_kill(xmlXPathContextPtr ctxt, struct qa_packet *qp) return 0; } +int +parse_qp_recvfile(xmlXPathContextPtr ctxt, struct qa_packet *qp) +{ + char *s; + + qp->qp_recvfile.qp_path = get_xpath_string(ctxt, "param[@name='path']"); + + s = get_xpath_string(ctxt, "param[@name='if_port']"); + qp->qp_recvfile.qp_if_port = atoi(s); + free(s); + + s = get_xpath_string(ctxt, "param[@name='count']"); + qp->qp_recvfile.qp_count = atoi(s); + free(s); + + s = get_xpath_string(ctxt, "param[@name='mode']"); + qp->qp_recvfile.qp_mode = atoi(s); + free(s); + + return 0; +} + +int +parse_qp_sendfile(xmlXPathContextPtr ctxt, struct qa_packet *qp) +{ + char *s; + + qp->qp_sendfile.qp_path = get_xpath_string(ctxt, "param[@name='path']"); + s = get_xpath_string(ctxt, "param[@name='of_port']"); + qp->qp_sendfile.qp_of_port = atoi(s); + free(s); + s = get_xpath_string(ctxt, "param[@name='count']"); + qp->qp_sendfile.qp_count = atoi(s); + free(s); + return 0; +} + + struct qa_packet * parse_packet(xmlXPathContextPtr ctxt) { @@ -377,6 +435,33 @@ void string_qp_kill(xmlNodePtr node, struct qa_packet *qp) xmlAddChild(node, make_param("signal", tmpstr)); } +void string_qp_recvfile(xmlNodePtr node, struct qa_packet *qp) +{ + char tmpstr[32]; + + xmlAddChild(node, make_param("path", qp->qp_recvfile.qp_path)); + + snprintf(tmpstr, 32, "%d", qp->qp_recvfile.qp_if_port); + xmlAddChild(node, make_param("if_port", tmpstr)); + + snprintf(tmpstr, 32, "%d", qp->qp_recvfile.qp_count); + xmlAddChild(node, make_param("count", tmpstr)); + + snprintf(tmpstr, 32, "%d", qp->qp_recvfile.qp_mode); + xmlAddChild(node, make_param("mode", tmpstr)); +} + +void string_qp_sendfile(xmlNodePtr node, struct qa_packet *qp) +{ + char tmpstr[32]; + + xmlAddChild(node, make_param("path", qp->qp_sendfile.qp_path)); + snprintf(tmpstr, 32, "%d", qp->qp_sendfile.qp_of_port); + xmlAddChild(node, make_param("of_port", tmpstr)); + snprintf(tmpstr, 32, "%d", qp->qp_sendfile.qp_count); + xmlAddChild(node, make_param("count", tmpstr)); +} + /* Must pass in a pointer, but not a malloc'ed pointer */ char * qptostr(struct qa_packet *qp, char **qpstr, int *qpsize) @@ -518,6 +603,38 @@ make_qp_kill(int sig) return qp; } +struct qa_packet * +make_qp_recvfile(const char *path, int if_port, size_t count, mode_t mode) +{ + struct qa_packet *qp; + qp = malloc(sizeof *qp); + assert(qp); + memset(qp, 0, sizeof *qp); + + qp->qp_type = QP_RECVFILE; + qp->qp_recvfile.qp_path = strdup(path); + qp->qp_recvfile.qp_if_port = if_port; + qp->qp_recvfile.qp_count = count; + qp->qp_recvfile.qp_mode = mode; + + return qp; +} + +struct qa_packet * +make_qp_sendfile(const char *path, int of_port, size_t count) +{ + struct qa_packet *qp; + qp = malloc(sizeof *qp); + assert(qp); + memset(qp, 0, sizeof *qp); + + qp->qp_type = QP_SENDFILE; + qp->qp_sendfile.qp_path = strdup(path); + qp->qp_sendfile.qp_of_port = of_port; + qp->qp_sendfile.qp_count = count; + return qp; +} + /* * Packet deallocation functions */ @@ -547,6 +664,19 @@ free_qp_setuser(struct qa_packet *qp) } void +free_qp_recvfile(struct qa_packet *qp) +{ + condfree(qp->qp_recvfile.qp_path); +} + +void +free_qp_sendfile(struct qa_packet *qp) +{ + condfree(qp->qp_sendfile.qp_path); +} + + +void qpfree(struct qa_packet *qp) { if (qp) { @@ -608,6 +738,23 @@ dump_qp_kill(struct qa_packet *qp) } void +dump_qp_recvfile(struct qa_packet *qp) +{ + printf("\tpath: %s\n", qp->qp_recvfile.qp_path); + printf("\tif_port: %d\n", qp->qp_recvfile.qp_if_port); + printf("\tcount: %d\n", qp->qp_recvfile.qp_count); + printf("\tmode: %o\n", qp->qp_recvfile.qp_mode); +} + +void +dump_qp_sendfile(struct qa_packet *qp) +{ + printf("\tpath: %s\n", qp->qp_sendfile.qp_path); + printf("\tof_port: %d\n", qp->qp_sendfile.qp_of_port); + printf("\tcount: %d\n", qp->qp_sendfile.qp_count); +} + +void dump_qp(struct qa_packet *qp) { printf("%s #%d\n", QP_NAME(qp->qp_type), qp->qp_seq); diff --git a/qarsh/qarsh_packet.h b/qarsh/qarsh_packet.h index 64b2b2a..b858631 100644 --- a/qarsh/qarsh_packet.h +++ b/qarsh/qarsh_packet.h @@ -13,7 +13,9 @@ enum qa_packet_type { QP_ACK = 4, QP_CMDEXIT = 5, QP_SETUSER = 6, - QP_KILL = 7 + QP_KILL = 7, + QP_RECVFILE = 8, + QP_SENDFILE = 9 }; struct qp_hello_pkt { @@ -53,6 +55,20 @@ struct qp_kill_pkt { int qp_sig; }; +struct qp_recvfile_pkt { + char *qp_path; + mode_t qp_mode; + int qp_if_port; + size_t qp_count; +}; + +struct qp_sendfile_pkt { + char *qp_path; + int qp_of_port; + size_t qp_count; +}; + + #define QP_VERSION 1 struct qa_packet { @@ -66,6 +82,8 @@ struct qa_packet { struct qp_cmdexit_pkt cmdexit; struct qp_setuser_pkt setuser; struct qp_kill_pkt kill; + struct qp_recvfile_pkt recvfile; + struct qp_sendfile_pkt sendfile; } qp_u; }; @@ -76,6 +94,8 @@ struct qa_packet { #define qp_cmdexit qp_u.cmdexit #define qp_setuser qp_u.setuser #define qp_kill qp_u.kill +#define qp_recvfile qp_u.recvfile +#define qp_sendfile qp_u.sendfile /* Prototypes */ char *qp_packet_type(enum qa_packet_type t); @@ -87,6 +107,8 @@ struct qa_packet *make_qp_runcmd(char *cmdline, int p_in, int p_out, int p_err); struct qa_packet *make_qp_cmdexit(pid_t pid, int status); struct qa_packet *make_qp_setuser(char *user, char *group); struct qa_packet *make_qp_kill(int sig); +struct qa_packet *make_qp_recvfile(const char *path, int if_port, size_t count, mode_t mode); +struct qa_packet *make_qp_sendfile(const char *path, int of_port, size_t count); char *qptostr(struct qa_packet *qp, char **qpstr, int *qpsize); void qpfree(struct qa_packet *qp); void dump_qp(struct qa_packet *qp); diff --git a/qarsh/qarshd.c b/qarsh/qarshd.c index 3eb9182..70ed09d 100644 --- a/qarsh/qarshd.c +++ b/qarsh/qarshd.c @@ -8,7 +8,9 @@ #include <sys/time.h> #include <assert.h> #include <syslog.h> +#include <fcntl.h> #include <sys/types.h> +#include <sys/stat.h> #include <sys/wait.h> #include <sys/socket.h> #include <arpa/inet.h> @@ -91,6 +93,59 @@ run_cmd(const char *cmd, int p_in, int p_out, int p_err) return pid; } +ssize_t +recvfile(const char *path, int if_port, size_t count, mode_t mode) +{ + int sd; + int ofd; + char buf[BUFSIZ]; + ssize_t nread; + ssize_t nwrote; + ssize_t nleft; + + /* Read count bytes from ifd (sd after we connect), + * write into file @ path + */ + + sd = connect_to_peer(&peername, if_port); + if (sd == -1) { + syslog(LOG_WARNING, "connect to if_port failed\n"); + return -1; + } + + if ((ofd = open(path, O_TRUNC|O_CREAT|O_WRONLY, mode)) < 0) { + syslog(LOG_WARNING, "Could not open %s to recv file: %s\n", + path, strerror(errno)); + return -1; + } + + fchmod(ofd, mode); + + nleft = count; + while (nleft > 0) { + nread = read(sd, buf, BUFSIZ); + if (nread < 0) { + return nread; + } else if (nread == 0) { /* EOF */ + break; + } + + nwrote = write(ofd, buf, nread); + nleft -= nread; + } + + if (nleft != 0) { + unlink(path); + syslog(LOG_WARNING, "Short file transfer in recvfile(), " + "%d bytes lost, wanted %d\n", nleft, count); + } + + close(sd); + close(ofd); + + return count - nleft; +} + void handle_packets(int infd) { @@ -98,11 +153,13 @@ handle_packets(int infd) int nfd; struct timespec timeout; struct qa_packet *qp = NULL, *rp = NULL; - sigset_t sigmask, orig_sigmask; + sigset_t sigmask, orig_sigmask; struct sigaction sa; pid_t child_pid = 0; int child_status; + ssize_t nbytes; + int i=0; sigemptyset(&sigmask); sigaddset(&sigmask, SIGCHLD); @@ -116,6 +173,7 @@ handle_packets(int infd) FD_SET(infd, &rfds); timeout.tv_sec = 3; + syslog(LOG_INFO, "Iteration: %d\n", i++); nfd = pselect(infd+1, &rfds, NULL, NULL, &timeout, &orig_sigmask); if (child_exitted) { @@ -165,6 +223,36 @@ handle_packets(int infd) qp->qp_runcmd.qp_stdout_port, qp->qp_runcmd.qp_stderr_port); break; + case QP_RECVFILE: + syslog(LOG_INFO, "Got a QP_GETFILE with path = %s, " + "ifd = %d, count = %d, mode = %o\n", + qp->qp_recvfile.qp_path, + qp->qp_recvfile.qp_if_port, + qp->qp_recvfile.qp_count, + qp->qp_recvfile.qp_mode); + nbytes = recvfile(qp->qp_recvfile.qp_path, + qp->qp_recvfile.qp_if_port, + qp->qp_recvfile.qp_count, + qp->qp_recvfile.qp_mode); + if (nbytes < 0) { + rp = make_qp_returncode(-1, errno, strerror(errno)); + } else if (nbytes < qp->qp_recvfile.qp_count) { + char tmpstr[512]; + sprintf(tmpstr, "Excpected %d, wrote %d\n", + qp->qp_recvfile.qp_count, nbytes); + rp = make_qp_returncode(-1, 0, tmpstr); + } else { + rp = make_qp_returncode(0, 0, "Tranfer Complete"); + } + send_packet(fileno(stdout), rp); + break; + case QP_SENDFILE: + syslog(LOG_INFO, "Got a QP_SENDFILE with path = %s, " + "ofd = %d, count = %d\n", + qp->qp_sendfile.qp_path, + qp->qp_sendfile.qp_of_port, + qp->qp_sendfile.qp_count); + break; default: syslog(LOG_WARNING, "Packet type %s unimplemented", |