Defines | Functions

server_main.c File Reference

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include "utility.h"
#include "server.h"
#include "comm_basics.h"
#include "comm_encode.h"
#include "comm_data.h"
#include "comm_protocol.h"
#include "mfork.h"
Include dependency graph for server_main.c:

Go to the source code of this file.

Defines

#define GS_SERVER_USAGE_STR   "Usage: GS_server [-l logfile] [-c] [-s server config]"

Functions

int gs_server_verify_connectivity (gs_server_t *server, int serversocket)
int gs_server_parse_cmd_line (int argc, char **argv, char *gridsolve_root, char **logfile, int *daemon, char **srv_cfg)
static void server_signal_handler (int sig)
int main (int argc, char **argv)

Detailed Description

This file contains a main() driver for the server, parsing the command line arguments and starting up the deamons. This main routine was seperated from the rest of the server code, so that the rest of the server code can be compiled as a library and used by other parts of GridSolve (e.g. the comm routines can manipulate the server data structures using the server library).

Definition in file server_main.c.


Define Documentation

#define GS_SERVER_USAGE_STR   "Usage: GS_server [-l logfile] [-c] [-s server config]"

Function Documentation

int gs_server_parse_cmd_line ( int  argc,
char **  argv,
char *  gridsolve_root,
char **  logfile,
int *  daemon,
char **  srv_cfg 
)

Parse the command line for flags passed to the server. Currently the syntax is: GS_server -c [-l logfile] [-s config]

Parameters:
argc -- arg count
argv -- array of arguments
gridsolve_root -- root of the GridSolve installation
logfile -- if a -l arg is seen this is set to the name of the log file upon return, otherwise it is set to NULL. The user need not allocate any memory.
daemon -- set TRUE upon return if this should be run as a daemon process, FALSE otherwise.
Returns:
0 if the args parsed successfully, -1 otherwise.

Definition at line 134 of file server_main.c.

{
  int c;

  *logfile = NULL;
  *daemon = 1;
  *srv_cfg = NULL;

  /* when making changes to the command line args, update
   * GS_SERVER_USAGE_STR so the usage information is printed
   * correctly upon error.
   */

#define GS_SERVER_USAGE_STR "Usage: GS_server [-l logfile] [-c] [-s server config]"

  while((c = getopt(argc,argv,"cl:s:")) != EOF) {
    switch(c) {
      case 'l':
        *logfile = strdup(optarg);
        break;
      case 's':
        *srv_cfg = strdup(optarg);
        break;
      case 'c':
        *daemon = 0;
        break;
      case '?':
        return -1;
        break;
      default:
        ERRPRINTF("Bad arg: '%c'.\n",c);
        return -1;
    }
  }

  /* Setup default log file name if necessary */
  if(!*logfile) {
    *logfile = dstring_sprintf("%s/gs_server.log", gridsolve_root);
    if(!*logfile) {
      fprintf(stderr,"Error generating log file name.\n");
      exit(EXIT_FAILURE);
    }
  }

  /* Setup default server config file name if necessary */
  if(!*srv_cfg) {
    *srv_cfg = dstring_sprintf("%s/server_config", gridsolve_root);
    if(!*srv_cfg) {
      fprintf(stderr,"Error generating server config file name.\n");
      exit(EXIT_FAILURE);
    }
  }

  return 0;
}

Here is the call graph for this function:

Here is the caller graph for this function:

int gs_server_verify_connectivity ( gs_server_t *  server,
int  serversocket 
)

Checks whether this server can be contacted by the agent as a simple test of network connectivity. this is mainly used for rejecting servers that should be proxied, but aren't.

Parameters:
server -- the server struct for this server
Returns:
0 if the args parsed successfully, -1 otherwise.

Definition at line 40 of file server_main.c.

{
  int sock_agent, tag;
  char *reqstr = NULL;

  sock_agent = gs_connect_direct(server->agenthost, server->agentport);

  if(sock_agent < 0) {
    ERRPRINTF("Unable to connect to agent.\n");
    return -1;
  }

  if((gs_send_tag(sock_agent, GS_PROT_AVAILABILITY_REQ) < 0) ||
     (gs_send_string(sock_agent, VERSION) < 0)) {
    ERRPRINTF("failed to send tag\n");
    gs_close_socket(sock_agent);
    return -1;
  }

  if(gs_recv_tag(sock_agent, &tag) < 0) {
    ERRPRINTF("Error communicating with agent.\n");
    gs_close_socket(sock_agent);
    return -1;
  }

  if(tag != GS_PROT_OK) {
    if(tag == GS_PROT_VERSION_MISMATCH)
      ERRPRINTF("Error: Agent is an incompatible version\n");
    else
      ERRPRINTF("Error: Agent refused with code %d\n", tag);
    gs_close_socket(sock_agent);
    return -1;
  }

  if(gs_encode_availability_request(&reqstr, server) < 0) {
    ERRPRINTF("Could not encode availability request.\n");
    return -1;
  }
    
  if(gs_send_string(sock_agent, reqstr) < 0) {
    ERRPRINTF("failed to send string\n");
    if(reqstr) free(reqstr);
    gs_close_socket(sock_agent);
    return -1;
  }

  gs_close_socket(sock_agent);
  free(reqstr);

  printf("Performing connectivity test.  If it hangs at this point\n");
  printf("and you are behind a NAT, check your GRIDSOLVE_PROXY\n");
  printf("environment variable.  If you are behind a firewall,\n");
  printf("check your port settings.\n");

  if((sock_agent = gs_accept_connection(serversocket)) == -1) {
    ERRPRINTF("Failed to accept connection on socket\n");
    return -1;
  }

  printf("\n\nConnectivity test successful.\n");

  if(gs_recv_tag(sock_agent, &tag) < 0) {
    ERRPRINTF("Failed to read tag.\n");
    return -1;
  }

  gs_close_socket(sock_agent);

  if(tag != GS_PROT_OK) {
    ERRPRINTF("Agent rejected this server (tag = %d).\n", tag);
    return -1;
  }

  return 0;
}

Here is the call graph for this function:

Here is the caller graph for this function:

int main ( int  argc,
char **  argv 
)

Main routine for the server. Checks to see if the appropriate environment variables are set, handle the log file, initialize server structures, make this server process into a daemon, bind server to port, fork the workload reporter, and then call routine to handle incoming messages.

Parameters:
argc -- arg count
argv -- array of arguments
Returns:
EXIT_SUCCESS or EXIT_FAILURE

Definition at line 219 of file server_main.c.

{
  gs_server_t *gs_server = NULL;
  int serversocket = -1, daemon;
  void **wr_args;
  pid_t pid;
  char *logfile, *gridsolve_root, *srv_cfg;

  if(gs_signal(SIGTERM, server_signal_handler) == SIG_ERR)
    ERRPRINTF("Warning: could not set up SIGTERM signal handler\n");
  if(gs_signal(SIGINT, server_signal_handler) == SIG_ERR)
    ERRPRINTF("Warning: could not set up SIGINT signal handler\n");

  /* Check environment */
  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(".");
  }

  /* Parse command line args */
  if(gs_server_parse_cmd_line(argc, argv, gridsolve_root, &logfile, 
       &daemon, &srv_cfg) < 0) {
    fprintf(stderr, "%s\n", GS_SERVER_USAGE_STR);
    exit(EXIT_FAILURE);
  }

  /* Make current process into a daemon */
  if(daemon && gs_daemon_init(gridsolve_root, logfile) < 0) {
    fprintf(stderr, "Failed to start server as a daemon.\n");
    exit(EXIT_FAILURE);
  }

  /* Call the server initialization code */
  if ((gs_server = gs_server_init(srv_cfg)) == NULL) {
    fprintf(stderr, "Failed to initialize the server.\n");
    exit(EXIT_FAILURE);
  }
  
#ifdef GS_DEBUG
  gs_server_dump(gs_server);
#endif

  /* Bind server to a port.  gs_server->port should be set in gs_server_init */
  serversocket = gs_establish_socket(&(gs_server->port), 0);

  if(serversocket == -1) {
    ERRPRINTF("Could not bind to port %d\n", gs_server->port);
    exit(EXIT_FAILURE);
  }

  DBGPRINTF("Bound to port %d\n\n", gs_server->port);
  proxy_print_local_info();

  if(gs_listen_on_socket(serversocket) < 0) {
    ERRPRINTF("Failed to listen on server socket.\n");
    exit(EXIT_FAILURE);
  }

  if(gs_server_verify_connectivity(gs_server, serversocket) < 0) {
    fprintf(stderr, "Connectivity test failed.  If you are behind a NAT,\n");
    fprintf(stderr, "check your GRIDSOLVE_PROXY settings.\n");
    exit(EXIT_FAILURE);
  }

  /* Register the server with the agent.  Cannot do this earlier, because
   * the listening port is not known until now
   */
  if(gs_server_register(gs_server) < 0) {
    ERRPRINTF("Failed to register with the agent.\n");
    return -1;
  }

  /* Register the problems with the agent */
  if(gs_server_register_problems(gs_server) < 0) {
    ERRPRINTF("Failed to register my problems with the agent.\n");
    return -1;
  }

  wr_args = (void **)malloc(sizeof(void *));
  if(!wr_args) {
    ERRPRINTF("Failed to allocate memory for child args\n");
    exit(EXIT_FAILURE);
  }

  wr_args[0] = gs_server;

  /* Monitored fork.  Forks a child processes to repeatedly run the
   * routine gs_workload_report.  
   */
  pid = mfork(gs_workload_report, GS_UPDATE_FREQUENCY, wr_args, 
    NULL, NULL, NULL, 30);

  if (pid < 0) {
    ERRPRINTF("Failed to fork workload manager process.\n");
    exit(EXIT_FAILURE);
  }

#ifdef GS_SMART_GRIDSOLVE
  if(gs_server->smart) {
    pid = mfork(gs_server_ping, GS_SERVER_PING_FREQUENCY, wr_args, 
      NULL, NULL, NULL, 30);

    if (pid < 0) {
      ERRPRINTF("Failed to fork server ping process.\n");
      exit(EXIT_FAILURE);
    }
  }
#endif

  /* Run the actual routine which listens for and handles messages */
  if (gs_server_listen_and_process_messages(gs_server, serversocket) < 0) 
    exit(EXIT_FAILURE);
  else 
    exit(EXIT_SUCCESS);
}

Here is the call graph for this function:

static void server_signal_handler ( int  sig  )  [static]

Signal handler for SIGTERM. Exit with normal status.

Parameters:
sig -- the signal that was caught

Definition at line 198 of file server_main.c.

{
  ERRPRINTF("Server terminating on signal %d...\n", sig);
  unlink(GS_SERVER_JOB_COUNT_FILE);
  exit(0);
}

Here is the call graph for this function:

Here is the caller graph for this function: