diff options
Diffstat (limited to 'qarsh/qacp.c')
-rw-r--r-- | qarsh/qacp.c | 334 |
1 files changed, 334 insertions, 0 deletions
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); +} |