/* * 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; } 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); }