Contents

  1. db-server.h
  2. dbdel.h
  3. globals.h
  4. read.h
  5. recdel.h
  6. utils.h
  7. write.h
  8. dbdel.c
  9. dbsrv.c
  10. globals.c
  11. read.c
  12. recdel.c
  13. t.c
  14. utils.c
  15. write.c

db-server.h 1/15

/*
 * QNX demo: Database server
 *
 * (c) Perlaki Attila, Fulep David 1996;  Drotos Daniel 1997
 *
 * Header file to define data types and constants required
 * to write client programs for the database server.
 *
 */

#ifndef DB_SERVER_HEADER
#define DB_SERVER_HEADER

/* This name is used to register the server process */

#define SERVER_NAME "/db-server"

/*
 * Commands understood by the server. This command must be placed
 * in the fix part of the query (see query_rec structure) sent by
 * client to the server.
 *
 */

#define CM_WRITE  'w'	/* Write record to database */
#define CM_STOP   's'	/* Stop the server */
#define CM_READ   'r'	/* Read record from database */
#define CM_RECDEL 'd'	/* Delete record(s) from database */
#define CM_DBDEL  't'	/* Delete database */
#define CM_TEST   'T'	/* Test if server is running */

/*
 * Answers from the server to clients. One of them will be
 * placed in fix part of the answer (see answer_rec structure).
 *
 */

#define RES_RECORD_NOT_FOUND 200   /* Record not found in database */
#define RES_UNKNOWN_COMMAND  201   /* Command is unknown */

#define ERR_CREATE_DB    300       /* Database creation failed */
#define ERR_OPEN_DB      301       /* Opening database failed */
#define ERR_FILE_ERROR   302       /* File manipulation error occured */
#define ERR_READ_ERROR   303       /* Read error occured */
#define ERR_WRITE_ERROR  304       /* Write error occured */


/* Specific data for WRITE query */

struct query_write
{
  int record_size;  /* Size of record to be written */
  int is_mask;      /* Query contains MASK record or not (boolean) */
};

/* Specific data for READ query */

struct query_read
{
  int record_size;  /* Size of record to be read */
  long index;       /* Index of record where the server starts
		       looking for MASK */
};

/* Specific data for RECDEL query */

struct query_recdel
{
  int record_size;  /* Size of records in the database */
};

/*
 * Fix part of query which must be sent as first part of every
 * messages from clients to the server. This record contains
 * some common data for all types of queries and specific parts
 * for different types of queries.
 *
 */

struct query_rec
{
  char command;              /* Command, see CM_XXX constants */
  char db_name[10];          /* name of the database file */
  union
    {
      struct query_write  w; /* Specific data for WRITE, see query_write */
      struct query_read   r; /* Specific data for READ query */
      struct query_recdel d; /* Specific data for RECDEL query */
    };
};


/* Specific data of answer of READ query */

struct answer_read
{
  long index;        /* Index of record matched to MASK, or -1 if
		        no record found */
};

/* Specific data of answer of RECDEL query */

struct answer_recdel
{
  long nuof_deleted; /* Number of records deleted */
};

/* Specific data of answer of TEST query */

struct answer_test
{
  int version_hi;       /* Version number (high) */
  int version_lo;       /* Version number (low) */
  int version_p;        /* Patch level */
  char version_str[10]; /* Version string in 1.2.3 format */
};

/* Structure to hold data of answer */

struct answer_rec
{
  int result;                 /* Result of operation, see RES_XXX and
				 ERR_XXX constants */
  union
    {
      struct answer_read   r; /* Specific data of answer of READ query */
      struct answer_recdel d; /* Specific data of answer of RECDEL query */
      struct answer_test   t; /* Specific data of asnwer of TEST query */
    };
};

#endif

/* End of db-server.h */

dbdel.h 2/15

/*
 * QNX demo: Database server
 *
 * (c) Perlaki Attila, Fulep David 1996;  Drotos Daniel 1997
 *
 */

#ifndef DBDEL_HEADER
#define DBDEL_HEADER

extern int db_write(struct query_rec *query, struct answer_rec *answer);

#endif

/* End of dbdel.h */

globals.h 3/15

/*
 * QNX demo: Database server
 *
 * (c) Drotos Daniel 1997
 *
 */

#ifndef GLOBALS_HEADER
#define GLOBALS_HEADER


extern int debug;


#endif

/* End of globals.h */

read.h 4/15

/*
 * QNX demo: Database server
 *
 * (c) Perlaki Attila, Fulep David 1996;  Drotos Daniel 1997
 *
 */

#ifndef READ_HEADER
#define READ_HEADER

extern int db_read(struct query_rec *query,
		   char *mask,
		   struct answer_rec *answer,
		   char *record);

#endif

/* End of read.h */

recdel.h 5/15

/*
 * QNX demo: Database server
 *
 * (c) Perlaki Attila, Fulep David 1996;  Drotos Daniel 1997
 *
 */

#ifndef RECDEL_HEADER
#define RECDEL_HEADER

extern int db_recdel(struct query_rec *query,
		     char *mask,
		     struct answer_rec *answer);

#endif

/* End of recdel.h */

utils.h 6/15

/*
 * QNX demo: Database server
 *
 * (c) Perlaki Attila, Fulep David 1996;  Drotos Daniel 1997
 *
 */

#ifndef UTILS_HEADER
#define UTILS_HEADER


extern int match(char *what, char *mask, int n);
extern int trunc_file(FILE *f, fpos_t new_size);


#endif

/* End of utils.h */

write.h 7/15

/*
 * QNX demo: Database server
 *
 * (c) Perlaki Attila, Fulep David 1996;  Drotos Daniel 1997
 *
 */

#ifndef WRITE_HEADER
#define WRITE_HEADER

extern int db_write(struct query_rec *query,
		    char *record,
		    char *mask,
		    struct answer_rec *answer);

#endif

/* End of write.h */

dbdel.c 8/15

/*
 * QNX demo: Database server
 *
 * (c) Perlaki Attila, Fulep David 1996;  Drotos Daniel 1997
 *
 * This file contains POSIX part of DBDEL operation.
 *
 */

#include <stdio.h>
#include <errno.h>

#include "globals.h"
#include "db-server.h"


int
db_dbdel(struct query_rec *query, struct answer_rec *answer)
{
  answer->result= 0;
  if (unlink(query->db_name) < 0)
    {
      perror("Deleting database");
      answer->result= ERR_FILE_ERROR;
    }
  return(answer->result);
}


/* End of dbdel.c */

dbsrv.c 9/15

/*
 * QNX demo: Database server
 *
 * (c) Perlaki Attila, Fulep David 1996;  Drotos Daniel 1997
 *
 * QNX part of the database server.
 *
 */

#include <stdio.h>
#include <sys/name.h>
#include <errno.h>
#include <sys/kernel.h>
#include <sys/sendmx.h>
#include <unistd.h>

#include "globals.h"
#include "db-server.h"
#include "write.h"
#include "read.h"


#define VERSION_STR "1.0.0"
#define VERSION_HI   1
#define VERSION_LO   0
#define VERSION_P    0
#define TRUE  1
#define FALSE 0


/*
   Query:   HEADER
            RECORD
            [MASK]
   Answer:  HEADER
*/

int
do_write(pid_t client_pid, struct query_rec *query)
{
  int i;
  char *record, *mask;
  struct answer_rec answer;
  struct _mxfer_entry mx;

  if (debug)
    {
      printf("WRITE command received from %d\n", client_pid);
      printf("Header: db_name= %s\n", query->db_name);
      printf("Header: w.record_size= %d\n", query->w.record_size);
      printf("Header: w.is_mask    = %d\n", query->w.is_mask);
    }
  record= (char *)malloc(query->w.record_size);
  _setmx(&mx, record, query->w.record_size);
  Readmsgmx(client_pid,
	    sizeof(struct query_rec),
	    1,
	    &mx);
  if (debug)
    {
      printf("Record: ");
      for (i=0; i < query->w.record_size; i++)
	printf("%c%02x ", record[i], record[i]);
      printf("\n");
    }
  if (query->w.is_mask)
    {
      mask= (char *)malloc(query->w.record_size);
      _setmx(&mx, mask, query->w.record_size);
      Readmsgmx(client_pid,
		sizeof(struct query_rec) +
		query->w.record_size,
		1,
		&mx);
      if (debug)
	{
	  printf("Mask: ");
	  for (i=0; i < query->w.record_size;i++)
	    printf("%c%02x ", mask[i], mask[i]);
	  printf("\n");
	}
    }
  else
    mask= NULL;

  _setmx(&mx, &answer, sizeof(struct answer_rec));
  i= db_write(query, record, mask, &answer);
  Replymx(client_pid, 1, &mx);
  free(record);
  if (mask)
    free(mask);
  return(answer.result);
}


/*
   Query:   HEADER
            MASK
   Answer:  HEADER
            RECORD
*/

int
do_read(pid_t client_pid, struct query_rec *query)
{
  int i;
  char *mask, *record;
  struct _mxfer_entry mx, mxa[2];
  struct answer_rec answer;

  if (debug)
    {
      printf("READ command received from %d\n", client_pid);
      printf("Header: db_name= %s\n", query->db_name);
      printf("Header: r.record_size= %d\n", query->r.record_size);
      printf("Header: r.index= %ld\n", query->r.index);
    }
  mask= (char *)malloc(query->r.record_size);
  _setmx(&mx, mask, query->r.record_size);
  Readmsgmx(client_pid,
	    sizeof(struct query_rec),
	    1,
	    &mx);
  if (debug)
    {
      printf("Mask: ");
      for (i=0; i < query->r.record_size; i++)
	printf("%c%02x ", mask[i], mask[i]);
      printf("\n");
    }

  record= (char *)malloc(query->r.record_size);
  _setmx(&mxa[0], &answer, sizeof(struct answer_rec));
  _setmx(&mxa[1], record, query->r.record_size);
  i= db_read(query, mask, &answer, record);
  Replymx(client_pid, 2, &mxa);
  free(mask);
  free(record);
  return(answer.result);
}


/*
   Query:   HEADER
            MASK
   Answer:  HEADER
*/

int
do_recdel(pid_t client_pid, struct query_rec *query)
{
  int i;
  char *mask;
  struct _mxfer_entry mx, mxa;
  struct answer_rec answer;

  if (debug)
    {
      printf("RECDEL command received from %d\n", client_pid);
      printf("Header: db_name= %s\n", query->db_name);
      printf("Header: d.record_size= %d\n", query->d.record_size);
    }
  mask= (char *)malloc(query->d.record_size);
  _setmx(&mx, mask, query->d.record_size);
  Readmsgmx(client_pid,
	    sizeof(struct query_rec),
	    1,
	    &mx);
  if (debug)
    {
      printf("Mask: ");
      for (i=0; i < query->d.record_size; i++)
	printf("%c%02x ", mask[i], mask[i]);
      printf("\n");
    }

  _setmx(&mxa, &answer, sizeof(struct answer_rec));
  i= db_recdel(query, mask, &answer);
  Replymx(client_pid, 1, &mxa);
  free(mask);
  return(answer.result);
}


/*
   Query:   HEADER
   Answer:  HEADER
*/

int
do_dbdel(pid_t client_pid, struct query_rec *query)
{
  struct _mxfer_entry mxa;
  struct answer_rec answer;

  if (debug)
    {
      printf("DBDEL command received from %d\n", client_pid);
      printf("Header: db_name= %s\n", query->db_name);
    }

  _setmx(&mxa, &answer, sizeof(struct answer_rec));
  db_dbdel(query, &answer);
  Replymx(client_pid, 1, &mxa);
  return(answer.result);
}


/*
   Query:   HEADER
   Answer:  HEADER
*/

int
do_test(pid_t client_pid, struct query_rec *query)
{
  struct _mxfer_entry mxa;
  struct answer_rec answer;

  if (debug)
    printf("TEST command received from %d\n", client_pid);

  _setmx(&mxa, &answer, sizeof(struct answer_rec));
  answer.result= 0;
  answer.t.version_hi= VERSION_HI;
  answer.t.version_lo= VERSION_LO;
  answer.t.version_p = VERSION_P;
  strcpy(answer.t.version_str, VERSION_STR);
  Replymx(client_pid, 1, &mxa);
  return(answer.result);
}


/*
   Query:   HEADER
   Answer:  HEADER
*/

int
do_unknown(pid_t client_pid, struct query_rec *query)
{
  struct _mxfer_entry mxa;
  struct answer_rec answer;

  if (debug)
    {
      printf("UNKNOWN command received from %d\n", client_pid);
      printf("Header: command= %c\n", query->command);
    }

  _setmx(&mxa, &answer, sizeof(struct answer_rec));
  answer.result= RES_UNKNOWN_COMMAND;
  Replymx(client_pid, 1, &mxa);
  return(answer.result);
}


static void
show_help(char *argv0)
{
  printf("dbsrv %s\n", VERSION_STR);
  printf("Use: %s [-Vhvs] [-N name]\n", argv0);
  printf("Options:\n");
  printf("\t-N name\tRegister server with `name'.\n");
  printf("\t-s\tEnable STOP operation.\n");
  printf("\t-V\tShow debug messages.\n");
  printf("\t-h\tShow this short help.\n");
  printf("\t-v\tPrint out version number.\n");
}


int
main(int argc, char *argv[])
{
  char *server_name= NULL;
  int NoRun= FALSE, enable_stop= FALSE;
  int name_id;
  int done;
  int i;

  server_name= (char *)malloc(strlen(SERVER_NAME)+1);
  strcpy(server_name, SERVER_NAME);
  while ((i= getopt(argc, argv, "VN:hvs")) != -1) 
    switch (i)
      {
      case 'V':
	debug= TRUE;
	break;
      case 'N':
	if (server_name)
	  free(server_name);
	server_name= (char *)strdup(optarg);
	break;
      case 'v':
	printf("dbsrv %s\n", VERSION_STR);
	NoRun= TRUE;
	break;
      case 'h':
	show_help(argv[0]);
	NoRun= TRUE;
	break;
      case 's':
	enable_stop= TRUE;
	break;
      case '?':
        if (isprint(optopt))
          fprintf(stderr, "Unknown option `-%c'.\n", optopt);
        else
          fprintf(stderr, "Unknown option character `\\x%x'.\n", optopt);
        exit(1);
        break;
      default:
	abort();
	break;
      }
  for (i= optind; i < argc; i++)
    ;

  if (NoRun)
    exit(0);

  if (enable_stop &&
      server_name[0] == '/')
    {
      fprintf(stderr, "Name can not be global if STOP is enabled.\n");
      exit(2);
    }
  name_id= qnx_name_attach(0, server_name);
  if (name_id < 0)
    {
      perror("Name attach");
      exit(1);
    }
  if (debug)
    printf("Server \"%s\" is running...\n", server_name);

  done= 0;
  while (!done)
    {
      struct _mxfer_entry mx;
      int client_pid;
      struct query_rec query;

      _setmx(&mx, &query, sizeof(struct query_rec));
      client_pid= Receivemx(0, 1, &mx);
      switch (query.command)
	{

	case CM_WRITE:
	  do_write(client_pid, &query);
	  break;

	case CM_READ:
	  do_read(client_pid, &query);
	  break;

	case CM_RECDEL:
	  do_recdel(client_pid, &query);
	  break;

	case CM_DBDEL:
	  do_dbdel(client_pid, &query);
	  break;

	case CM_TEST:
	  do_test(client_pid, &query);
	  break;

	case CM_STOP:
	  if (!enable_stop)
	    do_unknown(client_pid, &query);
	  else
	    {
	      if (debug)
		printf("STOP command received from %d\n", client_pid);
	      done= 1;
	    }
	  break;

	default:
	  do_unknown(client_pid, &query);
	  break;
	}
    }
  return(0);
}

/* End of server.c */

globals.c 10/15

/*
 * QNX demo: Database server
 *
 * (c) Drotos Daniel 1997
 *
 * Global variables
 *
 */


int debug= 0;


/* End of globals.c */

read.c 11/15

/*
 * QNX demo: Database server
 *
 * (c) Perlaki Attila, Fulep David 1996;  Drotos Daniel 1997
 *
 * This file contains POSIX part of READ operation.
 *
 */

#include <stdio.h>
#include <errno.h>

#include "globals.h"
#include "db-server.h"
#include "utils.h"


int
db_read(struct query_rec *query,
	char *mask,
	struct answer_rec *answer,
	char *record)
{
  FILE *f;
  fpos_t fpos;
  long idx;
  int i;

  answer->result= 0;
  answer->r.index= -1;

  if ((f= fopen(query->db_name, "r")) == NULL)
    {
      perror("Opening database");
      answer->result= ERR_OPEN_DB;
      return(ERR_OPEN_DB);
    }

  fpos= query->r.index * query->r.record_size;
  idx=query->r.index;
  if ((i= fsetpos(f, &fpos) < 0))
    {
      perror("Positioning to requested record");
      answer->result= ERR_FILE_ERROR;
      return(ERR_FILE_ERROR);
    }

  i= fgetpos(f, &fpos);
  i= fread(record, 1, query->r.record_size, f);
  if (i < 0)
    {
      perror("Reading database");
      answer->result= ERR_READ_ERROR;
    }
  else
    while ((i == query->r.record_size) &&
	   !(answer->result) &&
	   answer->r.index < 0)
      {
	if (debug)
	  {
	    printf("Comparing record %d (at pos %d) and the mask\n",
		   idx, fpos);
	    for (i= 0; i < query->r.record_size; i++)
	      printf("%c%02x ", record[i], record[i]);
	    printf("\n");
	    for (i= 0; i < query->r.record_size; i++)
	      printf("%c%02x ", mask[i], mask[i]);
	    printf("\n");
	  }
	if (match(record, mask, query->r.record_size))
	  {
	    /* Found! */
	    if (debug)
	      printf("Found requested record (%d)\n", idx);
	    answer->result= 0;
	    answer->r.index= idx;
	  }
	else
	  {
	    fgetpos(f, &fpos);
	    i= fread(record, 1, query->r.record_size, f);
	    if (i < 0)
	      {
		perror("Reading database");
		answer->result= ERR_READ_ERROR;
	      }
	    idx++;
	  }
      }
  if (answer->r.index < 0)
    {
      if (debug)
	printf("Record not found\n");
      answer->result= RES_RECORD_NOT_FOUND;
    }

  fclose(f);
  return(answer->result);
}

/* End of read.c */

recdel.c 12/15

/*
 * QNX demo: Database server
 *
 * (c) Perlaki Attila, Fulep David 1996;  Drotos Daniel 1997
 *
 * This file contains POSIX part of RECDEL operation.
 *
 */

#include <stdio.h>
#include <errno.h>
#include <sys/stat.h>

#include "globals.h"
#include "db-server.h"
#include "utils.h"


int
db_recdel(struct query_rec *query, char *mask, struct answer_rec *answer)
{
  FILE *f;
  fpos_t fpos, act_long;
  long idx;
  int i;
  char *record;
  struct stat st;

  answer->result= 0;
  answer->d.nuof_deleted= 0;

  if ((f= fopen(query->db_name, "r+")) == NULL)
    {
      perror("Opening database");
      answer->result= ERR_OPEN_DB;
      return(ERR_OPEN_DB);
    }
  if (fstat(fileno(f), &st) < 0)
    {
      perror("Checking size of database");
      fclose(f);
      answer->result= ERR_FILE_ERROR;
      return(ERR_FILE_ERROR);
    }
  act_long= st.st_size;
  if (debug)
    printf("File size: %ld; number of records: %d\n",
	   act_long, act_long/query->d.record_size);
  if (act_long % query->d.record_size)
    {
      if (debug)
	printf("Truncating broken record from the end of the database\n");
      if (trunc_file(f,
		     (act_long/query->d.record_size)*query->d.record_size) < 0)
	{
	  fclose(f);
	  answer->result= ERR_FILE_ERROR;
	  return(ERR_FILE_ERROR);
	}
    }
  if (fstat(fileno(f), &st) < 0)
    {
      perror("Checking size of database");
      fclose(f);
      answer->result= ERR_FILE_ERROR;
      return(ERR_FILE_ERROR);
    }
  act_long= st.st_size;

  record= (char *)malloc(query->d.record_size);

  idx= 0;
  fgetpos(f, &fpos);
  i= fread(record, 1, query->d.record_size, f);
  if (i < 0)
    {
      perror("Reading database");
      answer->result= ERR_READ_ERROR;
    }
  else
    while ((i == query->d.record_size) &&
	   !(answer->result) &&
	   act_long)
      {
	if (debug)
	  {
	    printf("Checking record %d at pos %ld to delete\n", idx, fpos);
	    printf("Record: ");
	    for (i= 0; i < query->d.record_size; i++)
	      printf("%c%02x ", record[i], record[i]);
	    printf("\nMask  : ");
	    for (i= 0; i < query->d.record_size; i++)
	      printf("%c%02x ", mask[i], mask[i]);
	    printf("\n");
	  }
	if (match(record, mask, query->d.record_size))
	  {
	    fpos_t l;
	    char *lr;
	    /* deleting record */
	    if (debug)
	      printf("Deleting record %d at pos %ld\n", idx, fpos);
	    answer->d.nuof_deleted++;
	    l= act_long - query->d.record_size;
	    fsetpos(f, &l);
	    lr= (char *)malloc(query->d.record_size);
	    if (l != fpos)
	      {
		if (fread(lr, 1, query->d.record_size, f) !=
		    query->d.record_size)
		  {
		    perror("Reading database");
		    answer->result= ERR_READ_ERROR;
		  }
		else
		  {
		    fsetpos(f, &fpos);
		    if (fwrite(lr,
			       1,
			       query->d.record_size, f) !=
			query->d.record_size)
		      {
			perror("Reading database");
			answer->result= ERR_WRITE_ERROR;
		      }
		    else
		      if (trunc_file(f, l) < 0)
			answer->result= ERR_FILE_ERROR;
		      else
			act_long= l;
		  }
	      }
	    else
	      /* deleting last record */
	      if (trunc_file(f, l) < 0)
		answer->result= ERR_FILE_ERROR;
	      else
		act_long= l;
	    fsetpos(f, &fpos);
	    free(lr);
	    idx--;
	  }
	fgetpos(f, &fpos);
	if ((i= fread(record, 1, query->d.record_size, f)) < 0)
	  {
	    perror("Reading database");
	    answer->result= ERR_READ_ERROR;
	  }
	idx++;
      }

  free(record);
  fclose(f);
  return(answer->result);
}


/* End of recdel.c */

t.c 13/15

/*
 * QNX demo: Database server
 *
 * (c) Drotos Daniel 1997
 *
 * Test program to demonstrate how to write client programs
 * for the database server.
 *
 */

#include <stdio.h>
#include <sys/name.h>
#include <errno.h>
#include <sys/kernel.h>
#include <sys/sendmx.h>

/* Header file of the database server */

#include "db-server.h"


/* Structure of records stored in our database file */

struct myrecord
{
  int data1;      /* Numeric field */
  char data2[10]; /* Character field */
};


/*
 * Testing WRITE operation
 *____________________________________________________________________________
 *
 */

int
test_write(pid_t server)
{
  struct _mxfer_entry mxq[2], mxa; /* Pointers for query and answer */
  struct query_rec query;          /* Query */
  struct answer_rec ans;           /* Answer */
  struct myrecord data;            /* Data to write into database */

  printf("Testing write...\n");
  /*
   * Setting up pointers to point to data structures. This query
   * will contain two part: fix header and one record to write
   */
  _setmx(&mxq[0], &query, sizeof(struct query_rec));
  _setmx(&mxq[1], &data , sizeof(struct myrecord));
  _setmx(&mxa   , &ans  , sizeof(struct answer_rec));

  /* Setting up query structure */
  query.command= CM_WRITE;
  strcpy(query.db_name, "proba.db");
  query.w.record_size= sizeof(struct myrecord);
  query.w.is_mask= 0; /* We do not provide MASK */

  /* Setting up data record */
  printf("Enter data1: ");
  scanf("%d", &data.data1);       /* Num field and.. */
  strcpy(data.data2, "adatmezo"); /* ..char field */

  /* Sending the query to the server and getting the answer */
  if (Sendmx(server,
	     2,   /* Two part query */
	     1,   /* One part answer */
	     &mxq,/* Pointers to parts of query */
	     &mxa /* Pointer to asnwer record */) < 0)
    perror("WRITE not answered");
  else
    printf("Answer: %d\n", ans.result);
  return(ans.result);
}


/*
 * Testing replace operation (write with MASK)
 *____________________________________________________________________________
 *
 */

int
test_replace(pid_t server)
{
  struct _mxfer_entry mxq[3], mxa; /* Pointers for query and answer */
  struct query_rec query;          /* Query */
  struct answer_rec ans;           /* Answer */
  struct myrecord mask, newr;      /* Data records: MASK and the new record */

  printf("Testing replace...\n");
  /*
   * Setting up pointers to point the real data structures.
   * Query will contain three part: fix header, record to write
   * and a mask record to search for
   */
  _setmx(&mxq[0], &query, sizeof(struct query_rec));
  _setmx(&mxq[1], &newr , sizeof(struct myrecord));
  _setmx(&mxq[2], &mask , sizeof(struct myrecord));
  _setmx(&mxa   , &ans  , sizeof(struct answer_rec));

  /* Setting up query */
  query.command= CM_WRITE;
  strcpy(query.db_name, "proba.db");
  query.w.record_size= sizeof(struct myrecord);
  query.w.is_mask= 1; /* We provide MASK record */

  /*
   * Setting up MASK record. '*' character in mask will
   * match with any byte in database's record
   */
  /* Filling MASK up with joker '*' character */
  memset(&mask, '*', sizeof(struct myrecord));
  /* Setting up some parts of the MASK with specific data */
  mask.data1= 123;
  strcpy(mask.data2, "adatmezo");

  /* Setting up new data record */
  newr.data1= 321;
  strcpy(newr.data2, "ujrekord");

  /* Sending query to the server and getting answer */
  if (Sendmx(server,
	     3,    /* Three part query */
	     1,    /* One part answer */
	     &mxq, /* Pointers to parts of query */
	     &mxa  /* Pointer to answer */) < 0)
    perror("WRITE not answered");
  else
    printf("Answer: %d\n", ans.result);
  return(ans.result);
}


/*
 * Testing READ operation
 *____________________________________________________________________________
 *
 */

int
test_read(pid_t server)
{
  struct _mxfer_entry mxq[2], mxa[2]; /* Pointers for query and answer */
  struct query_rec query;             /* Query */
  struct answer_rec ans;              /* Answer */
  struct myrecord mask, record;       /* Data records */

  printf("Testing read...\n");
  /* 
   * Setting up pointers to point to real query and answer structures.
   * READ query contains two parts: fix header and a MASK record.
   * Answer contains two parts as well: fix header and a record which
   * matches to MASK record.
   */
  _setmx(&mxq[0], &query , sizeof(struct query_rec));
  _setmx(&mxq[1], &mask  , sizeof(struct myrecord));
  _setmx(&mxa[0], &ans   , sizeof(struct answer_rec));
  _setmx(&mxa[1], &record, sizeof(struct myrecord));

  /* Setting up query */
  query.command= CM_READ;
  strcpy(query.db_name, "proba.db");
  query.r.record_size= sizeof(struct myrecord);
  query.r.index= 0; /* Start search with first record */

  /* Filling MASK record up with joker '*' */
  memset(&mask, '*', sizeof(struct myrecord));
  /* Setting up some parts of MASK with specific data */
  mask.data1= 123;
  strcpy(mask.data2, "adatmezo");

  /* Sending query to server and getting answer */
  if (Sendmx(server,
	     2,   /* Two part query */
	     2,   /* Two part asnwer */
	     &mxq,/* Pointers to parts of query */
	     &mxa /* Pointers to parts of answer */) < 0)
    perror("READ not answered");
  else
    {
      /* Checking asnwer */
      printf("Answer: %d\n", ans.result);
      printf("Answer: r.index= %ld\n", ans.r.index);
      /* If `index' of answer is not negative we found a record */
      if (ans.r.index >= 0)
	{
	  printf("Record: data1= %d\n", record.data1);
	  printf("Record: data2= \"%s\"\n", record.data2);
	}
    }

  /* Reading all records */
  printf("\nReading all records\n");
  /* Filling MASK up with joker '*' */
  memset(&mask, '*', sizeof(struct myrecord));
  /* Start search at the beginning of the database */
  query.r.index= 0;
  /* Continuing query until we run out of recors */
  if (Sendmx(server, 2, 2, &mxq, &mxa) < 0)
    ans.r.index= -1;
  while (ans.r.index >= 0)
    {
      printf("%3d. %05d \"%s\"\n", ans.r.index, record.data1, record.data2);
      /* Query again: continue search at the next record */
      query.r.index= ans.r.index+1;
      if (Sendmx(server, 2, 2, &mxq, &mxa) < 0)
	ans.r.index= -1;
    }
  return(ans.result);
}


/*
 * Testing RECDEL operation
 *____________________________________________________________________________
 *
 */

int
test_recdel(pid_t server)
{
  struct _mxfer_entry mxq[2], mxa; /* Pointers for query and asnwer */
  struct query_rec query;          /* Query */
  struct answer_rec ans;           /* Answer */
  struct myrecord mask;            /* MASK record */

  printf("Testing recdel...\n");
  /*
   * Setting up poniters. Delete operation needs two part into query.
   * First is fix header, and second is a MASK record which specifies
   * what are we going to delete.
   */
  _setmx(&mxq[0], &query , sizeof(struct query_rec));
  _setmx(&mxq[1], &mask  , sizeof(struct myrecord));
  _setmx(&mxa   , &ans   , sizeof(struct answer_rec));

  /* Setting up query */
  query.command= CM_RECDEL;
  strcpy(query.db_name, "proba.db");
  query.d.record_size= sizeof(struct myrecord);

  /* Filling MASK up with joker '*' */
  memset(&mask, '*', sizeof(struct myrecord));
  /* Setting up some parts of the MASK record */
  printf("Enter data1: ");
  scanf("%d", &mask.data1);

  /* Sending query to the server and getting answer */
  if (Sendmx(server,
	     2,   /* Two part query */
	     1,   /* One part answer */
	     &mxq,/* Pointers to parts of query */
	     &mxa /* Pointer to answer */) < 0)
    perror("RECDEL not answered");
  else
    {
      printf("Answer: %d\n", ans.result);
      printf("Answer: r.nuof_deleted= %ld\n", ans.d.nuof_deleted);
    }
  return(ans.result);
}


/*
 * Testing DBDEL operation
 *____________________________________________________________________________
 *
 */

int
test_dbdel(pid_t server)
{
  struct _mxfer_entry mxq, mxa; /* Pointers for query and answer */
  struct query_rec query;       /* Query */
  struct answer_rec ans;        /* Answer */

  printf("Testing dbdel...\n");
  /*
   * Setting up pointers to point real data structures.
   * This type of query contains only one part the fix header part.
   */
  _setmx(&mxq, &query, sizeof(struct query_rec));
  _setmx(&mxa, &ans  , sizeof(struct answer_rec));

  /* Setting up query */
  query.command= CM_DBDEL;
  strcpy(query.db_name, "proba.db");

  /* Sending query and getting answer */
  if (Sendmx(server,
	     1,   /* One part query */
	     1,   /* One part answer */
	     &mxq,/* Pointer to query */
	     &mxa /* Pointer to answer */) < 0)
    perror("DBDEL not answered");
  else
    printf("Answer: %d\n", ans.result);
  return(ans.result);
}


/*
 * Testing TEST operation
 *____________________________________________________________________________
 *
 */

int
test_test(pid_t server)
{
  struct _mxfer_entry mxq, mxa; /* Pointers for query and answer */
  struct query_rec query;       /* Query */
  struct answer_rec ans;        /* Answer */

  printf("Testing test...\n");
  /*
   * Setting up pointers to point the real data structures.
   * This type of query contains only one part the fix header part.
   */
  _setmx(&mxq, &query, sizeof(struct query_rec));
  _setmx(&mxa, &ans  , sizeof(struct answer_rec));

  /* Setting up query */
  query.command= CM_TEST;
  
  /* Sending query. This type of request is never answered! */
  if (Sendmx(server,
	     1,   /* One part query */
	     1,   /* We expect something... */
	     &mxq,/* Pointer to query */
	     &mxa /* Pointer to answer */) < 0)
    perror("STOP not answered");
  else
    {
      printf("Answer: %d\n", ans.result);
      printf("Answer: t.version_hi = %d\n", ans.t.version_hi);
      printf("Answer: t.version_lo = %d\n", ans.t.version_lo);
      printf("Answer: t.version_p  = %d\n", ans.t.version_p);
      printf("Answer: t.version_str= %s\n", ans.t.version_str);
    }
  return(ans.result);
}


/*
 * Testing STOP operation
 *____________________________________________________________________________
 *
 */

int
test_stop(pid_t server)
{
  struct _mxfer_entry mxq, mxa; /* Pointers for query and answer */
  struct query_rec query;       /* Query */
  struct answer_rec ans;        /* Answer */

  printf("Testing stop...\n");
  /*
   * Setting up pointers to point the real data structures.
   * This type of query contains only one part the fix header part.
   */
  _setmx(&mxq, &query, sizeof(struct query_rec));
  _setmx(&mxa, &ans  , sizeof(struct answer_rec));

  /* Setting up query */
  query.command= CM_STOP;
  
  /* Sending query. This type of request is never answered! */
  if (Sendmx(server,
	     1,   /* One part query */
	     1,   /* We expect something... */
	     &mxq,/* Pointer to query */
	     &mxa /* Pointer to answer */) < 0)
    perror("STOP not answered");
  else
    printf("Answer: %d\n", ans.result);
  return(ans.result);
}


/*
 * Program to test operations of database server
 *____________________________________________________________________________
 *
 */

int
main(int argc, char *argv[])
{
  pid_t server;
  char *c, *srvname;

  if (argv[1][0] == '-')
    {
      printf("%s\n", &argv[1][1]);
      server= qnx_name_locate(0, &argv[1][1], 0, NULL);
      if (argc < 3)
	exit(2);
      c= argv[2];
    }
  else
    {
      server= qnx_name_locate(0, SERVER_NAME, 0, NULL);
      if (argc < 2)
	exit(2);
      c= argv[1];
    }
  if (server < 0)
    {
      perror("Locate server");
      exit(1);
    }

  test_test(server);

  while (*c)
    {
      switch (*c)
	{
	case 'w':
	  test_write(server);
	  break;
	case 'R':
	  test_replace(server);
	  break;
	case 'r':
	  test_read(server);
	  break;
	case 's':
	  test_stop(server);
	  break;
	case 'd':
	  test_recdel(server);
	  break;
	case 'D':
	  test_dbdel(server);
	  break;
	default:
	  printf("Unknown test %c\n", *c);
	  break;
	}
      c++;
    }

  return(0);
}

/* End of t.c */

utils.c 14/15

/*
 * QNX demo: Database server
 *
 * (c) Perlaki Attila, Fulep David 1996;  Drotos Daniel 1997
 *
 * Utility routines for database server.
 *
 */

#include <stdio.h>

#include "globals.h"


/*
 * This function compares two records. One of them is a MASK record.
 *____________________________________________________________________________
 *
 * MASK record can contain '*' joker character which matches any
 * character in compared record.
 *
 */

int
match(char *what, char *mask, int n)
{
  int i;
  
  for (i= 0; i < n; i++)
    {
      if ((mask[i] != '*') &&
	  (what[i] != mask[i]))
	return(0);
    }
  return(1);
}


/*
 * This function sets length of a file to a specific value
 *____________________________________________________________________________
 *
 * It is used to truncate the database file.
 *
 * This function must be rewritten to use ftruncate on UNIX systems!!!
 *
 */

int
trunc_file(FILE *f, fpos_t new_size)
{
  int i;

  if ((i= ltrunc(fileno(f), new_size, SEEK_SET)) < 0)
    perror("Truncating file");
  return(i);
}


/* End of utils.c */

write.c 15/15

/*
 * QNX demo: Database server
 *
 * (c) Perlaki Attila, Fulep David 1996;  Drotos Daniel 1997
 *
 * This file contains POSIX part of WRITE operation.
 *
 */

#include <stdio.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/types.h>
#include <string.h>

#include "globals.h"
#include "db-server.h"
#include "utils.h"


static int
append(int fd, char *record, int n)
{
  if ((lseek(fd, 0, SEEK_END) < 0) ||
      (write(fd, record, n) < 0))
    {
      perror("Append to database");
      return(ERR_FILE_ERROR);
    }
  if (debug)
    printf("Record appended to database\n");
  return(0);
}


int
db_write(struct query_rec *query,
	 char *record,
	 char *mask,
	 struct answer_rec *answer)
{
  FILE *f;

  f= fopen(query->db_name, "r+");
  if (f == NULL)
    {
      if (errno == ENOENT)
	{
	  if ((f= fopen(query->db_name, "w+")) == NULL)
	    {
	      perror("Creating database");
	      return(ERR_CREATE_DB);
	    }
	  else
	    if (debug)
	      printf("Database \"%s\" created\n", query->db_name);
	}
      else
	{
	  perror("Opening database");
	  return(ERR_OPEN_DB);
	}
    }

  answer->result= 0;
  if (!mask)
    {
      /* Simply append */
      answer->result= append(fileno(f), record, query->w.record_size);
    }
  else
    {
      char *actual;
      fpos_t fpos;
      long idx;
      int i;
      int was= 0;

      actual= (char *)malloc(query->w.record_size);
      idx= 0;
      /* fpos is 0 now */
      fgetpos(f, &fpos);
      i= read(fileno(f), actual, query->w.record_size);
      if (i < 0)
	{
	  perror("Reading database");
	  answer->result= ERR_READ_ERROR;
	}
      else
	while ((i == query->w.record_size) &&
	       !answer->result)
	  {
	    if (debug)
	      {
		printf("Comparing record %d (at pos %d) and the mask\n",
		       idx, fpos);
		for (i= 0; i < query->w.record_size; i++)
		  printf("%c%02x ", actual[i], actual[i]);
		printf("\n");
		for (i= 0; i < query->w.record_size; i++)
		  printf("%c%02x ", mask[i], mask[i]);
		printf("\n");
	      }
	    if (match(actual, mask, query->w.record_size))
	      {
		/* replacing record */
		was= 1;
		if (debug)
		  printf("replacing record %d\n", idx);
		fsetpos(f, &fpos);
		if (write(fileno(f), record, query->w.record_size) <
		    query->w.record_size)
		  answer->result= ERR_WRITE_ERROR;
	      }
	    fgetpos(f, &fpos);
	    i= read(fileno(f), actual, query->w.record_size);
	    if (i < 0)
	      {
		perror("Reading database");
		answer->result= ERR_READ_ERROR;
	      }
	    idx++;
	  }
      if (!answer->result &&
	  !was)
	answer->result= append(fileno(f), record, query->w.record_size);
      free(actual);
    }

  fclose(f);
  return(answer->result);
}

/* End of write.c */

Generated by GNU enscript 1.5.1.