Defines | Functions | Variables

netsolveclient.c File Reference

#include "grpc.h"
#include "netsolveclient.h"
#include "utility.h"
Include dependency graph for netsolveclient.c:

Go to the source code of this file.

Defines

#define va_copy(dst, src)   memcpy (&dst, &src, sizeof(va_list))

Functions

int netslX (char *, va_list, int, int, int)
int netslset_criteria_file (char *criteria_file)
int netsl_profile (NS_profile *prof)
int netslset_criteria (char *criteria)
int netslinit (char *agent)
int netsl (char *nickname,...)
int netslnb (char *nickname,...)
int netslmajor (char *maj)
int netslwt (int request_id)
int netslpr (int request_id)
int netslkill (int request_id)
void netslerr (int status)
int netsl_assignment (char *nickname,...)
int netslnb_assignment (char *nickname,...)
char * netsolveErrorMessage (int e)
int * netsl_farm (char *iteration,...)
NS_Iterator * ns_int (char *s)
NS_Iterator * ns_int_array (int *array, char *expression)
NS_Iterator * ns_ptr_array (void **array, char *expression)

Variables

int ns_errno = NetSolveOK
static int ns_initialized = FALSE
static char * ns_criteria = NULL
NS_profile * ns_profile = NULL
grpc_error_t grpc_errno
int grpc_error_mapping []

Detailed Description

This file contains a C client calls to NetSolve.

Definition in file netsolveclient.c.


Define Documentation

#define va_copy ( dst,
src   )     memcpy (&dst, &src, sizeof(va_list))

Definition at line 25 of file netsolveclient.c.


Function Documentation

int netsl ( char *  nickname,
  ... 
)

This function is used to make NetSolve calls from a C client in a blocking fashion.

Parameters:
nickname -- Specifies the name of the problem that needs to be solved This should have the form "problemname()". Remember the parens.
... -- Variable length arguments to accomodate the variable arguments required by the problem.
Returns:
NetSolveOK on success, errno on failure

Definition at line 239 of file netsolveclient.c.

{
  va_list argptr;

  va_start(argptr, nickname);

  grpc_set_default_major("ROW");

  return netslX(nickname, argptr, GS_CALL_FROM_C, NS_BLOCK, NS_NOASSIGNMENT);
}

Here is the call graph for this function:

int netsl_assignment ( char *  nickname,
  ... 
)

This function is used to make 'assigned-server' NetSolve calls from a C client in a blocking fashion.

Parameters:
nickname -- Specifies the name of the problem that needs to be solved as well as the name of the server that should be used. The server name is encoded with the problem nickname as follows: "servername:problemname()".
... -- Variable length arguments to accomodate the variable arguments required by the problem.
Returns:
NetSolveOK on success, errno on failure

Definition at line 583 of file netsolveclient.c.

{
  va_list argptr;

  va_start(argptr, nickname);

  grpc_set_default_major("ROW");

  return netslX(nickname, argptr, GS_CALL_FROM_C, NS_BLOCK, NS_ASSIGNMENT);
}

Here is the call graph for this function:

int* netsl_farm ( char *  iteration,
  ... 
)

Definition at line 791 of file netsolveclient.c.

{
  va_list argptr;
  int start,end;
  char *buf;
  int i, len; 
  int *returned_value; 
  grpc_error_t rv;

  buf = strdup(iteration); 

  /* get the <start> end the <end> */
  if (sscanf(buf,"i=%d,%d",&start,&end) != 2)
  {
    free(buf);
    returned_value = (int *)calloc(1,sizeof(int));
    returned_value[0] =  -1;
    return returned_value;
  }
  free(buf); 
  
  /* Getting the iteration */
  va_start(argptr,iteration); 

  /* Get the problem name */
  buf = strdup((char*)va_arg(argptr, void*));

  /* Stripping off the parenthesis */
  len = strlen(buf);
  if (buf[len-2] != '(')
  {
    free(buf);
    returned_value = (int *)calloc(1,sizeof(int));
    returned_value[0] = -1;
    return returned_value;
  }

  if (buf[len-1] != ')')
  {
    free(buf);
    returned_value = (int *)calloc(1,sizeof(int));
    returned_value[0] = -1;
    return returned_value;
  }
  buf[len-2] = '\0'; 

   /* Initializing grpc    */
  rv = grpc_initialize(NULL);

  if((rv != GRPC_NO_ERROR) && (rv != GRPC_ALREADY_INITIALIZED)) {
    returned_value = (int *)calloc(1,sizeof(int));
    returned_value[0] = -1;
    return returned_value;
  }

  /* Initializing the major    */
  if(grpc_set_client_major("Row") != GRPC_NO_ERROR){
    returned_value = (int *)calloc(1,sizeof(int));
    returned_value[0] = -1;
    return returned_value;
  }

  returned_value = grpc_farming(start,end,buf,argptr);

  if(returned_value[0] != GRPC_NO_ERROR) {
    returned_value[0] = -1;
    for(i=1; i < (end - start + 1); i++)
      returned_value[i] = grpc_error_mapping[returned_value[i]];
  }

  return returned_value;  
}

Here is the call graph for this function:

int netsl_profile ( NS_profile *  prof  ) 

Enable profiling of subsequent requests.

Parameters:
prof -- pointer to profiling structure to be filled in
Returns:
0 on success, -1 on failure

Definition at line 114 of file netsolveclient.c.

{
#ifdef NS_PROFILING
  ns_profile = prof;
  return 0;
#else

#ifdef DEBUG
  fprintf(STDERR"Warning: netsl_profile() -- profiling not enabled.\n");
#endif

  return -1;
#endif
}

Here is the call graph for this function:

void netslerr ( int  status  ) 

This function is used to print the error message associated with a NetSolve return value. prints to stderr.

Parameters:
status -- Specifies the status of a previous call to netsolve

Definition at line 563 of file netsolveclient.c.

{
  fprintf(stderr, "NS:%s\n", netsolveErrorMessage(status));
}

Here is the call graph for this function:

int netslinit ( char *  agent  ) 

This function initializes the NetSolve system. It is called before any requests are made so the user does not have to explicitly call this function. However, this function can be used optionally by clients to set the agent to be used. Normally the environment variable NETSOLVE_AGENT is used to specify the agent.

Parameters:
agent -- The agent that will be contacted. If NULL the agent is derived from the environment variable
Returns:
NetSolveOK on success, errno on failure

Definition at line 175 of file netsolveclient.c.

{
  grpc_error_t rv;

  /* Get the agent name */
  if(agent == NULL) {           
    /* Get it from the environment.  Use GRIDSOLVE_AGENT first
     * if specified.  Otherwise, fall back on NETSOLVE_AGENT.
     */

    agent = getenv("GRIDSOLVE_AGENT");
    if(!agent) {
      agent = getenv("NETSOLVE_AGENT");
      if(!agent) {
        ns_errno = NetSolveSetNetSolveAgent;
        return ns_errno;
      }
    }
  }

  if(!getenv("GRIDSOLVE_AGENT")) {
    char *agent_env;

    /* store the user supplied agent name in the environment so that it'll be 
       available to the gridrpc client. */

    agent_env = dstring_sprintf("GRIDSOLVE_AGENT=%s", agent);

    if(!agent_env) {
      ns_errno = NetSolveSystemError;
      return ns_errno;
    }

    if(putenv(agent_env)) {
      ns_errno = NetSolveSystemError;
      return ns_errno;
    }
  }

  rv = grpc_initialize(NULL);

  if((rv != GRPC_NO_ERROR) && (rv != GRPC_ALREADY_INITIALIZED)) {
    ns_errno = grpc_error_mapping[grpc_errno];
    return ns_errno;
  }

  ns_initialized = TRUE;

  return NetSolveOK;
}

Here is the call graph for this function:

Here is the caller graph for this function:

int netslkill ( int  request_id  ) 

This function is used to in conjuction with the non-blocking call to cancel a previously submitted asynchronous request. It notifies the server that the request has been cancelled by the user.

Parameters:
request_id -- Specifies the id of the previous non-blocking request to be killed.
Returns:
NetSolveOK on success, errno on failure

Definition at line 529 of file netsolveclient.c.

{
  grpc_function_handle_t *handle;

  if(grpc_get_handle(&handle, request_id) != GRPC_NO_ERROR) {
    ns_errno = NetSolveInvalidRequestID;
    return ns_errno;
  }

  if(grpc_cancel(request_id) != GRPC_NO_ERROR) {
    if(grpc_errno == GRPC_NOT_INITIALIZED)
      ns_errno = NetSolveInvalidRequestID;
    else
      ns_errno = grpc_error_mapping[grpc_errno];
  }
  else
    ns_errno = NetSolveOK;

  if(handle) {
    grpc_function_handle_destruct(handle);
    free(handle);
  }

  return ns_errno;
}

Here is the call graph for this function:

int netslmajor ( char *  maj  ) 

This routine sets the major (row- or column-wise) used by the client.

Parameters:
maj -- Takes as input "[Rr]*" or "[Cc]*"
Returns:
On success returns 1 and sets ns_errno to NetSolveOK. On failure, returns -1 and sets ns_errno to NetSolveInvalidMajor.

Definition at line 444 of file netsolveclient.c.

{
  if(!ns_initialized)
    if(netslinit(NULL) != NetSolveOK)
      return ns_errno;

  if(grpc_set_client_major(maj) != GRPC_NO_ERROR) {
    ns_errno = NetSolveInvalidMajor;
    return -1;
  }
  ns_errno = NetSolveOK;
  return 1;
}

Here is the call graph for this function:

Here is the caller graph for this function:

int netslnb ( char *  nickname,
  ... 
)

This function is used to make NetSolve calls from a C client in a non-blocking fashion. This function will return as soon as the arguments have been sent to the server.

Parameters:
nickname -- Specifies the name of the problem that needs to be solved This should have the form "problemname()". Remember the parens.
... -- Variable length arguments to accomodate the variable arguments required by the problem.
Returns:
NetSolveOK on success, errno on failure

Definition at line 264 of file netsolveclient.c.

{
  va_list argptr;

  va_start(argptr, nickname);

  grpc_set_default_major("ROW");

  return netslX(nickname, argptr, GS_CALL_FROM_C, NS_NOBLOCK,
                NS_NOASSIGNMENT);
}

Here is the call graph for this function:

int netslnb_assignment ( char *  nickname,
  ... 
)

This function is used to make 'assigned-server' NetSolve calls from a C client in a non-blocking fashion.

Parameters:
nickname -- Specifies the name of the problem that needs to be solved as well as the name of the server that should be used. The server name is encoded with the problem nickname as follows: "servername:problemname()".
... -- Variable length arguments to accomodate the variable arguments required by the problem.
Returns:
NetSolveOK on success, errno on failure

Definition at line 609 of file netsolveclient.c.

{
  va_list argptr;

  va_start(argptr, nickname);

  grpc_set_default_major("ROW");

  return netslX(nickname, argptr, GS_CALL_FROM_C, NS_NOBLOCK, NS_ASSIGNMENT);
}

Here is the call graph for this function:

int netslpr ( int  request_id  ) 

This function is used to in conjuction with the non-blocking call to check whether a previously submitted asynchronous request has completed. It does not block, but returns a value signifying whether the call has completed or not.

Parameters:
request_id -- Specifies the id of the previous non-blocking request
Returns:
NetSolveOK on success, errno on failure

Definition at line 507 of file netsolveclient.c.

{
  if(grpc_probe_ft(request_id) == GRPC_NO_ERROR)
    ns_errno = NetSolveOK;
  else
    ns_errno = NetSolveNotReady;

  return ns_errno;
}

Here is the call graph for this function:

Here is the caller graph for this function:

int netslset_criteria ( char *  criteria  ) 

This function can be used optionally by clients to set the criteria specified by this criteria string. The criteria are used by the agent to perform resource selection.

Parameters:
criteria -- This string specifies the client criteria
Returns:
NetSolveOK on success, errno on failure

Definition at line 140 of file netsolveclient.c.

{
  /* free previous criteria */
  if(ns_criteria) free(ns_criteria);

  if(!criteria) {
    ns_criteria = NULL;
    return NetSolveOK;
  }
    
  ns_criteria = strdup(criteria);

  if(!ns_criteria) {
    ns_errno = NetSolveSystemError;
    return ns_errno;
  }

  return NetSolveOK;
}

int netslset_criteria_file ( char *  criteria_file  ) 

This function can be used optionally by clients to set the criteria specified in the criteria file. The criteria are used by the agent to perform resource selection.

Parameters:
criteria_file -- The criteria file specifying client criteria
Returns:
NetSolveOK on success, errno on failure

Definition at line 72 of file netsolveclient.c.

{
  gs_info_t *ca_list, *tmp;

  if(!criteria_file) {
    ns_errno = NetSolveSystemError;
    return ns_errno;
  }

  /* free previous criteria */
  if(ns_criteria) free(ns_criteria);

  gs_parse_config_file(criteria_file, &ca_list);
  for(tmp = ca_list; tmp != NULL; tmp = tmp->next) {
    if(!strcasecmp(tmp->type, "criteria")) {

      ns_criteria = strdup(tmp->value);

      if(!ns_criteria) {
        gs_free_infolist(ca_list);
        ns_errno = NetSolveSystemError;
        return ns_errno;
      }

      gs_free_infolist(ca_list);
      return NetSolveOK;
    }
  }

  gs_free_infolist(ca_list);
  return NetSolveOK;
}

Here is the call graph for this function:

int netslwt ( int  request_id  ) 

This function is used to in conjuction with the non-blocking call to wait for a previously submitted asynchronous request to complete. It blocks until the request has finished.

Parameters:
request_id -- Specifies the id of the previous non-blocking request
Returns:
NetSolveOK on success, errno on failure

Definition at line 469 of file netsolveclient.c.

{
  grpc_function_handle_t *handle;

  if(grpc_get_handle(&handle, request_id) != GRPC_NO_ERROR) {
    ns_errno = NetSolveInvalidRequestID;
    return ns_errno;
  }

  if(grpc_wait_ft(request_id) != GRPC_NO_ERROR) {
    if(grpc_errno == GRPC_NOT_INITIALIZED)
      ns_errno = NetSolveInvalidRequestID;
    else
      ns_errno = grpc_error_mapping[grpc_errno];
  }
  else
    ns_errno = NetSolveOK;

  if(handle) {
    grpc_function_handle_destruct(handle);
    free(handle);
  }

  return ns_errno;
}

Here is the call graph for this function:

Here is the caller graph for this function:

int netslX ( char *  nickname,
va_list  argptr,
int  lang,
int  blocking,
int  assign 
)

This function contains all the common code for making a remote procedure call. This is used internally by both the C and Fortran interfaces (both blocking and non-blocking).

Parameters:
nickname -- Specifies the name of the problem that needs to be solved. This has the form "problemname()". Remember the parens.
argptr -- Specifies the arguments for the problem 'nickname'. This is a va_list which should be created from the variable args.
lang -- Specifies the language. This is either GS_CALL_FROM_C for C or GS_CALL_FROM_FORTRAN for Fortran.
blocking -- If NS_BLOCK, perform a blocking call. If NS_NOBLOCK, perform a non-blocking call.
assign -- Specifies if this call uses the assigned server feature. If NS_ASSIGNMENT, the server to be used is encoded with the problem nickname as follows: "servername:problemname()". If NS_NOASSIGNMENT, the nickname is "problemname()" and the agent selects the server.
Returns:
NetSolveOK on success, errno on failure

Definition at line 298 of file netsolveclient.c.

{
  grpc_function_handle_t *handle;
  gs_va_list argstruct;
  char *grpc_name;
  grpc_error_t rv;
  int namelen;

  if(!nickname) {
    ns_errno = NetSolveBadProblemName;
    return ns_errno;
  }

  handle = (grpc_function_handle_t *) malloc(sizeof(grpc_function_handle_t));
  if(!handle) {
    ns_errno = NetSolveSystemError;
    return ns_errno;
  }

  if(!ns_initialized)
    if(netslinit(NULL) != NetSolveOK) {
      free(handle);
      return ns_errno;
    }

  if(grpc_set_client_language(lang) != GRPC_NO_ERROR) {
    ns_errno = NetSolveSystemError;
    free(handle);
    return ns_errno;
  }

  /* since name must end with "()" it cannot be less than 3 chars long */
  namelen = strlen(nickname);
  if(namelen < 3) {
    ns_errno = NetSolveBadProblemName;
    free(handle);
    return ns_errno;
  }

  /* now check that the "()" is actually there */
  if((nickname[namelen - 2] != '(') || (nickname[namelen - 1] != ')')) {
    ns_errno = NetSolveBadProblemName;
    free(handle);
    return ns_errno;
  }

  grpc_name = strdup(nickname);
  if(!grpc_name) {
    ns_errno = NetSolveSystemError;
    free(handle);
    return ns_errno;
  }
  grpc_name[namelen - 2] = '\0';

  if(assign == NS_ASSIGNMENT) {
    char *assigned_name, *host_part, *func_part;

    assigned_name = strdup(grpc_name);
    if(!assigned_name) {
      free(grpc_name);
      free(handle);
      ns_errno = NetSolveSystemError;
      return ns_errno;
    }

    host_part = strtok(assigned_name, ":");
    func_part = strtok(NULL, ":");

    if(!host_part || !func_part) {
      free(handle);
      free(grpc_name);
      free(assigned_name);
      ns_errno = NetSolveBadProblemName;
      return ns_errno;
    }

    if(grpc_function_handle_init(handle, host_part, func_part) != GRPC_NO_ERROR) {
      free(handle);
      free(grpc_name);
      free(assigned_name);
      ns_errno = NetSolveUnknownProblem;
      return ns_errno;
    }

    free(assigned_name);
  }
  else {
    /* Using the ns extension of grpc_function_handle_default_ns, we delay
     * the actual communication to the agent till the grpc_call. This allows
     * the scheduling (assignment of servers) to be delayed till the client
     * call parameters (N=1000 etc) are known, so these parameters can be
     * used in the scheduling.
     */
    if(grpc_function_handle_default_ns(handle, grpc_name) != GRPC_NO_ERROR) {
      free(handle);
      free(grpc_name);
      ns_errno = NetSolveUnknownProblem;
      return ns_errno;
    }
  }

  free(grpc_name);

  grpc_set_criteria(handle, ns_criteria);

#ifdef NS_PROFILING
  if(ns_profile)
    grpc_profile(ns_profile);
#endif

  va_copy(argstruct.args, argptr);

  if(blocking == NS_BLOCK) {
    rv = grpc_call_valist_ft(handle, &argstruct);
    grpc_function_handle_destruct(handle);
    free(handle);

    ns_errno = (rv == GRPC_NO_ERROR) ? NetSolveOK : grpc_error_mapping[rv];
  }
  else {
    grpc_sessionid_t reqid;

    rv = grpc_call_valist_async_ft(handle, &reqid, &argstruct);

    if(rv != GRPC_NO_ERROR) {
      grpc_function_handle_destruct(handle);
      free(handle);
    }

    ns_errno = (rv != GRPC_NO_ERROR) ? grpc_error_mapping[rv] : reqid;
  }

  return ns_errno;
}

Here is the call graph for this function:

Here is the caller graph for this function:

char* netsolveErrorMessage ( int  e  ) 

This function returns a string description of the specified error return value.

Parameters:
e -- Specifies the NetSolve errno.
Returns:
the string description corresponding to this error.

Definition at line 630 of file netsolveclient.c.

{
  switch (e) {
    case NetSolveBadIterationRange:
      return (" Invalid iteration range");
      break;
    case NetSolveCannotContactAgent:
      return (" Cannot contact agent");
      break;
    case NetSolveProxyError:
      return (" Error while talking to proxy");
      break;
    case NetSolveUnknownServer:
      return (" Unknown server");
      break;
    case NetSolveCannotStartProxy:
      return (" Cannot start proxy");
      break;
    case NetSolveFarmingError:
      return (" One or more requests failed");
      break;
    case NetSolveOK:
      return (" no error");
      break;
    case NetSolveCondorError:
      return (" Condor error");
      break;
    case NetSolveConnectionRefused:
      return (" connection refused");
      break;
    case NetSolveUPFUnsafe:
      return (" UPF security violation");
      break;
    case NetSolveServerError:
      return (" server error");
      break;
    case NetSolveUPFError:
      return (" impossible to compile UPF");
      break;
    case NetSolveCannotBind:
      return (" impossible to bind to port");
      break;
    case NetSolveSystemError:
      return (" system error");
      break;
    case NetSolveUnknownError:
      return (" unknown error");
      break;
    case NetSolveSetNetSolveArch:
      return (" GRIDSOLVE_ARCH not set");
      break;
    case NetSolveSetNetSolveAgent:
      return (" GRIDSOLVE_AGENT not set");
      break;
    case NetSolveSetNetSolveRoot:
      return (" GRIDSOLVE_ROOT not set");
    case NetSolveInternalError:
      return (" internal error");
      break;
    case NetSolveMismatch:
      return (" inconsistent object transfers");
      break;
    case NetSolveUnknownHost:
      return (" Unknown host");
      break;
    case NetSolveInvalidUPFFilename:
      return (" invalid upf filename");
      break;
    case NetSolveNetworkError:
      return (" network error");
      break;
    case NetSolveUnknownProblem:
      return (" unknown problem (Is GRIDSOLVE_AGENT set?)");
      break;
    case NetSolveProtocolError:
      return (" protocol error");
      break;
    case NetSolveNoServer:
      return (" no available server");
      break;
    case NetSolveBadProblemSpecification:
      return (" bad problem input/output");
      break;
    case NetSolveNotAllowed:
      return (" not allowed");
      break;
    case NetSolveBadValues:
      return (" bad input values");
      break;
    case NetSolveDimensionMismatch:
      return (" dimension mismatch");
      break;
    case NetSolveNoSolution:
      return (" no solution");
      break;
    case NetSolveNotReady:
      return (" not ready");
      break;
    case NetSolveInvalidRequestID:
      return (" invalid request ID");
      break;
    case NetSolveBadProblemName:
      return (" invalid problem name");
      break;
    case NetSolveInvalidMajor:
      return (" invalid major specification");
      break;
    case NetSolveTooManyPendingRequests:
      return (" too many pending requests");
      break;
    case NetSolveFileError:
      return (" file I/O error");
      break;
    case NetSolveUnknownDataFormat:
      return (" unknown machine type");
      break;
    case NetSolveTimedOut:
      return (" operation timed out");
      break;
    case NetSolveUnknownDsiFile:
      return (" DSI file not found");
      break;
    case NetSolveIBPAllocateError:
      return (" error in IBP_Allocate");
      break;
    case NetSolveIBPManageError:
      return (" error in IBP_Manage");
      break;
    case NetSolveIBPLoadError:
      return (" error in IBP_Load");
      break;
    case NetSolveIBPStoreError:
      return (" error in IBP_Store");
      break;
    case NetSolveDsiEACCESS:
      return (" permission denied to DSI file");
      break;
    case NetSolveDsiDisabled:
      return (" NetSolve not configured with DSI");
      break;
    case NetSolveAuthenticationError:
      return (" Authentication to server failed");
      break;
    case NetSolveUnknownHandle:
      return (" Handle not found ");
      break;
    default:
      return (" unknown error");
      break;
  }
}

Here is the caller graph for this function:

NS_Iterator* ns_int ( char *  s  ) 

Definition at line 865 of file netsolveclient.c.

               {
  return grpc_int(s);
}

Here is the call graph for this function:

NS_Iterator* ns_int_array ( int *  array,
char *  expression 
)

Definition at line 871 of file netsolveclient.c.

                                         {
 return grpc_int_array(array,expression);
}

Here is the call graph for this function:

NS_Iterator* ns_ptr_array ( void **  array,
char *  expression 
)

Definition at line 876 of file netsolveclient.c.

                                            {
  return grpc_ptr_array(array,expression);
}

Here is the call graph for this function:


Variable Documentation

grpc_error_t grpc_errno

Global variables

Definition at line 219 of file gsgrpc.c.

Initial value:
 {
  NetSolveOK,
  NetSolveInternalError,
  NetSolveFileError,
  NetSolveFileError,
  NetSolveNoServer,
  NetSolveUnknownProblem,
  NetSolveUnknownProblem,
  NetSolveInvalidRequestID,
  NetSolveConnectionRefused,
  NetSolveNetworkError,
  NetSolveServerError,
  NetSolveNotReady,
  NetSolveNotReady,
  NetSolveInternalError,
  NetSolveInternalError,
  NetSolveInternalError
}

This is a mapping from the errors returned by the GridRPC client to the errors we want to return to the NetSolve client.

Definition at line 42 of file netsolveclient.c.

char* ns_criteria = NULL [static]

Definition at line 32 of file netsolveclient.c.

int ns_errno = NetSolveOK

Definition at line 29 of file netsolveclient.c.

int ns_initialized = FALSE [static]

Definition at line 31 of file netsolveclient.c.

NS_profile* ns_profile = NULL

Definition at line 33 of file netsolveclient.c.