diff options
Diffstat (limited to 'qacp.c')
-rw-r--r-- | qacp.c | 382 |
1 files changed, 382 insertions, 0 deletions
@@ -0,0 +1,382 @@ +/* + * 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; +} + + +struct qp_rstat_pkt * +qacp_rstat(const char *rmtpath, int *rstaterrno) +{ + struct qa_packet *qp; + struct qp_rstat_pkt *rstatp; + + qp = make_qp_rstat(rmtpath, NULL); + send_packet(qacp_fd, qp); + qpfree(qp); + + qp = recv_packet(qacp_fd); + if (qp) { + if (qp->qp_type == QP_RSTAT) { + rstatp = malloc(sizeof *rstatp); + rstatp->qp_path = strdup(qp->qp_rstat.qp_path); + rstatp->qp_st_mode = qp->qp_rstat.qp_st_mode; + rstatp->qp_st_uid = qp->qp_rstat.qp_st_uid; + rstatp->qp_st_gid = qp->qp_rstat.qp_st_gid; + rstatp->qp_st_size = qp->qp_rstat.qp_st_size; + *rstaterrno = 0; + } else if (qp->qp_type == QP_RETURNCODE) { + rstatp = NULL; + *rstaterrno = qp->qp_returncode.qp_errno; + } + + qpfree(qp); + } + + return rstatp; +} + +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 *rdnp; /* remote path dirname */ + struct qp_rstat_pkt *rstatp; + int rstaterrno; + + + 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); + + rstatp = qacp_rstat(rmtpath, &rstaterrno); + + for (file = optind; file < (argc - 1); file++) { + tmpstr = strdup(argv[file]); + lbnp = strdup(basename(tmpstr)); + free(tmpstr); + + if (rstatp) { + /* + * If rmtpath is a dir, then we tack on the + * local files basename. + */ + if (S_ISDIR(rstatp->qp_st_mode)) { + destpath = malloc(strlen(rmtpath) + strlen(lbnp) + 2); + strcpy(destpath, rmtpath); + strcat(destpath, "/"); + strcat(destpath, lbnp); + } + + /* It rmtpath is a file, then we leave it as is */ + if (S_ISREG(rstatp->qp_st_mode)) { + destpath = strdup(rmtpath); + } + } else { + /* rmtpath does not exist, check if the dirname does */ + tmpstr = strdup(rmtpath); + rdnp = strdup(dirname(tmpstr)); + free(tmpstr); + + rstatp = qacp_rstat(rdnp, &rstaterrno); + if (rstatp) { + /* Ok, the dir exists, i.e. rmtpath is /tmp/foo + * and the first rstat was an error. Now we know + * that /tmp exists, we leave destpath == rmtpath. + */ + destpath = strdup(rmtpath); + } else { + fprintf(stderr, "%s:%s - %s\n", rhost, rmtpath, + strerror(rstaterrno)); + return -1; + } + } + + if (!quiet) { + printf("%-40.40s -> %s:%s\n", argv[file], rhost, destpath); + } + + qacp_sendonefile(rhost, argv[file], destpath); + + free(lbnp); + 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); +} |