src/vfer_rcpd.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_rcpd.c
00008  * @author Ivan Beschastnikh
00009  * @brief  A server program that accepts one connection from vfer_rcp and receives or sends a file
00010  *
00011  * This program implements reliable transfer of data over the vfer protocol.
00012  *
00013  * -     05/06/06        ivan            added -vv option
00014  * -     04/30/06        ivan            made to use vfer_recvfile and vfer_sendfile
00015  * -     04/12/06        ivan            Added call to vfer_rcp_print_stats() at the end of each transmission and on exit
00016  * -     03/21/06        ivan            Added daemonizing support and rehashed usage to include start|stop
00017  * -     02/18/06        ivan            Changed name to vfer_rcpd.c. Added SIGINT handler to exit racefully, added
00018  *                                       support for daemonization via daemon() syscall - only on BSD and Linux
00019  * -     01/15/05        ivan            Branched from test_sndfile.c
00020  */
00021 
00022 #include "vfer_rcp.h"
00023 
00024 /**
00025  * @brief info structure used for parsing the command line arguments and options of test_rcpd
00026  */
00027 typedef struct rcpd_info {
00028         char    user            [MAX_USER];
00029         char    home_dir        [MAX_PATH];
00030         int     frame_size;     /* frame size to use when sending files */
00031         int     port;           /* port to connect on */
00032         char    daemon;         /* 0: no , 1: yes */
00033 } rcpd_info;
00034 
00035 extern char *optarg;
00036 extern int optind;
00037 extern int optopt;
00038 extern int opterr;
00039 
00040 void    print_help      (char* prog, int verbose);
00041 int     parse_opts      (int argc, char** argv, FILE** testing_out);
00042 int     rcpd_connect    ();
00043 
00044 /* global vars */
00045 vfer_fd connect_skt;            /* vfer_socket structure for communicating with the vfer library */
00046 vfer_fd listen_skt;             /* vfer_socket structure for listening */
00047 rcpd_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]] [-d] [-P port] start|stop\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-d        start server as a daemon process        [optional]\n");
00064         printf("\t-P        specifies port on which to listen       [optional]\n");
00065         printf("\tstart     starts the server                       [start|stop required]\n");
00066         printf("\tstop      stops all currently running servers     [start|stop required]\n");
00067         printf("notes:\n");
00068         printf("\t- port is defaulted to 2046\n");
00069         printf("\t- username matching is the only authentication mechanism\n");
00070         printf("\t- remote->remote copying is not implemented\n");
00071         printf("\t- encryption 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 stdout
00079  *
00080  * @param argc is the argc argument to main
00081  * @param argv is the argv argument to main
00082  * @param testing_out is a FILE** type that is either set to NULL here or to 'stdout' depending on verbosity args
00083  * @return
00084  *      1 on success [stop service]
00085  *      0 on success [start service]
00086  *      -1 on error
00087  *      -2 if help option was specified
00088  */
00089 int parse_opts(int argc, char** argv, FILE** testing_out) {
00090         int c;
00091         struct passwd *pwentry; /* for getting current username */
00092         int uid;
00093         int ret;
00094 
00095         opterr=0;               /* suppress error printing by getopt() */
00096         bzero(info.user,MAX_USER);
00097         bzero(info.home_dir,MAX_PATH);
00098         info.frame_size = MAX_FRAME_SIZE;
00099         info.port = DEFAULT_PORT;
00100         info.daemon = 0;
00101         *testing_out = NULL;
00102         while (1)
00103         {
00104                 c = getopt (argc, argv, ":hvdP:");
00105                 if (c == -1) break;
00106                 switch (c) {
00107                 case 'h':
00108                         return -2;
00109                         break;
00110                 case 'P':
00111                         info.port = atoi(optarg);
00112                         break;
00113                 case 'd':
00114                         info.daemon = 1;
00115                         break;
00116                 case 'v':       /* allow for -vv for higher verbosity */
00117                         if (*testing_out == NULL) {
00118                                 /* no api output but VFER_RCPD output */
00119                                 *testing_out = stdout;
00120                                 vfer_debug(stdout, stdout, "a");
00121                         } else {
00122                                 vfer_debug(stdout, stdout, "aCcP");
00123                         }
00124                         break;
00125                 case ':':
00126                         T_ERR("parse_opts", "option %c missing value", optopt);
00127                         return -1;
00128                 case '?':
00129                         T_ERR("parse_opts", "unknown option: %c", optopt);
00130                         return -1;
00131                 default:
00132                         T_ERR("parse_opts", "getopt returned character code 0%o", c);
00133                         return -1;
00134                 }
00135         }
00136         if (argc - optind != 1) {
00137                 T_ERR("parse_opts", "exactly one non-option arg required");
00138                 return -1;
00139         }
00140 
00141         /* check whether the user wants to start vferd or to start it */
00142         if (strlen(argv[optind]) == 5 && strncmp(argv[optind], "start", 5) == 0) {
00143                 ret = 0;
00144         } else if (strlen(argv[optind]) == 4 && strncmp(argv[optind], "stop", 4) == 0) {
00145                 ret = 1;
00146         } else {
00147                 T_ERR("parse_opts", "non-option arg must be start/stop");
00148                 return -1;
00149         }
00150 
00151         /* find out and cache the username running this server */
00152         uid=getuid();
00153         if ((pwentry = getpwuid(uid)) == NULL) {
00154                 T_ERR("parse_opts", "failed getting password entry for uid(%d)", uid);
00155                 return -1;
00156         }
00157         if (strlen(pwentry->pw_name) > MAX_USER) {
00158                 T_ERR("parse_opts", "length of username must be less than %d", MAX_USER);
00159                 return -1;
00160         }
00161         if (strlen(pwentry->pw_dir) > MAX_PATH) {
00162                 T_ERR("parse_opts", "length of home dir must be less than %d", MAX_PATH);
00163                 return -1;
00164         }
00165 
00166         strncpy(info.home_dir, pwentry->pw_dir, MAX_PATH);
00167         strncpy(info.user, pwentry->pw_name, MAX_USER);
00168 
00169         /* ret is either 0 or 1, set above when checking the non-optional argument */
00170         return ret;
00171 } /* parse_opts() */
00172 
00173 /**
00174  * This function makes preliminary api calls to vfer lib to create a
00175  * listening socket
00176  *
00177  * @return -1 on error
00178  * @return 0 on success
00179  */
00180 int rcpd_connect() {
00181 
00182         struct sockaddr_in sa;
00183         int ret;
00184 
00185         bzero((char*)(&sa), sizeof(sa));
00186         sa.sin_family = AF_INET;
00187         sa.sin_addr.s_addr = htonl(INADDR_ANY);
00188         sa.sin_port = htons(info.port);
00189 
00190         if ((listen_skt = vfer_socket (SOCK_DGRAM)) < 0) {
00191                 TEST_PRINT("rcpd_connect", "vfer_socket failed [%s]", vfer_errortext(listen_skt));
00192                 close(fd);
00193                 return -1;
00194         }
00195 
00196         if ((ret = vfer_bind  (listen_skt, (struct sockaddr*) &sa, sizeof(sa))) < 0) {
00197                 TEST_PRINT("rcpd_connect", "vfer_bind failed [%s]", vfer_errortext(ret));
00198                 close(fd);
00199                 return -1;
00200         }
00201         if ((ret = vfer_listen (listen_skt, 3)) < 0) {
00202                 TEST_PRINT("rcpd_connect", "vfer_listen failed [%s]", vfer_errortext(ret));
00203                 close(fd);
00204                 return -1;
00205         }
00206         return 0;
00207 } /* rcpd_connect() */
00208 
00209 /**
00210  * This function is a catcher of SIGINT signal, installed in main()
00211  */
00212 void sig_handler(int sig) {
00213         exit(1);
00214 } /* sig_handler() */
00215 
00216 /**
00217  * main
00218  */
00219 int main (int argc, char** argv) {
00220         int ret;
00221         client_msg_t client_msg;
00222         server_msg_t server_msg;
00223         struct sockaddr_in sa_accepted;
00224         socklen_t accepted_len;
00225         char buf[INET_ADDRSTRLEN];
00226         struct stat stat_buf;
00227         char path[MAX_PATH * 2];
00228         struct sigaction sa;
00229         int pid;
00230         char fname[16];
00231         FILE* file;
00232         FILE* testing_out;
00233         DIR* dir;
00234         struct dirent* dir_e;
00235         vfer_stats stats;
00236         connect_skt = -1;
00237 
00238         ret = parse_opts(argc, argv, &testing_out);
00239         TEST_INIT("VFER_RCPD", "vfer_rcpd.c", testing_out, stdout);
00240 
00241         if (ret == -2) {
00242                 print_help(argv[0], 1);
00243                 return 0;
00244         } else if (ret == -1) {
00245                 print_help(argv[0], 0);
00246                 return -1;
00247         } else if (ret == 1) {
00248                 TEST_PRINT("main", "stopping all running vferd");
00249                 dir = opendir("/tmp/");
00250                 while ((dir_e = readdir(dir))) {
00251                         if (strncmp(dir_e->d_name, "vferd_", 6) == 0) {
00252                                 pid = atoi(dir_e->d_name + 6);
00253                                 printf("stopping vferd[%d]\n", pid);
00254                                 if (kill((pid_t)(pid), SIGKILL) == -1) {
00255                                         T_ERR("main", "SIGKILL failed on vferd with pid[%d]", pid);
00256                                 } else {
00257                                         TEST_PRINT("main", "stopped vferd with pid [%d]", pid);
00258                                 }
00259                                 sprintf(fname, "/tmp/%s", dir_e->d_name);
00260                                 unlink(fname);
00261                         }
00262                 }
00263                 closedir(dir);
00264                 return 0;
00265         } else if (ret != 0) {
00266                 T_ERR("main", "parse_opts returned bad value, quitting");
00267                 return -1;
00268         }
00269 
00270         /* else => ret == 0 */
00271         /* become a daemon if -d was specified on the cmd line */
00272         if (info.daemon == 1) {
00273                 TEST_PRINT("main", "daemonizing ..");
00274                 fflush(stdout);
00275                 if (daemon(1,0) == -1) {
00276                         TEST_PRINT("main", "couldn't start as daemon, daemon() call failed");
00277                 }
00278         }
00279 
00280         TEST_PRINT("main", "starting vferd");
00281         pid = (int) getpid();
00282         sprintf(fname, "/tmp/vferd_%d", pid);
00283         file = fopen(fname,"w");
00284         fclose(file);
00285 
00286         /* install a handler for SIGINT that will exit gracefully */
00287         sa.sa_handler = &(sig_handler);
00288         if (sigaction(SIGINT, &sa, NULL) == -1) {
00289                 TEST_PRINT("main", "couldn't install a signal handler for SIGINT");
00290         }
00291 
00292         /* connect to the listening port */
00293         if (rcpd_connect() == -1) {
00294                 return -1;
00295         }
00296 
00297         TEST_PRINT("main", "started with authenticating username: %s, home_dir: %s", info.user, info.home_dir);
00298 
00299         while (1) {
00300         retry_accept:
00301                 if ((connect_skt = vfer_accept(listen_skt, (SA*)(&sa_accepted), &accepted_len)) < 0) {
00302                         if (connect_skt == VFER_TIMEOUT) {
00303                                 TEST_PRINT("main", "vfer_accept timedout, retrying");
00304                                 goto retry_accept;
00305                         }
00306                         TEST_PRINT("main", "vfer_accept failed [%s]", vfer_errortext(connect_skt));
00307                         vfer_close(listen_skt);
00308                         return 1;
00309                 }
00310 
00311                 TEST_PRINT("main", "vfer_accept successfull new connection: [%s : %d]",
00312                            inet_ntop(AF_INET, &(sa_accepted.sin_addr),buf, INET_ADDRSTRLEN),
00313                            ntohs(sa_accepted.sin_port));
00314 
00315                 /* receive client msg */
00316                 if ((ret = vfer_recv(connect_skt, &client_msg, sizeof(client_msg_t))) < 0) {
00317                         TEST_PRINT("main", "failed to send server msg to client [%s]", vfer_errortext(ret));
00318                         goto CLEAN_UP;
00319                 }
00320                 client_msg.file_size = ntohl(client_msg.file_size);
00321 
00322                 TEST_PRINT("main", "received: rpath[%s] local_uname[%s] recvd_uname[%s] from_remote[%d] file_size[%u]",
00323                            client_msg.remote_path, client_msg.local_name, client_msg.user, client_msg.from_remote, client_msg.file_size);
00324 
00325                 server_msg.file_size = 0;
00326                 server_msg.file_size = htonl(server_msg.file_size);
00327 
00328                 /* verify user name */
00329                 if (strncmp(info.user, client_msg.user, MAX_USER) != 0) {
00330                         TEST_PRINT("main", "remote connection gave bad username[%s]", client_msg.user);
00331                         server_msg.status = RCP_EUSER;
00332                         server_msg.status = htonl(server_msg.status);
00333                         if ((ret = vfer_send(connect_skt, &server_msg, sizeof(server_msg_t))) < 0) {
00334                                 TEST_PRINT("main", "failed to send server msg to client [%s]", vfer_errortext(ret));
00335                         }
00336                         goto CLEAN_UP;
00337                 }
00338 
00339                 /* expand out the home directory truncation */
00340                 if (client_msg.remote_path[0] == '~') {
00341                         strncpy(path, info.home_dir, MAX_PATH);
00342                         strncpy(path + strlen(path), client_msg.remote_path+1, MAX_PATH);
00343                 } else {
00344                         strncpy(path, client_msg.remote_path, MAX_PATH);
00345                 }
00346 
00347                 TEST_PRINT("main", "path expanded to: %s", path);
00348                 if (client_msg.from_remote == 0) {
00349                         /* send local file to client */
00350                         if ((fd = open(path, O_RDONLY, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP)) == -1) {
00351                                 TEST_PRINT("main", "open for reading failed");
00352                                 perror("open for reading failed");
00353                                 /* send server msg to client */
00354                                 server_msg.status       = RCP_EOPEN;
00355                                 server_msg.status       = htonl(server_msg.status);
00356                                 if ((ret = vfer_send(connect_skt, &server_msg, sizeof(server_msg_t))) < 0) {
00357                                         TEST_PRINT("main", "failed to send server msg to client [%s]", vfer_errortext(ret));
00358                                         goto CLEAN_UP;
00359                                 }
00360                         } else {
00361                                 /* send server msg to client */
00362                                 server_msg.status       = RCP_READY;
00363                                 server_msg.file_size    = lseek(fd, 0, SEEK_END);
00364                                 server_msg.file_size    = htonl(server_msg.file_size);
00365                                 server_msg.status       = htonl(server_msg.status);
00366                                 if ((ret = vfer_send(connect_skt, &server_msg, sizeof(server_msg_t))) < 0) {
00367                                         TEST_PRINT("main", "failed to send server msg to client [%s]", vfer_errortext(ret));
00368                                         goto CLEAN_UP;
00369                                 }
00370                                 server_msg.file_size    = ntohl(server_msg.file_size);
00371                                 if ((ret = vfer_sendfile(connect_skt, fd, 0, server_msg.file_size)) < 0) {
00372                                         TEST_PRINT("main", "vfer_sendfile failed [%s]", vfer_errortext(ret));
00373                                         goto CLEAN_UP;
00374                                 }
00375                         }
00376                 } else { /* client_msg.from_remote == 1 */
00377                         /* receive file from client */
00378 
00379                         /* check if path ends if '/' */
00380                         if (path[strlen(path) - 1] == '/') {
00381                                 /* append local_name to path and continue */
00382                                 strncpy(path + strlen(path), client_msg.local_name, 2*MAX_PATH - strlen(path));
00383                         } else {
00384                                 /* check if the path is an existing directory */
00385                                 if (stat(path, &stat_buf) == -1) {
00386                                         server_msg.status = RCP_EOTHER;
00387                                         switch (errno) {
00388                                         case ENOENT:
00389                                                 /* file that does not exist, great, continue */
00390                                                 TEST_PRINT("main", "writing to a new file");
00391                                                 break;
00392                                         case ENOTDIR: /* bad path (one of directories doesn't exist, bail */
00393                                                 TEST_PRINT("main", "bad path from client");
00394                                                 server_msg.status = RCP_ENOENT;
00395                                         default: /* something is wrong, bail */
00396                                                 TEST_PRINT("main", "stat failed");
00397                                                 /* send server msg to client */
00398                                                 server_msg.status = htonl(server_msg.status);
00399                                                 if ((ret = vfer_send(connect_skt, &server_msg, sizeof(server_msg_t))) < 0) {
00400                                                         TEST_PRINT("main", "failed to send server msg to client [%s]", vfer_errortext(ret));
00401                                                         goto CLEAN_UP;
00402                                                 }
00403                                         }
00404                                 } else {
00405                                         /* check if path is an existing directory */
00406                                         if (stat_buf.st_mode & S_IFDIR) {
00407                                                 /* yes- append local_name to path and continue */
00408                                                 path[strlen(path)] = '/';
00409                                                 path[strlen(path)+1] = 0;
00410                                                 strncpy(path + strlen(path), client_msg.local_name, 256 - strlen(path));
00411                                         }
00412                                 }
00413                         }
00414 
00415                         if ((fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP)) == -1) {
00416                                 TEST_PRINT("main", "open for writing failed");
00417                                 perror("open for writing failed");
00418                                 /* send server msg to client */
00419                                 server_msg.status = RCP_EOPEN;
00420                                 server_msg.status = htonl(server_msg.status);
00421                                 if ((ret = vfer_send(connect_skt, &server_msg, sizeof(server_msg_t))) < 0) {
00422                                         TEST_PRINT("main", "failed to send server msg to client [%s]", vfer_errortext(ret));
00423                                         goto CLEAN_UP;
00424                                 }
00425                         } else {
00426                                 /* send server msg to client */
00427                                 server_msg.status = RCP_READY;
00428                                 server_msg.status = htonl(server_msg.status);
00429                                 if ((ret = vfer_send(connect_skt, &server_msg, sizeof(server_msg_t))) < 0) {
00430                                         TEST_PRINT("main", "failed to send server msg to client [%s]", vfer_errortext(ret));
00431                                         goto CLEAN_UP;
00432                                 }
00433                                 if ((ret = vfer_recvfile(connect_skt, fd, 0, client_msg.file_size)) < 0){
00434                                         TEST_PRINT("main","vfer_recvfile failed [%s]", vfer_errortext(ret));
00435                                         goto CLEAN_UP;
00436                                 }
00437                         }
00438                 }
00439         CLEAN_UP:
00440                 TEST_PRINT("main", "Closing the accepted socket");
00441                 vfer_close(connect_skt);
00442                 stats = vfer_sockstats(connect_skt);
00443                 connect_skt = -1;
00444                 vfer_rcp_print_stats(&stats);   /* vfer_rcp_print_stats() is defined in vfer_rcp.h */
00445                 close(fd);
00446         }
00447 
00448         return 0;
00449 } /* main() */

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