Logo Search packages:      
Sourcecode: condor version File versions  Download package

chirp_client.c

/***************************************************************
 *
 * Copyright (C) 1990-2007, Condor Team, Computer Sciences Department,
 * University of Wisconsin-Madison, WI.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); you
 * may not use this file except in compliance with the License.  You may
 * obtain a copy of the License at
 * 
 *    http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 ***************************************************************/

/*
Chirp C Client
*/

#include "chirp_protocol.h"
#include "chirp_client.h"

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <stdarg.h>
#include <ctype.h>

/* Sockets */
#if defined(WIN32)
      #include <winsock2.h>
      #include <windows.h>
      #include <io.h>
      #include <fcntl.h>
#else
      #define SOCKET int
      #define SOCKET_ERROR   (-1)
      #define INVALID_SOCKET (-1)
      #define closesocket close
      #include <unistd.h>
      #include <sys/errno.h>
      #include <sys/socket.h>
      #include <netdb.h>
      #include <netinet/in.h>
      #include <netinet/tcp.h>
#endif

static SOCKET tcp_connect( const char *host, int port );
static void chirp_fatal_request( const char *name );
static void chirp_fatal_response(void);
static int get_result( FILE *s );
static int convert_result( int response );
static int simple_command(struct chirp_client *c,char const *fmt,...);
static void vsprintf_chirp(char *command,char const *fmt,va_list args);
static char const *read_url_param(char const *url,char *buffer,size_t length);

static int sockets_initialized = 0;
static int initialize_sockets(void);
//static int shutdown_sockets();


struct chirp_client {
      FILE *rstream;
      FILE *wstream;
};

/*
  chirp_client_connect_url()

  Sets path_part to the position of the start of the path information
  in the URL and connects to the specified Chirp server.

  URL format:
    chirp:host.name:port/path   (absolute path)
    chirp:host.name:port./path  (relative path)
    chirp:/path                 (absolute path)
    chirp:./path                (relative path)
    chirp:path                  (sloppy relative path)

  Note that the initial part of a sloppy relative path can be confused
  for a host:port specification if it happens to look like one.  Example:
  'chirp:strange:1/file'.  For this reason, it is better to use one of
  the non-sloppy formats.

  In all of the above URL formats, any number of extra connection
  parameters may be inserted before the path part (including  the leading
  '/' or '.').  These are of the form ";parameter=value" where parameter
  and value are encoded using the standard Mime type
  application/x-www-form-url encoded, just like the parameters in a typical
  HTTP GET CGI request, but using ';' instead of '&' as the delimiter.

  At this time, no connection parameters are defined, and any that
  are supplied are simply ignored.

*/


static int
initialize_sockets()
{
#if defined(WIN32)
      int err;
      WORD wVersionRequested;
      WSADATA wsaData;
#endif

      if(sockets_initialized)
            return 1;

#if defined(WIN32)
      wVersionRequested = MAKEWORD( 2, 0 );
      err = WSAStartup( wVersionRequested, &wsaData );
      if(err)
            return 0;

      if(LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 0) 
      {
            WSACleanup();
            return 0;
      }
#endif

      sockets_initialized = 1;
      return 1;
}

/* This function doesn't seem to be used anywhere. */
#if 0
static int 
shutdown_sockets()
{
      if(!sockets_initialized)
            return 1;

#if defined(WIN32)
      WSACleanup();
#endif

      sockets_initialized = 0;
      return 1;
}
#endif

DLLEXPORT struct chirp_client * 
chirp_client_connect_url( const char *url, const char **path_part)
{
      struct chirp_client *client;
      char const *str;
      char *host = NULL;
      int port = 0;

      if(strncmp(url,"chirp:",6)) {
            //bare file name
            *path_part = url;
            return chirp_client_connect_default();
      }

      url += 6; // "chirp:"

      if(*url != '/' && *url != '\\' && *url != ';' && *url != '.' \
         && (str = strchr(url,':')))
      {
            char *end;
            port = strtol(str+1,&end,10);
            if(port && end > str+1 && 
               (*end == '\0' || *end == '/' || *end == '\\' ||
                *end == '.' || *end == ';'))
            {
                  //URL form chirp:host.name:port...
                  //Note that we try to avoid getting here on a "sloppy"
                  //relative path that happens to contain a ':' but
                  //which is not followed by a valid port/path.

                  host = (char *)malloc(str-url+1);
                  strncpy(host,url,str-url);
                  host[str-url] = '\0';

                  url = end;
            }
      }

      while(*url == ';') { //parse connection parameters
            char param[CHIRP_LINE_MAX];
            char value[CHIRP_LINE_MAX];

            url = read_url_param(++url,param,sizeof(param));
            if(!url) {
                  errno = EINVAL;
                  return NULL;
            }

            if(*url == '=') {
                  url = read_url_param(++url,value,sizeof(value));
                  if(!url) {
                        errno = EINVAL;
                        return NULL;
                  }
            }
            else *value = '\0';

            //No connection parameters are defined at this time!
            //Handle them here when they are defined.
      }

      *path_part = url;

      if(!host) { //URL must be in form 'chirp:path'
            client = chirp_client_connect_default();
      }
      else {
            client = chirp_client_connect(host,port);
      }

      free(host);
      return client;
}

DLLEXPORT struct chirp_client *
chirp_client_connect_default()
{
      FILE *file;
      int fields;
      int save_errno;
      struct chirp_client *client;
      char host[CHIRP_LINE_MAX];
      char cookie[CHIRP_LINE_MAX];
      int port;
      int result;

      file = fopen("chirp.config","r");
      if(!file) return 0;

      fields = fscanf(file,"%s %d %s",host,&port,cookie);
      fclose(file);

      if(fields!=3) {
            errno = EINVAL;
            return 0;
      }

      client = chirp_client_connect(host,port);
      if(!client) return 0;

      result = chirp_client_cookie(client,cookie);
      if(result!=0) {
            save_errno = errno;
            chirp_client_disconnect(client);
            errno = save_errno;
            return 0;
      }

      return client;
}

DLLEXPORT struct chirp_client *
chirp_client_connect( const char *host, int port )
{
      struct chirp_client *c;
      int save_errno;
      SOCKET fd;
      int osfh;

      c = (struct chirp_client*)malloc(sizeof(*c));
      if(!c) return 0;

      fd = tcp_connect(host,port);
      if(fd == INVALID_SOCKET) {
            save_errno = errno;
            free(c);
            errno = save_errno;
            return 0;
      }


#if defined(WIN32)
      // This allows WinNT to get a file handle from a socket
      // Note: this will not work on win95/98
      osfh = _open_osfhandle(fd, _O_RDWR | _O_BINARY);
      if(osfh < 0) {
          closesocket(fd);
          return 0;
      }
#else
      osfh = fd;
#endif


      c->rstream = fdopen(osfh,"r");
      if(!c->rstream) {
            save_errno = errno;
            closesocket(fd);
            free(c);
            errno = save_errno;
            return 0;
      }

      c->wstream = fdopen(osfh,"w");
      if(!c->wstream) {
            save_errno = errno;
            fclose(c->rstream);
            closesocket(fd);
            free(c);
            errno = save_errno;
            return 0;
      }

      setbuf(c->rstream, NULL);
      setbuf(c->wstream, NULL);
      //setvbuf( c->rstream, NULL, _IONBF, 0 );
      //setvbuf( c->rstream, NULL, _IONBF, 0 );

      return c;
}

DLLEXPORT void
chirp_client_disconnect( struct chirp_client *c )
{
      fclose(c->rstream);
      fclose(c->wstream);
      free(c);
}

DLLEXPORT int
chirp_client_cookie( struct chirp_client *c, const char *cookie )
{
      return simple_command(c,"cookie %s\n",cookie);
}

DLLEXPORT int
chirp_client_login( struct chirp_client *c, const char *name, const char *password )
{
      return simple_command(c,"login %s %s\n",name,password);
}

DLLEXPORT int
chirp_client_lookup( struct chirp_client *c, const char *logical_name, char **url )
{
      int result;
      int actual;

      result = simple_command(c,"lookup %s\n",logical_name);
      if(result>0) {
            *url = (char*)malloc(result);
            if(*url) {
                  actual = fread(*url,1,result,c->rstream);
                  if(actual!=result) chirp_fatal_request("lookup");
            } else {
                  chirp_fatal_request("lookup");
            }
      }

      return result;
}

DLLEXPORT int 
chirp_client_constrain( struct chirp_client *c, const char *expr)
{
      return simple_command(c,"constrain %s\n",expr);
}

DLLEXPORT int
chirp_client_get_job_attr( struct chirp_client *c, const char *name, char **expr )
{
      int result;
      int actual;

      result = simple_command(c,"get_job_attr %s\n",name);
      if(result>0) {
            *expr = (char*)malloc(result);
            if(*expr) {
                  actual = fread(*expr,1,result,c->rstream);
                  if(actual!=result) chirp_fatal_request("get_job_attr");
            } else {
                  chirp_fatal_request("get_job_attr");
            }
      }

      return result;
}

DLLEXPORT int
chirp_client_set_job_attr( struct chirp_client *c, const char *name, const char *expr )
{
      return simple_command(c,"set_job_attr %s %s\n",name,expr);
}

DLLEXPORT int
chirp_client_open( struct chirp_client *c, const char *path, const char *flags, int mode )
{
      return simple_command(c,"open %s %s %d\n",path,flags,mode);
}

DLLEXPORT int
chirp_client_close( struct chirp_client *c, int fd )
{
      return simple_command(c,"close %d\n",fd);
}

DLLEXPORT int
chirp_client_read( struct chirp_client *c, int fd, void *buffer, int length )
{
      int result;
      int actual;

      result = simple_command(c,"read %d %d\n",fd,length);

      if( result>0 ) {
            actual = fread(buffer,1,result,c->rstream);
            if(actual!=result) chirp_fatal_request("read");
      }

      return result;
}

DLLEXPORT int
chirp_client_write( struct chirp_client *c, int fd, const void *buffer, int length )
{
      int actual;
      int result;

      result = fprintf(c->wstream,"write %d %d\n",fd,length);
      if(result<0) chirp_fatal_request("write");

      result = fflush(c->wstream);
      if(result<0) chirp_fatal_request("write");

      actual = fwrite(buffer,1,length,c->wstream);
      if(actual!=length) chirp_fatal_request("write");

      return convert_result(get_result(c->rstream));
}

DLLEXPORT int
chirp_client_unlink( struct chirp_client *c, const char *path )
{
      return simple_command(c,"unlink %s\n",path);
}

DLLEXPORT int
chirp_client_rename( struct chirp_client *c, const char *oldpath, const char *newpath )
{
      return simple_command(c,"rename %s %s\n",oldpath,newpath);
}
DLLEXPORT int
chirp_client_fsync( struct chirp_client *c, int fd )
{
      return simple_command(c,"fsync %d\n",fd);
}

DLLEXPORT int
chirp_client_lseek( struct chirp_client *c, int fd, int offset, int whence )
{
      return simple_command(c,"lseek %d %d %d\n",fd,offset,whence);
}

DLLEXPORT int
chirp_client_mkdir( struct chirp_client *c, char const *name, int mode )
{
      return simple_command(c,"mkdir %s %d\n",name,mode);
}

DLLEXPORT int
chirp_client_rmdir( struct chirp_client *c, char const *name )
{
      return simple_command(c,"rmdir %s\n",name);
}

DLLEXPORT int
chirp_client_ulog( struct chirp_client *c, char const *message )
{
      return simple_command(c,"ulog %s\n",message);
}


static int
convert_result( int result )
{
      if(result>=0) {
            return result;
      } else {
            switch(result) {

                  case CHIRP_ERROR_NOT_AUTHENTICATED:
                  case CHIRP_ERROR_NOT_AUTHORIZED:
                        errno = EACCES;
                        break;
                  case CHIRP_ERROR_DOESNT_EXIST:
                        errno = ENOENT;
                        break;
                  case CHIRP_ERROR_ALREADY_EXISTS:
                        errno = EEXIST;
                        break;
                  case CHIRP_ERROR_TOO_BIG:
                        errno = EFBIG;
                        break;
                  case CHIRP_ERROR_NO_SPACE:
                        errno = ENOSPC;
                        break;
                  case CHIRP_ERROR_NO_MEMORY:
                        errno = ENOMEM;
                        break;
                  case CHIRP_ERROR_INVALID_REQUEST:
                        errno = EINVAL;
                        break;
                  case CHIRP_ERROR_TOO_MANY_OPEN:
                        errno = EMFILE;
                        break;
                  case CHIRP_ERROR_BUSY:
                        errno = EBUSY;
                        break;
                  case CHIRP_ERROR_TRY_AGAIN:
                        errno = EAGAIN;
                        break;
                  case CHIRP_ERROR_UNKNOWN:
                        chirp_fatal_response();
                        break;
            }
            return -1;
      }
}

static int
get_result( FILE *s )
{
      char line[CHIRP_LINE_MAX];
      char *c;
      int result;
      int fields;

      c = fgets(line,CHIRP_LINE_MAX,s);
      if(!c) chirp_fatal_response();

      fields = sscanf(line,"%d",&result);
      if(fields!=1) chirp_fatal_response();

#ifdef DEBUG_CHIRP
      fprintf(stderr,"chirp received: %s\n",line);
#endif

      return result;
}

static void
chirp_fatal_request( const char *name )
{
      fprintf(stderr,"chirp: couldn't %s: %s\n",name,strerror(errno));
      abort();
}

static
void chirp_fatal_response()
{
      fprintf(stderr,"chirp: couldn't get response from server: %s\n",strerror(errno));
      abort();
}

static SOCKET
tcp_connect( const char *host, int port )
{
      struct hostent *h;
      struct sockaddr_in address;
      int success;
      SOCKET fd;

      if(!initialize_sockets())
            return INVALID_SOCKET;

      h = gethostbyname(host);
      if(!h) return INVALID_SOCKET;

      address.sin_port = htons((unsigned short)port);
      address.sin_family = h->h_addrtype;
      memcpy(&address.sin_addr.s_addr,h->h_addr_list[0],sizeof(address.sin_addr.s_addr));

#if defined(WIN32)
      // Create the socket with no overlapped I/0 so we can later associate the socket
      // with a valid file descripter using _open_osfhandle.
      fd = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, 0);
#else
      fd = socket( AF_INET, SOCK_STREAM, 0 );
#endif
      if(fd == INVALID_SOCKET) return INVALID_SOCKET;

      success = connect( fd, (struct sockaddr *) &address, sizeof(address) );
      if(success == SOCKET_ERROR) {
            closesocket(fd);
            return INVALID_SOCKET;
      }

      return fd;
}

/*
  vsprintf_chirp -- simple sprintf capabilities with character escaping

  The following format characters are interpreted:

  %d -- decimal
  %s -- word (whitespace is escaped)
  %% -- output %
*/

void
vsprintf_chirp(char *command,char const *fmt,va_list args)
{
      char       *c;
      char const *f;

      c = command;
      f = fmt;
      while(*f) {
            if(*f == '%') {
                  switch(*(++f)) {
                  case 'd':
                        f++;
                        sprintf(c,"%d",va_arg(args,int));
                        c += strlen(c);
                        break;
                  case 's': {
                        char const *w = va_arg(args,char const *);
                        f++;
                        while(*w) {
                              switch(*w) {
                              case ' ':
                              case '\t':
                              case '\n':
                              case '\r':
                              case '\\':
                                    *(c++) = '\\';
                                    /*fall through*/
                              default:
                                    *(c++) = *(w++);
                              }
                        }
                        break;
                  }
                  case '%':
                        *(c++) = *(f++);
                        break;
                  default:
                        chirp_fatal_request(f);
                  }
            } else {
                  *(c++) = *(f++);
            }
      }
      *(c++) = '\0';
}

int
simple_command(struct chirp_client *c,char const *fmt,...)
{
      int     result;
      char    command[CHIRP_LINE_MAX];
      va_list args;

      va_start(args,fmt);
      vsprintf_chirp(command,fmt,args);
      va_end(args);

#ifdef DEBUG_CHIRP
      fprintf(stderr,"chirp sending: %s",command);
#endif

      result = fputs(command,c->wstream);
      if(result < 0) chirp_fatal_request(fmt);

      result = fflush(c->wstream);
      if(result == EOF) chirp_fatal_request(fmt);

      return convert_result(get_result(c->rstream));
}

char const *
read_url_param(char const *url,char *buffer,size_t length)
{
      size_t bufpos = 0;

      while(*url != '\0' && *url != '.' && *url != '/'
            && *url != '=' && *url != ';' && *url != '\\')
      {
            if(bufpos >= length) return NULL;   

            switch(*url) {
            case '+':
                  buffer[bufpos++] = ' ';
                  break;
            case '%': { //form-url-encoded escape sequence
                  //following two characters are hex digits
                  char d = tolower(*(++url));

                  if(d >= '0' && d <= '9') d -= '0';
                  else if(d >= 'a' && d <= 'f') d = d - 'a' + 0xA;
                  else return NULL; //invalid hex digit

                  buffer[bufpos] = d<<4;

                  d = tolower(*(++url));

                  if(d >= '0' && d <= '9') d -= '0';
                  else if(d >= 'a' && d <= 'f') d = d - 'a' + 0xA;
                  else return NULL; //invalid hex digit

                  buffer[bufpos++] |= d;

                  break;
            }
            default:
                  buffer[bufpos++] = *url;
                  break;
            }

            url++;
      }

      if(bufpos >= length) return NULL;
      buffer[bufpos] = '\0';

      return url;
}




Generated by  Doxygen 1.6.0   Back to index