src/vsl_rcp.c

Go to the documentation of this file.
00001 /*
00002  * Copyright 2005, 2006, Internet2
00003  * Legal conditions are in file LICENSE
00004  * (MD5 = c434f2e53b8089d8b4d0172c7ce07360).
00005  */
00006 
00007 /**
00008  * @file   vsl_rcp.c
00009  * @author Nikolaus Rath
00010  * @brief  VSL filetransfer client
00011  */
00012 
00013 #include "vsl_rcp.h"
00014 #include "vsl_api.h"
00015 #include <sys/types.h>
00016 #include <sys/stat.h>
00017 #include <unistd.h>
00018 #include "vsl.h"
00019 #include <pwd.h>
00020 #include <strings.h>
00021 #include <string.h>
00022 #include <stdlib.h>
00023 #include <stdio.h>
00024 #include <fcntl.h>
00025 #include <signal.h>
00026 #include "vfer.h"
00027 
00028 /**
00029  * @brief structure used for parsing the command line arguments and options of test_rcp
00030  */
00031 typedef struct vsl_rcp_info {
00032     char local_path[MAX_PATH];
00033     char remote_path[MAX_PATH];
00034     char cmd[MAX_PATH];
00035     char user[MAX_USER];
00036     char host[MAX_HOST];
00037     char from_remote;   /* 0: yes (get remote file to local sys),
00038                          * 1: no (put local file to remote sys)*/
00039     char logfile[MAX_PATH];
00040     int debug;
00041 } vsl_rcp_info;
00042 
00043 
00044 /* Prototypes */
00045 void    print_help      (char* prog, int verbose);
00046 int     parse_opts      (int argc, char** argv, vsl_rcp_info *t);
00047 
00048 
00049 /**
00050  * Prints a verbose or a non-verbose usage page
00051  *
00052  * @param prog program name to use in the help page
00053  * @param verbose controls whether the page is verbose(1) or not(0)
00054  */
00055 void print_help(char* prog, int verbose) {
00056     printf("usage: %s [-h] [-v[v]] [-c path_to_rcpd] [[user1@]host1:]file1 [[user2@]host2:]file2\n", prog);
00057     if (verbose == 0) return;
00058     printf("\t-h        brings up this help page                [optional]\n");
00059     printf("\t-v        verbose message printing                [optional]\n");
00060     printf("\t-vv       even more verbose message printing      [optional]\n");
00061     printf("\t-l        only in combination with -v. Specifies the (remote) filename\n"
00062            "\t          where the server writes its debug output to. Defaults to\n"
00063            "\t          " DEBUGLOG "\t\t [optional]\n");
00064     printf("\t-c        pathname of vsl_rcpd on remote host     [optional]\n");
00065     printf("\tuser[1,2] username to use when connecting to host [optional]\n");
00066     printf("\thost[1,2] hostname of remote system\n");
00067     printf("\tfile[1,2] local, remote file path\n");
00068     printf("notes:\n");
00069     printf("\t- one remote host is required (signaled with ':')\n");
00070     printf("\t- username is defauled to user invoking the program\n");
00071     printf("\t- remote->remote copying is not implemented\n");
00072 } /* print_help() */
00073 
00074 
00075 /**
00076  * Parses the command line arguments and options to main()
00077  *
00078  * This function will print descriptive errors it finds to stderr
00079  *
00080  * @param[in] argc is the argc argument to main
00081  * @param[in] argv is the argv argument to main
00082  * @param[out] info structure to store parsed arguments in
00083  * @return
00084  *      0 on success
00085  *      -1 on error
00086  *      -2 if help option was specified
00087  */
00088 int parse_opts(int argc, char** argv, vsl_rcp_info* info) {
00089     int c;
00090     char* r;            /* remote path ptr */
00091     char* l;            /* local path ptr */
00092     char *cp, *cp2;
00093     struct passwd *pwentry;     /* for getting current username */
00094     int uid;
00095 
00096     /* getopt variables */
00097     extern char *optarg;
00098     extern int optind;
00099     extern int optopt;
00100     extern int opterr;
00101 
00102     opterr=0;           /* suppress error printing by getopt() */
00103     bzero(info->local_path, MAX_PATH);
00104     bzero(info->remote_path, MAX_PATH);
00105     bzero(info->user, MAX_USER);
00106     bzero(info->host, MAX_HOST);
00107     strncpy(info->cmd, "vsl_rcpd", MAX_PATH);
00108     info->debug = 0;
00109     strncpy(info->logfile, DEBUGLOG, MAX_PATH);
00110     while (1) {
00111         c = getopt (argc, argv, ":hc:l:v");
00112         if (c == -1) break;
00113         switch (c) {
00114         case 'h':
00115             return -2;
00116             break;
00117         case 'c':
00118             strncpy(info->cmd, optarg, MAX_PATH);
00119             break;
00120         case 'l':
00121             strncpy(info->logfile, optarg, MAX_PATH);
00122             break;
00123         case 'v':
00124             if (info->debug == 0) {
00125                 vsl_debug(stderr, NULL);
00126                 info->debug=1;
00127             }
00128             else {
00129                 vsl_debug(stderr, "pacC");
00130                 info->debug=2;
00131             }
00132             break;
00133         case ':':
00134             fprintf(stderr,  "option %c missing value\n", optopt);
00135             return -1;
00136         case '?':
00137             fprintf(stderr,  "unknown option: %c\n", optopt);
00138             return -1;
00139         default:
00140             fprintf(stderr,  "getopt returned character code 0%o\n", c);
00141             return -1;
00142         }
00143     }
00144     if (argc - optind != 2) {
00145         fprintf(stderr,  "exactly two non-option args required\n");
00146         return -1;
00147     }
00148 
00149     /* detect which of 2 args is a local and remote path */
00150     cp = index(argv[optind], ':');
00151     cp2 = index(argv[optind+1], ':');
00152     if (cp != NULL && cp2 != NULL) {
00153         fprintf(stderr,  "one of file paths must be local\n");
00154         return -1;
00155     } else if (cp == NULL && cp2 == NULL) {
00156         fprintf(stderr,  "one of file paths must be remote\n");
00157         return -1;
00158     }
00159 
00160     /* set local and remote path pointers */
00161     if (cp != NULL) {
00162         if (cp == argv[optind]) { /* no hostname */
00163             fprintf(stderr,  "no hostname specified for remote path\n");
00164             return -1;
00165         }
00166         info->from_remote = 0;
00167         r=argv[optind];
00168         l=argv[optind+1];
00169     } else {
00170         if (cp2 == argv[optind+1]) { /* no hostname */
00171             fprintf(stderr,  "no hostname specified for remote path\n");
00172             return -1;
00173         }
00174         info->from_remote = 1;
00175         l=argv[optind];
00176         r=argv[optind+1];
00177     }
00178 
00179     /* set the local path */
00180     strncpy(info->local_path, l, MAX_PATH);
00181 
00182     /* extract the remote path */
00183     /* note that ':' must be in string pointed to by r (checked above) */
00184     cp = index(r, ':');
00185     if (strlen(r) - 1 + r == cp) { /* ..: */
00186         /* printf("remote path set to ~/");  */
00187         strcpy(info->remote_path, "~/");
00188     } else {            /* ..:.. */
00189         strncpy(info->remote_path, cp+1, MAX_PATH);
00190     }
00191 
00192     /* extract the host */
00193     cp2 = index(r, '@');
00194     if (cp2 == NULL) {  /* ..:.. */
00195         /* set the username to the current user name
00196          * implicitly as there is no @ symbol to tell us
00197          * username explicitly */
00198         uid=getuid();
00199         if ((pwentry = getpwuid(uid)) == NULL) {
00200             fprintf(stderr, "failed getting password entry for uid(%d)\n", uid);
00201             return -1;
00202         }
00203         if (strlen(pwentry->pw_name) > MAX_USER) {
00204             fprintf(stderr, "length of username must be less than %d\n", MAX_USER);
00205             return -1;
00206         }
00207         strncpy(info->user, pwentry->pw_name, MAX_USER);
00208         /* set the hostname to everything before the ':' char */
00209         if (cp - r > MAX_HOST) {
00210             fprintf(stderr, "length of hostname must be less than %d\n", MAX_HOST);
00211             return -1;
00212         }
00213         strncpy(info->host, r, cp - r);
00214     } else {
00215         if (cp2 > cp) { /* ..:@.. */
00216             fprintf(stderr, "'@' must come before ':'\n");
00217             return -1;
00218         } else if (cp2 + 1 == cp) { /* ..@:.. */
00219             fprintf(stderr, "no hostname specified for remote path\n");
00220             return -1;
00221         } else if (cp2 == r) { /* @.. */
00222             fprintf(stderr, "missing username for remote path\n");
00223             return -1;
00224         }
00225         /* extract the host from between '@' and ':' chars */
00226         if (cp - cp2 > MAX_HOST) {
00227             fprintf(stderr, "length of hostname must be less than %d\n", MAX_HOST);
00228             return -1;
00229         }
00230         strncpy(info->host, cp2+1, cp - cp2 - 1);
00231         /* extract the username before the '@' char */
00232         if (cp2 - r > MAX_USER) {
00233             fprintf(stderr, "length of username must be less than %d\n", MAX_USER);
00234             return -1;
00235         }
00236         strncpy(info->user, r, cp2 - r);
00237     }
00238 
00239     /* check if local path is a directory, if so append filename from remote path */
00240     if (info->local_path[strlen(info->local_path) - 1] == '/') {
00241         cp = rindex(info->remote_path, '/');
00242         strncpy(info->local_path + strlen(info->local_path), cp+1, MAX_PATH - strlen(info->local_path));
00243     }
00244 
00245     return 0;
00246 } /* parse_opts() */
00247 
00248 
00249 /**
00250  * This function is a catcher of SIGINT signal, installed in main()
00251  */
00252 void sig_handler(int sig) {
00253     exit(1);
00254 } /* sig_handler() */
00255 
00256 /**
00257  * main
00258  */
00259 int main (int argc, char **argv) {
00260     int ret;
00261 
00262     /* Handle commandline */
00263     vsl_rcp_info info;
00264     if (argc == 1) {
00265         print_help(argv[0], 0);
00266         return 0;
00267     }
00268     if ( (ret = parse_opts(argc, argv, &info)) == -2) {
00269         print_help(argv[0], 1);
00270         return 0;
00271     } else if (ret == -1) {
00272         print_help(argv[0], 0);
00273         return -1;
00274     }
00275 
00276     /* construct client_msg */
00277     vsl_client_msg_t client_msg;
00278     int fd;
00279     char *c;
00280     strncpy(client_msg.remote_path, info.remote_path, MAX_PATH);
00281     client_msg.from_remote = info.from_remote;
00282     if ((c = rindex(info.local_path, '/')) == NULL) {
00283         strncpy(client_msg.local_name, info.local_path, MAX_PATH);
00284     } else {
00285         strncpy(client_msg.local_name, c+1, MAX_PATH);
00286     }
00287     if (info.from_remote == 1) {
00288         if ((fd = open(info.local_path, O_RDONLY)) == -1) {
00289             fprintf(stderr, "couldn't open local file [%s] for reading\n", info.local_path);
00290             exit(1);
00291         }
00292         client_msg.file_size = lseek(fd, 0, SEEK_END);
00293         close(fd);
00294     } else {
00295         client_msg.file_size = 0;
00296     }
00297 
00298     /* Init sockets */
00299     vsl_sock sock;
00300     vfer_fd vfd;
00301     if ( (ret = vsl_init()) != 0) {
00302         fprintf(stderr, "cannot init vsl: %s!\n", vsl_errortext(ret));
00303         exit(1);
00304     }
00305     if ( (vfd = vfer_socket(SOCK_DGRAM)) < 0) {
00306         fprintf(stderr, "vfer_socket failed: %s!\n", vfer_errortext(vfd));
00307         exit(1);
00308     }
00309     if ( (ret = vsl_socket(&sock, vfd)) != 0) {
00310         fprintf(stderr, "vsl_socket failed: %s!\n", vsl_errortext(ret));
00311         exit(1);
00312     }
00313 
00314     /* connect to rcpd server */
00315     char errmsg[512] = "";
00316     int s_argc = 1;
00317     const char* s_argv[3];
00318     s_argv[0] = MAGIC_PARM;
00319     if ( info.debug > 0 ) {
00320         s_argc = 2;
00321         s_argv[1] = info.logfile;
00322     }
00323     if ( info.debug > 1 ) {
00324         s_argc = 3;
00325         s_argv[2] = "paCc";
00326     }
00327     if ( (ret = vsl_connect_ssh(&sock, info.host, info.user,
00328                                 info.cmd, s_argc, s_argv,
00329                                 errmsg)) != 0) {
00330         fprintf(stderr, "vsl_connect_ssh failed: %s!\n", vsl_errortext(ret));
00331         fprintf(stderr, "server said: %s!\n", errmsg);
00332         exit(1);
00333     }
00334 
00335     /* install a handler for SIGINT that will exit gracefully */
00336     struct sigaction sa;
00337     sa.sa_handler = &(sig_handler);
00338     if (sigaction(SIGINT, &sa, NULL) == -1)
00339         LOG("couldn't install a signal handler for SIGINT");
00340 
00341     /* send client msg to rcpd server */
00342     client_msg.file_size = htonl(client_msg.file_size);
00343     if ((ret = vsl_send(&sock, &client_msg, sizeof(vsl_client_msg_t))) < 0) {
00344         fprintf(stderr, "send client msg failed [%s]\n", vsl_errortext(ret));
00345         goto CLEAN_UP_NO_STAT;
00346     }
00347     client_msg.file_size = ntohl(client_msg.file_size);
00348 
00349     /* receive server msg from rcpd server */
00350     vsl_server_msg_t server_msg;
00351     if ((ret = vsl_recv(&sock, &server_msg, sizeof(vsl_server_msg_t))) < 0) {
00352         fprintf(stderr, "receive server msg failed [%s]\n", vsl_errortext(ret));
00353         goto CLEAN_UP_NO_STAT;
00354     }
00355     server_msg.file_size = ntohl(server_msg.file_size);
00356     server_msg.status = ntohl(server_msg.status);
00357 
00358     switch (server_msg.status) {
00359     case RCP_READY:
00360         if (info.from_remote == 0) {
00361             /* receive remote file */
00362             if ((fd = open(info.local_path, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP)) == -1) {
00363                 perror("open for writing failed");
00364                 goto CLEAN_UP_NO_STAT;
00365             }
00366             if ((ret = vsl_recvfile(&sock, fd, 0, server_msg.file_size)) < 0)
00367                 fprintf(stderr,"vfer_recvfile failed [%s]\n", vsl_errortext(ret));
00368 
00369         } else {
00370             /* send local file */
00371             if ((fd = open(info.local_path, O_RDONLY, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP)) == -1) {
00372                 perror("open for reading failed");
00373                 goto CLEAN_UP_NO_STAT;
00374             }
00375 
00376             if ((ret = vsl_sendfile(&sock, fd, 0, client_msg.file_size)) < 0)
00377                 fprintf(stderr, "vfer_sendfile failed [%s]\n", vsl_errortext(ret));
00378         }
00379         goto CLEAN_UP_STAT;
00380 
00381     case RCP_ENOENT:
00382         fprintf(stderr, "component of the path does not exist on remote file system\n");
00383         break;
00384 
00385     case RCP_EOPEN:
00386         if (info.from_remote == 0) {
00387             fprintf(stderr, "can't open remote file for reading\n");
00388         } else {
00389             fprintf(stderr, "can't open remote file for writing\n");
00390         }
00391         break;
00392 
00393     case RCP_ENOFILE:
00394         fprintf(stderr, "file doesn't exist on remote host\n");
00395         break;
00396 
00397     case RCP_EUSER:
00398         fprintf(stderr, "wrong username specified for remote host\n");
00399         break;
00400 
00401     case RCP_EOTHER:
00402         fprintf(stderr, "unspecified remote error\n");
00403         break;
00404 
00405     default:
00406         fprintf(stderr, "could not interpret server msg code\n");
00407         break;
00408     }
00409 
00410 CLEAN_UP_NO_STAT:
00411     vsl_close(&sock);
00412     vsl_uninit();
00413     close(fd);
00414     return 0;
00415 
00416 CLEAN_UP_STAT:
00417     ;
00418     vfer_stats stats;
00419     stats = vfer_sockstats(vsl_vferfd(&sock));
00420     if ( info.debug )
00421         vfer_rcp_print_stats(&stats);
00422     vsl_close(&sock);
00423     vfer_close(vfd);
00424     vsl_uninit();
00425     close(fd);
00426     return 0;
00427 }
00428 
00429 
00430 /*
00431  * Local Variables:
00432  * compile-command: "cd ..; make file_xfer"
00433  * compilation-search-path: ("..")
00434  * End:
00435  */

Generated on Tue Aug 8 16:07:20 2006 for VFER by  doxygen 1.4.7