src/vfer_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  * @file   vfer_rcp.c
00008  * @author Ivan Beschastnikh
00009  * @brief  An rcp (remote copy) client, to be used with vfer_rcpd.c which implements the daemon
00010  *
00011  * This program implements reliable transfer of data to and from the
00012  * server running vfer_rcpd. It support scp style syntax.
00013  *
00014  * -     05/06/06        ivan            added -vv option
00015  * -     04/30/06        ivan            made to use vfer_recvfile and vfer_sendfile
00016  * -     04/12/06        ivan            Restructured main() and added call to vfer_rcp_print_stats()
00017  * -     02/18/06        ivan            Changed name to vfer_rcp.c
00018  * -     01/14/06        ivan            Branched from test_rcvfile.c, added scp-line command line parsing
00019  */
00020 
00021 #include "vfer_rcp.h"
00022 
00023 /**
00024  * @brief structure used for parsing the command line arguments and options of test_rcp
00025  */
00026 typedef struct rcp_info {
00027         char local_path [MAX_PATH];
00028         char remote_path[MAX_PATH];
00029         char user       [MAX_USER];
00030         char host       [MAX_HOST];
00031         uint32_t frame_size;            /* frame size to use when sending files */
00032         int port;
00033         char from_remote;       /* 0: yes (get remote file to local sys), 1: no (put local file to remote sys)*/
00034 } rcp_info;
00035 
00036 extern char *optarg;
00037 extern int optind;
00038 extern int optopt;
00039 extern int opterr;
00040 
00041 void    print_help      (char* prog, int verbose);
00042 int     parse_opts      (int argc, char** argv, FILE** testing_out);
00043 int     rcp_connect     ();
00044 
00045 /* global vars */
00046 vfer_fd connect_skt;            /* vfer_fd for communicating with the vfer library */
00047 rcp_info info;                  /* info structure with parsed command line arguments and options */
00048 int fd;                         /* file descriptor used for reading\writing the local file */
00049 int sig_mark = 0;               /* is set to 1 once a sigint is received */
00050 
00051 /**
00052  * Prints a verbose or a non-verbose usage page
00053  *
00054  * @param prog program name to use in the help page
00055  * @param verbose controls whether the page is verbose(1) or not(0)
00056  */
00057 void print_help(char* prog, int verbose) {
00058         printf("usage: %s [-h] [-v[v]] [-P port] [[user1@]host1:]file1 [[user2@]host2:]file2\n", prog);
00059         if (verbose == 0) return;
00060         printf("\t-h        brings up this help page                [optional]\n");
00061         printf("\t-v        verbose message printing                [optional]\n");
00062         printf("\t-vv       more verbose message printing           [optional]\n");
00063         printf("\t-P        specifies port to connect to            [optional]\n");
00064         printf("\tuser[1,2] username to use when connecting to host [optional]\n");
00065         printf("\thost[1,2] hostname of remote system\n");
00066         printf("\tfile[1,2] local, remote file path\n");
00067         printf("notes:\n");
00068         printf("\t- to do a remote->local copy specify the remote host and path first\n");
00069         printf("\t- to do a local->remote copy specify the local host and path first\n");
00070         printf("\t- one remote host is required (signaled with ':')\n");
00071         printf("\t- port is defaulted to 2046\n");
00072         printf("\t- username is defauled to user invoking the program but may be specified with '@'\n");
00073         printf("\t- username must match the user who started the remote host daemon\n");
00074         printf("\t- there is no other authentication with the remote daemon\n");
00075         printf("\t- remote->remote copying is not implemented\n");
00076         printf("\t- encryption is not implemented\n");
00077         printf("examples:\n");
00078         printf("\t%s /tmp/file.txt fred@work-computer.univ.edu:\n", prog);
00079         printf("\t\tcopy local file [/tmp/file.txt] to remote computer [work-computer.univ.edu] at remote path [~/] \n\n");
00080         printf("\t%s fred@work-computer.univ.edu:/tmp/file.txt ./\n", prog);
00081         printf("\t\tcopy remote file [/tmp/file.txt] at remote computer [work-computer.univ.edu] to local path [./] \n");
00082 } /* print_help() */
00083 
00084 
00085 /**
00086  * Parses the command line arguments and options to main()
00087  *
00088  * This function will print descriptive errors it finds to stdout
00089  *
00090  * @param argc is the argc argument to main
00091  * @param argv is the argv argument to main
00092  * @param testing_out is a FILE** type that is either set to NULL here or to 'stdout' depending on verbosity args
00093  * @return
00094  *      0 on success
00095  *      -1 on error
00096  *      -2 if help option was specified
00097  */
00098 int parse_opts(int argc, char** argv, FILE** testing_out) {
00099         int c;
00100         char* r;                /* remote path ptr */
00101         char* l;                /* local path ptr */
00102         char *cp, *cp2;
00103         struct passwd *pwentry; /* for getting current username */
00104         int uid;
00105 
00106         opterr=0;               /* suppress error printing by getopt() */
00107         bzero(info.local_path,MAX_PATH);
00108         bzero(info.remote_path,MAX_PATH);
00109         bzero(info.user,MAX_USER);
00110         bzero(info.host,MAX_HOST);
00111         info.frame_size = (uint32_t)(MAX_FRAME_SIZE);
00112         info.port = DEFAULT_PORT;
00113         *testing_out = NULL;
00114         while (1) {
00115                 c = getopt (argc, argv, ":hvP:");
00116                 if (c == -1) break;
00117                 switch (c) {
00118                 case 'h':
00119                         return -2;
00120                         break;
00121                 case 'P':
00122                         info.port = atoi(optarg);
00123                         break;
00124                 case 'v':       /* allow for -vv for higher verbosity */
00125                         if (*testing_out == NULL) {
00126                                 /* no api output but VFER_RCP output */
00127                                 *testing_out = stdout;
00128                                 vfer_debug(stdout, stdout, "a");
00129                         } else {
00130                                 vfer_debug(stdout, stdout, "aCcP");
00131                         }
00132                         break;
00133                 case ':':
00134                         T_ERR("parse_opts", "option %c missing value", optopt);
00135                         return -1;
00136                 case '?':
00137                         T_ERR("parse_opts", "unknown option: %c", optopt);
00138                         return -1;
00139                 default:
00140                         T_ERR("parse_opts", "getopt returned character code 0%o", c);
00141                         return -1;
00142                 }
00143         }
00144         if (argc - optind != 2) {
00145                 T_ERR("parse_opts", "exactly two non-option args required");
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                 T_ERR("parse_opts", "one of file paths must be local");
00154                 return -1;
00155         } else if (cp == NULL && cp2 == NULL) {
00156                 T_ERR("parse_opts", "one of file paths must be remote");
00157                 return -1;
00158         }
00159 
00160         /* set local and remote path pointers */
00161         if (cp != NULL) {
00162                 if (cp == argv[optind]) { /* no hostname */
00163                         T_ERR("parse_opts", "no hostname specified for remote path");
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                         T_ERR("parse_opts", "no hostname specified for remote path");
00172                         return -1;
00173                 }
00174                 info.from_remote = 1;
00175                 l=argv[optind];
00176                 r=argv[optind+1];
00177         }
00178         /* printf ("r: %s\n",r);
00179            printf ("l: %s\n",l); */
00180 
00181         /* set the local path */
00182         strncpy(info.local_path, l, MAX_PATH);
00183 
00184         /* extract the remote path */
00185         /* note that ':' must be in string pointed to by r (checked above) */
00186         cp = index(r, ':');
00187         if (strlen(r) - 1 + r == cp) { /* ..: */
00188                 /* printf("remote path set to ~/");  */
00189                 strcpy(info.remote_path, "~/");
00190         } else {                /* ..:.. */
00191                 strncpy(info.remote_path, cp+1, MAX_PATH);
00192         }
00193 
00194         /* extract the host */
00195         cp2 = index(r, '@');
00196         if (cp2 == NULL) {      /* ..:.. */
00197                 /* set the username to the current user name
00198                  * implicitly as there is no @ symbol to tell us
00199                  * username explicitly */
00200                 uid=getuid();
00201                 if ((pwentry = getpwuid(uid)) == NULL) {
00202                         T_ERR("parse_opts", "failed getting password entry for uid(%d)", uid);
00203                         return -1;
00204                 }
00205                 if (strlen(pwentry->pw_name) > MAX_USER) {
00206                         T_ERR("parse_opts", "length of username must be less than %d", MAX_USER);
00207                         return -1;
00208                 }
00209                 strncpy(info.user, pwentry->pw_name, MAX_USER);
00210                 /* set the hostname to everything before the ':' char */
00211                 if (cp - r > MAX_HOST) {
00212                         T_ERR("parse_opts", "length of hostname must be less than %d", MAX_HOST);
00213                         return -1;
00214                 }
00215                 strncpy(info.host, r, cp - r);
00216         } else {
00217                 if (cp2 > cp) { /* ..:@.. */
00218                         T_ERR("parse_opts", "'@' must come before ':'");
00219                         return -1;
00220                 } else if (cp2 + 1 == cp) { /* ..@:.. */
00221                         T_ERR("parse_opts", "no hostname specified for remote path");
00222                         return -1;
00223                 } else if (cp2 == r) { /* @.. */
00224                         T_ERR("parse_opts", "missing username for remote path");
00225                         return -1;
00226                 }
00227                 /* extract the host from between '@' and ':' chars */
00228                 if (cp - cp2 > MAX_HOST) {
00229                         T_ERR("parse_opts", "length of hostname must be less than %d", MAX_HOST);
00230                         return -1;
00231                 }
00232                 strncpy(info.host, cp2+1, cp - cp2 - 1);
00233                 /* extract the username before the '@' char */
00234                 if (cp2 - r > MAX_USER) {
00235                         T_ERR("parse_opts", "length of username must be less than %d", MAX_USER);
00236                         return -1;
00237                 }
00238                 strncpy(info.user, r, cp2 - r);
00239         }
00240 
00241         /* check if local path is a directory, if so append filename from remote path */
00242         if (info.local_path[strlen(info.local_path) - 1] == '/') {
00243                 cp = rindex(info.remote_path, '/');
00244                 strncpy(info.local_path + strlen(info.local_path), cp+1, MAX_PATH - strlen(info.local_path));
00245         }
00246 
00247         return 0;
00248 } /* parse_opts() */
00249 
00250 /**
00251  * Sets up the connect_skt for sending and receiving files by
00252  * doing all the socket api stuff
00253  */
00254 int rcp_connect() {
00255 
00256         struct sockaddr_in client_sa;
00257         struct sockaddr_in server_sa;
00258         char hostname_buf[256];
00259         char local_ip[INET_ADDRSTRLEN];
00260         char remote_ip[INET_ADDRSTRLEN];
00261         struct hostent* h;
00262         int ret;
00263 
00264         /* find out local hostname */
00265         if (gethostname(hostname_buf, 256) == -1) {
00266                 perror("gethostname");
00267                 close(fd);
00268                 return -1;
00269         }
00270         /* get the remote ip */
00271         if ((h = gethostbyname((const char*)(info.host))) == NULL) {
00272                 perror("gethostbyname");
00273                 close(fd);
00274                 return -1;
00275         }
00276         if (h->h_addr_list != 0) {
00277                 inet_ntop(h->h_addrtype, (h->h_addr_list)[0], remote_ip, sizeof(remote_ip));
00278         }
00279         /* get the local ip */
00280         if ((h = gethostbyname((const char*)(hostname_buf))) == NULL) {
00281                 perror("gethostbyname");
00282                 close(fd);
00283                 return -1;
00284         }
00285         if (h->h_addr_list != 0) {
00286                 inet_ntop(h->h_addrtype, (h->h_addr_list)[0], local_ip, sizeof(local_ip));
00287         }
00288 
00289         TEST_PRINT("rcp_connect","remote (%s : %s)", info.host, remote_ip);
00290         TEST_PRINT("rcp_connect","local  (%s : %s)", hostname_buf, local_ip);
00291 
00292         /* setup sockaddr structure for client */
00293         bzero((char*)(&client_sa), sizeof(client_sa));
00294         client_sa.sin_port = htons(0);
00295         client_sa.sin_family = AF_INET;
00296         if (inet_pton(AF_INET, local_ip, &client_sa.sin_addr) != 1) {
00297                 TEST_PRINT("rcp_connect","inet_pton failed");
00298                 close(fd);
00299                 return -1;
00300         }
00301         /* setup sockaddr structure for server */
00302         bzero((char*)(&server_sa), sizeof(server_sa));
00303         server_sa.sin_family = AF_INET;
00304         server_sa.sin_port = htons(info.port);
00305         if (inet_pton(AF_INET, remote_ip, &server_sa.sin_addr) != 1) {
00306                 TEST_PRINT("rcp_connect","inet_pton failed");
00307                 close(fd);
00308                 return -1;
00309         }
00310         /* create the socket structure */
00311         if ((connect_skt = vfer_socket (SOCK_DGRAM)) < 0) {
00312                 TEST_PRINT("rcp_connect","vfer_socket failed [%s]", vfer_errortext(connect_skt));
00313                 close(fd);
00314                 return -1;
00315         }
00316         /* bind the socket to a local addr */
00317         if (vfer_bind  (connect_skt, (struct sockaddr*) &client_sa, sizeof(client_sa)) < 0) {
00318                 TEST_PRINT("rcp_connect","vfer_bind failed [%s]", vfer_errortext(connect_skt));
00319                 close(fd);
00320                 return -1;
00321         }
00322 
00323         /* connect on the socket */
00324         if ((ret = vfer_connect (connect_skt, (SA*) (&server_sa), sizeof(server_sa))) < 0) {
00325                 TEST_PRINT("rcp_connect","vfer_connect failed [%s]", vfer_errortext(ret));
00326                 close(fd);
00327                 return -1;
00328         }
00329         return 0;
00330 } /* rcp_connect() */
00331 
00332 /**
00333  * This function is a catcher of SIGINT signal, installed in main()
00334  */
00335 void sig_handler(int sig) {
00336         exit(1);
00337 } /* sig_handler() */
00338 
00339 /**
00340  * main
00341  */
00342 int main (int argc, char **argv) {
00343         int ret;
00344         char* c;
00345         client_msg_t client_msg;
00346         server_msg_t server_msg;
00347         vfer_stats stats;
00348         FILE* testing_out;
00349         struct sigaction sa;
00350         connect_skt = -1;
00351 
00352         if (argc == 1) {
00353                 print_help(argv[0], 0);
00354                 return 0;
00355         }
00356 
00357         ret = parse_opts(argc, argv, &testing_out);
00358         TEST_INIT("VFER_RCP", "vfer_rcp.c", testing_out, stdout);
00359 
00360         if (ret == -2) {
00361                 print_help(argv[0], 1);
00362                 return 0;
00363         } else if (ret == -1) {
00364                 print_help(argv[0], 0);
00365                 return -1;
00366         }
00367 
00368         TEST_PRINT("main", "parsed: from_remote[%d] lpath[%s] uname[%s] host[%s] port[%d] rpath[%s]",
00369                    info.from_remote, info.local_path, info.user, info.host, info.port, info.remote_path);
00370 
00371         /* construct client_msg */
00372         strncpy(client_msg.remote_path, info.remote_path, MAX_PATH);
00373         strncpy(client_msg.user, info.user, MAX_USER);
00374         strncpy(client_msg.user, info.user, MAX_USER);
00375         client_msg.from_remote = info.from_remote;
00376 
00377         if ((c = rindex(info.local_path, '/')) == NULL) {
00378                 strncpy(client_msg.local_name, info.local_path, MAX_PATH);
00379         } else {
00380                 strncpy(client_msg.local_name, c+1, MAX_PATH);
00381         }
00382 
00383         /* if we are sending a local file, record and send the file's size to the server */
00384         if (info.from_remote == 1) {
00385                 if ((fd = open(info.local_path, O_RDONLY)) == -1) {
00386                         TEST_PRINT("main", "couldn't open local file [%s] for reading", info.local_path);
00387                         return -1;
00388                 }
00389                 client_msg.file_size = lseek(fd, 0, SEEK_END);
00390                 close(fd);
00391         } else {
00392                 client_msg.file_size = 0;
00393         }
00394 
00395         /* connect to rcpd server */
00396         if (rcp_connect() == -1) {
00397                 return -1;
00398         }
00399         TEST_PRINT("main","connected!");
00400 
00401         /* install a handler for SIGINT that will exit gracefully */
00402         sa.sa_handler = &(sig_handler);
00403         if (sigaction(SIGINT, &sa, NULL) == -1) {
00404                 TEST_PRINT("main", "couldn't install a signal handler for SIGINT");
00405         }
00406 
00407         /* send client msg to rcpd server */
00408         client_msg.file_size = htonl(client_msg.file_size);
00409         if ((ret = vfer_send(connect_skt, &client_msg, sizeof(client_msg_t))) < 0) {
00410                 TEST_PRINT("main", "send client msg failed [%s]", vfer_errortext(ret));
00411                 goto CLEAN_UP_NO_STAT;
00412         }
00413         client_msg.file_size = ntohl(client_msg.file_size);
00414 
00415         /* receive server msg from rcpd server */
00416         if ((ret = vfer_recv(connect_skt, &server_msg, sizeof(server_msg_t))) < 0) {
00417                 TEST_PRINT("main", "receive server msg failed [%s]", vfer_errortext(ret));
00418                 goto CLEAN_UP_NO_STAT;
00419         }
00420         server_msg.file_size    = ntohl(server_msg.file_size);
00421         server_msg.status       = ntohl(server_msg.status);
00422 
00423         TEST_PRINT("main", "received server msg with status[%u] file_size[%u]", server_msg.status, server_msg.file_size);
00424 
00425         switch (server_msg.status) {
00426         case RCP_READY:
00427                 if (info.from_remote == 0) {
00428                         /* receive remote file */
00429                         if ((fd = open(info.local_path, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP)) == -1) {
00430                                 perror("open for writing failed");
00431                                 goto CLEAN_UP_NO_STAT;
00432                         }
00433                         if ((ret = vfer_recvfile(connect_skt, fd, 0, server_msg.file_size)) < 0){
00434                                 TEST_PRINT("main","vfer_recvfile failed [%s]", vfer_errortext(ret));
00435                                 goto CLEAN_UP_STAT;
00436                         }
00437 
00438                 } else { /* info.from_remote == 1 */
00439                         /* send local file */
00440                         if ((fd = open(info.local_path, O_RDONLY, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP)) == -1) {
00441                                 perror("open for reading failed");
00442                                 goto CLEAN_UP_NO_STAT;
00443                         }
00444 
00445                         if ((ret = vfer_sendfile(connect_skt, fd, 0, client_msg.file_size)) < 0) {
00446                                 TEST_PRINT("main","vfer_sendfile failed [%s]", vfer_errortext(ret));
00447                                 goto CLEAN_UP_STAT;
00448                         }
00449                 }
00450                 goto CLEAN_UP_STAT;
00451         case RCP_ENOENT:
00452                 T_ERR("main", "component of the path does not exist on remote file system");
00453                 break;
00454         case RCP_EOPEN:
00455                 if (info.from_remote == 0) {
00456                         T_ERR("main", "can't open remote file for reading");
00457                 } else {
00458                         T_ERR("main", "can't open remote file for writing");
00459                 }
00460                 break;
00461         case RCP_ENOFILE:
00462                 T_ERR("main", "file doesn't exist on remote host");
00463                 break;
00464         case RCP_EUSER:
00465                 T_ERR("main", "wrong username specified for remote host");
00466                 break;
00467         case RCP_EOTHER:
00468                 T_ERR("main", "unspecified remote error");
00469                 break;
00470         default:
00471                 T_ERR("main", "could not interpret server msg code");
00472                 break;
00473         }
00474  CLEAN_UP_NO_STAT:
00475         vfer_close(connect_skt);
00476         close(fd);
00477         return 0;
00478  CLEAN_UP_STAT:
00479         vfer_close(connect_skt);
00480         stats = vfer_sockstats(connect_skt);
00481         connect_skt = -1;
00482         if (test_out != NULL)
00483             vfer_rcp_print_stats(&stats);
00484         close(fd);
00485         return 0;
00486 } /* main() */

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