gs_agent_httpd.c

Go to the documentation of this file.
00001 /* 
00002  * @file
00003  *
00004  * This is a rudimentary http server to provide information about
00005  * the status of the system through web browsers.
00006  */
00007 /* $Id: gs_agent_httpd.c,v 1.29 2008/12/02 19:39:37 seymour Exp $ */
00008 /* $UTK_Copyright: $ */
00009 
00010 #include "gs_agent_httpd.h"
00011 
00012 extern char GRIDSOLVE_STATS_FILE[];
00013 
00014 /* mapping of file extensions to the appropriate content types */
00015 char *gs_content_types[][2] = {
00016   {"xsl", "application/xml"},
00017   {"html", "text/html"},
00018   {"xml", "application/xml"},
00019   {"ico", "image/x-ico"},
00020   {"avi", "video/avi"},
00021   {"md5", "text/md5"},
00022   {"srt", "text/srt"},
00023   {NULL, "text/html"}
00024 };
00025 
00026 gs_agent_t *gs_httpd_agent_ptr = NULL;
00027 
00034 static void
00035 gs_httpd_generic_signal_handler(int sig)
00036 {
00037   /* pass along SIGHUP to parent */
00038   if(sig == SIGHUP) {
00039     kill(getppid(), SIGHUP);
00040     return;
00041   }
00042 
00043   ERRPRINTF("HTTP server terminating on signal %d.\n", sig);
00044   _exit(0);
00045 }
00046 
00053 void
00054 gs_agent_exit(void **args)
00055 {
00056   _exit(0);
00057 }
00058 
00066 void 
00067 gs_agent_httpd(void **args)
00068 {
00069   char *gridsolve_root, *www_home_path;
00070   struct sockaddr_in servaddr;
00071   int listenfd;
00072   socklen_t dummyLen;
00073   void sig_chld(int);
00074   short port;
00075 
00076   if(!args || !args[0] || !args[1]) {
00077     ERRPRINTF("Bad args\n");
00078     _exit(EXIT_FAILURE);
00079   }
00080 
00081   /* handle all signals except SIGINT.  if the agent is running in
00082    * console mode, we only want the parent to get SIGINT, so that all
00083    * the children will realize the parent is dead via mfork_check_parent()
00084    * and cleanly terminate.  having all processes catch SIGINT causes
00085    * mfork to try to restart them, depending on whether they catch it
00086    * before or after the parent.
00087    */
00088   gs_setup_signal_handlers(gs_httpd_generic_signal_handler);
00089   signal(SIGINT, SIG_IGN);
00090 
00091   port = *((int *)args[0]);
00092   gs_httpd_agent_ptr = (gs_agent_t *)args[1];
00093 
00094   if((listenfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
00095     perror("Failed to create socket");
00096     _exit(EXIT_FAILURE);
00097   }
00098 
00099   if((gridsolve_root = getenv("GRIDSOLVE_ROOT")) == NULL)
00100     gridsolve_root = GRIDSOLVE_TOP_BUILD_DIR;
00101 
00102   if(!gridsolve_root) {
00103     ERRPRINTF("Warning: GRIDSOLVE_ROOT unknown, assuming cwd.\n");
00104     gridsolve_root = strdup(".");
00105   }
00106 
00107   www_home_path = (char *)malloc(strlen(gridsolve_root) + 
00108       strlen(GS_AGENT_WWW_DIR) + 2);
00109   if(!www_home_path) {
00110     ERRPRINTF("malloc failed\n");
00111     _exit(EXIT_FAILURE);
00112   }
00113 
00114   sprintf(www_home_path, "%s/%s", gridsolve_root, GS_AGENT_WWW_DIR);
00115 
00116   if(chdir(www_home_path) < 0) {
00117     ERRPRINTF("Failed to chdir to '%s'\n", www_home_path);
00118     _exit(EXIT_FAILURE);
00119   }
00120 
00121   free(www_home_path);
00122 
00123   memset(&servaddr, 0x0, sizeof(servaddr));
00124   servaddr.sin_family = AF_INET;
00125   servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
00126   servaddr.sin_port = htons(port);
00127 
00128   if(bind(listenfd, (struct sockaddr *) &servaddr, sizeof(servaddr)) < 0) {
00129     perror("Failed to bind");
00130     _exit(EXIT_FAILURE);
00131   }
00132 
00133   if(listen(listenfd, 20) < 0) {
00134     perror("Listen failed");
00135     _exit(EXIT_FAILURE);
00136   }
00137 
00138   dummyLen = sizeof(struct sockaddr);
00139   if(getsockname(listenfd, (struct sockaddr *)(&servaddr), &dummyLen) < 0)
00140     LOGPRINTF("HTTP server listening on some unknown port\n");
00141   else
00142     LOGPRINTF("HTTP server listening on port %d\n", ntohs(servaddr.sin_port));
00143 
00144   if(gs_signal(SIGCHLD, sig_chld) == SIG_ERR) {
00145     ERRPRINTF("Could not set up SIGCHLD signal handler\n");
00146     _exit(EXIT_FAILURE);
00147   }
00148 
00149   gs_agent_httpd_listen_and_process_connections(listenfd);
00150   _exit(EXIT_SUCCESS);
00151 }
00152 
00161 int
00162 gs_agent_httpd_listen_and_process_connections(int listenfd)
00163 {
00164   struct sockaddr_in cliaddr;
00165   fd_set rset, allset;
00166   socklen_t clilen;
00167   pid_t childpid;
00168   struct timeval tv;
00169   int nready, connfd;
00170 
00171   clilen = sizeof(cliaddr);
00172   FD_ZERO(&allset);
00173   FD_SET(listenfd, &allset);
00174 
00175   for(;;) {
00176     rset = allset;
00177     tv.tv_sec = 1;
00178     tv.tv_usec = 0;
00179 
00180     nready = select(listenfd + 1, &rset, NULL, NULL, &tv);
00181     
00182     if((nready < 0) && (errno == EINTR))
00183       continue;
00184 
00185     if(nready < 0) {
00186       ERRPRINTF("select failed.. aborting.\n");
00187       break;
00188     }
00189 
00190     if(nready == 0) {
00191       if(!mfork_check_parent()) {
00192         ERRPRINTF("Parent died, so I am exiting\n");
00193         break;
00194       }
00195       continue;
00196     }
00197 
00198     if(FD_ISSET(listenfd, &rset)) {
00199       if ((connfd = accept(listenfd, (struct sockaddr *) &cliaddr, &clilen)) < 0) {
00200         if (errno == EINTR)
00201           continue;
00202         else {
00203           perror("accept error");
00204           _exit(-1);
00205         }
00206       }
00207 
00208       if ((childpid = fork()) == 0) {   /* child process */
00209         close(listenfd);
00210         if(gs_agent_httpd_handle_connection(connfd) < 0) {
00211           ERRPRINTF("Failed to handle connection.\n");
00212           _exit(-1);
00213         }
00214         close(connfd);
00215         _exit(0);
00216       }
00217       close(connfd);  /* parent closes connected socket */
00218     }
00219   }
00220 
00221   return 0;
00222 }
00223 
00230 void 
00231 sig_chld(int signo)
00232 {
00233   pid_t pid;
00234   int stat;
00235 
00236   while ((pid = waitpid(-1, &stat, WNOHANG)) > 0)
00237     DBGPRINTF("child %d terminated\n", (int)pid);
00238 
00239   return;
00240 }
00241 
00252 const char *
00253 gs_get_content_type(const char *filename)
00254 {   
00255   const char *end;
00256   int i;
00257 
00258   if(filename && (strlen(filename) > 0)) {
00259     end = filename + strlen(filename) - 1;
00260 
00261     while(end != filename) {
00262       if(*end == '.') {
00263         end++;
00264         break;
00265       }
00266       end--;
00267     }
00268 
00269     if(end == filename)
00270       return GS_DEFAULT_CONTENT_TYPE;
00271 
00272     for(i=0; gs_content_types[i][0]; i++)
00273       if(!strcmp(end, gs_content_types[i][0]))
00274         return(gs_content_types[i][1]);
00275   }
00276 
00277   return GS_DEFAULT_CONTENT_TYPE;
00278 }
00279 
00287 void
00288 unescape_url(char *str)
00289 {
00290   char tmp[3] = {0, 0, 0}; 
00291   int unescaped_char;
00292   int i, idx;
00293 
00294   if(!str) return;
00295 
00296   idx = 0;
00297 
00298   for(i=0;i<strlen(str);i++) {
00299     memset(tmp, 0, 3);
00300 
00301     if(str[i] == '%') {
00302       if(str[i+1] != 0) {
00303         tmp[0] = str[i+1];
00304 
00305         if(str[i+2] != 0) {
00306           tmp[1] = str[i+2];
00307           i += 2;
00308         }
00309         else
00310           i++;
00311       }
00312 
00313       sscanf(tmp, "%x", &unescaped_char);
00314     }
00315     else
00316       unescaped_char = str[i];
00317 
00318     str[idx++] = unescaped_char;
00319   }
00320 
00321   str[idx] = 0;
00322 }
00323 
00332 int 
00333 gs_agent_httpd_handle_connection(int sockfd)
00334 {
00335   ssize_t n;
00336   char *cmd, *fn, line[MAXLINE];
00337   
00338   if((n = gs_readline(sockfd, line, MAXLINE)) > 0) {
00339     cmd = strtok(line, " ");
00340     fn = strtok(NULL, " ");
00341 
00342     if(!strcmp(cmd, "GET")) {
00343       int rv;
00344 
00345       LOGPRINTF("HTTP GET %s\n", fn);
00346 
00347       gs_read_until_crlf(sockfd);
00348 
00349       /* ignore idiots */
00350       if(!strncmp(fn, "http://", 7))
00351        return 0;
00352 
00353       fn++;   /* skip '/' */
00354 
00355       unescape_url(fn);
00356 
00357       if(*fn == '\0' || strchr(fn, '?')) {
00358         if(gs_httpd_connect_db(sockfd) < 0)
00359           return -1;
00360 
00361         if(*fn == '\0' || !strcmp(fn, "status?"))
00362           rv = gs_httpd_process_status(sockfd);
00363         else if(!strcmp(fn, "problemlist?"))
00364           rv = gs_httpd_process_problem_info(sockfd, NULL);
00365         else if(!strncmp(fn, "serverinfo?", strlen("serverinfo?")))
00366           rv = gs_httpd_process_server_info(sockfd, fn);
00367         else if(!strncmp(fn, "probleminfo?", strlen("probleminfo?")))
00368           rv = gs_httpd_process_problem_info(sockfd, fn);
00369         else if(!strncmp(fn, "tasklist?", strlen("tasklist?")))
00370           rv = gs_httpd_process_task_list(sockfd);
00371         else
00372           rv = gs_httpd_get_file(sockfd, fn);
00373 
00374         /* Disconnect from database manager */
00375         gs_storage_finalize(NULL);
00376       }
00377       else
00378         rv = gs_httpd_get_file(sockfd, fn);
00379 
00380       return rv;
00381     }
00382     else if(!strcmp(cmd, "HEAD")) {
00383       int rv;
00384 
00385       LOGPRINTF("HTTP HEAD %s\n", fn);
00386 
00387       gs_read_until_crlf(sockfd);
00388 
00389       /* ignore idiots */
00390       if(!strncmp(fn, "http://", 7))
00391        return 0;
00392 
00393       fn++;   /* skip '/' */
00394 
00395       rv = gs_httpd_head_file(sockfd, fn);
00396 
00397       return rv;
00398     }
00399   }
00400 
00401   gs_httpd_preamble(sockfd, 404, GS_DEFAULT_CONTENT_TYPE, 0);
00402   gs_httpd_reply(sockfd, GS_RESPONSE_404);
00403 
00404   return -1;
00405 }
00406 
00416 int
00417 gs_httpd_head_file(int sockfd, const char *filename)
00418 {
00419   struct stat stbuf;
00420   const char *mime_type;
00421 
00422   if(!filename) {
00423     ERRPRINTF("Invalid filename, ptr is null.\n");
00424     gs_httpd_preamble(sockfd, 404, GS_DEFAULT_CONTENT_TYPE, 0);
00425     gs_httpd_reply(sockfd, GS_RESPONSE_404);
00426     return -1;
00427   }
00428 
00429   if(stat(filename, &stbuf) < 0) {
00430     ERRPRINTF("Could not stat file '%s'\n", filename);
00431     gs_httpd_preamble(sockfd, 404, GS_DEFAULT_CONTENT_TYPE, 0);
00432     gs_httpd_reply(sockfd, GS_RESPONSE_404);
00433     return -1;
00434   }
00435 
00436   if((mime_type = gs_get_content_type(filename)) == NULL) {
00437     ERRPRINTF("Failed to get content type for '%s'\n", filename);
00438     ERRPRINTF("   [probably out of memory]\n");
00439     gs_httpd_preamble(sockfd, 404, GS_DEFAULT_CONTENT_TYPE, 0);
00440     gs_httpd_reply(sockfd, GS_RESPONSE_404);
00441     return -1;
00442   }
00443 
00444   gs_httpd_preamble(sockfd, 200, mime_type, (int)stbuf.st_size);
00445 
00446   return 0;
00447 }
00448 
00458 int
00459 gs_httpd_get_file(int sockfd, const char *filename)
00460 {
00461   const char *mime_type;
00462   double elapsed_time;
00463   struct stat stbuf;
00464   char buf[MAXLINE];
00465   int fd, n;
00466 
00467   if(!filename) {
00468     ERRPRINTF("Invalid filename, ptr is null.\n");
00469     gs_httpd_preamble(sockfd, 404, GS_DEFAULT_CONTENT_TYPE, 0);
00470     gs_httpd_reply(sockfd, GS_RESPONSE_404);
00471     return -1;
00472   }
00473 
00474   if(stat(filename, &stbuf) < 0) {
00475     ERRPRINTF("Could not stat file '%s'\n", filename);
00476     gs_httpd_preamble(sockfd, 404, GS_DEFAULT_CONTENT_TYPE, 0);
00477     gs_httpd_reply(sockfd, GS_RESPONSE_404);
00478     return -1;
00479   }
00480 
00481   if((fd = open(filename, O_RDONLY)) == -1) {
00482     ERRPRINTF("Failed to open file '%s'\n", filename);
00483     gs_httpd_preamble(sockfd, 404, GS_DEFAULT_CONTENT_TYPE, 0);
00484     gs_httpd_reply(sockfd, GS_RESPONSE_404);
00485     return -1;
00486   }
00487 
00488   if((mime_type = gs_get_content_type(filename)) == NULL) {
00489     ERRPRINTF("Failed to get content type for '%s'\n", filename);
00490     ERRPRINTF("   [probably out of memory]\n");
00491     close(fd);
00492     gs_httpd_preamble(sockfd, 404, GS_DEFAULT_CONTENT_TYPE, 0);
00493     gs_httpd_reply(sockfd, GS_RESPONSE_404);
00494     return -1;
00495   }
00496 
00497   gs_httpd_preamble(sockfd, 200, mime_type, (int)stbuf.st_size);
00498 
00499   elapsed_time = walltime();
00500 
00501   while((n=read(fd, buf, MAXLINE)) > 0) {
00502     if(gs_twrite(sockfd, buf, n) != n) {
00503       ERRPRINTF("error in sending data\n");
00504       close(fd);
00505       return -1;
00506     }
00507   }
00508 
00509   elapsed_time = walltime() - elapsed_time;
00510 
00511   close(fd);
00512 
00513   if(n < 0)
00514     ERRPRINTF("Warning: read error on file '%s'\n", filename);
00515 
00516   LOGPRINTF("Sent %d bytes in %g seconds (%g Kbyte/s)\n", (int)stbuf.st_size,
00517      elapsed_time, ((double)stbuf.st_size/1024.0) / elapsed_time);
00518 
00519   return 0;
00520 }
00521 
00534 char *
00535 gs_get_uptime_xml(struct timeval start_time, struct timeval cur_time) {
00536   struct timeval time_diff;
00537   char *xml_time;
00538   long s_diff;
00539   long days;
00540   long hours;
00541   long minutes;
00542   long seconds;
00543 
00544   timersub(&cur_time, &start_time, &time_diff);
00545 
00546   s_diff = time_diff.tv_sec;
00547 
00548   days = s_diff / SECSPERDAY;
00549   if(days > 0) s_diff -= days * SECSPERDAY;
00550   hours = s_diff / SECSPERHOUR;
00551   if(hours > 0) s_diff -= hours * SECSPERHOUR;
00552   minutes = s_diff / SECSPERMIN;
00553   if(minutes > 0) s_diff -= minutes * SECSPERMIN;
00554   seconds = s_diff;
00555 
00556   xml_time = dstring_sprintf("<agent_uptime>\n  <days>%d</days>\n  <hours>%d</hours>\n  <minutes>%d</minutes>\n  <seconds>%d</seconds>\n  </agent_uptime>\n", days, hours, minutes, seconds);
00557   if(!xml_time) goto uptime_error;
00558 
00559   return xml_time;
00560 
00561 uptime_error:
00562   return strdup("<agent_uptime></agent_uptime>\n");
00563 }
00564 
00573 char *
00574 gs_get_agent_stats()
00575 {
00576   gs_agent_stats_t *cur_stats;
00577   char *stats_xml, *hostname_xml, *port_xml, *uptime_xml;
00578   struct timeval cur_time;
00579   int sfd;
00580 
00581   stats_xml = hostname_xml = port_xml = uptime_xml = NULL;
00582 
00583   sfd = open(GRIDSOLVE_STATS_FILE, O_RDONLY);
00584   if(sfd < 0) goto stats_error;
00585 
00586   stats_xml = dstring_sprintf(GS_BEGIN_AGENT_STATS);
00587   if(!stats_xml) goto stats_error;
00588   
00589   cur_stats = (gs_agent_stats_t *) mmap((caddr_t)0, 
00590     sizeof(gs_agent_stats_t), PROT_READ, MAP_SHARED, sfd, 0);
00591 
00592   if(cur_stats == (gs_agent_stats_t *)(-1))
00593     goto stats_error;
00594 
00595   gettimeofday(&cur_time, NULL);
00596 
00597   uptime_xml = gs_get_uptime_xml(cur_stats->start_time, cur_time);
00598   hostname_xml = dstring_sprintf("%s%s%s", GS_BEGIN_AGENT_NAME,
00599      cur_stats->hostname, GS_END_AGENT_NAME);
00600   port_xml = dstring_sprintf("%s%d%s", GS_BEGIN_AGENT_PORT,
00601      cur_stats->port, GS_END_AGENT_PORT);
00602 
00603   if(hostname_xml)
00604     stats_xml = dstring_append(stats_xml, hostname_xml);
00605   if(port_xml)
00606     stats_xml = dstring_append(stats_xml, port_xml);
00607   if(uptime_xml)
00608     stats_xml = dstring_append(stats_xml, uptime_xml);
00609  
00610   stats_xml = dstring_append(stats_xml, GS_END_AGENT_STATS);
00611 
00612   close(sfd);
00613   if(hostname_xml) free(hostname_xml);
00614   if(port_xml) free(port_xml);
00615   if(uptime_xml) free(uptime_xml);
00616   return stats_xml;
00617 
00618 stats_error:
00619   if(sfd >= 0)
00620     close(sfd);
00621   if(hostname_xml) free(hostname_xml);
00622   if(port_xml) free(port_xml);
00623   if(uptime_xml) free(uptime_xml);
00624   return strdup("<agent_stats></agent_stats>");
00625 }
00626 
00636 int
00637 gs_httpd_connect_db(int sockfd)
00638 {
00639   int err;
00640 
00641   err = gs_storage_init(gs_httpd_agent_ptr);
00642   if ( err < 0 ) {
00643     ERRPRINTF("Could not connect to database manager.\n");
00644     gs_httpd_preamble(sockfd, 403, GS_DEFAULT_CONTENT_TYPE, 0);
00645     gs_httpd_reply(sockfd, GS_RESPONSE_DB_FAILURE);
00646     return -1;
00647   }
00648 
00649   return 0;
00650 }
00651 
00661 int
00662 gs_httpd_process_status(int sockfd)
00663 {
00664   gs_server_t **server_list = NULL;
00665   char *agent_stats;
00666   int count;
00667 
00668   count = gs_get_all_servers(NULL, &server_list, &count);
00669 
00670   if(count < 0)
00671     ERRPRINTF("Warning: failed to get list of all servers\n");
00672 
00673   agent_stats = gs_get_agent_stats();
00674 
00675   if(!agent_stats) {
00676     gs_httpd_preamble(sockfd, 403, GS_DEFAULT_CONTENT_TYPE, 0);
00677     gs_httpd_reply(sockfd, GS_RESPONSE_INTERNAL_ERROR);
00678     return -1;
00679   }
00680   
00681   gs_httpd_preamble(sockfd, 200, "application/xml", 0);
00682   gs_httpd_reply(sockfd, GS_XML_HEADER_SERVERLIST);
00683 
00684   gs_httpd_reply(sockfd, GS_BEGIN_SYSTEM_STATUS);
00685   gs_httpd_reply(sockfd, agent_stats);
00686   free(agent_stats);
00687 
00688   if(count > 0) {
00689     gs_send_server_array(sockfd, server_list, count);
00690     gs_free_server_array(server_list, count);
00691   }
00692 
00693   gs_httpd_reply(sockfd, GS_END_SYSTEM_STATUS);
00694 
00695   return 0;
00696 }
00697 
00706 int
00707 gs_httpd_process_task_list(int sockfd)
00708 {
00709   gs_server_t **server_list = NULL;
00710   int i, count;
00711 
00712   count = gs_get_all_servers(NULL, &server_list, &count);
00713 
00714   if(count < 0)
00715     ERRPRINTF("Warning: failed to get list of all servers\n");
00716 
00717   if(!server_list) {
00718     gs_httpd_preamble(sockfd, 403, GS_DEFAULT_CONTENT_TYPE, 0);
00719     gs_httpd_reply(sockfd, GS_RESPONSE_INTERNAL_ERROR);
00720     return -1;
00721   }
00722 
00723   gs_httpd_preamble(sockfd, 200, "application/xml", 0);
00724   gs_httpd_reply(sockfd, GS_XML_HEADER_TASKLIST);
00725 
00726   gs_httpd_reply(sockfd, GS_BEGIN_TASK_LIST);
00727 
00728   if(count > 0) {
00729     char printable_id[CID_LEN*2+1];
00730 
00731     for(i=0;i<count;i++) {
00732       proxy_cid_to_str(printable_id, server_list[i]->componentid);
00733       gs_send_task_list(sockfd, printable_id);
00734     }
00735 
00736     gs_free_server_array(server_list, count);
00737   }
00738 
00739   gs_httpd_reply(sockfd, GS_END_TASK_LIST);
00740 
00741   return 0;
00742 }
00743 
00754 int
00755 gs_httpd_process_server_info(int sockfd, const char *filename)
00756 {
00757   gs_problem_t **problem_list;
00758   gs_server_t **server_list;
00759   char *cid, *fn_copy;
00760   int count;
00761 
00762   problem_list = NULL;
00763   server_list = NULL;
00764 
00765   if(!filename) {
00766     gs_httpd_preamble(sockfd, 403, GS_DEFAULT_CONTENT_TYPE, 0);
00767     gs_httpd_reply(sockfd, GS_RESPONSE_INTERNAL_ERROR);
00768     return -1;
00769   }
00770 
00771   fn_copy = (char *)strdup(filename);
00772 
00773   if(!fn_copy) {
00774     gs_httpd_preamble(sockfd, 403, GS_DEFAULT_CONTENT_TYPE, 0);
00775     gs_httpd_reply(sockfd, GS_RESPONSE_INTERNAL_ERROR);
00776     return -1;
00777   }
00778 
00779   cid = strtok(fn_copy, "?");
00780   cid = strtok(NULL, "?");
00781 
00782   if(!cid) {
00783     free(fn_copy);
00784     gs_httpd_preamble(sockfd, 403, GS_DEFAULT_CONTENT_TYPE, 0);
00785     gs_httpd_reply(sockfd, GS_RESPONSE_BAD_SERVER_REQ);
00786     return -1;
00787   }
00788 
00789   server_list = (gs_server_t **)malloc(sizeof(gs_server_t *));
00790   server_list[0] = (gs_server_t *)calloc(1, sizeof(gs_server_t));
00791 
00792   if(gs_get_server_by_cid(NULL, cid, server_list[0]) < 0) {
00793     ERRPRINTF("failed to get servers\n");
00794     free(server_list[0]);
00795     free(server_list);
00796     free(fn_copy);
00797     gs_httpd_preamble(sockfd, 403, GS_DEFAULT_CONTENT_TYPE, 0);
00798     gs_httpd_reply(sockfd, GS_RESPONSE_BAD_SERVER_REQ);
00799     return -1;
00800   }
00801   
00802   gs_httpd_preamble(sockfd, 200, "application/xml", 0);
00803   gs_httpd_reply(sockfd, GS_XML_HEADER_SERVERINFO);
00804   gs_httpd_reply(sockfd, GS_BEGIN_SERVER_INFO);
00805 
00806   gs_send_server_array(sockfd, server_list, 1);
00807 
00808   if(gs_get_problem_list(NULL, server_list[0], &problem_list, &count) >= 0) {
00809     gs_send_problem_array(sockfd, problem_list, count);
00810     gs_free_problem_array(problem_list, count);
00811   }
00812 
00813   gs_httpd_reply(sockfd, GS_END_SERVER_INFO);
00814 
00815   gs_free_server_array(server_list, 1);
00816   free(fn_copy);
00817 
00818   return 0;
00819 }
00820 
00833 int
00834 gs_httpd_process_problem_info(int sockfd, const char *filename)
00835 {
00836   char *problem_name, *fn_copy;
00837   gs_problem_t **problem_list = NULL;
00838   int count;
00839 
00840   fn_copy = problem_name = NULL;
00841 
00842   if(filename) {
00843     fn_copy = (char *)strdup(filename);
00844 
00845     if(!fn_copy) return -1;
00846 
00847     problem_name = strtok(fn_copy, "?");
00848     problem_name = strtok(NULL, "?");
00849 
00850     if(!problem_name) {
00851       ERRPRINTF("Error parsing problem info request.\n");
00852       free(fn_copy);
00853       gs_httpd_preamble(sockfd, 404, GS_DEFAULT_CONTENT_TYPE, 0);
00854       gs_httpd_reply(sockfd, GS_RESPONSE_404);
00855       return -1;
00856     }
00857   }
00858 
00859   if(problem_name) {
00860     count = gs_get_problem_info(NULL, &problem_list, &count, problem_name);
00861 
00862     if(count == 0) {
00863       free(fn_copy);
00864       gs_httpd_preamble(sockfd, 403, GS_DEFAULT_CONTENT_TYPE, 0);
00865       gs_httpd_reply(sockfd, GS_RESPONSE_BAD_PROBLEM_REQ);
00866       return -1;
00867     }
00868   }
00869   else
00870     count = gs_get_all_problems(NULL, &problem_list, &count);
00871 
00872   if(count < 0)
00873     ERRPRINTF("Warning: failed to get problem(s)\n");
00874 
00875   if(problem_name) {
00876     gs_httpd_preamble(sockfd, 200, "application/xml", 0);
00877     gs_httpd_reply(sockfd, GS_XML_HEADER_PROBLEMINFO);
00878     gs_httpd_reply(sockfd, GS_BEGIN_PROBLEM_INFO);
00879   }
00880   else {
00881     char *agent_stats = gs_get_agent_stats();
00882     if(!agent_stats) {
00883       gs_httpd_preamble(sockfd, 403, GS_DEFAULT_CONTENT_TYPE, 0);
00884       gs_httpd_reply(sockfd, GS_RESPONSE_INTERNAL_ERROR);
00885       return -1;
00886     }
00887   
00888     gs_httpd_preamble(sockfd, 200, "application/xml", 0);
00889     gs_httpd_reply(sockfd, GS_XML_HEADER_SERVERLIST);
00890     gs_httpd_reply(sockfd, GS_BEGIN_SYSTEM_STATUS);
00891     gs_httpd_reply(sockfd, agent_stats);
00892     free(agent_stats);
00893   }
00894 
00895   if(count > 0) {
00896     gs_send_problem_array(sockfd, problem_list, count);
00897     gs_free_problem_array(problem_list, count);
00898   }
00899 
00900   if(problem_name) {
00901     gs_server_t **server_list = NULL;
00902     gs_problem_t *prob;
00903 
00904     prob = (gs_problem_t *)calloc(1, sizeof(gs_problem_t));
00905 
00906     if(prob) {
00907       /* dup the string here since gs_get_server_list will free it
00908        * before filling out the rest of the problem structure.
00909        */
00910       prob->name = strdup(problem_name);
00911 
00912       if(prob->name) {
00913         count =
00914           gs_get_server_list(NULL, prob, NULL, &server_list, &count);
00915   
00916         if(count > 0) {
00917           gs_send_server_array(sockfd, server_list, count);
00918           gs_free_server_array(server_list, count);
00919         }
00920       }
00921       gs_free_problem(prob);
00922     }
00923 
00924     free(fn_copy);
00925     gs_httpd_reply(sockfd, GS_END_PROBLEM_INFO);
00926   }
00927   else
00928     gs_httpd_reply(sockfd, GS_END_SYSTEM_STATUS);
00929 
00930   return 0;
00931 }
00932 
00944 int
00945 gs_send_problem_array(int sockfd, gs_problem_t **problem_list, int count)
00946 {
00947   int i;
00948 
00949   if(!problem_list) {
00950     ERRPRINTF("Invalid arg: null problem_list\n");
00951     return -1;
00952   }
00953 
00954   gs_httpd_reply(sockfd, GS_BEGIN_PROBLEM_LIST);
00955 
00956   for(i = 0; i < count; i++) {
00957     char *prob = NULL;
00958     if(gs_encode_problem(&prob, problem_list[i]) < 0) {
00959       FREE(prob);
00960       ERRPRINTF("Failed to send problem list\n");
00961       return -1;
00962     }
00963 
00964     gs_httpd_reply(sockfd, prob);
00965     gs_httpd_reply(sockfd, "\n");
00966 
00967     FREE(prob);
00968   }
00969 
00970   gs_httpd_reply(sockfd, GS_END_PROBLEM_LIST);
00971 
00972   return 0;
00973 }
00974 
00984 int
00985 gs_free_problem_array(gs_problem_t **problem_list, int count)
00986 {
00987   int i;
00988 
00989   for(i = 0; i < count; i++)
00990     gs_free_problem(problem_list[i]);
00991   FREE(problem_list);
00992   return 0;
00993 }
00994 
01004 int
01005 gs_free_server_array(gs_server_t **server_list, int count)
01006 {
01007   int i;
01008 
01009   for(i = 0; i < count; i++)
01010     gs_server_free(server_list[i]);
01011   FREE(server_list);
01012   return 0;
01013 }
01014 
01026 int
01027 gs_send_server_array(int sockfd, gs_server_t **server_list, int count)
01028 {
01029   int i;
01030 
01031   if(!server_list) {
01032     ERRPRINTF("Invalid arg: null server_list\n");
01033     return -1;
01034   }
01035 
01036   gs_httpd_reply(sockfd, GS_BEGIN_SERVER_LIST);
01037 
01038   for(i = 0; i < count; i++) {
01039     char *srv = NULL;
01040     DBGPRINTF("Encoding server: %s.\n", server_list[i]->hostname);
01041     if(gs_encode_server(&srv, server_list[i]) < 0) {
01042       FREE(srv);
01043       DBGPRINTF("Failed to send server list \n");
01044       return -1;
01045     }
01046 
01047     gs_httpd_reply(sockfd, srv);
01048     gs_httpd_reply(sockfd, "\n");
01049 
01050     FREE(srv);
01051   }
01052 
01053   gs_httpd_reply(sockfd, GS_END_SERVER_LIST);
01054 
01055   return 0;
01056 }
01057 
01068 int
01069 gs_read_until_crlf(int sockfd)
01070 {
01071   char line[MAXLINE];
01072   int n;
01073 
01074   for (;;) {
01075     if ((n = gs_readline(sockfd, line, MAXLINE)) == 0)
01076       return 0;
01077 
01078     line[strlen(line)-2] = 0;
01079 
01080     if(!strcmp(line, ""))
01081       return 0;
01082   }
01083 
01084   return 0;
01085 }
01086 
01098 int
01099 gs_httpd_reply(int sockfd, const char *msg)
01100 {
01101   char *xml;
01102   int n;
01103 
01104   xml = gs_xmlize(msg);
01105 
01106   n = gs_twrite(sockfd, xml, strlen(xml));
01107 
01108   free(xml);
01109 
01110   return n;
01111 }
01112 
01125 char *
01126 gs_xmlize(const char *msg)
01127 {
01128   char *xmlized_msg, *xp;
01129   int inside_string;
01130   const char *op;
01131 
01132   if(!msg) return NULL;
01133 
01134   /* in the worst case, the string will be 5 times longer, so
01135    * rather than constantly checking whether we need to realloc,
01136    * just alloc 5 * strlen(msg) here.
01137    */
01138 
01139   xmlized_msg = (char *)malloc(5 * strlen(msg));
01140   if(!xmlized_msg)
01141     return NULL;
01142 
01143   inside_string = FALSE;
01144 
01145   for(op = msg, xp = xmlized_msg; *op != '\0'; op++) {
01146     if(*op == '"') {
01147       inside_string = inside_string ? FALSE : TRUE;
01148       *xp++ = *op;
01149       continue;
01150     }
01151 
01152     if(inside_string) {
01153       switch(*op) {
01154         case '&':
01155           strcpy(xp, "&amp;");
01156           xp += strlen("&amp;");
01157           break;
01158         case '<':
01159           strcpy(xp, "&lt;");
01160           xp += strlen("&lt;");
01161           break;
01162         case '>':
01163           strcpy(xp, "&gt;");
01164           xp += strlen("&gt;");
01165           break;
01166         default:
01167           *xp++ = *op;
01168       }
01169     }
01170     else
01171       *xp++ = *op;
01172   }
01173 
01174   *xp = '\0';
01175 
01176   return xmlized_msg;
01177 }
01178 
01195 int
01196 gs_httpd_preamble(int sockfd, int num, const char *content_type, int len)
01197 {
01198   char http_resp_line[MAXLINE], mime_type_line[MAXLINE], len_line[MAXLINE];
01199 
01200   if(!content_type)
01201     content_type = GS_DEFAULT_CONTENT_TYPE;
01202 
01203   sprintf(http_resp_line, "HTTP/1.1 %d OK\n", num);
01204   sprintf(mime_type_line, "Content-Type: %s\n", content_type);
01205   sprintf(len_line, "Content-Length: %d\n", len);
01206 
01207   if((gs_httpd_reply(sockfd, http_resp_line) < 0) ||
01208      (gs_httpd_reply(sockfd, "Server: GridSolve beta\n") < 0) ||
01209      (gs_httpd_reply(sockfd, mime_type_line) < 0))
01210   {
01211     ERRPRINTF("Error sending preamble\n");
01212     return -1;
01213   }
01214 
01215   if(len > 0) {
01216     if(gs_httpd_reply(sockfd, len_line) < 0)
01217     {
01218       ERRPRINTF("Error sending preamble\n");
01219       return -1;
01220     }
01221   }
01222 
01223   if(gs_httpd_reply(sockfd, "\r\n") < 0)
01224   {
01225     ERRPRINTF("Error sending preamble\n");
01226     return -1;
01227   }
01228 
01229   return 0;
01230 }
01231 
01242 int
01243 gs_send_task_list(int sockfd, char *server_cid)
01244 {
01245   gs_htm_task **tasks = NULL;
01246   int i, count;
01247   char *info;
01248 
01249   gs_get_tasks_for_server(server_cid, &tasks, &count, 1);
01250 
01251   if(count < 0) {
01252     ERRPRINTF("failed to get list of all tasks\n");
01253     return -1;
01254   }
01255 
01256   if(!tasks) {
01257     ERRPRINTF("Empty task list despite non-negative return value!\n");
01258     return -1;
01259   }
01260 
01261   for(i=0;i<count;i++) {
01262     info = dstring_sprintf("<server_cid> %s </server_cid> <task_id> %s </task_id> <start> %10.2lf </start> <duration> %10.2lf </duration> <remaining> %10.2lf </remaining> <end> %10.2lf </end> <active> %d </active> <finished> %d </finished>\n",
01263       server_cid, tasks[i]->id, tasks[i]->start, tasks[i]->duration, tasks[i]->remaining,
01264       tasks[i]->end, tasks[i]->active, tasks[i]->finished);
01265 
01266     free(tasks[i]);
01267 
01268     if(info) {
01269       gs_httpd_reply(sockfd, GS_BEGIN_TASK_INFO);
01270 
01271       gs_httpd_reply(sockfd, info);
01272 
01273       free(info);
01274 
01275       gs_httpd_reply(sockfd, GS_END_TASK_INFO);
01276     }
01277   }
01278 
01279   free(tasks);
01280 
01281   return 0;
01282 }