/* * QA Remote Copy - client side * * Copy a file to, or from, server. * * */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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 [ ...] [user@]host:", "qacp [user@]host: [[user@]host: ...] "); } 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); }