Functions | Variables

gs_agent_httpd.c File Reference

#include "gs_agent_httpd.h"
Include dependency graph for gs_agent_httpd.c:

Go to the source code of this file.

Functions

static void gs_httpd_generic_signal_handler (int sig)
void gs_agent_exit (void **args)
void gs_agent_httpd (void **args)
int gs_agent_httpd_listen_and_process_connections (int listenfd)
void sig_chld (int signo)
const char * gs_get_content_type (const char *filename)
void unescape_url (char *str)
int gs_agent_httpd_handle_connection (int sockfd)
int gs_httpd_head_file (int sockfd, const char *filename)
int gs_httpd_get_file (int sockfd, const char *filename)
char * gs_get_uptime_xml (struct timeval start_time, struct timeval cur_time)
char * gs_get_agent_stats ()
int gs_httpd_connect_db (int sockfd)
int gs_httpd_process_status (int sockfd)
int gs_httpd_process_task_list (int sockfd)
int gs_httpd_process_server_info (int sockfd, const char *filename)
int gs_httpd_process_problem_info (int sockfd, const char *filename)
int gs_send_problem_array (int sockfd, gs_problem_t **problem_list, int count)
int gs_free_problem_array (gs_problem_t **problem_list, int count)
int gs_free_server_array (gs_server_t **server_list, int count)
int gs_send_server_array (int sockfd, gs_server_t **server_list, int count)
int gs_read_until_crlf (int sockfd)
int gs_httpd_reply (int sockfd, const char *msg)
char * gs_xmlize (const char *msg)
int gs_httpd_preamble (int sockfd, int num, const char *content_type, int len)
int gs_send_task_list (int sockfd, char *server_cid)

Variables

char GRIDSOLVE_STATS_FILE []
char * gs_content_types [][2]
gs_agent_t * gs_httpd_agent_ptr = NULL

Function Documentation

void gs_agent_exit ( void **  args  ) 

Func to be called on exit.

Parameters:
args -- original args specified when mforked

Definition at line 54 of file gs_agent_httpd.c.

{
  _exit(0);
}

void gs_agent_httpd ( void **  args  ) 

This is the entry point for the http server.

Parameters:
args -- args[0] should contain a pointer to an integer whose value is the port number to use.

Definition at line 67 of file gs_agent_httpd.c.

{
  char *gridsolve_root, *www_home_path;
  struct sockaddr_in servaddr;
  int listenfd;
  socklen_t dummyLen;
  void sig_chld(int);
  short port;

  if(!args || !args[0] || !args[1]) {
    ERRPRINTF("Bad args\n");
    _exit(EXIT_FAILURE);
  }

  /* handle all signals except SIGINT.  if the agent is running in
   * console mode, we only want the parent to get SIGINT, so that all
   * the children will realize the parent is dead via mfork_check_parent()
   * and cleanly terminate.  having all processes catch SIGINT causes
   * mfork to try to restart them, depending on whether they catch it
   * before or after the parent.
   */
  gs_setup_signal_handlers(gs_httpd_generic_signal_handler);
  signal(SIGINT, SIG_IGN);

  port = *((int *)args[0]);
  gs_httpd_agent_ptr = (gs_agent_t *)args[1];

  if((listenfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
    perror("Failed to create socket");
    _exit(EXIT_FAILURE);
  }

  if((gridsolve_root = getenv("GRIDSOLVE_ROOT")) == NULL)
    gridsolve_root = GRIDSOLVE_TOP_BUILD_DIR;

  if(!gridsolve_root) {
    ERRPRINTF("Warning: GRIDSOLVE_ROOT unknown, assuming cwd.\n");
    gridsolve_root = strdup(".");
  }

  www_home_path = (char *)malloc(strlen(gridsolve_root) + 
      strlen(GS_AGENT_WWW_DIR) + 2);
  if(!www_home_path) {
    ERRPRINTF("malloc failed\n");
    _exit(EXIT_FAILURE);
  }

  sprintf(www_home_path, "%s/%s", gridsolve_root, GS_AGENT_WWW_DIR);

  if(chdir(www_home_path) < 0) {
    ERRPRINTF("Failed to chdir to '%s'\n", www_home_path);
    _exit(EXIT_FAILURE);
  }

  free(www_home_path);

  memset(&servaddr, 0x0, sizeof(servaddr));
  servaddr.sin_family = AF_INET;
  servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
  servaddr.sin_port = htons(port);

  if(bind(listenfd, (struct sockaddr *) &servaddr, sizeof(servaddr)) < 0) {
    perror("Failed to bind");
    _exit(EXIT_FAILURE);
  }

  if(listen(listenfd, 20) < 0) {
    perror("Listen failed");
    _exit(EXIT_FAILURE);
  }

  dummyLen = sizeof(struct sockaddr);
  if(getsockname(listenfd, (struct sockaddr *)(&servaddr), &dummyLen) < 0)
    LOGPRINTF("HTTP server listening on some unknown port\n");
  else
    LOGPRINTF("HTTP server listening on port %d\n", ntohs(servaddr.sin_port));

  if(gs_signal(SIGCHLD, sig_chld) == SIG_ERR) {
    ERRPRINTF("Could not set up SIGCHLD signal handler\n");
    _exit(EXIT_FAILURE);
  }

  gs_agent_httpd_listen_and_process_connections(listenfd);
  _exit(EXIT_SUCCESS);
}

Here is the call graph for this function:

Here is the caller graph for this function:

int gs_agent_httpd_handle_connection ( int  sockfd  ) 

Handle a newly accepted http connection.

Parameters:
sockfd -- the socket to communicate on
Returns:
0 on success, -1 on failure.

Definition at line 333 of file gs_agent_httpd.c.

{
  ssize_t n;
  char *cmd, *fn, line[MAXLINE];
  
  if((n = gs_readline(sockfd, line, MAXLINE)) > 0) {
    cmd = strtok(line, " ");
    fn = strtok(NULL, " ");

    if(!strcmp(cmd, "GET")) {
      int rv;

      LOGPRINTF("HTTP GET %s\n", fn);

      gs_read_until_crlf(sockfd);

      /* ignore idiots */
      if(!strncmp(fn, "http://", 7))
       return 0;

      fn++;   /* skip '/' */

      unescape_url(fn);

      if(*fn == '\0' || strchr(fn, '?')) {
        if(gs_httpd_connect_db(sockfd) < 0)
          return -1;

        if(*fn == '\0' || !strcmp(fn, "status?"))
          rv = gs_httpd_process_status(sockfd);
        else if(!strcmp(fn, "problemlist?"))
          rv = gs_httpd_process_problem_info(sockfd, NULL);
        else if(!strncmp(fn, "serverinfo?", strlen("serverinfo?")))
          rv = gs_httpd_process_server_info(sockfd, fn);
        else if(!strncmp(fn, "probleminfo?", strlen("probleminfo?")))
          rv = gs_httpd_process_problem_info(sockfd, fn);
        else if(!strncmp(fn, "tasklist?", strlen("tasklist?")))
          rv = gs_httpd_process_task_list(sockfd);
        else
          rv = gs_httpd_get_file(sockfd, fn);

        /* Disconnect from database manager */
        gs_storage_finalize(NULL);
      }
      else
        rv = gs_httpd_get_file(sockfd, fn);

      return rv;
    }
    else if(!strcmp(cmd, "HEAD")) {
      int rv;

      LOGPRINTF("HTTP HEAD %s\n", fn);

      gs_read_until_crlf(sockfd);

      /* ignore idiots */
      if(!strncmp(fn, "http://", 7))
       return 0;

      fn++;   /* skip '/' */

      rv = gs_httpd_head_file(sockfd, fn);

      return rv;
    }
  }

  gs_httpd_preamble(sockfd, 404, GS_DEFAULT_CONTENT_TYPE, 0);
  gs_httpd_reply(sockfd, GS_RESPONSE_404);

  return -1;
}

Here is the call graph for this function:

Here is the caller graph for this function:

int gs_agent_httpd_listen_and_process_connections ( int  listenfd  ) 

This is the main loop that accepts and processes incoming requests.

Parameters:
listenfd -- the socket to accept connections on
Returns:
0 on success, -1 on failure.

Definition at line 162 of file gs_agent_httpd.c.

{
  struct sockaddr_in cliaddr;
  fd_set rset, allset;
  socklen_t clilen;
  pid_t childpid;
  struct timeval tv;
  int nready, connfd;

  clilen = sizeof(cliaddr);
  FD_ZERO(&allset);
  FD_SET(listenfd, &allset);

  for(;;) {
    rset = allset;
    tv.tv_sec = 1;
    tv.tv_usec = 0;

    nready = select(listenfd + 1, &rset, NULL, NULL, &tv);
    
    if((nready < 0) && (errno == EINTR))
      continue;

    if(nready < 0) {
      ERRPRINTF("select failed.. aborting.\n");
      break;
    }

    if(nready == 0) {
      if(!mfork_check_parent()) {
        ERRPRINTF("Parent died, so I am exiting\n");
        break;
      }
      continue;
    }

    if(FD_ISSET(listenfd, &rset)) {
      if ((connfd = accept(listenfd, (struct sockaddr *) &cliaddr, &clilen)) < 0) {
        if (errno == EINTR)
          continue;
        else {
          perror("accept error");
          _exit(-1);
        }
      }

      if ((childpid = fork()) == 0) {   /* child process */
        close(listenfd);
        if(gs_agent_httpd_handle_connection(connfd) < 0) {
          ERRPRINTF("Failed to handle connection.\n");
          _exit(-1);
        }
        close(connfd);
        _exit(0);
      }
      close(connfd);  /* parent closes connected socket */
    }
  }

  return 0;
}

Here is the call graph for this function:

Here is the caller graph for this function:

int gs_free_problem_array ( gs_problem_t **  problem_list,
int  count 
)

Free the memory allocated for the problem array.

Parameters:
problem_list -- the array of problems to free
count -- the number of elements in the problem_list array
Returns:
0 on success, -1 on failure.

Definition at line 985 of file gs_agent_httpd.c.

{
  int i;

  for(i = 0; i < count; i++)
    gs_free_problem(problem_list[i]);
  FREE(problem_list);
  return 0;
}

Here is the call graph for this function:

Here is the caller graph for this function:

int gs_free_server_array ( gs_server_t **  server_list,
int  count 
)

Free the memory allocated for the server array.

Parameters:
server_list -- the array of servers to free
count -- the number of elements in the server_list array
Returns:
0 on success, -1 on failure.

Definition at line 1005 of file gs_agent_httpd.c.

{
  int i;

  for(i = 0; i < count; i++)
    gs_server_free(server_list[i]);
  FREE(server_list);
  return 0;
}

Here is the call graph for this function:

Here is the caller graph for this function:

char* gs_get_agent_stats (  ) 

Return XML formatted information with all relevant agent statistics.

Returns:
the XML formatted agent information. on error, returns an empty stats tag: "<agent_stats></agent_stats>"

Definition at line 574 of file gs_agent_httpd.c.

{
  gs_agent_stats_t *cur_stats;
  char *stats_xml, *hostname_xml, *port_xml, *uptime_xml;
  struct timeval cur_time;
  int sfd;

  stats_xml = hostname_xml = port_xml = uptime_xml = NULL;

  sfd = open(GRIDSOLVE_STATS_FILE, O_RDONLY);
  if(sfd < 0) goto stats_error;

  stats_xml = dstring_sprintf(GS_BEGIN_AGENT_STATS);
  if(!stats_xml) goto stats_error;
  
  cur_stats = (gs_agent_stats_t *) mmap((caddr_t)0, 
    sizeof(gs_agent_stats_t), PROT_READ, MAP_SHARED, sfd, 0);

  if(cur_stats == (gs_agent_stats_t *)(-1))
    goto stats_error;

  gettimeofday(&cur_time, NULL);

  uptime_xml = gs_get_uptime_xml(cur_stats->start_time, cur_time);
  hostname_xml = dstring_sprintf("%s%s%s", GS_BEGIN_AGENT_NAME,
     cur_stats->hostname, GS_END_AGENT_NAME);
  port_xml = dstring_sprintf("%s%d%s", GS_BEGIN_AGENT_PORT,
     cur_stats->port, GS_END_AGENT_PORT);

  if(hostname_xml)
    stats_xml = dstring_append(stats_xml, hostname_xml);
  if(port_xml)
    stats_xml = dstring_append(stats_xml, port_xml);
  if(uptime_xml)
    stats_xml = dstring_append(stats_xml, uptime_xml);
 
  stats_xml = dstring_append(stats_xml, GS_END_AGENT_STATS);

  close(sfd);
  if(hostname_xml) free(hostname_xml);
  if(port_xml) free(port_xml);
  if(uptime_xml) free(uptime_xml);
  return stats_xml;

stats_error:
  if(sfd >= 0)
    close(sfd);
  if(hostname_xml) free(hostname_xml);
  if(port_xml) free(port_xml);
  if(uptime_xml) free(uptime_xml);
  return strdup("<agent_stats></agent_stats>");
}

Here is the call graph for this function:

Here is the caller graph for this function:

const char* gs_get_content_type ( const char *  filename  ) 

Gets the content type of a filename based on its extension.

Parameters:
filename -- the filename whose extension should be returned. if an appropriate content type can't be determined "text/html" is returned. DONT free the string returned by this function.

Definition at line 253 of file gs_agent_httpd.c.

{   
  const char *end;
  int i;

  if(filename && (strlen(filename) > 0)) {
    end = filename + strlen(filename) - 1;

    while(end != filename) {
      if(*end == '.') {
        end++;
        break;
      }
      end--;
    }

    if(end == filename)
      return GS_DEFAULT_CONTENT_TYPE;

    for(i=0; gs_content_types[i][0]; i++)
      if(!strcmp(end, gs_content_types[i][0]))
        return(gs_content_types[i][1]);
  }

  return GS_DEFAULT_CONTENT_TYPE;
}

Here is the caller graph for this function:

char* gs_get_uptime_xml ( struct timeval  start_time,
struct timeval  cur_time 
)

Computes the agent's uptime in terms of days, hours, minutes, and seconds based on two timeval structs (start time and current time). Return an XML string containing the information.

Parameters:
start_time -- the time the agent was started
cur_time -- the current time
Returns:
the XML formatted uptime information. on error, returns an empty uptime tag: "<agent_uptime></agent_uptime>"

Definition at line 535 of file gs_agent_httpd.c.

                                                                      {
  struct timeval time_diff;
  char *xml_time;
  long s_diff;
  long days;
  long hours;
  long minutes;
  long seconds;

  timersub(&cur_time, &start_time, &time_diff);

  s_diff = time_diff.tv_sec;

  days = s_diff / SECSPERDAY;
  if(days > 0) s_diff -= days * SECSPERDAY;
  hours = s_diff / SECSPERHOUR;
  if(hours > 0) s_diff -= hours * SECSPERHOUR;
  minutes = s_diff / SECSPERMIN;
  if(minutes > 0) s_diff -= minutes * SECSPERMIN;
  seconds = s_diff;

  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);
  if(!xml_time) goto uptime_error;

  return xml_time;

uptime_error:
  return strdup("<agent_uptime></agent_uptime>\n");
}

Here is the call graph for this function:

Here is the caller graph for this function:

int gs_httpd_connect_db ( int  sockfd  ) 

Connect to the database manager.

Parameters:
sockfd -- on error, the error information will be written to this descriptor.
Returns:
0 on success, -1 on failure.

Definition at line 637 of file gs_agent_httpd.c.

{
  int err;

  err = gs_storage_init(gs_httpd_agent_ptr);
  if ( err < 0 ) {
    ERRPRINTF("Could not connect to database manager.\n");
    gs_httpd_preamble(sockfd, 403, GS_DEFAULT_CONTENT_TYPE, 0);
    gs_httpd_reply(sockfd, GS_RESPONSE_DB_FAILURE);
    return -1;
  }

  return 0;
}

Here is the call graph for this function:

Here is the caller graph for this function:

static void gs_httpd_generic_signal_handler ( int  sig  )  [static]

Signal handler for various signals. Exit with normal status.

Parameters:
sig -- the signal that was caught

Definition at line 35 of file gs_agent_httpd.c.

{
  /* pass along SIGHUP to parent */
  if(sig == SIGHUP) {
    kill(getppid(), SIGHUP);
    return;
  }

  ERRPRINTF("HTTP server terminating on signal %d.\n", sig);
  _exit(0);
}

Here is the caller graph for this function:

int gs_httpd_get_file ( int  sockfd,
const char *  filename 
)

Handle an HTTP GET request.

Parameters:
sockfd -- the socket to communicate on
filename -- the name of the requested file
Returns:
0 on success, -1 on failure.

Definition at line 459 of file gs_agent_httpd.c.

{
  const char *mime_type;
  double elapsed_time;
  struct stat stbuf;
  char buf[MAXLINE];
  int fd, n;

  if(!filename) {
    ERRPRINTF("Invalid filename, ptr is null.\n");
    gs_httpd_preamble(sockfd, 404, GS_DEFAULT_CONTENT_TYPE, 0);
    gs_httpd_reply(sockfd, GS_RESPONSE_404);
    return -1;
  }

  if(stat(filename, &stbuf) < 0) {
    ERRPRINTF("Could not stat file '%s'\n", filename);
    gs_httpd_preamble(sockfd, 404, GS_DEFAULT_CONTENT_TYPE, 0);
    gs_httpd_reply(sockfd, GS_RESPONSE_404);
    return -1;
  }

  if((fd = open(filename, O_RDONLY)) == -1) {
    ERRPRINTF("Failed to open file '%s'\n", filename);
    gs_httpd_preamble(sockfd, 404, GS_DEFAULT_CONTENT_TYPE, 0);
    gs_httpd_reply(sockfd, GS_RESPONSE_404);
    return -1;
  }

  if((mime_type = gs_get_content_type(filename)) == NULL) {
    ERRPRINTF("Failed to get content type for '%s'\n", filename);
    ERRPRINTF("   [probably out of memory]\n");
    close(fd);
    gs_httpd_preamble(sockfd, 404, GS_DEFAULT_CONTENT_TYPE, 0);
    gs_httpd_reply(sockfd, GS_RESPONSE_404);
    return -1;
  }

  gs_httpd_preamble(sockfd, 200, mime_type, (int)stbuf.st_size);

  elapsed_time = walltime();

  while((n=read(fd, buf, MAXLINE)) > 0) {
    if(gs_twrite(sockfd, buf, n) != n) {
      ERRPRINTF("error in sending data\n");
      close(fd);
      return -1;
    }
  }

  elapsed_time = walltime() - elapsed_time;

  close(fd);

  if(n < 0)
    ERRPRINTF("Warning: read error on file '%s'\n", filename);

  LOGPRINTF("Sent %d bytes in %g seconds (%g Kbyte/s)\n", (int)stbuf.st_size,
     elapsed_time, ((double)stbuf.st_size/1024.0) / elapsed_time);

  return 0;
}

Here is the call graph for this function:

Here is the caller graph for this function:

int gs_httpd_head_file ( int  sockfd,
const char *  filename 
)

Handle an HTTP HEAD request.

Parameters:
sockfd -- the socket to communicate on
filename -- the name of the requested file
Returns:
0 on success, -1 on failure.

Definition at line 417 of file gs_agent_httpd.c.

{
  struct stat stbuf;
  const char *mime_type;

  if(!filename) {
    ERRPRINTF("Invalid filename, ptr is null.\n");
    gs_httpd_preamble(sockfd, 404, GS_DEFAULT_CONTENT_TYPE, 0);
    gs_httpd_reply(sockfd, GS_RESPONSE_404);
    return -1;
  }

  if(stat(filename, &stbuf) < 0) {
    ERRPRINTF("Could not stat file '%s'\n", filename);
    gs_httpd_preamble(sockfd, 404, GS_DEFAULT_CONTENT_TYPE, 0);
    gs_httpd_reply(sockfd, GS_RESPONSE_404);
    return -1;
  }

  if((mime_type = gs_get_content_type(filename)) == NULL) {
    ERRPRINTF("Failed to get content type for '%s'\n", filename);
    ERRPRINTF("   [probably out of memory]\n");
    gs_httpd_preamble(sockfd, 404, GS_DEFAULT_CONTENT_TYPE, 0);
    gs_httpd_reply(sockfd, GS_RESPONSE_404);
    return -1;
  }

  gs_httpd_preamble(sockfd, 200, mime_type, (int)stbuf.st_size);

  return 0;
}

Here is the call graph for this function:

Here is the caller graph for this function:

int gs_httpd_preamble ( int  sockfd,
int  num,
const char *  content_type,
int  len 
)

Sends the usual premble stuff back to the browser. Currently this consists of: HTTP/1.1 <num> OK Server: GridSolve beta Content-Type: <content_type> Content-Length: <file_length>

Parameters:
sockfd -- the socket to communicate on
num -- the protocol number (e.g. 200, 404, etc)
content_type -- the content type of the reply
len -- the content length of the file
Returns:
0 on success, -1 on failure.

Definition at line 1196 of file gs_agent_httpd.c.

{
  char http_resp_line[MAXLINE], mime_type_line[MAXLINE], len_line[MAXLINE];

  if(!content_type)
    content_type = GS_DEFAULT_CONTENT_TYPE;

  sprintf(http_resp_line, "HTTP/1.1 %d OK\n", num);
  sprintf(mime_type_line, "Content-Type: %s\n", content_type);
  sprintf(len_line, "Content-Length: %d\n", len);

  if((gs_httpd_reply(sockfd, http_resp_line) < 0) ||
     (gs_httpd_reply(sockfd, "Server: GridSolve beta\n") < 0) ||
     (gs_httpd_reply(sockfd, mime_type_line) < 0))
  {
    ERRPRINTF("Error sending preamble\n");
    return -1;
  }

  if(len > 0) {
    if(gs_httpd_reply(sockfd, len_line) < 0)
    {
      ERRPRINTF("Error sending preamble\n");
      return -1;
    }
  }

  if(gs_httpd_reply(sockfd, "\r\n") < 0)
  {
    ERRPRINTF("Error sending preamble\n");
    return -1;
  }

  return 0;
}

Here is the call graph for this function:

Here is the caller graph for this function:

int gs_httpd_process_problem_info ( int  sockfd,
const char *  filename 
)

Handle a problem information request. Return all relevant information about the specified problem.

Parameters:
sockfd -- the socket to communicate on
filename -- the request string. if NULL, returns all problems, if "probleminfo?problemname", then only returns information about "problemname".
Returns:
0 on success, -1 on failure.

Definition at line 834 of file gs_agent_httpd.c.

{
  char *problem_name, *fn_copy;
  gs_problem_t **problem_list = NULL;
  int count;

  fn_copy = problem_name = NULL;

  if(filename) {
    fn_copy = (char *)strdup(filename);

    if(!fn_copy) return -1;

    problem_name = strtok(fn_copy, "?");
    problem_name = strtok(NULL, "?");

    if(!problem_name) {
      ERRPRINTF("Error parsing problem info request.\n");
      free(fn_copy);
      gs_httpd_preamble(sockfd, 404, GS_DEFAULT_CONTENT_TYPE, 0);
      gs_httpd_reply(sockfd, GS_RESPONSE_404);
      return -1;
    }
  }

  if(problem_name) {
    count = gs_get_problem_info(NULL, &problem_list, &count, problem_name);

    if(count == 0) {
      free(fn_copy);
      gs_httpd_preamble(sockfd, 403, GS_DEFAULT_CONTENT_TYPE, 0);
      gs_httpd_reply(sockfd, GS_RESPONSE_BAD_PROBLEM_REQ);
      return -1;
    }
  }
  else
    count = gs_get_all_problems(NULL, &problem_list, &count);

  if(count < 0)
    ERRPRINTF("Warning: failed to get problem(s)\n");

  if(problem_name) {
    gs_httpd_preamble(sockfd, 200, "application/xml", 0);
    gs_httpd_reply(sockfd, GS_XML_HEADER_PROBLEMINFO);
    gs_httpd_reply(sockfd, GS_BEGIN_PROBLEM_INFO);
  }
  else {
    char *agent_stats = gs_get_agent_stats();
    if(!agent_stats) {
      gs_httpd_preamble(sockfd, 403, GS_DEFAULT_CONTENT_TYPE, 0);
      gs_httpd_reply(sockfd, GS_RESPONSE_INTERNAL_ERROR);
      return -1;
    }
  
    gs_httpd_preamble(sockfd, 200, "application/xml", 0);
    gs_httpd_reply(sockfd, GS_XML_HEADER_SERVERLIST);
    gs_httpd_reply(sockfd, GS_BEGIN_SYSTEM_STATUS);
    gs_httpd_reply(sockfd, agent_stats);
    free(agent_stats);
  }

  if(count > 0) {
    gs_send_problem_array(sockfd, problem_list, count);
    gs_free_problem_array(problem_list, count);
  }

  if(problem_name) {
    gs_server_t **server_list = NULL;
    gs_problem_t *prob;

    prob = (gs_problem_t *)calloc(1, sizeof(gs_problem_t));

    if(prob) {
      /* dup the string here since gs_get_server_list will free it
       * before filling out the rest of the problem structure.
       */
      prob->name = strdup(problem_name);

      if(prob->name) {
        count =
          gs_get_server_list(NULL, prob, NULL, &server_list, &count);
  
        if(count > 0) {
          gs_send_server_array(sockfd, server_list, count);
          gs_free_server_array(server_list, count);
        }
      }
      gs_free_problem(prob);
    }

    free(fn_copy);
    gs_httpd_reply(sockfd, GS_END_PROBLEM_INFO);
  }
  else
    gs_httpd_reply(sockfd, GS_END_SYSTEM_STATUS);

  return 0;
}

Here is the call graph for this function:

Here is the caller graph for this function:

int gs_httpd_process_server_info ( int  sockfd,
const char *  filename 
)

Handle a server information request. Return all relevant information about the specified server.

Parameters:
sockfd -- the socket to communicate on
filename -- the request string: "serverinfo?servername"
Returns:
0 on success, -1 on failure.

Definition at line 755 of file gs_agent_httpd.c.

{
  gs_problem_t **problem_list;
  gs_server_t **server_list;
  char *cid, *fn_copy;
  int count;

  problem_list = NULL;
  server_list = NULL;

  if(!filename) {
    gs_httpd_preamble(sockfd, 403, GS_DEFAULT_CONTENT_TYPE, 0);
    gs_httpd_reply(sockfd, GS_RESPONSE_INTERNAL_ERROR);
    return -1;
  }

  fn_copy = (char *)strdup(filename);

  if(!fn_copy) {
    gs_httpd_preamble(sockfd, 403, GS_DEFAULT_CONTENT_TYPE, 0);
    gs_httpd_reply(sockfd, GS_RESPONSE_INTERNAL_ERROR);
    return -1;
  }

  cid = strtok(fn_copy, "?");
  cid = strtok(NULL, "?");

  if(!cid) {
    free(fn_copy);
    gs_httpd_preamble(sockfd, 403, GS_DEFAULT_CONTENT_TYPE, 0);
    gs_httpd_reply(sockfd, GS_RESPONSE_BAD_SERVER_REQ);
    return -1;
  }

  server_list = (gs_server_t **)malloc(sizeof(gs_server_t *));
  server_list[0] = (gs_server_t *)calloc(1, sizeof(gs_server_t));

  if(gs_get_server_by_cid(NULL, cid, server_list[0]) < 0) {
    ERRPRINTF("failed to get servers\n");
    free(server_list[0]);
    free(server_list);
    free(fn_copy);
    gs_httpd_preamble(sockfd, 403, GS_DEFAULT_CONTENT_TYPE, 0);
    gs_httpd_reply(sockfd, GS_RESPONSE_BAD_SERVER_REQ);
    return -1;
  }
  
  gs_httpd_preamble(sockfd, 200, "application/xml", 0);
  gs_httpd_reply(sockfd, GS_XML_HEADER_SERVERINFO);
  gs_httpd_reply(sockfd, GS_BEGIN_SERVER_INFO);

  gs_send_server_array(sockfd, server_list, 1);

  if(gs_get_problem_list(NULL, server_list[0], &problem_list, &count) >= 0) {
    gs_send_problem_array(sockfd, problem_list, count);
    gs_free_problem_array(problem_list, count);
  }

  gs_httpd_reply(sockfd, GS_END_SERVER_INFO);

  gs_free_server_array(server_list, 1);
  free(fn_copy);

  return 0;
}

Here is the call graph for this function:

Here is the caller graph for this function:

int gs_httpd_process_status ( int  sockfd  ) 

Handle a status request. Return agent statistics and a list of all available servers.

Parameters:
sockfd -- the socket to communicate on
Returns:
0 on success, -1 on failure.

Definition at line 662 of file gs_agent_httpd.c.

{
  gs_server_t **server_list = NULL;
  char *agent_stats;
  int count;

  count = gs_get_all_servers(NULL, &server_list, &count);

  if(count < 0)
    ERRPRINTF("Warning: failed to get list of all servers\n");

  agent_stats = gs_get_agent_stats();

  if(!agent_stats) {
    gs_httpd_preamble(sockfd, 403, GS_DEFAULT_CONTENT_TYPE, 0);
    gs_httpd_reply(sockfd, GS_RESPONSE_INTERNAL_ERROR);
    return -1;
  }
  
  gs_httpd_preamble(sockfd, 200, "application/xml", 0);
  gs_httpd_reply(sockfd, GS_XML_HEADER_SERVERLIST);

  gs_httpd_reply(sockfd, GS_BEGIN_SYSTEM_STATUS);
  gs_httpd_reply(sockfd, agent_stats);
  free(agent_stats);

  if(count > 0) {
    gs_send_server_array(sockfd, server_list, count);
    gs_free_server_array(server_list, count);
  }

  gs_httpd_reply(sockfd, GS_END_SYSTEM_STATUS);

  return 0;
}

Here is the call graph for this function:

Here is the caller graph for this function:

int gs_httpd_process_task_list ( int  sockfd  ) 

Return list of all task info stored in the database.

Parameters:
sockfd -- the socket to communicate on
Returns:
0 on success, -1 on failure.

Definition at line 707 of file gs_agent_httpd.c.

{
  gs_server_t **server_list = NULL;
  int i, count;

  count = gs_get_all_servers(NULL, &server_list, &count);

  if(count < 0)
    ERRPRINTF("Warning: failed to get list of all servers\n");

  if(!server_list) {
    gs_httpd_preamble(sockfd, 403, GS_DEFAULT_CONTENT_TYPE, 0);
    gs_httpd_reply(sockfd, GS_RESPONSE_INTERNAL_ERROR);
    return -1;
  }

  gs_httpd_preamble(sockfd, 200, "application/xml", 0);
  gs_httpd_reply(sockfd, GS_XML_HEADER_TASKLIST);

  gs_httpd_reply(sockfd, GS_BEGIN_TASK_LIST);

  if(count > 0) {
    char printable_id[CID_LEN*2+1];

    for(i=0;i<count;i++) {
      proxy_cid_to_str(printable_id, server_list[i]->componentid);
      gs_send_task_list(sockfd, printable_id);
    }

    gs_free_server_array(server_list, count);
  }

  gs_httpd_reply(sockfd, GS_END_TASK_LIST);

  return 0;
}

Here is the call graph for this function:

Here is the caller graph for this function:

int gs_httpd_reply ( int  sockfd,
const char *  msg 
)

Send a string on the socket. Before sending, the string is processed to remove characters that XML doesn't like such as <, >, and &.

Parameters:
sockfd -- the socket to communicate on
msg -- the string to be sent.
Returns:
the number of bytes actually written. returns -1 on error.

Definition at line 1099 of file gs_agent_httpd.c.

{
  char *xml;
  int n;

  xml = gs_xmlize(msg);

  n = gs_twrite(sockfd, xml, strlen(xml));

  free(xml);

  return n;
}

Here is the call graph for this function:

Here is the caller graph for this function:

int gs_read_until_crlf ( int  sockfd  ) 

Reads from the socket until a newline is seen. This is used to grab and throw away all the junk that the browser sends after the HTTP GET.

Parameters:
sockfd -- the socket to communicate on
Returns:
0 on success, -1 on failure.

Definition at line 1069 of file gs_agent_httpd.c.

{
  char line[MAXLINE];
  int n;

  for (;;) {
    if ((n = gs_readline(sockfd, line, MAXLINE)) == 0)
      return 0;

    line[strlen(line)-2] = 0;

    if(!strcmp(line, ""))
      return 0;
  }

  return 0;
}

Here is the call graph for this function:

Here is the caller graph for this function:

int gs_send_problem_array ( int  sockfd,
gs_problem_t **  problem_list,
int  count 
)

Send an array of problems on the socket.

Parameters:
sockfd -- the socket to communicate on
problem_list -- array of problem structures to be sent in xml form
count -- the number of elements in the problem_list array
Returns:
0 on success, -1 on failure.

Definition at line 945 of file gs_agent_httpd.c.

{
  int i;

  if(!problem_list) {
    ERRPRINTF("Invalid arg: null problem_list\n");
    return -1;
  }

  gs_httpd_reply(sockfd, GS_BEGIN_PROBLEM_LIST);

  for(i = 0; i < count; i++) {
    char *prob = NULL;
    if(gs_encode_problem(&prob, problem_list[i]) < 0) {
      FREE(prob);
      ERRPRINTF("Failed to send problem list\n");
      return -1;
    }

    gs_httpd_reply(sockfd, prob);
    gs_httpd_reply(sockfd, "\n");

    FREE(prob);
  }

  gs_httpd_reply(sockfd, GS_END_PROBLEM_LIST);

  return 0;
}

Here is the call graph for this function:

Here is the caller graph for this function:

int gs_send_server_array ( int  sockfd,
gs_server_t **  server_list,
int  count 
)

Send an array of servers on the socket.

Parameters:
sockfd -- the socket to communicate on
server_list -- array of server structures to be sent in xml form
count -- the number of elements in the server_list array
Returns:
0 on success, -1 on failure.

Definition at line 1027 of file gs_agent_httpd.c.

{
  int i;

  if(!server_list) {
    ERRPRINTF("Invalid arg: null server_list\n");
    return -1;
  }

  gs_httpd_reply(sockfd, GS_BEGIN_SERVER_LIST);

  for(i = 0; i < count; i++) {
    char *srv = NULL;
    DBGPRINTF("Encoding server: %s.\n", server_list[i]->hostname);
    if(gs_encode_server(&srv, server_list[i]) < 0) {
      FREE(srv);
      DBGPRINTF("Failed to send server list \n");
      return -1;
    }

    gs_httpd_reply(sockfd, srv);
    gs_httpd_reply(sockfd, "\n");

    FREE(srv);
  }

  gs_httpd_reply(sockfd, GS_END_SERVER_LIST);

  return 0;
}

Here is the call graph for this function:

Here is the caller graph for this function:

int gs_send_task_list ( int  sockfd,
char *  server_cid 
)

Pulls all the task information out of the database and sends it back as xml-like document.

Parameters:
sockfd -- the socket to communicate on
server_cid -- the server whose tasks we should return
Returns:
0 on success, -1 on failure.

Definition at line 1243 of file gs_agent_httpd.c.

{
  gs_htm_task **tasks = NULL;
  int i, count;
  char *info;

  gs_get_tasks_for_server(server_cid, &tasks, &count, 1);

  if(count < 0) {
    ERRPRINTF("failed to get list of all tasks\n");
    return -1;
  }

  if(!tasks) {
    ERRPRINTF("Empty task list despite non-negative return value!\n");
    return -1;
  }

  for(i=0;i<count;i++) {
    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",
      server_cid, tasks[i]->id, tasks[i]->start, tasks[i]->duration, tasks[i]->remaining,
      tasks[i]->end, tasks[i]->active, tasks[i]->finished);

    free(tasks[i]);

    if(info) {
      gs_httpd_reply(sockfd, GS_BEGIN_TASK_INFO);

      gs_httpd_reply(sockfd, info);

      free(info);

      gs_httpd_reply(sockfd, GS_END_TASK_INFO);
    }
  }

  free(tasks);

  return 0;
}

Here is the call graph for this function:

Here is the caller graph for this function:

char* gs_xmlize ( const char *  msg  ) 

This funciton "XMLizes" a string. The input string is already in an xml-like format, but may have <, >, or & embedded in the attributes. This function only removes those characters that are in double-quoted attribute values. they are replaced with <, >, and &.

Parameters:
msg -- the string to "XMLize"
Returns:
the "XMLized" message.

Definition at line 1126 of file gs_agent_httpd.c.

{
  char *xmlized_msg, *xp;
  int inside_string;
  const char *op;

  if(!msg) return NULL;

  /* in the worst case, the string will be 5 times longer, so
   * rather than constantly checking whether we need to realloc,
   * just alloc 5 * strlen(msg) here.
   */

  xmlized_msg = (char *)malloc(5 * strlen(msg));
  if(!xmlized_msg)
    return NULL;

  inside_string = FALSE;

  for(op = msg, xp = xmlized_msg; *op != '\0'; op++) {
    if(*op == '"') {
      inside_string = inside_string ? FALSE : TRUE;
      *xp++ = *op;
      continue;
    }

    if(inside_string) {
      switch(*op) {
        case '&':
          strcpy(xp, "&amp;");
          xp += strlen("&amp;");
          break;
        case '<':
          strcpy(xp, "&lt;");
          xp += strlen("&lt;");
          break;
        case '>':
          strcpy(xp, "&gt;");
          xp += strlen("&gt;");
          break;
        default:
          *xp++ = *op;
      }
    }
    else
      *xp++ = *op;
  }

  *xp = '\0';

  return xmlized_msg;
}

Here is the caller graph for this function:

void sig_chld ( int  signo  ) 

Handler for SIGCHLD. calls waitpid() to get rid of zombies.

Parameters:
signo -- the signal caught (SIGCHLD)

Definition at line 231 of file gs_agent_httpd.c.

{
  pid_t pid;
  int stat;

  while ((pid = waitpid(-1, &stat, WNOHANG)) > 0)
    DBGPRINTF("child %d terminated\n", (int)pid);

  return;
}

Here is the caller graph for this function:

void unescape_url ( char *  str  ) 

Remove percent escape sequences from the URL.

Parameters:
str -- string containing the URL. This is modified in place to contain the unescaped URL.

Definition at line 288 of file gs_agent_httpd.c.

{
  char tmp[3] = {0, 0, 0}; 
  int unescaped_char;
  int i, idx;

  if(!str) return;

  idx = 0;

  for(i=0;i<strlen(str);i++) {
    memset(tmp, 0, 3);

    if(str[i] == '%') {
      if(str[i+1] != 0) {
        tmp[0] = str[i+1];

        if(str[i+2] != 0) {
          tmp[1] = str[i+2];
          i += 2;
        }
        else
          i++;
      }

      sscanf(tmp, "%x", &unescaped_char);
    }
    else
      unescaped_char = str[i];

    str[idx++] = unescaped_char;
  }

  str[idx] = 0;
}

Here is the caller graph for this function:


Variable Documentation

Definition at line 50 of file agent.c.

char* gs_content_types[][2]
Initial value:
 {
  {"xsl", "application/xml"},
  {"html", "text/html"},
  {"xml", "application/xml"},
  {"ico", "image/x-ico"},
  {"avi", "video/avi"},
  {"md5", "text/md5"},
  {"srt", "text/srt"},
  {NULL, "text/html"}
}

Definition at line 15 of file gs_agent_httpd.c.

gs_agent_t* gs_httpd_agent_ptr = NULL

Definition at line 26 of file gs_agent_httpd.c.