Advertisement
cmiN

rms-server

Feb 13th, 2014
149
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C 9.27 KB | None | 0 0
  1. /*
  2.  * rms-server: Release Management System - server
  3.  *
  4.  * Author: cmin764@yahoo/gmail.com
  5.  */
  6.  
  7.  
  8. #include <stdio.h>
  9. #include <string.h>
  10. #include <ctype.h>
  11. #include <fcntl.h>
  12. #include <stdlib.h>
  13. #include <pthread.h>
  14. #include <unistd.h>
  15. #include <dirent.h>
  16. #include <sys/socket.h>
  17. #include <sys/types.h>
  18. #include <sys/stat.h>
  19. #include <sys/sendfile.h>
  20. #include <netinet/in.h>
  21. #include <arpa/inet.h>
  22.  
  23.  
  24. // default server settings
  25. #define CPATH "res/rms.conf"    // config path
  26. #define PEXT ".tar.gz"          // package extension
  27.  
  28. // other constants
  29. #define BLEN 128    // default buffer length
  30. #define OCOUNT 2    // number of options
  31. #define DEBUG 1     // print debugging messages
  32. #define ASYNC 1     // asynchronous processing
  33.  
  34.  
  35. typedef struct {
  36.     int port;            // custom server port
  37.     char ppath[BLEN];    // extensions path
  38. } options_t;
  39.  
  40.  
  41. typedef enum {
  42.     PORT,
  43.     PPATH
  44. } option_c;
  45.  
  46. typedef enum {
  47.     SEARCH,
  48.     DOWNLOAD
  49. } task_c;
  50.  
  51.  
  52. options_t options = {.port = 0, .ppath = ""};
  53.  
  54.  
  55. static inline int str2const(const char *key)
  56. {
  57.     static const char *choices[OCOUNT] = {"port", "ppath"};
  58.     for (int i = 0; i < OCOUNT; ++i)
  59.         if (!strcmp(choices[i], key))
  60.             return i;
  61.     return -1;
  62. }
  63.  
  64.  
  65. void parse_option(const char *key, const char *value)
  66. {
  67.     /// Interpret custom options.
  68.     int port;
  69.     switch (str2const(key)) {
  70.     case PORT:
  71.         sscanf(value, "%d", &port);
  72.         options.port = port;
  73.         break;
  74.     case PPATH:
  75.         strcpy(options.ppath, value);
  76.         break;
  77.     default:
  78.         fputs("Invalid option in config file\n", stderr);
  79.         exit(EXIT_FAILURE);
  80.     }
  81. }
  82.  
  83.  
  84. void load_config(const char *cpath)
  85. {
  86.     /// Load settings from the default or specifiend config file.
  87.  
  88.     // open the file
  89.     FILE *fin = fopen(cpath, "r");
  90.     if (!fin) {
  91.         perror("Couldn't load config file");
  92.         exit(EXIT_FAILURE);
  93.     }
  94.  
  95.     // parse line by line
  96.     while (!feof(fin)) {
  97.         // prepare the items
  98.         static char line[BLEN], key[BLEN], value[BLEN];
  99.         // read the line
  100.         void *ret = fgets(line, BLEN, fin);
  101.         if (!ret)
  102.             break;
  103.         // skip comments and blank lines
  104.         if (line[0] == '#' || isspace(line[0]))
  105.             continue;
  106.         // split into pairs and process them
  107.         sscanf(line, "%s %s", key, value);
  108.         parse_option(key, value);
  109.     }
  110.  
  111.     // close it
  112.     fclose(fin);
  113. }
  114.  
  115.  
  116. void get_packname(int clsfd, char **_packname)
  117. {
  118.     int packlen;
  119.     read(clsfd, &packlen, sizeof(packlen));
  120.     packlen = ntohl(packlen);
  121.     char *packname = calloc(packlen + 1, sizeof(char));
  122.     read(clsfd, packname, packlen);
  123.     *_packname = packname;
  124. }
  125.  
  126.  
  127. void search_package(const char *packname,
  128.                     char ***packvec, int *packcnt)
  129. {
  130.     /// Search for a pattern and return a list of packages.
  131.  
  132.     // package vector
  133.     char **_packvec = *packvec = NULL;
  134.     int _packcnt = *packcnt = 0;
  135.  
  136.     // list all files from packages path
  137.     DIR *dirp = opendir(options.ppath);
  138.     if (!dirp) {
  139.         perror("Couldn't open package directory");
  140.         return;
  141.     }
  142.     struct dirent *dirobj;
  143.  
  144.     while ((dirobj = readdir(dirp))) {
  145.         char fname[BLEN];    // current file name
  146.         strcpy(fname, dirobj->d_name);
  147.  
  148.         // some basic checks
  149.         if (!strcmp(fname, ".") ||
  150.             !strcmp(fname, ".."))
  151.             continue;    // skip current and parent directory
  152.         char *pchr = strstr(fname, PEXT);
  153.         if (!pchr)
  154.             continue;    // invalid package
  155.         fname[pchr - fname] = '\0';    // strip extension
  156.         if (!strstr(fname, packname))
  157.             continue;    // nothing found
  158.  
  159.         // package name found, save it
  160.         _packvec = realloc(_packvec, sizeof(char *) * ++_packcnt);
  161.         _packvec[_packcnt - 1] = malloc(sizeof(char) *
  162.                                         (strlen(fname) + 1));
  163.         strcpy(_packvec[_packcnt - 1], fname);
  164.     }
  165.  
  166.     *packvec = _packvec;
  167.     *packcnt = _packcnt;
  168. }
  169.  
  170.  
  171. void download_package(int clsfd, const char *packname)
  172. {
  173.     /// Retrieve and upload a package to the client.
  174.  
  175.     // build path to file
  176.     char fpath[BLEN];
  177.     strcat(
  178.         strcat(
  179.             strcat(
  180.                 strcpy(
  181.                     fpath,
  182.                     options.ppath
  183.                 ),
  184.                 "/"
  185.             ),
  186.             packname
  187.         ),
  188.         PEXT
  189.     );
  190.  
  191.     // get the size of that file
  192.     FILE *fin = fopen(fpath, "r");
  193.     if (!fin) {
  194.         perror("Package doesn't exist");
  195.         return;
  196.     }
  197.     fseek(fin, 0, SEEK_END);
  198.     int fsize = ftell(fin);
  199.     fclose(fin);
  200.  
  201.     #if DEBUG
  202.     printf("Sending file %s (%d bytes)...\n", fpath, fsize);
  203.     #endif
  204.  
  205.     // send the file
  206.     int _fsize = htonl(fsize);
  207.     write(clsfd, &_fsize, sizeof(_fsize));
  208.     int ifd = open(fpath, O_RDONLY | O_SYNC);
  209.     sendfile(clsfd, ifd, NULL, fsize);
  210.     close(ifd);
  211. }
  212.  
  213.  
  214. void handle_client(int clsfd)
  215. {
  216.     /// Communicate with this client and process received tasks.
  217.  
  218.     // receive the task
  219.     task_c task;
  220.     read(clsfd, &task, sizeof(task));
  221.     task = ntohl(task);
  222.     // process it
  223.     switch (task) {
  224.     case SEARCH:
  225.         {
  226.             // receive package name
  227.             char *packname;
  228.             get_packname(clsfd, &packname);
  229.             #if DEBUG
  230.             printf("Searching for package %s...\n", packname);
  231.             #endif
  232.             // search for that package
  233.             char **packvec;
  234.             int packcnt;
  235.             search_package(packname, &packvec, &packcnt);
  236.             // send the packages (if any)
  237.             #if DEBUG
  238.             printf("Found %d packages\n", packcnt);
  239.             #endif
  240.             int _packcnt = htonl(packcnt);
  241.             write(clsfd, &_packcnt, sizeof(_packcnt));
  242.             for (int i = 0; i < packcnt; ++i) {
  243.                 int slen = strlen(packvec[i]);
  244.                 int _slen = htonl(slen);
  245.                 write(clsfd, &_slen, sizeof(_slen));
  246.                 write(clsfd, packvec[i], slen);
  247.             }
  248.             // free resources
  249.             free(packname);
  250.             for (int i = 0; i < packcnt; ++i)
  251.                 free(packvec[i]);
  252.             free(packvec);
  253.         }
  254.         break;
  255.     case DOWNLOAD:
  256.         {
  257.             char *packname;
  258.             get_packname(clsfd, &packname);
  259.             #if DEBUG
  260.             printf("Uploading package %s...\n", packname);
  261.             #endif
  262.             download_package(clsfd, packname);
  263.         }
  264.         break;
  265.     default:
  266.         fputs("Received invalid task type\n", stderr);
  267.     }
  268.  
  269.     close(clsfd);
  270. }
  271.  
  272.  
  273. void *threaded_handle_client(void *clsfd)
  274. {
  275.     handle_client((int)clsfd);
  276.     pthread_exit(NULL);
  277. }
  278.  
  279.  
  280. void serve()
  281. {
  282.     /// Server core.
  283.  
  284.     // create the socket
  285.     int svsfd = socket(AF_INET, SOCK_STREAM, 0);
  286.     if (svsfd == -1) {
  287.         perror("Couldn't create server socket");
  288.         exit(EXIT_FAILURE);
  289.     }
  290.     // reuse address
  291.     int op = 1;
  292.     if (setsockopt(svsfd, SOL_SOCKET, SO_REUSEADDR, &op, sizeof(op))) {
  293.         perror("Couldn't set socket option");
  294.         exit(EXIT_FAILURE);
  295.     }
  296.  
  297.     // configure it
  298.     struct sockaddr_in svaddr;
  299.     memset(&svaddr, 0, sizeof(svaddr));
  300.     svaddr.sin_family = AF_INET;
  301.     svaddr.sin_addr.s_addr = htonl(INADDR_ANY);
  302.     svaddr.sin_port = htons(options.port);
  303.  
  304.     // bind localhost with the desired port
  305.     if (bind(svsfd, (struct sockaddr *) &svaddr, sizeof(svaddr)) == -1) {
  306.         perror("Couldn't bind server socket address");
  307.         exit(EXIT_FAILURE);
  308.     }
  309.     // listening queue
  310.     if (listen(svsfd, 5) == -1) {
  311.         perror("Couldn't listen for clients");
  312.         exit(EXIT_FAILURE);
  313.     }
  314.  
  315.     // client socket data
  316.     struct sockaddr_in claddr;
  317.     memset(&claddr, 0, sizeof(claddr));
  318.     socklen_t clslen = sizeof(claddr);
  319.  
  320.     // accept clients, then process the work in a separate thread
  321.     #if DEBUG
  322.     puts("Waiting for clients...");
  323.     #endif
  324.     while (1) {
  325.         int clsfd = accept(svsfd, (struct sockaddr *) &claddr, &clslen);
  326.         if (clsfd == -1) {
  327.             perror("Couldn't accept the client, skip");
  328.             continue;
  329.         }
  330.         #if DEBUG
  331.         printf("Accepted: %d.%d.%d.%d:%d\n",
  332.                (int)(claddr.sin_addr.s_addr & 0xFF),
  333.                (int)((claddr.sin_addr.s_addr & 0xFF00) >> 8),
  334.                (int)((claddr.sin_addr.s_addr & 0xFF0000) >> 16),
  335.                (int)((claddr.sin_addr.s_addr & 0xFF000000) >> 24),
  336.                ntohs(claddr.sin_port));
  337.         #endif
  338.         #if ASYNC
  339.         pthread_t thread;
  340.         pthread_create(&thread, NULL,
  341.                        threaded_handle_client, (void *)clsfd);
  342.         #else
  343.         handle_client(clsfd);
  344.         #endif
  345.     }
  346. }
  347.  
  348.  
  349. int main(int argc, const char *argv[])
  350. {
  351.     // parse optional positional arguments
  352.     if (argc > 2) {
  353.         puts("Release Management System - server");
  354.         printf("Usage: %s [CONFIG]\n", argv[0]);
  355.         return 1;
  356.     }
  357.  
  358.     // load config file
  359.     char cpath[BLEN];
  360.     if (argc == 2)
  361.         strcpy(cpath, argv[1]);
  362.     else
  363.         strcpy(cpath, CPATH);
  364.     load_config(cpath);
  365.  
  366.     // start the server
  367.     serve();
  368.  
  369.     return 0;
  370. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement