#define have_subdirs
CODE=Imakefile
-CLIENTDIRS=util include gdb lib clients man update
+CLIENTDIRS=util include lib clients man update
SVRDIRS=server db backup dbck dcm gen reg_svr incremental regtape afssync
#ifdef SVRBUILD
SUBDIRS=$(CLIENTDIRS) $(SVRDIRS)
CODE=${SRCS} Imakefile
OBJS=addusr.o reg_stubs.o
-program(addusr,${OBJS},${MR_LIBDEP} ${GDB_LIBDEP}, -lkadm ${CLIBS} ${CRYPT},${PROGDIR})
+program(addusr,${OBJS},${MR_LIBDEP}, -lkadm ${CLIBS} ${CRYPT},${PROGDIR})
reg_stubs.c: ../userreg/reg_stubs.c
cp ../userreg/reg_stubs.c reg_stubs.c
CODE=${SRCS} Imakefile
OBJS=blanche.o
-program(blanche,${OBJS},${MR_LIBDEP} ${GDB_LIBDEP}, ${CLIBS},${PROGDIR})
+program(blanche,${OBJS},${MR_LIBDEP}, ${CLIBS},${PROGDIR})
CODE=${SRCS} Imakefile
OBJS=mailmaint.o
-program(mailmaint,${OBJS},${MR_LIBDEP} ${GDB_LIBDEP}, ${CLIBS} ${CURSES},${PROGDIR})
+program(mailmaint,${OBJS},${MR_LIBDEP}, ${CLIBS} ${CURSES},${PROGDIR})
OBJS= ${MOBJS} ${NOBJS}
INCLUDE= -I$(BUILDTOP)/include -I$(BUILDTOP)/lib ${GDSSINC} ${COM_ERR_INC} -I/usr/athena/include -I.
-program(moira, ${MOBJS},${MR_LIBDEP} ${GDB_LIBDEP}, ${LIBGDSS} ${CLIBS} ${CRYPT} ${CURSES},${PROGDIR})
-program(namespace, ${NOBJS},${MR_LIBDEP} ${GDB_LIBDEP}, ${LIBGDSS} ${CLIBS} ${CRYPT} ${CURSES},${PROGDIR})
+program(moira, ${MOBJS},${MR_LIBDEP}, ${LIBGDSS} ${CLIBS} ${CRYPT} ${CURSES},${PROGDIR})
+program(namespace, ${NOBJS},${MR_LIBDEP}, ${LIBGDSS} ${CLIBS} ${CRYPT} ${CURSES},${PROGDIR})
install::
$(RM) $(DESTDIR)$(PROGDIR)/listmaint
CODE=${SRCS} Imakefile
OBJS=mrcheck.o
-program(mrcheck,${OBJS},${MR_LIBDEP} ${GDB_LIBDEP}, ${CLIBS},${PROGDIR})
+program(mrcheck,${OBJS},${MR_LIBDEP}, ${CLIBS},${PROGDIR})
OBJS= mrtest.o
LOCAL_INCLUDES= $(READLINE_INCLUDES)
-program(mrtest, ${OBJS},${MR_LIBDEP} ${GDB_LIBDEP}, ${READLINE_LIBS} ${CLIBS},${ETCDIR})
+program(mrtest, ${OBJS},${MR_LIBDEP}, ${READLINE_LIBS} ${CLIBS},${ETCDIR})
RCSID("$Header$");
int recursion = 0, interactive;
-extern int sending_version_no;
int count, quit = 0, cancel = 0;
char *whoami;
sigjmp_buf jb;
void test_connect(int argc, char **argv);
void test_disconnect(void);
void test_host(void);
-void test_new(void);
-void test_old(void);
void test_motd(void);
void test_query(int argc, char **argv);
void test_auth(void);
test_disconnect();
else if (!strcmp(argv[0], "host"))
test_host();
- else if (!strcmp(argv[0], "new") || !strcmp(argv[0], "2"))
- test_new();
- else if (!strcmp(argv[0], "old") || !strcmp(argv[0], "1"))
- test_old();
else if (!strcmp(argv[0], "motd"))
test_motd();
else if (!strcmp(argv[0], "query") || !strcmp(argv[0], "qy"))
com_err("moira (noop)", status, "");
}
-void test_new(void)
-{
- sending_version_no = MR_VERSION_2;
-}
-
-void test_old(void)
-{
- sending_version_no = MR_VERSION_1;
-}
-
void test_connect(int argc, char *argv[])
{
char *server = "";
printf("connect, c\t\tConnect to Moira server\n");
printf("disconnect, d\t\tDisconnect from server\n");
printf("host\t\t\tIdentify the server host\n");
- printf("new, 2\t\t\tUse new protocol\n");
- printf("old, 1\t\t\tUse old protocol\n");
printf("motd, m\t\t\tGet the Message of the Day\n");
printf("query, qy\t\tMake a query.\n");
printf("auth, a\t\t\tAuthenticate to Moira.\n");
CODE=${SRCS} Imakefile
OBJS=chfn.o chpobox.o chsh.o
-program(chfn, chfn.o,${MR_LIBDEP} ${GDB_LIBDEP}, ${CLIBS},${PROGDIR})
-program(chpobox, chpobox.o,${MR_LIBDEP} ${GDB_LIBDEP}, ${CLIBS},${PROGDIR})
-program(chsh, chsh.o,${MR_LIBDEP} ${GDB_LIBDEP}, ${CLIBS},${PROGDIR})
+program(chfn, chfn.o,${MR_LIBDEP}, ${CLIBS},${PROGDIR})
+program(chpobox, chpobox.o,${MR_LIBDEP}, ${CLIBS},${PROGDIR})
+program(chsh, chsh.o,${MR_LIBDEP}, ${CLIBS},${PROGDIR})
CODE=${SRCS} ${TEXT} ${SCRIPTS} Imakefile files.h userreg.h
OBJS= userreg.o display.o reg_stubs.o disable.o
-program(userreg, ${OBJS},${MR_LIBDEP} ${GDB_LIBDEP}, -lkadm ${CLIBS} ${CRYPT} ${CURSES},${PROGDIR})
+program(userreg, ${OBJS},${MR_LIBDEP}, -lkadm ${CLIBS} ${CRYPT} ${CURSES},${PROGDIR})
depend:: ${CODE}
#include <sys/stat.h>
#include <sys/wait.h>
+#include <errno.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
-#include <gdb.h>
-
EXEC SQL INCLUDE sqlca;
void sqlglm(char *, unsigned int *, unsigned int *);
#define SOFT_FAIL(x) (((x) == MR_NO_MEM) || ((x) == MR_CANT_CONNECT) || ((x) == MR_CCONFIG) || ((x) == MR_DEADLOCK) || ((x) == MR_BUSY) || ((x) == MR_ABORT))
char whobuf[256], *whoami = whobuf, *db = "moira";
-extern CONNECTION conn;
int main(int argc, char **argv)
{
EXEC SQL END DECLARE SECTION;
time(&now);
- gdb_init();
+ mr_init();
EXEC SQL CONNECT :db IDENTIFIED BY :db;
int dcm_send_file(char *service, char *host, char *target)
{
- char addr[256], data[MAXPATHLEN];
- int code;
+ char data[MAXPATHLEN];
+ int code, conn;
- sprintf(addr, "%s:moira_update", host);
- conn = start_server_connection(addr, "");
- if (!conn || (connection_status(conn) == CON_STOPPED))
+ conn = mr_connect_internal(host, "moira_update");
+ if (!conn)
{
- com_err(whoami, connection_errno(conn), "can't connect to %s", addr);
+ com_err(whoami, errno, "can't connect to %s", host);
return MR_CANT_CONNECT;
}
- code = send_auth(host);
+ code = send_auth(conn, host);
if (code)
{
com_err(whoami, code, "authenticating to %s", host);
}
sprintf(data, "%s/%s.out", DCM_DIR, service);
- code = send_file(data, target, 1);
- if (code == MR_UNKNOWN_PROC)
- code = send_file(data, target, 0);
+ code = send_file(conn, data, target, 0);
if (code)
com_err(whoami, code, "sending data to %s", host);
done:
- send_quit();
- sever_connection(conn);
+ send_quit(conn);
+ close(conn);
return code;
}
int dcm_execute(char *service, char *host, char *script)
{
- char addr[256], inst[MAXPATHLEN];
- int code;
+ char inst[MAXPATHLEN];
+ int code, conn;
- sprintf(addr, "%s:moira_update", host);
- conn = start_server_connection(addr, "");
- if (!conn || (connection_status(conn) == CON_STOPPED))
+ conn = mr_connect_internal(host, "moira_update");
+ if (!conn)
{
- com_err(whoami, connection_errno(conn), "can't connect to %s", addr);
+ com_err(whoami, errno, "can't connect to %s", host);
return MR_CANT_CONNECT;
}
- code = send_auth(host);
+ code = send_auth(conn, host);
if (code)
{
com_err(whoami, code, "authenticating to %s", host);
sprintf(inst, "/tmp/moira-update.XXXXXX");
mktemp(inst);
- code = send_file(script, inst, 0);
+ code = send_file(conn, script, inst, 0);
if (code)
{
com_err(whoami, code, "sending instructions to %s", host);
goto done;
}
- code = execute(inst);
+ code = execute(conn, inst);
if (code)
com_err(whoami, code, "executing instructions on %s", host);
done:
- send_quit();
- sever_connection(conn);
+ send_quit(conn);
+ close(conn);
return code;
}
+++ /dev/null
-# $Header$
-#
-# Copyright 1990 by the Massachusetts Institute of Technology.
-#
-# For copying and distribution information,
-# please see the file <mit-copyright.h>.
-#
-# Imakefile for GDB library.
-#
-
-SRCS = gdb_struct.c gdb_stype.c gdb.c gdb_trans.c gdb_trans2.c \
- gdb_conn.c gdb_serv.c gdb_fserv.c gdb_db.c gdb_debug.c gdb_ops.c
-
-OBJECTS = gdb.o gdb_struct.o gdb_stype.o gdb_trans.o gdb_trans2.o \
- gdb_conn.o gdb_ops.o gdb_serv.o gdb_fserv.o gdb_db.o gdb_debug.o
-
-SRCDIR=$(SRCTOP)/gdb
-CODE=$(SRCS) Imakefile gdb_lib.h
-
-all::
-
-depend:: ${CODE}
-
-library_obj_rule()
-install_library_target(mrgdb,$(OBJECTS),$(SRCS))
+++ /dev/null
-/************************************************************************/
-/*
-/* Instructions for Installing GDB
-/*
-/* Noah Mendelsohn
-/* April 22, 1987
-/*
-/* Copyright (C) 1987 Massachusetts Institute of Technology
-/*
-/* =========================
-/*
-/* NOTE: GDB is not currently a supported service of Project Athena
-/* or of any other MIT organization. It is a set of programs which
-/* I have written while working for Project Athena and which I
-/* am making available to the MIT community in the hope that they
-/* will prove useful. The decision as to whether GDB will become
-/* supported is not mine to make. If you wish to express an opinion,
-/* you should write a note including the following people on the
-/* distibution:
-/*
-/* Prof. Steve Lerman (lerman@athena.mit.edu)
-/* Prof. Jerry Saltzer (Saltzer@athena.mit.edu)
-/* Dan Geer (geer@athena.mit.edu)
-/* Noah Mendelsohn (noah@athena.mit.edu)
-/*
-/* Thank you!
-/*
-/* This file is named: README
-/*
-/************************************************************************/
-
-
-/************************************************************************/
-/*
-/* Obtaining GDB
-/*
-/************************************************************************/
-
-
-GDB is now available to the MIT community by anonymous ftp
-from meathead.mit.edu. There are currently two files:
-
- 1) README (this file)
-
- 2) tar.file.Z (compressed tar.file containing all of gdb)
-
-To install gdb, make a directory on your own system. It can
-have any name, but we'll use gdb in this example:
-
- % mkdir gdb
- % cd gdb
- % ftp meathead.mit.edu
- ===>login as anonymous and give your user i.d. as passwd
- ftp> cd gdb
- ftp> get README
- ftp> bin <= make sure transfer of tar.file.Z
- is done in binary mode, or the
- uncompress step below may fail
- ftp> get tar.file.Z
- ftp> quit
-
- % uncompress tar.file.Z <== this re-creates tar.file
- If this step fails, you may have
- forgotten to transfer the file
- in binary mode (see above.)
- % tar xf tar.file <== this extracts all the gdb
- files from the tar file
-
-/************************************************************************/
-/*
-/* Making the gdb libraries
-/*
-/************************************************************************/
-
-To build the GDB library:
-
- % make libgdb.a
-
-/************************************************************************/
-/*
-/* Formatting documentation
-/*
-/************************************************************************/
-
-To format the gdb documents, you need scribe and access to a PostScript
-printer. To make the user guide:
-
- % make gdb_ugd.PS
- % rm gdb_ugd.PS
- % make gdb_ugd.PS
-
-The reason you do it twice is that scribe requires two tries at a document
-in order to get the forward references to page numbers right. The resulting
-gdb_ugd.PS file may be sent to your PostScript printer:
-
- % lpr gdb_ugd.PS
-
-To format the library reference manual:
-
- % make library.PS
- % rm library.PS
- % make library.PS
-
-/************************************************************************/
-/*
-/* Writing and running programs
-/*
-/************************************************************************/
-
-The user guide tells you how to do this. Note that gdb.h winds up in
-the gdb directory (where the tar.file is) when you use these installation
-instructions, and so does libgdb.a. You will have to use the appropriate
-Unix incantations, i.e. making links or telling cc and ld where to find
-things in order for your compilations and loads to work. Make sure you
-specify libgdb.a as one of the files to load along with your program.
-
-/************************************************************************/
-/*
-/* Building the database server
-/*
-/************************************************************************/
-
-The database server program is named dbserv. To build it:
-
- % make dbserv
-
-This MUST be done on a machine where RTI Ingres is installed. You may
-have to edit the makefile if /rtingres is not the directory where Ingres
-lives, and you may have to put /rtingres/bin in your path before doing the
-make.
-
-/************************************************************************/
-/*
-/* Sample programs
-/*
-/************************************************************************/
-
-Various test programs are included in the tar.file. These are not intended
-primarily for users, they are used for my private testing, but a few of
-them are well commented and many of them work. The ones whose names begin
-with the letter 't' are most likely to be useful. Each of them can be
-built with 'make'.
-
-/************************************************************************/
-/*
-/* Debugging
-/*
-/************************************************************************/
-
-Both the dbx and the Saber C debuggers have successfully executed many
-GDB applications. I have found Saber to be particularly useful, and I
-have included a .saberinit file which should serve as a starting point
-for you. (Warning: .saberinit is supplied as a courtesy and I don't
-guarantee to check it out with every re-release. If you know enough
-to use Saber, you can probably make any minor corrections which may
-be needed without too much trouble.) Note that both debuggers have trouble
-on programs that fork. See the gdb_debug routine and the GDB_NOFORK flag
-for a useful trick to avoid forking when debugging.
-
-/************************************************************************/
-/*
-/* Mailing lists
-/*
-/************************************************************************/
-
-gdb-users@athena.mit.edu reaches all users of GDB bugs and fixes
- are advertised here
-gdb-news@athena.mit.edu announcements of interest to a wider
- community of people who may not want to
- know about every 2 line change to the system
-
-noah@athena.mit.edu the author
-
-
-
+++ /dev/null
-/*
- * $Source$
- * $Header$
- */
-
-#ifndef lint
-static char *rcsid_gdb_c = "$Header$";
-#endif
-
-
-/************************************************************************/
-/*
-/* gdb.c
-/*
-/* Global Database Library - main controlling functions and globally
-/* shared routines.
-/*
-/* Author: Noah Mendelsohn
-/*
-/* Copyright: 1986 MIT Project Athena
-/*
-/*
-/* In addition to defining the main routines for gdb, this source
-/* file #includes gdb_lib.h, which does the external definitions
-/* for all global data used by the library. Everyone else gets
-/* externs for this data defined by gdb.h.
-/*
-/************************************************************************/
-
-#include <stdio.h>
-#include <string.h>
-#include <signal.h>
-#include <pwd.h>
-#include "gdb.h"
-#include "gdb_lib.h"
-#include <errno.h>
-#ifdef POSIX
-#include <sys/utsname.h>
-#include <unistd.h>
-#endif
-
-#ifndef __NetBSD__
-extern int sys_nerr;
-extern char *sys_errlist[];
-#endif
-
-extern int errno;
-
-/* This global is defined to make sure that Moira clients are linked
- * against the correct library.
- */
-int link_against_the_moira_version_of_gdb = 0;
-int g_inited = FALSE; /* gdb_init has not been */
- /* called */
-
- /*----------------------------------------------------------*/
- /*
- /* gdb_init
- /*
- /* Initialize the global database facility. Must be
- /* called before any of the other gdb functions
- /* are used. Among other things, this function
- /* sets to ignore signals for writing on broken pipes.
- /*
- /*----------------------------------------------------------*/
-
-int
-gdb_init()
-{
- register int i;
- char hostname[255]; /* name of local host */
- int uid; /* Unix user-i.d. number */
- char *username; /* string form of i.d. */
-
- struct passwd *pw_struct; /* passwd entry comes back */
- /* here */
-#ifdef POSIX
- struct utsname nameposix;
- struct sigaction act;
-
- sigemptyset(&act.sa_mask);
- act.sa_flags = 0;
-#endif
-
- /*
- * So we know we've been initialized, and we do it only once
- */
- if (g_inited)
- return 0;
- g_inited = TRUE;
-
- /*
- * Initialize the system defined types table
- */
- gdb_i_stype();
-
- /*
- * Initialize the connection control data structures.
- */
- gdb_mcons = 0; /* number of connections */
- /* in use so far */
- for (i=0; i<GDB_MAX_CONNECTIONS; i++) {
- gdb_cons[i].id = GDB_CON_ID;
- gdb_cons[i].in.stream_buffer = NULL;
- gdb_cons[i].in.stream_buffer_length = GDB_STREAM_BUFFER_SIZE;
- }
- /*
- * Initialize the fd maps
- */
- gdb_mfd = 0;
-
- FD_ZERO(&gdb_crfds);
- FD_ZERO(&gdb_cwfds);
- FD_ZERO(&gdb_cefds);
-
- /*
- * Initialize the server/client layer
- */
- gdb_i_srv();
-
- /*
- * Ignore the signal generated when writing to a pipe which has been
- * closed at the other end. gdb_move_data handles this condition
- * synchronously.
- */
-#ifdef POSIX
- act.sa_handler = (void(*)()) SIG_IGN;
- (void) sigaction(SIGPIPE, &act, NULL);
-#else
- (void) signal(SIGPIPE, SIG_IGN);
-#endif
-
- /*
- * Make a note of the local host and user name
- */
-#ifdef POSIX
- (void) uname(&nameposix);
- strncpy(hostname, nameposix.nodename, sizeof(hostname) - 1);
-#else
- if (gethostname(hostname, sizeof(hostname)-1)!=0)
- (void) strcpy(hostname, "????");
-#endif
- gdb_host = db_alloc(strlen(hostname)+1);
- (void) strcpy(gdb_host, hostname);
-
- uid = getuid();
-
- pw_struct = getpwuid(uid);
-
- if (pw_struct != NULL && pw_struct ->pw_name != NULL &&
- *pw_struct->pw_name !='\0')
- username = pw_struct->pw_name;
- else
- username = "????";
- gdb_uname = db_alloc(strlen(username)+1);
- (void) strcpy(gdb_uname, username);
-
- return 0;
-}
- /*----------------------------------------------------------*/
- /*
- /* g_chk_init
- /*
- /* Make sure gdb has been initialized, blow up if not.
- /*
- /*----------------------------------------------------------*/
-
-int
-g_chk_init()
-{
- if (!g_inited)
- GDB_GIVEUP("You must call gdb_init before using GDB services.")
-}
-
- /*----------------------------------------------------------*/
- /*
- /* g_bitcount
- /*
- /* Count the number of bits in a word. Adapted from
- /* K&R.
- /*
- /*----------------------------------------------------------*/
-
-int
-g_bitcount(inp)
-unsigned int inp;
-{
- register unsigned int x = inp; /* the word to check */
- register int count; /* the count to return */
-
- for (count=0; x !=0; x=x>>1)
- if (x & 01)
- count++;
-
- return count;
-}
-
- /*----------------------------------------------------------*/
- /*
- /* gdb_fstring
- /*
- /* Utility routine to conditionally free a null
- /* terminated string.
- /*
- /*----------------------------------------------------------*/
-
-int
-gdb_fstring(str)
-char *str;
-{
- if (str != NULL)
- db_free(str, strlen(str)+1);
-}
-
- /*----------------------------------------------------------*/
- /*
- /* g_givup
- /*
- /* Called when a fatal error occurs.
- /*
- /*----------------------------------------------------------*/
-
-int
-g_givup(errormsg)
-char *errormsg;
-{
- fprintf(gdb_log,"\n\nFATAL GDB ERROR:\n\n\t");
- fprintf(gdb_log,errormsg);
- fprintf(gdb_log,"\n\n");
- exit(100);
-}
-
- /*----------------------------------------------------------*/
- /*
- /* connection_perror
- /*
- /* Acts like gdb_perror, but takes its errno from
- /* the connection. Resets errno as a side effect.
- /* THIS ROUTINE IS A NO_OP IF connection_status(con)!=CON_STOPPING
- /*
- /*----------------------------------------------------------*/
-
-int
-connection_perror(con, msg)
-CONNECTION con;
-char *msg;
-{
- if (con == NULL || connection_status(con) != CON_STOPPING)
- return;
- errno = connection_errno(con);
- gdb_perror(msg);
-}
-
- /*----------------------------------------------------------*/
- /*
- /* gdb_perror
- /*
- /* Performs same function as system perror, but does
- /* it on gdb_log instead of stderr.
- /*
- /*----------------------------------------------------------*/
-
-int
-gdb_perror(msg)
-char *msg;
-{
- if(msg != NULL)
- fprintf(gdb_log, "%s: ", msg);
- if(errno < sys_nerr)
- fprintf(gdb_log, "%s.\n", sys_errlist[errno]);
- else
- fprintf(gdb_log, "errno %d is out of range of message table.\n", errno);
-}
-
+++ /dev/null
-@device(imprint10)
-@make(Report, Form 1)
-@style(hyphenation on)
-@style(BindingMargin 0)
-@style(justification off)
-@style(FontFamily ComputerModernRoman10)
-@style(footnotes "")
-@begin(center)
-@majorheading(GDB)
-@heading(Global Databases for Project Athena)
-@subheading(Noah Mendelsohn)
-@subheading(April 17, 1986)
-@end(center)
-@blankspace(1 cm)
-This note is intended as a brief introduction to the Global Database
-(GDB) system being
-developed at MIT Project Athena.
-GDB is an ongoing effort, still in its early stages, to provide the
-services of a high performance shared relational database to the
-heterogeneous systems comprising the Athena network. Specifications
-have been developed for a set of library routines to be used by
-@i[clients] to access the database.
-Current plans are to use the Ingres relational database product from
-RTI as a local data manager, but to support access via the client
-library from any Berkeley Unix@+[TM]@foot(@+[TM]Unix is a trademark of
-AT&T Bell Laboratories) system in the internet. Though early
-versions will manage only a single copy of any given relation,
-replication may be added at some point in the future.
-In the meantime the client library provides a uniform framework for
-writing database applications at Athena.
-
-While designing the client library it became apparent that many of its
-underlying services for structured data storage and transmission would
-be of value for a variety of applications. Most of these interfaces
-have been exposed, and the GDB project has undertaken as a secondary
-goal the development of these simple services for structured data
-maintenance and transmission.
-
-@section(Raison d'etre@+[1]@foot[@+[1]with apologies for lack of accents in the
-font!])
-
-The GDB project was motivated by the observation that Athena
-applications tend to exploit the computational and display services of
-the system much more effectively than they use the network.
-Furthermore,
-those applications which do use the network tend to have strong
-machine type affinities, running comfortably on either a Vax or an
-RT/PC,
-but rarely both. Indeed, the @i[strategic] Athena database system is
-currently unavailable on the RT/PC's.
-
-Of the many unexplored uses of the network, globally accessible
-databases seem to have great value in a variety of disciplines, and
-they are also badly needed for certain aspects of Athena
-administration. By providing well architected services for global
-data sharing, we hope to achieve at least two goals: (1) set the
-precedent that user written applications and Athena supplied services,
-like @b[madm], @b[chhome], and @b[passwd], run compatibly
-from any machine in the network, and (2) encourage
-the development of new database applications by eliminating the need
-for individual projects and departments to develop their own
-transmission and encapsulation protocols.
-
-@section(Implementation Goals)
-
-The following goals have been established for the architecture and
-implementation of GDB:
-
-@begin(itemize)
-Access to databases stored on incompatible machines (e.g. RT/PC to
-Vax) should be supported transparently.
-
-Multiple databases, possibly at several sites, should be accessible
-simultaneously. The ability to do concurrent activity on the several
-databases is desirable.
-
-Appropriate facilities for managing structured data returned from the
-database should be provided for programmers (e.g. access fields by
-name.)
-
-Asynchronous operation should be supported, for several reasons:
-@begin(itemize)
-Required for control of simultaneous access to multiple databases.
-
-Needed for graceful interruption of long-running or erroneous
-requests.
-
-Facilitates pipelining of requests, thereby maximizing overlap of
-server and client processing.
-@end(itemize)
-
-When the internal interfaces used for session control and data
-transmission can be generalized without adding unnecessarily to their
-complexity, then those interfaces should be documented and exposed.
-@end(itemize)
-
-@section(Implementation Strategy)
-
-Several approaches to achieving these goals were considered, and an
-implementation strategy has been chosen.
-
-One approach to achieving the required function would be to rely on
-the appearance of RTI products containing the necessary facilities.
-At the very least, we would need a full function Ingres port to the
-4.2 system on the RT/PC. RTI would further have to extend Ingres for
-access to databases through the internet, and they would have to
-support such access across multiple machine types. These extensions
-would give us a core of function suitable for limited application,
-though we would have to see whether flexibility and performance were
-truly appropriate for our needs. If RTI should come forward with a
-commitment to produce these products within the next few months, then
-need for the libraries described herein might not be so great.
-Lacking such products from RTI, it seems essential that we carry
-forward with a strategy for database access from @i[all] of the
-workstations in the Athena network.
-
-Having decided to do at least some of the necessary work ourselves,
-several implementation strategies are possible. One, which is
-currently being pursued by Roman Budzianowski, is to interpose the
-appropriate transmission services between the RTI Ingres front end and
-back end. This technique has a number of interesting advantages, and
-some disadvantages. The primary advantage is the ability to run
-existing Ingres applications, including some of the forms and query
-facilities, through the network. Also, Roman reports that he has
-succeeded in running an interesting subset of applications without too
-much effort. The disadvantages of Roman's approach are the lack of
-a strategy for supporting non-Vax machines until RTI comes out with
-the appropriate base products, and the dependence on undocumented
-interfaces.
-In some cases, the front-end and the back-end are sharing files, while
-in others signals seem to be sent. It remains to be seen how successfully
-we can divine these undocumented interfaces, how stable they remain
-over time, and whether--given an RTI Ingres on the RT/PC--we can
-figure out how to do the right byte swapping on the binary data sent
-through Ingres' pipes and files. Our conclusion is that Roman's
-effort should continue, because it can
-achieve valuable results without excessive effort.
-Nevertheless, this scheme falls short of our requirement for balanced
-support of all the machines in the Athena network, so we recommend an
-alternate implementation as the primary base for Athena application
-development.
-
-A machine independent access method for relational databases could be
-constructed in many different ways. One technique we have considered
-and rejected is to base an implementation on the RPC prototype
-developed by Steve Miller and Bob Souza. While RPC is
-convenient, and the prototype appears to be of very high quality, it
-fails to meet our needs in several crucial areas. We have concluded
-that asynchronous interaction between clients and servers is
-essential for performance, for parallel execution of multiple queries,
-and for interruptibility of ongoing operations. Synchronous RPC seems
-ill-suited to these requirements. A secondary concern is the lack of
-support for procedure calls across heterogeneous architectures in the
-current version of the RPC prototype. The right hooks are supposedly
-there, but the necessary alignment and type conversion routines have
-not been built. Indeed, the prototype has yet to be ported to the
-RT/PC.
-
-As a result of this analysis, we have designed a system which uses RTI
-Ingres for the things it does (or purports to do) well, and we have
-added a flexible, asynchronous transport mechanism for transmission of
-structured data between heterogeneous processors. The specification,
-outlined in a separate document, includes libraries for creation and
-management of tuples and relations in virtual memory, along with a
-simple mechanism for typing the fields comprising a tuple. Layered
-upon these are services for transmitting fields, tuples and relations
-through the internet, doing the necessary conversions and
-re-alignments when moving between incompatible machines. These
-services, in turn, are used by a library which provides almost
-all of the services of Ingres EQUEL to clients throughout the network.
-
-@section(Project Status)
-
-The specification for the interfaces to the client library routines is
-available in draft form and is now being refined. In parallel, design
-is proceeding on the protocol to be used between the sites, and on the
-related software structures used to encapsulate and parse the
-transmitted data. The design does include a simple but flexible
-proposal for managing asynchronous activities. Coding will start soon
-on those pieces of the library which seem to be stable; refinement of
-the other parts will take a few more weeks. While our rate of
-progress will depend greatly on the number of people doing the work
-and on their other responsibilities (neither of which are clear at
-this time), I'm optimistic that a basic implementation will start
-showing signs of life within a couple of months, with polishing taking
-a bit longer.
+++ /dev/null
-/************************************************************************/
-/*
-/* PROBLEMS WITH THE GDB CODE
-/*
-/************************************************************************/
-
-* Go through all of wesommers changes in /u1/sms/gdb
- - Eye catchers faster
- - fastprogress
- - signal handling
- - some fatal error stuff
-
-* check error return handling on all system calls used by GDB
-
-* terminate_db was never written
-
-* comments on start_performing_db_operation lie
-
-* clean up comments in dbserv.qc
-
-* clean up comments in gdb_db.c
-
-* Fix ptr <--> int casts on semantic routines
-
-* Buffer input through gdb_move_data
-
-* Ingres version 5 support
-
-* Bouncing connections so that there is only one GDB port
-
-* decode of descriptor currently assumes that NULL will be passed if receiving
- into unallocated descriptor. Autos sometimes aren't null.
-
-* Straighten out description of db_query in the library reference.
-
-* Document retrieval limit setting
-
-* Debug retrieval limit setting
-
-* Document -l option on dbserv
-
-* Document user and host fields and how to zap for anonymous use.
- Also, indicate risks of trusting the information in those fields.
-
-* There is some inconsistency in the use of tuple vs. tuple descriptor
- as argument to the routines like FIELD_OFFSET_IN_TUPLE
-
-* Offsets would be more convenient if from the start of the tuple rather than
- from the start of the 0th field.
-
-* Some of the routines defined as macros may not work if semicolons are
- used after invocation. This should be documented our changed.
-
-* there is some inconsistency in the semantic routines over which
- decode routines presume null values of their targets, and which
- ones actively free any existing data. Likewise for the null
- routines. For the moment, we'll ignore it as an internal issue,
- but this should be fixed when it starts causing more trouble than
- it's worth.
-
-
-* should check all uses of register declaration to make sure they
- are appropriate
-
-* make sure null values are handlined properly when tuples are
- initialized. Looks like the string routines may be easily confused
- about non-initialized null values.
-
-* must update documentation and sample programs to indicate requirement
- to call the gdb_init routine. Perhaps there should be a closeup routine
- too?
-
-** create-tuple-descriptor may be mis-computing data-len in the case
- where alignment is leaving gas. Looks like the gas is not getting
- added in. (I think this was fixed during debugging with Saber.)
-
-* must make a plan to exhaustively test code paths. Several whole
- routines have not been tried at all
-
-* the design for out of band signalling and operation interruption must
- be considered more carefully.
-
-* perhaps gdb.h should be split into a piece which is visible to all
- users (i.e. lives in /usr/include) and an internal piece used only
- for compiling the library.
-
-* we need some way to mark and distinguish inbound and outbound half
- sessions. For the moment, they can be implicit in their position in
- the connection data structure, but that seems a bit tacky. Could
- use routine pointers to to do the reading and the writing.
-
-* could implement a buffer/flush scheme at some level to avoid repeated
- and unnecessary system calls for write/read. Not clear what level is
- best to do this--I'm inclined to do it in the oeprations themselves, not
- in the transport service.
-
-* should come up with a way to encode those types which need a recursive
- call on cleanup. We can probably get rid of null_tuple_strings that
- way, and pick up a bit of generality.
-
-* don't yet have a proper architecture for the routines used at the
- server end. Which one's should be exposed and documented to
- implementors of new services? How general should they be?
-
-* CONN_STATUS values should be cleaned up.
-
-* make sure the cancellation function in OPERATION_DATA is handled properly.
-
-* How do we really want to handle predecessor failing conditions. Should
- we purge the queue, as currently claimed, or just attempt the next one?
- Probably just attempt the next one.
-
-* select timer handling is being implemented very approximately for now
- We are setting the full timer each time the real select is called, not
- accounting for time already elapsed when select is called repeatedly.
-
-* level of indirection on select is really not clear
-
-* in dealing with file descriptor masks passed to functions, one should
- probably use the & notation consistently to avoid unnecessary structure
- copying. See especially in the interfaces to gdb_fd_copy, etc. in
- gdb_trans.c
-
-* current semantics of op_select imply that lots of manipulation of
- operation queues will be required as operations complete and the
- lists get logically shorter. We may want to change the semantics
- of op_select and/or provide new operations for adding and deleting
- operations from an operation list, possibly with attendant re-allocation.
-
-* how do we do exception handling on the connections?
-
-* gdb_hcon_progress: how to handle cancelling, etc.
-
-* the distinction between initialization and continuation routines
- is looking pretty artificial. Leading to duplicate code in
- places like gdb_hcon_progress.
-
-* out of band signalling not implemented. The sockets are set to null
- during creation.
-
-* when starting peer sessions a race condition is built into the current
- algorithm. startup may fail when the two sides are initiated at about
- the same time. Must be fixed for production use.
-
-* start_peer_connection has no way to return a reason for failure.
-
-* connections are currently made to a well known port. Eventually this
- should be fixed.
-
-* one could argue that the creation of the listening socket should be done
- once during initialization, not repeatedly. On the other hand, this
- significantly raises the cost for someone who never listens.
-
-* We could automate the activation of gdb_init by checking for a flag
- in the appropriate routines.
-
-* define consistent set of op_results, apply them in gdb_ops.c
-
-* synchronous calling of continuation routines may be a mistake? I don't
- think so, since the length of the chain is statically limited. Would
- be a mistake in the case of extensive re-queueing, but then we go back
- to the queue manager, NO?
-
-* queue_operation takes a half connection as an argument, but they are
- not properly described in the spec. There should be macros to
- extract the input and output half connections from a connection.
-
-* our writeup should note ease of supporting shared state servers
- (e.g. a very live conferencing system?)
-
-* for performance, changes should be made to minimize the number of read
- and write system calls done. This could (perhaps) be done with look-behind
- on the outbound and buffered read on the inbound queues, used in conjunction
- with the iovec technique. Should improve performance significantly.
- May also need a 'block' hint for use on a half connection which allows
- an application queueing lots of little requests to get them batched
- together.
-
-* we're not handling the case where select returns -1 in cnsel, particularly
- for EBADF indicating a closed descriptor.
-
-* start_peer_connection should use randomized waits to re-try connect and
- avoid the current startup window.
-
-* need a better way to return errors on things like starting a connection.
- should scan all of the synchronous interfaces to find out where this
- should be used.
-
-* it's possible that HCON_BUSY should be replaced by a mechanism to lock
- all of gdb_progress
-
-* most operations should have cancellation routines.
-
-* when tuples are sent for server/client stuff, make sure the descriptors
- are properly destroyed when not needed
-
-* should integrate deletion as an operation on a type, along with
- optimization for nop'ing it. Should then go through all of code
- which does things like null-tuple-strings and straighten it out.
-
-* document start_sending_object start_receiving_object
-
-* anything which starts an operation should make sure that the
- status of the supplied operation is NOT_RUNNING. There is a
- nasty condition which results when the same operation is started
- twice.
-
-* disable GDB_PORT because it encourages conflict among applications
-
-* implement validate routines in create_forking_server
-
-* check userinfod and inetd to make sure that server environment is
- appropriate.
-
-* build async versions of db ops, at both client and server
-
-* use of "string" vs "STRING" in documentation is confusing
-
-* macros for getting at fields in tuples
-
-* document non-portabilities
- - 4.2 dependencies
- - representation of data by enc/dec
-
-* are text lengths being handled well on queries? (Looks ok, 9/17/86)
-
-* lots of error checking is missing in dbserv
-
-* Confusion in documentation as to whether receive_object allocates
- a new relation (for example)
-
-* delete relation needs a way to delete non-contiguous data other
- than strings
-
-* syntax checking of queries
-
-* return codes for operations, especially DB operations, are a mess.
-
-* we should have some equivalent of perror
-
-* we can possibly give some information about where we were at
- the time of a failure if we keep a logical stack.
-
-* gdb_init should be callable multiple times
-
-* i.d. fields are currently implemented with strings, which is very
- slow. Should re-implement with integers as magic numbers
-
-* must figure out handling of errors on queued database requests.
- which ones cause a tear-down. Are there problems dequeueing
- the appropriate operations in the right order? Could things get
- out of sync if other things are queued ahead? Seems to require
- a pretty good understanding of how things get cancelled implicitly
- and explicitly. Maybe all explicit cancellations should be done
- using terminate_db. There is also some question about how
- careful we have to be in handling the explicit cancellation of
- a single db operation, or whether that should be supported. Not
- clear that we have enough of a bracket context to do that cleanly.
-
-* document preempt_and_start_receiving_object, also, maybe should
- do one for sending.
-
-* Notes should be provided on create/delete semantics. Current is:
- tuple_descs are reference counted. Your creates should
- match your deletes. Same is true for relations and operations.
- Likewise tuples EXCEPT: if you create a tuple, add it to
- a relation, and delete the relation, the tuple itself is
- deleted, and all dangling pointers to it are invalid.
-
-* Must assign version numbers to all programs, documents and files
-
-* gdb_ugd should reference K&R
-
-* should make a CSTRING_T to represent a null terminated string by its pointer
-
-* should be able to limit the number of tuples retrieved.
-
-* Perhaps should be able to interrupt ongoing database operations, or
- in any case, the restriction on severing the connection should be
- documented.
-
-* send_object and receive_object should return OP_SUCCESS not OP_COMPLETE
-
-* create_type is documented, but it does not exist. Also, it is mentioned
- in the language reference, but not really written up properly.
-
-* add missing chapters to the user's guide
-
-* Steve: forward reference in user guide to async
-
-* Steve: diagrams of general system structure and flow
-
-* More reference material in user guide
- - GDB data types
- - Other structures
-
-* what are rules about memory allocation
- - what does it
-
-* confusion over pointers and macros in documentation
-
-* Missing chapters
-
-* i.d. fields should be changed upon deletion to try and catch misuse
- of deleted data
-
-* explicit host addresses are not accepted
-
-*debugging forked servers is very difficult
-
-*there should be more detailed failure codes on send_object receive_object
-
-\f/************************************************************************/
-/*
-/* "Solved" Problems
-/*
-/************************************************************************/
-
-* need a routine to wait for all the operations in a list
- should be easy to build out of op_sel (DONE)
-
-* there should be a macro called TERMINATED for internal use which
- would be true when an operation was completed or cancelled. This
- should be used by most of the routines which check for completion,
- in anticipation of the day when error recovery is done properly.
- (DONE...name of the macro is OP_DONE)
-
-* see if select can be used with a listening socket (DONE...it works)
-
-* we need a send_object and receive_object as well as start_sending_object
- and start_receiving_object (DONE, but documentation is missing)
-
-* should put handling in select for the exceptfd case to catch closing
- connections, and likewise should check for errors on read/write. Maybe
- only errors on read/write should be checked. (DONE..exceptfd's are
- irrelavent...I think they're for getting signals, anyway, we do the
- right thing now when a socket fails by closing the corresponding
- connection.)
-
-* make sure port numbers are handled OK in the server/client model
- (DONE)
-
-* the database server MUST delete its relations, and we must decide
- how tuple descriptor reclamation is to be done (Done)
-
-* make db_alloc and db_free replaceable (Done)
-
-* James had problems with create_tuple_descriptor in that he got
- one of the parameters duplicated (not a gdb problem)
-
-* async queries and db_ops (Done)
-
-* we should put eye catchers into structured data and check it. (done)
-
-* db_query is not nearly asynchronous enough (Done)
-
-* Strings: James forgot to initialize strings after creating tuple;
-
-* we are missing a delete_relation routine, which would have to
- recursively delete all tuples after calling delete_tuple_strings
- or whatever on them. This should be called by the decode relation
- and the null relation semantic routines. (Done)
-
-* naming inconsistency between "new-tuple" and "create-_____" all others (Done)
-
-
-/************************************************************************/
-/*
-/* Problems in Providing SQL Support
-/*
-/************************************************************************/
-
-* Character strings in SQL limited to 255 bytes, in literal at least
-
-
+++ /dev/null
-/*
- * $Source$
- * $Header$
- */
-
-#ifndef lint
-static char *rcsid_gdb_conn_c = "$Header$";
-#endif
-
-/************************************************************************
- *
- * gdb_conn.c
- *
- * GDB - Connection Management Services
- *
- * Author: Noah Mendelsohn
- * Copyright: 1986 MIT Project Athena
- * For copying and distribution information, please see
- * the file <mit-copyright.h>.
- *
- * Routines used in the creation and maintenance of CONNECTIONS.
- * Note: these are closely related to the services provided
- * by gdb_trans.c and gdb_trans2.c.
- *
- *
- ************************************************************************/
-
-#include <mit-copyright.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include "gdb.h"
-#include <sys/types.h>
-#include <sys/uio.h>
-#include <sys/socket.h>
-#include <sys/ioctl.h>
-#include <netinet/in.h>
-#include <netdb.h>
-#include <errno.h>
-#ifdef SOLARIS
-#include <sys/filio.h>
-#endif /* SOLARIS */
-#ifdef POSIX
-#include <unistd.h>
-#endif
-
-extern int errno;
-
-CONNECTION gdb_allocate_connection();
-
-/************************************************************************
- *
- * start_peer_connection (start_peer_connection)
- *
- * Starts a connection to another process which itself will be
- * issuing a start_peer_connection to us. Current implementation
- * builds at most one stream, with the risk of a hang if
- * the attempts to connect cross in the night. This is a bug,
- * but this level of support is acceptable for casual debugging
- * of applications, and perhaps for some production use in
- * controlled settings. I think the only other way to do it
- * is to risk building two streams in parallel, possibly tearing
- * one down when the duplication is discovered. Seems complicated
- * and messy.
- *
- ************************************************************************/
-
-CONNECTION
-start_peer_connection(id)
-char *id; /* null terminated string */
-{
- register CONNECTION con; /* the connection we're */
- /* creating */
-
- GDB_INIT_CHECK
-
- /*
- * Try to allocate a connection and fill it in with null values.
- */
-
- con = g_make_con();
-
- /*
- * In this implementation, we use a single fd for both inbound and
- * outbound traffic. Try to connect to other side. If that
- * doesn't work, wait to accept a connection from the other side.
- * Current implementation of this is synchronous--may be a problem?
- * Also note timing window bug in the following. If the two peers
- * are started at just about the same time, the race may not be handled
- * propoerly. If the connections come up, then verify the level of
- * protocol being observed on the connections. If incompatible,
- * then turn off the connection.
- */
-
- if(!g_try_connecting(con,id)) {
- g_try_accepting(con,id);
- if(con->status == CON_STARTING)
- g_ver_iprotocol(con);
- } else
- if(con->status == CON_STARTING)
- g_ver_oprotocol(con);
-
-
- if (con->status == CON_UP) {
- /*
- * We've successfully started the connection, now mark
- * it for non-blocking I/O. Also, update the high water
- * mark of fd's controlled by our system.
- */
- int nb = 1;
- if(ioctl(con->in.fd, FIONBIO, (char *)&nb)== (-1)) {
- g_stop_with_errno(con);
- return con;
- }
- if (con->in.fd +1 > gdb_mfd)
- gdb_mfd = con->in.fd + 1;
- /*
- * Allocate a buffer, if necessary, and reset buffer pointers
- * so first request will result in a long read into the buffer
- */
- g_allocate_connection_buffers(con);
-
- return con;
- } else
- return NULL;
-}
-
-
-/************************************************************************/
-/*
-/* g_make_con
-/*
-/* Internal routine to allocate a new connection structure and
-/* initialize all its fields to logical null values.
-/*
-/************************************************************************/
-
-CONNECTION
-g_make_con()
-{
- register CONNECTION con;
-
- /*
- * Try to allocate a connection, fatal error if none available
- */
- con = gdb_allocate_connection();
- if (con == NULL)
- GDB_GIVEUP("start_peer_connection: Tried to allocate too many connections") /* <==RECOVERABLE */
-
- /*
- * Give the fields their initial values
- */
- g_null_con(con);
-
- return con;
-
-}
-
-
-/************************************************************************/
-/*
-/* g_null_con
-/*
-/* Sets a connection descriptor to have all null values in
-/* its fields. This routine does NOT do any of the cleanup
-/* which is necessary after the connection has really been used.
-/*
-/************************************************************************/
-
-int
-g_null_con(con)
-CONNECTION con;
-{
- /*
- * Initialize the connection control data structure.
- */
- con->id = GDB_CON_ID;
- con->status = CON_STARTING;
- con->oob_fcn = NULL; /* out of band signalling */
- /* is not currently */
- /* implemented */
- con->errno = 0; /* system errno gets */
- /* copied here iff it */
- /* causes this con to die */
- /*
- * Initialize input half connection to null state before trying
- * to bring it up.
- */
- con->in.status = OP_NOT_STARTED;
- con->in.fd = -1;
- con->in.oob_fd = -1;
- con->in.op_q_first = (struct oper_data *)&con->in;
- con->in.op_q_last = (struct oper_data *)&con->in;
- con->in.next_byte = NULL;
- con->in.remaining = 0;
- con->in.flags = 0;
-
- /*
- * Initialize output half connection to null state before trying
- * to bring it up.
- */
- con->out.status = OP_NOT_STARTED;
- con->out.fd = -1;
- con->out.oob_fd = -1;
- con->out.op_q_first = (struct oper_data *)&con->out;
- con->out.op_q_last = (struct oper_data *)&con->out;
- con->out.next_byte = NULL;
- con->out.remaining = 0;
- con->out.flags = 0;
-
- return;
-
-}
-
-
-/************************************************************************/
-/*
-/* gdb_allocate_connection
-/*
-/* Return an unused entry in the connection array. Unused entries
-/* are recognized by being marked as CON_STOPPED.
-/*
-/* Note that gdb_mcons is the number of descriptors which have
-/* ever been used (i.e. a high water mark), so status fields
-/* are invalid above that.
-/*
-/************************************************************************/
-
-CONNECTION
-gdb_allocate_connection()
-{
- register int i; /* index of next one */
- /* to check */
-
- /*
- * First look for one below the high water mark
- */
- for(i=0; i<gdb_mcons; i++) {
- if (gdb_cons[i].status == CON_STOPPED)
- return &gdb_cons[i];
- }
-
- /*
- * Allocate one which has never been used, if possible
- */
-
- if (i>=GDB_MAX_CONNECTIONS)
- GDB_GIVEUP("gdb: tried to allocate too many simulataneous connections.\n, See GDB_MAX_CONNECTIONS in gdb.h.") /* <==RECOVERABLE */
-
- gdb_mcons++; /* bump the high water mark */
- gdb_cons[i].status = CON_STOPPED; /* initialize status of the */
- /* new connection */
- return &gdb_cons[i]; /* return new highest con */
- /* ever used*/
-}
-
-
-/************************************************************************/
-/*
-/* g_try_connecting
-/*
-/* Try to start a connection to the designated site, filling
-/* in the appropriate information in the connection descriptor
-/* if successful. Return TRUE if connection succeeded or if
-/* error was fatal enough that we shouldn't try accepting. Returns
-/* FALSE if we should try accepting.
-/*
-/************************************************************************/
-
-int
-
-g_try_connecting(con,id)
-CONNECTION con;
-char *id;
-{
- int peer; /* socket for talking to
- peer */
- char on = 1; /* flag for ioctl */
- struct sockaddr_in target; /* build the peer address */
- /* here */
- struct hostent *peer_host; /* host where peer is */
-
- /*----------------------------------------------------------*/
- /*
- /* Make sure connection is marked stopped until we
- /* get it going.
- /*
- /*----------------------------------------------------------*/
-
- con->status = CON_STOPPED;
-
- /*----------------------------------------------------------*/
- /*
- /* Find out host where peer is, and validate it. Take
- /* care of port at the same time.
- /*
- /*----------------------------------------------------------*/
-
- memset((char *)&target, 0, sizeof(target));
- g_parse_target(id, &peer_host, &target.sin_port);
- if (peer_host == NULL) {
- fprintf(gdb_log,"gdb: g_try_connecting... '%s' is not a valid host:server\n",
- id);
- return TRUE; /* so we won't try accepting */
- }
-
- /*----------------------------------------------------------*/
- /*
- /* Create a socket
- /*
- /*----------------------------------------------------------*/
-
- peer = socket(AF_INET, SOCK_STREAM, 0);
- if (peer < 0) {
- g_stop_with_errno(con);
- return TRUE; /* fatal error */
- }
-
- /*----------------------------------------------------------*/
- /*
- /* Get information and bind socket using well known
- /* port (BUG: this restricts us to one pair of peers
- /* per host pair, as well as being bad practice on
- /* the network. It will do for debugging.
- /*
- /*----------------------------------------------------------*/
-
-
- memcpy((char *)&target.sin_addr,peer_host->h_addr,peer_host->h_length);
- target.sin_family = peer_host->h_addrtype;
-
- /*----------------------------------------------------------*/
- /*
- /* Make the connection
- /*
- /*----------------------------------------------------------*/
-
- if(connect(peer, (struct sockaddr *)&target, sizeof(target)) < 0) {
- if (errno == ECONNREFUSED)
- return FALSE; /* other side not yet */
- /* up, but no other fatal */
- /* errors*/
- else {
- gdb_perror("gdb: unexpected error connecting");
- g_stop_with_errno(con);
- return TRUE;
- }
- }
-
- if ((gdb_Options & GDB_OPT_KEEPALIVE) &&
- setsockopt(peer, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof(on)) < 0) {
- gdb_perror("gdb: unable to start keepalives");
- g_stop_with_errno(con);
- return(TRUE);
- }
-
- /*----------------------------------------------------------*/
- /*
- /* The connection has been made, fill in the connection
- /* control data structure.
- /*
- /*----------------------------------------------------------*/
-
- con->in.fd = peer;
- con->out.fd = peer;
- con->status = CON_STARTING;
-
- return TRUE;
-
-}
-
-
-/************************************************************************/
-/*
-/* g_parse_target
-/*
-/* For a given server or peer i.d., figure out the host and the
-/* port. Arguments are:
-/*
-/* string i.d. of the server, which is
-/* in one of two forms:
-/*
-/* host:servicename (where service name must not begin
-/* with #)
-/*
-/* host:#portnumber (where portnumber is the actual
-/* number of the port to be used)
-/*
-/* (actually, a 3rd form, with no port number supplied,
-/* will use a default GDB_PORT, but this is unsafe
-/* and it will be disabled in production versions
-/* of the gdb system.)
-/*
-/* **hostent: returned to indicate host to be used. Null
-/* if host could not be found
-/*
-/* *port pointer to an integer where the port number will
-/* be put. We return the port number in network
-/* byte order.
-/*
-/************************************************************************/
-
-int
-g_parse_target(id, host, port)
-char *id;
-struct hostent **host;
-u_short *port;
-{
- char buffer[256]; /* longest host name */
- register char *ip, *bp; /* for copying name */
- struct servent *serv; /* returned from */
- /* get service by name */
-
- /*----------------------------------------------------------*/
- /*
- /* copy the host name part only to local buffer
- /*
- /*----------------------------------------------------------*/
-
- ip = id;
- bp = buffer;
-
- while (*ip != '\0' && *ip != ':')
- *bp++ = *ip++;
- *bp = '\0';
-
- /*----------------------------------------------------------*/
- /*
- /* Look up the host name, return if bad.
- /*
- /*----------------------------------------------------------*/
-
- *host = gethostbyname(buffer);
-
- if (*host == NULL)
- return;
-
- /*----------------------------------------------------------*/
- /*
- /* Set up the port address
- /*
- /*----------------------------------------------------------*/
-
- if (*ip++ != ':') {
- *port = GDB_PORT;
- return;
- }
-
- if (*ip == '\0') {
- *host = NULL;
- return;
- }
-
- if (*ip == '#') {
- /*
- * port number supplied explictly
- */
- ip++;
- if (*ip < '0' || *ip>'9') {
- *host = NULL;
- return;
- }
- *port = htons((u_short)atoi(ip));
- } else {
- /*
- * service identified by name
- */
- serv = getservbyname(ip, "tcp");
- if (serv == NULL) {
- *host = NULL;
- return;
- }
- *port = serv->s_port;
- }
-}
-
-
-/************************************************************************/
-/*
-/* g_try_accepting
-/*
-/* Try to accept a connection to the designated site, filling
-/* in the appropriate information in the connection descriptor
-/* if successful.
-/*
-/************************************************************************/
-
-int
-g_try_accepting(con,id)
-CONNECTION con;
-char *id;
-{
- int slisten; /* socket on which
- we listen for connections */
-
- int peer; /* socket for talking to
- peer */
- int fromlen;
- struct sockaddr_in self, from;
- int retries = GDB_BIND_RETRY_COUNT;
- int onoff = 1; /* used as argument to */
- /* setsockopt */
-
- struct hostent *peer_host; /* host where peer is */
-
- /*----------------------------------------------------------*/
- /*
- /* Make sure connection is marked stopped until we
- /* get it going.
- /*
- /*----------------------------------------------------------*/
-
- con->status = CON_STOPPED;
-
- /*----------------------------------------------------------*/
- /*
- /* Create a socket on which to listen. Tell it that
- /* it's OK to re-use the port address, which may still
- /* appear busy if connections are taking awhile to go
- /* away.
- /*
- /*----------------------------------------------------------*/
-
- slisten = socket(AF_INET, SOCK_STREAM, 0);
- if (slisten < 0) {
- gdb_perror("g_try_accepting: error creating listen socket");
- g_stop_with_errno(con);
- }
- /* Try 4.2 method */
- if(setsockopt(slisten, SOL_SOCKET, SO_REUSEADDR, (char *)0, 0)<0)
- /* that didn't work, try 4.3 */
- if(setsockopt(slisten, SOL_SOCKET, SO_REUSEADDR,
- (char *)&onoff, sizeof(int)) <0)
- GDB_GIVEUP("g_try_accepting: could not set SO_REUSEADDR");
-
- /*----------------------------------------------------------*/
- /*
- /* Find out host where peer is, and validate it. Take
- /* care of port at the same time. This is redundant
- /* given that g_try_connecting is always called first.
- /*
- /*----------------------------------------------------------*/
-
- memset((char *)&self, 0, sizeof(self));
- g_parse_target(id, &peer_host, &self.sin_port);
- if (peer_host == NULL) {
- GDB_GIVEUP("gdb_try_accepting: bad port not caught by try connecting")
- }
-
- /*----------------------------------------------------------*/
- /*
- /* Bind the socket to ourselves, using the well known
- /* port (See bug note in g_try_connecting.
- /*
- /* This code should really go in initialization, I think.
- /*
- /*----------------------------------------------------------*/
-
- while (bind(slisten,(struct sockaddr *)&self,sizeof(self)) < 0) {
- if (errno == EADDRINUSE && retries--) {
- fprintf(gdb_log,"gdb: port address in use, will retry %d more time(s)\n",retries+1);
- sleep(GDB_BIND_RETRY_INTERVAL);
- continue;
- } else {
- gdb_perror("gdb: error binding listen socket");
- g_stop_with_errno(con);
- (void) close(slisten);
- return;
- }
- }
-
- /*----------------------------------------------------------*/
- /*
- /* Listen for connections.
- /*
- /*----------------------------------------------------------*/
-
- (void) listen (slisten, 5); /* does not block, just */
- /* sets the maximum backlog */
- /* of pending non-accepted */
- /* cons.*/
- fromlen = sizeof(from);
- peer = accept(slisten, (struct sockaddr *)&from, &fromlen);
- if (peer < 0) {
- g_stop_with_errno(con);
- gdb_perror("gdb_try_accepting: error accepting connection");
- (void) close(slisten);
- return;
- }
-
- (void) close (slisten); /* we're not using the */
- /* listening socket */
- /* anymore, only the */
- /* connection to the peer */
-
- /*----------------------------------------------------------*/
- /*
- /* The connection has been made, fill in the connection
- /* control data structure.
- /*
- /*----------------------------------------------------------*/
-
- con->in.fd = peer;
- con->out.fd = peer;
- con->status = CON_STARTING;
-}
-
-
-/************************************************************************/
-/*
-/* g_ver_oprotocol
-/*
-/* Called when an outbound connection is started to verify
-/* the version of the protocol being observed.
-/*
-/************************************************************************/
-
-int
-g_ver_oprotocol(con)
-CONNECTION con;
-{
-#ifdef VERIFY_PROTOCOL
- char ver = GDB_PROTOCOL_VERSION;
- char theirs;
- int len;
-
- int onoff = 0; /* for ioctl to turn off */
-
- /*
- * Because the connection was accepted on a non-blocking
- * listening socket, the connection itself may be non-blocking.
- * We can't tolerate that here. It will be reset later.
- */
- if (ioctl(con->in.fd, FIONBIO, (char *)&onoff) < 0) {
- g_stop_with_errno(con);
- gdb_perror("Can't turn off FIONBIO in g_ver_iprotocol");
- return;
- }
-
- while (write(con->out.fd, &ver, 1) < 0) {
- g_stop_with_errno(con);
- return;
- }
-
- do {
- len = read(con->in.fd, &theirs, 1);
- if (len == (-1)) {
- g_stop_with_errno(con);
- return;
- }
- } while (len !=1);
-
- if (theirs == ver)
- con->status = CON_UP;
- else
- con->status = CON_STOPPED;
-#else
- con->status = CON_UP;
-#endif
-}
-
-
-/************************************************************************/
-/*
-/* g_ver_iprotocol
-/*
-/* Called when an inbound connection is started to verify
-/* the version of the protocol being observed.
-/*
-/************************************************************************/
-
-int
-g_ver_iprotocol(con)
-CONNECTION con;
-{
-#ifdef VERIFY_PROTOCOL
- char ver = GDB_PROTOCOL_VERSION;
- char theirs;
- int len;
- int old_nbio;
- int onoff = 0; /* for ioctl to turn off */
-
- /*
- * Because the connection was accepted on a non-blocking
- * listening socket, the connection itself may be non-blocking.
- * We can't tolerate that here. It will be reset later.
- */
- if (ioctl(con->in.fd, FIONBIO, (char *)&onoff) < 0) {
- g_stop_with_errno(con);
- gdb_perror("Can't turn off FIONBIO in g_ver_iprotocol");
- return;
- }
-
- do {
- len = read(con->in.fd, &theirs, 1);
- if (len == (-1)) {
- g_stop_with_errno(con);
- return;
- }
- } while (len !=1) ;
-
- if (theirs == ver)
- con->status = CON_UP;
- else
- con->status = CON_STOPPED;
-
- while (write(con->out.fd, &ver, 1) < 0) {
- g_stop_with_errno(con);
- return;
- }
-#else
- con->status = CON_UP;
-#endif
-}
-
-
-/************************************************************************/
-/*
-/* sever_connection (sever_connection)
-/*
-/* Unconditionally, but cleanly, terminates a connection. All
-/* pending operations on the connection are cancelled, and the
-/* file descriptor for the connection is closed. This routine
-/* should be called directly from applications wishing to shut
-/* down a connection. No transmissions are attempted
-/* by this routine. Returns NULL, in the hope that applications
-/* will assign this to their old CONNECTION variable.
-/*
-/************************************************************************/
-
-CONNECTION
-sever_connection(con)
-CONNECTION con;
-{
- if (con == NULL)
- return NULL;
- GDB_CHECK_CON(con, "sever_connection")
- if (con->status == CON_UP || con->status == CON_STARTING)
- g_stop_connection(con);
- if (con->status != CON_STOPPED)
- gdb_de_allocate_connection(con);
-
- return NULL;
-}
-
-
-/************************************************************************/
-/*
-/* g_stop_with_errno
-/*
-/* This connection is stopping because of a problem on a syscall.
-/* We record the errno in the connection descriptor for inspection
-/* by the application, then stop the connection.
-/*
-/************************************************************************/
-
-int
-g_stop_with_errno(con)
-CONNECTION con;
-{
- con->errno = errno;
- g_stop_connection(con);
-
-}
-
-
-/************************************************************************/
-/*
-/* g_stop_connection
-/*
-/* Unconditionally, but cleanly, terminates a connection. All
-/* pending operations on the connection are cancelled, and the
-/* file descriptor for the connection is closed. This routine is
-/* for internal use. Applications call sever_connection, which
-/* also de_allocates the descriptor. No transmissions are attempted
-/* by this routine.
-/*
-/************************************************************************/
-
-int
-g_stop_connection(con)
-CONNECTION con;
-{
- /*
- * Shutdown activity on the two half connections.
- */
- g_cleanup_half_connection(&(con->in));
- g_cleanup_half_connection(&(con->out));
-
- /*
- * Remove the file descriptor from the select bit maps
- */
- if (!(con->in.flags & HCON_UNUSED) && con->in.fd >= 0)
- FD_CLR(con->in.fd, &gdb_crfds);
- if (!(con->out.flags & HCON_UNUSED) && con->out.fd >= 0)
- FD_CLR(con->out.fd, &gdb_cwfds);
- /*
- * Close the file descriptor. Note, this presumes that in fact
- * 1) in is never the unused half and
- * 2) when the connection is bi-directional, in and out share an
- * fd. We could do with a more elaborate scheme to control
- * this in the future.
- */
- (void) close(con->in.fd);
-
- /*
- * Mark the connection as stopping. We can't reclaim the
- * descriptor until the application does a sever, or else there
- * would be a risk of re-allocating it out from under the application.
- */
-
- con->status = CON_STOPPING;
-
- return;
-}
-
-
-/************************************************************************/
-/*
-/* gdb_de_allocate_connection
-/*
-/* Return a connection whose file descriptors have been closed
-/* to the pool.
-/*
-/************************************************************************/
-
-int
-gdb_de_allocate_connection(con)
-CONNECTION con;
-{
- register int i;
-
- con->status = CON_STOPPED;
-
- i = gdb_mcons-1; /* start at last one used */
-
- /*
- * Reset gdb_mcons to be the number of connections in use
- */
- while (i>=0 && gdb_cons[i].status == CON_STOPPED)
- i--;
-
- gdb_mcons = i + 1;
-}
-
-
-/************************************************************************/
-/*
-/* g_cleanup_half_conection
-/*
-/* Terminate all pending operations on the supplied half
-/* connection. Note that the algorithm used here presumes
-/* that cancel_operation will de-queue the operation descriptor,
-/* therefore we have to be careful here about when we look at
-/* chain pointers.
-/*
-/************************************************************************/
-
-int
-g_cleanup_half_connection(hcon)
-HALF_CONNECTION hcon;
-{
- OPERATION current, next;
-
- current = hcon->op_q_first;
-
- /*
- * Loop through all operations in the queue canceling them.
- * Make sure to pick up pointer to 'next' before the current
- * one is canceled, as cancelling may invalidate the pointer.
- */
-
- while (current != (OPERATION)hcon) {
- next = current->next;
- (void) cancel_operation(current);
- current = next;
- }
-}
-
-
-/************************************************************************/
-/*
-/* create_listening_connection (create_listening_connection)
-/*
-/* Starts a special type of connection which is used to listen
-/* for incoming connection requests. The inbound half-connection
-/* is the only one used for this special kind of connection.
-/*
-/* It is the user's responsibility to insure that only appropriate
-/* types of operation are queued on a connection of this sort. In
-/* general, these connections are intended for internal use by
-/* GDB, and they are not intended to be visible to servers or
-/* clients directly.
-/*
-/* The id supplied should be in one of two forms. If just a
-/* string is supplied then it is presumed to be the name of
-/* a registered tcp service. If the name begins with a #, then
-/* the rest is interpreted as the integer port number to be used.
-/*
-/* In future implementations, the id may have more structure, which
-/* is why we define it as a string.
-/*
-/************************************************************************/
-
-CONNECTION
-create_listening_connection(id)
-char *id;
-{
- register CONNECTION con; /* the connection we're */
- /* creating */
-
- register int slisten; /* socket on which
- we listen for connections */
-
- struct sockaddr_in self;
- int retries = GDB_BIND_RETRY_COUNT;
- int onoff = 1; /* used as argument to */
- /* setsockopt */
- struct servent *serv;
-
- GDB_INIT_CHECK
-
- /*
- * Try to allocate a connection and fill it in with null values.
- */
-
- con = g_make_con();
-
- /*
- * Try to create a socket for listening
- */
- con->in.fd = socket(AF_INET, SOCK_STREAM, 0);
- slisten = con->in.fd; /* easier and faster than */
- /* using con->in.fd all the */
- /* time*/
- if (slisten < 0 ) {
- gdb_perror("create_listening_connection: error creating listen socket");
- (void) g_stop_with_errno(con);
- return con;
- }
- /*
- * Set options so the listening address can be re-used (this
- * has its dangers, but otherwise we can't restart our servers
- * for long periods after they crash because of connections which
- * take a long to time clean up and hold ports in use.)
- */
-
- /* Try 4.2 method */
- if(setsockopt(slisten, SOL_SOCKET, SO_REUSEADDR, (char *)0,0)<0)
- /* that didn't work, try 4.3 */
- if(setsockopt(slisten, SOL_SOCKET, SO_REUSEADDR,
- (char *)&onoff, sizeof(int)) <0)
- GDB_GIVEUP("create_listening_connection: could not set SO_REUSEADDR")
- ;
- /*
- * Make the listening socket non-blocking so we won't have to do
- * selects before polling it (change made by Bill Sommerfeld - wesommer)
- */
- if (ioctl(slisten, FIONBIO, (char *)&onoff) < 0) { /*<==FIX,,,add comment */
- g_stop_with_errno(con);
- gdb_perror("ioctl for listening socket");
- return con;
- }
- /*----------------------------------------------------------*/
- /*
- /* Bind the socket to ourselves, using port derived from
- /* the supplied id string.
- /*
- /*----------------------------------------------------------*/
-
- memset((char *)&self, 0, sizeof(self));
- /*
- * Determine our port number
- */
- if (*id == '#')
- self.sin_port = htons((u_short)atoi(id+1));
- else {
- serv = getservbyname(id, "tcp");
- if (serv == NULL) {
- fprintf(gdb_log,"gdb create_listening_connection: cannot become service named %s\n",id);
- return NULL; /* BUG: causes connetion */
- /* descriptor leakage. Should */
- /* return an error code in */
- /* the connection descriptor*/
- }
- self.sin_port = serv->s_port;
-
- }
- /*
- * Try and re-try the bind until it works or until retry count
- * is exhausted.
- */
- while (bind(slisten,(struct sockaddr *)&self,sizeof(self)) < 0) {
- if (errno == EADDRINUSE && retries--) {
- fprintf(gdb_log,"gdb create_listening_connection: port address in use, will retry %d more time(s)\n",retries+1);
- sleep(GDB_BIND_RETRY_INTERVAL);
- continue;
- } else {
- gdb_perror("gdb create_listening_connection: error binding listen socket");
- g_stop_with_errno(con);
- return con;
- }
- }
-
- /*----------------------------------------------------------*/
- /*
- /* Listen for connections.
- /*
- /*----------------------------------------------------------*/
-
- (void) listen (slisten, 5); /* does not block, just */
- /* sets the maximum backlog */
- /* of pending non-accepted */
- /* cons.*/
-
- con->in.flags |= HCON_LISTEN;
- con->out.flags |= HCON_UNUSED;
- con->status = CON_UP;
- if (con->in.fd +1 > gdb_mfd)
- gdb_mfd = con->in.fd + 1;
- return con;
-}
-
-
-/************************************************************************/
-/*
-/* g_allocate_connection_buffers
-/*
-/* Create a buffer which can be used to receive large
-/* chunks of data from the socket. This is currently done only
-/* on the inbound half connection. Also, the buffers are not freed
-/* once allocated, even if the connection descriptor is re-used.
-/*
-/************************************************************************/
-
-int
-g_allocate_connection_buffers(con)
-CONNECTION con;
-{
- HALF_CONNECTION inbound = &(con->in);
-
- /*
- * See if there is already one allocated, if not, allocate one.
- */
- if (inbound->stream_buffer == (char *)NULL) {
- inbound->stream_buffer =
- db_alloc(inbound->stream_buffer_length);
- }
-
- /*
- * In any case, make sure that it is effectively empty
- */
- inbound -> stream_buffer_next = inbound -> stream_buffer;
- inbound -> stream_buffer_remaining = 0;
-}
+++ /dev/null
-/*
- * $Source$
- * $Header$
- */
-
-#ifndef lint
-static char *rcsid_gdb_db_c = "$Header$";
-#endif
-
-/************************************************************************/
-/*
-/* gdb_db.c
-/*
-/* Authors: Susan Ryan and Noah Mendelsohn
-/*
-/* Copyright: 1986 MIT Project Athena
-/* For copying and distribution information, please see
-/* the file <mit-copyright.h>.
-/*
-/************************************************************************/
-
-#include <mit-copyright.h>
-#include <stdio.h>
-#include <string.h>
-#include "gdb.h"
-
-
-
-\f/************************************************************************/
-/*
-/* start_accessing_db (start_accessing_db)
-/*
-/* 1) Creates a new db structure to describe the database.
-/*
-/* 2) Parses the supplied db_ident into a server and a db name
-/*
-/* 3) Opens a connection to the server, sending the name of the
-/* database as an argument
-/*
-/* 4) Asynchronously receives a return code from the server
-/* to indicate whether the database could be accessed
-/*
-/* 5) If successful, the handle on the new structure is placed into
-/* the variable supplied by the caller. Otherwise, the structure
-/* is de_allocated and failure status is returned to the caller.
-/*
-/* Note: this code was adapted from an earlier synchronous
-/* implementation and there may yet be some loose ends.
-/*
-/************************************************************************/
-
-#define MIN_NAME_LEN 1 /*completely arbitrary */
-#define FAILURE NULL /*until we decide what it should really be */
- /*note if fails returns NULL for the db_handle value*/
- /*as per above */
-
-DATABASE g_make_db();
-int g_iadb();
-
-struct adb_data {
- DATABASE db;
- OPERATION get_retcode;
-};
-
-int
-start_accessing_db (op, db_ident, db_handle)
-OPERATION op;
-char *db_ident;
-DATABASE *db_handle;
-{
- /*----------------------------------------------------------*/
- /*
- /* Declarations
- /*
- /*----------------------------------------------------------*/
-
- register DATABASE db; /* the newly created */
- /* structure */
- register struct adb_data *arg; /* holds our state */
- /* during async operation*/
- char *ident, *server, *temp_server; /* loop variables for */
- /* parsing*/
- char *db_name, *temp_name;
- int count; /* counts chars during parse */
-
- CONNECTION connexn; /* the connection to the */
- /* database server*/
-
- /*----------------------------------------------------------*/
- /*
- /* Execution begins here
- /*
- /* Make sure parameters are correct, then allocate a
- /* structure.
- /*
- /*----------------------------------------------------------*/
-
- GDB_INIT_CHECK
-
- db_name = NULL;
- temp_name = NULL;
- ident = NULL ;
- server = NULL;
-
- GDB_CHECK_OP(op, "start_accessing_db")
-
- if ((db_ident == NULL)|| (strlen(db_ident)<MIN_NAME_LEN)) {
- fprintf (gdb_log,"access_db: correct syntax is db_name@server \n");
- *db_handle = NULL;
- return (OP_CANCELLED);
- }
-
- db = g_make_db();
- *db_handle = db;
-
- /*----------------------------------------------------------*/
- /*
- /* Loop to count lengths of server and database names
- /* Allocate space for each and copy them both
- /*
- /*----------------------------------------------------------*/
-
- count = 1;
- ident = db_ident;
- while (*ident++ != '@') {
- count++;
- }
-
- db_name = db_alloc (count); /* space for db_name */
- /* note: this is cleaned */
- /* up by the tear down rtn*/
-
- count = 1;
- while (*ident++ != '\0')
- count++;
-
- count += strlen(GDB_DB_SERVICE)+1; /* leave room for :service */
- server = db_alloc (count); /* space for host:service */
- /* note: this is cleaned */
- /* up by the tear down rtn*/
-
- /*
- * copy head of db_ident string from db_name@server to db_name
- */
- temp_name = db_name;
- while (*db_ident != '@') {
- *temp_name = *db_ident;
- temp_name++;
- db_ident++;
- }
- *temp_name = '\0';
-
- db->name = db_name;
-
- /*
- * Set up server host name
- */
- temp_server = server;
- db_ident++; /* skip the '@' */
- while (*db_ident!= '\0') {
- *temp_server = *db_ident;
- temp_server++;
- db_ident++;
- }
-
- /*
- * Append :service id to the server host name
- */
- *temp_server++ = ':';
- *temp_server = '\0';
- (void) strcat(server, GDB_DB_SERVICE);
-
- db->server = server;
-
-
- /*----------------------------------------------------------*/
- /*
- /* Create a connection to the server.
- /*
- /*----------------------------------------------------------*/
-
- connexn = start_server_connection (server, db_name);
-
- if (connexn==NULL || connection_status(connexn) != CON_UP) {
- connection_perror(connexn, "Error starting server connection");
- fprintf (gdb_log, "gdb:access_db: couldn't connect to server %s \n", server);
- g_tear_down(*db_handle);
- OP_STATUS(op) = OP_CANCELLED;
- return (OP_CANCELLED);
- }
-
- db->connection = connexn;
-
- /*
- * Start asynchronously receiving the return code from the
- * data base server. May take awhile, since ingres is so
- * slow to start up.
- */
-
- arg = (struct adb_data *)db_alloc(sizeof(struct adb_data));
- arg->get_retcode = create_operation();
- arg->db = db;
-
- start_receiving_object (arg->get_retcode, connexn,
- (char *)&(OP_RESULT(op)), INTEGER_T);
- /*
- * Error handling
- */
- if (OP_STATUS(arg->get_retcode) == OP_CANCELLED) {
- g_tear_down (*db_handle);
- OP_STATUS(op) = OP_CANCELLED;
- delete_operation(arg->get_retcode);
- db_free((char *)arg, sizeof(struct adb_data));
- return OP_CANCELLED;
- }
-
- /*
- * We've successfully queued the receive of the return code.
- * That's about all we have to do if things go well, but if the
- * operation fails later, we have to be there to clean up. To
- * get control back, we queue ourselves as a second operation
- * so we can see how the first did, and so we can free up arg.
- */
- initialize_operation(op, g_iadb, (char *)arg, (int (*)())NULL);
- (void) queue_operation(connexn, CON_INPUT, op);
-
- return OP_RUNNING;
-}
-
- /*----------------------------------------------------------*/
- /*
- /* g_iadb
- /*
- /* Init routine for getting return code on accessing a
- /* database. If all went well, (or even if it didn't), then
- /* we are done. All we have to do is clean up the stuff we've
- /* allocated.
- /*
- /*----------------------------------------------------------*/
-
-/*ARGSUSED*/
-int
-g_iadb(op, hcon, arg)
-OPERATION op;
-HALF_CONNECTION hcon;
-struct adb_data *arg;
-{
- int rc;
-
- /*
- * Figure out how the receipt went
- */
- rc = OP_STATUS(arg->get_retcode);
-
- /*
- * Release all transient data structures.
- */
- if (rc != OP_COMPLETE || op->result != DB_OPEN)
- g_tear_down(arg->db);
- else
- DB_STATUS(arg->db) = DB_OPEN;
-
- delete_operation(arg->get_retcode);
- db_free((char *)arg, sizeof(struct adb_data));
-
- return rc;
-}
-\f
-/************************************************************************/
-/*
-/* g_tear_down
-/*
-/* this is called by access_db and perf_db_op when a fatal error
-/* is reached. It is an attempt to intelligently handle the error,
-/* and tear down connections and data structures if necessary.
-/*
-/* The current version simply tears down everything, perhaps later
-/* versions should make provision for closing the db as necessary,
-/* and/or other less drastic ways to handle the errors.
-/*
-/************************************************************************/
-
-int
-g_tear_down (db_handle)
-DATABASE db_handle;
-{
- register DATABASE db = db_handle;
-
- /*----------------------------------------------------------*/
- /*
- /* If the db is opened, and the connexn is severed,
- /* some error handling, closing of the db should be done
- /* at the server.
- /*
- /* Also, at the server, perhaps a return code to indicate
- /* that user tried to open non-existant db???
- /*
- /*----------------------------------------------------------*/
-
-
- if (db==NULL)
- return;
-
-
- (void) sever_connection (db->connection);
-
- /*
- * Free up the separately allocated strings to which the
- * database descriptor points
- */
- gdb_fstring(db->server);
- gdb_fstring(db->name);
-
- /*
- * Free the descriptor itself
- */
- db_free ((char *)db,sizeof(struct db_struct));
- return;
-}
-\f
-/************************************************************************/
-/*
-/* g_make_db
-/*
-/* Allocate and initialize a database descriptor structure.
-/*
-/************************************************************************/
-
-DATABASE
-g_make_db()
-{
- register DATABASE db;
-
- db = (DATABASE)db_alloc (sizeof(struct db_struct));
- db->id = GDB_DB_ID;
- db->connection = NULL;
- db->name = NULL;
- db->server = NULL;
- DB_STATUS(db) = DB_CLOSED;
-
- return db;
-}
-\f/************************************************************************/
-/*
-/* access_db (access_db)
-/*
-/* Does a start_accessing_db and waits for it to complete.
-/*
-/************************************************************************/
-
-int
-access_db (db_ident, db_handle)
-char *db_ident;
-DATABASE *db_handle;
-{
- register OPERATION op;
- register int status;
- register int result;
-
- GDB_INIT_CHECK
-
- /*
- * Create an operation and use it to asynchronously access
- * the database
- */
- op = create_operation();
- (void) start_accessing_db(op, db_ident, db_handle);
-
- /*
- * Wait for it to complete, note whether the operation completed
- * at all, and if so, whether it returned a successful result
- * in accessing the database. Then reclaim the space used for
- * the operation.
- */
- (void) complete_operation(op);
- status = OP_STATUS(op);
- result = OP_RESULT(op);
-
- delete_operation(op);
-
- /*
- * Tell the caller either that we were interrupted, or pass
- * on the actual result of accessing the database. If it
- * failed, then tear everything down after all.
- */
- if (status==OP_COMPLETE)
- return result;
- else
- return status;
-}
-\f/************************************************************************/
-/*
-/* start_performing_db_operation (start_performing_db_operation)
-/*
-/* Asynchronously performs any operation except for a query
-/* on the remote database.
-/*
-/* The operation is encoded as a GDB string and sent to the server.
-/*
-/* An integer return code is received back and returned to the caller.
-/*
-/* Note that this operation executes on both the outbound and inbound
-/* half connections. Since there is no explicit sync between the two
-/* directions, operations like this pipeline freely from requestor
-/* to server, but there is no way to cancel this operation once it
-/* has started without severing the accompanying connection.
-/*
-/************************************************************************/
-
-int g_ipdb();
-
-struct pdb_data {
- DATABASE db; /* the database we're */
- /* working on */
- OPERATION send_request; /* used to send the string */
- /* containing the db oper. */
- /* to be performed */
- OPERATION get_retcode; /* used to get back the */
- /* response to our request */
- STRING s; /* the operation string */
- /* itself. This is sent. */
-};
-
-#define MIN_REQUEST_LEN 1 /*completely arbitrary */
-#undef FAILURE
-#define FAILURE -1
-
-int
-start_performing_db_operation (op, db_handle,request)
-OPERATION op;
-DATABASE db_handle;
-char *request;
-{
- /*----------------------------------------------------------*/
- /*
- /* Declarations
- /*
- /*----------------------------------------------------------*/
-
- register struct pdb_data *arg; /* holds our state */
- /* during async operation*/
- register DATABASE db = db_handle; /* fast working copy */
-
- /*----------------------------------------------------------*/
- /*
- /* Execution begins here
- /*
- /* Make sure parameters are correct, then allocate a
- /* structure.
- /*
- /*----------------------------------------------------------*/
-
- GDB_CHECK_OP(op, "start_performing_db_operation ")
- if (db==NULL) {
- fprintf (gdb_log, "gdb: start_performing_db_operation: supplied database is NULL\n");
- OP_STATUS(op) = OP_CANCELLED;
- return OP_CANCELLED;
- }
-
- GDB_CHECK_DB(db, "start_performing_db_operation")
-
- if (DB_STATUS(db) != DB_OPEN) {
- fprintf (gdb_log, "gdb: start_performing_db_operation: request to closed database ");
- OP_STATUS(op) = OP_CANCELLED;
- return OP_CANCELLED;
- }
-
- if (db->connection == NULL) {
- fprintf (gdb_log,
- "gdb: start_performing_db_operation: connection severed, request cancelled\n");
- OP_STATUS(op) = OP_CANCELLED;
- return OP_CANCELLED;
- }
-
- if (connection_status(db->connection) != CON_UP ) {
- fprintf (gdb_log, "gdb: start_performing_db_operation: problems maintaining connection ");
- connection_perror(db->connection, "Reason for connection failure");
- fprintf (gdb_log, "request cancelled \n");
- OP_STATUS(op) = OP_CANCELLED;
- return OP_CANCELLED;
- }
-
- if ((request == NULL) || (strlen (request)<MIN_REQUEST_LEN)) {
- fprintf (gdb_log, "gdb: start_performing_db_operation: request either missing or too short\n");
- OP_STATUS(op) = OP_CANCELLED;
- return OP_CANCELLED;
- /*should we disallow empty requests? */
- }
-
-
- /*----------------------------------------------------------*/
- /*
- /* Asynchronously send the request to the server
- /*
- /*----------------------------------------------------------*/
-
- /*
- * Allocate a structure to hold our state while we're gone
- * waiting for this to complete.
- */
-
- arg = (struct pdb_data *)db_alloc(sizeof(struct pdb_data));
- arg->db = db;
- arg->send_request = create_operation();
-
- /*
- * Send the request string to the server
- */
- STRING_DATA(arg->s) = request;
- MAX_STRING_SIZE(arg->s) = strlen (request) +1;
- start_sending_object (arg->send_request, db->connection,
- (char *)&(arg->s), STRING_T);
- if (OP_STATUS(arg->send_request) == OP_CANCELLED) {
- OP_STATUS(op) = OP_CANCELLED;
- delete_operation(arg->send_request);
- db_free((char *)arg, sizeof(struct pdb_data));
- return OP_CANCELLED;
- }
-
- /*----------------------------------------------------------*/
- /*
- /* Asynchronously receive the return code (note, we
- /* really don't know whether the request has even been
- /* sent yet...doesn't really matter.)
- /*
- /*----------------------------------------------------------*/
-
- arg->get_retcode = create_operation();
- /*
- * This must come here as it sets op_result
- */
- initialize_operation(op, g_ipdb, (char *)arg, (int (*)())NULL);
-
- start_receiving_object (arg->get_retcode, db->connection,
- (char *)&(OP_RESULT(op)), INTEGER_T);
- if (OP_STATUS(arg->get_retcode) == OP_CANCELLED) {
- OP_STATUS(op) = OP_CANCELLED;
- (void) cancel_operation(arg->send_request);/* this could be a bug, */
- /* because we introduce */
- /* indeterminism into */
- /* the reply stream, probably */
- /* should shutdown the whole */
- /* db here */
- delete_operation(arg->send_request);
- delete_operation(arg->get_retcode);
- db_free((char *)arg, sizeof(struct adb_data));
- return OP_CANCELLED;
- }
-
- /*
- * We've successfully queued the receive of the return code.
- * That's about all we have to do if things go well, but if the
- * operation fails later, we have to be there to clean up. To
- * get control back, we queue ourselves as a second operation
- * so we can see how the first did, and so we can free up arg.
- */
- (void) queue_operation(db->connection, CON_INPUT, op);
- return OP_RUNNING;
-}
-
- /*----------------------------------------------------------*/
- /*
- /* g_ipdb
- /*
- /* Init routine for getting return code on performin a db
- /* operation. If all went well, (or even if it didn't),
- /* then we are done. All we have to do is clean up the
- /* stuff we've allocated.
- /*
- /*----------------------------------------------------------*/
-
-/*ARGSUSED*/
-int
-g_ipdb(op, hcon, arg)
-OPERATION op;
-HALF_CONNECTION hcon;
-struct pdb_data *arg;
-{
- int rc1, rc2;
-
- /*
- * Figure out how the receipt went
- */
- rc1 = OP_STATUS(arg->send_request);
- rc2 = OP_STATUS(arg->get_retcode);
-
- /*
- * Release all transient data structures.
- */
- if (rc1 != OP_COMPLETE || rc2 != OP_COMPLETE)
- g_tear_down(arg->db);
-
- delete_operation(arg->send_request);
- delete_operation(arg->get_retcode);
- db_free((char *)arg, sizeof(struct pdb_data));
-
- return rc2;
-}
-\f
-/************************************************************************/
-/*
-/* perform_db_operation (perform_db_operation)
-/*
-/* Do a database operation synchronously. This just calls
-/* the async routine and waits for it to complete.
-/*
-/************************************************************************/
-
-perform_db_operation (db_handle,request)
-DATABASE db_handle;
-char *request;
-{
- register OPERATION op;
- register int status;
- register int result;
-
- /*
- * Create an operation and use it to asynchronously perform
- * the operation
- */
- op = create_operation();
- (void) start_performing_db_operation(op, db_handle, request);
-
- /*
- * Wait for it to complete, note whether the operation
- * completed at all, and if so, whether it returned a
- * successful result. Then reclaim the space used for the
- * operation.
- */
- (void) complete_operation(op);
- status = OP_STATUS(op);
- result = OP_RESULT(op);
-
- delete_operation(op);
-
- /*
- * Tell the caller either that we were interrupted, or pass
- * on the actual result of accessing the database. If it
- * failed, then tear everything down after all.
- */
- if (status==OP_COMPLETE)
- return result;
- else
- return status;
-}
-\f/************************************************************************/
-/*
-/* start_db_query (start_db_query)
-/*
-/* Asynchronously performs a database query on the remote
-/* database.
-/*
-/* The operation is encoded as a GDB string and sent to the server.
-/*
-/* An integer return code is received back and returned to the caller.
-/*
-/* If the return code indicates success, then we go into a loop
-/* receiving the retrieved data. Each returned tuple is preceeded by
-/* a so-called yes/no flag, which indicates whether tuple data is really
-/* to follow. Last tuple is followed by a NO flag.
-/*
-/* Note that this operation executes on both the outbound and inbound
-/* half connections. Since there is no explicit sync between the two
-/* directions, operations like this pipeline freely from requestor
-/* to server, but there is no way to cancel this operation once it
-/* has started without severing the accompanying connection.
-/*
-/************************************************************************/
-
-int g_idbq();
-int g_cdbq();
-
-struct dbq_data {
- /*
- * Following may be used throughout processing
- */
- DATABASE db; /* the database we're */
- /* working on */
- RELATION rel;
- TUPLE_DESCRIPTOR tpd;
- /*
- * used primarily in first phase for sending query and getting
- * return code
- */
- OPERATION send_query; /* used to send the string */
- /* containing the query */
- /* to be performed */
- OPERATION send_descriptor; /* used to send the tuple */
- /* descriptor to the server */
- OPERATION get_retcode; /* used to get back the */
- /* response to our request */
- STRING s; /* the operation string */
- /* itself. This is sent. */
- /*
- * Following are used during later phase to receive the tuples
- */
- int state; /* are we expecting a yes/no */
- /* or a tuple next? */
-#define YESNO 1
-#define TUPDATA 2
- int yesno; /* an indicator of whether */
- /* another tuple is to follow*/
-#define YES 1
- OPERATION receive_yesno_or_data;
- TUPLE tup; /* a place to put */
- /* the next tuple */
-};
-
-int
-start_db_query (op, db_handle,rel, query)
-OPERATION op;
-DATABASE db_handle;
-RELATION rel;
-char *query;
-{
- /*----------------------------------------------------------*/
- /*
- /* Declarations
- /*
- /*----------------------------------------------------------*/
-
- register struct dbq_data *arg; /* holds our state */
- /* during async operation*/
- register DATABASE db = db_handle; /* fast working copy */
-
- /*----------------------------------------------------------*/
- /*
- /* Execution begins here
- /*
- /* Make sure parameters are correct, then allocate a
- /* structure.
- /*
- /*----------------------------------------------------------*/
-
- GDB_CHECK_OP(op, "start_db_query ")
-
- if (rel ==NULL) {
- fprintf (gdb_log, "gdb: query_db: input rel is null \n");
- OP_STATUS(op) = OP_CANCELLED;
- return OP_CANCELLED;
- }
-
- if (db==NULL) {
- fprintf (gdb_log, "gdb: start_db_query: supplied database is NULL\n");
- OP_STATUS(op) = OP_CANCELLED;
- return OP_CANCELLED;
- }
-
- GDB_CHECK_DB(db, "start_db_query")
-
- if (DB_STATUS(db) != DB_OPEN) {
- fprintf (gdb_log, "gdb: start_db_query: request to closed database ");
- OP_STATUS(op) = OP_CANCELLED;
- return OP_CANCELLED;
- }
-
- if (db->connection == NULL) {
- fprintf (gdb_log,"gdb: start_db_query: connection severed, request cancelled\n");
- OP_STATUS(op) = OP_CANCELLED;
- return OP_CANCELLED;
- }
-
- if (connection_status(db->connection) != CON_UP ) {
- fprintf (gdb_log,"gdb: start_db_query: problems maintaining connection ");
- connection_perror(db->connection, "Reason for connection failure");
- fprintf (gdb_log,"request cancelled \n");
- OP_STATUS(op) = OP_CANCELLED;
- return OP_CANCELLED;
- }
-
- if (query == NULL || *query == '\0') {
- fprintf (gdb_log, "gdb: start_db_query: request string is null\n");
- OP_STATUS(op) = OP_CANCELLED;
- return OP_CANCELLED;
- }
-
-
- /*----------------------------------------------------------*/
- /*
- /* Asynchronously send the query to the server
- /*
- /*----------------------------------------------------------*/
-
- /*
- * Allocate a structure to hold our state while we're gone
- * waiting for this to complete.
- */
-
- arg = (struct dbq_data *)db_alloc(sizeof(struct dbq_data));
- arg->db = db;
- arg->rel = rel;
- arg->send_query = create_operation();
-
- /*
- * Send the query string to the server
- */
- (void) string_alloc(&(arg->s), strlen(query)+11);
- (void) strcpy(STRING_DATA(arg->s), "retrieve ");
- (void) strcat(STRING_DATA(arg->s), query);
- MAX_STRING_SIZE(arg->s) = strlen (query) +11;
- start_sending_object (arg->send_query, db->connection,
- (char *)&(arg->s), STRING_T);
- if (OP_STATUS(arg->send_query) == OP_CANCELLED) {
- OP_STATUS(op) = OP_CANCELLED;
- delete_operation(arg->send_query);
- string_free(&(arg->s));
- db_free((char *)arg, sizeof(struct dbq_data));
- return OP_CANCELLED;
- }
-
- /*
- * Send the tuple descriptor to the server
- */
-
- arg->send_descriptor = create_operation();
- arg->tpd = DESCRIPTOR_FROM_RELATION(arg->rel);
-
- start_sending_object (arg->send_descriptor, db->connection,
- (char *)&(arg->tpd), TUPLE_DESCRIPTOR_T);
- if (OP_STATUS(arg->send_descriptor) == OP_CANCELLED) {
- OP_STATUS(op) = OP_CANCELLED;
- (void) cancel_operation(arg->send_query);/* this could be a bug, */
- /* because we introduce */
- /* indeterminism into */
- /* the reply stream, probably */
- /* should shutdown the whole */
- /* db here */
- delete_operation(arg->send_query);
- delete_operation(arg->send_descriptor);
- string_free(&(arg->s));
- db_free((char *)arg, sizeof(struct dbq_data));
- return OP_CANCELLED;
- }
-
- /*----------------------------------------------------------*/
- /*
- /* Asynchronously receive the return code (note, we
- /* really don't know whether the query/and the descriptor
- /* have even been sent yet...doesn't really matter.)
- /*
- /*----------------------------------------------------------*/
-
- arg->get_retcode = create_operation();
- start_receiving_object (arg->get_retcode, db->connection,
- (char *)&(OP_RESULT(op)), INTEGER_T);
- if (OP_STATUS(arg->get_retcode) == OP_CANCELLED) {
- OP_STATUS(op) = OP_CANCELLED;
- (void) cancel_operation(arg->send_query);/* this could be a bug, */
- /* because we introduce */
- /* indeterminism into */
- /* the reply stream, probably */
- /* should shutdown the whole */
- /* db here */
- (void) cancel_operation(arg->send_descriptor);
- string_free(&(arg->s));
- delete_operation(arg->send_query);
- delete_operation(arg->send_descriptor);
- delete_operation(arg->get_retcode);
- db_free((char *)arg, sizeof(struct adb_data));
- return OP_CANCELLED;
- }
-
- /*
- * We've successfully queued the receive of the return code.
- * That's about all we have to do if things go well, but if the
- * operation fails later, we have to be there to clean up. To
- * get control back, we queue ourselves as a second operation
- * so we can see how the first did, and so we can free up arg.
- */
- initialize_operation(op, g_idbq, (char *)arg, (int (*)())NULL);
- (void) queue_operation(db->connection, CON_INPUT, op);
-
- return OP_RUNNING;
-}
-
- /*----------------------------------------------------------*/
- /*
- /* g_idbq
- /*
- /* Init routine for getting return code on performing a
- /* bd query. If there was an error, then we are done except for
- /* cleaning up all the dynamic memory we allocated.
- /* If the return code was 0,then we must asynchronously
- /* do the following iteratively until a no is received:
- /*
- /* while (async_receive(yes/no) == yes) {
- /* async receive new tuple
- /* add it to the relation
- /* }
- /*
- /*----------------------------------------------------------*/
-
-/*ARGSUSED*/
-int
-g_idbq(op, hcon, arg)
-OPERATION op;
-HALF_CONNECTION hcon;
-struct dbq_data *arg;
-{
- int rc1, rc2, rc3;
-
- /*----------------------------------------------------------*/
- /*
- /* See how the three asynchronous operations went,and
- /* clean up after them.
- /*
- /*----------------------------------------------------------*/
-
- /*
- * Figure out how the receipt went
- */
- rc1 = OP_STATUS(arg->send_query);
- rc2 = OP_STATUS(arg->send_descriptor);
- rc3 = OP_STATUS(arg->get_retcode);
-
- /*
- * Release all transient data structures which were used in the
- * preliminary operations.
- */
- delete_operation(arg->send_query);
- delete_operation(arg->get_retcode);
- string_free(&(arg->s));
- /*
- * If we've failed for any reason, then mark ourselves complete and
- * return.
- */
- if (rc1 != OP_COMPLETE || rc2 != OP_COMPLETE|| rc3 != OP_COMPLETE
- || OP_RESULT(op) != OP_SUCCESS) {
- OP_STATUS(op) = rc3; /* we must have done */
- /* about as well as */
- /* the last one */
-
- db_free((char *)arg, sizeof(struct dbq_data));
- return rc3; /* tell the dispatcher */
- /* that we're either */
- /* cancelled or complete */
- }
-
- /*----------------------------------------------------------*/
- /*
- /* We've successfully received a return code of 0 from
- /* Ingres, which means we are now going to begin the
- /* yes/no loop.
- /*
- /*----------------------------------------------------------*/
-
- op->fcn.cont = g_cdbq; /* after the preempting */
- /* receive completes, the */
- /* dispatcher will call */
- /* this routine. */
- arg->state = YESNO; /* tell continuation routine */
- /* that we're receiving */
- /* a yes/no */
- arg->tup = NULL; /* so we won't try to clean */
- /* it up */
- arg->receive_yesno_or_data = create_operation();
-
- preempt_and_start_receiving_object(arg->receive_yesno_or_data,
- op,
- (char *)&(arg->yesno),
- INTEGER_T);
- return OP_PREEMPTED;
-}
-
- /*----------------------------------------------------------*/
- /*
- /* g_cdbq
- /*
- /* Continuation routine for receiving results of a query.
- /* Tbis is called repeatedly each time either a yes/no or
- /* a new tuple is received. It repeatedly preempts itself
- /* to receive the next yes/no or tuple until a 'no'
- /* is finally received.
- /*
- /*----------------------------------------------------------*/
-
-/*ARGSUSED*/
-int
-g_cdbq(op, hcon, arg)
-OPERATION op;
-HALF_CONNECTION hcon;
-struct dbq_data *arg;
-{
- /*----------------------------------------------------------*/
- /*
- /* See whether the preempting operation completed
- /* successfully. If not, we just clean up and cancel
- /*
- /*----------------------------------------------------------*/
-
- if (OP_STATUS(arg->receive_yesno_or_data) != OP_COMPLETE) {
- delete_operation(arg->receive_yesno_or_data);
- if (arg->tup != NULL)
- delete_tuple(arg->tup);
- db_free((char *)arg, sizeof(struct dbq_data));
- OP_STATUS(op) = OP_CANCELLED;
- return OP_CANCELLED;
- }
-
- /*----------------------------------------------------------*/
- /*
- /* Whatever it was, we received it cleanly. If it
- /* was tuple data, then accept it and prepare to receive
- /* a yesno. If it was a yes, then prepare to receive
- /* the tuple data. If it was a NO, then we're all done.
- /*
- /* Note that g_cdbg will be recalled by the dispatcher
- /* after the preempting routines have completed.
- /*
- /*----------------------------------------------------------*/
-
- /*
- * New TUPLE DATA
- */
-
- if (arg->state == TUPDATA) {
- ADD_TUPLE_TO_RELATION(arg->rel, arg->tup);
- arg->tup = NULL; /* so we won't try to */
- /* delete it in case of error*/
- reset_operation(arg->receive_yesno_or_data);
- arg->state = YESNO;
- preempt_and_start_receiving_object(arg->receive_yesno_or_data,
- op,
- (char *)&(arg->yesno),
- INTEGER_T);
- return OP_PREEMPTED;
- }
-
- /*
- * We just received a yes or no. If it's a YES, prepare to
- * receive some more tuple data.
- */
- if (arg->yesno == YES) {
- arg->tup = create_tuple(arg->tpd);
- reset_operation(arg->receive_yesno_or_data);
- arg->state = TUPDATA;
- preempt_and_start_receiving_object(arg->receive_yesno_or_data,
- op,
- (char *)arg->tup,
- TUPLE_DATA_T);
- return OP_PREEMPTED;
- }
- /*
- * We just received a NO. Looks like we're all done cleanly.
- */
- delete_operation(arg->receive_yesno_or_data);
- if (arg->tup != NULL)
- delete_tuple(arg->tup);
- db_free((char *)arg, sizeof(struct dbq_data));
- OP_STATUS(op) = OP_COMPLETE;
- return OP_COMPLETE;
-
-}
-\f
-/************************************************************************/
-/*
-/* db_query (db_query)
-/*
-/* Perform a relational query on the specified database.
-/*
-/* This just calls the asynchronous form of doing a query and
-/* waits for it to complete.
-/*
-/*
-/************************************************************************/
-
-int
-db_query(db_handle, rel, query)
-DATABASE db_handle;
-RELATION rel;
-char *query;
-{
- register OPERATION op;
- register int status;
- register int result;
-
- /*
- * Create an operation and use it to asynchronously perform
- * the operation
- */
- op = create_operation();
- (void) start_db_query(op, db_handle, rel, query);
-
- /*
- * Wait for it to complete, note whether the operation
- * completed at all, and if so, whether it returned a
- * successful result. Then reclaim the space used for the
- * operation.
- */
- (void) complete_operation(op);
- status = OP_STATUS(op);
- result = OP_RESULT(op);
-
- delete_operation(op);
-
- /*
- * Tell the caller either that we were interrupted, or pass
- * on the actual result of accessing the database. If it
- * failed, then tear everything down after all.
- */
- if (status==OP_COMPLETE)
- return result;
- else
- return status;
-}
+++ /dev/null
-/*
- * $Source$
- * $Header$
- */
-
-#ifndef lint
-static char *rcsid_gdb_debug_c = "$Header$";
-#endif
-
-/************************************************************************/
-/*
-/* gdb_debug.c
-/*
-/* Debugging interfaces for gdb. Some of these are
-/* designed to be called from dbx.
-/*
-/* routines included are:
-/*
-/* gdb_debug set the debugging status flags
-/*
-/* gd_types_print prints all the available types currently in
-/* the type table
-/*
-/* gd_con_status prints all the status codes for connections
-/*
-/* gd_sum_cons summarizes all current connections detailing
-/* for each flags, half connections with their
-/* respective operation queues.
-/*
-/* gd_op_q (halfcon) prints the queue associated with a
-/* specified half connection (not directly callable)
-/*
-/* gd_op_status a listing of the available states of an
-/* operation
-/*
-/* Copyright 1986 MIT Project Athena
-/* For copying and distribution information, please see
-/* the file <mit-copyright.h>.
-/*
-/************************************************************************/
-
-#include <mit-copyright.h>
-#include <stdio.h>
-#include "gdb.h"
-
-/************************************************************************/
-/*
-/* gdb_debug
-/*
-/* Toggle a debugging flag. Warning: the interface to this routine
-/* may change over time.
-/*
-/************************************************************************/
-
-int
-gdb_debug(flag)
-{
- gdb_Debug ^= flag; /* toggle the flag */
-}
-
-/************************************************************************/
-/*
-/* print_relation
-/*
-/************************************************************************/
-
-int
-print_relation(name, relation)
-char *name;
-RELATION relation;
-{
- FCN_PROPERTY(RELATION_T,FORMAT_PROPERTY)(name, (char *)&(relation));
-}
-/************************************************************************/
-/*
-/* print_tuple
-/*
-/************************************************************************/
-
-int
-print_tuple(name, tuple)
-char *name;
-TUPLE tuple;
-{
- FCN_PROPERTY(TUPLE_T,FORMAT_PROPERTY)(name, (char *)&(tuple));
-}
-
-/************************************************************************/
-/*
-/* print_tuple_descriptor
-/*
-/************************************************************************/
-
-int
-print_tuple_descriptor(name, tuple_descriptor)
-char *name;
-TUPLE_DESCRIPTOR tuple_descriptor;
-{
- FCN_PROPERTY(TUPLE_DESCRIPTOR_T,FORMAT_PROPERTY)(name,
- (char *)&(tuple_descriptor));
-}
-
-/************************************************************************/
-/*
-/* gd_types_print
-/*
-/* This is a routine for printing all the available types and
-/* their typecodes.
-/*
-/************************************************************************/
-
-int
-gd_types_print ()
-{
- register int i;
-
- printf ("\n\nTHE AVAILABLE TYPES WITH THEIR TYPE CODES ARE: \n\n");
-
- printf ("typecode name\n");
-
- for (i = 0; i < gdb_n_types; i++) {
- printf ("%2d %s \n", i, STR_PROPERTY(i,NAME_PROPERTY));
- }
-}
-\f
-/************************************************************************/
-/*
-/* con_status
-/*
-/* This routine will print all the status codes for operations
-/* This is just a listing of the status numbers located in gdb.h
-/*
-/************************************************************************/
-
-int
-gd_con_status ()
-{
- /*----------------------------------------------------------*/
- /*
- /* REMEMBER... these need to be fixed when the connection
- /* status coded in gdb.h are redefined.
- /*
- /*----------------------------------------------------------*/
-
- printf ("THE STATUS CODES ARE: \n\n");
- printf (" CODE STATUS\n");
- printf (" 1 CON_STOPPED\n");
- printf (" 2 CON_UP\n");
- printf (" 3 CON_STARTING\n");
- printf (" 4 CON_STOPPING\n");
-
-}
-
-\f
-/************************************************************************/
-/*
-/* summarize connections (gd_sum_con)
-/*
-/************************************************************************/
-
-gd_sum_con (index)
-int index;
-{
- if ((index > gdb_mcons) || (gdb_cons[index].status<1)) {
- printf ("gdb_cons[%d] is not a valid connection \n",index);
- return;
- }
-
- if (gdb_cons[index].status == CON_STOPPED) {
- printf ("connection gdb_cons[%d] is stopped\n",index);
- return;
- }
-
- /*----------------------------------------------------------*/
- /*
- /* REMEMBER this also must be changed when the def'n
- /* of status fields in gdb.h is changed
- /*
- /*----------------------------------------------------------*/
-
-
- printf ("status of connection number %d is %2d \n",index,gdb_cons[index].status);
- printf ("The information for each half-connexn: \n\n");
-
- printf (" the inbound half-connection\n");
- printf (" status: %2d \n",gdb_cons[index].in.status);
- printf (" flags : %2d \n",gdb_cons[index].in.status);
- printf (" file descr: %2d \n",gdb_cons[index].in.fd);
- printf (" The operation queue is:\n");
- gd_op_q (&(gdb_cons[index].in));
-
- printf (" the outbound half-connection\n");
- printf (" status: %2d \n",gdb_cons[index].out.status);
- printf (" flags : %2d \n",gdb_cons[index].out.status);
- printf (" file descr: %2d \n",gdb_cons[index].out.fd);
- printf (" The operation queue is:\n");
- gd_op_q (&(gdb_cons[index].out));
- }
-
-\f
-/************************************************************************/
-/*
-/* op_q (gd_op_q)
-/*
-/************************************************************************/
-
-
-int
-gd_op_q (half_con)
-HALF_CONNECTION half_con;
-
-{
- int i; /*counter for the ith
- queued op */
- OPERATION current;
-
- current = half_con->op_q_first;
-
- i = 0;
-
- if (current == NULL) {
- printf ("no operations in queue yet\n");
- return ;
- }
-
-
- printf ("OPERATION STATUS\n\n");
-
- while (current != (OPERATION)half_con) {
- printf ("%2d %2d \n", i++ , current->status);
- current = current ->next;
- }
-}
-\f
-/************************************************************************/
-/*
-/* op status
-/* this is a routine in which all the status codes and their
-/* translations are printed.
-/*
-/************************************************************************/
-
-int
-gd_op_status ()
-{
- /*----------------------------------------------------------*/
- /*
- /* REMEMBER these also need to be changed when
- /* states of an operation in gdb.h is redefined
- /*
- /*----------------------------------------------------------*/
-
- printf ("CODE OPERATION STATE\n\n");
- printf (" 1 OP_NOT_STARTED\n");
- printf (" 2 OP_QUEUED\n");
- printf (" 3 OP_RUNNING\n");
- printf (" 4 OP_COMPLETE\n");
- printf (" 5 OP_CANCELLING\n");
- printf (" 6 OP_CANCELLED\n");
- printf (" 7 OP_MARKED\n");
-}
+++ /dev/null
-/*
- * $Header$
- */
-
-#ifndef lint
-static char *rcsid_gdb_fserv_c = "$Header$";
-#endif
-
-
-/************************************************************************
- *
- * gdb_fserv.c
- *
- * GDB - Routines to implement forking servers.
- *
- * Author: Noah Mendelsohn
- * Copyright: 1986 MIT Project Athena
- * For copying and distribution information, please see
- * the file <mit-copyright.h>.
- *
- ************************************************************************/
-
-#include <mit-copyright.h>
-#include <stdio.h>
-#include <sys/types.h>
-#include <sys/uio.h>
-#include <sys/socket.h>
-#include <sys/wait.h>
-#include <sys/signal.h>
-#include "gdb.h"
-#include <sys/resource.h>
-#ifdef POSIX
-#include <unistd.h>
-#endif
-
-
-/************************************************************************
- *
- * create_forking_server (create_forking_server)
- *
- * Called by an application to turn itself into a forking model
- * server. Returns from this routine occur only in the forked
- * children. The parent lives in this routine forever, waiting
- * for incoming connection requests and doing the appropriate
- * forking.
- *
- * Children are expected to do their own cleanup, but this routine
- * does do the work of reaping the resulting zombie processes.
- *
- * ARGUMENTS:
- * ----------
- *
- * service-id identifies the port to be used for
- * listening. Same rules as for
- * create_listening_connection.
- *
- * validate-rtn pointer to a function to be called to
- * validate the incoming client. Should
- * return TRUE if client is acceptable,
- * else false. If this is NULL, all clients
- * are accepted.
- *
- * GLOBAL VARIABLES
- * ----------------
- *
- * Children created by this routine inherit the global variables
- * gdb_sockaddr_of_client, which is of type sockaddr_in and
- * gdb_socklen, which is the returned length of the sockaddr.
- * These are the Berkeley identifiers of the clients as accepted.
- * Use of this interface is non-portable to other than Berkeley
- * systems.
- *
- * The client's request tuple may be found in gdb_client_tuple.
- *
- ************************************************************************/
-
-
-CONNECTION
-create_forking_server(service, validate)
-char *service;
-int (*validate)();
-{
- void start_accepting_client();
- CONNECTION incoming; /* listen for incoming */
- /* children here */
- CONNECTION client = NULL; /* connection to client */
- /* is created here */
- OPERATION listenop; /* used to asynchronously */
- /* listen for a child */
- /* connection request */
- GDB_INIT_CHECK
-
- /*----------------------------------------------------------*/
- /*
- /* Set up parent execution environment
- /*
- /*----------------------------------------------------------*/
-
- g_do_signals(); /* set up signal handling */
-
- incoming = create_listening_connection(service);
- if (incoming == NULL || connection_status(incoming) != CON_UP)
- GDB_GIVEUP("create_forking_server: can't create listening connection")
-
- /*----------------------------------------------------------*/
- /*
- /* Repeatedly listen for incoming
- /*
- /*----------------------------------------------------------*/
-
- listenop = create_operation();
- while (TRUE) {
- gdb_socklen = sizeof(gdb_sockaddr_of_client);
- client = NULL;
- (void) start_accepting_client(incoming, listenop, &client,
- gdb_sockaddr_of_client, &gdb_socklen,
- &gdb_client_tuple);
- if(complete_operation(listenop) != OP_COMPLETE ||
- client == NULL) {
- gdb_perror("GDB create_forking_server: failed to accept client");
- reset_operation(listenop);
- (void) sever_connection(client);
- continue;
- }
-
-
- /*
- * Call the validate routine, if it fails,
- * refuse the client.
- */
- if (validate != NULL && !(*validate)()) {
- reset_operation(listenop);
- start_replying_to_client(listenop, client,
- GDB_REFUSED, "","");
- (void) complete_operation(listenop);
- reset_operation(listenop);
- (void) sever_connection(client);
- continue;
- }
- /*
- * Create the child for this client
- */
- if ((gdb_Debug & GDB_NOFORK) || fork() == 0) {
- /*
- * This is the child, or we're in noforking
- * debug mode.
- */
- reset_operation(listenop);
- start_replying_to_client(listenop, client,
- GDB_ACCEPTED, "","");
- if (complete_operation(listenop) != OP_COMPLETE)
- exit(8);
- return client; /* return to application */
- /* program */
- }
- /*
- * Still in the parent
- */
- (void) sever_connection(client);
- reset_operation(listenop);
- }
-}
-\f
-/************************************************************************/
-/*
-/* gdb_reaper
-/*
-/* Called on SIGCHILD to reap all dead children.
-/*
-/************************************************************************/
-#ifndef POSIX
-int
-#else
-void
-#endif
-gdb_reaper()
-{
-#ifdef POSIX
- int status;
-#else
- union wait status;
-#endif
-
-#ifdef POSIX
- while (waitpid(-1, &status, WNOHANG) >0);
-#else
- while (wait3(&status, WNOHANG, (struct rusage *)0) >0);
-#endif
-}
-\f
-/************************************************************************/
-/*
-/* g_do_signals
-/*
-/* Set up signal handling for a forking server.
-/*
-/************************************************************************/
-
-int
-g_do_signals()
-{
-#ifdef POSIX
- struct sigaction act;
-
- act.sa_handler = gdb_reaper;
- sigemptyset(&act.sa_mask);
- act.sa_flags = 0;
- (void) sigaction(SIGCHLD, &act, (struct sigaction *)0);
-#else /* sun */
- (void) signal(SIGCHLD, gdb_reaper);
-#endif
-}
+++ /dev/null
-/*
- * $Source$
- * $Header$
- */
-
-/************************************************************************/
-/*
-/* gdb_lib.h
-/*
-/* Includes used within the Global Database Facilities library.
-/* Should NOT be included by typical users of gdb.
-/*
-/* Author: Noah Mendelsohn
-/* Copyright: 1986 MIT Project Athena
-/*
-/************************************************************************/
-
-/************************************************************************/
-/*
-/* USER IDENTIFICATION
-/*
-/* gdb_init puts the user's i.d. and hostname as strings here.
-/*
-/************************************************************************/
-
-char *gdb_uname; /* user's string name */
-char *gdb_host; /* name of local host */
- /* goes here */
-
-
-/************************************************************************/
-/*
-/* MEMORY MANAGEMENT
-/*
-/* These vectors point to the memory allocation and free routines.
-/* The standard routines supplied with the system are gdb_am and
-/* gdb_fm, but users may supply their own by clobbering the vectors.
-/*
-/************************************************************************/
-
-char *((*gdb_amv)()) = gdb_am;
-int (*gdb_fmv)() = gdb_fm;
-
-
-
-/************************************************************************/
-/*
-/*
-/* SYSTEM TYPE DEFINITIONS
-/*
-/* Declarations used to control the definition and use of 'types'
-/* as supported by the global database system.
-/*
-/*
-/************************************************************************/
-
-int gdb_Options=0; /* GDB options are set here */
-
-int gdb_Debug=0; /* debugging flags are */
- /* stored here */
-FILE *gdb_log = stderr; /* all debugging */
- /* output goes on stderr*/
-
-char g_errstr[150]; /* build emsgs here */
-
-/*
- * g_type_table
- *
- * This is the table where the actual definitions for the types are
- * kept.
- */
-
-gdb_type_def g_type_table[GDB_MAX_TYPES];
-int gdb_n_types; /* number of entries in */
- /* table */
-/*
- * connection failure indicator
- *
- * This variable is used to communicate between gdb_move_data and
- * g_con_progress without passing an extra parameter through lots
- * of procedure calls. When set to FALSE, it indicates that the
- * connection currently being processed has encountered a fatal error
- * and should be severed.
- */
-int gdb_conok;
-\f
-/************************************************************************/
-/*
-/* CONNECTION AND DATA TRANSMISSION SERVICES
-/*
-/* These are the global data structures used by the routines
-/* which maintain connections and do asynchronous data transfer
-/* on them.
-/*
-/************************************************************************/
-
- /*----------------------------------------------------------*/
- /*
- /* gdb_cons
- /*
- /* This is the array of connection control data
- /* structures for gdb. Every connection has its
- /* structure stored here, but they are in no
- /* particular order. Because the connection data
- /* itself cannot be moved (due to possible dangling
- /* pointers), there may be some unused connections
- /* in the middle of this array. gdb_mcons is the
- /* 1 based number of the highest connection which is
- /* actually in use at this time. This is a considerable
- /* optimization for the typical case where very few
- /* are in use, and turnover is low.
- /*
- /*----------------------------------------------------------*/
-
-int gdb_mcons; /* 0 based index of */
- /* last connection */
- /* currently in use */
-
-int gdb_mfd; /* number of the highest */
- /* file descriptor in use */
- /* for a connection */
-struct con_data gdb_cons[GDB_MAX_CONNECTIONS]; /* actual connection data */
- /* is stored here */
-
- /*----------------------------------------------------------*/
- /*
- /* Bit maps of the file descriptors involved in connections.
- /* Technically, this is redundant with the information in
- /* the connection descriptors above, but it makes select
- /* preparation much faster.
- /*
- /*----------------------------------------------------------*/
-
-fd_set gdb_crfds, gdb_cwfds, gdb_cefds; /* connection related file */
- /* descriptor maps to be */
- /* used in select */
-fd_set last_crfds, last_cwfds, last_cefds; /* these file desc. bit */
- /* masks are set up */
- /* for each select call */
- /* to include the user */
- /* supplied and the */
- /* connection related */
- /* fd's */
-
- /*----------------------------------------------------------*/
- /*
- /* gdb_notime
- /*
- /* Pass this to select when doing a poll.
- /*
- /*----------------------------------------------------------*/
-
-struct timeval gdb_notime = {0,0};
-\f
-/************************************************************************/
-/*
-/* SERVER/CLIENT MANAGEMENT
-/*
-/* Definitions used in starting and maintaining communication
-/* between servers and clients (as opposed to peers.)
-/*
-/************************************************************************/
-
-TUPLE_DESCRIPTOR gdb_tosrv; /* descriptor for request */
- /* tuples sent to the */
- /* server during negotiation*/
-
-TUPLE_DESCRIPTOR gdb_fmsrv; /* descriptor for request */
- /* tuples sent from the */
- /* server during negotiation*/
-
- /*----------------------------------------------------------*/
- /*
- /* Global variables inherited by a child from a server
- /* parent.
- /*
- /*----------------------------------------------------------*/
-
-TUPLE gdb_client_tuple; /* request tuple sent from */
- /* the client */
-
-char gdb_sockaddr_of_client[100]; /* this should really be */
- /* sockaddr_in, but I don't */
- /* want everyone to have */
- /* to include all those */
- /* big .h files */
-int gdb_socklen; /* length of above */
-
+++ /dev/null
-/*
- * $Source$
- * $Header$
- */
-
-#ifndef lint
-static char *rcsid_gdb_ops_c = "$Header$";
-#endif
-
-
-/************************************************************************
- *
- * gdb_ops.c
- *
- * GDB - Asynchronous Operations and Their Synchronous
- * Counterparts
- *
- * Author: Noah Mendelsohn
- * Copyright: 1986 MIT Project Athena
- * For copying and distribution information, please see
- * the file <mit-copyright.h>.
- *
- * These routines provide a suite of asynchronous operations
- * on connections.
- *
- ************************************************************************/
-
-#include <mit-copyright.h>
-#include <stdio.h>
-#include "gdb.h"
-#include <netinet/in.h>
-#include <sys/ioctl.h>
-#ifdef SOLARIS
-#include <sys/filio.h>
-#endif
-
-/************************************************************************
- *
- * send_object (send_object)
- *
- * Synchronous form of start_sending_object. Returns either
- * OP_CANCELLED, or OP_RESULT(op).
- *
- ************************************************************************/
-
-int
-send_object(con, objp, type)
-CONNECTION con;
-char *objp;
-int type;
-{
- register OPERATION op;
- register int retval;
-
-
- op = create_operation();
- start_sending_object(op, con, objp, type);
- (void) complete_operation(op);
- if (OP_STATUS(op) == OP_COMPLETE)
- retval = OP_RESULT(op);
- else
- retval = OP_STATUS(op);
- delete_operation(op);
- return retval;
-}
-
-
-/************************************************************************/
-/*
-/* start_send_object (g_snobj)
-/*
-/* Start the asynchronous transmission of a gdb object.
-/* Note that this routine must be passed the address of the object,
-/* not the object itself.
-/*
-/* The following three routines work together, and may be considered
-/* as a single entity implementing the operation. The first merely
-/* saves away its arguments and queues the operation on the designated
-/* connection. These stay there until they percolate to the head of
-/* the queue. The second is the initialization routine, which is
-/* called by the connection maintenance logic when the operation
-/* first reaches the head of the queue. This routine encodes
-/* the supplied data for transmission, and then sends it. If the
-/* transmission executes synchronously, then the third routine is
-/* called immediately to clean up. If not, the third routine is
-/* marked as the 'continuation' routine, which will cause its
-/* invocation when the transmission completes.
-/*
-/* The data is preceded by its length expressed as a 32-bit number in
-/* network byte order.
-/*
-/************************************************************************/
-
-struct obj_data {
- char *objp; /* address of the object to */
- /* be sent */
- int type; /* type code for the object */
- /* to be sent*/
- char *flattened; /* address of first byte */
- /* of flattened data */
- int len; /* length of the flattened */
- /* data */
-};
-
-int g_isnobj();
-int g_csnobj();
-
-int
-start_sending_object(op, con, objp, type)
-OPERATION op;
-CONNECTION con;
-char *objp;
-int type;
-{
- struct obj_data *arg;
-
- /*
- * Make sure the supplied connection is a legal one
- */
- GDB_CHECK_CON(con, "start_sending_object")
- GDB_CHECK_OP(op, "start_sending_object")
-
- arg = (struct obj_data *)db_alloc(sizeof(struct obj_data));
-
- arg->objp = objp;
- arg->type = type;
- initialize_operation(op, g_isnobj, (char *)arg, (int (*)())NULL);
- (void) queue_operation(con, CON_OUTPUT, op);
-}
-
- /*----------------------------------------------------------*/
- /*
- /* g_isnobj
- /*
- /* Init routine for sending an object. This routine is
- /* called by the connection management logic when the send
- /* request percolates to the top of the queue. This routine
- /* reformats the data into an appropriate form for transmission.
- /* The format used is a length, represented as a 32-bit # in
- /* network byte order, followed by the data itself. The
- /* continuation routine below is called, either synchronously
- /* or asynchronously, once the transmission is complete.
- /*
- /*----------------------------------------------------------*/
-
-int
-g_isnobj(op, hcon, arg)
-OPERATION op;
-HALF_CONNECTION hcon;
-struct obj_data *arg;
-{
- /*
- * Find out the encoded length of the data
- */
- arg->len = FCN_PROPERTY(arg->type, CODED_LENGTH_PROPERTY)
- (arg->objp, hcon);
-
- /*
- * Allocate space and flatten (encode) the data
- */
- arg->flattened = db_alloc(arg->len+sizeof(int32));
- *(uint32 *)arg->flattened = htonl((uint32)arg->len);
-
- FCN_PROPERTY(arg->type, ENCODE_PROPERTY)
- (arg->objp, hcon, arg->flattened+sizeof(int32));
-
- /*
- * Set up continuation routine in case it's needed after the return
- */
- op->fcn.cont = g_csnobj;
-
- /*
- * Start sending the data, maybe even complete
- */
- if (gdb_send_data(hcon, arg->flattened, arg->len + sizeof(int32)) ==
- OP_COMPLETE) {
- return g_csnobj(op, hcon, arg) ;/* this return is a little */
- /* subtle. As continuation */
- /* routines call each other */
- /* synchronously, the last */
- /* one determines whether we */
- /* completed or are still */
- /* running. That status */
- /* percolates back through */
- /* the entire call chain. */
- } else {
- return OP_RUNNING;
- }
-}
-
-
-
-
-
-
- /*----------------------------------------------------------*/
- /*
- /* g_csnobj
- /*
- /* Continuation routine for sending an object. Since there is
- /* only one transmission, started by the init routine, this is
- /* called when that transmission is done, and it does all the
- /* associated clean up.
- /*
- /*----------------------------------------------------------*/
-
-/*ARGSUSED*/
-int
-g_csnobj(op, hcon, arg)
-OPERATION op;
-HALF_CONNECTION hcon;
-struct obj_data *arg;
-{
- op->result = OP_SUCCESS;
- db_free((char *)arg->flattened, arg->len + sizeof(int32));
- /* free the sent data */
- db_free((char *)arg, sizeof(struct obj_data)); /* free the state structure */
- return OP_COMPLETE;
-}
-
-
-/************************************************************************/
-/*
-/* receive_object (receive_object)
-/*
-/* Synchronous form of start_receiving_object. Returns either
-/* OP_CANCELLED, or OP_RESULT(op).
-/*
-/************************************************************************/
-
-int
-receive_object(con, objp, type)
-CONNECTION con;
-char *objp;
-int type;
-{
- register OPERATION op;
- register int retval;
-
- op = create_operation();
- start_receiving_object(op, con, objp, type);
- (void) complete_operation(op);
- if (OP_STATUS(op) == OP_COMPLETE)
- retval = OP_RESULT(op);
- else
- retval = OP_STATUS(op);
- delete_operation(op);
- return retval;
-}
-
-
-/************************************************************************/
-/*
-/* start_receiving_object (g_rcobj)
-/*
-/* Start the asynchronous receipt of a gdb object. Note that this
-/* routine must be passed the address of the object, not the object
-/* itself. In the case of structured objects, this routine may
-/* allocate the necessary storage. The work to build the object is
-/* done by the object's decode routine.
-/*
-/* The following three routines work together, and may be considered
-/* as a single entity implementing the operation. The first merely
-/* saves away its arguments and queues the operation on the designated
-/* connection. These stay there until they percolate to the head of
-/* the queue. The second is the initialization routine, which is
-/* called by the connection maintenance logic when the operation
-/* first reaches the head of the queue. This routine initiates a read
-/* for the length of the object, and sets up a continuation routine
-/* to read the object itself. When the object itself has been read, it
-/* is decoded and the operation completes.
-/*
-/* The data is preceded by its length expressed as a 32-bit number in
-/* network byte order.
-/*
-/* preempt_and_start_receiving_object (g_prcobj)
-/*
-/* Similar to above, but may be called only from an active operation
-/* (i.e. an init or continue routine) on an inbound half connection.
-/* The receive effectively pre-empts the old operation, which wil
-/* continue after the receive is done.
-/*
-/*
-/************************************************************************/
-
-struct robj_data {
- char *objp; /* address of the object to */
- /* be received */
- int type; /* type code for the object */
- /* to be received */
- char *flattened; /* address of first byte */
- /* of flattened data */
- int len; /* length of the flattened */
- /* data */
-};
-
-int g_ircobj();
-int g_c1rcobj();
-int g_c2rcobj();
-
- /*----------------------------------------------------------*/
- /*
- /* start_receiving_object
- /*
- /*----------------------------------------------------------*/
-
-int
-start_receiving_object(op, con, objp, type)
-OPERATION op;
-CONNECTION con;
-char *objp;
-int type;
-{
- struct robj_data *arg;
-
- /*
- * Make sure the supplied connection is a legal one
- */
- GDB_CHECK_CON(con, "start_receiving_object")
- GDB_CHECK_OP(op, "start_receiving_object")
-
- arg = (struct robj_data *)db_alloc(sizeof(struct robj_data));
-
- arg->objp = objp;
- arg->type = type;
- initialize_operation(op, g_ircobj, (char *)arg, (int (*)())NULL);
- (void) queue_operation(con, CON_INPUT, op);
-}
-
- /*----------------------------------------------------------*/
- /*
- /* preempt_and_start_receiving_object
- /*
- /*----------------------------------------------------------*/
-
-int
-preempt_and_start_receiving_object(op, oldop, objp, type)
-OPERATION op;
-OPERATION oldop;
-char *objp;
-int type;
-{
- struct robj_data *arg;
-
- /*
- * Make sure the supplied connection is a legal one
- */
- GDB_CHECK_OP(op, "preempt_and_start_receiving_object")
- GDB_CHECK_OP(oldop, "preempt_and_start_receiving_object")
-
- arg = (struct robj_data *)db_alloc(sizeof(struct robj_data));
-
- arg->objp = objp;
- arg->type = type;
- initialize_operation(op, g_ircobj, (char *)arg, (int (*)())NULL);
- (void) g_preempt_me(oldop, op);
-}
-
- /*----------------------------------------------------------*/
- /*
- /* g_ircobj
- /*
- /* Initialization routine for receiving an object.
- /* Called when the receive operation percolates to the
- /* top of the queue. First, we must receive the single
- /* 32-bit # which carries the length of the rest of the data.
- /* We do that now, either synchronously or asynchronously.
- /*
- /*----------------------------------------------------------*/
-
-int
-g_ircobj(op, hcon, arg)
-OPERATION op;
-HALF_CONNECTION hcon;
-struct robj_data *arg;
-{
- op->fcn.cont = g_c1rcobj;
- if(gdb_receive_data(hcon, (char *)&(arg->len), sizeof(int32)) == OP_COMPLETE) {
- return g_c1rcobj(op, hcon, arg);/* this return is a little */
- /* subtle. As continuation */
- /* routines call each other */
- /* synchronously, the last */
- /* one determines whether we */
- /* completed or are still */
- /* running. That status */
- /* percolates back through */
- /* the entire call chain. */
- } else {
- return OP_RUNNING;
- }
-}
-
- /*----------------------------------------------------------*/
- /*
- /* g_c1rcobj
- /*
- /* At this point, we have received the length. Now, allocate
- /* the space for the rest of the data, and start receiving
- /* it.
- /*
- /*----------------------------------------------------------*/
-
-int
-g_c1rcobj(op, hcon, arg)
-OPERATION op;
-HALF_CONNECTION hcon;
-struct robj_data *arg;
-{
- /*
- * Now we know the length of the encoded data, convert the length
- * to local byte order, and allocate the space for the receive.
- */
- arg->len = (int) ntohl((uint32)arg->len);
- if (arg->len > 65536)
- return OP_CANCELLED;
-
- arg->flattened = db_alloc(arg->len);
- if (arg->flattened == NULL)
- return OP_CANCELLED;
- /*
- * Now start receiving the encoded object itself. If it all comes in
- * synchronously, then just go on to the c2 routine to decode it and
- * finish up. Else return OP_RUNNING, so the rest of the system
- * can get some work done while we wait.
- */
- op->fcn.cont = g_c2rcobj;
- if(gdb_receive_data(hcon, arg->flattened, arg->len ) == OP_COMPLETE) {
- return g_c2rcobj(op, hcon, arg);
- } else {
- return OP_RUNNING;
- }
-}
-
- /*----------------------------------------------------------*/
- /*
- /* g_c2rcobj
- /*
- /* At this point, all the data has been received. Decode
- /* it into the place provided by the caller, free all
- /* temporarily allocated memory, and return.
- /*
- /*----------------------------------------------------------*/
-
-int
-g_c2rcobj(op, hcon, arg)
-OPERATION op;
-HALF_CONNECTION hcon;
-struct robj_data *arg;
-{
- /*
- * Decode the received data into local representation.
- */
- FCN_PROPERTY(arg->type, DECODE_PROPERTY)
- (arg->objp, hcon, arg->flattened);
- op->result = OP_SUCCESS;
- db_free(arg->flattened, arg->len); /* free the received data */
- db_free((char *)arg, sizeof(struct robj_data)); /* free the state structure */
- return OP_COMPLETE;
-}
-
-
-/************************************************************************/
-/*
-/* complete_operation(complete_operation)
-/*
-/* Wait for a given operation to complete, allowing everything
-/* to progress in the meantime. Returns the last known status
-/* of the operation, which in general will be OP_COMPLETE unless
-/* errors were encountered (and this version of the code doesn't
-/* do error handing right anyway!)
-/*
-/* We do this by (1) calling gdb_progress to assure that all
-/* possible progress has been made, which is always a good thing
-/* to do when we get the chance and (2) looping on calls to
-/* con_select, which will make all possible future progress,
-/* but without burning cycles unnecessarily in the process.
-/*
-/************************************************************************/
-
-int
-complete_operation(op)
-OPERATION op;
-{
- (void) gdb_progress();
-
- while(op->status != OP_COMPLETE && op->status != OP_CANCELLED)
- (void) con_select(0, (fd_set *)NULL, (fd_set *)NULL,
- (fd_set *)NULL, (struct timeval *)NULL);
-
- return op->status;
-
-}
-
-
-/************************************************************************/
-/*
-/* cancel_operation(cancel_operation)
-/*
-/* Attempts to cancel an operation.
-/*
-/************************************************************************/
-
-int
-cancel_operation(op)
-OPERATION op;
-{
- register HALF_CONNECTION hcon = op->halfcon;
-
- if (op->status != OP_RUNNING && op->status != OP_QUEUED)
- return op->status;
-
- if (hcon == NULL)
- GDB_GIVEUP("cancel_operation: operation is queued but half connection is unknown")
-
- /*
- * If we're at the head of the queue and running, then we have to
- * call the cancelation routine for this particular operation so
- * it can clean up.
- */
- if (op->prev == (OPERATION)hcon) {
- if (op->status == OP_RUNNING && op->cancel != NULL)
- (*op->cancel)(op->halfcon, op->arg);
- }
-
- /*
- * Looks safe, now cancel it.
- */
- op->next->prev = op->prev; /* de-q it */
- op->prev->next = op->next; /* " " " */
- op->status = OP_CANCELLED;
- op->halfcon = NULL;
-
- return OP_CANCELLED;
-}
-
-
-/************************************************************************/
-/*
-/* start_listening
-/*
-/* Start the asynchronous acquisition of a connection. This
-/* results in the queuing of a GDB "OPERATION" to do the
-/* requested listening.
-/*
-/************************************************************************/
-
-struct lis_data {
- char *otherside; /* data returned from an */
- /* accept */
- int *otherlen; /* length of the otherside */
- /* field */
- int *fdp; /* ptr to the fd of the */
- /* newly accepted */
- /* connection */
-};
-
-int g_ilis();
-int g_clis();
-
-void
-gdb_start_listening(op, con, otherside, lenp, fdp)
-OPERATION op;
-CONNECTION con;
-char *otherside;
-int *lenp;
-int *fdp;
-{
- struct lis_data *arg;
-
- GDB_INIT_CHECK
-
- /*
- * Make sure the supplied connection is a legal one
- */
- GDB_CHECK_CON(con, "start_listening")
- GDB_CHECK_OP(op, "start_listening")
-
- arg = (struct lis_data *)db_alloc(sizeof(struct lis_data));
-
- arg->otherside = otherside;
- arg->otherlen = lenp;
- arg->fdp = fdp;
- initialize_operation(op, g_ilis, (char *)arg, (int (*)())NULL);
- (void) queue_operation(con, CON_INPUT, op);
-}
-
- /*----------------------------------------------------------*/
- /*
- /* g_ilis
- /*
- /* Init routine for doing a listen.
- /*
- /*----------------------------------------------------------*/
-
-int
-g_ilis(op, hcon, arg)
-OPERATION op;
-HALF_CONNECTION hcon;
-struct lis_data *arg;
-{
- int rc;
-
- /*
- * Set up continuation routine in case it's needed after the return
- */
- op->fcn.cont = g_clis;
-
- /*
- * Try doing the listen now, and then decide whether to go
- * right on to the continuation routine or to let things hang
- * for the moment.
- */
- rc = gdb_start_a_listen(hcon, arg->otherside, arg->otherlen, arg->fdp);
- if (rc==OP_COMPLETE) {
- return g_clis(op, hcon, arg); /* this return is a little */
- /* subtle. As continuation */
- /* routines call each other */
- /* synchronously, the last */
- /* one determines whether we */
- /* completed or are still */
- /* running. That status */
- /* percolates back through */
- /* the entire call chain. */
- } else {
- return OP_RUNNING;
- }
-}
-
-
-
- /*----------------------------------------------------------*/
- /*
- /* g_clis
- /*
- /* Continuation routine for accepting a connection.
- /*
- /* At this point, the fd has been accepted and all
- /* the necessary information given back to the caller.
- /*
- /*----------------------------------------------------------*/
-
-/*ARGSUSED*/
-int
-g_clis(op, hcon, arg)
-OPERATION op;
-HALF_CONNECTION hcon;
-struct lis_data *arg;
-{
- op->result = OP_SUCCESS;
- db_free((char *)arg, sizeof(struct lis_data));
- /* free the state structure */
- return OP_COMPLETE;
-}
-
-
-/************************************************************************/
-/*
-/* start_accepting_client
-/*
-/* Start the asynchronous acquisition of a client. This queueable
-/* operation first tries to accept a connection. On this connection,
-/* it reads a startup string from the client, and then completes.
-/*
-/* The return values from this are not quite what you might expect.
-/* In general, the operation will show complete, rather than cancelled,
-/* if it gets as far as creating the new connection at all. If
-/* subsequent activities result in errors from system calls, then
-/* this operation will complete with a status of OP_COMPLETE and a
-/* result of OP_CANCELLED. In this case, the applications IS given
-/* a connection descriptor for the new connection, and that descriptor
-/* has an errno value indicating why the failure occurred. The
-/* caller must then sever this connection to free the descriptor.
-/*
-/************************************************************************/
-
-struct acc_data {
- char *otherside; /* data returned from an */
- /* accept */
- int *otherlen; /* length of the otherside */
- /* field */
- OPERATION listenop; /* used to listen for */
- /* the fd */
- OPERATION receiveop; /* used when receiving */
- /* tuple from the client */
- CONNECTION con; /* the connection we're */
- /* trying to create */
- CONNECTION *conp; /* this is where the caller */
- /* wants the connection */
- /* returned */
- TUPLE *tuplep; /* pointer to tuple we */
- /* are going to receive */
- /* from new client */
-};
-
-int g_iacc();
-int g_i2acc();
-
-void
-start_accepting_client(listencon, op, conp, otherside, lenp, tuplep)
-CONNECTION listencon;
-OPERATION op;
-CONNECTION *conp;
-char *otherside;
-int *lenp;
-TUPLE *tuplep;
-{
- struct acc_data *arg;
-
- GDB_INIT_CHECK
-
- /*
- * Make sure the supplied connection and operation are legal
- */
- GDB_CHECK_CON(listencon, "start_accepting_client")
- GDB_CHECK_OP(op, "start_accepting_client")
-
- arg = (struct acc_data *)db_alloc(sizeof(struct acc_data));
-
- arg->otherside = otherside;
- arg->otherlen = lenp;
- arg->conp = conp;
- *conp = NULL; /* in case we fail */
- arg->listenop = create_operation();
- arg->receiveop = create_operation();
- arg->con = g_make_con();
- arg->tuplep = tuplep;
- *tuplep = NULL; /* in case we fail */
-
- /*
- * Queue an operation ahead of us which will accept an fd and
- * put it in arg->con->in. As a byproduct, pick up the from
- * information that we return to the caller.
- */
- gdb_start_listening(arg->listenop, listencon,
- arg->otherside,
- arg->otherlen, &(arg->con->in.fd));
-
- /*
- * Now queue us behind it. By the time we run our init routine,
- * a connection should have been acquired.
- */
- initialize_operation(op, g_iacc, (char *)arg, (int (*)())NULL);
- (void) queue_operation(listencon, CON_INPUT, op);
-}
-
- /*----------------------------------------------------------*/
- /*
- /* g_iacc
- /*
- /* Init routine for accepting a connection. By the
- /* time this runs, the listen has been done, the
- /* 'from' data put in position for the caller, and
- /* the fd plugged into the connection descriptor.
- /* If all went well, fill out the connection descriptor
- /* and then requeue us on that to do the receive of
- /* the requested tuple.
- /*
- /*----------------------------------------------------------*/
-
-/*ARGSUSED*/
-int
-g_iacc(op, hcon, arg)
-OPERATION op;
-HALF_CONNECTION hcon;
-struct acc_data *arg;
-{
- register CONNECTION con = arg->con;
-
- /*
- * Set up 2nd init routine for after we re-queue ourselves
- */
- op->fcn.cont = g_i2acc;
- /*
- * See whether we successfully accepted a connection. If
- * not, we just cancel ourselves. If so, fill out the
- * connection descriptor and related data structures properly,
- * then requeue ourselves on the new connection.
- */
- if (OP_STATUS(arg->listenop) != OP_COMPLETE ||
- OP_RESULT(arg->listenop) != OP_SUCCESS ||
- con->in.fd <=0) {
- (void) sever_connection(con);
- g_clnup_accept(arg);
- op->result = OP_CANCELLED;
- return OP_CANCELLED;
- }
-
- /*
- * OK, we got an fd, but the connection and related structures
- * aren't really set up straight, and the fd must be put
- * into non-blocking mode. There really should be a common
- * routine for this, since some of the logic exists in 2
- * or 3 places.
- */
- con->status = CON_STARTING;
- con->out.fd = con->in.fd;
- g_ver_iprotocol(con); /* make sure we're at */
- /* same level of protocol */
- if (con->status == CON_UP) {
- /*
- * We've successfully started the connection, now mark
- * it for non-blocking I/O. Also, update the high water
- * mark of fd's controlled by our system.
- */
- int nb = 1;
- if(ioctl(con->in.fd, FIONBIO, (char *)&nb)== (-1)) {
- g_stop_with_errno(con);
- *arg->conp = con; /* give failed con to */
- /* caller so he can find */
- /* errno */
- gdb_perror("gdb: ioctl for non-block failed");
- g_clnup_accept(arg);
- op->result = OP_CANCELLED; /* we didn't really, but */
- /* we want caller to look */
- /* at the connection so he */
- /* can find errno*/
- return OP_COMPLETE;
- }
- if (con->in.fd +1 > gdb_mfd)
- gdb_mfd = con->in.fd + 1;
- /*
- * Allocate a buffer, if necessary, and reset buffer pointers
- * so first request will result in a long read into the buffer
- */
- g_allocate_connection_buffers(con);
-
- } else {
- *arg->conp = con; /* give failed con to */
- /* caller so he can find */
- /* errno */
- g_clnup_accept(arg);
- op->result = OP_CANCELLED;
- return OP_COMPLETE;
- }
-
- /*
- * Before we requeue ourselves on the new connection, queue
- * up a receive for the expected tuple. Then we'll be
- * sure that it's there by the time we run.
- */
- start_receiving_object(arg->receiveop, con, (char *)(arg->tuplep),
- TUPLE_T);
- /*
- * Requeue ourselves behind the receive operation.
- */
-
- (void) requeue_operation(con, CON_INPUT, op);
- return OP_REQUEUED;
-}
-
-
-
- /*----------------------------------------------------------*/
- /*
- /* g_i2acc
- /*
- /* Second init routine for accepting a connection.
- /* This one is run after the operation is requeued on
- /* the new connection. By the time we run here, the
- /* attempt to receive the tuple has already been made.
- /* We just check on status and clean-up.
- /*
- /*----------------------------------------------------------*/
-
-/*ARGSUSED*/
-int
-g_i2acc(op, hcon, arg)
-OPERATION op;
-HALF_CONNECTION hcon;
-struct acc_data *arg;
-{
- int rc;
-
- rc = OP_STATUS(arg->receiveop); /* if it completes, then */
- /* so do we! */
- *arg->conp = arg->con; /* give caller the new con */
- if (rc != OP_COMPLETE)
- (void) g_stop_connection(arg->con);
- /*
- * Release all transient data structures.
- */
- g_clnup_accept(arg);
-
- return OP_COMPLETE;
-}
-
- /*----------------------------------------------------------*/
- /*
- /* g_clnup_accept
- /*
- /* Free all data structures used by start_accepting_client.
- /*
- /*----------------------------------------------------------*/
-
-int
-g_clnup_accept(arg)
-struct acc_data *arg;
-{
- delete_operation(arg->listenop);
- delete_operation(arg->receiveop);
- db_free((char *)arg, sizeof(struct acc_data));
-}
+++ /dev/null
-/*
- * $Source$
- * $Header$
- */
-
-#ifndef lint
-static char *rcsid_gdb_serv_c = "$Header$";
-#endif
-
-
-/************************************************************************
- *
- * gdb_serv.c
- *
- * GDB - Routines to implement the server/client model
- * of connections.
- *
- * Author: Noah Mendelsohn
- * Copyright: 1986 MIT Project Athena
- * For copying and distribution information, please see
- * the file <mit-copyright.h>.
- *
- ************************************************************************/
-
-#include <mit-copyright.h>
-#include <stdio.h>
-#include <string.h>
-#include "gdb.h"
-#include <sys/ioctl.h>
-#ifdef SOLARIS
-#include <sys/filio.h>
-#endif
-#ifdef POSIX
-#include <unistd.h>
-#endif
-
- /*----------------------------------------------------------
- *
- * The following field names and types describe the
- * tuple sent from clients to servers during negotiation.
- *
- *----------------------------------------------------------*/
-
-char *g_tsv_field_names[] = {"server_id",
- "parms",
- "host",
- "user"};
-FIELD_TYPE g_tsv_ftypes[] = {STRING_T,
- STRING_T,
- STRING_T,
- STRING_T};
-
-#define TSV_FLDCOUNT 4
-
-#define TSV_SERVER_ID 0
-#define TSV_PARMS 1
-#define TSV_HOST 2
-#define TSV_USER 3
-
-
- /*----------------------------------------------------------*/
- /*
- /* The following field names and types describe the
- /* tuple returned from the server to the client during
- /* negotiation.
- /*
- /*----------------------------------------------------------*/
-
-char *g_fsv_field_names[] = {"disposition",
- "server_id",
- "parms"};
-FIELD_TYPE g_fsv_ftypes[] = {INTEGER_T,
- STRING_T,
- STRING_T};
-
-#define FSV_FLDCOUNT 3
-
-#define FSV_DISPOSITION 0
-#define FSV_SERVER_ID 1
-#define FSV_PARMS 2
-
-
-/************************************************************************/
-/*
-/* gdb_i_srv
-/*
-/* Initialize the server client layer.
-/*
-/* This routine is called during gdb_init to do the processing
-/* common to all server/client handing.
-/*
-/* In particular, we build the tuple descriptors for the
-/* messages used in negotiating the server/client startup.
-/*
-/************************************************************************/
-
-int
-gdb_i_srv()
-{
- gdb_tosrv = create_tuple_descriptor(TSV_FLDCOUNT, g_tsv_field_names,
- g_tsv_ftypes);
- gdb_fmsrv = create_tuple_descriptor(FSV_FLDCOUNT, g_fsv_field_names,
- g_fsv_ftypes);
- gdb_client_tuple = NULL;
- gdb_socklen = sizeof(gdb_sockaddr_of_client);
-}
-
-
-/************************************************************************/
-/*
-/* start_server_connection (start_server_connection)
-/*
-/* This routine is called from a client that wishes to make a
-/* connection to a server. In the current implementation, the
-/* string argument supplied is just the internet name of the
-/* host on which the server runs. This will later be generalized
-/* to a more flexible naming scheme.
-/*
-/* This routine builds a connection to the requested server,
-/* sends the server i.d. and parms to the server (as strings),
-/* and waits for a response indicating whether the server has
-/* agreed to the connection. The server responds in one of three
-/* ways (1) connection accepted (2) connection declined (3) redirect.
-/* In this last case, the server returns a forwarding address to
-/* be substituted for the server_id, and the whole process is tried
-/* again repeatedly until a connection is established or a
-/* retry limit is exceeded.
-/*
-/************************************************************************/
-
-CONNECTION
-start_server_connection(server_id, parms)
-char *server_id;
-char *parms;
-{
- CONNECTION con; /* the connection we're */
- /* creating */
- TUPLE response = NULL; /* each time we try a server */
- /* it sends back its */
- /* response here */
- int retries = GDB_MAX_SERVER_RETRIES; /* number of servers we'll */
- /* try before giving up in */
- /* fear of a loop */
-
- char serv_id[GDB_MAX_SERVER_ID_SIZE]; /* a place to store server */
- /* id's. New ones go here */
- /* when our request is */
- /* forwarded */
- char latest_parms[GDB_MAX_SERVER_PARMS_SIZE];
- /* likewise for parms */
-
- GDB_INIT_CHECK
-
- /*
- * Try to allocate a connection and fill it in with null values.
- */
-
- con = g_make_con();
-
- /*
- * Loop asking different servers to accept our connection
- * until one does or we are flatly refused.
- */
-
- /*
- * Allocate a buffer, if necessary, and reset buffer pointers
- * so first request will result in a long read into the buffer
- */
- g_allocate_connection_buffers(con);
-
-
- g_try_server(&con, server_id, parms, &response);
-
- while ((retries--) &&
- con != NULL &&
- response != NULL &&
- *(int *)FIELD_FROM_TUPLE(response,FSV_DISPOSITION)==GDB_FORWARDED) {
-
- (void) sever_connection(con);
- con = g_make_con();
- (void) strcpy(serv_id,
- STRING_DATA(*(STRING *)
- (FIELD_FROM_TUPLE(response,
- FSV_SERVER_ID))));
- (void) strcpy(latest_parms,
- STRING_DATA(*(STRING *)
- (FIELD_FROM_TUPLE(response,
- FSV_PARMS))));
- null_tuple_strings(response);
- delete_tuple(response);
- g_try_server(&con, serv_id, latest_parms, &response);
- }
-
- /*
- * At this point, we are done trying servers, now find out
- * whether we get to keep the connnection or whether it
- * didn't work. First, see whether the connection is even up.
- */
- if (con == NULL ||
- connection_status(con) != CON_UP) {
- return con;
- }
-
- /*
- * We have at least some active connection, now see whether we
- * are going to get to keep it
- */
- if (response != NULL &&
- *(int *)FIELD_FROM_TUPLE(response,FSV_DISPOSITION) == GDB_ACCEPTED) {
- null_tuple_strings(response);
- delete_tuple(response);
- return con;
- } else {
- if (response != NULL) {
- null_tuple_strings(response);
- delete_tuple(response);
- }
- (void) sever_connection(con);
- return NULL;
- }
-}
-
-/************************************************************************/
-/*
-/* g_try_server
-/*
-/* Builds a single connection to a server and returns status
-/* to indicate whether the connection has been accepted, declined,
-/* or is to be retried. This status is conveyed in a tuple received
-/* back from the server.
-/*
-/************************************************************************/
-
-int
-g_try_server(conp, server_id, parms, responsep)
-CONNECTION *conp;
-char *server_id;
-char *parms;
-TUPLE *responsep;
-{
-
- register CONNECTION con = *conp;
- int flag = 1;
-
- /*
- * In this implementation, we use a single fd for both inbound and
- * outbound traffic. Try to connect to other side. Current
- * implementation of this is synchronous--may be a problem? If the
- * connections come up, then verify the level of protocol being
- * observed on the connections. If incompatible, then turn off the
- * connection.
- */
-
- if(!g_try_connecting(con,server_id) ||
- con->status != CON_STARTING) {
- return; /* If there we an error, */
- /* connection will have been */
- /* left CON_STOPPING with */
- /* possible errno set */
- }
- g_ver_oprotocol(con);
- if (con->status != CON_UP) {
- return;
- }
-
- /*
- * We've successfully started the connection, now mark
- * it for non-blocking I/O. Also, update the high water
- * mark of fd's controlled by our system.
- */
- if(ioctl(con->in.fd, FIONBIO, (char *)&flag)== (-1)) {
- g_stop_with_errno(con);
- gdb_perror("gdb: ioctl for non-block failed");
- return;
- }
- if (con->in.fd +1 > gdb_mfd)
- gdb_mfd = con->in.fd + 1;
-
- g_ask_server(conp, server_id, parms, responsep);
-
- return;
-}
-
-
-/************************************************************************/
-/*
-/* g_ask_server
-/*
-/* Called once we are in touch with the server and our physical
-/* transmission protocol is comprehensible. This routine
-/* sends out a tuple containing the server i.d. and parameter
-/* strings and it returns a tuple received back from the server
-/* containing the server's response.
-/*
-/************************************************************************/
-
-int
-g_ask_server(conp, server_id, parms, responsep)
-CONNECTION *conp;
-char *server_id;
-char *parms;
-TUPLE *responsep;
-{
- register CONNECTION con = *conp;
- TUPLE out_tuple;
- int rc;
- /*----------------------------------------------------------*/
- /*
- /* Create a tuple to be sent out containing the
- /* server_id and parms.
- /*
- /*----------------------------------------------------------*/
-
- out_tuple = create_tuple(gdb_tosrv); /* descriptor was pre- */
- /* built during */
- /* initialization*/
-
- (void) string_alloc((STRING *)FIELD_FROM_TUPLE(out_tuple, TSV_SERVER_ID),
- strlen(server_id)+1);
- (void) strcpy(STRING_DATA(*((STRING *)FIELD_FROM_TUPLE(out_tuple,0))),
- server_id);
-
- if (parms == NULL) parms = "";
- (void) string_alloc((STRING *)FIELD_FROM_TUPLE(out_tuple, TSV_PARMS),
- strlen(parms)+1);
- (void) strcpy(STRING_DATA(*((STRING *)FIELD_FROM_TUPLE(out_tuple,1))),
- parms);
-
- (void) string_alloc((STRING *)FIELD_FROM_TUPLE(out_tuple, TSV_HOST),
- strlen(gdb_host)+1);
- (void) strcpy(STRING_DATA(*((STRING *)FIELD_FROM_TUPLE(out_tuple,
- TSV_HOST))),
- gdb_host);
- (void) string_alloc((STRING *)FIELD_FROM_TUPLE(out_tuple, TSV_USER),
- strlen(gdb_uname)+1);
- (void) strcpy(STRING_DATA(*((STRING *)FIELD_FROM_TUPLE(out_tuple,TSV_USER))),
- gdb_uname);
-
- /*----------------------------------------------------------*/
- /*
- /* Send the tuple to the server, and make sure that
- /* we succeeded.
- /*
- /*----------------------------------------------------------*/
-
- rc = send_object(con, (char *)&out_tuple, TUPLE_T);
-
- null_tuple_strings(out_tuple);
- delete_tuple(out_tuple);
-
- if (rc != OP_SUCCESS) {
- return; /* cleanup from dying send */
- /* should have made this */
- /* CON_STOPPING with errno */
- }
-
- /*----------------------------------------------------------*/
- /*
- /* OK, we sent it out, now lets read back the response.
- /*
- /*----------------------------------------------------------*/
-
- rc = receive_object(con, (char *)responsep, TUPLE_T);
-
- if (rc != OP_SUCCESS) {
- return; /* cleanup from dying send */
- /* should have made this */
- /* CON_STOPPING with errno */
- }
-}
-
-
-/************************************************************************/
-/*
-/* start_replying_to_client
-/*
-/* Queue an operation which will send a reply to the specified
-/* client.
-/*
-/************************************************************************/
-
-struct rtc_data {
- TUPLE reply_data;
- OPERATION tuple_send;
-};
-
-int g_irtc();
-int g_i2rtc();
-
-int
-start_replying_to_client(op, con, disposition, serverid, parms)
-OPERATION op;
-CONNECTION con;
-int disposition;
-char *serverid; /* null terminated */
-char *parms; /* " " */
-{
- register struct rtc_data *arg;
- register TUPLE t;
-
- /*
- * Make sure the supplied connection is a legal one
- */
- GDB_CHECK_CON(con, "start_replying_to_client")
-
- arg = (struct rtc_data *)db_alloc(sizeof(struct rtc_data));
-
- /*
- * create an empty operation and a tuple
- */
- arg->tuple_send = create_operation();
- arg->reply_data = create_tuple(gdb_fmsrv);
- /*
- * Fill in the response tuple
- */
- t = arg->reply_data; /* quicker and easier here */
-
- *(int *)FIELD_FROM_TUPLE(t,FSV_DISPOSITION) = disposition;
- (void) string_alloc((STRING *)FIELD_FROM_TUPLE(t, FSV_SERVER_ID),
- strlen(serverid)+1);
- (void) strcpy(STRING_DATA(*(STRING *)(FIELD_FROM_TUPLE(t, FSV_SERVER_ID))),
- serverid);
-
- (void) string_alloc((STRING *)FIELD_FROM_TUPLE(t, FSV_PARMS),
- strlen(parms)+1);
- (void) strcpy(STRING_DATA(*(STRING *)(FIELD_FROM_TUPLE(t, FSV_PARMS))),
- parms);
-
- /*
- * Queue an operation ahead of us which will send the response tuple
- * to the client
- */
- start_sending_object(arg->tuple_send, con, (char *)&arg->reply_data,
- TUPLE_T);
-
- /*
- * Now queue us behind it. By the time we run our init routine,
- * a connection should have been acquired.
- */
- initialize_operation(op, g_irtc, (char *)arg, (int (*)())NULL);
- (void) queue_operation(con, CON_INPUT, op);
-}
-
- /*----------------------------------------------------------*/
- /*
- /* g_irtc
- /*
- /* Init routine for replying to a client. If all went
- /* well, (or even if it didn't), then we are done.
- /* All we have to do is clean up the stuff we've allocated.
- /*
- /*----------------------------------------------------------*/
-
-/*ARGSUSED*/
-int
-g_irtc(op, hcon, arg)
-OPERATION op;
-HALF_CONNECTION hcon;
-struct rtc_data *arg;
-{
- int rc;
-
- /*
- * Figure out the return information to our caller
- */
- rc = OP_STATUS(arg->tuple_send);
-
- /*
- * Release all transient data structures.
- */
- null_tuple_strings(arg->reply_data);
- delete_tuple(arg->reply_data);
- delete_operation(arg->tuple_send);
- db_free((char *)arg, sizeof(struct rtc_data));
- return rc;
-}
+++ /dev/null
-/*
- * $Header$
- */
-
-#ifndef lint
-static char *rcsid_gdb_struct_c = "$Header$";
-#endif
-
-
-/************************************************************************/
-/*
-/* gdb_struct.c
-/*
-/* GDB - Structured Data Maintenance Routines
-/*
-/* Author: Noah Mendelsohn
-/* Copyright: 1986 MIT Project Athena
-/* For copying and distribution information, please see
-/* the file <mit-copyright.h>.
-/*
-/* These routines implement the following layers of the
-/* Client Library Specification of the GDB system:
-/*
-/* Layer Function
-/* ----- --------
-/* 2 Structured Data Management at a
-/* Single Site
-/*
-/* 4 Memory Management
-/*
-/* 5 String Management
-/*
-/* Some of the routines specified are actually implemented as
-/* macros defined in gdb.h.
-/*
-/************************************************************************/
-
-#include <mit-copyright.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include "gdb.h"
-
-\f
-/************************************************************************/
-/*
-/* MEMORY MANAGEMENT
-/*
-/* In anticipation of the day when we may want to do something
-/* fancy with memory management, all of the gdb routines which
-/* require dynamic allocation of memory call the routines named
-/* db_alloc and db_free. For the moment, these are implemented
-/* as calls to malloc and free.
-/*
-/************************************************************************/
-
- /*----------------------------------------------------------*/
- /*
- /* gdb_am
- /*
- /* Allocate memory for use by gdb. Current implementation
- /* just does a malloc.
- /*
- /*----------------------------------------------------------*/
-
-char *
-gdb_am(bytes)
-int bytes;
-{
- return(malloc((unsigned)bytes));
-}
-
- /*----------------------------------------------------------*/
- /*
- /* gdb_fm
- /*
- /* Return allocated memory. Note: the current
- /* implementation ignores the byte count supplied,
- /* but future implementations may require that it
- /* be correct.
- /*
- /*----------------------------------------------------------*/
-
-/*ARGSUSED*/
-int
-gdb_fm(ptr, bytes)
-char *ptr;
-int bytes;
-{
- free(ptr);
- return;
-}
-\f
-/************************************************************************/
-/*
-/* STRING MANAGEMENT
-/*
-/* To allow dynamic manipulation of strings in gdb without
-/* excessive memory re-allocation, we define a string as a
-/* counted byte space. Though this space will frequently be used
-/* to store a standard null terminated string, that is not
-/* required.
-/*
-/* Current representation for a string is a pointer followed by
-/* an integer length. A null pointer indicates a null string, in
-/* which case the length is arbitrary. Any other pointer is to
-/* memory which was allocated by db_alloc in which must be free'd
-/* eventually with db_free.
-/*
-/************************************************************************/
-
- /*----------------------------------------------------------*/
- /*
- /* string_alloc (string_alloc)
- /*
- /* Fills in supplied string descriptor and returns
- /* pointer to the newly allocated data.
- /*
- /*----------------------------------------------------------*/
-
-char *
-string_alloc(stringp, bytes)
-STRING *stringp; /* pointer to string */
- /* descriptor to be */
- /* filled in */
-int bytes; /* number of bytes to alloc */
-{
- GDB_INIT_CHECK
-
- MAX_STRING_SIZE(*stringp) = bytes; /* put length in returned */
- /* string descriptor-- */
- /* will be irrelavent if */
- /* alloc fails */
-
- STRING_DATA(*stringp) = db_alloc(bytes); /* try to get the data */
- return (STRING_DATA(*stringp)); /* return ptr to new string */
- /* if any */
-}
-
- /*----------------------------------------------------------*/
- /*
- /* string_free (string_free)
- /*
- /* Releases the data space for a gdb string. Must have
- /* been allocated with string_alloc. Remember to pass
- /* in the address of the string descriptor, not the
- /* descriptor itself!
- /*
- /*----------------------------------------------------------*/
-int
-string_free(stringp)
-STRING *stringp;
-{
- GDB_INIT_CHECK
-
- if (stringp->ptr == NULL)
- return;
- db_free(stringp->ptr, stringp->length);
- stringp->ptr = NULL;
- stringp->length = 0;
- return;
-}
-\f
-/************************************************************************/
-/*
-/* STRUCTURED DATA MANAGEMENT AT A SINGLE SITE
-/*
-/* These routines provide the abstraction of typed, structured
-/* data at a single site. Tuples are collections of typed fields,
-/* and they are each described by a tuple descriptor. Relations
-/* are circularly linked lists of tuples. For completeness, a
-/* relation also carries a tuple descriptor, which should match
-/* the descriptor for each of its constituent tuples. This allows
-/* a null relation to be typed.
-/*
-/* Some of the facilities of structured data management are
-/* defined as macros in gdb.h. In many cases, the routines
-/* declared below are known by defines of more descriptive
-/* names, also in gdb.h.
-/*
-/************************************************************************/
-/************************************************************************/
-/*
-/* TUPLE_DESCRIPTOR MANAGEMENT
-/*
-/************************************************************************/
-
-
- /*----------------------------------------------------------*/
- /*
- /* create_tuple_descriptor (create_tuple_descriptor)
- /*
- /* Allocates space for a tuple descriptor and fills
- /* it in. Gives up if space is not available.
- /* Should be passed a list of integer coded types
- /* and a list of string field names.
- /*
- /* Tuple descriptors are reference counted, and they are
- /* not really deleted until the reference count goes
- /* to zero. It is presumed that all callers use the
- /* create and delete routines, or otherwise maintain
- /* the reference count appropriately.
- /*
- /*----------------------------------------------------------*/
-
-
-TUPLE_DESCRIPTOR
-create_tuple_descriptor(number_of_fields, name_list, type_list)
-char *name_list[];
-FIELD_TYPE type_list[];
-int number_of_fields;
-{
- register TUPLE_DESCRIPTOR tpd; /* pointer to new descriptor */
- register int i;
- register int data_len; /* length of the actual */
- int field_len; /* length of current field */
- int align; /* code describing alignment */
- /* requirement for this field*/
- /* (4 for fullword, 1 for */
- /* char, etc.) */
- int next_offset; /* byte offset to next field */
- int descriptor_length; /* length of the part of */
- /* the allocated storage */
- /* actually used for the */
- /* descriptor.*/
- int str_len; /* we also have to retain */
- /* the string names for the */
- /* fields. These are stored */
- /* immediately off the end */
- /* of the descriptor, and */
- /* all are allocated */
- /* together. This is the */
- /* length of the string data */
- char *next_name; /* place to put the next */
- /* copied fieldname*/
-
-
- GDB_INIT_CHECK
-
- /*
- * Calculate size and allocate descriptor
- */
-
- descriptor_length = sizeof(struct tupl_desc) +
- (number_of_fields-1) * sizeof(struct tupld_var);
-
- str_len = 0;
-
- for (i=0; i<number_of_fields; i++)
- str_len += strlen(name_list[i]) +1;
-
- tpd = (TUPLE_DESCRIPTOR)db_alloc(descriptor_length+str_len);
- /* try to allocate it */
-
- if (tpd == NULL)
- GDB_GIVEUP("create_tuple_descriptor (gbd_ctd) could not get enough memory.\n")
-
- /*
- * Fill in the descriptor fields:
- *
- * Each field is aligned according to its own alignment code.
- * Going in to the top of the for loop, next_offset is set to
- * the offset of the first possible byte for storing the next field.
- * During the loop, that number is rounded up, if necessary, to
- * achieve the alignment actually required for the field. Finally,
- * the length of the new field is added, which yields the first
- * possible byte of any field to follow.
- */
-
- tpd->id = GDB_DESC_ID;
-
- tpd->ref_count = 1; /* whoever asked for creation*/
- /* is expected to delete*/
-
- tpd->field_count = number_of_fields;
- tpd->str_len = str_len;
-
- data_len = sizeof(struct tuple_dat) - 1; /* tuple_dat includes the */
- /* first byte of data */
- next_offset = 0;
- next_name = ((char *)tpd) + descriptor_length;
- /* place to put first */
- /* field name*/
- for (i=0; i<number_of_fields; i++) {
- /*
- * Calculate lengths and alignments for the field data.
- */
- field_len = INT_PROPERTY(type_list[i],LENGTH_PROPERTY);
- align = INT_PROPERTY(type_list[i],ALIGNMENT_PROPERTY);
- /*
- * Copy the string field name into the newly allocated
- * space just after the descriptor itself.
- */
- tpd->var[i].name = strcpy(next_name, name_list[i]);
- next_name += strlen(next_name) + 1;
- /*
- * Put in the type and the length for the field data
- */
- tpd->var[i].type = type_list[i];
- tpd->var[i].length = field_len;
- /*
- * Now store the actual offset of this field, and
- * compute the first byte address we could conceivably
- * use for the next field.
- */
- next_offset = GDB_ROUNDUP(next_offset, align);
- tpd->var[i].offset = next_offset;
- next_offset += field_len;
- }
-
- data_len += next_offset;
- tpd->data_len = data_len;
-
- return tpd; /* return the new descriptor */
-}
-
- /*----------------------------------------------------------*/
- /*
- /* delete_tuple_descriptor (delete_tuple_descriptor)
- /*
- /* Return the space for a tuple descriptor
- /*
- /*----------------------------------------------------------*/
-
-int
-delete_tuple_descriptor(t)
-TUPLE_DESCRIPTOR t;
-{
- int descriptor_length;
- register int ref_count; /* buffer the reference */
- /* count here */
-
- if (t == NULL)
- return ;
-
- GDB_CHECK_TPD(t, "delete_tuple_descriptor")
-
-
- /*
- * Decrement the reference count. If it's not zero, then just
- * return.
- */
- if ((ref_count = --(t->ref_count)) >0)
- return ;
- if (ref_count <0)
- GDB_GIVEUP("Tuple descriptor reference count is less than zero")
- /*
- * Current representation is to allocate the space for the string
- * right off the end of the descriptor itself. We therefore have
- * to add their length into the amount we free.
- */
- descriptor_length = gdb_descriptor_length(t->field_count);
- db_free((char *)t, descriptor_length+t->str_len);
-
- return ;
-}
-
- /*----------------------------------------------------------*/
- /*
- /* field_index (field_index)
- /*
- /*----------------------------------------------------------*/
-
-int
-field_index(tuple_descriptor, field_name)
-TUPLE_DESCRIPTOR tuple_descriptor;
-char *field_name;
-{
- register int i;
- register int n;
- register TUPLE_DESCRIPTOR tpd = tuple_descriptor;
-
- /*
- * Make sure supplied descriptor is valid
- */
- if (tpd == NULL)
- GDB_GIVEUP("null tuple descriptor passed to field_index function")
- GDB_CHECK_TPD(tpd, "field_index")
-
- n = tpd -> field_count;
-
- /*
- * Loop through each field in descriptor, return index if match
- */
-
- for(i=0; i<n; i++)
- if (strcmp(field_name, tpd->var[i].name) == 0)
- return i;
- /*
- * No match, return -1
- */
- return (-1);
-}
-
-\f
-/************************************************************************/
-/*
-/* TUPLE MANAGEMENT
-/*
-/************************************************************************/
-
- /*----------------------------------------------------------*/
- /*
- /* create_tuple (create_tuple)
- /*
- /* Allocate space for a new tuple, given its
- /* descriptor. Giveup if out of memory.
- /*
- /*----------------------------------------------------------*/
-
-TUPLE
-create_tuple(descriptor)
-TUPLE_DESCRIPTOR descriptor;
-{
- register TUPLE t;
-
- GDB_CHECK_TPD(descriptor, "create_tuple")
-
- t = (TUPLE)db_alloc(descriptor -> data_len);
-
- if (t == NULL)
- GDB_GIVEUP("create_tuple (create_tuple) could not allocate enough memory.\n")
-
- t->id = GDB_TUP_ID;
-
- t->desc = descriptor; /* fill in descriptor */
- /* pointer in new tuple */
- REFERENCE_TUPLE_DESCRIPTOR(descriptor); /* bump the reference count */
-#ifdef GDB_CHECK
- /*
- * Only for the sake of keeping things clean, null out the pointers.
- * Wastes time, but helps debugging.
- */
- t->next = t->prev = NULL;
-#endif
-
- return t;
-}
-
- /*----------------------------------------------------------*/
- /*
- /* delete_tuple (delete_tuple)
- /*
- /* Release the data space occupied by a tuple.
- /*
- /*----------------------------------------------------------*/
-
-int
-delete_tuple(t)
-TUPLE t;
-{
- register TUPLE_DESCRIPTOR tpd;
- if (t==NULL)
- GDB_GIVEUP("Delete_tuple called with null tuple")
- GDB_CHECK_TUP(t, "delete_tuple")
- tpd = t->desc;
- db_free((char *)t, t->desc->data_len);
- delete_tuple_descriptor(tpd); /* does a reference counted */
- /* delete*/
-}
-
- /*----------------------------------------------------------*/
- /*
- /* initialize_tuple (initialize_tuple)
- /*
- /* Set each field in tuple to its null value.
- /*
- /*----------------------------------------------------------*/
-
-int
-initialize_tuple(t)
-TUPLE t;
-{
- register char *field_data; /* pointer to first byte */
- /* of field data in tuple*/
- register TUPLE_DESCRIPTOR tpd; /* pointer to descriptor */
- register int i; /* counter of fields */
- int num_fields; /* total number of fields */
-
- /*
- * Return if no tuple at all supplied--perhaps this should be
- * an error. If supplied, make sure it looks like a tuple.
- */
-
- if (t == NULL)
- return;
-
- GDB_CHECK_TUP(t, "initialize_tuple")
-
- /*
- * Set up to loop through fields: get tuple descriptor, field count
- * and pointer to first data byte in the tuple.
- */
-
- tpd = t->desc;
- num_fields = tpd->field_count;
- field_data = t->data; /* address of first byte of */
- /* user data */
-
-
- /*
- * For each field in the tuple, loop calling its null value
- * initialization routine.
- */
-
- for (i=0; i<num_fields; i++) {
- FCN_PROPERTY(tpd->var[i].type, NULL_PROPERTY)
- (field_data+tpd->var[i].offset);
- }
-}
-
-
- /*----------------------------------------------------------*/
- /*
- /* null_tuple_strings (null_tuple_strings)
- /*
- /* Reclaim the space for all fields in the tuple
- /* whose type is 'string.'
- /*
- /*----------------------------------------------------------*/
-
-int
-null_tuple_strings(t)
-TUPLE t;
-{
- register char *field_data; /* pointer to first byte of */
- /* field data in tuple */
- register TUPLE_DESCRIPTOR tpd; /* pointer to descriptor */
- register int i; /* counter of fields */
- int num_fields; /* total number of fields */
-
- /*
- * Return if no tuple at all supplied--perhaps this should be
- * an error
- */
-
- if (t == NULL)
- return;
-
- GDB_CHECK_TUP(t, "null_tuple_strings")
-
- /*
- * Set up to loop through fields: get tuple descriptor, field count
- * and pointer to first data byte in the tuple.
- */
-
- tpd = t->desc;
- num_fields = tpd->field_count;
- field_data = t->data; /* address of first byte of */
- /* user data */
-
- /*
- * For each field in the tuple, loop calling its null value
- * initialization routine.
- */
-
- for (i=0; i<num_fields; i++) {
- if(FIELD_TYPE_IN_TUPLE(tpd,i) == STRING_T &&
- (*(char **)(field_data+FIELD_OFFSET_IN_TUPLE(tpd,i)))!=NULL)
- string_free((STRING *)(field_data+
- FIELD_OFFSET_IN_TUPLE(tpd,i)));
- }
-}
-\f
-/************************************************************************/
-/*
-/* RELATION MANAGEMENT
-/*
-/************************************************************************/
-
- /*----------------------------------------------------------*/
- /*
- /* create_relation (create_relation)
- /*
- /*----------------------------------------------------------*/
-
-RELATION
-create_relation(desc)
-TUPLE_DESCRIPTOR desc;
-{
- register RELATION r;
-
- GDB_CHECK_TPD(desc, "create_relation")
-
- r = (RELATION)db_alloc(sizeof(struct rel_dat));
-
- if (r == NULL)
- GDB_GIVEUP("create_relation (create_relation) could not get enough space.\n")
-
- /*
- * Fill in the empty relation. Create a null circular list
- * of tuples and also hang the description.
- */
-
- r->id = GDB_REL_ID;
-
- r->first = (TUPLE)r;
- r->last = (TUPLE)r;
- r->desc = desc;
- REFERENCE_TUPLE_DESCRIPTOR(desc); /* bump the reference count */
-
- return r;
-}
-
- /*----------------------------------------------------------*/
- /*
- /* delete_relation
- /*
- /* Deletes the tuples which comprise a relation.
- /* For each tuple, it does a null-tuple-strings
- /* prior to deleting, but it does not yet handle
- /* any other non-contiguous data.
- /*
- /*----------------------------------------------------------*/
-
-int
-delete_relation(rel)
-RELATION rel;
-{
- register TUPLE t, next;
- TUPLE_DESCRIPTOR desc;
-
- /*
- * Make sure a proper relation is supplied.
- */
-
- if (rel == NULL)
- GDB_GIVEUP("delete_relation called with null relation")
- GDB_CHECK_REL(rel, "delete_relation")
-
- t = FIRST_TUPLE_IN_RELATION(rel);
-
- /*----------------------------------------------------------*/
- /*
- /* Free all the tuples
- /*
- /*----------------------------------------------------------*/
-
- while (t!=NULL) {
- null_tuple_strings(t);
- next = NEXT_TUPLE_IN_RELATION(rel, t);
- delete_tuple(t);
- t = next;
- }
-
- /*----------------------------------------------------------*/
- /*
- /* Give back the memory for the relation
- /*
- /*----------------------------------------------------------*/
-
- desc = DESCRIPTOR_FROM_RELATION(rel);
- db_free((char *)rel, sizeof(struct rel_dat));
- delete_tuple_descriptor(desc); /* does a reference */
- /* counted delete */
-}
-
- /*----------------------------------------------------------*/
- /*
- /* tuples_in_relation
- /*
- /* Returns number of tuples in a relation.
- /*
- /*
- /*----------------------------------------------------------*/
-
-int
-tuples_in_relation(rel)
-RELATION rel;
-{
- register int count;
- register RELATION r=rel;
- register TUPLE t;
-
- count = 0;
-
- for (t=FIRST_TUPLE_IN_RELATION(r);
- t != NULL;
- t=NEXT_TUPLE_IN_RELATION(r,t)) {
- count++;
- }
- return count;
-}
-
-
-
+++ /dev/null
-/*
- * $Source$
- * $Header$
- */
-
-#ifndef lint
-static char *rcsid_gdb_stype_c = "$Header$";
-#endif
-
-
-/************************************************************************
- *
- * gdb_stype.c
- *
- * GDB - System Data Type Definitions
- *
- * Author: Noah Mendelsohn
- * Copyright: 1986 MIT Project Athena
- * For copying and distribution information, please see
- * the file <mit-copyright.h>.
- *
- * This file initializes the definitions for all system defined
- * data types, and it includes the type specific semantic routines
- * for each of the system defined types.
- *
- * The initialization routine which adds these type definitions
- * to the type definition table is at the end of this source file.
- *
- ************************************************************************
- *
- * This file is organized into one section for each system
- * defined type followed at the end by a final section which
- * initializes the type tables. Each of the type specific
- * sections does #defines for each type specific parameter. The
- * gdb_i_stype initialization routine at the end of this source
- * file uses these defines to initialize the appropriate entry in
- * the type definition tables.
- *
- * NOTE: some of the type definitions in this file may be machine
- * dependent.
- *
- ************************************************************************/
-
-#include <mit-copyright.h>
-#include <stdio.h>
-#include <string.h>
-#include "gdb.h"
-#include <netinet/in.h> /* for htonl routine */
-
-
-/************************************************************************
- *
- * INTEGER_T
- *
- ************************************************************************/
-
-#define IN_LEN (sizeof(int))
-#define IN_ALI IN_LEN
-#define IN_NULL g_in_null
-#define IN_CDLEN g_in_cdlen
-#define IN_ENC g_in_enc
-#define IN_DEC g_in_dec
-#define IN_FORM g_in_form
-#define IN_NAME "INTEGER_T"
-
-#define IN_EXTERNSIZE 4 /* length of an encoded */
- /* integer */
- /*----------------------------------------------------------*/
- /*
- /* g_in_null
- /*
- /* Fill in a null value for an integer.
- /*
- /*----------------------------------------------------------*/
-
-int
-g_in_null(dp)
-char *dp; /* pointer to the data */
-{
- *((int *)dp) = 0; /* fill in a null value */
-}
-
- /*----------------------------------------------------------*/
- /*
- /* g_in_cdlen
- /*
- /* Return coded length for an integer. We're currently
- /* using the Berkeley 'htonl' routine which converts
- /* an integer (actually a long, ahem!) to a canonical
- /* 4 byte form.>
- /*
- /*----------------------------------------------------------*/
-
-
-/*ARGSUSED*/
-int
-g_in_cdlen(dp,hcon)
-char *dp; /* pointer to the data */
-HALF_CONNECTION hcon;
-{
- return IN_EXTERNSIZE;
-}
-
- /*----------------------------------------------------------*/
- /*
- /* g_in_enc
- /*
- /* Encode an integer for transmission
- /*
- /*----------------------------------------------------------*/
-
-/*ARGSUSED*/
-char *
-g_in_enc(dp, hcon, outp)
-char *dp; /* pointer to data */
-HALF_CONNECTION hcon; /* connection descriptor */
-char *outp; /* place to put the output */
-{
- register char *cp; /* next char in output */
- register char *op = outp;
- register char *endp = outp+IN_EXTERNSIZE;
-
- uint32 converted; /* the integer goes here */
- /* in network byte order*/
-
- /*
- * Put it in network format, then copy one byte at a time to
- * account for the fact that the RT has trouble with unaligned longs
- */
-
- converted = htonl(*(uint32 *)dp);
-
- cp = (char *)&converted;
- *op++ = *cp++;
- *op++ = *cp++;
- *op++ = *cp++;
- *op++ = *cp++;
-
- return endp; /* return pointer to next */
- /* unused output byte*/
-}
-
- /*----------------------------------------------------------*/
- /*
- /* g_in_dec
- /*
- /* Decode an integer from external form to local
- /* representation.
- /*
- /*
- /*----------------------------------------------------------*/
-
-/*ARGSUSED*/
-char *
-g_in_dec(outp, hcon, inp)
-char *inp; /* pointer to data */
-HALF_CONNECTION hcon; /* connection descriptor */
-char *outp; /* place to put the output */
-{
- register char *ip = inp; /* next byte of input */
- int buffer;
- register char *bp; /* next byte in buffer */
-
- /*
- * Copy a byte at a time to buffer to account for RT difficulties
- * with unaligned ints.
- */
- bp = (char *)&buffer;
- *bp++ = *ip++;
- *bp++ = *ip++;
- *bp++ = *ip++;
- *bp++ = *ip++;
-
- /*
- * Convert it and return pointer to next byte of input.
- */
-
- *(int *)outp = ntohl((u_long)buffer);
- return ip;
-}
-
- /*----------------------------------------------------------*/
- /*
- /* g_in_form
- /*
- /* Format an integer on output logging file for
- /* debugging.
- /*
- /*----------------------------------------------------------*/
-
-int
-g_in_form(name, dp)
-char *name; /* string name of the field */
-char *dp; /* pointer to the data */
-{
- fprintf(gdb_log, "INTEGER_T\t%s=%d\n",name,(*(int *)dp));
-}
-
-
-/************************************************************************/
-/*
-/* STRING_T
-/*
-/************************************************************************/
-
-#define ST_LEN (sizeof(STRING))
-#define ST_ALI (sizeof(char *))
-#define ST_NULL g_st_null
-#define ST_CDLEN g_st_cdlen
-#define ST_ENC g_st_enc
-#define ST_DEC g_st_dec
-#define ST_FORM g_st_form
-#define ST_NAME "STRING_T"
-
- /*----------------------------------------------------------*/
- /*
- /* g_st_null
- /*
- /* Fill in a null value for a string.
- /*
- /*----------------------------------------------------------*/
-int
-g_st_null(dp)
-char *dp; /* pointer to the data */
-{
- register STRING *stp = (STRING *)dp; /* re-type as string */
- STRING_DATA(*stp) = NULL; /* no data */
- MAX_STRING_SIZE(*stp) = 0; /* for cleanliness */
-}
-
- /*----------------------------------------------------------*/
- /*
- /* g_st_cdlen
- /*
- /* Return coded length for a string. We have to send the
- /* actual length of the data along with the data itself.
- /* For this reason, we leave space for a coded integer
- /* in addition to the data bytes. We actually call the
- /* integer coding routines to code the length.
- /*
- /* Note that a separate type understanding null termination
- /* might be an interesting optimization someday.
- /*
- /*----------------------------------------------------------*/
-
-int
-g_st_cdlen(dp,hcon)
-char *dp; /* pointer to the data */
-HALF_CONNECTION hcon;
-{
- register STRING *stp = (STRING *)dp; /* re-type as string */
-
- return (MAX_STRING_SIZE(*stp) +
- g_in_cdlen((char *)&MAX_STRING_SIZE(*stp),hcon));
-}
-
- /*----------------------------------------------------------*/
- /*
- /* g_st_enc
- /*
- /* Encode a string for transmission
- /*
- /*----------------------------------------------------------*/
-
-char *
-g_st_enc(dp, hcon, outp)
-char *dp; /* pointer to data */
-HALF_CONNECTION hcon; /* connection descriptor */
-char *outp; /* place to put the output */
-{
- register STRING *stp = (STRING *)dp; /* re-type as string */
- int len;
- register char *nextp; /* place to put next output */
- /* byte */
- /*
- * Use the integer coding routine to get the length encoded first
- */
-
- len = MAX_STRING_SIZE(*stp); /* length of both source */
- /* and coded form*/
- nextp = (char *)g_in_enc((char *)&len, hcon, outp);
-
- /*
- * Now, copy the data itself after the encoded integer length
- */
- if (len > 0)
- memcpy(nextp, STRING_DATA(*stp), len);
- /* copy the data without */
- /* changing representation*/
- return nextp+len;
-}
-
- /*----------------------------------------------------------*/
- /*
- /* g_st_dec
- /*
- /* Decode a string from external form. We always
- /* allocate new space for the string, intentionally
- /* ignoring any which may have been in use before. If we
- /* freed it, we would not be robust against calls on
- /* uninitialized fields. This may have nasty side
- /* effects if the intention was to leave 'gas' at the end
- /* of the string, but we want to accurately copy the
- /* data. Note that string_free is robust against null
- /* pointers.
- /*
- /*----------------------------------------------------------*/
-
-char *
-g_st_dec(outp, hcon, inp)
-char *inp; /* pointer to input data */
-HALF_CONNECTION hcon; /* connection descriptor */
-char *outp; /* place to put the output */
-{
- register STRING *stp = (STRING *)outp; /* re-type as string */
- int len;
- register char *nextp; /* next byte to scan */
- /*
- * Use the integer coding routine to get the length encoded first
- */
-
- nextp = (char *)g_in_dec((char *)&len, hcon, inp);
-
-
- /*
- * Allocate memory for the string. If length is 0, then null it
- * out. Note that we had considered freeing any existing strings
- * which might be there, but this turns out to cause lots of
- * trouble for the many callers who don't want to initialize before
- * a decode.
- */
- if (len == 0) {
- STRING_DATA(*stp) = NULL;
- MAX_STRING_SIZE(*stp) = 0;
- return nextp;
- }
- (void) string_alloc(stp, len); /* this sets string length */
- /* in addition to doing the */
- /* allocation */
-
- /*
- * Now, copy the data itself
- */
- memcpy(STRING_DATA(*stp), nextp, len); /* copy the data without */
- /* changing representation*/
- return nextp+len;
-}
-
- /*----------------------------------------------------------*/
- /*
- /* g_st_form
- /*
- /* Format a string on output logging file for
- /* debugging.
- /*
- /*----------------------------------------------------------*/
-
-int
-g_st_form(name, dp)
-char *name; /* string name of the field */
-char *dp; /* pointer to the data */
-{
- register STRING *stp = (STRING *)dp; /* re-type as string */
- int len;
- register char *cp; /* next char to print */
- register char *past_end; /* 1st one not to print */
-
- len = MAX_STRING_SIZE(*stp);
- fprintf(gdb_log, "STRING_T\t%s[%d]=\"", name,len);
-
- if (len == 0 ) {
- fprintf(gdb_log, "\"\n");
- return;
- }
-
-
- cp = STRING_DATA(*stp);
- past_end = cp + len;
-
- while (cp < past_end)
- (void) putc(*cp++, gdb_log);
-
- fprintf(gdb_log,"\"\n");
-}
-
-
-/************************************************************************/
-/*
-/* REAL_T
-/*
-/************************************************************************/
-
-#define RL_LEN (sizeof(double))
-#define RL_ALI RL_LEN
-#define RL_NULL g_rl_null
-#define RL_CDLEN g_rl_cdlen
-#define RL_ENC g_rl_enc
-#define RL_DEC g_rl_dec
-#define RL_FORM g_rl_form
-#define RL_NAME "REAL_T"
-
-#define RL_EXTERNSIZE 32 /* length of ascii coding */
- /* must change lengths in */
- /* encode and decode */
- /* routines to match*/
- /*----------------------------------------------------------*/
- /*
- /* g_rl_null
- /*
- /* Fill in a null value for an real.
- /*
- /*----------------------------------------------------------*/
-int
-g_rl_null(dp)
-char *dp; /* pointer to the data */
-{
- *((double *)dp) = 0.0; /* fill in a null value */
-}
-
- /*----------------------------------------------------------*/
- /*
- /* g_rl_cdlen
- /*
- /* Return coded length for an real. For now, we just
- /* code as a 12 digit ASCII converted string. Obviously,
- /* we can do much better in the future.
- /*
- /*----------------------------------------------------------*/
-
-
-/*ARGSUSED*/
-int
-g_rl_cdlen(dp,hcon)
-char *dp; /* pointer to the data */
-HALF_CONNECTION hcon;
-{
- return RL_EXTERNSIZE;
-}
-
- /*----------------------------------------------------------*/
- /*
- /* g_rl_enc
- /*
- /* Encode an real for transmission
- /*
- /*----------------------------------------------------------*/
-
-/*ARGSUSED*/
-char *
-g_rl_enc(dp, hcon, outp)
-char *dp; /* pointer to data */
-HALF_CONNECTION hcon; /* connection descriptor */
-char *outp; /* place to put the output */
-{
- register char *cp; /* next char in output */
- register char *endp = outp+RL_EXTERNSIZE;
-
- /*
- * Convert the data into printable ASCII in the output stream
- * Note that the width in the format below must be less than
- * RL_EXTERNSIZE, because sprintf needs space for its terminating
- * null.
- */
-
- (void) sprintf(outp,"%30le",*((double *)dp));
-
- /*
- * Sprintf produces output of unpredictable length, and with
- * a null termination. Pad it out to the desired length.
- */
-
- cp = outp + strlen(outp); /* find out where convertd */
- /* string stops*/
- while (cp < endp)
- *cp++ = ' '; /* pad to desired length */
-
- return outp+RL_EXTERNSIZE; /* return pointer to next */
- /* unused output byte*/
-}
-
- /*----------------------------------------------------------*/
- /*
- /* g_rl_dec
- /*
- /* Decode an real from external form
- /*
- /*----------------------------------------------------------*/
-
-/*ARGSUSED*/
-char *
-g_rl_dec(outp, hcon, inp)
-char *inp; /* pointer to data */
-HALF_CONNECTION hcon; /* connection descriptor */
-char *outp; /* place to put the output */
-{
- (void) sscanf(inp,"%30le", (double *)outp);
- return inp+RL_EXTERNSIZE;
-}
-
- /*----------------------------------------------------------*/
- /*
- /* g_rl_form
- /*
- /* Format an real on output logging file for
- /* debugging.
- /*
- /*----------------------------------------------------------*/
-
-int
-g_rl_form(name, dp)
-char *name; /* string name of the field */
-char *dp; /* pointer to the data */
-{
- fprintf(gdb_log, "REAL_T\t\t%s=%le\n",name,*((double *)dp) );
-}
-
-
-/************************************************************************/
-/*
-/* DATE_T
-/*
-/************************************************************************/
-
-#define DT_LEN 25 /* see ingres definition */
-#define DT_ALI 1 /* char data, need not align */
-#define DT_NULL g_dt_null
-#define DT_CDLEN g_dt_cdlen
-#define DT_ENC g_dt_enc
-#define DT_DEC g_dt_dec
-#define DT_FORM g_dt_form
-#define DT_NAME "DATE_T"
-
-#define DT_EXTERNSIZE DT_LEN /* length of ascii coding */
- /* must change lengths in */
- /* encode and decode */
- /* routines to match*/
- /*----------------------------------------------------------*/
- /*
- /* g_dt_null
- /*
- /* Fill in a null value for a date.
- /*
- /*----------------------------------------------------------*/
-int
-g_dt_null(dp)
-char *dp; /* pointer to the data */
-{
- register char *cp = dp; /* next character to fill in */
- register char *endp = dp + DT_LEN;
-
- /*
- * Fill the field with character blanks
- */
- while (cp < endp)
- *cp++ = ' ';
-}
-
- /*----------------------------------------------------------*/
- /*
- /* g_dt_cdlen
- /*
- /* Return coded length for an date. For now, we just
- /* code as a 25 digit ASCII converted string.
- /*
- /*----------------------------------------------------------*/
-
-
-/*ARGSUSED*/
-int
-g_dt_cdlen(dp,hcon)
-char *dp; /* pointer to the data */
-HALF_CONNECTION hcon;
-{
- return DT_EXTERNSIZE;
-}
-
- /*----------------------------------------------------------*/
- /*
- /* g_dt_enc
- /*
- /* Encode a date for transmission
- /*
- /*----------------------------------------------------------*/
-
-/*ARGSUSED*/
-char *
-g_dt_enc(dp, hcon, outp)
-char *dp; /* pointer to data */
-HALF_CONNECTION hcon; /* connection descriptor */
-char *outp; /* place to put the output */
-{
- register char *ip = dp; /* next char in input */
- register char *op = outp; /* next char in output */
- register char *endp = op+DT_EXTERNSIZE;
-
- /*
- * Copy the input untransformed to the output
- */
-
- while (op < endp)
- *op++ = *ip++; /* pad to desired length */
-
- return endp; /* return pointer to next */
- /* unused output byte*/
-}
-
- /*----------------------------------------------------------*/
- /*
- /* g_dt_dec
- /*
- /* Decode an date from external form
- /*
- /*----------------------------------------------------------*/
-
-/*ARGSUSED*/
-char *
-g_dt_dec(outp, hcon, inp)
-char *inp; /* pointer to data */
-HALF_CONNECTION hcon; /* connection descriptor */
-char *outp; /* place to put the output */
-{
- register char *ip = inp; /* next char in input */
- register char *op = outp; /* next char in output */
- register char *endp = op+DT_EXTERNSIZE;
-
- /*
- * Copy the input untransformed to the output
- */
-
- while (op < endp)
- *op++ = *ip++; /* pad to desired length */
-
- return endp; /* return pointer to next */
- /* unused output byte*/
-}
-
- /*----------------------------------------------------------*/
- /*
- /* g_dt_form
- /*
- /* Format a date on output logging file for
- /* debugging.
- /*
- /*----------------------------------------------------------*/
-
-int
-g_dt_form(name, dp)
-char *name; /* string name of the field */
-char *dp; /* pointer to the data */
-{
- char buf[DT_EXTERNSIZE+1];
-
- memcpy(buf, dp, DT_EXTERNSIZE); /* copy date to buffer */
- buf[DT_EXTERNSIZE] = '\0'; /* null terminate it */
- fprintf(gdb_log, "DATE_T\t\t%s=%s\n",name,buf);
-}
-
-
-/************************************************************************/
-/*
-/* TUPLE_DESCRIPTOR_T
-/*
-/* The external representation of a tuple descriptor will be to
-/* send the count of the number of fields, and then a one byte
-/* signed integer describing each type followed by all the
-/* corresponding null terminated strings. The tuple descriptor
-/* will really get re-created wth proper offsets and lengths upon
-/* receipt by the create_tuple_descriptor operation.
-/*
-/************************************************************************/
-
-#define TPD_LEN (sizeof(TUPLE_DESCRIPTOR))
-#define TPD_ALI (sizeof(TUPLE_DESCRIPTOR))
-#define TPD_NULL g_tpd_null
-#define TPD_CDLEN g_tpd_cdlen
-#define TPD_ENC g_tpd_enc
-#define TPD_DEC g_tpd_dec
-#define TPD_FORM g_tpd_form
-#define TPD_NAME "TUPLE_DESCRIPTOR_T"
-
- /*----------------------------------------------------------*/
- /*
- /* g_tpd_null
- /*
- /* Fill in a null value for a tuple_descriptor.
- /*
- /*----------------------------------------------------------*/
-int
-g_tpd_null(dp)
-char *dp; /* pointer to the data */
-{
- register TUPLE_DESCRIPTOR *tdp = (TUPLE_DESCRIPTOR *)dp;
- /* re-type as */
- /* tuple_descriptor */
- (*tdp) = NULL; /* no data */
-}
-
- /*----------------------------------------------------------*/
- /*
- /* g_tpd_cdlen
- /*
- /* Return coded length for a tuple_descriptor.
- /*
- /*----------------------------------------------------------*/
-
-int
-g_tpd_cdlen(dp,hcon)
-char *dp; /* pointer to the data */
-HALF_CONNECTION hcon;
-{
- register TUPLE_DESCRIPTOR tdp = *((TUPLE_DESCRIPTOR *)dp);
- /* re-type as */
- /* tuple_descriptor */
- register int coded_len; /* the value we're trying */
- /* to compute */
-
- /*
- * Validate the descriptor
- */
- if (tdp == NULL)
- GDB_GIVEUP("g_tpd_cdlen (coded length) was given a null tuple descriptor\nthis may be due to an attempt to transmit invalid data")
- GDB_CHECK_TPD(tdp,"g_tpd_cdlen: compute coded length of tuple descriptor")
-
- coded_len = g_in_cdlen((char *)&(tdp->field_count),hcon);
- /* we're going to send */
- /* the field count as a */
- /* true integer*/
-
- coded_len += tdp->str_len + tdp->field_count;
- /* space for all the */
- /* strings, with nulls, */
- /* and for the one byte */
- /* types*/
-
- return coded_len;
-
-}
-
- /*----------------------------------------------------------*/
- /*
- /* g_tpd_enc
- /*
- /* Encode a tuple_descriptor for transmission
- /*
- /*----------------------------------------------------------*/
-
-char *
-g_tpd_enc(dp, hcon, outp)
-char *dp; /* pointer to data */
-HALF_CONNECTION hcon; /* connection descriptor */
-char *outp; /* place to put the output */
-{
- register TUPLE_DESCRIPTOR tdp = *((TUPLE_DESCRIPTOR *)dp);
- /* re-type as */
- /* tuple_descriptor */
- register char *nextp; /* place to put next output */
- /* byte */
- register int i; /* a loop counter */
-
- /*
- * Validate the descriptor
- */
- if (tdp == NULL)
- GDB_GIVEUP("g_tpd_enc (encode) was given a null tuple descriptor\nthis may be due to an attempt to transmit invalid data")
- GDB_CHECK_TPD(tdp,"g_tpd_enc: encode tuple descriptor")
-
- /*
- * Use the integer coding routine to send the number of fields first
- */
- /* and coded form*/
- nextp = (char *)g_in_enc((char *)&(tdp->field_count), hcon, outp);
-
- /*
- * Next, put in the one byte codes for each of the field types
- */
-
- for (i=0; i<tdp->field_count; i++) {
- *nextp++ = tdp->var[i].type & 0xff; /* put out the one byte */
- /* type codes */
- }
-
- /*
- * Finally, copy all the null terminated strings.
- */
- memcpy(nextp,((char *)(tdp))+gdb_descriptor_length(tdp->field_count),
- tdp->str_len); /* copy the string data all */
- /* at once */
- return nextp+tdp->str_len;
-}
-
- /*----------------------------------------------------------*/
- /*
- /* g_tpd_dec
- /*
- /* Decode a tuple_descriptor from external form. For
- /* safety in memory management, we always re-allocate the
- /* space for the tuple_descriptor. If the pointer passed
- /* to us is not null, then we assume that it points to a
- /* legal tuple descriptor, which we first free. Because
- /* data representation may change, we must re-do the
- /* create-tuple-descriptor, so it can determine the local
- /* machine dependent representation and alignment rules
- /* for the data.
- /*
- /*----------------------------------------------------------*/
-
-#define GDB_MAX_DECODED_FIELDS 100
-
-char *
-g_tpd_dec(outp, hcon, inp)
-char *inp; /* pointer to input data */
-HALF_CONNECTION hcon; /* connection descriptor */
-char *outp; /* place to put the output */
-{
- register TUPLE_DESCRIPTOR *tdp = (TUPLE_DESCRIPTOR *)outp;
- /* re-type as */
- /* tuple_descriptor */
- int field_count; /* number of fields in the */
- /* newly received descriptor*/
- register int i; /* a loop counter */
-
- register int tmp; /* working variable to hold */
- /* type while they're being */
- /* sign extended */
- char *nextt; /* next byte to scan for */
- /* a type code byte*/
- char *nextn; /* next byte to scan for */
- /* a string name */
- char *field_names[GDB_MAX_DECODED_FIELDS];
- /* put pointers to the */
- /* field names here */
- FIELD_TYPE field_types[GDB_MAX_DECODED_FIELDS];
- /* put the field types in */
- /* the array here*/
- /*
- * Use the integer coding routine to get the number of fields
- */
-
- nextt = (char *)g_in_dec((char *)&field_count, hcon, inp);
- if (field_count > GDB_MAX_DECODED_FIELDS)
- GDB_GIVEUP("g_tpd_dec: Trying to decode tuple descriptor with too many fields.\n")
-
-
- /*
- * For each field, pick up its type code, being sure to sign extend,
- * and a pointer to its string name.
- */
- nextn = nextt + field_count; /* there is one byte of */
- /* type info for each field, */
- /* after that comes the */
- /* first string. nextn */
- /* now points to the first */
- /* string */
- for (i=0; i<field_count; i++) {
- tmp = *nextt++; /* type code, may need */
- /* sign extension */
- if (tmp & 0x80)
- tmp |= ((~0) ^ 0xff); /* sign extend if needed */
- /* this is the most machine */
- /* independent sign extension */
- /* I could come up with. */
- /* Presumes char is one byte, */
- /* but makes no assumption */
- /* about sizeof(int) */
- field_types[i] = tmp;
- field_names[i] = nextn; /* pointer to name of the */
- /* field */
- nextn += strlen(nextn) +1; /* set up for possible name */
- /* to follow */
- }
-
- /*
- * In case there was already a tuple descriptor here, free it.
- */
-
- delete_tuple_descriptor(*tdp);
-
- /*
- * Create a new descriptor based on the information we have received.
- */
- *tdp = create_tuple_descriptor(field_count, field_names, field_types);
-
- return nextn;
-}
-
- /*----------------------------------------------------------*/
- /*
- /* g_tpd_form
- /*
- /* Format a tuple_descriptor on output logging file for
- /* debugging.
- /*
- /*----------------------------------------------------------*/
-
-int
-g_tpd_form(name, dp)
-char *name; /* tuple_descriptor name of the field */
-char *dp; /* pointer to the data */
-{
- register TUPLE_DESCRIPTOR tdp = *((TUPLE_DESCRIPTOR *)dp);
- /* re-type as */
- /* tuple_descriptor */
- register int i; /* loop variable through */
- /* field definitions */
-
-
- /*
- * Handle the special case where the descriptor is null
- */
- if (tdp == NULL) {
- fprintf(gdb_log, "TUPLE_DESCRIPTOR %s (loc=NULL)\n", name);
- return;
- }
-
- /*
- * Validate the descriptor
- */
- GDB_CHECK_TPD(tdp,"g_tpd_form: format tuple descriptor")
-
- /*
- * Descriptor is not null
- */
- fprintf(gdb_log, "TUPLE_DESCRIPTOR %s (loc=0x%x)\n", name, tdp);
-
- for (i=0; i<tdp->field_count; i++) {
- fprintf(gdb_log,"\tField Type Code = %3d %20s\tField Name=%s\n" ,
- tdp->var[i].type,
- STR_PROPERTY(tdp->var[i].type,NAME_PROPERTY),
- tdp->var[i].name);
- }
- fprintf(gdb_log,"\n");
-}
-
-
-/************************************************************************/
-/*
-/* TUPLE_T
-/*
-/* There is a distinction between the type tuple_t and the
-/* type tuple_data_t. Tuple_t is a complete self-contained
-/* tuple, with its descriptor. It actually refers to the
-/* tuple variable itself, which is a pointer. Tuple_data
-/* is only the data portion of the tuple, not the descriptor.
-/* It is used when the receiving tuple is already allocated,
-/* with a correct descriptor, for sending just the data.
-/*
-/* Note that some of the routines for tuple_t could have been
-/* implemented in terms of tuple_data_t routines. For the
-/* moment, they have not been, but that may later be changed.
-/* Doesn't seem to make much difference as long as they are
-/* short and simple, and this way does save a bit of overhead.
-/*
-/************************************************************************/
-
-#define TP_LEN (sizeof(TUPLE))
-#define TP_ALI TP_LEN
-#define TP_NULL g_tp_null
-#define TP_CDLEN g_tp_cdlen
-#define TP_ENC g_tp_enc
-#define TP_DEC g_tp_dec
-#define TP_FORM g_tp_form
-#define TP_NAME "TUPLE_T"
-
- /*----------------------------------------------------------*/
- /*
- /* g_tp_null
- /*
- /* Fill in a null value for a tuple.
- /*
- /*----------------------------------------------------------*/
-int
-g_tp_null(dp)
-char *dp; /* pointer to the data */
-{
- *((TUPLE *)dp) = NULL;
-}
-
- /*----------------------------------------------------------*/
- /*
- /* g_tp_cdlen
- /*
- /* Return coded length for a tuple. We have to send the
- /* descriptor along with the data itself. We do this
- /* with calls to the appropriate encodeing routines.
- /*
- /*----------------------------------------------------------*/
-
-int
-g_tp_cdlen(dp,hcon)
-char *dp; /* pointer to the data */
-HALF_CONNECTION hcon;
-{
- register TUPLE tup = *((TUPLE *)dp); /* deref as tuple */
- register int len; /* accumulated length */
- register int i; /* index to fields */
- TUPLE_DESCRIPTOR tpd; /* descriptor for this tuple */
-
- /*
- * Validate the tuple
- */
- if (tup == NULL)
- GDB_GIVEUP("g_tp_cdlen (coded length) was given a null tuple\nthis may be due to an attempt to transmit invalid data")
- GDB_CHECK_TUP(tup,"g_tp_cdlen: compute coded length of tuple")
-
- /*
- * First, get length of the descriptor when coded.
- */
-
- tpd = DESCRIPTOR_FROM_TUPLE(tup);
- len = g_tpd_cdlen((char *)&tpd,hcon);
-
- /*
- * Now, for each field, add in its coded length
- */
-
- for (i=0; i<tpd->field_count; i++) {
- len += (int)FCN_PROPERTY(FIELD_TYPE_IN_TUPLE(tpd,i),
- CODED_LENGTH_PROPERTY)
- (FIELD_FROM_TUPLE(tup, i),hcon);
- }
-
- return len;
-}
-
- /*----------------------------------------------------------*/
- /*
- /* g_tp_enc
- /*
- /* Encode a tuple for transmission
- /*
- /*----------------------------------------------------------*/
-
-char *
-g_tp_enc(dp, hcon, outp)
-char *dp; /* pointer to data */
-HALF_CONNECTION hcon; /* connection descriptor */
-char *outp; /* place to put the output */
-{
- register TUPLE tup = *((TUPLE *)dp); /* deref as tuple */
- register int i; /* index to fields */
- TUPLE_DESCRIPTOR tpd; /* descriptor for this tuple */
- char *op; /* next byte of output */
-
- /*
- * Validate the tuple
- */
- if (tup == NULL)
- GDB_GIVEUP("g_tp_enc (encode) was given a null tuple\nthis may be due to an attempt to transmit invalid data")
- GDB_CHECK_TUP(tup,"g_tp_enc: encode tuple")
-
- /*
- * First, get the tuple descriptor and encode it
- */
-
- tpd = DESCRIPTOR_FROM_TUPLE(tup);
- op = (char *)g_tpd_enc((char *)&tpd, hcon, outp);
-
- /*
- * Now, for each field, code it
- */
-
- for (i=0; i<tpd->field_count; i++) {
- op = (char *)FCN_PROPERTY(FIELD_TYPE_IN_TUPLE(tpd,i),
- ENCODE_PROPERTY)
- (FIELD_FROM_TUPLE(tup, i),hcon, op);
- }
-
- return op;
-}
-
- /*----------------------------------------------------------*/
- /*
- /* g_tp_dec
- /*
- /* Decode a tuple from external form. For safety
- /* in memory management, we always re-allocate the
- /* space for the tuple, so the lengths come out right.
- /* This may have nasty side effects if the intention
- /* was to leave 'gas' at the end of the tuple, but
- /* we want to accurately copy the data. Note that
- /* tuple_free is robust against null pointers.
- /*
- /*----------------------------------------------------------*/
-
-char *
-g_tp_dec(outp, hcon, inp)
-char *inp; /* pointer to input data */
-HALF_CONNECTION hcon; /* connection descriptor */
-char *outp; /* place to put the output */
-{
- register TUPLE tup; /* the new tuple */
- register int i; /* index to fields */
- TUPLE_DESCRIPTOR tpd; /* descriptor for this tuple */
- char *ip; /* next byte of input */
-
- /*
- * First, get the tuple descriptor and decode it
- */
-
- tpd = NULL; /* so decode will know */
- /* there's no existing one */
- /* to free */
- ip = (char *)g_tpd_dec((char *)&tpd, hcon, inp);
-
- /*
- * Now make an empty tuple based on the descriptor
- */
-
- tup = create_tuple(tpd);
-
- /*
- * The tuple descriptor has a reference count of 2 here, one
- * from the tpd_dec routine, and one from the create_tuple.
- * Since we don't expect to explicitly undo the two separately,
- * we decrement the count here.
- */
-
- UNREFERENCE_TUPLE_DESCRIPTOR(tpd); /* decr. the reference count */
-
- /*
- * Now, for each field, decode it.
- */
-
- for (i=0; i<tpd->field_count; i++) {
- ip = (char *)FCN_PROPERTY(FIELD_TYPE_IN_TUPLE(tpd,i),
- DECODE_PROPERTY)
- (FIELD_FROM_TUPLE(tup, i),hcon, ip);
- }
-
- *((TUPLE *)outp) = tup; /* put the new tuple */
- /* pointer where the */
- /* caller wants it */
- return ip;
-}
-
- /*----------------------------------------------------------*/
- /*
- /* g_tp_form
- /*
- /* Format a tuple on output logging file for
- /* debugging.
- /*
- /*----------------------------------------------------------*/
-
-int
-g_tp_form(name, dp)
-char *name; /* tuple name of the field */
-char *dp; /* pointer to the data */
-{
- register TUPLE tup = *((TUPLE *)dp); /* deref as tuple */
- register int i; /* index to fields */
- TUPLE_DESCRIPTOR tpd; /* descriptor for this tuple */
-
-
- /*
- * Handle special case where tuple is null
- */
-
- if (tup==NULL) {
- fprintf(gdb_log,"\nTUPLE Name=%s is NULL\n---------------------------\n",name);
- return;
- }
-
- GDB_CHECK_TUP(tup,"g_tp_form: format tuple")
- /*
- * Get the descriptor--for now, we won't print it
- */
- tpd = DESCRIPTOR_FROM_TUPLE(tup);
-
- /*
- * Print a header
- */
-
- fprintf(gdb_log,"\nTUPLE at address: 0x%x Name=%s\n---------------------------\n",tup,name);
-
- /*
- * Now, for each field, print it
- */
-
- for (i=0; i<tpd->field_count; i++) {
- FCN_PROPERTY(FIELD_TYPE_IN_TUPLE(tpd,i),
- FORMAT_PROPERTY)
- (tpd->var[i].name,FIELD_FROM_TUPLE(tup, i));
- }
-
- fprintf(gdb_log,"END_OF_TUPLE\n");
-}
-
-
-/************************************************************************/
-/*
-/* TUPLE_DATA_T
-/*
-/* The distinction between tuple_data_t and tuple_t is a
-/* subtle one. Tuple_t is used when a single tuple is to
-/* be decoded, outside of any larger context. It (re)allocates
-/* memory for both the tuple itself and its descriptor.
-/*
-/* Tuple_data is used in the case where the tuple and its
-/* descriptor are already allocated, but only the data is
-/* to be received. This is useful in cases like receiving an
-/* entire relation, in which the descriptor is common to
-/* all the tuples, and should not be resent or reallocated
-/* with each one. Receive relation can send the tuple descriptor
-/* once, then do a create_tuple followed by a decode tuple_data
-/* to receive the tuple field data into the existing tuple.
-/*
-/* Note that the definition of null is different in the two cases.
-/* The null value for a tuple is just a null pointer. The null
-/* for tuple data is to null each of the fields in the tuple
-/* recursively. The routines in this section may dereference
-/* null pointers if the tuples they are passed are null. Note
-/* also that there is one less level of indirection in passing
-/* data to these routines than to those of tuple_t.
-/*
-/* Note also that the null and decode routines supplied here
-/* presume that any fields with dependent memory (e.g. string_t
-/* fields have already been cleaned up.)
-/*
-/* Note that this is not quite a kosher type, in the sense that
-/* it's length is not fixed. The entry for length below
-/* is meaningless, because the real length is computed from the
-/* desc. Among other things, this means that TUPLEs cannot
-/* contain fields of this type.
-/*
-/************************************************************************/
-
-#define TDT_LEN (sizeof(TUPLE))
-#define TDT_ALI TDT_LEN
-#define TDT_NULL g_tdt_null
-#define TDT_CDLEN g_tdt_cdlen
-#define TDT_ENC g_tdt_enc
-#define TDT_DEC g_tdt_dec
-#define TDT_FORM g_tdt_form
-#define TDT_NAME "TUPLE_DATA_T"
-
- /*----------------------------------------------------------*/
- /*
- /* g_tdt_null
- /*
- /* Fill in a null value for a tuple.
- /*
- /*----------------------------------------------------------*/
-int
-g_tdt_null(dp)
-char *dp; /* pointer to the data */
-{
- TUPLE tup = (TUPLE)dp; /* dp is of type TUPLE, */
- /* which is actually */
- /* a pointer to the */
- /* tuple data */
- TUPLE_DESCRIPTOR tpd; /* the descriptor for this */
- /* tuple*/
- register int i; /* a loop counter */
-
- /*
- * For each field in the tuple, call its null routine
- */
- tup->id = GDB_TUP_ID;
-
- tpd = DESCRIPTOR_FROM_TUPLE(tup);
-
- for (i=0; i<tpd->field_count; i++) {
- FCN_PROPERTY(FIELD_TYPE_IN_TUPLE(tpd,i),NULL_PROPERTY)
- (FIELD_FROM_TUPLE(tup,i));
- }
-}
-
- /*----------------------------------------------------------*/
- /*
- /* g_tdt_cdlen
- /*
- /* Return coded length for tuple data. Since the descriptor
- /* for the tuple is known at both sides, we send only
- /* the coded fields, not even the field counts.
- /*
- /*----------------------------------------------------------*/
-
-int
-g_tdt_cdlen(dp,hcon)
-char *dp; /* pointer to the data */
-HALF_CONNECTION hcon;
-{
- register TUPLE tup = (TUPLE)dp; /* arg typed as tuple */
- register int len; /* accumulated length */
- register int i; /* index to fields */
- TUPLE_DESCRIPTOR tpd; /* descriptor for this tuple */
-
- /*
- * Validate the tuple data
- */
- if (tup == NULL)
- GDB_GIVEUP("g_tdt_cdlen (coded length) was given null tuple data\nthis may be due to an attempt to transmit invalid data")
- GDB_CHECK_TUP(tup,"g_tdt_cdlen: compute coded length of tuple data")
- /*
- * First, find the tuple descriptor and set initial coded len to 0
- */
-
- tpd = DESCRIPTOR_FROM_TUPLE(tup);
- len = 0;
-
- /*
- * Now, for each field, add in its coded length
- */
-
- for (i=0; i<tpd->field_count; i++) {
- len += (int)FCN_PROPERTY(FIELD_TYPE_IN_TUPLE(tpd,i),
- CODED_LENGTH_PROPERTY)
- (FIELD_FROM_TUPLE(tup, i),hcon);
- }
-
- return len;
-}
-
- /*----------------------------------------------------------*/
- /*
- /* g_tdt_enc
- /*
- /* Encode tuple data for transmission.
- /*
- /*----------------------------------------------------------*/
-
-char *
-g_tdt_enc(dp, hcon, outp)
-char *dp; /* pointer to data */
-HALF_CONNECTION hcon; /* connection descriptor */
-char *outp; /* place to put the output */
-{
- register TUPLE tup = (TUPLE)dp; /* type as tuple */
- register int i; /* index to fields */
- TUPLE_DESCRIPTOR tpd; /* descriptor for this tuple */
- char *op = outp; /* next byte of output */
-
- /*
- * Validate the tuple data
- */
- if (tup == NULL)
- GDB_GIVEUP("g_tdt_enc (encode) was given null tuple data\nthis may be due to an attempt to transmit invalid data")
- GDB_CHECK_TUP(tup,"g_tdt_enc: encode of tuple data")
- /*
- * First, get the tuple descriptor
- */
-
- tpd = DESCRIPTOR_FROM_TUPLE(tup);
-
- /*
- * Now, for each field, code it
- */
-
- for (i=0; i<tpd->field_count; i++) {
- op = (char *)FCN_PROPERTY(FIELD_TYPE_IN_TUPLE(tpd,i),
- ENCODE_PROPERTY)
- (FIELD_FROM_TUPLE(tup, i),hcon, op);
- }
-
- return op;
-}
-
- /*----------------------------------------------------------*/
- /*
- /* g_tdt_dec
- /*
- /* Decode tuple data from external form. We presume
- /* that the tuple itself is allocated, and the descriptor
- /* properly set up for the local machine representation.
- /* Here we just decode the fields.
- /*
- /*----------------------------------------------------------*/
-
-char *
-g_tdt_dec(outp, hcon, inp)
-char *inp; /* pointer to input data */
-HALF_CONNECTION hcon; /* connection descriptor */
-char *outp; /* place to put the output */
-{
- register TUPLE tup = (TUPLE)outp; /* the filled in tuple */
- register int i; /* index to fields */
- TUPLE_DESCRIPTOR tpd; /* descriptor for this tuple */
- char *ip = inp; /* next byte of input */
-
- /*
- * Validate the tuple data
- */
- if (tup == NULL)
- GDB_GIVEUP("g_tdt_dec (decode) was given null tuple data\nthis may be due to an attempt to transmit invalid data")
- GDB_CHECK_TUP(tup,"g_tdt_dec: decode of tuple data")
- /*
- * First, get the tuple descriptor
- */
-
- tpd = DESCRIPTOR_FROM_TUPLE(tup);
-
- /*
- * Now, for each field, decode it.
- */
-
- for (i=0; i<tpd->field_count; i++) {
- ip = (char *)FCN_PROPERTY(FIELD_TYPE_IN_TUPLE(tpd,i),
- DECODE_PROPERTY)
- (FIELD_FROM_TUPLE(tup, i),hcon, ip);
- }
-
- return ip;
-}
-
- /*----------------------------------------------------------*/
- /*
- /* g_tdt_form
- /*
- /* Format tuple data on output logging file for
- /* debugging.
- /*
- /*----------------------------------------------------------*/
-
-int
-g_tdt_form(name, dp)
-char *name; /* tuple name of the field */
-char *dp; /* pointer to the data */
-{
- register TUPLE tup = (TUPLE)dp; /* as tuple */
- register int i; /* index to fields */
- TUPLE_DESCRIPTOR tpd; /* descriptor for this tuple */
-
-
- /*
- * Handle special case where we're given a null address for the
- * tuple
- */
- if (tup==NULL) {
- fprintf(gdb_log,"\nTUPLE Name=%s is NULL\n---------------------------\n",name);
- return;
- }
-
-
- /*
- * Validate the tuple data
- */
- GDB_CHECK_TUP(tup,"g_tdt_form: format tuple data")
- /*
- * Get the descriptor--for now, we won't print it
- */
- tpd = DESCRIPTOR_FROM_TUPLE(tup);
-
- /*
- * Print a header
- */
-
- fprintf(gdb_log,"\nTUPLE at address: 0x%x Name=%s\n---------------------------\n",tup,name);
-
- /*
- * Now, for each field, print it
- */
-
- for (i=0; i<tpd->field_count; i++) {
- FCN_PROPERTY(FIELD_TYPE_IN_TUPLE(tpd,i),
- FORMAT_PROPERTY)
- (tpd->var[i].name,FIELD_FROM_TUPLE(tup, i));
- }
-
- fprintf(gdb_log,"END_OF_TUPLE\n");
-}
-
-
-/************************************************************************/
-/*
-/* RELATION_T
-/*
-/* Relations consist of link lists of tuples, all of which are
-/* presumed to share a tuple descriptor. For transmission,
-/* these are encoded as follows:
-/*
-/* 1) A count of the number of tuples, sent as a properly coded
-/* integer.
-/*
-/* 2) The tuple descriptor itself, encoded by its encoding routine.
-/*
-/* 3) For each tuple, its tuple data, encoded using the routines
-/* of the tuple_data_t type.
-/*
-/************************************************************************/
-
-#define REL_LEN (sizeof(RELATION))
-#define REL_ALI REL_LEN
-#define REL_NULL g_rel_null
-#define REL_CDLEN g_rel_cdlen
-#define REL_ENC g_rel_enc
-#define REL_DEC g_rel_dec
-#define REL_FORM g_rel_form
-#define REL_NAME "RELATION_T"
-
-
- /*----------------------------------------------------------*/
- /*
- /* g_rel_null
- /*
- /* Fill in a null value for a relation. Maybe we should
- /* check for an existing relation and properly free it,
- /* but for now, we don't.
- /*
- /*----------------------------------------------------------*/
-int
-g_rel_null(dp)
-char *dp; /* pointer to the data */
-{
- *((RELATION *)dp) = NULL;
-}
-
- /*----------------------------------------------------------*/
- /*
- /* g_rel_cdlen
- /*
- /* Return coded length for a relation.
- /*
- /*----------------------------------------------------------*/
-
-int
-g_rel_cdlen(dp,hcon)
-char *dp; /* pointer to the data */
-HALF_CONNECTION hcon;
-{
- register RELATION rel = *((RELATION *)dp); /* deref as relation */
- int len; /* accumulated length */
- register TUPLE t; /* index to a tuple */
- int tuple_count = 0; /* number of tuples in this */
- /* relation*/
- TUPLE_DESCRIPTOR tpd; /* descriptor for this */
- /* relation */
-
- /*
- * Validate the relation
- */
- if (rel == NULL)
- GDB_GIVEUP("g_rel_cdlen (coded length) was given null relation\nthis may be due to an attempt to transmit invalid data")
- GDB_CHECK_REL(rel,"g_rel_cdlen: compute coded length of relation")
- /*
- * First, get the tuple descriptor for this relation
- */
-
- tpd = DESCRIPTOR_FROM_RELATION(rel);
-
- /*
- * Count the number of tuples in the relation
- */
- for (t=FIRST_TUPLE_IN_RELATION(rel); t != NULL;
- t = NEXT_TUPLE_IN_RELATION(rel,t))
- tuple_count++;
- /*
- * Start with the coded length for the tuple count and the
- * descriptor, which are sent first.
- */
-
- len = g_in_cdlen((char *)&tuple_count, hcon); /* length of tuple_count */
- /* in coded form */
- len += g_tpd_cdlen((char *)&tpd, hcon);
-
- /*
- * Now, for each tuple, add in its coded length
- */
-
- for (t=FIRST_TUPLE_IN_RELATION(rel); t != NULL;
- t = NEXT_TUPLE_IN_RELATION(rel,t))
- len += g_tdt_cdlen((char *)t, hcon);
-
- return len;
-}
-
- /*----------------------------------------------------------*/
- /*
- /* g_rel_enc
- /*
- /* Encode a relation for transmission
- /*
- /*----------------------------------------------------------*/
-
-char *
-g_rel_enc(dp, hcon, outp)
-char *dp; /* pointer to data */
-HALF_CONNECTION hcon; /* connection descriptor */
-char *outp; /* place to put the output */
-{
- register RELATION rel = *((RELATION *)dp); /* deref as relation */
- char *op; /* pointer to next unused */
- /* output byte*/
- register TUPLE t; /* index to a tuple */
- int tuple_count = 0; /* number of tuples in this */
- /* relation*/
- TUPLE_DESCRIPTOR tpd; /* descriptor for this */
- /* relation */
-
- /*
- * Validate the relation
- */
- if (rel == NULL)
- GDB_GIVEUP("g_rel_enc (encode) was given null relation\nthis may be due to an attempt to transmit invalid data")
- GDB_CHECK_REL(rel,"g_rel_enc: encode relation")
-
- /*
- * First, get the tuple descriptor for this relation
- */
-
- tpd = DESCRIPTOR_FROM_RELATION(rel);
-
- /*
- * Count the number of tuples in the relation
- */
- for (t=FIRST_TUPLE_IN_RELATION(rel); t != NULL;
- t = NEXT_TUPLE_IN_RELATION(rel,t))
- tuple_count++;
- /*
- * Encode the count and the tuple descriptor for this relation
- */
-
- op = (char *)g_in_enc((char *)&tuple_count, hcon,outp);
- /* length of tuple_count */
- /* in coded form */
- op = (char *)g_tpd_enc((char *)&tpd, hcon,op);
-
- /*
- * Now, encode each tuple
- */
-
- for (t=FIRST_TUPLE_IN_RELATION(rel); t != NULL;
- t = NEXT_TUPLE_IN_RELATION(rel,t))
- op = (char *)g_tdt_enc((char *)t, hcon, op);
-
- return op;
-}
-
- /*----------------------------------------------------------*/
- /*
- /* g_rel_dec
- /*
- /* Decode a relation from external form. We should
- /* really check to make sure the relation we are given
- /* is null, and if not, call delete_relation on it
- /* first. For the moment, we just presume it's null.
- /*
- /* We proceed by decoding the integer count and the
- /* tuple descriptor, from which we create the null
- /* relation. We then loop for each tuple, doing a
- /* create, a decode, and an add to relation.
- /*
- /*----------------------------------------------------------*/
-
-char *
-g_rel_dec(outp, hcon, inp)
-char *inp; /* pointer to input data */
-HALF_CONNECTION hcon; /* connection descriptor */
-char *outp; /* place to put the output */
-{
- register RELATION rel; /* build the relation here */
- char *ip; /* pointer to next unused */
- /* input byte*/
- register TUPLE t; /* index to a tuple */
- register int i; /* loop counter on tuples */
- int tuple_count = 0; /* number of tuples in this */
- /* relation*/
- TUPLE_DESCRIPTOR tpd; /* descriptor for this */
- /* relation */
-
- /*
- * First, get the field count and tuple descriptor for this relation
- */
-
- ip = (char *)g_in_dec((char *)&tuple_count, hcon, inp);
-
- tpd = NULL; /* so decode will know */
- /* there's no existing one */
- /* to free */
- ip = (char *)g_tpd_dec((char *)&tpd, hcon, ip);
-
- /*
- * Now, create a null relation using the descriptor
- */
-
- rel = create_relation(tpd);
-
- /*
- * The reference count for the tuple descriptor is currently 2,
- * one from the tpd_dec and one from the create relation. Since
- * these will not be undone separately, we decrement the reference
- * count to 1
- */
-
- UNREFERENCE_TUPLE_DESCRIPTOR(tpd);
-
- /*
- * For each tuple, create it, receive it, add it to the relation
- */
-
- for (i=0; i<tuple_count; i++) {
- t = create_tuple(tpd);
- ip = (char *)g_tdt_dec((char *)t, hcon, ip);
- ADD_TUPLE_TO_RELATION(rel, t);
- }
-
- /*
- * Now store the address of the created relation where requested
- * and return pointer to next available input byte.
- */
-
- *((RELATION *)outp) = rel;
-
- return ip;
-}
-
- /*----------------------------------------------------------*/
- /*
- /* g_rel_form
- /*
- /* Format a relation on output logging file for
- /* debugging.
- /*
- /*----------------------------------------------------------*/
-
-int
-g_rel_form(name, dp)
-char *name; /* relation name of the field */
-char *dp; /* pointer to the data */
-{
- register RELATION rel = *((RELATION *)dp); /* deref as relation */
- register TUPLE t;
- int count =0;
- char buffer[50];
-
- /*
- * Handle special case where relation is null
- */
-
- if (rel == NULL) {
- fprintf(gdb_log,"\nRELATION Name=%s is NULL\n===========================\n",name);
- return;
- }
-
- GDB_CHECK_REL(rel,"g_rel_form: format relation")
-
- /*
- * Print a header
- */
-
- fprintf(gdb_log,"\nRELATION at address: 0x%x Name=%s\n===========================\n",rel,name);
-
- /*
- * Now, for each field, print it
- */
-
- for (t=FIRST_TUPLE_IN_RELATION(rel); t != NULL;
- t = NEXT_TUPLE_IN_RELATION(rel,t)){
- (void) sprintf(buffer,"Number %d",++count);
- g_tdt_form(buffer,(char *)t);
- }
-
- fprintf(gdb_log,"END_OF_RELATION\n");
-}
-
-
-/************************************************************************/
-/*
-/* DECLARE AND INITIALIZE THE SYSTEM TYPE DEFINITION
-/* TABLES
-/*
-/* This representation is clearly a real pain to keep up to date
-/* properly, mostly because C has such a lousy pre-processor.
-/* Probably this should be re-arranged so an initialization routine
-/* is called to set up the tables, but even that might be a nuissance.
-/*
-/************************************************************************/
-
- /*----------------------------------------------------------*/
- /*
- /* gdb_i_stype
- /*
- /* Called at startup to initialize the type table with
- /* the entries for the system types.
- /*
- /*----------------------------------------------------------*/
-
-#define ITYPE(inx,lp,ap,np,clp,ep,dp,fp,name) {\
- g_type_table[inx][LENGTH_PROPERTY].i = lp; \
- g_type_table[inx][ALIGNMENT_PROPERTY].i = ap; \
- g_type_table[inx][NULL_PROPERTY].f = np; \
- g_type_table[inx][CODED_LENGTH_PROPERTY].f = clp; \
- g_type_table[inx][ENCODE_PROPERTY].cpf = ep; \
- g_type_table[inx][DECODE_PROPERTY].cpf = dp; \
- g_type_table[inx][FORMAT_PROPERTY].f = fp; \
- g_type_table[inx][NAME_PROPERTY].cp = name; \
-}
-
-int
-gdb_i_stype()
-{
- gdb_n_types = SYSTEM_TYPE_COUNT;
-
- ITYPE(INTEGER_T,IN_LEN,IN_ALI,IN_NULL,IN_CDLEN,IN_ENC,IN_DEC,IN_FORM,
- IN_NAME)
- ITYPE(STRING_T,ST_LEN,ST_ALI,ST_NULL,ST_CDLEN,ST_ENC,ST_DEC,ST_FORM,
- ST_NAME)
- ITYPE(REAL_T,RL_LEN,RL_ALI,RL_NULL,RL_CDLEN,RL_ENC,RL_DEC,RL_FORM,
- RL_NAME)
- ITYPE(DATE_T,DT_LEN,DT_ALI,DT_NULL,DT_CDLEN,DT_ENC,DT_DEC,DT_FORM,
- DT_NAME)
- ITYPE(TUPLE_DESCRIPTOR_T,TPD_LEN,TPD_ALI,TPD_NULL,TPD_CDLEN,TPD_ENC,
- TPD_DEC,TPD_FORM,TPD_NAME)
- ITYPE(TUPLE_T,TP_LEN,TP_ALI,TP_NULL,TP_CDLEN,TP_ENC,TP_DEC,TP_FORM,
- TP_NAME)
- ITYPE(TUPLE_DATA_T,TDT_LEN,TDT_ALI,TDT_NULL,TDT_CDLEN,TDT_ENC,TDT_DEC,
- TDT_FORM,TDT_NAME)
- ITYPE(RELATION_T,REL_LEN,REL_ALI,REL_NULL,REL_CDLEN,REL_ENC,REL_DEC,
- REL_FORM,REL_NAME)
-}
+++ /dev/null
-/*
- * $Source$
- * $Header$
- */
-
-#ifndef lint
-static char *rcsid_gdb_trans_c = "$Header$";
-#endif
-
-/************************************************************************/
-/*
-/* gdb_trans.c
-/*
-/* GDB - Data Transport Services Routines (Part 1)
-/*
-/* Author: Noah Mendelsohn
-/* Copyright: 1986 MIT Project Athena
-/* For copying and distribution information, please see
-/* the file <mit-copyright.h>.
-/*
-/* These routines implement layer 6 of the Client Library
-/* Specification of the GDB system, as well as the facilities
-/* outlined in the GDB Protocol Specification. (Part 1 of 2)
-/*
-/* Some of the routines specified are actually implemented as
-/* macros defined in gdb.h.
-/*
-/************************************************************************/
-
-#include <mit-copyright.h>
-#include <sys/types.h>
-#include <string.h>
-#include <stdio.h>
-#include <varargs.h>
-#include <errno.h>
-extern int errno;
-#include "gdb.h"
-
-\f
-/************************************************************************/
-/*
-/* OPERATION Manipulation
-/*
-/* The routines in this section provide services for creating
-/* and manipulating GDB queueable operations.
-/*
-/************************************************************************/
-
- /*----------------------------------------------------------*/
- /*
- /* create_operation (create_operation)
- /*
- /* Allocates space for an operation descriptor and
- /* returns a pointer to it. Initialize the eyecatcher
- /* so that in the future we can prove by inspection that
- /* this really is an operation descriptor.
- /*
- /*----------------------------------------------------------*/
-
-OPERATION
-create_operation()
-{
- register OPERATION op;
- GDB_INIT_CHECK
- op = (OPERATION)db_alloc(sizeof(OPERATION_DATA));
- op->status = OP_NOT_STARTED; /* things like */
- /* reset_operation */
- /* want valid status */
- /* at all times*/
- op->id = GDB_OP_ID;
- return op;
-}
-
- /*----------------------------------------------------------*/
- /*
- /* reset_operation (reset_operation)
- /*
- /* Sets the status of an operation to OP_NOT_STARTED
- /*
- /*----------------------------------------------------------*/
-
-int
-reset_operation(op)
-OPERATION op;
-{
- register int status;
-
- GDB_CHECK_OP(op, "reset_operation")
-
- status = OP_STATUS(op);
- if (status == OP_QUEUED ||
- status == OP_RUNNING)
- GDB_GIVEUP("gdb:reset_operation invalid on running operation")
- op->status = OP_NOT_STARTED;
-}
-
- /*----------------------------------------------------------*/
- /*
- /* delete_operation(delete_operation)
- /*
- /* Frees an operation descriptor.
- /*
- /*----------------------------------------------------------*/
-
-int
-delete_operation(op)
-OPERATION op;
-{
- GDB_CHECK_OP(op, "delete_operation");
- op->id = -1;
- db_free((char *)op, sizeof(OPERATION_DATA));
-}
-
- /*----------------------------------------------------------*/
- /*
- /* initialize_operation(initialize_operation)
- /*
- /* Initialize the data in an operation descriptor
- /* based on the supplied parameters.
- /*
- /*----------------------------------------------------------*/
-
-int
-initialize_operation(operation, init_function, arg, cancel_function)
-OPERATION operation; /* initialize this */
-int (*init_function)(); /* function to call when */
- /* starting operation */
-char *arg; /* arg to pass to init and */
- /* continue routines */
-int (*cancel_function)(); /* call this function */
- /* when cancelling the op. */
- /* may be NULL, may not */
- /* be elided */
-{
- register OPERATION op = operation; /* just for speed */
-
- GDB_CHECK_OP(op, "initialize_operation")
-
-
- /*
- * Fill in boilerplate - same for all newly initialized ops.
- */
- op->next = op->prev = NULL; /* it's not in a queue yet */
- op->tag = (-1); /* not tagged until queued */
- op->status = OP_NOT_STARTED;
- op->flags = 0;
- op->result = (-1); /* result is not valid until */
- /* status is OP_COMPLETE. */
- /* All users of result field */
- /* are expected to treat -1 */
- /* invalid */
- op->halfcon = NULL; /* not queued yet */
-
- /*
- * Fill in supplied parameters
- */
- op->arg = arg;
- op->fcn.init = init_function;
- op->cancel = cancel_function;
-
- return;
-}
-
- /*----------------------------------------------------------*/
- /*
- /* create_list_of_operations (create_list_of_operations)
- /*
- /* Allocates and fills in a data structure which
- /* contains a list of operations. This list is
- /* suitable for passing to op_select.
- /*
- /* The facilities of <varargs.h> are used to parse
- /* the variable length parameter list to this function.
- /* See "man 3 varargs" for details.
- /*
- /*----------------------------------------------------------*/
-
-/*VARARGS1*/
-
-LIST_OF_OPERATIONS
-create_list_of_operations(count, va_alist)
-int count;
-va_dcl
-{
- va_list ap; /* the control structure */
- /* for varargs parsing */
-
- register int i;
- register LIST_OF_OPERATIONS newlist; /* newly allocated list */
-
- /*
- * Allocate the new list of operations, initialize its count.
- */
- newlist = (LIST_OF_OPERATIONS)
- db_alloc(size_of_list_of_operations(count));
- newlist->count = count;
-
- /*
- * Using the facilities of varargs.h, pick up the operations
- * and put them in the list.
- */
- va_start(ap);
- for (i=0; i<count; i++) {
- newlist->op[i] = va_arg(ap, OPERATION);
- GDB_CHECK_OP((newlist->op[i]), "create_list_of_operations")
- }
- va_end(ap);
-
- return newlist;
-}
-
- /*----------------------------------------------------------*/
- /*
- /* delete_list_of_operations(delete_list_of_operations)
- /*
- /* Deallocate the space for a list of operations.
- /*
- /*----------------------------------------------------------*/
-
-int
-delete_list_of_operations(op_list)
-LIST_OF_OPERATIONS op_list;
-{
- db_free((char *)op_list, size_of_list_of_operations(op_list->count));
-}
-\f
-/************************************************************************/
-/*
-/* OPERATION QUEUE MANIPULATION
-/*
-/* Routines to queue (initiate) and track completion of GDB
-/* operations.
-/*
-/************************************************************************/
-
- /*----------------------------------------------------------*/
- /*
- /* op_select(op_select)
- /*
- /* This function is an analog of the standard Berkeley
- /* select system call. It provides all the functions
- /* of select, but in addition, it awaits completion
- /* of a specified list of queued gdb operations.
- /*
- /* This function returns when any combination of the
- /* following are found to be true:
- /*
- /* 1) Any of the designated operations has completed
- /* or terminated prematurely.
- /*
- /* 2) Activity is detected on any of the explictly supplied
- /* file descriptors or the supplied timer has expired.
- /*
- /* The count returned is only for file descriptors
- /* explicitly listed. Completed operatons may be detected
- /* by checking OP_STATUS for each of the operations in
- /* the list. File descriptors controlled by CONNECTIONS
- /* should never be passed explictly in the bit maps to
- /* this routine. Only user controlled file descriptors
- /* may be explictly selected.
- /*
- /* Return code summary:
- /*
- /* -2 One or more listed operations completed.
- /* Timer did not ring (as far as we could
- /* tell), other fd's did not complete, or
- /* we didn't get far enough to bother
- /* looking. (see fairness below.)
- /*
- /* -1 An error was returned on select of a
- /* user supplied socket. errno has the
- /* error code.
- /*
- /* 0 The timer rang. Some operations may
- /* be complete, but it's unlikely.
- /*
- /* >0 This many user supplied fd's were
- /* satisfied--same as for select.
- /* Operations in the list may also have
- /* completed.
- /*
- /* Fairness is not guaranteed. This routine tends to
- /* favor activity on CONNECTIONs. In particular, if
- /* some operation(s) in the list can be completed
- /* synchronously, a real select is never done to
- /* check on the file descriptors.
- /*
- /*----------------------------------------------------------*/
-
-int
-op_select(op_list, nfds, readfds, writefds, exceptfds, timeout)
-LIST_OF_OPERATIONS op_list;
-int nfds;
-fd_set *readfds, *writefds, *exceptfds;
-struct timeval *timeout;
-{
- int rc; /* return code holder */
- fd_set rfds, wfds, efds; /* local copys of read */
- /* write, and exception */
- /* fd's */
-
- /*
- * Make sure that any activity which can be done immediately is
- * indeed done now--we may not have to wait at all.
- */
- (void) gdb_fastprogress(); /*<==FIX (check to make sure this is OK)*/
-
- /*
- * Loop checking for termination conditions, and if none arise,
- * use con_select to make wait for one or more of the
- * connections to wake up and do the appropriate processing.
- */
- while (TRUE) {
- /*
- * If any of the queue operations have completed,
- * then just return now.
- */
- if (gdb_list_complete(op_list))
- return (-2); /* no fd's satisfied here */
-
- /*
- * Use con_select to await all the appropriate events
- */
- g_fd_copy(nfds, readfds, &rfds);
- g_fd_copy(nfds, writefds, &wfds);
- g_fd_copy(nfds, exceptfds, &efds);
-
- rc = con_select(nfds, &rfds, &wfds, &efds, timeout);
-
- /*
- * At this point, either some of the supplied fd's have
- * been satisfied(rc>0), or the timer has rung (rc=0),
- * an error was returned from select on a user specified socket
- * (-1) or none of these (-2). In any case, there may
- * have been progress on one of the connections, and
- * con_select will already have progressed as far as
- * possible before returning. Now, decide what to
- * do, given the return code.
- */
- if (rc>= (-1)) {
- /*
- * Return updated bit-maps to caller.
- */
- g_fd_copy(nfds, &rfds, readfds);
- g_fd_copy(nfds, &wfds, writefds);
- g_fd_copy(nfds, &efds, exceptfds);
- return rc;
- }
- }
-}
-\f
- /*----------------------------------------------------------*/
- /*
- /* op_select_all(op_select_all)
- /*
- /* Similar to op_select_any, but returns (-1) only
- /* in the case that all of the designated descriptors
- /* are OP_COMPLETE or OP_CANCELLED.
- /*
- /*----------------------------------------------------------*/
-
-int
-op_select_all(op_list, nfds, readfds, writefds, exceptfds, timeout)
-LIST_OF_OPERATIONS op_list;
-int nfds;
-fd_set *readfds, *writefds, *exceptfds;
-struct timeval *timeout;
-{
- register int i;
- int rc = -1;
- register int left = op_list->count;
-
- /*
- * take care of those which are already complete by decrementing
- * and reseting them
- */
-
- for (i=0; i<op_list->count; i++) {
- if (op_list->op[i]->status==OP_COMPLETE) {
- op_list->op[i]->flags |= OPF_MARKED_COMPLETE;
- op_list->op[i]->status = OP_MARKED;
- /* so op_select_any won't */
- /* fall right through*/
-
- left--;
- continue;
- }
- if (op_list->op[i]->status==OP_CANCELLED) {
- op_list->op[i]->flags |= OPF_MARKED_CANCELLED;
- op_list->op[i]->status = OP_MARKED;
- /* so op_select_any won't */
- /* fall right through*/
-
- left--;
- }
- }
-
-
- /*
- * As long as there are incomplete operations left in the list,
- * keep calling op_select_any
- */
- while (left) {
- rc = op_select(op_list, nfds, readfds, writefds, exceptfds,
- timeout);
- if (rc>=0)
- break;
- for (i=0; i<op_list->count; i++) {
- if (op_list->op[i]->status==OP_COMPLETE) {
- op_list->op[i]->flags |= OPF_MARKED_COMPLETE;
- op_list->op[i]->status = OP_MARKED;
- /* so op_select_any won't */
- /* fall right through*/
-
- left--;
- continue;
- }
- if (op_list->op[i]->status==OP_CANCELLED) {
- op_list->op[i]->flags |= OPF_MARKED_CANCELLED;
- op_list->op[i]->status = OP_MARKED;
- /* so op_select_any won't */
- /* fall right through*/
-
- left--;
- }
- }
- }
-
- /*
- * Clean up the marked operations and return
- */
- for (i=0; i<op_list->count; i++) {
- if (op_list->op[i]->status==OP_MARKED) {
- op_list->op[i]->status = (op_list->op[i]->flags &
- OPF_MARKED_COMPLETE) ?
- OP_COMPLETE :
- OP_CANCELLED;
- op_list->op[i]->flags &= ~(OPF_MARKED_COMPLETE |
- OPF_MARKED_CANCELLED);
- }
- }
-
- return rc;
-
-}
-\f
-/************************************************************************/
-/*
-/* Internal transport layer routines, not called directly
-/* from client library.
-/*
-/************************************************************************/
-
- /*----------------------------------------------------------*/
- /*
- /* gdb_list_complete
- /*
- /* Given a list of gdb operations, return TRUE if any
- /* of them are complete, otherwise return FALSE.
- /*
- /*----------------------------------------------------------*/
-
-int
-gdb_list_complete(op_list)
-LIST_OF_OPERATIONS op_list;
-{
- register int i;
- register int status;
- register LIST_OF_OPERATIONS oplist = op_list; /* for speed */
- int count = oplist -> count;
-
- for (i=0; i<count; i++) {
- status = OP_STATUS(oplist->op[i]);
- if (status == OP_COMPLETE ||
- status == OP_CANCELLED)
- return TRUE;
- }
-
- return FALSE;
-}
-
- /*----------------------------------------------------------*/
- /*
- /* g_fd_copy
- /*
- /* Copy one set of fd masks to another.
- /*
- /*----------------------------------------------------------*/
-
-g_fd_copy(nfds, source, targ)
-int nfds;
-fd_set *source, *targ;
-{
- register int i;
- register int n = howmany(nfds, NFDBITS); /* number of words for */
- /* this many fd's */
-
- for (i=0; i<n; i++)
- targ->fds_bits[i] = source->fds_bits[i];
-}
- /*----------------------------------------------------------*/
- /*
- /* g_fd_or_and_copy
- /*
- /* Or two sets of file descriptor masks together and
- /* copy to a third. Parameters are:
- /*
- /* 1) First mask count
- /* 2) Second mask count
- /* 3) pointer to first mask
- /* 4) Pointer to second mask
- /* 5) Pointer to output mask
- /*
- /*----------------------------------------------------------*/
-
-g_fd_or_and_copy(nfds1, nfds2 , source1, source2, targ)
-int nfds1, nfds2;
-fd_set *source1, *source2, *targ;
-{
- register int i;
- fd_set *longer; /* points to whichever */
- /* of the two masks is */
- /* longer */
- int tot_words = howmany(max(nfds1,nfds2), NFDBITS);
- int shared_words = howmany(min(nfds1,nfds2), NFDBITS);
-
-
- /*
- * For words which exist in both masks, or the bits together
- */
- for (i=0; i<shared_words; i++)
- targ->fds_bits[i] = source1->fds_bits[i] |
- source2->fds_bits[i];
- /*
- * Copy the rest of whichever is longer
- */
- longer = (nfds1>nfds2) ? source1 : source2;
-
- while (i<tot_words) {
- targ->fds_bits[i] = longer->fds_bits[i];
- i++;
- }
-
- return;
-}
-
- /*----------------------------------------------------------*/
- /*
- /* g_compute_return_fds
- /*
- /* When con_select returns, the count it gives back
- /* applies only to user supplied fd's, not those
- /* relating to connections. Unfortunately, select
- /* gives back both in the masks it returns. Here we
- /* fix only the count to reflect the fact that we will be
- /* turning off bits when and if they are returned.
- /*
- /* We can assume here that the masks provided are as long
- /* as merged_nfds, which means they are at least as long
- /* as gdb_mfd.
- /*
- /*----------------------------------------------------------*/
-
-int
-g_compute_return_fds(select_count, rfds, wfds, efds)
-int select_count; /* count of 1 bits as */
- /* returned from select */
-fd_set *rfds, *wfds, *efds; /* read, write, and except */
- /* maps as returned from */
- /* select */
-{
- register int i;
- register int return_count = select_count; /* the value we'll return */
- register int words; /* number of words in each */
- /* of the connection masks */
-
-
- /*
- * Since we can only decrement the count, there's no sense doing
- * any work if it's already 0;
- */
- if (return_count == 0)
- return 0;
- /*
- * Figure out how many words we have to look at to get all the
- * bits covered by connection masks.
- */
- words = howmany(gdb_mfd, NFDBITS);
-
- /*
- * For words which are involved in the connection masks, check
- * for matches and decrement the count accordingly. Stop when
- * the count hits 0 or we're out of words.
- */
- for (i=0; i<words && (return_count>0) ; i++) {
- return_count -= g_bitcount((unsigned int)
- (gdb_crfds.fds_bits[i] &
- rfds->fds_bits[i]));
- return_count -= g_bitcount((unsigned int)
- (gdb_cwfds.fds_bits[i] &
- wfds->fds_bits[i]));
- return_count -= g_bitcount((unsigned int)
- (gdb_cefds.fds_bits[i] &
- efds->fds_bits[i]));
- }
-
- return return_count;
-}
-
- /*----------------------------------------------------------*/
- /*
- /* g_fd_reset_conbits
- /*
- /* Given a user supplied fd bit mask and a connection
- /* related bit mask, turn off any of the connection related
- /* bits in the user mask, and copy the result to a supplied
- /* target.
- /*
- /* 1) User mask count
- /* 2) Connection mask count
- /* 3) Pointer to user mask
- /* 4) Pointer to connection mask
- /* 5) Pointer to output mask
- /*
- /* Output is always the same length as the user mask.
- /*
- /*----------------------------------------------------------*/
-
-
-g_fd_reset_conbits(nfds, con_nfds , source, conbits, targ)
-int nfds, con_nfds;
-fd_set *source, *conbits, *targ;
-{
- register int i;
- register int tot_words = howmany(nfds, NFDBITS); /* this rtn never*/
- /* returns a mask longer */
- /* than nfds */
- register int shared_words = howmany(min(nfds, con_nfds), NFDBITS);
-
-
- /*
- * For words which exist in both masks, turn off bits from conmask
- */
- for (i=0; i<shared_words; i++)
- targ->fds_bits[i] = source->fds_bits[i] &
- ~(conbits->fds_bits[i]);
- /*
- * Copy the rest of source, if any
- */
- if (tot_words > shared_words)
- while (i<tot_words) {
- targ->fds_bits[i] = source->fds_bits[i];
- i++;
- }
-
- return;
-}
-
-\f
-/************************************************************************/
-/*
-/* CONNECTION MANIPULATION
-/*
-/* Routines to control data transmission on gdb connections.
-/*
-/************************************************************************/
-
- /*----------------------------------------------------------*/
- /*
- /* con_select (con_select)
- /*
- /* This operation has exactly the same semantics as the select
- /* system call, except that (1) it implicitly selects all file
- /* descriptors controlled by connections, as well as those
- /* explictly specified and (2) it allows transmission and
- /* receipt to progress on all connections and (3) it considers
- /* a connection to be selected iff a transmission operation
- /* which had been pending becomes complete. One may consider
- /* that con_select turns the fd's controlled by sockets into
- /* packet streams rather than byte streams. Note also that
- /* this operation differs from a traditional select and an
- /* op_select in that it is not robust against waiting for
- /* connections with pre-completed activity. This could be
- /* added, but since it's an internal routine anyway, it seems
- /* not to be worthwhile. Also, this routine presumes that all
- /* possible progress has been made before con_select is invoked.
- /*
- /* This operation hangs in a select. If activity is
- /* discovered on any of the sockets controlled by the database
- /* library, then the corresponding input is read and
- /* appropriate processing is done. If any of the transmission
- /* operations which had been pending on one of these
- /* connections completes, then con_select may return. In
- /* fact, con_select attempts to complete any further
- /* connection related activity which can be done without
- /* blocking, but con_select never blocks AFTER a transmission
- /* operation has gone complete.
- /*
- /* If activity is detected on any of the file descriptors
- /* supplied by the user, then a count and bit fields are
- /* returned just as for select. (Activity on database sockets
- /* is never reflected in either count or bitfields.) Timeout
- /* causes a return, as with select. Upon return, the program
- /* must check for competion or termination on all of the
- /* connections in which he/she is interested, for activity on
- /* the selected file descriptors, and for timeouts, if
- /* requested, since any or all of these may be reported
- /* together.
- /*
- /* Return values for con_select: >0 same as for select, 0 time
- /* expired, -1, error in select on user fd, -2, connections
- /* have progressed but nothing else of interest.
- /*
- /*
- /*----------------------------------------------------------*/
-
-int
-con_select(nfds, readfds, writefds, exceptfds, timeout)
-int nfds;
-fd_set *readfds, *writefds, *exceptfds;
-struct timeval *timeout;
-{
- int merged_nfds; /* the index of the last */
- /* file desc we care about.*/
- int select_fds; /* number of file */
- /* returned from select */
- /* descriptors */
- int return_fds; /* fds count to be returned */
- /* to the user */
- int complete_count; /* number of connections on */
- /* which operations have */
-
- /* completed */
-
- /*
- * figure out highest number file descriptor to worry about
- */
- merged_nfds = max(nfds, gdb_mfd); /* the number we control */
- /* or last one the user */
- /* supplied, whichevere is */
- /* higher */
- /*
- * Loop waiting for activity to occur and processing it as appropriate.
- * Note that the order of the tests, calls to select, and gdb_progress
- * in this code is critical. Think it through before you change it
- * to make sure that progress is always made when possible, and that
- * returns are always made when needed.
- */
- while (TRUE) {
- /*
- * Prepare working copies of the file descriptor maps
- * based on the ones supplied by the caller, as well as
- * all the descriptors used by connections. The latter
- * are mapped in the gdb_c?fds global variables.
- */
- g_fd_or_and_copy(nfds,gdb_mfd,readfds,&gdb_crfds,&last_crfds);
- g_fd_or_and_copy(nfds,gdb_mfd,writefds,&gdb_cwfds,&last_cwfds);
- g_fd_or_and_copy(nfds,gdb_mfd,exceptfds,&gdb_cefds,&last_cefds);
- /*
- * Use select to wait for something to happen. Compute
- * number select would have returned if connection related
- * fd's had not been supplied.
- */
- select_fds = select(merged_nfds, &last_crfds, &last_cwfds, &last_cefds,
- timeout);
- /*
- * There are some messy things we have to worry about here:
- *
- * 1) Select could return an error. In particular, some
- * versions of Unix will return -1 with EBADF if one
- * of the sockets has closed. We should call a
- * procedure here to see if this has happened to one
- * of ours.
- *
- * 2) Other versions of Unix will claim that there is activity
- * on our socket when in fact the other end is closed.
- * We will leave it to gdb_move_data to try the select
- * and make the appropriate decision.
- *
- * Yes folks, messy but true. If we got an error (case 1),
- * then let's see if we get the same error when we're only
- * looking at the caller's fd's.
- *
- */
- return_fds = 0;
- if (select_fds < 0)
- if ( errno != EBADF)
- return -1;
- else {
- g_fd_or_and_copy(nfds,0,readfds,&gdb_crfds,&last_crfds);
- g_fd_or_and_copy(nfds,0,writefds,&gdb_cwfds,&last_cwfds);
- g_fd_or_and_copy(nfds,0,exceptfds,&gdb_cefds,&last_cefds);
- if (select(nfds, &last_crfds, &last_cwfds, &last_cefds,
- &gdb_notime)<0) {
- g_fd_copy(nfds, &last_crfds, readfds);
- g_fd_copy(nfds, &last_cwfds, writefds);
- g_fd_copy(nfds, &last_cefds, exceptfds);
- return -1; /* select EBADF */
- } else {
- /*
- * We should close the connection here
- */
- GDB_GIVEUP("con_select: EBADF on GDB controlled file.")
- }
- }
- else {
- return_fds = g_compute_return_fds(select_fds,
- &last_crfds, &last_cwfds, &last_cefds);
- }
- /*
- * If some connection related descriptors were selected, then
- * try to make progress on them. Find out if any new operations
- * could be completed.
- */
- if (select_fds != return_fds)
- complete_count = gdb_fastprogress();
- else
- complete_count = 0;
- /*
- * Now, based on the number of operations complete,
- * the number of user file descriptors satisfied, and
- * the possible return of a timeout from select, decide
- * whether to return to the caller. Note that timeout
- * is identified by select_fds == 0.
- */
- if (complete_count > 0 || /* operations are complete */
- return_fds >0 || /* user files satisfied */
- select_fds ==0) { /* timeout in select */
- /*
- * Connection related fd bits are never returned
- * to the caller. Reset them. The count of bits
- * was already adjusted appropriately above. I don't
- * think there's too much wasted effort in this,
- * but we should watch it if profiling indicates
- * lots of time being spent computing in con_select.
- * Put the updated bit masks in the caller supplied
- * maps.
- */
- g_fd_reset_conbits(nfds,gdb_mfd,&last_crfds,&gdb_crfds,
- readfds);
- g_fd_reset_conbits(nfds,gdb_mfd,&last_cwfds,&gdb_cwfds,
- writefds);
- g_fd_reset_conbits(nfds,gdb_mfd,&last_cefds,&gdb_cefds,
- exceptfds);
- if (select_fds ==0)
- return 0; /* real timeout */
- if (return_fds ==0) /* something must have */
- /* completed, but our */
- /* count looks like a */
- /* timeout*/
- return (-2); /* only connections have */
- /* somethine complete */
- else
- return return_fds;
- }
- }
-}
+++ /dev/null
-/*
- * $Source$
- * $Header$
- */
-
-#ifndef lint
-static char *rcsid_gdb_trans2_c = "$Header$";
-#endif
-
-
-/************************************************************************
- *
- * gdb_trans2.c
- *
- * GDB - Data Transport Services Routines (Part 2)
- *
- * Author: Noah Mendelsohn
- * Copyright: 1986 MIT Project Athena
- * For copying and distribution information, please see
- * the file <mit-copyright.h>.
- *
- *
- * These routines implement layer 6 of the Client Library
- * Specification of the GDB system, as well as the facilities
- * outlined in the GDB Protocol Specification. Part 2 of 2.
- *
- * Some of the routines specified are actually implemented as
- * macros defined in gdb.h.
- *
- ************************************************************************/
-
-#include <mit-copyright.h>
-#include <sys/types.h>
-#include <errno.h>
-#include <stdio.h>
-#include "gdb.h"
-#include <sys/uio.h>
-#include <sys/socket.h>
-#include <string.h>
-extern int errno; /* Unix error slot */
-
-/*
- * The following values are returned by g_con_progress
- */
-#define NOPROGRESS 0 /* nothing happened on this */
- /* connection--must be 0*/
-#define PROGRESS 1 /* connection has progressed */
-#define COMPLETE 2 /* an operation has */
- /* completed on this con */
-\f
-/************************************************************************/
-/*
-/* queue_operation(queue_operation)
-/*
-/* Add an operation to the queue for a given connection, and
-/* then allows all connections to progress. Returns the last
-/* known status of the operation.
-/*
-/************************************************************************/
-
-int
-queue_operation(con, direction, op)
-CONNECTION con;
-int direction;
-OPERATION op;
-{
- register HALF_CONNECTION hcon = (direction==CON_INPUT)?(&(con->in)):
- (&(con->out));
- GDB_CHECK_CON(con, "queue_operation")
- /*
- * Write message to debugging log
- */
- if (gdb_Debug & GDB_LOG)
- fprintf(gdb_log, "op queued: con=0x%x dir=%s op=0x%x Q was %s empty\n",
- con, (direction == CON_INPUT)?"INPUT":"OUTPUT",
- op, (hcon->op_q_first == (OPERATION)hcon)?"":"not");
- /*
- * Make sure connection is up
- */
- if (con->status != CON_UP) {
- op->status = OP_CANCELLED;
- if (gdb_Debug & GDB_LOG)
- fprintf(gdb_log, "\nop NOT queued\n");
- return OP_CANCELLED;
- }
-
- /*
- * Put the new operation at the end of the queue
- */
- op->prev = hcon->op_q_last;
- op->next = (OPERATION)hcon;
- hcon->op_q_last->next = op;
- hcon->op_q_last = op;
- /*
- * Mark it as queued
- */
- op->status = OP_QUEUED;
- op->halfcon = hcon;
-
- /*
- * Force progress on this connection
- */
- (void) g_con_progress(con - gdb_cons);
- /*
- * Con_select with notime is used here as a kind of fudge for
- * doing a fastprogress with a select built in before it.
- */
- (void) con_select(0, (fd_set *)0, (fd_set *)0, (fd_set *)0,
- &gdb_notime);/* XXX */
- /*
- * Return the last known status of the operation
- */
- return op->status;
-}
-/************************************************************************/
-/*
-/* requeue_operation
-/*
-/* This routine may be called from an init or continuation routine
-/* to cause the current operation to be requeued on a new connection.
-/* The init routine field ofthe operation should be properly set to
-/* indicate the routine to receive control when the operation actually
-/* runs on the new connection. The caller of this routine is
-/* responsible for returning the status OP_REQUEUED to its caller.
-/*
-/* This routine returns the status of the newly queued operation.
-/* Note, however, that even if this operation returns the status
-/* CANCELLED, the operation itself may not continue to execute
-/* on the old connection and it should return the status OP_REQUEUED,
-/* NOT OP_CANCELLED (at least in this implementation.)
-/*
-/************************************************************************/
-
-int
-requeue_operation(con, direction, op)
-CONNECTION con;
-int direction;
-OPERATION op;
-{
- /*
- * Make sure the connection supplied is a legal one
- */
- GDB_CHECK_CON(con, "requeue_operation")
- /*
- * Write message to debugging log
- */
- if (gdb_Debug & GDB_LOG)
- fprintf(gdb_log, "op requeued: new con=0x%x dir=%s op=0x%x\n",
- con, (direction == CON_INPUT)?"INPUT":"OUTPUT",
- op);
- /*
- * Dequeue the operation from its old half connection
- */
- (void) g_op_newhead(op->halfcon);
-
- /*
- * Now queue it on the new one
- */
- return queue_operation(con, direction, op);
-}
-
-/************************************************************************/
-/*
-/* g_preempt_me
-/*
-/* Sticks a new operation in ahead of the current one and runs it
-/* on the current connection. May be called only from an init or
-/* continuation routine. The old operation must have completely
-/* prepared the descriptor for the new operation, i.e. it should
-/* be in the same state as it would be for a call to queue_operation.
-/* g_preempt_me makes it possible for operations to be built by
-/* composition of other smaller operations, since newop runs, in
-/* a sense, as a subroutine of oldop. opdop must (1) reset its
-/* initialization routine to be a routine to be called when newop
-/* completes or cancels and (2) return the status OP_PREEMPTED to
-/* its caller.
-/*
-/************************************************************************/
-
-int
-g_preempt_me(oldop, newop)
-OPERATION oldop;
-OPERATION newop;
-{
- register OPERATION old=oldop, new=newop;
- register HALF_CONNECTION hc = old->halfcon;
-
- /*
- * Write message to debugging log
- */
- if (gdb_Debug & GDB_LOG)
- fprintf(gdb_log, "op preempted: halfcon=0x%x oldop=0x%x newop=0x%x\n",
- hc,oldop,newop);
- /*
- * link in the new operation
- */
- old->prev = new;
- hc->op_q_first = new;
- new->prev = (OPERATION)hc;
- new->next = old;
- /*
- * Set the status of the new operation
- */
- new->status = OP_QUEUED;
- new->halfcon = hc;
- /*
- * Change the status of the old operation (one could argue that
- * this should be done in gdb_hcon_progress after the return code
- * is detected.)
- */
- old->status = OP_QUEUED;
- return OP_QUEUED;
-}
-
-
-\f
-/************************************************************************/
-/*
-/* gdb_progress
-/*
-/* This routine should be called whenever it is suspected that
-/* progress can be made on any connection. This routine will
-/* cause all connections to proceed as far as they can without
-/* blocking, and will make a best effort to avoid long blocks.
-/* This routine MAY retain control for long periods when sustained
-/* progress is possible, but it will not knowingly hang.
-/*
-/* Returns: number of connections on which OPERATIONS have
-/* COMPLETED (not just progressed).
-/*
-/************************************************************************/
-
-int
-
-gdb_progress()
-{
- register int i; /* index to available */
- /* connections */
- register int return_value = 0; /* the value we return */
- int rc; /* short term storage for */
- /* a return code */
- int progress_made; /* true when some con */
- /* made progress during */
- /* latest pass through list */
- int complete_map[GDB_MAX_CONNECTIONS]; /* indicates whether a */
- /* transmission operation */
- /* is newly complete on */
- /* corresponding connection */
- /* 1 if yes else 0 */
- int maxcon = gdb_mcons; /* gdb_mcons may change */
- /* out from under us if */
- /* connections break. This */
- /* is the initial value. */
-
- /*
- * Zero out the completion map for all connections.
- */
- for (i=0; i<maxcon; i++)
- complete_map[i]=0;
-
- /*
- * Make repeated passes through all the fd's until a pass is made
- * in which none makes any progress. This logic is important,
- * because it catches the case where A is blocked, B makes progress,
- * and A unblocks during the period where B is progressing.
- */
-
- do {
- progress_made = FALSE;
- for (i=0; i<gdb_mcons; i++) {
- if (rc = g_con_progress(i)) { /* note: NOPROGRESS==0 */
- progress_made = TRUE;
- if (rc == COMPLETE)
- complete_map[i] = 1;
- }
- }
- } while (progress_made);
-
- /*
- * We've gone as far as we can, now find out how many connections
- * have had operations complete.
- */
- for (i=0; i<maxcon; i++)
- return_value += complete_map[i];
-
- return return_value;
-}
-
-\f
-/************************************************************************/
-/*
-/* gdb_fastprogress
-/*
-/* Similar to gdb_progress, but this routine attempts progress
-/* only on those connections which have already shown themselves
-/* to be ready for activity by a prior select. This is safe to do
-/* when (1) the only activity we are interested in is that related
-/* to ongoing I/O and (2) a select was recently done to set the
-/* last_c.fds flags. Condition (1) is violated in the case where
-/* an operation may be newly at the head of a queue and its init
-/* routine may not have had a chance to run. Condition (2) is violated
-/* when we are entering after having done significant computation.
-/*
-/* This routine was introduced by Bill Sommerfeld after profiling
-/* revealed that unnecessary attempts to progress on quiescent
-/* sockets were causing excessive overhead in the system. I am
-/* still suspicious that this routine may be getting called in
-/* places where a full gdb_progress is needed. e.g. I'm not
-/* sure its use in op_select is entirely safe.
-/*
-/************************************************************************/
-
-gdb_fastprogress()
-{
- int i;
- int retval=0, rc;
-
- for (i=0; i<gdb_mcons; i++) {
- register CONNECTION con = &gdb_cons[i];
- register int infd = gdb_cons[i].in.fd;
- register int outfd = gdb_cons[i].out.fd;
-
- if(connection_status(con) != CON_UP)
- continue;
-
- gdb_conok = TRUE;
- if ((!(con->in.flags&HCON_UNUSED))&&
- ((con->in.stream_buffer_remaining > 0)
- || FD_ISSET(infd, &last_crfds))) {
- rc = gdb_hcon_progress(CON_INPUT, &con->in);
- if (!gdb_conok) {
- g_stop_with_errno(con);
- rc = COMPLETE;
- }
- if (rc == COMPLETE)
- retval++;
- }
- if ((!(con->out.flags&HCON_UNUSED))&&
- (FD_ISSET(outfd, &last_cwfds))) {
- rc = gdb_hcon_progress(CON_OUTPUT, &con->out);
- if (!gdb_conok) {
- g_stop_with_errno(con);
- rc = COMPLETE;
- }
- if (rc == COMPLETE)
- retval++;
- }
- }
- /*
- * We've gone as far as we can, now find out how many connections
- * have had operations complete.
- */
-
- return retval;
-}
-\f
-/************************************************************************/
-/*
-/* g_con_progress
-/*
-/* Make as much progress as possible on the specified connection.
-/* Returns NOPROGRESS if no bytes moved on either half connection,
-/* PROGRESS, if some moved and no operations completed, or COMPLETE if
-/* any of the operations completed. Note that each connection
-/* consists of two half connections, and we must make each of them
-/* progress as far as possible.
-/*
-/* The nest here starts getting so deep that it's hard to pass state
-/* around efficiently. We use a single global variable, gdb_conok,
-/* to indicate whether the connection we're working on now has died.
-/* The move data routines set this to FALSE whenever there is a
-/* fatal error on a connection. We check it, and do a proper
-/* sever on the connection if it seems to be in trouble.
-/*
-/************************************************************************/
-
-
-int
-g_con_progress(con_id)
-int con_id; /* index of this connection */
- /* in the connection desc. */
- /* arrays*/
-{
- register CONNECTION con= (&gdb_cons[con_id]);
- /* pointer to the connection */
- /* data structure */
- register int progress = NOPROGRESS;
- register int live = TRUE; /* true when we've seen */
- /* enough to make sure we */
- /* want to go around again*/
- int rc;
- /*
- * Check status of connection-if it's not running, then just return.
- */
- if (con->status != CON_UP)
- return NOPROGRESS;
- /*
- * Repeatedly make progress on each half connection until both
- * are idle. Important to keep trying as one may become active
- * while the other is progressing.
- */
-
- gdb_conok = TRUE; /* this gets set to FALSE */
- /* for fatal I/O errors */
- /* there may be a timing */
- /* window here in use of */
- /* HCON_BUSY. Also: it is */
- /* essential that errno */
- /* remain valid after conok */
- /* goes bad */
- while (live) {
- live = FALSE; /* until proven otherwise */
- /*
- * make progress on the input connection note that following
- * logic depends on NOPROGRESS being 0
- */
- if (rc = gdb_hcon_progress(CON_INPUT, &con->in)) {
- live = TRUE;
- progress = max(rc, progress);
- }
- /*
- * See if connection has died
- */
- if (!gdb_conok) {
- g_stop_with_errno(con);
- return COMPLETE; /* dying connection always */
- /* implies that the */
- /* operation at the head */
- /* of the queue completed */
- }
- /*
- * make progress on the output connection
- */
- if (rc = gdb_hcon_progress(CON_OUTPUT, &con->out)) {
- live = TRUE;
- progress = max(rc, progress);
- }
- /*
- * See if connection has died
- */
- if (!gdb_conok) {
- g_stop_with_errno(con);
- return COMPLETE;
- }
- }
-
- return progress;
-}
-
-
-/************************************************************************/
-/*
-/* gdb_hcon_progress
-/*
-/* Allows a specified half-connection to progress as much as possible,
-/* and returns true iff at least one operation is newly completed.
-/*
-/************************************************************************/
-
-int
-gdb_hcon_progress(direction, hc)
-int direction; /* CON_INPUT or CON_OUTPUT */
-struct half_con_data *hc; /* pointer to control struct */
- /* for this half connection */
-{
- HALF_CONNECTION half_con = hc;
- /* half connection pointer */
- /* fast copy in register */
- register OPERATION op; /* current operation on this */
- /* half connection */
- int progress = NOPROGRESS; /* can indicate any progress */
- /* on the half con or */
- /* whether any operations */
- /* completed */
- int done; /* true when no more progress*/
- /* can be made */
- int fcn_result; /* result of latest init or */
- /* continue function */
-
- /*
- * Write message to debugging log
- */
- if (gdb_Debug & GDB_LOG)
- fprintf(gdb_log, "hcon_progress: halfcon=0x%x dir=%s ",
- half_con, (direction==CON_INPUT)?"INPUT":"OUTPUT");
-
- /*----------------------------------------------------------*/
- /*
- /* See if we are being re-entered and are already working
- /* on this half_con. If so, return right now.
- /*
- /*----------------------------------------------------------*/
-
- if (half_con->flags & HCON_BUSY) {
- /*
- * Write message to debugging log
- */
- if (gdb_Debug & GDB_LOG)
- fprintf(gdb_log, "BUSY, returning\n");
- return NOPROGRESS;
- }
-
- /*----------------------------------------------------------*/
- /*
- /* See if there is an operation on this half connection.
- /* If not, return.
- /*
- /*----------------------------------------------------------*/
-
-
- op = half_con->op_q_first; /* pick up first operation */
- /* in queue */
- if (op == (OPERATION)half_con) { /* see if end of circular */
- /* list */
- /*
- * Write message to debugging log
- */
- if (gdb_Debug & GDB_LOG)
- fprintf(gdb_log, "Q EMPTY, returning\n");
- return NOPROGRESS; /* nothing to do on */
- /* this half session */
- }
-
-
- /*----------------------------------------------------------*/
- /*
- /* Loop until all operations are complete, or until no further
- /* progress can be made on this one.
- /*
- /* Loop invariants:
- /*
- /* 1) Op contains the operation at the head of the q, or
- /* else is == half_con, indicating no more operationos
- /* to be processed.
- /*
- /* 2) The operation at the head of the queue is either running
- /* or continuing. As soon as one completes, it is dequeued.
- /*
- /* Progress is declared whenever an operation newly
- /* returns OP_COMPLETE, i.e. whenever there has been
- /* an operation which went from running to complete.
- /*
- /* Done is declared whenever an operation returns anything
- /* other than complete, indicating that it cannot progress
- /* further at this time. Loop ends.
- /*
- /* While we're here, mark us busy so we won't try the
- /* same half_con on reentry.
- /*
- /*----------------------------------------------------------*/
-
- done = FALSE; /* this one may be able to */
- /* progress */
-
- half_con->flags |= HCON_BUSY; /* don't try this hcon */
- /* while we already doing */
- /* it. Could happen if */
- /* we queue new ops */
- half_con->flags &= ~HCON_PROGRESS; /* gdb_move_data will */
- /* indicate progress here*/
- if (gdb_Debug & GDB_LOG)
- fprintf(gdb_log, "LOOPING\n");
-
- /*----------------------------------------------------------*/
- /*
- /* Loop through the operations queued on this half con
- /* trying to make progress on them, in order.
- /*
- /*----------------------------------------------------------*/
-
- while (!done &&
- op != (OPERATION)half_con) {
-
- if (gdb_Debug & GDB_LOG)
- fprintf(gdb_log, "\top=0x%x status%d...",
- op, OP_STATUS(op));
-
- switch (op->status) {
- /*
- * Operation is at head of queue for first time and has
- * never been started. Try to start it up.
- */
- case OP_QUEUED:
- /*
- * Call the initialization routine for this operation
- */
- fcn_result = (*op->fcn.init)(op,half_con,op->arg);
- if (gdb_Debug & GDB_LOG)
- fprintf(gdb_log, "init result=%d\n",
- fcn_result);
-
- switch (fcn_result) {
- case OP_COMPLETE:
- case OP_CANCELLED:
- op->status = fcn_result;
- op = g_op_newhead(half_con);
- progress = COMPLETE;
- break;
- case OP_PREEMPTED:
- op->status = OP_QUEUED;
- /* fall thru */
- case OP_REQUEUED:
- /* important: don't set status on re-queued */
- /* op as it may already have completed in */
- /* its second life ! */
- op = half_con->op_q_first;
- progress = max(progress, PROGRESS);
- break;
- default:
- op->status = fcn_result;
- done = TRUE; /* could not get done */
- }
- break;
- /*
- * Operation is at head of queue and has already
- * started trying to run. The only reason we could be in this
- * state is that the last time we tried to do the requested input
- * or output, all the data could not be moved synchronously.
- * We therefore try to move some more, and if it all goes now,
- * we call the continuation routine.
- */
- case OP_RUNNING:
- /*
- * Try to move some more data. If it won't all
- * go now, we're done with this half connection.
- *
- * If this is a special listening connection which
- * has an operation queued trying to do a listen,
- * then do the listen. Otherwise do an ordinary
- * data move.
- */
- if (half_con->flags & HCON_PENDING_LISTEN) {
- if (gdb_listen(half_con)==FALSE) {
- if (gdb_Debug & GDB_LOG)
- fprintf(gdb_log, "NO LISTEN\n");
- done = TRUE;
- break;
- }
- } else
- if (gdb_move_data(direction, half_con)==FALSE) {
- done = TRUE;
- if (gdb_Debug & GDB_LOG)
- fprintf(gdb_log, "NO DATA\n");
- break;
- }
- /*
- * The pending data transmission has now completed.
- * Call the continuation routine for this operation
- */
- fcn_result = (*op->fcn.cont)(op,half_con,op->arg);
- if (gdb_Debug & GDB_LOG)
- fprintf(gdb_log, "cont result=%d\n",
- fcn_result);
-
- switch (fcn_result) {
- case OP_COMPLETE:
- case OP_CANCELLED:
- op->status = fcn_result;
- op = g_op_newhead(half_con);
- progress = COMPLETE;
- break;
- case OP_PREEMPTED:
- op->status = OP_QUEUED;
- /* fall thru */
- case OP_REQUEUED:
- /* important: don't set status on re-queued */
- /* op as it may already have completed in */
- /* its second life ! */
- op = half_con->op_q_first;
- progress = max(progress, PROGRESS);
- break;
- default:
- op->status = fcn_result;
- done = TRUE; /* could not get done */
- }
- break;
- /*
- * Following cases are all unexpected, at least for the
- * moment. (See explanation of loop invariants for this while
- * loop. Give up if they turn up.
- */
- case OP_COMPLETE:
- GDB_GIVEUP("gdb_hcon_progress: found OP_COMPLETE on q")
- case OP_CANCELLED:
- GDB_GIVEUP("gdb_hcon_progress: found OP_CANCELLED on q")
- case OP_CANCELLING:
- GDB_GIVEUP("gdb_hcon_progress: OP_CANCELLING")
- default:
- GDB_GIVEUP("gdb_hcon_progress: Operation is queued, but is not runnable")
- }
- }
-
- if (progress == NOPROGRESS && (half_con->flags & HCON_PROGRESS))
- progress = PROGRESS;
-
- half_con->flags &= ~HCON_BUSY;
-
- if (gdb_Debug & GDB_LOG)
- fprintf(gdb_log, "hcon_progress: returns %d\n",progress);
-
- return progress; /* NOPROGRESS, PROGRESS */
- /* or COMPLETE */
-}
-\f
-/************************************************************************/
-/*
-/* g_op_newhead
-/*
-/* Dequeues the operation at the head of the queue for the
-/* given half connection and returns the pointer to the
-/* new head of the queue. If the queue is null, then a pointer
-/* to the half_con itself is returned. (The lists are
-/* linked circularly.)
-/*
-/************************************************************************/
-
-OPERATION
-g_op_newhead(hcp)
-HALF_CONNECTION hcp;
-{
- register OPERATION newhead, oldhead;
-
- /*
- * Get old and new heads of chain
- */
- oldhead = hcp->op_q_first;
- newhead = oldhead->next;
- /*
- * Make sure nobody chained a bad one on us
- */
- if (newhead == NULL) {
- if (gdb_Debug & GDB_LOG) {
- fprintf(gdb_log,"\t\tg_op_newhead: found null link, oldhead = 0x%x newhead=0x%x halfcon=0x%x\n\t\t\t hc->first=0x%x hc->last=0x%x\n",
- oldhead, newhead, hcp, hcp->op_q_first,
- hcp->op_q_last);
- }
- GDB_GIVEUP("g_op_newhead: found NULL chain link")
- }
- /*
- * Remove oldhead from chain, fixing up chain pointers
- */
- newhead->prev = oldhead->prev;
- hcp->op_q_first = newhead;
-
- /*
- * Clean up pointers in the newly dequeued operation. This is
- * just for cleanliness and ease of debugging.
- */
- oldhead->next = oldhead->prev = NULL;
- oldhead->halfcon = NULL;
-
- return newhead;
-}
-\f
-/************************************************************************/
-/*
-/* gdb_move_data
-/*
-/* This routine attempts to make further progress on the pending
-/* level transmission operation pending on this half connection.
-/* (Presumes that such an operation is pending.) Returns TRUE
-/* if all the requested data has been moved, else FALSE.
-/*
-/* We assume here that all fd's are set to non-blocking I/O, so
-/* we can safely try reading and writing until they return 0 bytes.
-/*
-/************************************************************************/
-
-#define FIX_BUFFER_POINTERS(hc, count) if (count>0) {hc->next_byte += count; \
- hc->remaining -= count;}
-int
-gdb_move_data(direction, hc)
-int direction; /* CON_INPUT or CON_OUTPUT */
-struct half_con_data *hc; /* pointer to control struct */
- /* for this half connection */
-{
- register HALF_CONNECTION half_con = hc;
- /* half connection pointer */
- /* fast copy in register */
- register fd_set *fdbits; /* the mask we should adjust */
- /* for this direction */
-
- /*
- * For safety, in case we're called when nothing is pending.
- */
- if (half_con->remaining == 0)
- return TRUE;
- /*
- * Move the data into the user's buffer. In the case of input
- * data may come first from the stream buffer, then from the socket
- * itself.
- */
- if (direction == CON_INPUT) {
- gdb_transfer_from_buffer(half_con);
- /*
- * If remaining is greater than 0, then we emptied
- * the stream buffer and still weren't done. Try
- * to read it from the pipe and re-fill the stream
- * buffer.
- */
- if (half_con->remaining) {
- gdb_read_data_and_buffer(half_con);
- }
- } else {
- gdb_write_data(half_con);
- }
- /*
- * The file descriptor masks used for doing selects must be activated
- * when and only when there is a pending operation trying to use
- * the connection. Update the masks for this half connection.
- */
- fdbits = (direction == CON_INPUT)? &gdb_crfds : &gdb_cwfds;
- if (half_con->remaining >0 && gdb_conok)
- FD_SET(half_con->fd, fdbits);
- else
- FD_CLR(half_con->fd, fdbits);
-
- return (half_con->remaining == 0);
-}
-\f
-/************************************************************************/
-/*
-/* gdb_transfer_from_buffer
-/*
-/* Given an inbound half connection, satisfy as much as possible
-/* of desired data from the stream buffer.
-/*
-/************************************************************************/
-
-int
-gdb_transfer_from_buffer(hc)
-register HALF_CONNECTION hc;
-{
- register int count; /* amount to move */
-
- /*
- * Figure out how much, if any, we'll be able to do here
- */
- count = min(hc->remaining, hc->stream_buffer_remaining);
- if (count <= 0)
- return; /* could not satisfy */
- /* any from buffered data*/
-
- /*
- * Copy the data, update both stream and data buffer pointers
- */
-
- memcpy(hc->next_byte, hc->stream_buffer_next, count);
-
- hc->stream_buffer_next += count;
- hc->stream_buffer_remaining -= count;
- FIX_BUFFER_POINTERS(hc, count)
-
-}
-
-\f/************************************************************************/
-/*
-/* gdb_write_data
-/*
-/* This routine implements gdb_move_data for an outbound half
-/* connection.
-/*
-/************************************************************************/
-
-int
-gdb_write_data(hc)
-struct half_con_data *hc; /* pointer to control struct */
- /* for this half connection */
-{
- register HALF_CONNECTION half_con = hc;
- /* half connection pointer */
- /* fast copy in register */
- register int count; /* number of bytes read */
- /* or written in latest */
- /* attempt */
- fd_set *fdbits; /* the mask we should adjust */
- /* for this direction */
- fd_set tst_bits; /* these are used for */
- /* the select we do prior */
- /* to reading which tells */
- /* us whether 0 byte read */
- /* means empty or closed */
- int selected; /* TRUE iff select says */
- /* we should be able to */
- /* progress */
-
- /*
- * Loop writing to the socket until it claims that no more
- * progress can be made. Note that some versions of Unix report
- * socket failure by select = 1, write count = 0. To avoid
- * extra selects, we try the write first, and only do the select/write
- * sequence if write seems not to be progressing.
- */
- FD_ZERO(&tst_bits);
- while(half_con->remaining>0) {
- count = write(half_con->fd, half_con->next_byte,
- (int)min(half_con->remaining,
- GDB_MAX_SOCK_WRITE));
- if (count == 0) {
- FD_SET(half_con->fd,&tst_bits);
- selected = select(gdb_mfd,
- (fd_set *)NULL, &tst_bits,
- (fd_set *)NULL,
- &gdb_notime);
- if (selected == (-1)) {
- gdb_conok = FALSE;
- break;
- }
- if (selected == 0) {
- count =0;
- break;
- }
- count = write(half_con->fd, half_con->next_byte,
- (int)min(half_con->remaining,
- GDB_MAX_SOCK_WRITE));
- if (count==0) {
- if (selected == 1)
- gdb_conok = FALSE;
- break; /* no more data available now*/
- }
- }
- /*
- * Count is != 0
- */
- if (count<0) {
- count = 0;
- if (errno != EWOULDBLOCK) {
- gdb_conok = FALSE; /* tell callers */
- /* that con has */
- /* died */
- }
- break;
- }
-
- half_con->flags |= HCON_PROGRESS;
- FIX_BUFFER_POINTERS(half_con, count)
- }
- /*
- * The file descriptor masks used for doing selects must be activated
- * when and only when there is a pending operation trying to use
- * the connection. Update the masks for this half connection.
- */
- fdbits = &gdb_cwfds;
- if (half_con->remaining >0 && gdb_conok)
- FD_SET(half_con->fd, fdbits);
- else
- FD_CLR(half_con->fd, fdbits);
-
- return;
-}
-\f
-/************************************************************************/
-/*
-/*
-/* gdb_read_data_and_buffer
-/*
-/* This routine is called only when the half_connection stream
-/* buffer is known to be empty and the "next-byte" buffer
-/* has more to be filled in. We try in one read to finish
-/* off the user's request and at the same time fill the stream
-/* buffer for later.
-/*
-/* We assume here that all fd's are set to non-blocking I/O, so
-/* we can safely try reading and writing until they return 0 bytes.
-/*
-/************************************************************************/
-
-int
-gdb_read_data_and_buffer(hc)
-struct half_con_data *hc; /* pointer to control struct */
- /* for this half connection */
-{
- register HALF_CONNECTION half_con = hc;
- /* half connection pointer */
- /* fast copy in register */
- register int count; /* number of bytes read */
- /* or written in latest */
- /* attempt */
- fd_set *fdbits; /* the mask we should adjust */
- /* for this direction */
- struct iovec iov[2]; /* we use this to hold */
- /* pointers to (1) the */
- /* actual user data buffer */
- /* and (2) the pipe length */
- /* pre-read buffer */
- int fix_amount; /* amount to adjust */
- /* half_con->remaining*/
-
- /*----------------------------------------------------------*/
- /*
- /* Mark the stream buffer as empty, in case we don't
- /* get around to filling it.
- /*
- /*----------------------------------------------------------*/
-
- half_con -> stream_buffer_next = half_con -> stream_buffer;
- half_con -> stream_buffer_remaining = 0;
-
- /*----------------------------------------------------------*/
- /*
- /* Loop trying to read data from the socket. We scatter
- /* first into the user's buffer directly, then into
- /* the stream buffer (which helps us save system
- /* calls next time around.) We stop either when:
- /* socket reports error/no progress or user's buffer is
- /* full.
- /*
- /*----------------------------------------------------------*/
-
- /*
- * Loop until either (1) the connection reported that it could
- * not progress any further or (2) the full count has been
- * satisfied. Some versions of Unix observe the rule that
- * a closed connection, especially when reading, is indicated
- * by returning a count of 0 on read when select claims that progress
- * can be made. We used to handle this case. Bill Sommerfeld
- * has introduced a performance change which leaves that checking
- * out in the latest version. To add it back, then ONLY in
- * the case where read returned 0, do a select followed by another
- * read (the order is important). If we ever run on a system that
- * works in this way, we may hang at close time.
- */
-
- while(half_con->remaining>0) {
- /*
- * First we try a read, and if it works, we believe it
- */
- iov[0].iov_base = half_con -> next_byte;
- iov[0].iov_len = half_con -> remaining;
- iov[1].iov_base = half_con -> stream_buffer;
- iov[1].iov_len = half_con -> stream_buffer_length;
- count = readv(half_con->fd, iov, 2);
-
- if (count<0) {
- count = 0;
- if (errno != EWOULDBLOCK)
- gdb_conok = FALSE; /* tell callers that */
- /* con has died */
- break;
-
- }
- if (count == 0) {/* We hit EOF */
- gdb_conok = FALSE;
- break;
- }
-
- /*
- * Count is >0, we moved some data. Note, setting of
- * stream_buffer_remaining can only be non-zero on last
- * time through the loop, because that will be when
- * half_con->remaining goes to zero.
- */
- half_con->flags |= HCON_PROGRESS;
- half_con->stream_buffer_remaining=max(0, count-iov[0].iov_len);
- fix_amount = min(count,half_con->remaining);
- FIX_BUFFER_POINTERS(half_con, fix_amount);
- }
-
- /*
- * The file descriptor masks used for doing selects must be activated
- * when and only when there is a pending operation trying to use
- * the connection. Update the masks for this half connection.
- */
- fdbits = &gdb_crfds;
- if (half_con->remaining >0)
- FD_SET(half_con->fd, fdbits);
- else
- FD_CLR(half_con->fd, fdbits);
-
- return ;
-}
-\f
-/************************************************************************/
-/*
-/* gdb_receive_data (gdb_receive_data)
-/*
-/* This routine is called by an init or continuation routine to
-/* request that a specified amount of data be read, without
-/* blocking, on the supplied connection. This routine returns
-/* OP_COMPLETE if the entire read completed synchronously,
-/* or OP_RUNNING if the read remains ongoing or is cancelling
-/* due to error on the socket.
-/*
-/************************************************************************/
-
-int
-gdb_receive_data(half_con, ptr, len)
-HALF_CONNECTION half_con; /* read on this connection*/
-char *ptr; /* put first byte here */
-int len; /* number of bytes to read */
-{
- /*
- * Fill in the initial state of the attempted receive
- */
- half_con->remaining = len;
- half_con->next_byte = ptr;
-
- /*
- * Now see if we can make some progress on this read, possibly
- * even completing it synchronously. Return appropriate
- * result to our caller. Note: errors are reflected as OP_RUNNING
- * with global variable gdb_cnok set to FALSE.
- */
- if(gdb_move_data(CON_INPUT, half_con))
- return OP_COMPLETE;
- else
- return OP_RUNNING;
-}
-
-/************************************************************************/
-/*
-/* gdb_send_data (gdb_send_data)
-/*
-/* This routine is called by an init or continuation routine to
-/* request that a specified amount of data be written, without
-/* blocking, on the supplied connection. This routine returns
-/* OP_COMPLETE if the entire write completed synchronously,
-/* or OP_RUNNING if the output remains ongoing or there was an error.
-/*
-/************************************************************************/
-
-int
-gdb_send_data(half_con, ptr, len)
-HALF_CONNECTION half_con; /* write on this connection*/
-char *ptr; /* put first byte here */
-int len; /* number of bytes to read */
-{
-
- /*
- * Fill in the initial state of the attempted receive
- */
- half_con->remaining = len;
- half_con->next_byte = ptr;
-
- /*
- * Now see if we can make some progress on this read, possibly
- * even completing it synchronously. Return appropriate
- * result to our caller.
- */
- if(gdb_move_data(CON_OUTPUT, half_con))
- return OP_COMPLETE;
- else
- return OP_RUNNING;
-}
-
-/************************************************************************/
-/*
-/* gdb_start_a_listen
-/*
-/* This routine is called by an init or continuation routine to
-/* request that a connection be done. This routine returns
-/* OP_COMPLETE if the accept completed synchronously,
-/* or OP_RUNNING if the output remains ongoing or there was an error.
-/*
-/************************************************************************/
-
-int
-gdb_start_a_listen(half_con, otherside, lenp, fdp)
-HALF_CONNECTION half_con; /* write on this connection*/
-char *otherside; /* put first byte here */
-int *lenp; /* number of bytes to read */
-int *fdp;
-{
-
- /*
- * Fill in the initial state of the attempted accept
- */
- half_con->accepted_len = lenp;
- half_con->next_byte = otherside;
- half_con->accepted_fdp = fdp;
-
- /*
- * Now see if we can make some progress on this read, possibly
- * even completing it synchronously. Return appropriate
- * result to our caller.
- */
- if(gdb_listen(half_con))
- return OP_COMPLETE;
- else
- return OP_RUNNING;
-}
-\f
-/************************************************************************/
-/*
-/* gdb_listen (gdb_listen)
-/*
-/* This routine is called from gdb_start_a_listen or hcon_progress to attempt
-/* to continue making progress in accepting a connection on a
-/* listening connection.
-/*
-/************************************************************************/
-
-int
-gdb_listen(hc)
-struct half_con_data *hc; /* pointer to control struct */
- /* for this half connection */
-{
- register HALF_CONNECTION half_con = hc;
- /* half connection pointer */
- /* fast copy in register */
-
- GDB_INIT_CHECK
-
- half_con->flags &= ~HCON_PENDING_LISTEN;/* in case we succeed */
-
- /*
- * The first implementatin of this used to do a select to make sure
- * that the accept would not block. Bill Sommerfeld has changed this
- * to non-blocking I/O, so the following code is commented out.
- */
-#ifdef notdef
- FD_ZERO(&tst_bits);
- FD_SET(half_con->fd,&tst_bits);
- selected = select(gdb_mfd,&tst_bits, (fd_set *)NULL, (fd_set *)NULL,
- &gdb_notime);
- /*
- * If selected==(-1), then we know there's something
- * wrong with the socket
- */
- if (selected == (-1)) {
- gdb_conok = FALSE;
- return FALSE;
- }
- /*
- * if selected==0, then we know accept won't do anything, so
- * don't try.
- */
- if (selected == 0) {
- half_con->flags |= HCON_PENDING_LISTEN;
- FD_SET(half_con->fd, &gdb_crfds); /* we'll be looking for */
- /* this whenever we select*/
- return FALSE;
- }
- /*
- * Selected is >0. The accept SHOULD not hang.
- */
-#endif
-
- /*
- * Here is Bill's non-blocking implementation
- */
- *(half_con->accepted_fdp) = accept(half_con->fd,
- (struct sockaddr *)half_con->next_byte,
- half_con->accepted_len);
- /*
- * See whether the accept succeeded
- */
- if (*(half_con->accepted_fdp) < 0) {
- if (errno != EWOULDBLOCK) {
- gdb_conok = FALSE; /* error will be returned */
- /* in shut-down listening con*/
- }
- half_con->flags |= HCON_PENDING_LISTEN;
- FD_SET(half_con->fd, &gdb_crfds);
- return FALSE;
- }
-
- FD_CLR(half_con->fd, &gdb_crfds); /* don't select on this */
- return TRUE;
-}
+++ /dev/null
-@device(PostScript)
-@make(Manual)
-@style(FontFamily "Helvetica", Size 11)
-@style(hyphenation on)
-@style(indent 0)
-@style(leftmargin +4cm)
-@style(footnotes "")
-@modify(example,Size 10, below 1cm, above 1cm, leftmargin +3, rightmargin +0)
-@define(F, FaceCode F, Size 11, TabExport)
-@modify(HD1, Below .75cm, Above 1cm, indent -1cm)
-@modify(HD1A, Below .75cm, Above 1cm)
-@modify(HD2, Below .6cm, Above 1cm, indent -1cm)
-@modify(HD3, Below .6cm, Above 1cm)
-@modify(itemize, Below .5cm, Above .6cm)
-@modify(example, Below .5cm, Above .6cm)
-@modify(float, Below .5cm, Above .6cm)
-@begin(titlepage)
-@begin(titlebox)
-@majorheading(A Guide to Using GDB)
-@heading(Noah Mendelsohn)
-
-@end(titlebox)
-Document Version: 0.4 (DRAFT)
-For use with GDB Release: 0.4
-This draft printed on: @value(Date)
-@copyrightnotice(MIT Project Athena)
-@end(titlepage)
-@Counter(ProgramNumber, Numbered <@1>, Referenced <@1>)
-@set(ProgramNumber=1)
-@Chapter(Introduction)
-
-Many of the most important computer applications involve sharing
-information among the users of a computer system.
-These applications may be simple to create if all users do their
-work on a single timesharing system, but the job is much harder in
-a @i[distributed] system, where each user has his or her own workstation.
-The purpose of GDB Global Database system is to simplify the creation
-of distributed applications in which information sharing is important.
-
-This guide is intended for prospective users of GDB who want to know
-what GDB can do for them and need a general guide to doing the
-job right.
-A complete description of GDB services and interfaces is provided in
-another document titled: "GDB C Library Reference Manual".
-Both guides are intended for readers with a good working knowledge
-of C and Unix@+[TM]@foot(@+[TM]Unix is a trademark of
-AT&T Bell Laboratories) programming.
-A short note titled: "GDB Global Databases for Project Athena" provides
-a general introduction to GDB
-for non-programmers.
-The sections below are intended to answer some of the most basic
-questions about GDB and using this guide.
-@section(What can GDB do?)
-Here is a brief list of the programming tasks that GDB facilitates:
-@begin(itemize)
-@begin(multiple)
-@b[Sharing a relational database through a network]
-
-GDB includes everything needed for a program running on one machine
-in a Berkeley Unix network to create, alter, and query the relations
-in an RTI Ingres database stored on some other machine in the network.
-Programs running on several machines may access the same data simultaneously,
-and a single program may use more than one database at a time.
-The programs may easily be ported across machine architectures, RT/PC
-to Vax for example, and programs running on different machine types
-may share a common database.
-@end(multiple)
-
-@begin(multiple)
-@b[Writing network servers and their clients]
-
-GDB handles most of the bookkeeping and error recovery needed to build
-servers and clients in a Berkeley Unix environment. A completely
-functional demonstration server capable of supporting multiple clients
-with reasonable error detection has been written in about 20 lines
-of C code using the services of GDB (page @pageref[tfsr]).
-Clients running on various types of machine may easily access common
-servers, with GDB handling the necessary data conversions automatically.
-@end(multiple)
-
-@begin(multiple)
-@b[Single Process Unix Servers]
-
-It is customary when writing servers under Unix to fork a separate
-server process to handle each client.
-In addition to this traditional model, GDB provides a rich set
-of asynchronous communication services which facilitate the
-creation of servers in which a single process supports all clients.
-This model is particularly appropriate to the implementation
-of such high performance applications as network games.
-@end(multiple)
-
-@begin(multiple)
-@b[Peer-to-peer communication]
-
-Some communicating applications, like the Berkeley Unix @f[talk] program,
-@index(talk)
-don't fit naturally into a server/client model. GDB also provides
-services for managing connections between @i[peer] programs, of which
-neither is viewed as master or slave.
-@end(multiple)
-@end(itemize)
-
-@section(Which Chapters in this Guide Should I Read?)
-@Index(Chapters, guide to)
-
-Although GDB has a rich set of features for elaborate
-asynchronous programming, many useful applications can be built
-with just the simplest GDB tools. This guide is intended to be read
-selectively. The following are some hints on what to read, depending
-on the job you are trying to do:
-
-@begin(itemize)
-Everyone who uses GDB should read the rest of this "Introduction",
-and the chapter titled "Managing Structured Data".
-
-If you wish to use GDB to access a remote Ingres database, then the
-only other chapter you have to read is "Creating and Using Relational
-Databases with GDB". If you're interested in doing database accesses
-@i[asynchronously], allowing you to overlap your access to several
-databases or to keep control of the local terminal while a query is
-executing, then you should also read the chapter titled "Synchronous
-and Asynchronous Operations".
-
-If you are planning to write your own servers, clients, or other
-communicating programs, then you can skip the chapter on relational
-databases, but you should read instead the chapter "Creating
-Servers, Clients, and Other Communicating Programs". This gives
-all the information you need to use the basic communication capabilities
-of GDB, which will be adequate for the majority of applications.
-Asynchronous communication techniques are described in the chapter
-"Synchronous and Asynchronous Operations".
-@end(itemize)
-
-Three other chapters provide additional material which may be useful on
-occasion. "Hints and Tricks" has some suggestions which may be helpful
-if you can't figure out how to make GDB do exactly what you want.
-"Bugs" lists some of the known shortcomings in current implementations.
-If you're having trouble, this is one of the places to look.
-"How GDB Works" gives a general overview of GDB's internal
-structure, information which should not be needed in writing an
-application, but which may be of interest to serious users. The
-sample programs in the appendices are referenced in the appropriate
-chapters.
-
-@section(What is needed to run GDB?)
-The services of GDB are accessed through a C library.
-You will have to insure that the appropriate C include files are available
-on your system, and that the gdb library is available when you link your
-application. Your system administrator can take care of these things
-for you.
-@i[Note to early users: You may receive the pieces of GDB in slightly
-different form. Documentation supplied with the distribution should
-explain what to do.]
-
-@begin(group)
-@blankspace(1 line)
-Every C main program
-that uses GDB should include the following:
-
-@begin(example)
-#include <stdio.h>
-#include "gdb.h"
-
-int
-main()
-{
- gdb_init();
- /*
- * Once gdb_init has been called, the services of gdb are
- * available. gdb_init may be called only once.
- */
-}
-@end(example)
-@end(group)
-
-Other source files should @f[#include "gdb.h"], but should @i[not]
-@index(gdb.h)
-invoke @f[gdb_init].
-@index(gdb_init)
-
-If you are using GDB to access an Ingres database, then there are
-several other things you must do:
-
-@begin(itemize)
-@begin(multiple)
-Make sure RTI Ingres is installed at the server site where the data
-@index(RTI Ingres)
-@index(Ingres)
-is to be stored.
-@end(multiple)
-
-@begin(multiple)
-Make sure that there is an entry in the file named @f[/etc/services]
-@index(/etc/services)
-at both server and client sites for the service named gdb_db@index(gdb_db).
-The entries at the server and client should be identical.
-Talk to your system administrator if this is not the case.
-@i[Note: as of this writing, GDB does not check @f[/etc/services] when
-accessing Ingres, but soon it will!]
-@end(multiple)
-
-@begin(multiple)
-Make sure that the @f[dbserv] program supplied with GDB
-@index(dbserv)
-is started at the server site.
-@end(multiple)
-
-@end(itemize)
-
-Once these steps have been followed, the full services of the Ingres
-database are available to GDB clients throughout the network. In
-future releases of GDB, the Kerberos Authentication
-Service@index(Kerberos) will be used to verify the identity of clients
-and to provide some options for access control. In the meantime, keep
-in mind that any GDB user on the internet@index(Internet, access
-rights from)
-effectively inherits the access rights of the userid under which the
-database server is running.
-
-@Chapter(Managing Structured Data)
-@label(structure)
-
-This chapter presents a brief overview of the services that GDB
-provides for managing structured data.
-Although the discussion in this chapter uses relational database
-examples, the same GDB features are used to implement servers and
-clients of all kinds.
-
-When Ingres is used to retrieve data from a relational database, the
-result of the query is known as a @i[relation]@index(relation). Each
-relation consists of 0 or more @i[tuples]@index(tuple), and the tuples
-themselves consist of @i[fields]@index(field). Informally, one may
-think of the relation as a table containing one row for each "match"
-found in the database. The rows are called tuples, and each such
-tuple contains the fields which were requested in the query. For
-example, a query to a database containing information on military
-personnel might be:
-
-@begin(example)
-@center[Get the name, rank, and serial number of all personal with over
-5 years of service]
-@end(example)
-
-If there were two such people, then the resulting relation would contain
-two tuples, each with three fields. For example:
-
-@begin(example)
- Name Rank Serial
- ----------------------
- Mary 123 876543
- John 121 875432
-@end(example)
-
-When GDB is used to perform a query, then some standard
-representation must be used to hold the resulting relation.
-Furthermore, GDB must know quite a bit about the @i[types] of the
-fields, whether they are @i[integer] or @i[real] for example, so that
-it can do necessary conversions when transmitting between
-incompatible machines. Since C itself provides no convenient
-way of doing this, GDB uses the following conventions:
-
-GDB has its own notion of @i[data object]@index(Data object, GDB),
-which is slightly different from a C variable or structure. A GDB
-data object may be as simple as a single integer or as complex as an
-entire relation. Each GDB object has a @i[type]@Index(Typing of GDB
-objects), which tells GDB what kind of object it is. Though some of
-the types supported by GDB are similar to those provided by the C
-language, GDB has its own typing mechanism which is intended to
-support the transmission and data conversion services that GDB must
-provide.
-
-Every GDB object has one of the following types:
-
-@begin(itemize)
-@begin(multiple)
-INTEGER_T
-@index[INTEGER_T]
-
-Usually the same as a @i[long] in C.
-@end(multiple)
-
-@begin(multiple)
-REAL_T
-@index[REAL_T]
-
-Usually the same as a @i[double] in C.
-@end(multiple)
-
-@begin(multiple)
-STRING_T
-@index[STRING_T]
-
-For various reasons, GDB uses counted byte strings. The actual implementation
-of a STRING_T is:
-
-@begin(example)
- struct {
- char *ptr; /* pointer to first*/
- /* byte of data, or*/
- /* NULL if none */
- int length; /* number of bytes */
- /* allocated */
- }
-@end(example)
-
-Though these strings are commonly used to hold C language null
-terminated strings, that is not required. Any null(s) must be
-included in the length. GDB includes library services for creating
-and manipulating STRING_T data (page @pageref[string_data_in_gdb]).
-Future releases of GDB may include a C_STRING_T type, which would
-correspond directly to a C null terminated string. @end(multiple)
-
-@begin(multiple)
-DATE_T
-@index[DATE_T]
-
-Used to contain a date field retrieved from Ingres. This is implemented
-as a 25 byte character array. See Ingres documentation for conventions.
-@end(multiple)
-
-@begin(multiple)
-TUPLE_DESCRIPTOR_T
-@index[TUPLE_DESCRIPTOR_T]
-
-Every tuple and relation is described by a tuple descriptor. The tuple
-descriptor contains the names of the fields, their position in the tuple,
-and the types of the fields. Tuple descriptors are themselves GDB objects.
-@end(multiple)
-
-@begin(multiple)
-TUPLE_T
-@index[TUPLE_T]
-
-Refers to an entire tuple, including its descriptor. An item of
-type TUPLE_T is self-describing. GDB can tell by inspection what
-its structure is, as well as the names, types and contents of its fields.
-@end(multiple)
-
-@begin(multiple)
-RELATION_T
-@index[RELATION_T]
-
-Refers to an entire relation, all its tuples and their descriptors. An item of
-
-type RELATION_T is also self-describing.
-@end(multiple)
-
-@begin(multiple)
-TUPLE_DATA_T
-@index[TUPLE_DATA_T]
-
-This is for internal use of GDB. It refers to just the data part of
-a tuple, when the description can be inferred.
-@end(multiple)
-
-@begin(multiple)
-User Defined Types
-@Index[User defined types]
-
-GDB allows users to define their own types. This can be useful for
-sophisticated applications which want GDB to take care of transmission
-and data conversion for unusual data structures. Few applications
-will actually require this flexibility, which is explained briefly under
-"Hints and Tricks".
-@end(multiple)
-@end(itemize)
-
-Why go to all this trouble, and what does it mean to say that things
-as simple as INTEGER_T and as complex as RELATION_T are both just types?
-As it turns out, this convention is the basis of a very powerful scheme
-for transmitting structured data from one computer to another. GDB
-includes a service called @f[send_object] which is used to transmit
-@index(send_object)
-information through a network, and a matching service called
-@f[receive_object].
-@index(receive_object)
-Because of the uniform typing scheme outlined above, the same routine
-used to send a single integer may be used to send an entire relation.
-Furthermore, the typing information is just what GDB needs to do efficient
-data conversions when two incompatible machines are communicating.
-
-There is one more point of confusion which should be clarified. As
-@Index(Types, GDB vs. C)
-shown above, the system supplied GDB types have names like INTEGER_T,
-STRING_T, and so on.
-The confusion arises because these are @i[not] C language typedefs,
-they are preprocessor constants used as indices into GDB's type
-tables. For each GDB type there is usually a corresponding C language
-type. For example, INTEGER_T is GDB's term for what C calls a
-@f[long]. Table @ref[GDBTypes] shows the correspondence between GDB types
-and C types:
-
-@begin(table)
-@caption(GDB Types and C Types)
-@tag[GDBTypes]
-@tabset(.75in, 3.5in)
-
-@u[@\GDB Type@\C Language Type]
-
-@\INTEGER_T@\long
-
-@\REAL_T@\double
-
-@\STRING_T@\STRING
-
-@\DATE_T@\char xx[25]
-
-@\TUPLE_DESCRIPTOR_T@\TUPLE_DESCRIPTOR
-
-@\TUPLE_T@\TUPLE
-
-@\RELATION_T@\RELATION
-
-@\TUPLE_DATA@\TUPLE->
-@\@\(i.e. a de-referenced tuple)
-@end(table)
-
-All of the GDB type identifiers, as well as the C language types with
-uppercase names are defined for you in @f[gdb.h].
-@index(gdb.h)
-
-When should you use the GDB type and when should you use the C type?
-In general, the C type is used whenever you are declaring a variable
-to C, or casting @index[casting pointers] a pointer to a new type.
-For example:
-
-@begin(example)
-int
-main()
-{
- TUPLE t; /* C declaration */
- /* for a TUPLE */
- RELATION r; /* C declaration */
- /* for a RELATION */
- STRING s; /* C declaration */
- /* for a counted */
- /* byte string */
-
- string_alloc(&s,25); /* allocate a 25 */
- /* character string */
-}
-@end(example)
-
-The GDB types are used when you are telling @i[GDB] about the type
-of data it's going to manipulate:
-
-@begin(example)
-int
-main()
-{
- STRING s; /* C declaration */
- /* for a counted */
- /* byte string */
- CONNECTION c;
-
- /*
- * send the data in STRING s on connection c. The
- * STRING_T indication below tells GDB what kind of
- * object is being sent.
- */
- send_object(c, &s, STRING_T);
-}
-@end(example)
-
-@section(Building and Using Relations)
-
-@index[Relations, creating] Each time you do an Ingres query, the
-results are added to the local relation that you supply. That means
-that you will usually want to create an empty relation before doing a
-query, then pass that to the @f[db_query] routine @index(db_query)
-@index(querying Ingres databases)
-which will fill it in with the results of the query. Creating the
-relation is done in two steps: (1) you create a
-TUPLE_DESCRIPTOR@index[tuple descriptor]@index[TUPLE_DESCRIPTOR]
-which contains information about the names and types of the fields
-which will be in the tuples of the relation and (2) using the
-descriptor, you create the empty relation. Note that the same
-descriptor may be used repeatedly to create many relations. Here is
-an example with one descriptor and two relations:
-
-@begin(example)
-int
-main()
-{
- char *fld_names[] = {"name", "rank", "serial"};
- FIELD_TYPE fld_types[] = {STRING_T, INTEGER_T, INTEGER_T};
-
- TUPLE_DESCRIPTOR desc;
- RELATION old_timers;
- RELATION new_recruits;
-
- desc = create_tuple_descriptor(3, fld_names, fld_types);
-
- old_timers = create_relation(desc);
- new_recruits = create_relation(desc);
-
-}
-@end(example)
-
-GDB provides many macros and procedures for manipulating relations,
-tuples, and fields. Let's assume that the program goes on to do two
-successful queries, one for old_timers and one for new_recruits. The
-declarations and code in Program #@ref(ProgramNumber) might be added
-to the example to print the results of the new recruits query.
-
-@begin(float)
-@index(Fields, accessing)
-@index(Relations, tuples in)
-@index(Tuples in relations)
-
-@center(PROGRAM #@ref(ProgramNumber))
-@set(ProgramNumber=+1)
-@begin(example)
-TUPLE t;
-STRING *name;
-int *rank;
-int *serial;
-/*
- * Define symbolic names for the positions of the fields
- * in the tuples. There are other ways to do this,
- * but this does work:
- */
-#define NAME 0
-#define RANK 1
-#define SERIAL 2
-/*
- * the following code would go after the query
- *
- * the loop is executed once for each new
- * recruit returned by the query
- */
- for(t=FIRST_TUPLE_IN_RELATION(new_recruits);
- t != NULL;
- t = NEXT_TUPLE_IN_RELATION(new_recruits, t)) {
- /*
- * Get a pointer to each field in the tuple
- */
- name = (STRING *)FIELD_FROM_TUPLE(t, NAME);
- rank = (int *)FIELD_FROM_TUPLE(t, RANK);
- serial = (int *)FIELD_FROM_TUPLE(t, SERIAL);
- /*
- * print the fields
- */
- printf("name=%s rank=%d serial=%d\n",
- STRING_DATA(*name), *rank, *serial);
- }
-/*
- * return the memory used to hold the new_recruits relation
- */
-delete_relation(new_recruits);
-@end(example)
-@end(float)
-
-The example illustrates several important points. Variables
-representing structured objects
-like TUPLE, TUPLE_DESCRIPTOR and RELATION are actually pointers.
-@index(Relations, pointer representation of)
-@index(Tuples, pointer representation of)
-@index(Relations, tuples in)
-@index(Tuples in relations)
-Because TUPLE is a pointer type, TUPLE variables may be used
-as cursors in a relation. For example, you might declare a TUPLE
-variable called @f[ranking_recruit] and set it in the loop above to
-the tuple of the recruit with the highest rank.
-Lots of TUPLE variables may point into
-the same relation, or even to the same tuple, but like any other
-pointers, they become invalid if the corresponding structures are
-deleted. The C value NULL is used to represent non-existent TUPLES,
-RELATIONS, and descriptors.
-
-As shown in the program, the @f[FIELD_FROM_TUPLE] macro is used to find
-@index(Fields, accessing)
-@index(FIELD_FROM_TUPLE)
-a field within a tuple. @f[FIELD_FROM_TUPLE] returns a
-@i[pointer] to the requested field, and it is up to you to cast the
-pointer according to the type of field being retrieved. In the
-program, @f(name) is of type STRING_T, so the
-cast @f[(STRING *)] is used. Pointer casting is a feature of
-the C language which is explained in Kernighan and Ritchie and other C
-programming guides.
-
-Note that @f[FIELD_FROM_TUPLE] refers to fields by their
-position in the tuple, not by name. This was done for efficiency, as
-@f[FIELD_FROM_TUPLE] is likely to be called frequently by GDB
-applications. The argument supplied may be a variable,
-but it is your responsibility to correctly interpret the type of
-the resulting pointer. GDB can also help you find fields by symbolic
-name, but that takes longer. The @f[field_index]
-@Index(Fields, finding by name)
-function takes
-as its argument a tuple descriptor (@i[not] a tuple) and the string
-name of a field; it returns the field number of the named field. If
-you want to pull fields by name from several similar tuples, the
-efficient way is to find the index once using @f[field_index],
-then use that repeatedly in calls to @f[FIELD_FROM_TUPLE].
-
-A brief guide to the functions and macros provided for manipulating structured
-data is provided in Appendix I.
-
-@section(STRING Data in GDB)
-@label(string_data_in_gdb)
-
-@index(string)
-@index(STRING_T)
-As noted previously, GDB represents string data as counted fields of
-bytes. If you create a tuple with a field of type STRING_T, then the
-space actually allocated in the tuple is just that needed to hold the
-location and length of the string data, not the space for the data
-itself. @f[gdb.h] @index[gdb.h] contains the following definition,
-which may be used when referring to STRING fields, or to create your
-own variables of type STRING.
-
-@begin(example)
-@index[FIELD_FROM_TUPLE]
-@index[string_alloc]
-@index[MAX_STRING_SIZE]
-@index[STRING_DATA]
- typedef struct str_dat {
- char *ptr; /* pointer to first */
- /* byte of data, or */
- /* NULL if none */
- int length; /* number of bytes of*/
- /* data allocated */
- } STRING;
-@end(example)
-
-If you have such a tuple and wish to initialize the string field to have
-space for up to 100 bytes of data, then the code in Program
-#@ref(ProgramNumber) might be used.
-
-@begin(float)
-
-@center(PROGRAM #@ref(ProgramNumber))
-@set(ProgramNumber=+1)
-@begin(example)
- TUPLE t;
- #define STRFIELD 3 /* index of the string*/
- /* field in the tuple */
- STRING *sp; /* pointer to the field*/
-
- /*
- * Get a pointer to the string field in the tuple
- */
- sp = (STRING *)FIELD_FROM_TUPLE(t,STRFIELD);
-@index[FIELD_FROM_TUPLE]
- /*
- * Allocate space for 100 bytes
- */
- string_alloc(sp, 100);
-@index[string_alloc]
- /*
- * Copy some text into the new buffer
- */
- strcpy(STRING_DATA(*sp), "Hi there");
-@index[STRING_DATA]
- /*
- * Note, at this point:
- *
- * MAX_STRING_SIZE(*sp) == 100
- *
- * strlen(STRING_DATA(*sp)) == 9
- */
-@end(example)
-@end(float)
-
-A call to @f[string_free] would free all 100 bytes of data. Note that
-@index(string_free)
-a routine named @f[null_tuple_strings] frees all the strings in a given
-@index(null_tuple_strings)
-tuple.
-
-If you're writing your own servers and clients, then STRING variables
-are a convenient way of sending uninterpreted byte strings from one
-machine to another, as shown in Program #@ref(ProgramNumber).
-
-@begin(float)
-
-@center(PROGRAM #@ref(ProgramNumber))
-@set(ProgramNumber=+1)
-@begin(example)
- CONNECTION con;
- char buffer[500];
- STRING s;
-
- /*
- * Set up string descriptor s as required by gdb
- */
-
- STRING_DATA(s) = buffer; /* copy the pointer*/
-
- /*
- * For demonstration purposes, put some data into the
- * buffer.
- */
-
- strcpy(buffer, "Some string");
- MAX_STRING_SIZE(s) = strlen(buffer)+1;
- /* include space for */
- /* the null */
-
- /*
- * send the object through the connection.
- * we assume that the connection is already
- * started
- */
- send_object(con, &s, STRING_T);
-@end(example)
-@end(float)
-
-As of this writing, the STRING_T type is the only means GDB provides
-for manipulating byte strings. Future releases may also support a
-C_STRING_T, which would more closely match C's convention of null
-terminated strings. The current implementation was chosen for its
-generality and efficiency. STRING_T may be used to hold any array of
-bytes, including those with embedded null characters, and for long
-strings, STRING_T avoids the overhead of searching the string to
-compute its length.
-
-@Chapter(Creating and Using Relational Databases with GDB)
-
-@index[Relational databases]
-GDB may be used to obtain the full services of an RTI Ingres
-@index[RTI Ingres]
-@index[Ingres]
-relational database from a program running at any node in a Berkeley
-Unix network. In addition to most of the services provided by RTI
-Ingres version 3, GDB supports:
-
-@begin(itemize)
-Remote access from any machine in a network.
-
-Parallel access to multiple databases, possibly but not necessarily at
-multiple sites.
-
-Transparent access from incompatible machines (e.g. RT/PC to VAX.)
-@end(itemize)
-
-In fact, GDB is designed to be used with a variety of database
-products, but RTI Ingres is the only one for which an implementation
-exists at present. Nevertheless, every attempt has been made to
-isolate the Ingres dependencies in GDB.
-GDB's current support of relational databases is based on the Ingres QUEL
-query language, which is documented in the appropriate manuals from
-RTI. It is @i[essential] that you familiarize yourself with QUEL
-before attempting to use GDB to manipulate relational databases. A
-knowledge of QUEL is presumed in the sections which follow.
-
-@section(Accessing Databases)
-
-The general procedure for using relational databases in GDB is:
-
-@begin(itemize)
-Use @f[access_db] to initiate access to the database(s) you want to
-@index(access_db)
-use. You specify the name of the database and the host in the network
-which stores it, and GDB sets up the appropriate connection to the
-database server.
-
-Use @f[perform_db_operation] and @f[db_query] to manipulate or query
-@index(db_query)
-@index(perform_db_operation)
-the databases.
-
-Use @f[terminate_db] to sever the connection to the database server.
-@index(terminate_db)
-It is also acceptable for your program to exit without terminating its
-databases, as
-the server will notice that the connection has been severed.
-@end(itemize)
-
-Because some programs require access to several databases at once, GDB
-uses DATABASE@index[DATABASE] variables to keep track of the various
-databases which are being used:
-
-@begin(example)
-int
-main()
-{
- DATABASE personnel, inventory;
-
- personnel = access_db("people_data@@host1");
- inventory = access_db("stuff_data@@host2")
- /*
- * Both databases are now available. Either one
- * may be queried or manipulated, or both may be used
- * in parallel.
- */
-}
-@end(example)
-
-Once an attempt is made to access a database, the status of the
-corresponding database connection may be checked by using the
-DB_STATUS@index[DB_STATUS] macro. The values of DB_STATUS are shown in
-table
-@ref(DB_STATUS).
-
-@begin(table)
-@caption(Database Status Values)
-@tag(DB_STATUS)
-@tabset(.75in, 2.5in)
-
-@\DB_OPEN@index[DB_OPEN]@\The connection to the database
-@\@\appears to be intact. Operations
-@\@\on the database may be attempted.
-
-@\DB_CLOSED@index[DB_CLOSED]@\The connection to the database
-@\@\has been lost. @f[terminate_db] is the
-@index(terminate_db)
-@\@\only operation which will be accepted.
-@end(table)
-
-There is a distinction between a database which is @f[DB_CLOSED] and
-@index(DB_CLOSED) one which has been explicitly terminated.
-@f[terminate_db] allows GDB @index(terminate_db) to clean up the data
-structures which had been used to control access to the database. It
-sets the supplied database variable to NULL. @f[DB_STATUS] should not
-be used on a database which has been @index(DB_STATUS) explicitly
-terminated; it is intended for checking the status of a database which
-is believed to be accessible. @section(Performing Operations on
-Databases) With the exception of queries, all Ingres QUEL@index[QUEL]
-operations may be performed using the GDB @f[perform_db_operation]
-function. @index(perform_db_operation) Program #@ref(ProgramNumber)
-accesses the database named @f[personnel_data] at site @f[host1],
-creates an empty table named @f[new_recruits], and puts three entries
-in the new table.
-
-@begin(float)
-
-@center(PROGRAM #@ref(ProgramNumber))
-@set(ProgramNumber=+1)
-@begin(example)
-int
-main()
-{
- DATABASE personnel;
- char *quel_command;
- int retcode;
- /*
- * Access the database and check for errors
- */
- access_db("personnel@@host1", &personnel);
- if (DB_STATUS(personnel) != DB_OPEN) {
- fprintf(stderr,"Could not access database\n");
- exit(4);
- }
- /*
- * Create the new table by sending the appropriate QUEL
- * command.
- */
- quel_command=
- "create recruits (name=c8, rank = i4, serial=i4)");
- retcode = perform_db_operation(personnel,quel_command);
- if (retcode != OP_SUCCESS) {
- fprintf(stderr,"Could not create table.\n");
- exit(4);
- }
- /*
- * Add three rows to the table
- */
- quel_command=
-"append to recruits (name=\"Mary\", rank = 123, serial=876543)";
- retcode = perform_db_operation(personnel,quel_command);
- if (retcode != OP_SUCCESS) {
- fprintf(stderr,"Could not append\n");
- exit(8);
- }
- quel_command=
-"append to recruits (name=\"John\", rank = 121, serial=875432)";
- retcode = perform_db_operation(personnel,quel_command);
- if (retcode != OP_SUCCESS) {
- fprintf(stderr,"Could not append\n");
- exit(8);
- }
- quel_command=
-"append to recruits (name=\"Noah\", rank = 1, serial=123)";
- retcode = perform_db_operation(personnel,quel_command);
- if (retcode != OP_SUCCESS) {
- fprintf(stderr,"Could not append\n");
- exit(8);
- }
- /*
- * Sever the connection to the database. This is
- * optional.
- */
- terminate_db(&personnel);
-}
-@end(example)
-@end(float)
-
-It is a complete example, with all necessary error recovery. The
-return code provided by @f[perform_db_operation] is the Ingres error
-@index(perform_db_operation)
-@index(Ingres error)
-@index(Error, Ingres)
-number for the operation attempted, or else the special reserved value
-@f[OP_CANCELLED]. The latter indicates that GDB lost its connection
-@index(OP_CANCELLED)
-to the server either just before or just after the operation was
-attempted. There is no way to tell whether the operation took effect
-before the connection was lost. Subsequent operations on the database
-are unlikely to work unless another @f[access_db] is done.
-@index(access_db)
-
-Note that @i[any] QUEL@index[QUEL commands] command, with the
-exception of a retrieve, may be done using @f[perform_db_operation].
-All clients are considered to @index(perform_db_operation) have the
-same access and update rights as the userid under which the @f[dbserv]
-program is running. @index(dbserv)@index(Internet, access
-rights from)@Index(Access rights)
-
-Hint: the @f[sprintf] function is a very useful means of putting
-@index(sprintf)
-variable information into command strings. Just use @f[sprintf] to
-format the QUEL command in a C character array, then pass that to
-@f[perform_db_operation] for execution. @f[sprintf] is found in
-the standard Unix C library. Try @f[man sprintf] if you don't know
-how to use it.
-@section(Performing Database Queries)
-
-@index[Retrieval from relational database]
-@index[Querying relational databases]
-Retrieves are different from most other QUEL commands because they
-return query result data in addition to the usual return code. For this
-reason, GDB provides a special @f[db_query] function which is used to
-@index(db_query)
-do information retrieval from an Ingres database into a GDB relation.
-Techniques for building an empty relation and for getting at the
-fields of a result were discussed in Chapter @ref(structure). All
-that remains is to explain how the query itself is formulated and sent
-to the server.
-
-@begin(multiple) The format of a GDB retrieve request is exactly the
-same as the corresponding QUEL@index[QUEL] retrieve @i[except that the
-target of each retrieve is specified differently]. In QUEL, the
-command:
-
-@begin(example)
-retrieve (name=recruits.name, rank=recruits.rank,
- serial=recruits.serial)
- where recruits.rank >10
-@end(example)
-
-would result in a table with three columns named @f[name, rank] and
-@f[serial]. If we assume that a null GDB relation @f[result] has been created
-with fields named @f[name, rank] and
-@f[serial], then the following call to @f[db_query] would accomplish a
-similar query:
-
-@begin(example)
-db_query(personnel, result,
- "(>*name*<=recruits.name, >*rank*<=recruits.rank,
- >*serial*<=recruits.serial)
- where recruits.rank >10");
-@end(example)
-
-Here are the general rules for converting a QUEL query into a GDB
-query:
-@end(multiple)
-
-@begin(itemize)
-Create a GDB relation with fields suitable for holding the result.
-The types of the fields must match the data to be retrieved.
-Extra fields are OK, they will be set to null values in the retrieved
-tuples. You may find the extra fields useful for various purposes in
-your program.
-
-Prepare a C character string which specifying the query to be
-performed. The string has exactly the same form as the
-arguments to a QUEL retrieve, except that @i[each target will be the
-name of a field in the relation, bracketed by >*...*<]. All the usual
-syntactic rules governing QUEL retrieves apply. In particular,
-remember the parenthesis which are required around the target list.
-The verb @i[retrieve] should @i[not] be specified at the beginning of
-the string.
-
-Use @f[db_query] to perform the query.
-@index(db_query)
-@end(itemize)
-
-GDB sends the query request to the remote host and executes the query
-there. Return codes are handled in the same manner as for
-@index(Return codes, Ingres)
-@index(Ingres error)
-@index(Error, Ingres)
-@f[perform_db_operation]. In the case where no errors are found,
-tuples resulting from the query are appended to the supplied relation.
-If the server is running on a different type of machine than your
-client program, GDB does any necessary data conversions to local
-representation for you.
-It is quite possible to get a return code of OP_SUCCESS with no tuples
-added to the relation. This indicates that Ingres had no trouble
-executing the query, but found no data matching your retrieval
-criteria.
-
-You may use @f[perform_db_operation] to establish range names which
-@index(perform_db_operation)
-may then be used in your query. Range names are private to each user
-of the database, and to each connection to the database.
-
-Program #@ref(ProgramNumber) is a copy of the sample program from
-Chapter @ref(structure), fleshed out to show the actual retrieval.
-For brevity, no error checking is done in this example. In practice,
-the return code from each GDB function should be checked.
-
-@begin(float)
-
-@center(PROGRAM #@ref(ProgramNumber))
-@set(ProgramNumber=+1)
-@begin(example)
-int
-main()
-{
- DATABASE personnel;
-
- char *fld_names[] = {"name", "rank", "serial"};
- FIELD_TYPE fld_types[] = {STRING_T, INTEGER_T, INTEGER_T};
-#define NAME 0
-#define RANK 1
-#define SERIAL 2
-
- TUPLE_DESCRIPTOR desc;
- RELATION new_recruits;
- TUPLE t;
- STRING *name;
- int *rank;
- int *serial;
-
- /*
- * Access the database
- */
- access_db("personnel@@host1", &personnel);
-
- /*
- * Create a null relation to hold the results
- */
- desc = create_tuple_descriptor(3, fld_names, fld_types);
- new_recruits = create_relation(desc);
-
- /*
- * Do the query
- */
- db_query(personnel, new_recruits,
- "(>*name*<=recruits.name, >*rank*<=recruits.rank,
- >*serial*<=recruits.serial)
- where recruits.rank >10");
- /*
- * print the results
- */
- for(t=FIRST_TUPLE_IN_RELATION(new_recruits);
- t != NULL;
- t = NEXT_TUPLE_IN_RELATION(new_recruits, t)) {
- /*
- * Get a pointer to each field in the tuple
- */
- name = (STRING *)FIELD_FROM_TUPLE(t, NAME);
- rank = (int *)FIELD_FROM_TUPLE(t, RANK);
- serial = (int *)FIELD_FROM_TUPLE(t, SERIAL);
- /*
- * print the fields
- */
- printf("name=%s rank=%d serial=%d\n",
- STRING_DATA(*name), *rank, *serial);
- }
-}
-@end(example)
-@end(float)
-
-
-@Chapter(Creating Servers, Clients, and Other Communicating Programs)
-@Index(Communicating programs, writing)
-
-One of the main purposes of GDB is to facilitate the programming of
-servers, clients, and other communicating programs. Many useful
-programs can be created in just a few lines of code, but GDB also
-allows you to write very complex asynchronous servers which support
-many clients from a single Unix process. This chapter outlines the various
-techniques which may be used for writing communicating programs in
-GDB.
-
-@section(Connections)
-
-All communication in GDB is done using connections. The state of
-each connection is kept in a variable of type @f[CONNECTION], or more
-@index(CONNECTION)
-specifically, in a structure to which the connection variable points.
-GDB has several functions for initiating and terminating connections.
-They are implemented using Berkeley Unix socket facilities, but
-are intended to be much easier to use. There are several different
-ways to start connections, depending mainly on whether you are writing
-a server, a client, or sets of peers. These are described in sections
-below. Once successfully started, all
-connections are full duplex, reliable, ordered data paths on which GDB
-objects may be sent and received.
-
-Once it is started, each connection has a status which may be
-determined using the @f[connection_status] macro. Values of concern
-@index(connection_status)
-to users are shown in table @ref(ConnectionStatus).
-
-@begin(table)
-@caption(Connection Status Values)
-@tag(ConnectionStatus)
-@tabset(.75in, 2.5in)
-
-@\CON_UP@index[CON_UP]@\Connection is operational.
-@\@\Data transmission may be attempted.
-
-@\CON_STOPPING@index[CON_STOPPING]@\Connection has failed.
-
-@\@\User may check the @f[connection_errno]
-@\@\@index[connection_errno] for cause, or may call
-@\@\@f[connection_perror]@index[connection_perror] to print
-@\@\message. In any case, user must issue a @f[sever_connection]@index[sever_connection]
-@\@\in order for the connection to be re-used by gdb.
-
-@\CON_STOPPED@index[CON_STOPPED]@\Connection has been
-@\@\severed by the user.
-@end(table)
-
-Note that the routines which start connections may return NULL values
-in case of failure or they may return a connection descriptor in the
-CON_STOPPING state.
-When a connection enters the CON_STOPPING state, the only operations which
-users may perform are to check the @f[connection_status] and the @f[connection_errno], or to sever the connection.
-
-If any connection, including a listening connection, fails due to an
-error reported by Unix on a system call, GDB records the corresponding
-Unix @f[errno]@index[errno] in the connection descriptor, and puts the
-connection in the CON_STOPPING state. All pending operations on the
-connection are cancelled. This errno value may be queried with the
-@f[connection_errno] macro.
-
-Users may terminate connections at any time from
-either end by using the @f[sever_connection] function. All operations
-queued at either @index(sever_connection) end are cancelled and the
-communication path is closed. Connection @i[variables] which have
-been severed may be re-used to start other connections. A connection
-which has entered the @f[CON_STOPPING] @index(CON_STOPPING) state
-should be severed before its connection variable is re-used.
-
-
-@section(Sending and Receiving Data)
-
-GDB provides a @f[send_object] function which may be used to send any
-@index(send_object)
-GDB object on a connection, and a matching @f[receive_object]
-@index(receive_object)
-function. Objects are sent in order on each connection. Calls to
-@f[receive_object] should match one for one with calls to
-@f[send_object], and the types specified on the receives must
-match those on the sends. @i[Failure to do this will result in
-unpredictable program failures including segmentation faults, hangs,
-prematurely severed connections, etc.]
-
-Chapter @ref(structure) describes GDB's typing scheme.
-@f[send_object] is a @i[polymorphic] routine which can transmit any
-type of GDB object. Each object is automatically converted to the
-local representation of the receiving machine. Program
-#@ref(ProgramNumber) is a simple example which starts a connection to
-a server, sends a single integer to the server, and gets back a
-relation.
-
-@begin(float)
-
-@center(PROGRAM #@ref(ProgramNumber))
-@set(ProgramNumber=+1)
-@begin(example)
-int
-main{
- CONNECTION server;
- int i = ???somevalue???;
- RELATION result;
- int rc;
-
- server = start_server_connection("serverid", "");
-
- if (server == NULL) {
- fprintf(stderr, "Failed to connect to server\n")
- exit(4);
- }
-
- rc = send_object(server, &i, INTEGER_T);
- if (rc == OP_CANCELLED) {
- fprintf(stderr, "Could not send to server.\n")
- exit(4);
- }
-
- rc = receive_object(server, &result, RELATION_T);
- if (rc == OP_CANCELLED) {
- fprintf(stderr, "Could not receive from server.\n")
- exit(4);
- }
-
- sever_connection(server);
-
-}
-@end(example)
-@end(float)
-
-This program is complete, if not particularly useful,
-and it contains all testing needed to detect
-communication related errors. Many very useful applications employ GDB
-in just this simple manner. GDB takes care of locating the server
-(naming of servers is discussed below), managing the connection,
-converting data for transmission, and detecting errors.
-
-As discussed in chapter @ref(async), many GDB operations have
-@index[Asynchronous operations]
-asynchronous equivalents. @f[start_sending_object] and
-@index(start_sending_object)
-@f[start_receiving_object] may be used to asynchronously send and
-@index(start_receiving_object)
-receive data on GDB connections. GDB maintains a separate operation
-queue for the inbound and the outbound activity on each connection.
-If you invoke @f[start_sending_object] several times in rapid
-succession on the same connection, the requests queue up in
-order. The same is true for @f[start_receiving_object], but sends
-and receives are queued independently. In certain cases, it may
-be more efficient to queue several asynchronous transmissions at once
-than to do just one at a time. You must not change any
-variables being sent or received until the operations have completed,
-otherwise unpredictable results may occur. If an ordinary @f[send_object] or
-@f[receive_object] is invoked, then the corresponding queue is flushed
-before the synchronous operation is attempted.
-
-@section(Naming Communicating Programs)
-@Index(Naming communicating programs)
-
-A GDB program attempting to connect to a correspondent somewhere else
-in the network must have some means of addressing its connection
-request. Network servers awaiting connections from clients must have
-some means of establishing their own identity to the network. The
-appropriate form for such addresses may change over time and according
-to the organization in which GDB is being used. To isolate these
-concerns in the cleanest possible manner, all GDB routines use an
-ordinary C character string to encode any desired network or service
-address. Though the form of the string may change when GDB is
-re-implemented in new environments, the number and nature of
-parameters to GDB's functions will not.
-
-At present, GDB uses the naming facilities of Berkeley 4.2 or 4.3
-Unix. Berkeley Unix uses a file called @f[/etc/services] to define
-@index(/etc/services)
-the names of network services and to map them to internal addresses
-known as port numbers. Setting up @f[/etc/services] is a job for your
-Unix system administrator, but you must ensure that each GDB service name
-you intend to use has been defined in @f[/etc/services] at all
-communicating sites to be a @i[tcp port]. Your system administrator
-should know what that means. Once this has been done, the service
-name is available for use by GDB. For example, a program intended to
-act as a server for clients playing chess with each other might be
-created with the GDB function:
-
-@begin(example)
- client = create_forking_server("chess", NULL)
-@end(example)
-
-where @f[chess] is a service name defined in @f[/etc/services].
-@index(/etc/services)
-(Other considerations in using @f[create_forking_server] are discussed
-@index(create_forking_server)
-below. Here we are interested only in the service name @f[chess].)
-
-A client desiring a connection to this server would have to specify
-the host at which the server was running as well as its service name.
-The client might create the connection with:
-
-@begin(example)
- server = start_server_connection("hostname:chess", NULL)
-@end(example)
-
-@Index(start_server_connection)
-where @f[hostname] is the name of the host where the server is
-running. GDB takes care of finding the host, looking up the service
-name @f[chess] in @f[/etc/services], and making the appropriate
-connection.
-
-Sometimes its convenient to bypass @f[/etc/services] and specify a
-port number directly. GDB assumes that any service name beginning
-with @f[#] is a port number. For example:
-@Index[# (Port number designator)]
-@Index(Port numbers)
-
-@begin(example)
- client = create_forking_server("#9502", NULL)
-@end(example)
-
-creates a server at port 9502. No checking for conflicts with
-existing services or entries in @f[/etc/services] is done. In
-general, you should be sure that you and your system administrator
-agree on any service names or port numbers you are going to use before
-you try it. Otherwise, you may interfere with other network services
-at your site.
-
-@section(Connecting to A Server)
-@Index(Connecting to a server)
-
-The code fragment:
-
-@begin(example)
- CONNECTION server;
-
- server = start_server_connection("hostname:chess", NULL)
- if (server == NULL)
- fprintf(stderr, "Could not start connection.\n");
-@end(example)
-@Index(start_server_connection)
-
-may be used to start a connection to a service named @f[chess] on the
-specified host. If the returned connection variable is not NULL, then
-a GDB connection has been created which may be used for sending and
-receiving GDB objects. The @f[sever_connection] function may be used
-@index(sever_connection)
-to break the connection at any time, or the client program may simply
-exit. A properly written server will notice that the connection has
-failed and do the appropriate cleanup.
-The second parameter to @f[start_server_connection] is an optional
-parameter string which is made available to the server. Rules for
-using this string are established by each server.
-There is no asynchronous form of start_server_connection at the
-present time. GDB delays until the request either succeeds or
-encounters an unrecoverable error.
-
-@section(Writing Servers)
-
-There are several kinds of GDB server, each of which is discussed in a
-separate section below.
-
-@subsection(Forking Servers - the simplest kind)
-
-@index[Forking servers]
-The simplest kind of GDB server, which most programmers will use, is
-called the @i[forking] server.
-This is the traditional kind of Unix server in which a new process is
-forked to deal with each client. GDB does the forking for you, and
-takes care of most of the Unix bookkeeping required in a forking
-program.
-Creation of forking servers is best illustrated by the simple example
-in Program #@ref(ProgramNumber).
-
-@modify(example,Size 9)
-@begin(float)
-
-@center(PROGRAM #@ref(ProgramNumber))
-@set(ProgramNumber=+1)
-@begin(example)
-@label(tfsr)
-{
- CONNECTION client; /* talk on this to client */
- int data; /* receive data here */
-
- gdb_init(); /* set up gdb */
-
- client = create_forking_server("echoserver",NULL);
- fprintf(stderr,"forked\n");
-
- while (TRUE) {
- if (receive_object(client, &data, INTEGER_T) ==
- OP_CANCELLED) {
- fprintf(stderr,"Client has terminated\n");
- exit(4);
- }
- if (data >= 'a' && data <= 'z')
- data += 'A'-'a'; /* upcase the response */
- if (send_object(client, &data, INTEGER_T) ==
- OP_CANCELLED) {
- fprintf(stderr,"send error\n");
- exit(4);
- }
- }
-}
-@end(example)
-@end(float)
-
-This is a fully functional server capable of supporting an arbitrary
-number of clients. It does the trivial work of accepting a stream of
-characters (stored in integers) converting them to uppercase, and
-sending the result back to the client. The @f[create_forking_server]
-@index(create_forking_server)
-function behaves somewhat like the Unix @f[fork] system call. Each
-@index(fork)
-time a new client connects to the server, a fork is done and the
-variable @f[client] in the child process is set to the new client
-@index(client)
-connection. In other words, the code below @f[create_forking_server]
-is automatically invoked over and over again in a new process each
-time a connection is received from a client. When the processing for
-each client is done, an @f[exit] or @f[return] is all that is needed
-@index(return)
-@index(exit)
-to cause the appropriate cleanup in the server. Note that this sample
-server detects termination of its clients by checking for errors when
-receiving and sending. This technique is convenient and reliable,
-since the connection is always severed as soon as the client
-terminates.
-
-@subsection(Single Process Servers)
-
-@index[Single process servers]
-Though forking servers are particularly easy to write, they have the
-disadvantage of breaking the server into a separate process for each
-client. In particular, forking servers are unsuited to applications
-in which data is to be shared among the various clients. A server
-which implemented a bridge game, for example, might relay the bids
-played by one client to all of the three others. This kind of program
-is very difficult to implement in a forking server, so GDB also allows
-you to write servers in which all clients are handled by a single Unix
-process. Because so much asynchronous activity has to be coordinated
-within a single process, these servers are generally much harder to
-design and debug. Nevertheless, they can be the basis of some of the
-most exciting, high performance applications which can be written with
-GDB.
-
-Chapter @ref(async) introduces the techniques
-used to do asynchronous communication with GDB.
-Read it now, if you have not already done so, before proceeding
-with this section.
-Here we show how a
-single server process may @i[asynchronously] acquire new connections
-from its clients. In general, a single process server works in the
-following manner:
-
-@begin(itemize)
-@f[create_listening_connection] is used to create a special connection
-@index(create_listening_connection)
-on which connection requests from clients arrive.
-
-The asynchronous operation @f[start_accepting_client] is
-@index(start_accepting_client)
-queued on the listening connection. This operation completes whenever
-a new client requests a connection. The server immediately issues a
-new @f[start_accepting_client] in case another client is
-trying to connect.
-
-The server examines the parameters supplied by the client, and if it
-decides to accept the client's connection, transmits its decision
-by using the asynchronous operation @f[start_replying_to_client].
-@index(start_replying_to_client)
-When this becomes OP_COMPLETE, the new client is ready for use.
-
-@f[start_sending_object] and @f[start_receiving_object] are used in
-@index(start_receiving_object)
-@index(start_sending_object)
-the usual manner to send data to and from the clients.
-@end(itemize)
-
-The typical server must be capable of doing all of these operations
-simultaneously and in any order. It maintains a LIST_OF_OPERATIONS
-which includes all of the operations on which completion is awaited,
-and it drops into an @f[op_select_any] to wait for something to
-@index(op_select_any)
-happen. Based on the operation which has completed, it determines the
-next step to perform. If a client has sent data, it may queue a
-response or prepare data for transmission to other clients. If a new
-connection request is received it must queue the appropriate response,
-and so on.
-
-There are many ways to organize such a program. A sample, which
-performs the same character echoing function as the forking server on
-page @PageRef(tfsr), is included in Appendix @ref(nonforkingserver).
-Studying this sample is the best way to learn about creating
-non-forking servers. The GDB library routines are documented in the
-Library Reference Manual.
-
-@section(Communicating Peers)
-
-@index[Peer to peer communication]
-Sometimes it is useful to write communicating @i[peer] programs which
-do not fit the client/server model.
-When two peers communicate, neither may be presumed to start before
-the other. GDB provides a
-@f[start_peer_connection]@index[start_peer_connection] function which
-starts a connection to a peer elsewhere in the network.
-If necessary, @f[start_peer_connection] delays indefinitely until its
-peer is started and issues a matching @f[start_peer_connection]. Once
-started, peer connections are used just like any others.
-@f[start_peer_connection] is documented in the Library Reference Manual.
-
-@Chapter(Synchronous and Asynchronous Operations)
-@label(async)
-
-
-Most of the GDB programs discussed so far have been completely
-@i[synchronous], meaning that they only try to do one thing
-at a time. The @f[perform_db_operation] and @f[db_query] routines
-@index(db_query)
-@index(perform_db_operation)
-retain control until the requested operations have run to completion,
-which makes it impossible for a program to do several queries in
-parallel, or to continue with its own execution while a database is
-being updated. GDB also provides @i[asynchronous] versions of many of
-@index(Asynchronous operations)
-its services, allowing execution of the client to proceed while the
-requested operation is attempted. Asynchronous programming is usually
-more difficult than synchronous, but it is much more flexible. This
-chapter describes GDB's facilities for managing asynchronous
-activities.
-
-@section(Operations)
-@index(Asynchronous operations)
-@index(OPERATION)
-
-An asynchronous program may have several operations running in
-parallel while its own local execution continues. In order to track
-the progress of these activities, the program uses an OPERATION
-@index[OPERATION]
-variable for each one, as illustrated in Program
-#@ref(ProgramNumber). This program fragment presumes that DATABASEs
-@f[personnel] and @f[inventory] have been successfully accessed.
-Several important points are illustrated by the example. Each
-operation variable must be initialized with the @f[create_operation]
-@index(create_operation)
-function. Like most other data structures in GDB, OPERATIONS are
-really pointer types; @f[create_operation] allocates and initializes
-the data structure which GDB actually uses to track the operation.
-
-@begin(float)
-
-@center(PROGRAM #@ref(ProgramNumber))
-@set(ProgramNumber=+1)
-@begin(example)
- DATABASE personnel, inventory;
- char *quel_command;
- RELATION new_recruits;
-
- OPERATION query_op, append_op;
-
- /*
- * Each operation must be created before it
- * can be used
- */
- query_op = create_operation();
- append_op = create_operation();
-
- /*
- * Start an append to the parts database in parallel
- * with a query to the personnel database. We assume that
- * both DB's are accessed and the relation was created.
- */
- quel_command=
- "append to parts (type=\"resistor\", price = .12, quantity=150)";
- retcode = start_performing_db_operation(append_op,
- personnel,
- quel_command);
- start_db_query(query_op, personnel, new_recruits,
- "(>*name*<=recruits.name, >*rank*<=recruits.rank,
- >*serial*<=recruits.serial)
- where recruits.rank >10");
-
- /*
- * Wait for both to complete. Order of the following
- * two statements does NOT matter!
- */
- complete_operation(append_op);
- complete_operation(query_op);
-
- /*
- * Check for errors
- */
- if(OP_RESULT(append_op) != OP_SUCCESS ||
- OP_RESULT(query_op) != OP_SUCCESS)
- fprintf(stderr,"Something didn't work\n");
-@end(example)
-@end(float)
-
-There are many ways to check for progress or await completion of GDB
-operations.
-One of the simplest is @f[complete_operation], which is
-@index(complete_operation)
-shown in this example. The program hangs in GDB until the specified
-operation completes. As noted in the program comments, the order of
-the two calls to @f[complete_operation] is @i[not] significant. This
-might be surprising, as the calls seem to imply that @f[append_op]
-must complete before @f[query_op] will proceed. That is not the case.
-@i[Whenever GDB is given control for any reason, it always makes
-progress as quickly as possible on all pending operations.] This
-means that @f[query_op] may progress or even complete while
-awaiting completion of
-@f[append_op]. Should @f[query_op] complete before
-@f[append_op], the second call to @f[complete_operation] will not delay
-at all.
-
-Each operation has two types of status, which may be queried by
-@f[OP_STATUS] and @f[OP_RESULT] respectively. OP_STATUS is used to
-@index(OP_RESULT)
-@index(OP_STATUS)
-track the progress of an operation while it is executing. It has the
-values shown in table @ref(OP_STATUS).
-
-@begin(table)
-@caption(OP_STATUS Values)
-@tag(OP_STATUS)
-@tabset(.75in, 2.5in)
-
-@\OP_NOT_STARTED@\The operation
-@\@\variable is not in use.
-
-@\OP_COMPLETE@\The operation completed
-@\@\without any transmission
-@\@\related errors.
-
-@\OP_CANCELLED@\GDB was forced to
-@\@\give up on the operation
-@\@\prematurely. The likeliest
-@\@\explanation is failure of
-@\@\the connection.
-
-@\other@\OP_STATUS takes on
-@\@\several other values while
-@\@\an operation is queued or
-@\@\executing. These are not
-@\@\of concern to users of
-@\@\the library.
-@end(table)
-
-
-The operation should be considered running until its status becomes
-either
-@f[OP_COMPLETE] or @f[OP_CANCELLED]. The macro @f[OP_DONE] may be
-@index(OP_DONE)
-@index(OP_CANCELLED)
-@index(OP_COMPLETE)
-@index[Return codes from GDB operations]
-used to test for both of these. When an operation reaches
-@f[OP_COMPLETE] status, then the @f[OP_RESULT] macro may be used to
-get the return code from the operation itself. In the case of an
-Ingres query, for example, the operation will be considered complete
-when all results have been returned to the client. @f[OP_STATUS] will be
-@index(OP_STATUS)
-@f[OP_COMPLETE] and @f[OP_RESULT] will contain the return code from Ingres.
-If the connection fails before the query is successfully attempted,
-@f[OP_STATUS] will be @f[OP_CANCELLED] and @f[OP_RESULT] will be
-undefined. The result value @f[OP_SUCCESS] is usually used to
-indicate that an operation completed successfully.
-
-@f[OPERATIONS] are created in the state @f[OP_NOT_STARTED]. After
-@index(OP_NOT_STARTED)
-use, the state is generally @f[OP_CANCELLED] or @f[OP_COMPLETE]. It is
-possible to re-use such an @f[OPERATION], which saves the overhead of
-deleting and re-creating it. You must use @f[reset_operation] to
-prepare an operation for re-use, returning it to the state
-@f[OP_NOT_STARTED].
-
-Hint: it is often handy to create arrays of operations, or to include
-@Index(Arrays of operations)
-operations in other C structures. Make sure to do a
-@f[create_operation] on each one before it is used.
-@f[delete_operation] may be used to reclaim the space for operations
-@index(delete_operation)
-which have terminated and are no longer needed.
-
-@section(op_select_any and op_select_all)
-
-In certain programs you may wish to take explicit action when any of a
-list of pending operations completes, or you may wish to monitor
-activities on file descriptors not controlled by GDB. Berkeley Unix
-uses @f[select] to control asynchronous file handling, and GDB
-@index(select)
-provides two generalized forms which facilitate control of GDB's
-operations.
-
-@f[op_select_any] has semantics similar to the select system
-@index(op_select_any)
-call, but in addition, it allows pending GDB operations to
-progress. Along with a list of file descriptors, you must give
-@f[op_select_any] a list of the GDB operations whose completion is of
-interest. Like @f[select], this routine waits quietly when all
-descriptors are blocked. Unlike @f[select], @f[op_select_any] allows
-processing to proceed on all GDB connections, and it returns only when
-one of the operations specified in the list actually completes. While
-@f[select] would return whenever any data could be read or written on
-a connection, @f[op_select_any] returns only when enough progress
-been made that one of the specified operations actually completes. One
-may think of @f[op_select_any] as raising the level of abstraction on
-a connection from single bytes to entire operations. As with
-@f[select], you may supply your own file descriptors and a timeout,
-and GDB will treat these in the same manner as @f[select] would.
-@f[op_select_all] is similar to @f[op_select_any], but it waits for
-@index(op_select_all)
-all of the specified operations to complete before returning.
-
-Both of these functions take as arguments a @f[LIST_OF_OPERATIONS],
-@index(LIST_OF_OPERATIONS)
-which may be created using the @f[create_list_of_operations] function.
-@index(create_list_of_operations)
-Since manipulating lists is clumsy, it is sometimes useful to create
-one long list with all the operations you ever expect to wait for, and
-just pass that to @f[op_select_any]. Once an operation has completed,
-you should use @f[reset_operation] to keep it from pre-satisfying the
-next select call.
-The program in Appendix @ref(nonforkingserver) illustrates all of these
-techniques.
-
-@chapter(Hints and Tricks)
-@blankspace(2in)
-@center(To be supplied.)
-@chapter(Bugs)
-@blankspace(2in)
-@center(To be supplied.)
-
-@chapter(How GDB Works)
-
-The ultimate authority on this is, of course, the GDB code.
-@index[source code organization]
-Most of it is pretty well commented (some would say over-commented,
-but that is my style), which means that you can usually figure out
-what's happening IF you know where to look and have some general idea
-of how things work.
-This chapter presents the general concepts and implementation
-techniques which underlie GDB.
-It is not comprehensive, but I do recommend that you read this before
-looking at the code.
-
-GDB is organized into several interacting sub-components or layers.
-Some are so trivial as to require little or no explanation.
-Others, such as the layers which do asynchronous operations and
-communication,
-are much more complex.
-The sections below describe the components of GDB and their
-relationship to each other.
-A thorough knowledge of Berkeley Unix, C programming, and creation and
-manipulation of sockets is presumed.
-It is also presumed that the reader is familiar with the other
-chapters in this guide.
-
-@section(General Code Layout)
-
-GDB is supplied as a suite of .c source files which are compiled to
-build the libgdb.a library archive. The gdb.h include file is used by
-all of these, and also by user applications.
-@index[dbserv, compiling]
-The @f[dbserv] database server is supplied in a separate @f[dbserv.qc]
-file, which can @i[only] be compiled on a machine which has RTI Ingres
-installed and accessible. See RTI's documentation for details.
-If you are not using dbserv then there is no need to build it. The
-rest of GDB, including database client applications, may be compiled
-and used on machines which do not have RTI's products installed.
-
-The file named gdb.c contains the routine @f[gdb_init], which is
-@index[gdb_init]
-called first by all users of gdb.
-Initialization of GDB data structures can be tracked by reading this
-source.
-This source file also contains a few utility routines.
-Most of the other source is organized more or less by layer, with some
-of the larger layers split into several source files.
-The chapters below give hints, or you can grep for the name
-of the routine in which you are interested.
-It is always safe to grep in g*.c; this will get all of the executable
-GDB source.
-
-gdb.h is organized more or less by gdb layer, with a ^L page break
-@index[gdb.h]
-between each section.
-You should always have the corresponding section of gdb.h handy while
-trying to learn about a piece of GDB. It is essential.
-
-@section(Memory Management)
-
-@index[Memory management]
-GDB makes extensive use of dynamically allocated (heap) memory.
-ALL calls for dynamic memory are done through the two routines
-@index[db_alloc]
-@index[db_free]
-@f[db_alloc] and @f[db_free]. These are actually defines in gdb.h
-which cause invocation of @f[*gdb_amv] and @f[*gdb_fmv] respectively.
-@index[gdb_amv]
-@index[gdb_fmv]
-@index[gdb_am]
-@index[gdb_fm]
-These in turn point, by default, to @f[gdb_am] and @f[gdb_fm], which
-just call malloc and free.
-This is all done so an application can easily replace the default
-memory allocators.
-The addresses of the new routines should be plugged into the vectors
-prior to calling @f[gdb_init]. Note that the interfaces are @i[not]
-quite the same as to malloc and free; check the source for details.
-
-Many dynamically allocated GDB objects contain 4 character @f[id]
-fields. These are just eye catchers for debugging, initialized to
-"REL" for relations, "TUP" for tuples, and so on.
-Most parameters passed by users are
-checked for these fields, which helps catch bad parameters and some
-memory management errors.
-
-@section(GDB Type Management)
-@index[GDB types]
-@index[Types]
-
-Make sure you understand GDB types from a users point of view (Chapter
-@ref[structure]) before continuing here.
-
-The primary purpose of GDB types is to support the polymorphic
-@index[Polymorphism]
-operations @f[start_sending_object] and @f[start_receiving_object].
-These two operations suffice to send and receive @i[any] GDB object,
-regardless of its structure or complexity.
-@index[Type definition]
-@index[GDB Type definition]
-Given these limited goals, GDB needs only some very basic information
-about its types:
-
-@begin(itemize)
-The length of an item as represented locally (may depend on local
-machine type).
-
-Required alignment (e.g. 4 or fullword, 8 for double)
-
-A function to return a null value for the type.
-This is used to initialize newly allocated variables.
-
-A function to encode values of this type for transmission.
-
-A function to decode data of this type from its transmitted form to the
-local representation (which is usually machine dependent.)
-
-A function to return the length which a value would take when encoded
-for transmission.
-GDB calls these functions to decide how much memory to allocate for
-the transmission buffer before calling the encode routine to fill in
-the buffer.
-
-A format function which is used to format variables of this type for
-debugging output.
-
-A string name for the type. Used only for debugging output.
-@end(itemize)
-
-@index[Properties]
-@index[FCN_PROPERTY]
-@index[INT_PROPERTY]
-@index[STR_PROPERTY]
-All this information is coded into the array named g_type_table.
-The macros INT_PROPERTY, FCN_PROPERTY, and STR_PROPERTY are used by
-the GDB code to index into the type table and pull out any desired
-property for the given type.
-For example, INT_PROPERTY(REAL_T, LENGTH_PROPERTY) returns the length
-of the local representation of a real number.
-
-@begin(example)
- FCN_PROPERTY(REAL_T, ENCODE_PROPERTY) (....)
-@end(example)
-
-calls the encode routine on a real number, with the supplied (....)
-arguments. Though the macro expansions look messy, they are designed
-to be optimizable by a good compiler.
-
-The routine @f[gdb_i_stype] is used to initialize the type table,
-@index[gdb_i_stype]
-thereby defining types. The typing system is extensible in that new
-types may be defined by adding entries to the type table at any time.
-The most difficult aspect of this is writing the encode and decode
-functions for the new type. Note that the routines for encoding and
-decoding structured types, like tuples and relations, generally make
-repeated calls to the encoding routines for components. Thus, the
-relation encoding routine calls the tuple encoding routine, which
-calls the routines for the various fields. The end result is a single
-buffer containing the entire structured datum flattened for
-transmission.
-
-@subsection (The TUPLE_T and RELATION_T Structured Types)
-
-@index[TUPLE_T]
-@index[RELATION_T]
-The @f[TUPLE_T] and @f[RELATION_T] types are supplied with GDB; they
-are commonly used to contain the results of a relational database
-query, but they are also useful for transmitting other types of
-structured data. For brevity, we will refer simply to tuples and
-relations.
-
-Both tuples and relations are self-describing.
-Given a tuple or relation, one can determine its complete structure as
-well as its contents.
-A tuple is an ordered, non-extensible collection of named, typed,
-fields, and a relation is an ordered, extensible collection of tuples.
-Because one program, and certainly one relation, will commonly use
-many tuples with the same structure (i.e. the same field names and
-types), the description information for these tuples may be shared in
-a single @f[TUPLE_DESCRIPTOR].
-@index[Tuple descriptors]
-The tuple descriptor is allocated and initialized by
-@f[create_tuple_descriptor] with the names and types of a set of
-fields.
-Internally, the descriptor is also initialized to contain the offset
-and length of the fields within a tuple.
-Accessing a field within a tuple is thus extremely fast, as the
-offsets are pre-computed.
-Note that the offsets and lengths of the fields are local properties
-which may vary from one machine type to another. Also, this structure
-supports retrieval of a field whose index or name (and thus its type)
-is variable at execution time.
-
-A single tuple descriptor may be shared by an arbitrary number of
-tuples, some of which may be members of relations.
-Also, GDB may implicitly allocate tuple descriptors for tuples and
-relations received through the network.
-To facilitate management of these descriptors GDB uses reference
-counting. Each time a descriptor is used in a tuple or
-relation, its reference count is incremented. Each time such a tuple
-or relation is deleted, the reference count is decremented.
-When the reference count goes to 0, the descriptor itself is
-reclaimed.
-@i[As a general rule, users should explicitly de-allocate the
-descriptors, tuples, and relations that they create, and they should
-de-allocate objects that they explicitly receive from the network.
-Implicitly created descriptors will be reclaimed automatically by the
-system.]
-
-@index[TUPLE]
-@index[TUPLE_DESCRIPTOR]
-@index[RELATION]
-The names @F[TUPLE, TUPLE_DESCRIPTOR] and @f[RELATION] supplied in
-gdb.h are actually pointers and may be manipulated as such (e.g.
-copied, passed as arguments to functions, etc.) The create functions
-dynamically allocate memory and fill in the pointers.
-
-A tuple descriptor consists of a fixed header, an array containing
-descriptive information for each field, and then a contiguous list of
-null terminated strings, which are the text names of the fields.
-
-A TUPLE is a pointer to a single contiguous chunk of memory
-containing a header followed by the data for the tuple fields.
-The header includes chain pointers, which are used @i[only] when the
-tuple is contained in a relation, and a pointer to the corresponding
-tuple descriptor. The data follows immediately, at the offsets listed
-in the tuple descriptor.
-Offsets are relative to the start of the first field, not the tuple
-header.
-
-Relations consist of a header structure circularly double linked to a
-(possibly null) list of tuples.
-The last tuple points to the relation header, as does the back pointer
-from the first tuple.
-A null relation has forward and back pointers to itself.
-The queue manipulation macros like ADD_TUPLE_TO_RELATION depend on the
-@index[ADD_TUPLE_TO_RELATION]
-fact that the next and prev fields are at the same offset in both the
-tuple and relation header data structures; this may not actually be
-true on machines which allocate structures backwards, and as much, it
-represents a non-portability.
-
-@section(Communications and Asynchronous Operations)
-
-@index[Communication services]
-@index[Asynchronous operations]
-This layer of GDB provides management for connections between programs
-on separate hosts, and for asynchronous transmission of typed data
-between those hosts. It relies on the memory management and data
-typing layers discussed above. Due primarily to the extensive support
-for asynchronous, non-blocking communication on multiple connections,
-this is by far the most complicated layer of GDB. Rather than trying
-to discuss every detail, I will emphasize the most important general
-concepts, and give warnings about implementation features which may be
-difficult to follow in the code.
-
-@subsection(Connections)
-
-@index[Connections]
-A @f[connection] is an abstraction for an asynchronous, reliable,
-full-duplex path on which GDB can transmit and receive its typed data.
-The current implementation uses Berkeley TCP stream sockets for
-transport.
-
-Unlike most GDB data structures, all connection descriptors are
-@index[gdb_cons]
-statically allocated in the @f[gdb_cons] array. When a new connection is
-started, the first available slot in the array is returned as the
-corresponding connection descriptor. NOTE: the actual state of each
-connection descriptor is contained in its status field. @f[gdb_mcons]
-is a high water mark variable indicating the highest numbered
-connection which has ever been used; it provides an optimization for
-the common case of a process which only uses one or two connections.
-Below the water mark, the status fields must be checked to determine
-whether a connection descriptor is actually in use.
-@index[GDB_MAX_CONNECTIONS]
-GDB_MAX_CONNECTIONS determines the size of the array, and hence limits
-the number of simultaneous connections which may be controlled by a
-single process. The GDB library may be rebuilt with a larger value if
-desired.
-
-Each connection actually consists of two half connections, one for
-each direction. In the current implementation, they use the same
-file descriptor, but this could be changed for other networking
-environments. There is also some stray code which refers to
-out-of-band connections. This was intended to be used for out of band
-signalling, primarily to cancel ongoing operations. It was never
-implemented.
-
-@subsection(Operations and Half Connections)
-
-@index[Operations]
-@index[Half connections]
-Each transmission operation which GDB supports on a half connection is
-fundamentally asynchronous, meaning that the application program can
-continue to execute while the operation proceeds.
-GDB is @i[not] signal driven; it uses non-blocking I/O to make as
-much progress as possible whenever it gets control, leaving
-@index[SIGIO]
-SIGIO available to
-the application
-programmer. GDB does set a handler for SIGPIPE.
-@index[SIGPIPE]
-Though
-synchronous versions are provided for most operations, these usually
-are implemented by invoking the asynchronous version and then
-immediately waiting for it to complete.
-
-Each pending operation is represented by a data structure of type
-OPERATION (actually, OPERATION is the pointer to the data structure)
-@i[which is queued on the corresponding half connection.] It is thus
-a fundamental limitation of the current version of GDB that
-asynchronous activities exist @i[only] in the context of a connection.
-The only reason that such an operation may ever block is due to lack
-of progress in data transmission on the corresponding path. GDB does
-not provide for any more general kind of multiplexing or lightweight
-process management.
-
-The operation data structure contains the complete state of the
-pending operation. This is summarized in a status field, which
-indicates that the operation is either OP_QUEUED (waiting behind
-others in a queue), OP_RUNNING (at the head of the queue and
-proceeding as fast a data transmission will allow), OP_COMPLETE
-(completed without GDB detected error) or OP_CANCELLED (abandoned
-before completion, usually due to loss of the corresponding
-connection.) There is also a result field, which becomes valid
-@i[only] when and if the operation reaches the OP_COMPLETE state. It
-is effectively the final return code, with legal values depending on the
-operation being attempted. In addition to these state fields, the
-operation structure contains a pointer to a function which is to be
-invoked the next time progress can be made (i.e. the next time the
-communication path unblocks) and the @f[arg] field, which points to a
-dynamically allocated structure used by the functions to contain their
-own ongoing state.
-
-The operation actually running on each half connection is the one at
-the head of the queue. The only reason such an operation may be
-blocked is because it has tried to send or receive data which the
-@f[gdb_move_data] routine was unable to move immediately. The pending state of
-this partially
-completed
-@index[next_byte]
-@index[remaining]
-transmission is summarized by the @f[next_byte] and @f[remaining]
-fields of the half_connection structure. If an operation seems not to
-be progressing, and the @f[remaining] field is non-zero, it may be
-that the socket is not making progress.
-
-The heart of GDB's transport layer is a routine called
-@index[gdb_progress]
-@f[gdb_progress], which has the effect of making all possible progress
-on all pending operations regardless of the connections involved.
-This routine never blocks.
-Instead, it cycles through the connections, trying repeatedly to make
-progress, and returning when no further progress can be made.
-The actual work is done by
-@index[gdb_hcon_progress]
-@f[gdb_hcon_progress], which is one of the
-most important (and deceptively complicated) in GDB.
-If the operation at the head of the queue has never been run before,
-then its initialization function (@f[*init] from the operation
-structure) is called to start it off.
-If the operation has been run before, then it @i[must] be blocked
-waiting for data to be transmitted. In this case,
-@index[gdb_move_data]
-@f[gdb_move_data]
-is called to move the data.
-If that still results in blockage, then no further progress can be
-made;
-otherwise, the operation's continuation function (@f[*cont] from the operation
-structure) is called to process the data or proceed in some other
-manner.
-
-Most of the subtlety in this area comes from the interplay between
-multiple connections and operations.
-If connection 1 blocks, connection 2 may be able to proceed. By the
-time 2 finishes, 1 may be able to progress after all. For this
-reason, @f[gdb_hcon_progress] is called over and over again until a
-full sweep is made through all active connections without a report of
-any progress at all. Furthermore, it is sometimes the case that an
-ongoing operation will actually create and queue new operations on
-either the same or a different connection, which may result in a
-recursive invocation of @f[gdb_progress]. The
-@index[HCON_BUSY]
-@f[HCON_BUSY] flag is
-used to avoid recursive processing of the same half connection.
-Another subtle flag is
-@index[HCON_PROGRESS]
-@f[HCON_PROGRESS], which is used by
-@f[gdb_move_data] to indicate whether progress was made on a given half
-connection. It looks to me in hindsight like this should have been
-passed as a parameter, perhaps there was a good reason why not. In
-any case, it is effectively a value returned from @f[gdb_move_data].
-A final complication arises because an operation is allowed to
-re-queue itself from one half connection to another. This effectively
-puts it back from OP_RUNNING to OP_QUEUED state, to be caught by a
-subsequent sweep of @f[gdb_hcon_progress].
-
-@subsection(GDB Select Features)
-
-@index[Select operations]
-In a Berkeley Unix system, the customary means of waiting for I/O to
-progress is to hang in the @f[select] system call.
-One of the purposes of GDB is to hide byte level communication from
-applications, which are interested only in the progress of their higher
-level requests to GDB. For this reason, GDB provides the new
-selection operations @f[op_select_any] and @f[op_select_all]. The
-former is also known by the historical name @f[op_select], which will
-be used here for brevity. It is also the more interesting of the
-two, since @f[op_select_all] is built trivially from @f[op_select].
-
-@f[op_select] is given a list of queued and/or completed GDB
-operations, a list of file descriptors not controlled by GDB, and a
-timeout value in the same form as for Berkeley select. @f[op_select]
-returns as soon as (1) one of the operations supplied is completed or
-cancelled (2) a select call indicates that progress can be made on one
-of the other file descriptors or (3) the timeout is satisfied.
-Actually, the current version of the system has a bug which
-effectively resets the time every time any connection makes progress.
-
-@index[con_select]
-The real work here is done in the routine named @f[con_select]. This
-routine contains the @i[only] significant @f[select] call in the
-entire GDB system. There are a few others, but those never block.
-@f[con_select] is similar to a real select except that: (1) it
-implicitly selects all file descriptors controlled by connections, as
-well as those explicitly specified (2) it allows transmission and
-receipt to progress on all connections and (3) it considers a
-connection to be selected if and only if a transmission operation
-which had been pending becomes complete. One may consider that
-@f[con_select] turns the fd's controlled by sockets into operation
-streams rather than byte streams. Note also that this operation
-differs from a traditional select and op_select in that it is not
-robust against waiting for connections with pre-completed activity.
-This could be added, but since it's an internal routine anyway, it
-seems not to be worthwhile. Also, this routine presumes that all
-possible progress has been made before con_select is invoked.
-
-Con_select does not know about the specific list of operations for
-which completion is awaited. Op_select and the other selection
-routines call con_select repeatedly until the termination criteria are
-satisfied.
-
-@index[complete_operation]
-Many simple applications use @f[complete_operation] rather
-than the more complex selection operations to await completion of
-asynchronous activities. This routine merely calls con_select
-repeatedly until the desired operation completes. NOTE: even though
-the user is waiting for activity on a particular operation, all
-connections and all operations progress. This tends to make it easier
-for the programmer to avoid deadlocks, and it means that the order in
-which complete_operation requests are issued is frequently
-unimportant.
-
-@subsection(Connection Error Handling)
-
-@index[Error handling]
-For reasons beyond the control of GDB, connections may fail at any
-time, and GDB must be prepared for this.
-In general, all queued operations on the connection are cancelled.
-Note that the application must still sever the connection, to indicate
-that it is no longer using its pointers to the connection descriptor.
-
-GDB operations are responsible for implementing their own error
-recovery.
-None of the existing operations do anything elaborate, and many of
-them cannot be cancelled at all. It would be very difficult to
-provide on the fly cancellation of complex operations like database
-queries, given the asynchronous, full duplex nature of their
-operation.
-Right now, the only way to prematurely terminate such an operation is
-to sever the corresponding connection and restart it.
-This is always safe, if not convenient.
-
-@section(Server/Client Management)
-
-@index[Server/client management]
-GDB supports two general styles of communicating programs:
-peer-to-peer, and server client.
-Within the framework of the server/client model, GDB supports both
-forking and non-forking servers. The interfaces to the two server
-types are identical, so a given client need not be aware of the style
-of its server.
-
-@subsection(Non-forking servers)
-
-@index[Non-forking servers]
-@index[Servers, non-forking]
-GDB's asynchronous communication primitives support the creation of
-non-forking servers, in which multiple clients are serviced by a
-single Unix server process. The maximum number of clients
-which can be served by a single non-forking server process is
-GDB_MAX_CONNECTIONS-1 (one connection is used for the "listening
-connection", described below.) In addition to
-@f[start_sending_object] and @f[start_receiving_object] discussed
-above, @f[start_accepting_client] provides a service for
-@i[asynchronously] acquiring connections from new clients.
-Start_accepting_client connection is an asynchronous GDB operation,
-which may be queued @i[only] on special connections created with the
-@f[create_listening_connection] primitive.
-Create_listening_connection allocates a connection descriptor, creates
-a listening Berkeley Unix Internet Stream socket, and binds a supplied
-port address to it. Only the inbound half connection of the
-connection descriptor is significant.
-
-The structure of start_accepting_client is subtle, and it
-serves as a good example of some of the complex ways in which GDB
-operations can be created. At the time it is issued @i[two] operations
-are queued on the listening connection. The first one merely does the
-accept of a new connection, but without filling in any of the
-connection data structures. The second one is the one which uses the
-operation descriptor passed by the caller. Its init routine is
-g_iacc, which therefore runs @i[after] the connection has been
-acquired from the kernel. This routine proceeds to fill in the new
-connection descriptor so that communication may be done on the new
-stream using GDB's services. It also calls g_ver_iprotocol to insure
-that both sides are running compatible versions of GDB.
-
-At this point, the new connection exists, but we still have to acquire
-the client's parameters. This too must be done asynchronously. On
-the new connection, a @f[start_receiving_object] is queued, and then
-the @f[start_accepting_client] re-queues itself onto the data
-connection. Success of the start_accepting_client now depends on
-completion of activity on the newly created data connection, not the
-listening connection. When the start_receiving_object for the client
-data finally completes, the original start_accepting_client operation,
-now requeued behind it, takes control again, this time in the routine
-g_i2acc. This merely checks to insure that the receive completed
-successfully. It's final status, returned to the caller as OP_STATUS,
-is the same as that of the receive. The net result of all this is a
-newly allocated connection for which the accept and the acquisition of
-the initial parameters were all done without blocking.
-
-At this point a tentative connection exists, but the server still has
-three options in dealing with the new client. These are (1) accept
-the new client (2) refuse the client or (3) bounce the client to
-another server. To indicate the disposition, the server application
-must issue the @f[start_replying_to_client] asynchronous operation,
-indicating either @f[GDB_ACCEPTED, GDB_REFUSED,] or @f[GDB_FORWARDED].
-@f[start_replying_to_client] creates a tuple containing the response
-parameters to be sent to the client. It then queues a
-@f[start_sending_object] for that tuple on the client connection, and
-queues itself behind that. By the time @f[start_replying_to_client]
-receives control again, the send of the tuple has completed. The
-tuple and other dynamically allocated data is freed, and the return
-code passed back to the application.
-
-@subsection(Forking Servers)
-
-@index[Forking servers]
-@index[Servers, forking]
-GDB provides a @f[create_forking_server] routine which does most of
-the bookkeeping required for a single server program to support an
-arbitrary number of clients, forking once each time a new client
-connects. The number of simultaneous clients supported is limited
-only by the number of processes Unix allows the server to create.
-A GDB forking server has a single parent process, which hangs forever
-in the @f[create_forking_server] routine, and one child for each
-active client. @f[create_forking_server] returns in each child the
-connection descriptor for the corresponding client. The usual GDB
-communication services may then be used to communicate with the
-client. The supplied @f[dbserv.qc] database server is an example of a
-forking server.
-
-Internally, create_forking_server begins in the parent by setting up
-@f[gdb_reaper] as the handler for @f[SIGCHLD]. GDB thus reaps dying
-children automatically for the application.
-
-In the parent, @f[create_forking_server] issues an ordinary
-@f[create_listening_connection] to establish its own server address.
-It then loops forever using @f[start_accepting_client] to acquire
-sockets for new clients.
-The application may supply a validation routine which is used to
-determine whether a given client is to be accepted by the server. If
-the validation routine returns FALSE, then the
-@f[start_replying_to_client] is used to refuse the connection.
-Otherwise, the server forks the and the child issues a
-@f[start_replying_to_client] to accept the connection. In the
-parent, the client connection is severed (to prevent file and
-connection descriptor leakage) and the listening operation is reset
-so that it may be used again. @f[create_forking_server] thus
-hides the complexity of dynamically acquiring connections, forking,
-replying to clients, and reaping 'dead' children.
-
-The @f[gdb_debug] routine may be used to set a flag called
-@f[GDB_NOFORK]. When this flag is set, @f[create_forking_server]
-semantics are changed so that no fork is done when the first client
-connects. Instead, the single client is served directly by the
-parent. This flag is useful when using debuggers like dbx, which are
-incapable of debugging forking programs.
-
-@section(Peer-to-peer Communication)
-
-@index[Peer-to-peer communication]
-@f[start_peer_connection] is used by programs wishing to do
-symmetrical communication, in which neither is the server or the
-client.
-The current implementation tries first to connect to the other side,
-and if that fails, it hangs in an accept. There is a race condition
-in which the programs may hang if both are started at the same time;
-each tries a connect, which fails, and then each side hangs in an
-accept. The only solutions I know to this problem involve either (1)
-unconditionally creating multiple connections between the two sides,
-or (2) running randomized delay loops to re-try the connects
-periodically. Even option (2) will sometimes result in multiple
-connections. Either of these implementations is a possibility for the
-future. In the meantime, @f[start_peer_connection] merely fills in a
-connection descriptor, and does connects and accepts as described
-above.
-A check is also made to insure that both partners are running
-compatible versions of GDB (see g_ver_iprotocol and g_ver_oprotocol.)
-
-@section(Database Management)
-
-@index[Database management]
-GDB's database services are cleanly layered on top of the GDB
-facilities described above. The supplied @f[dbserv.qc] is a forking
-server implementing a fairly complete set of database access protocols
-for remote clients. Client functions are provided which encapsulate
-most of the low level GDB transmission services used for accessing the
-server.
-
-Database clients use operations like @f[start_db_query] and
-@f[perform_db_operation] to manipulate the database.
-These functions make extensive use of GDB's
-asynchronous communication capabilities to maximize the overlap
-between server and client processing, and to achieve pipelining of
-data transfers for retrievals returning large quantities of data.
-Application programmers are given asynchronous versions of most
-database functions, allowing them to overlap their own execution with
-GDB's access to the database. A single application may have
-simultaneous connections to multiple databases at multiple sites, and
-may have parallel operations in progress to the several databases
-simultaneously. The current implementation allows a single client to
-have multiple connections to a single database, with (simulated)
-parallel activity.
-
-@subsection(Database Client Operations)
-
-@f[start_accessing_database] returns a pointer of type DATABASE to a
-dynamically allocated structure used as a handle for subsequent access
-to that database. Stored within the structure is a pointer of type
-CONNECTION, which is used for data transmission to and from the
-server. In reviewing the code I am embarrassed to note that the
-@f[terminate_db] function described in the library reference manual
-was never implemented. It's function should be to sever the
-corresponding connection and de-allocate the database descriptor. In
-fact, the client may safely terminate or sever the connection at
-anytime if desired. The server will detect the loss of the connection
-and do the appropriate cleanup.
-
-The application uses @f[start_performing_db_operation] and
-@f[start_db_query] and their synchronous equivalents to perform
-operations against the database. @f[start_performing_db_operation] is
-suitable for any Ingres QUEL operation which is expressible as a
-single string, and which returns only an integer return code. This is
-in fact true of almost all QUEL statements except for retrieve, which
-returns a relation as well as a return code. @f[start_db_query] is
-used for retrieval.
-
-Both of these database access operations are asynchronous and full
-duplex. This means that an application can stream multiple requests
-to the server, mixing regular operations in with retrievals. The requests are transmitted
-to the server as fast as possible, and the responses are received
-asynchronously, but in order, as they become available.
-
-@f[start_performing_db_operation] takes the string containing the QUEL
-statement, converts it to the GDB type STRING_T, and queues an
-ordinary @f[start_sending_object] for the string on the outbound half
-connection. It then queues a @f[start_receiving_object] of an
-INTEGER_T on the inbound connection to receive the corresponding
-return code, and queues itself behind that on the @i[inbound]
-half connection. By the time this operation takes control again,
-the return code has been received, and cleanup can be done. Note that
-all phases of this activity are fully asynchronous. In particular,
-the outbound and inbound activities are completely independent as seen
-at the client. Of course, the server does not generate the return
-code until after it has inspected the request, but the client side is
-fully asynchronous. If another request is issued by the application,
-then the outbound
-data is queued for transmission immediately, regardless of how far the
-first operation has progressed, and the operations to receive the
-return code are queued behind those from the first request.
-For example, applications commonly queue in immediate succession the
-operations to begin a transaction, do several updates, and commit
-the transaction, waiting for completion only on the commit. These
-activities proceed much more rapidly than they would if each piece
-were implemented synchronously.
-
-@f[start_db_query] is similar in spirit, but it must also handle the
-receipt of the retrieved relation.
-In addition to sending the query string itself, the client sends a
-copy of the tuple descriptor for the relation to be created. This
-allows the server to properly format tuples for transmission back to
-the client, and it also provides type information for each field.
-As above, a receive operation is queued for a return code, and then
-the query operation itself is queued on the inbound half connection.
-It receives control in function g_idbq once the return code has been
-received. If the return code indicates failure, then the
-operation is complete. If success is indicated, then the resulting
-(possibly null) relation must be received from the server.
-
-There is no way to tell at this point how many tuples must be
-received. The server therefore prefaces each tuple transmission with
-a YES/NO flag, indicating whether another tuple is to follow. The
-client repeatedly receives these flags, and each time a YES is
-received, it prepares to receive another TUPLE. These tuples are then
-inserted into the application's relation. The @f[g_cdbq] routine, which gets
-control each time a flag or tuple is received, runs in the
-context of the OPERATION supplied by the application. Each time it
-receives control, it preempts itself by issuing
-@f[preempt_and_start_receiving_object], effectively sticking a receive
-operation ahead of itself on the inbound half connection. This
-continues until all of the flags and tuple data have been received.
-
-Note that @f[start_db_query] and the corresponding server could have
-been written to move the entire relation as a single GDB object.
-Though the code for this is much simpler, it means that the entire
-relation must be prepared at the server before any transmission can
-begin. The current implementation provides pipelining; each tuple is
-sent on its way as soon as it is retrieved from Ingres. Transmission
-time tends to be hidden by the overhead of the Ingres retrieval loop.
-
-@subsection(The dbserv.qc Database Server)
-
-@index[dbserv]
-As noted above, the supplied Ingres server is an ordinary GDB forking
-server whose purpose is to receive database requests from clients and
-pass them on to Ingres.
-Most of its operation is straightforward, with transmission of query
-results being a notable exception.
-
-When dbserv receives a query, it first parses the query string to
-determine which tuple fields will be assigned by the retrieval.
-The array named @f[offs] in the routine @f[do_query] is
-filled in with the offset that each retrieved field occupies in the
-tuple.
-A query buffer is prepared containing the text of the query in the
-form required by Ingres. The Ingres type indicators (e.g. %i4 for
-integer) are placed into the queries in place of the GDB field name
-indicators.
-
-Text fields are handled specially, because their lengths cannot be
-determined until after retrieval. do_query has a large local array of
-character strings, into which the actual Ingres retrieval is done.
-The routine named @f[fix_strings] is then used to copy the resulting
-text into GDB STRING_T variables, which are assigned to the tuple
-fields. Copies are needed because the large text buffers may be
-re-used for subsequent retrieves before the earlier tuples have been
-transmitted.
-
-The main retrieval loop in @f[do_query] repeatedly retrieves
-information from Ingres into a dummy tuple. This is promptly copied
-to buffer it for transmission, and @f[fix_strings] is called, as noted
-above.
-
-do_query may have several transmissions outstanding at once, the maximum
-being determined by the constant @f[OVERLAP]. The @f[pending]
-structure array, which contains the state of each outstanding
-transmission is used circularly. This has the effect of insuring that
-that the server never hangs in a @f[complete_operation] unless more
-than @f[OVERLAP] transmissions are outstanding at a time. At the time
-of this writing, @f[OVERLAP] is defined as 10. For each tuple, the
-server sends the YES/NO flag described above, followed by the data.
-Note that the data is sent as type TUPLE_DATA_T, rather than TUPLE_T,
-to eliminate redundant transmissions of the tuple descriptor.
-
-@appendix(Functions and Macros for Manipulating Structured Data)
-
-Here is a partial list of the GDB functions and macros which may be
-used to manipulate structured data. This list is intended as a quick
-reference guide. All user callable GDB routines are documented in the
-Library Reference Manual.
-
-@begin(description)
-@f[descriptor_from_tuple]@\returns the tuple descriptor of the supplied tuple.
-@index(descriptor_from_tuple)
-
-@f[descriptor_from_relation]@\returns the tuple descriptor of the supplied
-@index(descriptor_from_relation)
-relation.
-
-@f[create_tuple_descriptor]@\allocates a tuple descriptor.
-@index(create_tuple_descriptor)
-
-@f[delete_tuple_descriptor]@\destroys a tuple descriptor, deallocating the
-@index(delete_tuple_descriptor)
-memory which had been used to hold it.
-
-@f[create_tuple]@\allocates a tuple.
-@index(create_tuple)
-
-@f[null_tuple_strings]@\If a tuple contains fields of type STRING_T, then
-@index(null_tuple_strings)
-the memory for these is allocated separately from that used for other
-fields in the tuple. This routine releases the memory used for all
-string fields in the tuple. It should usually be called just before
-invoking @f[delete_tuple].
-
-@f[delete_tuple]@\destroys a tuple, deallocating the
-@index(delete_tuple)
-memory which had been used to hold it.
-
-@f[string_alloc]@\allocates space for a STRING, filling in the pointer and
-@index(string_alloc)
-length fields of the string descriptor.
-
-@f[string_free]@\returns the memory used for a STRING.
-@index(string_free)
-
-@f[db_alloc]@\Allocate memory using GDB's memory allocator. In most
-@index(db_alloc)
-implementations this just calls Unix @f[malloc], but GDB's memory allocators
-@index(malloc)
-can be replaced by users running in unusual environments.
-
-@f[db_free]@\Return memory allocated by db_alloc.
-@index(db_free)
-
-@f[STRING_DATA]@\Given an argument of type STRING, returns a @f[(char *)]
-@index(STRING_DATA)
-pointer to the string data. This is a macro which may appear as the
-target of an assignment statement; you can construct your own STRING
-by setting the STRING_DATA and MAX_STRING_SIZE.
-
-@f[MAX_STRING_SIZE]@\Given an argument of type STRING, returns an integer equal
-
-@index(MAX_STRING_SIZE)
-to the size of the allocated string data. If the STRING is used to
-hold a null terminated C character string, then @f[strlen] of that string
-@index(strlen)
-must be @f[<= MAX_STRING_SIZE].
-
-@f[ADD_TUPLE_TO_RELATION]@\a macro which inserts a new tuple after all the
-@index(ADD_TUPLE_TO_RELATION)
-others in a relation. The tuple must share the same descriptor as the
-relation itself.
-
-@f[ADD_TUPLE_AFTER_TUPLE]@\inserts a tuple into the middle of a relation.
-@index(ADD_TUPLE_AFTER_TUPLE)
-
-@f[REMOVE_TUPLE_FROM_RELATION]@\the specified tuple is removed from the
-@index(REMOVE_TUPLE_FROM_RELATION)
-relation, but not de-allocated. It may subsequently be inserted in
-another relation if desired. Each tuple may be in at most one
-relation at a time.
-
-@f[FIRST_TUPLE_IN_RELATION]@\Finds the first tuple in a relation. Returns
-@index(FIRST_TUPLE_IN_RELATION)
-NULL if the relation is empty.
-
-@f[NEXT_TUPLE_IN_RELATION]@\Given a relation and a tuple which is presumed
-@index(NEXT_TUPLE_IN_RELATION)
-to be in that relation, return the tuple which follows the one
-supplied. NULL is returned if the tuple supplied is the last one in
-the relation.
-
-@f[PREV_TUPLE_IN_RELATION]@\Given a relation and a tuple which is presumed
-@index(PREV_TUPLE_IN_RELATION)
-to be in that relation, return the tuple which preceeds the one
-supplied. NULL is returned if the tuple supplied is the first one in
-the relation.
-
-@f[DESCRIPTOR_FROM_TUPLE]@\Given a tuple, get back its tuple descriptor.
-@index(DESCRIPTOR_FROM_TUPLE)
-
-@f[DESCRIPTOR_FROM_RELATION]@\Given a relation, get back its tuple descriptor.
-@index(DESCRIPTOR_FROM_RELATION)
-
-@f[FIELD_FROM_TUPLE]@\Given a tuple and a field index, return a pointer to the
-@index(FIELD_FROM_TUPLE)
-field. The results of this macro must be cast properly by the caller.
-
-@f[FIELD_TYPE_IN_TUPLE]@\Given a tuple descriptor (@i[not] a tuple) and
-@index(FIELD_TYPE_IN_TUPLE)
-the index (@i[not] the name) of a field, return the type code of the
-field. Symbolic names for type codes, such as @f[STRING_T] and
-@Index(STRING_T)
-@f(INTEGER_T) are defined in gdb.h; they may be stored in
-@Index(INTEGER_T)
-variables of type @f[FIELD_TYPE].
-@index(FIELD_TYPE)
-
-@f[field_index]@\Given a tuple descriptor and the name of a field, return the
-@index(field_index)
-index of that field.
-
-@end(description)
-
-The following routines are often useful for debugging GDB
-applications. @b[Important note: each of these routines takes two
-arguments, the first is an i.d. string, the second is the object to be printed!]
-
-@begin(description)
-@f[print_tuple]@\Prints a tuple on the gdb_log file (which defaults to
-@index(print_tuple)
-stderr.) All fields are correctly labeled and formatted according to
-their type.
-
-@f[print_relation]@\Prints an entire relation, with all of its tuples, on gdb_log.
-@index(print_relation)
-@end(description)
-@appendix(Sample Non-Forking Server)
-@label(nonforkingserver)
-@modify(example,Size 8)
-@begin(example)
-/************************************************************************/
-/*
-/* tsr (test server)
-/* -----------------
-/*
-/* Author: Noah Mendelsohn (IBM T.J. Watson Research and MIT Project
-/* Athena)
-/*
-/* Copyright: 1986 MIT Project Athena
-/*
-/************************************************************************/
-/*
-/* PURPOSE
-/* -------
-/*
-/* A GDB server program demonstrating techniques for asynchronously
-/* communicating with an arbitrary number of clients from a single
-/* Unix server process. This server accepts GDB connections from
-/* clients as requests come in (up to the arbitrary maximum
-/* MAXCLIENTS.) On each connection, it receives a stream of integers,
-/* which it interprets as ASCII characters. The characters are
-/* converted to uppercase, and then sent back to the client from
-/* which they came.
-/*
-/* All of this is done completely asynchronously. No client is
-/* locked out while characters are being echoed to another, and
-/* new connections are accepted at any time.
-/*
-/* NOTES
-/* -----
-/*
-/* 1) The complete state of each client is kept in the array
-/* named client. The client[i].state variable indicates whether
-/* client i is active, and if so, the client[i].action variable
-/* indicates what kind of asynchronous activity the client is
-/* engaged in. Note that these are local conventions, having
-/* nothing to do with GDB or its interfaces.
-/*
-/* 2) Communication to each client is done over its connection,
-/* named client[i].con.
-/*
-/* 3) There is at most one asynchronous activity pending to
-/* each client at any given time, and its state is tracked
-/* in the variable named client[i].pending_op. The operation
-/* may be a send, a receive, or an accept, depending on
-/* the contents of client[i].action. These operations are
-/* allocated when the server starts up, and then re-used
-/* repeatedly. They are the GDB analog of a lightweight process,
-/* which is activated when queued on a connection.
-/*
-/* 4) A special form of connection and a special listening operation
-/* are used for asynchronously listening for new connection
-/* requests. These are 'listencon' and 'listenop' respectively.
-/*
-/* 5) GDB includes a special form of select which waits for
-/* completion of operations as well as for activity on user
-/* specified file descriptors. The list of operations to be
-/* monitored is stored in the variable
-/* named op_list. The call to op_select_any hangs until one
-/* or more of these operations complete, then terminates.
-/*
-/* 6) The main server loop acts on any new connection requests,
-/* processes any newly completed activity on the active
-/* clients, then drops into op_select_any to allow asynchronous
-/* activities to progress.
-/*
-/*
-/************************************************************************/
-@end(example)
-@begin(example)
-#include <stdio.h>
-#include "gdb.h"
-
-/************************************************************************/
-/*
-/* DECLARATIONS
-/*
-/************************************************************************/
-
-#define MAXCLIENTS 10
-
- /*----------------------------------------------------------*/
- /*
- /* State of each possible client
- /*
- /*----------------------------------------------------------*/
-
-struct client {
- int state; /* state of this client */
- /* descriptor */
-#define CL_DEAD 1 /* client not started */
-#define CL_STARTING 2 /* accepted, reply ongoing */
-#define CL_UP 3 /* ready to go */
- int action; /* what are we doing now */
-#define CL_RECEIVE 4 /* waiting for a packet */
-#define CL_SEND 5 /* sending a packet */
-#define CL_ACCEPT 6 /* sending a reply */
- CONNECTION con; /* connection to this */
- /* client, if any */
- OPERATION pending_op; /* pending operation */
- /* on this connection, */
- /* if any */
- int data; /* the character to echo */
- /* goes here, expressed as */
- /* an int */
-};
-
-struct client client[MAXCLIENTS];
-
- /*----------------------------------------------------------*/
- /*
- /* Connections and operations for listening for
- /* new clients.
- /*
- /*----------------------------------------------------------*/
-
-CONNECTION listencon; /* listen on this */
- /* connection */
-OPERATION listenop; /* this operation is used */
- /* repeatedly for listening */
- /* for new clients */
-
-int nextcl = 0; /* index of the next client */
- /* we'll accept */
-
- /*----------------------------------------------------------*/
- /*
- /* Miscellaneous variables used in acquiring connections.
- /* These are ignored in a simple server like this; a
- /* more sophisticated server might want to validate the
- /* names of its clients before accepting connections.
- /*
- /*----------------------------------------------------------*/
-
-TUPLE client_tuple; /* client request goes */
- /* here */
-char otherside[100];
-int othersize;
-@end(example)
-@begin(example)
-/************************************************************************/
-/*
-/* MAIN
-/*
-/************************************************************************/
-
-int
-main(argc, argv)
-int argc;
-char *argv[];
-{
- /*----------------------------------------------------------*/
- /*
- /* LOCAL VARIABLES
- /*
- /*----------------------------------------------------------*/
-
- register int i; /* loop index */
- LIST_OF_OPERATIONS op_list; /* for op_select_any */
-
- /*----------------------------------------------------------*/
- /*
- /* Check parameters
- /*
- /*----------------------------------------------------------*/
-
- if (argc != 2) {
- fprintf(stderr,"Correct form is %s <servicename>\n",
- argv[0]);
- exit(4);
- }
-
- /*----------------------------------------------------------*/
- /*
- /* Initialize
- /*
- /*----------------------------------------------------------*/
-
- gdb_init(); /* set up gdb */
- init_clients(); /* null the client states */
- do_listen(argv[1]); /* start the listening */
- /* connection and queue */
- /* a listening operation */
- make_oplist(&op_list); /* create wait list */
-
- /*----------------------------------------------------------*/
- /*
- /* Loop forever taking care of business.
- /*
- /* 1) If any new connection requests have come in,
- /* accept them.
- /*
- /* 2) For each client on which some activity is newly
- /* completed, take care of it.
- /*
- /*----------------------------------------------------------*/
-
- while (TRUE) {
- if (OP_DONE(listenop))
- new_connection();
- for (i=0; i<MAXCLIENTS; i++) {
- if (OP_DONE(client[i].pending_op))
- do_client(i);
- }
- op_select_any(op_list, 0, NULL, NULL, NULL, NULL);
- }
-}
-@end(example)
-@begin(example)
-/************************************************************************/
-/*
-/* do_client
-/*
-/* An operation has completed on the specified client.
-/*
-/************************************************************************/
-
-int
-do_client(id)
-int id;
-{
- register struct client *cp = &(client[id]);
-
- /*
- * If there has been an error, shutdown the client.
- */
- if (OP_STATUS(cp->pending_op) == OP_CANCELLED) {
- sever_connection(cp->con);
- reset_operation(cp->pending_op);
- cp->state = CL_DEAD;
- cp->action = 0;
- return;
- }
- /*
- * The operation completed successfully. Figure out what it was
- * and do the right thing.
- */
- switch (cp->action) {
- case CL_ACCEPT:
- case CL_SEND:
- start_receiving_object(cp->pending_op, cp->con,
- (char *)&cp->data,
- INTEGER_T);
- cp->action = CL_RECEIVE;
- break;
- case CL_RECEIVE:
- if (cp->data >= 'a' && cp->data <= 'z')
- cp->data += 'A'-'a'; /* upcase the response */
- start_sending_object(cp->pending_op, cp->con,
- (char *)&cp->data,
- INTEGER_T);
- cp->action = CL_SEND;
- }
-}
-@newpage
-/************************************************************************/
-/*
-/* init_clients
-/*
-/************************************************************************/
-
-int
-init_clients()
-{
- register struct client *c;
-
- for (c=client; c<client+MAXCLIENTS; c++){
- c->state = CL_DEAD;
- c->action = 0;
- c->con = NULL;
- c->pending_op = create_operation();
- reset_operation(c->pending_op);
- }
-}
-
-
-@end(example)
-@begin(example)
-/************************************************************************/
-/*
-/* make_oplist
-/*
-/************************************************************************/
-
-int
-make_oplist(oplp)
-LIST_OF_OPERATIONS *oplp;
-{
- /*
- * ugh! we've got to fix create_list_of_operations to be
- * more flexible!!
- */
-
- *oplp = create_list_of_operations(MAXCLIENTS+1, listenop,
- client[0].pending_op,
- client[1].pending_op,
- client[2].pending_op,
- client[3].pending_op,
- client[4].pending_op,
- client[5].pending_op,
- client[6].pending_op,
- client[7].pending_op,
- client[8].pending_op,
- client[9].pending_op);
-}
-@end(example)
-@begin(example)
-/************************************************************************/
-/*
-/* do_listen
-/*
-/* Do the one time setup for listening for clients, and
-/* also start a listen for an actual client.
-/*
-/************************************************************************/
-
-int
-do_listen(service)
-char *service;
-{
-
- /*----------------------------------------------------------*/
- /*
- /* Make a listening connection
- /*
- /*----------------------------------------------------------*/
-
- fprintf(stderr, "Server creating listening connection\n");
- listencon = create_listening_connection(service);
-
- if (listencon == NULL) {
- fprintf(stderr,"tsr: could not create listening connection\n");
- exit (4);
- }
-
- /*----------------------------------------------------------*/
- /*
- /* On that connection, put up an operation to listen
- /* for our first client.
- /*
- /*----------------------------------------------------------*/
-
- listenop = create_operation();
-
- othersize = sizeof(otherside);
-
- start_accepting_client(listencon, listenop, &(client[nextcl].con),
- (char *)otherside,
- &othersize, &client_tuple);
-
-}
-@end(example)
-@begin(example)
-/************************************************************************/
-/*
-/* new_connection
-/*
-/* We have just gotten a connection for client nextcl.
-/*
-/************************************************************************/
-
-int
-new_connection()
-{
- register struct client *cp = &client[nextcl];
- /*
- * Make sure there's been no error
- */
- if(OP_STATUS(listenop) != OP_COMPLETE ||
- cp->con == NULL) {
- fprintf(stderr,"Error on listening operation\n");
- exit(8);
- }
- /*
- * Set up the new connection and reply to the client
- */
- cp->state = CL_STARTING;
- cp->action = CL_ACCEPT;
- start_replying_to_client(cp->pending_op, cp->con, GDB_ACCEPTED,
- "", "");
-
- /*
- * Find a new free connection descriptor. Blow up if we've used the
- * last one
- */
- for (nextcl=0; nextcl<MAXCLIENTS; nextcl++)
- if (client[nextcl].state == CL_DEAD)
- break;
-
- if (nextcl == MAXCLIENTS) {
- fprintf(stderr,"Too many clients, giving up\n");
- exit(8);
- }
- /*
- * Start listening again
- */
- reset_operation(listenop);
- othersize = sizeof(otherside);
-
- start_accepting_client(listencon, listenop, &(client[nextcl].con),
- (char *)otherside,
- &othersize, &client_tuple);
-
-
-}
-@end(example)
-
-@begin(comment)
-
-@Chapter(Hints and Tricks)
-select variants
-
-redefining GDB_GIVEUP
-
-replacing storage allocators
-
-print_relation, tuple, etc.
-
-tuples as analogs for structures.
-
-Using procedures to keep tuples up to date.
-
-Creating your own types
-
-Debug switch and the log
-
-Output from dbserv
-
-Reference counting of tuple descriptors.
-
-@Chapter(Bugs)
-Macros which require no ; after invocation
-
-/etc/services entries for database server
-
-Only one database server per site.
-@Chapter(How GDB Works)
-
-@end(comment)
+++ /dev/null
-/************************************************************************/
-/*
-/* This file accompanies GDB version 0.4
-/*
-/* Versions designated 0.x are prototype releases supported
-/* informally by the author.
-/*
-/* Author: Noah Mendelsohn
-/* IBM T.J. Watson Research Center -and- MIT Project Athena
-/*
-/* Copyright (C) 1987 Massachusetts Institute of Technology
-/*
-/************************************************************************/
-
+++ /dev/null
-@device(PostScript)
-@make(Manual)
-@style(FontFamily "Helvetica", Size 11)
-@style(hyphenation on)
-@style(indent 0)
-@style(leftmargin +4cm)
-@style(footnotes "")
-@modify(example,Size 10, below 1cm, above 1cm, leftmargin +3, rightmargin +0)
-@define(F, FaceCode F, Size 11, TabExport)
-@define(FunctionSection, leftmargin +1cm, rightmargin +1cm, size 10)
-@define(Function, leftmargin -.5cm, nofill, indent 0, break, group,
- above .6cm, below .4cm, blanklines kept, spread .7 line)
-@modify(HD1, Below .75cm, Above 1cm, indent -1cm)
-@modify(HD1A, Below .75cm, Above 1cm)
-@modify(HD2, Below .6cm, Above 1cm, indent -1cm)
-@modify(HD3, Below .6cm, Above 1cm)
-@modify(itemize, Below .5cm, Above .6cm)
-@modify(example, Below .5cm, Above .6cm)
-@modify(float, Below .5cm, Above .6cm)
-@begin(titlepage)
-@begin(titlebox)
-@majorheading(GDB C Library Reference Manual)
-@heading(Noah Mendelsohn)
-
-@end(titlebox)
-Document Version: 0.4 (DRAFT)
-For use with GDB Release: 0.4
-This draft printed on: @value(Date)
-@copyrightnotice(MIT Project Athena)
-@end(titlepage)
-@Counter(ProgramNumber, Numbered <@1>, Referenced <@1>)
-@set(ProgramNumber=1)
-@PrefaceSection(Preface)
-
-GDB is a set of C library routines and related utilities which
-facilitate construction of network services. This preliminary version
-of the Library Reference Manual is based on specifications which were
-written before the code. In some cases, it refers to design decisions
-which had yet to be made at the time of writing, or uses terminology
-which is slightly inconsistent with the other GDB documentation. The
-tone of some of the descriptions is informal, reflecting debates over
-design philosophy which occured during development of GDB.
-Nevertheless, almost all of the services described herein are
-currently operational in Release 0.1 of GDB, and they conform very
-closely to their written specifications. This guide is intended
-primarily for reference. For a general introduction to programming
-with GDB, read the document titled "A Guide to Using GDB." Please
-report any discrepancies you discover in either document to the
-author. A more carefully edited version of this manual will accompany
-GDB Release 1.
-
-@PageHeading(Even, Right "@c[GDB Library Reference Manual]",
- Left "@c[@value(Page)]")
-@PageHeading(Odd, Right "@c[@value(Page)]",
- Left "@c[@Title(Chapter)]")
-
-@Chapter(Introduction)
-@Index[Introduction]
-
-This document is the program interface description for a collection of
-Unix@+[TM]@foot(@+[TM]Unix is a trademark of AT&T Bell Laboratories)
-library routines which provide transparent distributed support for
-network servers, and for relational databases in particular. The
-several libraries work together to implement a relational database
-which may be conveniently accessed from anywhere in a "Berkeley" Unix
-network, but the lower level libraries may themselves be useful for
-building other sorts of network services, or for
-transmitting other kinds of structured data. The database access
-and transmission services support communication between incompatible
-machine architectures; data conversions are done as necessary by the
-library routines.
-
-This document contains a separate section for each of the libraries.
-Interdependencies are noted in the sections.
-
-@chapter(Using the Libraries)
-
-The sections below outline the library services provided by GDB.
-Programs using GDB
-to access relational databases will use, directly or indirectly, the
-services of all of the GDB layers. Programs using GDB for local management
-of structured data or as a means of writing servers, clients, or other
-communicating programs may use only some of the layers. In any case,
-Every C main program
-that uses GDB should include the following:
-
-@begin(group)
-@begin(example)
-
-#include <stdio.h>
-#include "gdb.h"
-
-int
-main()
-{
- gdb_init();
- /*
- * Once gdb_init has been called, the services of gdb are
- * available. gdb_init may be called only once.
- */
-}
-@end(example)
-@end(group)
-
-Other source files should @f[#include "gdb.h"], but should @i[not]
-@index(gdb.h)
-invoke @f[gdb_init].
-@index(gdb_init)
-
-If you are using GDB to access an Ingres database, then there are
-several other things you must do:
-
-@begin(itemize)
-@begin(multiple)
-Make sure RTI Ingres is installed at the server site where the data
-@index(RTI Ingres)
-@index(Ingres)
-is to be stored.
-@end(multiple)
-
-@begin(multiple)
-Make sure that there is an entry in the file named @f[/etc/services]
-@index(/etc/services)
-at both server and client sites for the service named gdb_db@index(gdb_db).
-The entries at the server and client should be identical.
-Talk to your system administrator if this is not the case.
-@i[Note: as of this writing, GDB does not check @f[/etc/services] when
-accessing Ingres, but soon it will!]
-@end(multiple)
-
-@begin(multiple)
-Make sure that the @f[dbserv] program supplied with GDB
-@index(dbserv)
-is started at the server site.
-@end(multiple)
-
-@end(itemize)
-
-Gdb produces two kinds of terminal output. Serious error messages,
-which are always directed to stderr, and other output, which are
-directed to the file indicated by the gdb_log global variable.
-Included in the latter are the results of functions like
-@f[print_relation]@index[print_relation], which are described below.
-Users wishing to redirect @f[gdb_log]@index[gdb_log] may open any
-stream of their choice and put the resulting FILE * variable into
-gdb_log after calling gdb_init.
-
-The remainder of this manual is taken with the definitions of the
-functions provided by GDB. The functions are organized by general
-category, but all are listed alphabetically in the index. Each
-section is prefaced by a brief introduction, followed by the function
-definitions themselves. In all the descriptions below, the
-pseudo-type @f[string]@Index[string] is used for @f[char *] in cases
-where a null terminated string is to be supplied. This is just a
-documentation convention, the type string is not defined in the
-libraries. Here is the description of the @f[gdb_init] function, the
-only one which has been introduced so far:
-
-@begin(functionsection)
-@begin(function)
-void
-@f[gdb_init]()
-@index[gdb_init]
-@end(function)
-
-Gdb_init initializes the data structures used by gdb. Gdb_init must be called
-once and only once prior to use of any other GDB services. No value is
-returned.
-@end(functionsection)
-
-@chapter(Database Manipulation)
-@Index(Database manipulation)
-
-This library provides most of the services of a shared, network wide,
-relational database. It is built on the structured object
-manipulation and transmission libraries described in Chapters
-@ref(structdata) and @ref(trans). A query, for example, returns a
-RELATION in the local process space.
-
-These routines have the following important characteristics:
-
-@begin(itemize)
-Access to databases stored on incompatible machines (e.g. RT/PC to
-Vax) is supported transparently.
-
-Pipelining of requests from client to server is encouraged. This
-maximizes overlap in processing and minimizes server swapouts between
-repeated requests, thereby improving performance.
-
-Requests proceed asynchronously with client execution, though
-synchronous interfaces are provided for applications where asynchrony
-is an unnecessary complication. Asynchronous processing is managed
-by appropriate interaction with the Berkeley 'select' mechanism;
-applications can be created which drive a user's terminal
-or other I/O devices while simultaneously retrieving the results
-of a database query. One important use of such overlap is to support
-abort operations (e.g. a screen menu selection to abort a long running
-query.)
-
-Multiple databases may be accessed simultaneously. If desired, these
-may reside at several sites. Using the asynchronous processing options,
-queries may be executed in parallel on the several databases. The
-same facilities may be used to open parallel connections to the same
-database, allowing parallel execution of independent operations.
-@end(itemize)
-
-Unfortunately, the syntax of a few of the actual library calls described
-herein is Ingres dependent. It is hoped that one day an effective
-cannonical form will be developed, and these routines replaced with a
-suite that might be supported using a variety of database engines. The
-Ingres dependency is isolated to a couple of routines.
-
-@section(Types Used for Database Manipulation)
-
-The types @f[RELATION, TUPLE, and FIELD_TYPE] are inherited from the
-structured data management libary (see Chapter @ref(structdata).) The
-following additional type is also used.
-
-@begin(table)
-@caption(Types used in Database Manipulation)
-@tabset(.75in, 2.5in)
-
-@\DATABASE@INDEX[DATABASE]@\A handle on an accessible database.
-@end(table)
-
-@Section(Routines)
-
-NOTE: See also the "Communications and Server Management" Section of
-this specification. This section contains only those functions
-particular to database manipulation. General facilities for
-starting operations and querying their status are found in the other
-section.
-
-@begin(functionsection)
-@begin(function)
-DBSTATUS
-@f[access_db](db_ident, &db_handle);
-@index[access_db]
-DATABASE db_handle;
-string db_ident;
-@end(function)
-
-Establishes a database context for future database calls. A successful
-accessdb call must be executed before any database operations can be done.
-The return code DB_OPEN indicates that the database was successfully accessed.
-A successful call to accessdb returns a local handle which must be
-supplied to subsequent library routines to indicate the database to be
-accessed. Most implementations support simultaneous access to several
-databases, but that is not required. The DB_TOO_MANY_ACCESSED error is
-raised when and if implementation defined limits are exceeded. Each
-database should be closed with a 'terminatedb' call when no longer needed.
-Failure to explicitly terminate databases may waste resources at the
-server, though most implementations will detect improper termination and
-do the necessary reclamation.
-
-The form of the db_ident string depends on the implementation. For
-example, in a network with nameservers, a single string such as
-'partsdata' may suffice to identify a database on the network. When
-name resolution services are not available, a string of the form
-'databasename@@site' may be required. There is a question of access
-rights and authorization for this operation. I'm hoping that we can
-eventually build on Kerberos, but there may be problems. Worst of all,
-we may have to re-invent Ingres' protection mechanism, which would
-involve a very detailed understanding of their operations. I hope not.
-
-@begin(function)
-int
-@f[start_accessing_db](op, db_ident, &db_handle);
-@index[start_accessing_db]
-OPERATION
-DATABASE db_handle;
-string db_ident;
-@end(function)
-
-Same as accessdb, but the program continues to execute (if possible)
-while the operation is sent to the server and performed. See
-'complete_operation' and 'op_select.' All operations and queries are
-processed in fifo order. The value of db_handle is undefined until the
-operation_status(pending_operation) is OP_COMPLETE and the
-operation_result(pending_operation) is DB_OPEN.
-
-@begin(function)
-DBSTATUS
-@f[terminate_db](&db_handle);
-@index[terminate_db]
-DATABASE db_handle;
-@end(function)
-
-This routine reclaims server and client resources used to access the
-database and severs any connections which may no longer be needed. Data
-which has been retrieved and stored locally remains available.
-
-@begin(function)
-OPERATION
-@f[start_terminatedb](db_ident);
-@index[start_terminatedb]
-string db_ident;
-@end(function)
-
-Same as terminatedb, but the program continues to execute (if
-possible) while the operation is sent to the server and performed.
-See 'complete_operation' and 'op_select.' All operations and queries
-are processed in fifo order.
-
-@begin(function)
-OPERATION
-@f[DB_STATUS](db_handle);
-@index[DB_STATUS]
-DATABASE db_handle;
-@end(function)
-
-Returns the status of the specified database. Values returned are
-DB_OPEN@index[DB_OPEN], indicating that the database is available for use,
-or DB_CLOSED@index[DB_CLOSED], indicating that it is not.
-
-@begin(function)
-int
-@f[perform_db_operation](db_handle,request);
-@index[perform_db_operation]
-DATABASE db_handle;
-string request;
-@end(function)
-
-This routine may be used to perform any database operation which does
-not require structured data as an argument and which only returns a
-return code. In the case of Ingres, this covers all operations except
-retrieve. Requests are represented as null terminated strings. In
-other words, this library routine sends a simple string command to the
-server for execution and returns an integer return code. Any pending
-asynchronous operations on this database are completed before the
-supplied operation is attempted.
-
-@begin(function)
-int
-@f[db_query](db_handle, relation, query_string)
-@index[db_query]
-DATABASE db_handle;
-RELATION relation;
-string query_string;
-@end(function)
-
-Executes the query specified by query string,
-appending its result to the supplied relation.
-An integer return code is returned. OP_SUCCESS is returned for a
-successful query. The relation is unchanged unless the query
-succeeds. Note that it is possible to have a successful query (i.e.
-one which is well formed with respect to the database) which returns
-no data; such a query will return OP_SUCCESS leaving the relation unchanged.
-
-Query_string has the same form as an EQUEL query, except that the
-names of returned fields are enclosed in >* ....*< brackets (we can
-change the brackets to anything that won't conflict with Ingres
-syntax.) Note that the relation must be 'created' before it may be
-given to query_db. This is so we know typing information for
-constructing the actual database query. The caller may use sprintf or
-a similar function to prepare a query string containing variable data.
-The selected fields MUST match the names of fields in the relation.
-If not, a DB_BAD_FIELD_NAME error is raised before the query is
-executed. Null values are provided for tuple fields not retrieved by
-the query.
-
-The types of the referenced fields in the supplied relation must be
-drawn from a limited set of INTEGER_T, REAL_T, STRING_T and DATE_T types
-(the exact list will be provided in a later version of this
-specification document.) If not, a DB_BAD_FIELD_TYPE error is raised
-before the query is executed. The query will be constructed using the
-supplied types. If Ingres is incapable of doing the appropriate
-conversions upon retrieval, then a DB_CONVERSION_ERROR is returned.
-If the field type in the relation is STRING_T and Ingres succesfully
-converts the retrieved data to character form, then a null is appended
-iff there is room at the end. If the supplied field is too short,
-data is quietly dropped on the right end. The string is allocated by
-string_alloc. The
-string may be free'd with string_free, by the routine
-'null_tuple_strings', or by the delete_relation routine. Normally,
-space is reclaimed automatically when the corresponding tuple or
-relation is freed. Fields not assigned by the query are set to null
-values.
-
-As specified, this routine is incapable of retrieving data of
-indeterminate structure, e.g. it cannot achieve the effect of an
-e.all retrieve in Ingres. This sort of function could be layered
-upon the base support suggested here, by first querying the Ingres
-structure database and then issuing the appropriate second query for
-the fields actually in the relation. This implementation is presumably
-slow, but I am reluctant to greatly complicate the design at this
-point. Another approach would be to add a special library routine
-db_query_relation which would create a relation, making assumptions
-about field representations for the fields actually retrieved. I'm
-inclined to put this off until we see the core of the library up and
-running. It would (probably) not involve a major restructure.
-
-@begin(function)
-int
-@f[start_performing_db_operation](operation, db_handle,
-@index[start_performing_db_operation]
-request);
-OPERATION operation;
-DATABASE db_handle;
-string request;
-@end(function)
-
-Same as perform_db_operation, but the program continues to execute (if
-possible) while the operation is sent to the server and performed. See
-'operation_status, operation_result, complete_operation and
-op_select.' All operations and queries are processed in fifo order.
-Callers must insure that the request string
-remains valid until the operation completes (be careful about using local
-variables.)
-
-
-@begin(function)
-int
-@f[start_db_query](operation, db_handle, relation,
-@index[start_db_query]
-query_string)
-OPERATION operation;
-DATABASE db_handle;
-RELATION relation;
-string query_string;
-@end(function)
-
-Same as query_db, but the program continues to execute (if possible)
-while the operation is sent to the server and performed. See
-'operation_status, operation_result, complete_operation and
-op_select.' The relation is undefined during the execution of this
-operation. It may not be referenced or free'd until the operation
-completes. All operations and queries are processed in fifo order.
-The value of the relation is indeterminate in the case of failure,
-but it is guaranteed to be of legal structure and self-consistent.
-Callers must insure that the query string
-remains valid until the operation completes (be careful about using local
-variables.)
-@end(functionsection)
-
-@chapter(Structured Data Management at a Single Site)
-@Index(Structured data management)
-@label(structdata)
-
-These routines provide a simple, limited facility for managing
-structured data on behalf of a single process. A basic datum is called
-a field, and it may be of arbitrary type (including user defined types.)
-Named fields may be collected into tuples, and lists of tuples may
-form relations. These constructs are used to represent the data
-retrieved from a relational database. A separate library (Chapter
-@ref(trans))
-supports transmission of these data structures through a
-network. The typing scheme provided is sufficient to drive the
-transformations and re-alignment required when transmitting across
-incompatible machine architectures. It is NOT intended as a general
-purpose extension to the C language.
-
-@section(Type Definition and Manipulation)
-
-The information in this section is relavent to those users wishing to
-define their own types. User defined types are NOT necessary for
-typical database retrieval and update activities. Users desiring just
-those services need only know the names of built in system types,
-such as @f[INTEGER_T] and @f[STRING_T] which have direct counterparts in an
-Ingres database. @i[In the likely event that you have no need to
-define your own types, skip ahead now to section @ref(structypes).]
-
-Each datum or field managed by the system must be of some specified
-type. For each type, the system must have access to certain
-information (e.g. alignment restrictions) and to certain routines
-(initialize, encapsulate for transmission, etc.) Limited facilities to
-support user defined types are also helpful, since they allow such
-services as transmission across heterogeneous machines to be supported
-for arbitrary user data (this notion is stolen directly from ARGUS.)
-On the other hand, we don't want to tackle the many complicated
-problems inherent in a general solution to the dynamic typing problem.
-Instead, we employ the following simple, ad hoc mechanism, which seems
-to be sufficient for our needs.
-
-The system supports a set of types identified signed integers (e.g.
-type 1 might be STRING_T, type 2 might be DATE_T, etc.) Out of this
-name space, the first few starting from 0 are reserved for the system
-defined types, and the rest, up to GDB_MAX_TYPES, are available for
-dynamic assignment of user defined types. Appropriate pre-processor
-symbols, such as INTEGER_T, are provided in an include file for system
-types.
-
-For each type, certain information must be available to the system at
-execution time. This information is coded as a two dimensional array,
-each element of which holds either an integer, a pointer to a string,
-or a function pointer. One index selects the type, the other the
-property. For example, property number 6 of each type might be a code
-for its alignment requirement (4 for fullword, etc.). Using the
-example above, entry [2,6] in the type definition array would give the
-alignment rule for a field of type DATE_T. In certain cases, the
-information to be coded is a pointer to a routine. For example, a
-NULL value routine is provided for each type which can be used to initialize
-fields of that type.
-
-In order to create a user defined type, it is necessary to code
-functions for each of the necesary properties (e.g. the function which
-computes null values) and then use the "create_type" function to
-register the new type in the type tables. "create_type" returns the
-integer code assigned to the new type. WARNING: create_type is not
-yet implemented.
-
-@subsection(Information Required for Each Type)
-
-The simple mechanism described above is sufficient to encode the
-properties of each type. The information currently required is:
-
-@begin(itemize)
-LENGTH_PROPERTY: the length of the field
-
-ALIGNMENT_PROPERTY: an integer code for the alignment rule for the
-type (8 for doubleword, etc., may vary by machine type.)
-
-NULL_PROPERTY: a routine to initialize a null value of the type
-
-CODED_LENGTH_PROPERTY: a routine to calculate the length of the
-field when encoded for transmission.
-
-
-ENCODE_PROPERTY: a routine to encode the field for transmission
-
-DECODE_PROPERTY: a routine to decode tranmitted data into local
-representation
-
-FORMAT_PROPERTY: a routine to format the field (optional, for debugging)
-
-NAME_PROPERTY: a string name for the type.
-@end(itemize)
-
-For system defined types, this information is provided automatically by
-include files and libraries which initialize the system type definition
-array. Interface specifications for the various routines will be
-provided in a later version of this specification. For the moment,
-the code in source file @f[gdb_stype.c]@Index[gdb_stype.c] provides a
-useful model.
-
-@newpage
-@section(C Typedefs Relating to Data Structuring)
-@label(structypes)
-
-The following C types are defined in include files for use with the
-routines listed below. These should not be confused with types
-declared using the mechanisms above, which the C compiler knows nothing
-about.
-
-@begin(table)
-@caption(Typedefs for Data Structuring)
-@tabset(.75in, 2.5in)
-@\TUPLE_DESCRIPTOR@index[TUPLE_DESCRIPTOR]@\information needed to describe the structure
-@\@\of a given tuple.
-
-@\TUPLE@index[TUPLE]@\a handle on an actual tuple. This is always
-@\@\a pointer, and it may be manipulated
-@\@\accordingly (e.g. its length may be presumed
-@\@\small, and NULL is used for non-existent
-@\@\TUPLES.)
-
-@\RELATION@index[RELATION]@\a handle on an actual relation. This is always
-@\@\a pointer, and it may be manipulated
-@\@\accordingly.
-
-@\FIELD_TYPE@index[FIELD_TYPE]@\a type code for a field...generally
-@\@\represented as a signed integer (see above)
-@end(table)
-
-@section(Routines)
-
-(Note some of these may be implemented as macros. Where such
-implementation is presumed, the name is put in UPPERCASE. This
-allusion to Unix convention is meant to suggest that the associated
-operations are extremely fast, and that they are likely to suffer/gain
-from the semantic differences which distinguish macros from C
-functions. Note also that some of the macros contain define statements
-in C of the form @f[{}], these must be used without semicolon's or
-a syntax error will occur.)
-
-@begin(functionsection)
-@begin(function)
-TUPLE_DESCRIPTOR
-@f[create_tuple_descriptor](number_of_fields, name_list,
-@index(create_tuple_descriptor)
- type_list)
-string name_list[];
-FIELD_TYPE type_list[];
-int number_of_fields;
-@end(function)
-
-Allocates and fills in a tuple descriptor data structure. The
-descriptor records the number of fields in the tuple, the length in
-bytes of each field, and a pointer to the type descriptor of each
-field. Each field's offset is automatically assigned as the sum of
-the offsets of the fields preceeding it, adjusted according to the
-alignment condition found in the type descriptor. Note that the type
-named STRING_T has a reserved meaning in some contexts (see below).
-Specifically, string data is generally stored discontiguously from the
-tuple itself; the tuple holds only control information and a pointer
-to the string. Space for tuple descriptors is obtained using
-db_alloc. The tuple_descriptor reference count is set to 1.
-
-@begin(function)
-void
-@f[delete_tuple_descriptor](tuple_descriptor)
-@index(delete_tuple_descriptor)
-@end(function)
-
-Decrements the reference count, and if it goes to zero,
-releases the memory occupied by the tuple descriptor.
-
-@begin(function)
-TUPLE
-@f[create_tuple](tuple_descriptor)
-@index(create_tuple)
-@end(function)
-
-Allocates space for a new tuple, and initializes its description. The
-values of fields in the tuple are not initialized. String types are
-not distinguished in this operation; no space is allocated for the
-target of a string pointer. The reference count of the tuple
-descriptor is incremented.
-
-@begin(function)
-void
-@f[delete_tuple](tuple)
-@index[delete_tuple]
-TUPLE tuple;
-@end(function)
-
-Deallocates the space for the specified tuple. Neither the tuple descriptor
-nor any of the string data which may be referenced by the tuple is
-deleted. The reference count of the tuple descriptor is decremented,
-and if necessary, the descriptor is deleted too.
-
-@begin(function)
-void
-@f[initialize_tuple](tuple)
-@index[initialize_tuple]
-TUPLE tuple;
-@end(function)
-
-Sets each field in a tuple to its null value.
-
-@begin(function)
-void
-@f[null_tuple_strings](tuple)
-@index(null_tuple_strings)
-TUPLE tuple;
-@end(function)
-
-Reclaims the space for all fields in the tuple whose type is STRING_T.
-The value of the string itself is set to uninitialized. The sequence
-"null_tuple_strings(t); delete_tuple(t);" may be used to reclaim all
-the space used for a tuple containing string fields.
-
-@begin(function)
-RELATION
-@f[create_relation](tuple_descriptor);
-@index[create_relation]
-TUPLE_DESCRIPTOR tuple_descriptor;
-@end(function)
-
-Creates an empty relation. Tuples subsequently added to the relation
-must contain the fields described in the tuple descriptor at the time
-the relation is created. Not sure yet whether this will be checked.
-May just create a generalized relation in which each tuple is
-self_describing and the consistent case is viewed as a subset.
-The tuple descriptor reference count is incremented.
-
-
-@begin(function)
-int
-@f[delete_relation](rel);
-@index[delete_relation]
-RELATION rel;
-@end(function)
-
-Reclaims the space for the specified relation, which should not be
-used again by the caller. All tuples are deleted, along with any strings
-which have been allocated in the tuples. The caller must insure that the
-contents of any string fields are self consistent, i.e. if the string is
-non-null, then it must refer to data which was properly allocated by gdb.
-The tuple descriptor reference count is decremented, and if necessary,
-the descriptor is deleted.
-
-@begin(function)
-void
-@f[ADD_TUPLE_TO_RELATION ]@index[ADD_TUPLE_TO_RELATION](relation, tuple)
-
-void
-@f[ADD_TUPLE_AFTER_TUPLE] (relation, new_tuple,
-@index[ADD_TUPLE_AFTER_TUPLE]
- previous_tuple)
-
-RELATION relation;
-TUPLE tuple, new_tuple, previous_tuple;
-@end(function)
-
-These operations insert a new tuple into a relation. They do NOT
-allocate storage for the tuple, because there may be times when it is
-convenient to insert an existing tuple into a relation. These
-routines will probably be implemented as macros, hence the uppercase
-names. ADD_TUPLE_TO_RELATION places the new tuple after all those
-already in the relation. Each tuple may in in only one relation at a
-time. This routine may or may not check for conformity between
-the description of the tuple and the description of the relation. A
-given tuple may be in only one relation at a time; this is not
-checked. Note: to insert a tuple as the first one in the relation,
-provide the relation pointer as the previous tuple, e.g.
-ADD_TUPLE_AFTER_TUPLE(r, t, (TUPLE)r).
-
-@begin(function)
-TUPLE
-@f[REMOVE_TUPLE_FROM_RELATION](relation, tuple)
-@index[REMOVE_TUPLE_FROM_RELATION]
-RELATION relation;
-TUPLE tuple;
-@end(function)
-
-Removes the designated tuple from the relation (this function may
-be implemented to remove the tuple from whichever relation it is in,
-or it may check which relation it is in, or it may presume that the
-correct relation is specified. No checking need be done; caller
-is responsible for insuring that the tuple is indeed in the
-specified relation.) The tuple is not de-allocated, merely removed
-from the relation. It may subsequently be inserted in a different
-relation.
-
-
-@begin(function)
-RELATION
-@f[tuples_in_relation](rel);
-@index[tuples_in_relation]
-RELATION rel;
-@end(function)
-
-Returns a count of the tuples in the supplied relation.
-
-@begin(function)
-TUPLE_DESCRIPTOR
-@f[DESCRIPTOR_FROM_TUPLE](tuple)
-@index[DESCRIPTOR_FROM_TUPLE]
-
-TUPLE_DESCRIPTOR
-@f[DESCRIPTOR_FROM_RELATION](relation)
-
-@index[DESCRIPTOR_FROM_RELATION]
-TUPLE tuple;
-RELATION relation;
-@end(function)
-
-Given a tuple or relation, return the descriptor of its entries.
-
-@begin(function)
-int
-@f[field_index](tuple_descriptor, field_name)
-@index[field_index]
-TUPLE_DESCRIPTOR tuple_descriptor;
-string field_name;
-@end(function)
-
-Given a tuple descriptor and the string name of a field in the tuple,
-this routine returns the 0 relative index of the field in the tuple.
-If the named field is not in the tuple, -1 is returned.
-
-@begin(function)
-(?? *)
-@f[FIELD_FROM_TUPLE](tuple, field_index);
-@index[FIELD_FROM_TUPLE]
-TUPLE tuple;
-int field_index;
-string field_name;
-@end(function)
-
-Given the relative index of a field in a tuple (0 origin), return a
-pointer to the first byte of data in the field. See also @f[field_index].
-
-@begin(function)
-int
-@f[FIELD_OFFSET_IN_TUPLE](tuple_descriptor, field_index);
-@index[FIELD_OFFSET_IN_TUPLE]
-
-FIELD_TYPE
-@f[FIELD_TYPE_IN_TUPLE](tuple_descriptor, field_index);
-
-@index[FIELD_TYPE_IN_TUPLE]
-@end(function)
-
-Given the type descriptor for an entire tuple and the name of a field
-in the tuple, these routines return the byte offset, and the
-field_type of the named field in the tuple. Their results are
-undefined in the case where the field_index is negative or too large.
-Note, the offset returned is from the start of the tuple data, NOT from
-the start of the tuple itself. To address the field, the construction:
-(tup->data)+offset gives the true addresss of the first byte of the field.
-This may change in the future, since it seems to require an unnecessary
-knowledge of the tuple structure on the part of the programmer.
-
-@begin(function)
-TUPLE
-@f[FIRST_TUPLE_IN_RELATION](relation)
-@index[FIRST_TUPLE_IN_RELATION]
-
-TUPLE
-@f[NEXT_TUPLE_IN_RELATION](relation,tuple)
-@index[NEXT_TUPLE_IN_RELATION]
-
-TUPLE
-@f[PREV_TUPLE_IN_RELATION](relation,tuple)
-@index[PREV_TUPLE_IN_RELATION]
-
-RELATION relation;
-TUPLE tuple;
-@end(function)
-
-Returns tuples from a relation relative to the order in which they were
-inserted. NULL is returned for the successor of the last tuple or the
-predecessor of the first tuple in a relation.
-
-@begin(function)
-void
-@f[print_relation](name, relation)
-@index[print_relation]
-
-void
-@f[print_tuple](name, tuple)
-
-@index[print_tuple]
-
-void
-@f[print_tuple_descriptor](name, desc)
-@index[print_tuple_descriptor]
-
-string name;
-RELATION relation;
-TUPLE tuple;
-TUPLE_DESCRIPTOR desc;
-@end(function)
-
-These routines format the contents of the specified relation or tuple
-and write the results to the gdb logging file descriptor. The
-supplied name, an ordinary C null terminated string, is used to label
-the printed entry in the log. These routines are intended primarily
-for debugging.
-@end(functionsection)
-
-@chapter(Structured Data Transmission Services)
-@index(Data transmission)
-@index(Transmission of data)
-@index(Structured data transmission)
-@label(trans)
-
-These services are used to move structured data between communicating
-programs. To provide some context for this discussion, we summarize
-here the basic techniques used to create servers and clients, and
-introduce the use of OPERATIONS for handling asynchronous activities.
-This discussion partially duplicates that of Chapter
-@ref(serverclient), which is devoted to server/client management, and
-general techniques for managing asynchronous operations in GDB. The
-emphasis in this chapter is on transmission of structured data.
-
-@section(Background)
-
-Two styles of communicating program are supported: peer-to-peer, in which
-the two sides of the connection are considered to be symmetrical, and
-client/server, in which a server is pre-started, awaiting connections from
-one or more clients. The following types are used by the structured data
-transmission services:
-
-@begin(itemize)
-
-@begin(multiple)
-CONNECTION
-
-represents a communication path to some other program using
-GDB. Usually, this is a network communication path between
-programs on different machines. Connections may
-be closed explictly by issueing a "sever_connection", and they
-are implicitly closed when the process owning the
-connection exits.
-@end(multiple)
-
-@begin(multiple)
-OPERATION
-
-GDB is capable of managing asynchronous activities on its
-CONNECTIONS. When a program wishes to do asynchronous
-communication it creates an OPERATION to represent the state
-of the asynchronous activity. GDB provides functions and
-macros for manipulating OPERATIONS. For example,
-OP_DONE(operation) returns true iff the operation has
-completed.
-@end(multiple)
-@end(itemize)
-
-@section(Service and Network Addresses)
-
-In the current implementation, the address of a GDB program
-running on the network has the form:
-
-@begin(example)
- "hostname:servicename"
-@end(example)
-
-where hostname is the internet name of the host on which the program
-is running, and servicename is the name of the service as registered
-in @f[/etc/services]@index[/etc/services]. Services should be of type
-tcp. An alternate form of servicename is #portnumber, where
-portnumber is the integer number of the Berkeley port to
-be used for the service. Use of explicit port numbers is
-discouraged, except for testing. Examples:
-"myhost:dbserver" or "yourhost:#1234". All addresses are
-represented as null terminated strings in C.
-
-
-
-@section(Establishing Communication)
-
-As noted above, there are two general styles of communicating
-programs. Peer-to-peer communication is started by use of the
-following function. These services and other related routines are
-fully documented in Chapter @ref(serverclient). The discussion here
-is intended to introduce the GDB services which transmit structured
-data.
-
-@begin(functionsection)
-
-@begin(function)
-CONNECTION
-@f[start_peer_connection](service_address)
-@index[start_peer_connection]
-string service_address;
-@end(function)
-
-Creates a connection to a peer at the designated address (see service and
-network addresses above.) Either peer may start first. This routine will
-hang indefinitely waiting for its peer, unless a fatal error is
-encountered. There is currently no asynchronous form of this service.
-NULL is returned in case of an error. Messages describing some errors are
-written to stderr, but otherwise, there is currently no way for the program
-to determine why a failure occurred.
-@end(functionsection)
-
-Clients may request a connection to a server by using the following
-function:
-
-@begin(functionsection)
-@begin(function)
-CONNECTION
-@f[start_server_connection](service_address, parms)
-@index[start_server_connection]
-string service_address;
-string parms;
-@end(function)
-
-Rules are the same as for start_peer_connection except: (1) The server must be
-started and listening at the time the connection is attempted, or this
-request will fail. (2) The supplied parms are made available to the
-server, which may use them in deciding whether to accept the connection or
-how to process it. This request will generally not hang for very long, but
-it does set up a socket and do a limited amount of communication
-synchronously. Typical delays on a local network with no gateways are a
-fraction of a second. Note that GDB supports several styles of server
-(e.g. forking and non-forking), but clients connect to and communicate with
-all of them in the same manner.
-
-@end(functionsection)
-
-The techniques for creating servers are described in Chapter
-@ref(serverclient).
-
-@section(Synchronous Communication)
-
-GDB is capable of transmitting data of any of the types described in
-Chapter @ref(structdata). This means that the services below may be
-used to send data as simple as a single integer, or as complex as an
-entire relation with all of its tuples and fields. In all cases, data
-is automatically converted to the appropriate representation when it
-is transmitted between machines of different architecture.
-
-@begin(functionsection)
-
-@begin(function)
-int
-@f[send_object](con, obj_ptr, type_id)
-@index[send_object]
-CONNECTION con;
-char *obj_ptr;
-FIELD_TYPE type_id;
-@end(function)
-
-Synchronously transmits the specified data on the connection con. Obj_ptr
-must be the address of the object to be sent, and type_id must indicate the
-type of the object to be sent. The final status of the transmit operation
-is returned. In general, OP_SUCCESS indicates success, OP_CANCELLED
-indicates an error. OP_CANCELLED generally implies that the corresponding
-connection has broken and should be severed. Examples:
-
-@begin(example)
-rc = send_object(server, &my_int, INTEGER_T);
- /* send an integer */
-rc = send_object(peer, &tuple, TUPLE_T);
- /* send a tuple */
-rc = send_object(peer, &rel, RELATION_T);
- /* send a relation */
-@end(example)
-
-@begin(function)
-int
-@f[receive_object](con, obj_ptr, type_id)
-@index[receive_object]
-CONNECTION con;
-char *obj_ptr;
-FIELD_TYPE type_id;
-@end(function)
-
-Synchronously receives the specified data on the connection con. Obj_ptr
-must be the address of the object to be received, and type_id must indicate the
-type of the object. The final status of the receive operation
-is returned. In general, OP_SUCCESS indicates success, OP_CANCELLED
-indicates an error. OP_CANCELLED generally implies that the corresponding
-connection has broken and should be severed. Examples:
-
-@begin(example)
-rc = receive_object(server, &my_int, INTEGER_T);
- /* receive integer */
-rc = receive_object(peer, &tuple, TUPLE_T);
- /* receive a tuple */
-rc = receive_object(peer, &rel, RELATION_T);
- /* receive relation */
-@end(example)
-
-Note that receipt of certain structured objects implies that local memory
-is dynamically allocated to hold the newly received objects. The system
-defined types for which this is the case incude: TUPLE_T, RELATION_T,
-TUPLE_DESCRIPTOR_T, and STRING_T. TUPLE_DATA_T implies memory allocation
-only if the fields in the tuple require it.
-@end(functionsection)
-
-@section(Asynchronous communication)
-
-GDB provides a general architecture for executing asynchronous
-communication activities on a connection. Several asynchronous operations
-are provided with the system, and sophisticated users can write their own
-(it's a little tricky, but it can be done.) Most users will find the
-supplied operations to be sufficient.
-
-Each asynchronous activity is known as an operation. Programmers using the
-asynchronous capabilities of GDB must declare an OPERATION to keep track of
-the state of each asynchronous activity. For example:
-
-@begin(example)
- OPERATION send_op1, send_op2, receive_op;
-@end(example)
-
-declares 3 operations. Before an operation can be used it must be
-"created":
-
-@begin(example)
- send_op1 = create_operation();
-@end(example)
-
-The newly allocated 'operation' may then be used to track the state of an
-asynchronous activity, and if desired, it may later be reset and re-used
-without being re-created. These techniques lower the cost of doing
-repeated asynchronous activities. Many applications can create all their
-operations at start-up, resetting and re-using them as necessary.
-
-Each of the asynchronous routines described below takes an OPERATION as an
-argument. In each case, the supplied operation should be newly created or
-reset, and in no case may it be in use by another routine; each
-OPERATION tracks the state of one activity at a time. The simplest way to
-synchronize asynchronous activity is with the 'complete_operation'
-function:
-
-@begin(example)
- complete_operation@index[complete_operation](send_op1);
- complete_operation(send_op2);
-@end(example)
-
-Note that GDB is coded to maximize throughput and avoid deadlock when
-several activities are proceeding simultaneously. For example, the two
-calls shown above can be done in either order, since send_op2 progresses
-even while waiting for completion of send_op1. A simple way to do
-parallel communication on several connections is to start each of the
-activities and then wait for completion of all of them. Each progresses as
-fast as possible and as nearly in parallel as GDB can manage. GDB uses
-Berkeley 'select' to avoid spinning when work cannot proceed.
-
-In addition to 'complete_operation', several routines are provided which
-can wait for any of a list of operations to complete, and which can combine
-a GDB wait with the facilities of a Berkeley 'select' on fd's not
-controlled by GDB. These features are documented at the end of this
-chapter.
-
-Operations executed on a given half connection are executed in FIFO order,
-unless specifically indicated to the contrary. (A half connection refers
-to either the inbound or outbound stream of a given connection; send and
-receive requests are queued independently.) A synchronous request as
-executed only after the completion of all prior asyncrhonous requests on
-the same half connection.
-
-An operation which is attempted on a failed connection, or an operation
-which encounters a connection error during execution returns the status
-@f[OP_CANCELLED]. Severing a connection implicitly cancels any operations
-which are queued on that connection.
-
-GDB generally makes as much progress as it can whenever the
-communication layer is given control. For example, whenever a new
-operation is queued on a given connection, all connections are allowed
-to proceed as far as they can. When a complete_operation is issued,
-all activities proceed while awaiting completion of the specified
-operation. However, gdb only progresses when explictly given control;
-it does not use SIGIO@index[SIGIO]. For this reason, GDB programs
-should hang in a 'complete_operation' or in one of the forms of
-'op_select' when there is no work to be done, as this will allow GDB
-activities to proceed. If the user issues his/her own select, sleep
-or a wait, GDB activities may be delayed.
-
-@subsection(Asynchronous Data Transmission)
-@label(async)
-@index(Asynchronous data transmission)
-
-The following routines are similar to send_object and receive_object, but
-they do their work asynchronously.
-
-@begin(functionsection)
-@begin(function)
-int
-@f[start_sending_object](op, con, obj_ptr, type_id)
-@index[start_sending_object]
-OPERATION op;
-CONNECTION con;
-char *obj_ptr;
-FIELD_TYPE type_id;
-@end(function)
-
-Asynchronously transmits the specified data on the connection con.
-Obj_ptr must be the address of the object to be sent, and type_id must
-indicate the type of the object to be sent. The final status
-(OP_STATUS(OP)) of the transmit operation is not available until the
-operation 'op' completes. In general, OP_COMPLETE indicates success,
-OP_CANCELLED indicates an error. OP_CANCELLED generally implies that
-the corresponding connection has broken and should be severed.
-OP_RESULT of a successful transmission will be OP_SUCCESS.
-Example:
-
-@begin(example)
-op1 = create_operation();
-op2 = create_operation();
-op3 = create_operation();
-start_sending_object(op1, server, &my_int, INTEGER_T);
-start_sending_object(op2, peer, &tuple, TUPLE_T);
-start_sending_object(op3, peer, &rel, RELATION_T);
-compete_operation(op1);
-compete_operation(op2);
-compete_operation(op3);
-if (OP_STATUS(op1) == OP_CANCELLED ||
- OP_STATUS(op2) == OP_CANCELLED ||
- OP_STATUS(op2) == OP_CANCELLED)
- printf("Couldn't do it\n");
-@end(example)
-
-@begin(function)
-int
-@f[start_receiving_object](op, con, obj_ptr, type_id)
-@index[start_receiving_object]
-OPERATION op;
-CONNECTION con;
-char *obj_ptr;
-FIELD_TYPE type_id;
-@end(function)
-
-Asynchronously receives the specified data on the connection con.
-Obj_ptr must be the address of the object to be received, and type_id
-must indicate the type of the object. The final status of the receive
-operation is not available until the operation 'op' completes. In
-general, an OP_STATUS ofOP_COMPLETE indicates success, OP_CANCELLED
-indicates an error. OP_CANCELLED generally implies that the
-corresponding connection has broken and should be severed. OP_RESULT
-of a successful receipt will be OP_SUCCESS. Example:
-
-@begin(example)
-op1 = create_operation();
-op2 = create_operation();
-op3 = create_operation();
-start_receiving_object(op1, server, &my_int, INTEGER_T);
-start_receiving_object(op2, peer, &tuple, TUPLE_T);
-start_receiving_object(op3, peer, &rel, RELATION_T);
-compete_operation(op1);
-compete_operation(op2);
-compete_operation(op3);
-if (OP_STATUS(op1) == OP_CANCELLED ||
- OP_STATUS(op2) == OP_CANCELLED ||
- OP_STATUS(op2) == OP_CANCELLED)
- printf("Couldn't do it\n");
-@end(example)
-@end(functionsection)
-
-@chapter(Memory Management)
-@Index(Memory management)
-@Index(Storage allocation)
-
-The following routines are provided to encapsulate calls to memory
-management services for all of the libraries defined in this
-specification. Early implementations will just use malloc, but
-more sophisticated techniques might be used in the future.
-
-@begin(functionsection)
-@begin(function)
-(?? *)
-@f[db_alloc](bytes)
-@index[db_alloc]
-int bytes;
-@end(function)
-
-Allocates the number of bytes requested and returns the corresponding
-pointer. The allocated space is always word aligned.
-
-@begin(function)
-void
-@f[db_free](ptr, bytes)
-@index[db_free]
-(??) *p;
-int bytes;
-@end(function)
-
-De-allocates the memory pointed to by ptr. Some implementations by
-ignore the supplied size, requiring that ptr be a value obtained from
-db_alloc. Others may support more flexible pool management for
-which the size will be useful.
-@end(functionsection)
-
-@section(Overriding the default memory management routines)
-
-Two global function pointers are provided which point to the allocation
-and free routines which are actually called when db_alloc and db_free
-are used. A user of gdb may supply his or her own memory management
-routines by replacing the supplied pointers with pointers to new routines.
-The global variables to be changed are:
-
-@begin(format)
-@tabset(.75in, 2.5in)
-@\@f[gdb_amv]@index[gdb_amv]@\vector to the allocate memory routine
-
-@\@f[gdb_fmv]@index[gdb_fmv]@\vector to the free memory routine
-@end(format)
-
-The pointers should be to routines matching the specification for
-@f[db_alloc]@index[db_alloc] and @f[db_free]@index[db_free] above.
-The pointers should be changed prior to calling
-gdb_init@index[gdb_init] to insure that all memory allocated by gdb is
-obtained using the new services.
-
-@chapter(String Management)
-@Index(String management)
-
-These routines provide allocation and de-allocation services for
-STRING_T data. Like db_alloc and db_free, their
-primary raison d'etre is to centralize creation and deletion of
-strings.
-
-The only special TYPEDEF used for string management is @f[STRING].
-STRING describes a datum of GDB type STRING_T. This must contain
-sufficient information to record the location of the data
-(e.g. a pointer) and the length with which the data
-was allocated (which need not be the length of null
-terminated data actually contained in the field.)
-
-@section(Routines)
-
-@begin(functionsection)
-@begin(function)
-int
-@f[string_alloc](stringp,bytes);
-@index[string_alloc]
-STRING *stringp;
-int bytes;
-@end(function)
-
-Allocates memory to contain a string of the specified length
-and returns the associated string handle. The supplied length should
-include space for a terminating null if one is to be used, but these
-libraries make no such presumption. A null is stored in the first byte
-of the returned data area.
-
-@begin(function)
-void
-@f[string_free](stringp);
-@index[string_free]
-STRING *stringp;
-
-De-allocates the supplied string. The length given must match that used
-to allocate the space with string_alloc (which need not be the same as
-the length of the null terminated data currently in the string.)
-@end(function)
-
-@begin(function)
-(char *)
-@f[STRING_DATA](string)
-@index[STRING_DATA]
-STRING string;
-@end(function)
-
-A macro which returns the pointer to the first byte of the referenced
-string. NULL is returned for an uninitialized string.
-
-@begin(function)
-int
-@f[MAX_STRING_SIZE](string)
-@index[MAX_STRING_SIZE]
-STRING string;
-@end(function)
-
-A macro which returns the total number of bytes available in the string
-(which may be greater than the length of any null terminated data which
-happens to reside there at the moment.)
-@end(functionsection)
-
-@Chapter(Server and Communications Management)
-@label(serverclient)
-@Index(Server and communication management)
-
-These are routines whose purpose is to facilitate the creation of shared,
-centralized, or peer-to-peer network services in a Berkeley Unix system.
-At the server, they manage the creation of sub-processes for a given client
-or sub-service, and the allocation and use of sockets for communication to
-the clients. A corresponding set of routines is used by clients to request
-and maintain sessions with various services.
-
-The following types are used for server and communications management:
-
-@begin(table)
-@caption(Types used in Server and Communications Management)
-@tabset(.75in, 2.5in)
-@\@f[CONNECTION]@Index[CONNECTION]@\Represents an ongoing full duplex
-@\@\connection to a central service.
-
-@\@f[CONN_STATUS]@Index[CONN_STATUS]@\a returned value indicating the
-@\@\disposition of anattempted
-@\@\library call.
-
-@\@f[OPERATION, OPERATION_DATA]@Index[OPERATION]@Index[OPERATION_DATA]
-@\@\Represents an ongoing or completed
-@\@\operation requested of
-@\@\the server or transport system.
-@\@\OPERATION is a handle
-@\@\on OPERATION_DATA.
-
-@\@f[LIST_OF_OPERATIONS]@Index[LIST_OF_OPERATIONS]
-@\@\A collection of OPERATIONs.
-
-@\@f[OP_STATUS]@Index[OP_STATUS]@\A return value describing the
-@\@\progress of an asynchronous
-@\@\operation.
-@end(table)
-
-@section(Host and service names)
-@Index(Host names)
-@Index(Service names)
-@Index(Naming hosts)
-@Index(Naming services)
-@Index(Naming communicating programs)
-
-In the current implementation, the address of a GDB program running on the
-network has the form:
-
-@begin(example)
- "hostname:servicename"
-@end(example)
-
-where hostname is the internet name of the host on which the program
-is running, and servicename is the name of the service as registered
-in @f[/etc/services]@index[/etc/services]. Services should be of type
-tcp. An alternate form of servicename is #portnumber, where
-portnumber is the integer number of the Berkeley port to be used for
-the service. Use of explicit port numbers is discouraged, except for
-testing. Examples: "myhost:dbserver" or "yourhost:#1234". All
-addresses are represented as null terminated strings in C.
-
-The form of GDB addresses may evolve over time. For example, host names
-may disappear when better nameservers become available.
-
-@section(Routines for Use at Client)
-
-@begin(functionsection)
-@begin(function)
-CONNECTION
-@f[start_server_connection](server_id, parms)
-@index[start_server_connection]
-string server_id;
-string parms;
-@end(function)
-
-Sets up a connection to a server process at the server site. server_id
-identifies the service; its form is specified above. This routine
-allocates a local socket and negotiates with the server to bind it to a
-socket owned by the appropriate server process. The parms are made
-available to the server.
-
-@begin(function)
-CONNECTION
-@f[start_peer_connection](peer_id)
-@index[start_peer_connection]
-string peer_id;
-@end(function)
-
-Sets up a connection to a peer process, which may be at a remote site.
-peer_id identifies the process to which the connection is to be made; its
-form is specified above. This routine allocates a
-local socket and negotiates to bind to a socket owned by the appropriate
-peer process. Returns NULL if the connection could not be started.
-Right now, there is no way to tell why the connection attempt failed. This
-should be fixed in subsequent versions of the spec.
-
-@begin(function)
-CONN_STATUS
-@f[connection_status](connection)
-@index[connection_status]
-CONNECTION connection;
-@end(function)
-
-Returns the status of the indicated connection. Possible return values are:
-CONN_STOPPED (never started or severed by user), CON_UP (currentfly usable),
-CON_STARTING (transient state on way up), CON_STOPPING(transient state on
-way down--user is expected to check connection_errno and sever the connection.)
-
-@begin(function)
-int
-@f[connection_errno](connection)
-@index[connection_errno]
-CONNECTION connection;
-@end(function)
-
-Returns the Unix errno which resulted in this connection being stopped.
-While errno may be queried for any connection, it's value is meaningful
-only for a connection in the CON_STOPPING state.
-
-@begin(function)
-int
-@f[connection_perror](connection, msg)
-@index[connection_perror]
-CONNECTION connection;
-char *msg;
-@end(function)
-
-Does the equivalent of the Unix perror library routine except (1) the
-value of errno is that which caused this connection to stop and (2)
-the error message is written to gdb_log, which may or may not be
-stderr. This routine does nothing of connection is NULL or if
-connection_status(connection) != CON_STOPPING.
-
-@begin(function)
-CONNECTION
-@f[sever_connection](connection)
-@index[sever_connection]
-CONNECTION connection;
-@end(function)
-
-Applications should use this routine to terminate a connection. It may be
-called on any active or stopping connection. The connection is terminated,
-all pending operations are completed or (more likely) cancelled, and the
-associated descriptor is released. This routine always returns NULL. It
-is the applcation's responsibility to NULL its own connection variable, which
-may conveniently be done with a construction of the form:
-
-@begin(example)
- peer = sever_connection(peer);
-@end(example)
-
-which has the effect of terminating the peer connection and safely nulling
-the applications pointer. This is important because connection
-descriptors are re-used when new connections are started. The old value
-in a connection variable should NEVER be used after a sever. In particular,
-the construction:
-
-@begin(example)
- connection_status(severed_connection)
-@end(example)
-
-is safe only when the application can insure that no new connections have
-been started since the sever.
-
-Sever_connection is the inverse of start_peer_connection and
-start_server_connection. It informs the foreign process that the client is
-done with the connection, and releases all resources associated with the
-connection.
-@end(functionsection)
-
-@section(Routines for Use in Forking Servers)
-@Index(Forking servers)
-@Index(Servers, forking)
-
-@begin(functionsection)
-@begin(function)
-CONNECTION
-@f[create_forking_server](servicename, validate-rtn)
-@index[create_forking_server]
-string servicename;
-int *validate-rtn();
-@end(function)
-
-Turns the current process into a forking style server. The validate
-routine, if supplied, is called before a connection is accepted to allow
-the server to screen clients. (Interface to be documented later.) GDB
-handles all management of connections, forking, child reaping, and the
-like. No return is ever made in the parent, which remains under the
-control of GDB forever. GDB will take care of terminating children
-whenever the parent is killed. As new clients are received, GDB does the
-necessary creation of connections and returns in the child processes. The
-connection returned by this call is to be used in communicating with the
-client. Because a true fork is done, global data may be inherited from the
-master process. In addition, the global variable gdb_client_tuple is
-initialized to contain four string fields named "server_id", "parms",
-"host", and "user".
-The first is the string name of the server as passed by the client, and the
-second is the parms string supplied by the client. The last two are the
-string names of the client host and user, as best GDB could determine them.
-In some cases, GDB may supply "????" for either of these fields if
-the true values cannot be determined. The child
-may use the full facilities of GDB to communicate with its client, and when
-communication is finished, the child should sever its connection (not
-stricly required, but it's a good thing to do) and exit. The server will
-appropriately reap the child process. Note that the servicename supplied
-to this routine is NOT a full network address, since the host is implicitly
-local. See service addressing discussion above.
-
-As an aid to debugging, the GDB_NOFORK@index[GDB_NOFORK] flag may be
-set to prevent this routine from forking when a client arrives. See
-@f[gdb_debug]@index[gdb_debug].
-@end(functionsection)
-
-@section(Routines for Use in Non-Forking Servers)
-@begin(functionsection)
-@begin(function)
-int
-@f[create_listening_connection](servicename)
-@index[create_listening_connection]
-string servicename;
-@end(function)
-
-Creates a special 'listening' connection on which start_accepting_client
-operations (see below) may be queued. Using these two facilities together,
-a server may asynchronously wait for and acquire connections to new clients
-while continuing to service existing clients. Servicename has the same
-forms as for create_forking_server.
-
-@begin(function)
-int
-@f[start_accepting_client_connection](listencon, op,
- &clientcon, &otherside
-@index[start_accepting_client_connection]
- &othersize, &client_tuple);
-CONNECTION listencon;
-OPERATION op;
-CONNECTION clientcon;
-struct sockaddr_in otherside;
-int othersize;
-TUPLE client_tuple;
-@end(function)
-
-Begins the process of asynchronously acquiring a tentative connection from
-a client. When this operation completes successfully, clientcon contains
-the connection to the new client, and client_tuple contains the request tuple
-(which includes the address requested and the client supplied parms).
-The otherside and othersize fields are provided for those programmers who
-need access to the Berkeley supplied address of the client. These are the
-same as the values returned by a Berkeley 'accept' call. Most callers will
-not need these, but it is required that appropriate parameters be supplied.
-otherside may be a character array of length 100 (less will probably do)
-and othersize should be an integer variable set to sizeof(otherside). The
-actual size of the client address is returned, a la Berkeley accept.
-
-@begin(function)
-int
-@f[start_replying_to_client](op, con, disposition, newaddr, parms)
-@index[start_replying_to_client]
-OPERATION op;
-CONNECTION con;
-int disposition;
-string newaddr;
-string parms;
-@end(function)
-
-The first thing a server must do after successful competion of a
-start_accepting_client is to reply to the client, indicating whether its
-request is being accepted. The reply is sent on the new client connection
-using start_replying_to_client. Disposition takes one of three values:
-GDB_ACCEPTED, GDB_REFUSED, or GDB_FORWARDED. In most cases, newaddr and
-parms should be null strings (""). GDB_ACCEPTED completes the process of
-creating the connection from client to server. Once the start_replying
-operation is complete, the client connection is available for data
-transmission. GDB_REFUSED indicates that the server declines to talk to
-the client, and if desired, it may indicate a reason in the parms string.
-The server should wait for completion of the start_replying_to_client, then
-sever the client connection. GDB_FORWARDED is used to tell the client that
-the service being requested has moved, and to suggest a new address for the
-client to try. The suggested address should be supplied in newaddr in the
-hostname:servicename form shown above. The 'start_server_connection' which
-the client has done takes care of the forwarding request automatically and
-transparently.
-@end(functionsection)
-
-@section(Routines for Manipulating Operations)
-
-@begin(functionsection)
-@begin(function)
-OPERATION
-@f[create_operation]();
-@index[create_operation]
-@end(function)
-
-Allocates an operation descriptor and returns a pointer to it. Note
-that the type OPERATION is just a handle, and the system assumes that
-operation data structures remain allocated for as long as they are
-needed. Knowledgeable application programmers can allocate their own
-structures of type OPERATION_DATA and use the macro OPERATION_FROM_DATA
-to create the necessary OPERATIONS efficiently. create_operation is
-appropriate where dynamic allocation is desired or for convenience in
-simple applications.
-
-@begin(function)
-int
-@f[delete_operation](operation)
-@index[delete_operation]
-OPERATION operation;
-@end(function)
-
-Releases space for the specified operation;
-
-@begin(function)
-OPERATION
-@f[OPERATION_FROM_DATA](op_data)
-@index[OPERATION_FROM_DATA]
-OPERATION_DATA op_data;
-@end(function)
-
-Given the operation data, this macro returns the corresponding OPERATION
-handle.
-
-@begin(function)
-Bool
-@f[OP_DONE](operation)
-@index[OP_DONE]
-OPERATION operation;
-@end(function)
-
-True iff the operation is OP_COMPLETE or OP_CANCELLED.
-
-@begin(function)
-int
-@f[OP_TAG](operation)
-@index[OP_TAG]
-@end(function)
-
-Every operation initiated by a given process has a unique integer tag
-which identifies it. This macro returns the tag for a given operation.
-Tags are re-assigned each time the queue_op routine is called.
-Applications programmers will seldom have need to query or manipulate the
-tags directly; they are an internal mechanism used primarily to track
-operations which are being cancelled prematurely. Applications
-programmers may occasionally find them useful as a means of generating
-operation identifiers which are guaranteed unique over the life of the
-process.
-
-@begin(function)
-OP_STATUS
-@f[complete_operation](pending_operation)
-@index[complete_operation]
-OPERATION pending_operation;
-@end(function)
-
-Waits unconditionally for completion of the specified operation. If
-several operations are outstanding, then waiting for a given operation
-implies a wait for all of its predecessors. There is a separate queue
-of operations for each connection, so the term predecessor is defined
-only with respect to a given connection. Operations pending on other
-connections DO proceed while awaiting completion of the specified
-operation. This means that when waiting for several operations to complete,
-the calls to complete_operation may be done in any order without affecting
-the performance of the system.
-
-@begin(function)
-LIST_OF_OPERATIONS
-@f[create_list_of_operations](n, op1, op2, ..... opn)
-@index[create_list_of_operations]
-int n;
-OPERATION op1, op2 ... opn)
-@end(function)
-
-Creates and returns a list containing the supplied operation
-descriptors. This list may then be passed to the op_select routines.
-
-@begin(function)
-void
-@f[delete_list_of_operations](op_list)
-@index[delete_list_of_operations]
-LIST_OF_OPERATIONS op_list;
-@end(function)
-
-Reclaims the space for the supplied list of operations.
-
-@begin(function)
-int
-@f[op_select_any](list_of_pending_operations, nfds,
-@index[op_select_any]
- &readfds, &writefds,
- &exceptfds, timeout)
-LIST_OF_OPERATIONS list_of_pending_operations;
-int nfds;
-fd_set readfds, writefds, exceptfds;
-struct timeval *timeout;
-@end(function)
-
-This operation has exactly the same semantics as the select system
-call, but in addition, it allows pending OPERATIONs to
-progress. This operation hangs in a select. If activity is discovered
-on any of the sockets controlled by the GDB library, then the
-corresponding input is read and appropriate processing is done. If any
-of the listed pending_operations completes or terminates due to error,
-op_select returns. op_select also returns immediately if any of the
-supplied operations is already complete.
-
-If activity is detected on any of the file descriptors supplied by the
-user, then a count and bit fields are returned just as for select.
-(Activity on database sockets is never reflected in either count or
-bitfields.) Timeout causes a return, as with select. Upon return, the
-program must check for competion or termination of any of the listed
-operations, for activity on the selected file descriptors, and for
-timeouts, if requested, since any or all of these may be reported
-together.
-
-Return values are (>0) same as for regular select, (0) timeout, (-1) only
-completion of one of the specified operations was detected.
-
-@begin(function)
-int
-@f[op_select_all](list_of_pending_operations, nfds,
-@index[op_select_all]
- &readfds, &writefds,
- &exceptfds, timeout)
-LIST_OF_OPERATIONS list_of_pending_operations;
-int nfds;
-fd_set readfds, writefds, exceptfds;
-struct timeval *timeout;
-@end(function)
-
-Same as op_select_any, except that (-1) is returned only when ALL of the
-specified operations in the list have completed. Values (>=0) are returned
-for timeout or activity on FD's specified in the masks.
-
-@begin(function)
-int
-@f[reset_operation](operation)
-@index[reset_operation]
-OPERATION operation;
-@end(function)
-
-Sets the state of an operation back to OP_NOT_STARTED. This is
-particularly useful for operations in a list passed to op_select_any.
-@Index(op_select_any)
-Op_select_any drops through immediately if any operation in the list
-is marked complete. Once the operation is reset, it may be left in
-the list without detrimental effect. reset_operation cannot be used
-on an operation which is queued or running.
-
-@begin(function)
-OP_STATUS
-@f[cancel_operation](operation)
-@index[cancel_operation]
-OPERATION operation;
-@end(function)
-
-Attempts to prematurely terminate execution of the specified operation. If
-the operation is queued, then it is removed from the queue of the
-connection on which it resides. If it is at the head of the queue and
-running, then an attempt is made to signal the other end of the connection
-to terminate execution. If the operation has already completed,
-successfully or otherwise, then CANCEL_OPERATION does nothing. In general,
-the application cannot assume that the cancellation takes effect
-immediately, or that it will always succeed. The status of the operation
-must be checked to ascertain its true state, and if necessary, the
-application must wait for the operation to complete or terminate
-prematurely. This call always returns immediately. It does not wait to
-see whether the operation was indeed terminated prematurely. There is no
-guarantee of prompt termination for any particular operation, since some
-may be uninterruptible. The intent is to make a best effort. Returned
-status covers only errors in the cancel operation itself. The caller must
-still use 'complete_ operation' or 'op_select' to check for completion or
-successful cancellation of the operation.
-
-@begin(function)
-int
-@f[OP_STATUS](operation);
-@index[OP_STATUS]
-OPERATION operation;
-@end(function)
-
-Returns a code describing the progress or completion of the operation.
-Returned values are OP_RUNNING, OP_COMPLETE or OP_COULDNT_START. In the
-case of OP_COMPLETE OP_CANCELLING, OP_CANCELLED, or OP_COULDNT_START, the
-operation_result library routine may be used to get information about the
-success or failure of the actual operation. Note that OP_COMPLETE applies
-to either success or failure, it merely implies that no further processing
-remains to be done on the requested operation. OP_CANCELLED implies that
-the operation was indeed terminated prematurely by a cancellation request.
-In this case, OP_RESULT is not valid.
-
-@begin(function)
-int
-@f[OP_RESULT](operation);
-@index[OP_RESULT]
-OPERATION operation;
-@end(function)
-
-Returns detailed error information from the previous database or other
-operation request. The possible result codes depend on the particular
-operation being performed, and they are documented along with the
-operations themselves. The operation result is available ONLY when
-the operation is OP_COMPLETE. Attempts to get results at other times
-may result in fatal errors.
-@end(functionsection)
-
-@section(Creating new asynchronous operations)
-
-In addition to operations like start_sending_object, which are
-supplied with GDB, it is possible for users to create their own. This
-is a tricky business, because Unix lacks any notion of lightweight
-process. Until proper instructions are added to the GDB Users' Guide,
-the best way to learn is to look at the source code for existing
-operations provided with GDB.
-
-The general idea is that @f[initialize_operation] is called to fill in
-the operation descriptor with enough state to drive the operation, and
-then @f[queue_operation] is used to queue the descriptor on the
-appropriate half connection. When the operation reaches the head of
-the queue, GDB automatically invokes the first routine of the
-operation, which was specified during initialization. This routine
-proceeds as far as it can, updates its own state in the operation
-descriptor (and an operation specific argument appendage), and
-designates a new routine to receive control when further progress can
-be made. The following routines are among those used in creating new
-operations:
-
-@begin(functionsection)
-@begin(function)
-int
-@f[initialize_operation](operation, init_function, arg,
-cancel_function)
-@index[initialize_operation]
-OPERATION operation;
-int (*init_function)()
-char *arg;
-int (*cancel_function)()
-@end(function)
-
-Initializes the supplied operation with pointers to the supplied
-initialization function with the supplied argument. The initialization
-routine will be called when and if the operation reaches the head of the
-operation queue for some connection, and it will be supplied the argument
-arg. The cancel function, if non-null, will be called with the supplied
-argument if for any reason the operation is to be canceled. It can do
-things like freeing the space for the argument or other associated cleanup.
-If cancel_function is NULL, then no special processing is done during
-cancellation. The operation is put into the state OP_NOT_STARTED.
-
-@begin(function)
-OP_STATUS
-@f[queue_operation](con, direction, operation)
-@index[queue_operation]
-CONNECTION con;
-int direction;<= values are CON_INPUT
- and CON_OUTPUT
-OPERATION operation;
-@end(function)
-
-Takes the supplied operation and queues it for execution on the specified
-half connection, assigning a new unique tag for the operation. The
-supplied operation must be in the state OP_NOT_STARTED (this may or may not
-be checked.) If possible, execution actually begins, and in some cases,
-execution may complete by the time queue_operation returns. Status returned
-reflects the latest known state of the operation. Transmission on all
-connections may progress as a result of this operation. If the connection
-is not currently operating, then it is marked as severed. NOTE: queue
-operation is used only by programmers creating their own asynchronous
-services. It should NOT be called by USERS of asynchronous services.
-@end(functionsection)
-@chapter(Miscellaneous System Services)
-@Index(Miscellaneous services)
-@begin(functionsection)
-@begin(function)
-void
-@f[gdb_debug](flag)
-@index[gdb_debug]
-int flag;
-@end(function)
-
-Toggles the specified debugging flag. Flags currently supported are:
-@begin(itemize)
-@begin(multiple)
-@end(multiple)
-GDB_LOG@index[GDB_LOG flag]
-
-When set, this flag tells gdb to write a variety of debugging information on
-the file gdb_log. By default, this file is set to @f[stderr]@index[stderr],
-but users may put their own stream pointers in gdb_log. The debugging
-information is very detailed. It is intended primarily for those with
-detailed knowledge of GDB's inner workings.
-@begin(multiple)
-GDB_NOFORK@index[GDB_NOFORK flag]
-
-Tells GDB not to fork when a client arrives for a forking server.
-When this is set, the server accepts only a single client, and terminates
-when the client severs its connection. This is useful for debugging servers
-with dbx@index[dbx] or other debuggers which are incapable of handling
-forking programs.
-@end(multiple)
-@end(itemize)
-Warning: the interface to this routine is likely to change in subsequent
-releases of GDB.
-
-@end(functionsection)
-
-@Appendix(Revisions to this document)
-
-@begin(table)
-@caption(Library Reference Manual Revisions)
-@tabset(.75in, 2in)
-@\5/26/86@\Base document created
-
-@\6/23/86@\Re-define database operations for consistency
-@\@\with protocol.specification.
-
-@\7/10@\Add reset_operation
-
-@\7/18@\Added some notes in sever_connection on
-@\@\questions relating to maintenenance of
-@\@\an out of band control stream.
-@\@\Also, added requirement to call gdb_init.
-
-@\7/25@\Considerations for re-queueing operations
-@\@\Changed names on OP_STATUS, OP_RESULT
-
-@\8/27@\General cleanup to match current state
-@\@\of implemented system
-
-@\9/2@\Correct documentation of string routines
-@\@\and show requirement for stdio.h
-
-@\9/10@\Corrections to start_db_operation and
-@\@\start_db_query
-
-@\9/17@\Added information on printing
-
-@\11/11@\Added gdb_debug and associated flags.
-
-@\11/19@\Added DB_STATUS and associated return codes.
-@\@\Fixed parm list to db_query.
-
-@\12/31@\Added host and user fields to client tuple
-
-@\1/9/87@\Fixed return values on send/receive object
-@end(table)
+++ /dev/null
-/*
- * $Source$
- * $Header$
- */
-
-#ifndef lint
-static char *rcsid_samp1_c = "$Header$";
-#endif lint
-
-/*
- * sample 1
- *
- * A simple database query program.
- *
- * This sample program illustrates the use of the 'client library'
- * for remote access to a relational database. This version of
- * the sample is about the simplest possible. It accesses only
- * a single database, it does the access syncrhonously, and it uses
- * only system defined types. Many real applications will fit this
- * model.
- *
- * For purposes of illustration, this program accesses a database
- * of parts. For each part where the value of current inventory,
- * defined as number on hand times cost per unit, exceeds $1000,
- * this routine prints the description of the part, a code number,
- * the manufacturer, the cost, and the number on hand.
- *
- * Because this program accesses the database using the client
- * library, it may be run from any Berkeley Unix system on the internet.
- * Necessary data conversions are done automatically if the database
- * itself happens to live on an incompatible machine.
- *
- * Author: Noah Mendelsohn
- */
-
-#include <stdio.h>
-#include "gdb.h"
-
-int
-main(argc, argv)
-int argc;
-char *argv[];
-{
-
-\f /************************************************************
- * DECLARATIONS *
- ************************************************************/
-
- /*
- * Declare the names of fields to be retrieved and their types
- */
-
- int field_count = 5;
- char *field_names[] = {"desc",
- "code",
- "man",
- "cost",
- "count"};
- FIELD_TYPE field_types[] = {STRING_T, /* desc */
- INTEGER_T, /* code */
- STRING_T, /* man */
- FLOAT_T, /* cost */
- INTEGER_T}; /* count */
-
- /*
- * The following defines are for convenience in addressing
- * the fields.
- */
-
-#define DESC 0
-#define CODE 1
-#define MAN 2
-#define COST 3
-#define COUNT 4
-
- /*
- * Declare the relation and related data structures for
- * storing the retrieved data.
- */
-
- TUPLE_DESCRIPTOR tuple_desc;
- RELATION retrieved_data;
-
- /*
- * Declare a handle to identify our session with the database
- * server.
- */
-
- DATABASE parts_data;
-
- /*
- * Declarations for misc. variables
- */
-
- TUPLE t; /* next tuple to print */
- int rc; /* A return code */
-
-\f /************************************************************
- * EXECUTION BEGINS HERE *
- ************************************************************/
-
- /*
- * Open a connection to the database - identify session as parts_data
- */
-
- if (accessdb("partsdata@hostname", &parts_data) != DB_SUCCESS) {
- printf("Cannot connect to parts database--giving up\n");
- return;
- }
-
- /*
- * Build the descriptor describing the layout of the tuples
- * to be retrieved, and create an empty relation into which
- * the retrieval will be done.
- */
-
- tuple_desc = create_tuple_descriptor(field_count, field_names,
- field_types);
- retrieved_data = create_relation(tuple_desc);
-
- /*
- * Do the query for parts with inventory over $1000
- * Put results in the relation named retrieved_data.
- */
-
- rc = db_query(parts_data, retrieved_data,
- "(>*desc*< = parts.desc,
- >*code*< = parts.code,
- >*man*< = parts.man,
- >*cost*< = parts.cost,
- >*count*< = parts.count)
- where (parts.count * parts.cost > 1000.00)");
-
- if (rc != DB_SUCCESS) {
- printf("Error during retrieval--giving up\n");
- terminatedb(parts_data);
- return;
- }
-
- /*
- * Print out the results
- */
-
- for (t = FIRST_TUPLE_IN_RELATION(retrieved_data); t!= NULL;
- t = NEXT_TUPLE_IN_RELATION(retrieved_data))
- print_a_line(t);
-
- printf("\nEnd of Report.\n\n");
-
- /*
- * Clean up and leave
- */
-
- terminatedb(parts_data);
- return;
-}
-
-\f/*
- * print_a_line
- *
- * Given a tuple with parts data, print it on standard output.
- * NOTE: for clarity, we've left out the casts which should be
- * done on the pointers returned from FIELD_FROM_TUPLE.
- */
-int
-print_a_line(tup)
-TUPLE tup;
-{
- printf("desc=%s ", *(FIELD_FROM_TUPLE(tup, DESC)));
- printf("code=%d ", *(FIELD_FROM_TUPLE(tup, CODE)));
- printf("manufacturer=%s ",*(FIELD_FROM_TUPLE(tup, MAN)));
- printf("cost=%f ", *(FIELD_FROM_TUPLE(tup, COST)));
- printf("count=%d\n", *(FIELD_FROM_TUPLE(tup, COUNT)));
-}
+++ /dev/null
-/*
- * $Source$
- * $Header$
- */
-
-#ifndef lint
-static char *rcsid_samp1a_c = "$Header$";
-#endif lint
-
-#define terminatedb(x) /* nothin doin yet */
-/*
- * sample 1
- *
- * A simple database query program.
- *
- * This sample program illustrates the use of the 'client library'
- * for remote access to a relational database. This version of
- * the sample is about the simplest possible. It accesses only
- * a single database, it does the access syncrhonously, and it uses
- * only system defined types. Many real applications will fit this
- * model.
- *
- * For purposes of illustration, this program accesses a database
- * of parts. For each part where the value of current inventory,
- * defined as number on hand times cost per unit, exceeds $1000,
- * this routine prints the description of the part, a code number,
- * the manufacturer, the cost, and the number on hand.
- *
- * Because this program accesses the database using the client
- * library, it may be run from any Berkeley Unix system on the internet.
- * Necessary data conversions are done automatically if the database
- * itself happens to live on an incompatible machine.
- *
- * Author: Noah Mendelsohn
- */
-
-#include <stdio.h>
-#include "gdb.h"
-
-char *field_names[] = {"desc",
- "code",
- "man",
- "cost",
- "cnt"};
-FIELD_TYPE field_types[] = {STRING_T, /* desc */
- INTEGER_T, /* code */
- STRING_T, /* man */
- REAL_T, /* cost */
- INTEGER_T}; /* count */
-
-int
-main(argc, argv)
-int argc;
-char *argv[];
-{
-
-\f /************************************************************
- * DECLARATIONS *
- ************************************************************/
-
- /*
- * Declare the names of fields to be retrieved and their types
- */
-
- int field_count = 5;
-
-
- /*
- * The following defines are for convenience in addressing
- * the fields.
- */
-
-#define DESC 0
-#define CODE 1
-#define MAN 2
-#define COST 3
-#define CNT 4
-
- /*
- * Declare the relation and related data structures for
- * storing the retrieved data.
- */
-
- TUPLE_DESCRIPTOR tuple_desc;
- RELATION retrieved_data;
-
- /*
- * Declare a handle to identify our session with the database
- * server.
- */
-
- DATABASE parts_data;
-
- /*
- * Declarations for misc. variables
- */
-
- TUPLE t; /* next tuple to print */
- int rc; /* A return code */
-
-\f /************************************************************
- * EXECUTION BEGINS HERE *
- ************************************************************/
- /*
- * Initialize gdb
- */
- gdb_init();
-
- /*
- * Open a connection to the database - identify session as parts_data
- */
-
- if (access_db("noah@menelaus", &parts_data) != DB_OPEN) {
- printf("Cannot connect to parts database--giving up\n");
- return;
- }
-
- /*
- * Build the descriptor describing the layout of the tuples
- * to be retrieved, and create an empty relation into which
- * the retrieval will be done.
- */
-
- tuple_desc = create_tuple_descriptor(field_count, field_names,
- field_types);
- retrieved_data = create_relation(tuple_desc);
-
- /*
- * Do the query for parts with inventory over $1000
- * Put results in the relation named retrieved_data.
- */
-
- rc = db_query(parts_data, retrieved_data,
- "(>*desc*< = parts.desc,>*code*< = parts.code,>*man*< = parts.man,>*cost*< = parts.cost,>*cnt*< = parts.cnt) where (parts.cnt * parts.cost > 1000.00)");
-
- if (rc != OP_SUCCESS) {
- printf("Error during retrieval--giving up\n");
- terminatedb(parts_data);
- return;
- }
-
- /*
- * Print out the results
- */
-
- for (t = FIRST_TUPLE_IN_RELATION(retrieved_data); t!= NULL;
- t = NEXT_TUPLE_IN_RELATION(retrieved_data,t))
- print_a_line(t);
-
- printf("\nEnd of Report.\n\n");
-
- /*
- * Clean up and leave
- */
-
- terminatedb(parts_data);
- return;
-}
-
-\f/*
- * print_a_line
- *
- * Given a tuple with parts data, print it on standard output.
- * NOTE: for clarity, we've left out the casts which should be
- * done on the pointers returned from FIELD_FROM_TUPLE.
- */
-int
-print_a_line(tup)
-TUPLE tup;
-{
- printf("desc=%s ", STRING_DATA(*(STRING *)(FIELD_FROM_TUPLE(tup, DESC))));
- printf("code=%d ", *(int *)(FIELD_FROM_TUPLE(tup, CODE)));
- printf("manufacturer=%s ",STRING_DATA(*(STRING *)(FIELD_FROM_TUPLE(tup, MAN))));
- printf("cost=%f ", *(double *)(FIELD_FROM_TUPLE(tup, COST)));
- printf("count=%d\n", *(int *)(FIELD_FROM_TUPLE(tup, CNT)));
-}
+++ /dev/null
-@device(PostScript)
-@make(slides)
-@style(justification off)
-@style(FontFamily "TimesRoman")
-@modify(text, spread 1cm)
-@modify(heading, below 1.25in)
-@blankspace(2 in)
-@begin(center)
-@begin(majorheading)
-GDB:
-
-Global Databases on High Speed Networks
-@end(majorheading)
-
-@heading(Noah Mendelsohn)
-
-@subheading(IBM T.J. Watson Research Center@*and@*MIT Project Athena)
-
-@blankspace(1.8 in)
-@value(Date)
-@end(center)
-@newpage
-@Heading(Problems)
-@begin(itemize)
-There are few network applications at Athena
-
-Network applications are difficult to implement
-
-Heterogeneous hardware complicates the problem
-
-Synchronization and performance issues are frequently misunderstood
-@end(itemize)
-@newpage
-@Heading(GDB Approach@*A C Library to Support:)
-@begin(Itemize)
-Shared, global relational databases
-
-Simplified implementation of servers and clients
-
-Explicit support for single process Unix servers
-@end(itemize)
-@newpage
-@heading(Potential Appplications of GDB)
-@begin(itemize)
-Subject specific databases
-
-Calendars
-
-Cooperative development environments
-
-Access to network resources (e.g. videodisc)
-
-Interactive game-like environments
-
-Discussion systems (conferences)
-@end(itemize)
-@newpage
-@Heading(GDB Database Services)
-@begin(itemize)
-Full services of RTI Ingres relational databases
-
-Available from any GDB client on internet
-
-Machine dependencies hidden
-
-Synchronous and asynchronous flavors of most services
-
-Based on GDB data structuring facilities
-@end(itemize)
-@newpage
-@Heading(GDB Data Structuring Services)
-@begin(itemize)
-Intended to support GDB's specific needs, @i[not] a generalized
-extension to C language typing
-
-Explicit support for `database types' (e.g. relation, tuple)
-
-Representation may vary according to local hardware type
-
-Automatic conversion when transmitting between different architectures
-(e.g. VAX to RT/PC)
-
-Limited polymorphism
-
-Limited services for user defined types
-@end(itemize)
-@newpage
-@Heading(GDB Communication Services)
-@begin(itemize)
-CONNECTIONS: underlying implementation is TCP byte streams, but much
-easier to use
-
-Services provided for sending and receiving GDB typed data
-
-Queuing for asynchronous operations
-
-Synchronization services
-
-All services are available to GDB applications
-
-Very low latencies can be achieved on suitable networks
-
-Asynchronous services can hide network delays from the application
-
-GDB @i[database] services are built on top of GDB communication services
-@end(itemize)
-@newpage
-@Heading(GDB Servers and Clients)
-@begin(itemize)
-@i[Forking] servers are particularly easy to implement
-
-@i[Non-forking] servers provide high performance data sharing
-
-Limited support for @i[peer-to-peer] communication is also provided
-@end(itemize)
-@newpage
-@Heading(GDB Project Status)
-@begin(itemize)
-Project started: late winter of 1986
-
-A basic, unoptimized, but 95% complete implementation of the specification
-is now available for Berkeley Unix systems
-
-Runs on both RT/PC and Vax machines, should port to others easily
-
-Preliminary versions of a User's Guide and a Library Reference Manual
-are available
-
-A few applications have been built, and several others are under development
-
-Several groups and individuals at MIT have expressed interest in the system
-@end(itemize)
-@newpage
-@Heading(Plans for the Future)
-@begin(itemize)
-Develop applications
-
-Find and support new `customers'
-
-Evaluate design, and possibly re-implement some portions based on
-experiences of users
-
-Enhancements? (e.g. automatic encapsulation of C structures as GDB types)
-
-Continue comparisons with other approaches to similar function
-(integrated distributed databases, RPC, etc.)
-@end(itemize)
+++ /dev/null
-/*
- * $Source$
- * $Header$
- */
-
-#ifndef lint
-static char *rcsid_tcl_c = "$Header$";
-#endif lint
-
-/************************************************************************/
-/*
-/* tcl (test client)
-/* -----------------
-/*
-/* Author: Noah Mendelsohn (IBM T.J. Watson Research and MIT Project
-/* Athena)
-/*
-/* Copyright: 1986 MIT Project Athena
-/*
-/************************************************************************/
-/*
-/* PURPOSE
-/* -------
-/*
-/* A simple GDB client program. This program is the client in a
-/* server/client pair. It does a start server connection and then
-/* sends characters to the server for echoing.
-/*
-/* NOTES
-/* -----
-/*
-/* 1) For simplicity, and do demonstrate the interactive response
-/* of GDB connections, this program echoes single characters,
-/* represented as ASCII coded integers. Because of the polymorphism
-/* inherent in the send_object and receive_object functions,
-/* similar one line calls are used to transmit entire tuples
-/* (aggregates of typed fields), relations (aggregates of tuples,
-/* which are customarily represented by linked lists), etc.
-/*
-/* 2) All communication done by this program and any other
-/* GDB based program is supported across incompatible
-/* machine types. For example, this program may run on an
-/* RT/PC while the server may be running on a Vax. Since
-/* these machines use differing representations for integers,
-/* GDB must do the necessary byte order conversions.
-/*
-/* 4) Though this program is straightforward, it includes all
-/* necessary error checking of the communication
-/* path to the server. For example, this program will
-/* terminate gracefully if the server crashes or is terminated
-/* while this client is running.
-/*
-/*
-/************************************************************************/
-
-
-#include <stdio.h>
-#include <sgtty.h>
-#include "gdb.h"
-
-struct sgttyb sgtty;
-\f
-int
-main(argc, argv)
-int argc;
-char *argv[];
-{
-
- /*----------------------------------------------------------*/
- /*
- /* DECLARATIONS
- /*
- /*----------------------------------------------------------*/
-
- CONNECTION server; /* this represents the */
- /* server*/
-
- int data; /* the input as typed */
- char c; /* same input as a char */
- /* instead of an int */
-
- /*----------------------------------------------------------*/
- /*
- /* EXECUTION BEGINS HERE
- /*
- /* Make sure the command line specifies the name
- /* of the host on which the server program is running.
- /*
- /*----------------------------------------------------------*/
-
- if (argc != 2) {
- /*
- * Tell 'em the syntax
- */
- fprintf(stderr, "tcl <server-host-name:service-i.d.>\n");
- fprintf(stderr, "\tservice-i.d. is from #port number or name from /etc/services.\n");
- fprintf(stderr, "\tShould match name service i.d. given to tsr.\n");
- exit (4);
- }
-
- /*----------------------------------------------------------*/
- /*
- /* Initialize the GDB library.
- /*
- /*----------------------------------------------------------*/
-
- gdb_init();
-\f
- /*----------------------------------------------------------*/
- /*
- /* Try for a connection to the server on the
- /* specified host.
- /*
- /*----------------------------------------------------------*/
-
- printf("Attempting connection to server: %s\n", argv[1]);
-
- server = start_server_connection(argv[1], "Dummy parms");
-
- if (server == NULL) {
- fprintf(stderr,"Could not start connection to server\n");
- exit(8);
- }
-
- printf("Server connection started successfully.\n");
-
- /*----------------------------------------------------------*/
- /*
- /* Put the terminal into CBREAK mode, so that we'll
- /* see each character as it's typed. Also, turn off
- /* local character echoing, since the whole point of
- /* this program is to echo through the network.
- /*
- /*----------------------------------------------------------*/
-
- ioctl(0, TIOCGETP, &sgtty); /* go into cbreak */
- sgtty.sg_flags |= CBREAK;
- sgtty.sg_flags &= ~ECHO;
- ioctl(0, TIOCSETP, &sgtty); /* go into cbreak */
-\f
- /*----------------------------------------------------------*/
- /*
- /* Start reading from keyboard and writing to server.
- /* Send each character to the server separately, and
- /* when it is echoed, present it on the screen. <CRLF>
- /* processing is done locally. Loop until CTRL_D (EOT)
- /* is entered.
- /*
- /*----------------------------------------------------------*/
-
- printf("Start typing data to be uppercased and echoed. ^D Exits.\n\n\n");
- data = getchar();
-
-#define EOT 4
-
- while (data != EOT) {
- if (send_object(server, (char *)&data, INTEGER_T) ==
- OP_CANCELLED) {
- fprintf(stderr,"\n\nSend error.\n");
- break;
- }
- if (receive_object(server, (char *)&data, INTEGER_T) ==
- OP_CANCELLED) {
- fprintf(stderr,"\n\nSend error.\n");
- break;
- }
- c = data;
- putchar(c);
- if (c == '\r')
- putchar('\n');
- data = getchar();
- }
-
- /*----------------------------------------------------------*/
- /*
- /* Put terminal back into normal line-at-a-time with
- /* echoing mode.
- /*
- /*----------------------------------------------------------*/
-
- sgtty.sg_flags |= ECHO;
- sgtty.sg_flags &= ~CBREAK;
- ioctl(0, TIOCSETP, &sgtty); /* go into cbreak */
-
- /*----------------------------------------------------------*/
- /*
- /* Terminate the connection to the server.
- /*
- /*----------------------------------------------------------*/
-
- printf("\n\nClosing connection\n\n");
- sever_connection(server);
- return;
-}
-
+++ /dev/null
-/*
- * $Source$
- * $Header$
- */
-
-#ifndef lint
-static char *rcsid_tdbcl_c = "$Header$";
-#endif lint
-
-/************************************************************************/
-/*
-/* tdbcl (test database client)
-/* ----------------------------
-/*
-/* Author: Noah Mendelsohn (IBM T.J. Watson Research and MIT Project
-/* Athena)
-/*
-/* Copyright: 1986 MIT Project Athena
-/*
-/************************************************************************/
-/*
-/* PURPOSE
-/* -------
-/*
-/* A GDB client program which accesses a relational database
-/* using the services of GDB.
-/*
-/************************************************************************/
-
-
-#include <stdio.h>
-#include "gdb.h"
-
-\f
-
-int ftypes[] = {STRING_T, INTEGER_T, INTEGER_T};
-char *fnames[] = {"name", "rank", "serial"};
-int
-main(argc, argv)
-int argc;
-char *argv[];
-{
-
- /*----------------------------------------------------------*/
- /*
- /* DECLARATIONS
- /*
- /*----------------------------------------------------------*/
-
- DATABASE db; /* this is the database */
- /* we access */
- int rc; /* return code from */
- /* access_db */
- TUPLE_DESCRIPTOR tpd;
- RELATION rel;
-#define MAX_OPS 50
- OPERATION ops[MAX_OPS];
- int next_op=0; /* next op to use */
- int i;
-
- /*----------------------------------------------------------*/
- /*
- /* EXECUTION BEGINS HERE
- /*
- /* Make sure the command line specifies the name
- /* of the host on which the server program is running.
- /*
- /*----------------------------------------------------------*/
-
- if (argc != 3) {
- /*
- * Tell 'em the syntax
- */
- fprintf(stderr, "tcl <database-name@host> <query>\n");
- exit (4);
- }
-
- /*----------------------------------------------------------*/
- /*
- /* Initialize the GDB library.
- /*
- /*----------------------------------------------------------*/
-
- gdb_init();
-
- /*----------------------------------------------------------*/
- /*
- /* Create all the operations
- /*
- /*----------------------------------------------------------*/
- for (i=0; i<MAX_OPS; i++)
- ops[i] = create_operation();
-\f
- /*----------------------------------------------------------*/
- /*
- /* Try for a connection to the server on the
- /* specified host.
- /*
- /*----------------------------------------------------------*/
-
- printf("Attempting database: %s\n", argv[1]);
-
- if (rc = access_db(argv[1], &db) != DB_OPEN) {
- fprintf(stderr,"Return code from access_db is %d\n",rc);
- exit(8);
- }
-
- if (db == NULL) {
- fprintf(stderr,"Got null db after access_db reported success\n");
- exit(8);
- }
-
- /*----------------------------------------------------------*/
- /*
- /* Try destroying and creating a table
- /*
- /*----------------------------------------------------------*/
-
- printf("Now queueing operations\n");
-
- rc = start_performing_db_operation(ops[next_op++],db, "destroy tdbcl");
-
- rc = start_performing_db_operation(ops[next_op++],db, "create tdbcl (name=c8, rank = i4, serial=i4)");
-
- /*----------------------------------------------------------*/
- /*
- /* Append some data to the table
- /*
- /*----------------------------------------------------------*/
-
- rc = start_performing_db_operation(ops[next_op++],db, "append to tdbcl (name=\"noah\", rank = 1, serial=123)");
- rc = start_performing_db_operation(ops[next_op++],db, "append to tdbcl (name=\"mike\", rank = 5, serial=456)");
- rc = start_performing_db_operation(ops[next_op++],db, "append to tdbcl (name=\"mike1\", rank = 5, serial=456)");
- rc = start_performing_db_operation(ops[next_op++],db, "append to tdbcl (name=\"mike2\", rank = 5, serial=456)");
- rc = start_performing_db_operation(ops[next_op++],db, "append to tdbcl (name=\"mike3\", rank = 5, serial=456)");
- rc = start_performing_db_operation(ops[next_op++],db, "append to tdbcl (name=\"mike4\", rank = 5, serial=456)");
- rc = start_performing_db_operation(ops[next_op++],db, "append to tdbcl (name=\"mike5\", rank = 5, serial=456)");
- rc = start_performing_db_operation(ops[next_op++],db, "append to tdbcl (name=\"mike6\", rank = 5, serial=456)");
- rc = start_performing_db_operation(ops[next_op++],db, "append to tdbcl (name=\"mike7\", rank = 5, serial=456)");
- rc = start_performing_db_operation(ops[next_op++],db, "append to tdbcl (name=\"mike8\", rank = 5, serial=456)");
- rc = start_performing_db_operation(ops[next_op++],db, "append to tdbcl (name=\"mike9\", rank = 5, serial=456)");
- rc = start_performing_db_operation(ops[next_op++],db, "append to tdbcl (name=\"mike10\", rank = 5, serial=456)");
- rc = start_performing_db_operation(ops[next_op++],db, "append to tdbcl (name=\"mike11\", rank = 5, serial=456)");
- rc = start_performing_db_operation(ops[next_op++],db, "append to tdbcl (name=\"mike12\", rank = 5, serial=456)");
- rc = start_performing_db_operation(ops[next_op++],db, "append to tdbcl (name=\"mike13\", rank = 5, serial=456)");
-
-
- /*----------------------------------------------------------*/
- /*
- /* Complete the operations and print the return codes
- /* from them
- /*
- /*----------------------------------------------------------*/
-
- printf("Operations have been queued, waiting for last one to complete\n");
- complete_operation(ops[next_op-1]);
- for (i=0; i<=next_op; i++)
- printf("%d: status=%d, result=%d\n", i, OP_STATUS(ops[i]),
- OP_RESULT(ops[i]));
-
- /*----------------------------------------------------------*/
- /*
- /* Do a query
- /*
- /*----------------------------------------------------------*/
-
- tpd = create_tuple_descriptor(3, fnames, ftypes);
-
- rel = create_relation(tpd);
-
- rc = db_query(db, rel, /*argv[2]*/ "(>*name*<=tdbcl.name, >*serial*<=tdbcl.serial) where tdbcl.serial>200");
-
- printf("return code from query is %d\n",rc);
- if (rc == 0)
- print_relation("Query result", rel);
-
- delete_relation(rel);
- rel = create_relation(tpd);
-
- rc = db_query(db, rel, /*argv[2]*/ "(>*name*<=tdbcl.name, >*serial*<=tdbcl.serial) where tdbcl.serial>200");
-
- printf("return code from query is %d\n",rc);
- if (rc == 0)
- print_relation("Query result", rel);
-
- delete_relation(rel);
- delete_tuple_descriptor(tpd);
-
- return;
-}
-
+++ /dev/null
-/*
- * $Source$
- * $Header$
- */
-
-#ifndef lint
-static char *rcsid_test_c = "$Header$";
-#endif lint
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-/************************************************************************/
-/*
-/* gdb_trans2.c
-/*
-/* GDB - Data Transport Services Routines (Part 2)
-/*
-/* Author: Noah Mendelsohn
-/* Copyright: 1986 MIT Project Athena
-/*
-/* These routines implement layer 6 of the Client Library
-/* Specification of the GDB system, as well as the facilities
-/* outlined in the GDB Protocol Specification. Part 2 of 2.
-/*
-/* Some of the routines specified are actually implemented as
-/* macros defined in gdb.h.
-/*
-/************************************************************************/
-
-#include <sys/types.h>
-#include <errno.h>
-#include <stdio.h>
-#include "gdb.h"
-
-extern int errno; /* Unix error slot */
-
-/*
- * The following values are returned by g_con_progress
- */
-#define NOPROGRESS 0 /* nothing happened on this */
- /* connection--must be 0*/
-#define PROGRESS 1 /* connection has progressed */
-#define COMPLETE 2 /* an operation has */
- /* completed on this con */
-\f
-/************************************************************************/
-/*
-/* queue_operation(queue_operation)
-/*
-/* Add an operation to the queue for a given connection, and
-/* then allows all connections to progress. Returns the last
-/* known status of the operation.
-/*
-/************************************************************************/
-
-int
-queue_operation(con, direction, op)
-CONNECTION con;
-int direction;
-OPERATION op;
-{
- register HALF_CONNECTION hcon = (direction==CON_INPUT)?(&(con->in)):
- (&(con->out));
-
- GDB_CHECK_CON(con, "queue_operation")
- /*
- * Write message to debugging log
- */
- if (gdb_Debug & GDB_LOG)
- fprintf(gdb_log, "op queued: con=0x%x dir=%s op=0x%x Q was %s empty\n",
- con, (direction == CON_INPUT)?"INPUT":"OUTPUT",
- op, (hcon->op_q_first == (OPERATION)hcon)?"":"not");
- /*
- * Make sure connection is up
- */
- if (con->status != CON_UP) {
- op->status = OP_CANCELLED;
- if (gdb_Debug & GDB_LOG)
- fprintf(gdb_log, "\nop NOT queued\n");
- return OP_CANCELLED;
- }
-
- /*
- * Put the new operation at the end of the queue
- */
- op->prev = hcon->op_q_last;
- op->next = (OPERATION)hcon;
- hcon->op_q_last->next = op;
- hcon->op_q_last = op;
- /*
- * Mark it as queued
- */
- op->status = OP_QUEUED;
- op->halfcon = hcon;
- /*
- * Force progress on all connections
- */
- gdb_progress();
-
- /*
- * Return the last known status of the operation
- */
- return op->status;
-}
-/************************************************************************/
-/*
-/* requeue_operation
-/*
-/* This routine may be called from an init or continuation routine
-/* to cause the current operation to be requeued on a new connection.
-/* The init routine field ofthe operation should be properly set to
-/* indicate the routine to receive control when the operation actually
-/* runs on the new connection. The caller of this routine is
-/* responsible for returning the status OP_REQUEUED to its caller.
-/*
-/* This routine returns the status of the newly queued operation.
-/* Note, however, that even if this operation returns the status
-/* CANCELLED, the operation itself may not continue to execute
-/* on the old connection and it should return the status OP_REQUEUED,
-/* NOT OP_CANCELLED (at least in this implementation.)
-/*
-/************************************************************************/
-
-int
-requeue_operation(con, direction, op)
-CONNECTION con;
-int direction;
-OPERATION op;
-{
- /*
- * Make sure the connection supplied is a legal one
- */
- GDB_CHECK_CON(con, "requeue_operation")
- /*
- * Write message to debugging log
- */
- if (gdb_Debug & GDB_LOG)
- fprintf(gdb_log, "op requeued: new con=0x%x dir=%s op=0x%x\n",
- con, (direction == CON_INPUT)?"INPUT":"OUTPUT",
- op);
- /*
- * Dequeue the operation from its old half connection
- */
- g_op_newhead(op->halfcon);
-
- /*
- * Now queue it on the new one
- */
- return queue_operation(con, direction, op);
-}
-
-/************************************************************************/
-/*
-/* g_preempt_me
-/*
-/* Sticks a new operation in ahead of the current one and runs it
-/* on the current connection. May be called only from an init or
-/* continuation routine. The old operation must have completely
-/* prepared the descriptor for the new operation, i.e. it should
-/* be in the same state as it would be for a call to queue_operation.
-/* g_preempt_me makes it possible for operations to be built by
-/* composition of other smaller operations, since newop runs, in
-/* a sense, as a subroutine of oldop. opdop must (1) reset its
-/* initialization routine to be a routine to be called when newop
-/* completes or cancels and (2) return the status OP_PREEMPTED to
-/* its caller.
-/*
-/************************************************************************/
-
-int
-g_preempt_me(oldop, newop)
-OPERATION oldop;
-OPERATION newop;
-{
- register OPERATION old=oldop, new=newop;
- register HALF_CONNECTION hc = old->halfcon;
-
- /*
- * Write message to debugging log
- */
- if (gdb_Debug & GDB_LOG)
- fprintf(gdb_log, "op preempted: halfcon=0x%x oldop=0x%x newop=0x%x\n",
- oldop,newop);
- /*
- * link in the new operation
- */
- old->prev = new;
- hc->op_q_first = new;
- new->prev = (OPERATION)hc;
- new->next = old;
- /*
- * Set the status of the new operation
- */
- new->status = OP_QUEUED;
- new->halfcon = hc;
- /*
- * Change the status of the old operation (one could argue that
- * this should be done in gdb_hcon_progress after the return code
- * is detected.)
- */
- old->status = OP_QUEUED;
- return OP_QUEUED;
-}
-
-
-\f
-/************************************************************************/
-/*
-/* gdb_progress
-/*
-/* This routine should be called whenever it is suspected that
-/* progress can be made on any connection. This routine will
-/* cause all connections to proceed as far as they can without
-/* blocking, and will make a best effort to avoid long blocks.
-/* This routine MAY retain control for long periods when sustained
-/* progress is possible, but it will not knowingly hang.
-/*
-/* Returns: number of connections on which OPERATIONS have
-/* COMPLETED (not just progressed).
-/*
-/************************************************************************/
-
-int
-
-gdb_progress()
-{
- register int i; /* index to available */
- /* connections */
- register int return_value = 0; /* the value we return */
- int rc; /* short term storage for */
- /* a return code */
- int progress_made; /* true when some con */
- /* made progress during */
- /* latest pass through list */
- int complete_map[GDB_MAX_CONNECTIONS]; /* indicates whether a */
- /* transmission operation */
- /* is newly complete on */
- /* corresponding connection */
- /* 1 if yes else 0 */
- int maxcon = gdb_mcons; /* gdb_mcons may change */
- /* out from under us if */
- /* connections break. This */
- /* is the initial value. */
-
- /*
- * Zero out the completion map for all connections.
- */
- for (i=0; i<maxcon; i++)
- complete_map[i]=0;
-
- /*
- * Make repeated passes through all the fd's until a pass is made
- * in which none makes any progress. This logic is important,
- * because it catches the case where A is blocked, B makes progress,
- * and A unblocks during the period where B is progressing.
- */
-
- do {
- progress_made = FALSE;
- for (i=0; i<gdb_mcons; i++) {
- if (rc = g_con_progress(i)) { /* note: NOPROGRESS==0 */
- progress_made = TRUE;
- if (rc == COMPLETE)
- complete_map[i] = 1;
- }
- }
- } while (progress_made);
-
- /*
- * We've gone as far as we can, now find out how many connections
- * have had operations complete.
- */
- for (i=0; i<maxcon; i++)
- return_value += complete_map[i];
-
- return return_value;
-}
-\f
-/************************************************************************/
-/*
-/* g_con_progress
-/*
-/* Make as much progress as possible on the specified connection.
-/* Returns NOPROGRESS if no bytes moved on either half connection,
-/* PROGRESS, if some moved and no operations completed, or COMPLETE if
-/* any of the operations completed. Note that each connection
-/* consists of two half connections, and we must make each of them
-/* progress as far as possible.
-/*
-/* The nest here starts getting so deep that it's hard to pass state
-/* around efficiently. We use a single global variable, gdb_conok,
-/* to indicate whether the connection we're working on now has died.
-/* The move data routines set this to FALSE whenever there is a
-/* fatal error on a connection. We check it, and do a proper
-/* sever on the connection if it seems to be in trouble.
-/*
-/************************************************************************/
-
-
-int
-g_con_progress(con_id)
-int con_id; /* index of this connection */
- /* in the connection desc. */
- /* arrays*/
-{
- register CONNECTION con= (&gdb_cons[con_id]);
- /* pointer to the connection */
- /* data structure */
- register int progress = NOPROGRESS;
- register int live = TRUE; /* true when we've seen */
- /* enough to make sure we */
- /* want to go around again*/
- int rc;
- /*
- * Check status of connection-if it's not running, then just return.
- */
- if (con->status != CON_UP)
- return NOPROGRESS;
- /*
- * Repeatedly make progress on each half connection until both
- * are idle. Important to keep trying as one may become active
- * while the other is progressing.
- */
-
- gdb_conok = TRUE; /* this gets set to FALSE */
- /* for fatal I/O errors */
- /* there may be a timing */
- /* window here in use of */
- /* HCON_BUSY */
- while (live) {
- live = FALSE; /* until proven otherwise */
- /*
- * make progress on the input connection note that following
- * logic depends on NOPROGRESS being 0
- */
- if (rc = gdb_hcon_progress(CON_INPUT, &con->in)) {
- live = TRUE;
- progress = max(rc, progress);
- }
- /*
- * See if connection has died
- */
- if (!gdb_conok) {
- g_stop_connection(con);
- return COMPLETE; /* dying connection always */
- /* implies that the */
- /* operation at the head */
- /* of the queue completed */
- }
- /*
- * make progress on the output connection
- */
- if (rc = gdb_hcon_progress(CON_OUTPUT, &con->out)) {
- live = TRUE;
- progress = max(rc, progress);
- }
- /*
- * See if connection has died
- */
- if (!gdb_conok) {
- g_stop_connection(con);
- return COMPLETE;
- }
- }
-
- return progress;
-}
-
-
-/************************************************************************/
-/*
-/* gdb_hcon_progress
-/*
-/* Allows a specified half-connection to progress as much as possible,
-/* and returns true iff at least one operation is newly completed.
-/*
-/************************************************************************/
-
-int
-gdb_hcon_progress(direction, hc)
-int direction; /* CON_INPUT or CON_OUTPUT */
-struct half_con_data *hc; /* pointer to control struct */
- /* for this half connection */
-{
- HALF_CONNECTION half_con = hc;
- /* half connection pointer */
- /* fast copy in register */
- register OPERATION op; /* current operation on this */
- /* half connection */
- int progress = NOPROGRESS; /* can indicate any progress */
- /* on the half con or */
- /* whether any operations */
- /* completed */
- int done; /* true when no more progress*/
- /* can be made */
- int fcn_result; /* result of latest init or */
- /* continue function */
-
- /*
- * Write message to debugging log
- */
- if (gdb_Debug & GDB_LOG)
- fprintf(gdb_log, "hcon_progress: halfcon=0x%x dir=%s ",
- half_con, (direction==CON_INPUT)?"INPUT":"OUTPUT");
-
- /*----------------------------------------------------------*/
- /*
- /* See if we are being re-entered and are already working
- /* on this half_con. If so, return right now.
- /*
- /*----------------------------------------------------------*/
-
- if (half_con->flags & HCON_BUSY) {
- /*
- * Write message to debugging log
- */
- if (gdb_Debug & GDB_LOG)
- fprintf(gdb_log, "BUSY, returning\n");
- return NOPROGRESS;
- }
-
- /*----------------------------------------------------------*/
- /*
- /* See if there is an operation on this half connection.
- /* If not, return.
- /*
- /*----------------------------------------------------------*/
-
-
- op = half_con->op_q_first; /* pick up first operation */
- /* in queue */
- if (op == (OPERATION)half_con) { /* see if end of circular */
- /* list */
- /*
- * Write message to debugging log
- */
- if (gdb_Debug & GDB_LOG)
- fprintf(gdb_log, "Q EMPTY, returning\n");
- return NOPROGRESS; /* nothing to do on */
- /* this half session */
- }
-
-
- /*----------------------------------------------------------*/
- /*
- /* Loop until all operations are complete, or until no further
- /* progress can be made on this one.
- /*
- /* Loop invariants:
- /*
- /* 1) Op contains the operation at the head of the q, or
- /* else is == half_con, indicating no more operationos
- /* to be processed.
- /*
- /* 2) The operation at the head of the queue is either running
- /* or continuing. As soon as one completes, it is dequeued.
- /*
- /* Progress is declared whenever an operation newly
- /* returns OP_COMPLETE, i.e. whenever there has been
- /* an operation which went from running to complete.
- /*
- /* Done is declared whenever an operation returns anything
- /* other than complete, indicating that it cannot progress
- /* further at this time. Loop ends.
- /*
- /* While we're here, mark us busy so we won't try the
- /* same half_con on reentry.
- /*
- /*----------------------------------------------------------*/
-
- done = FALSE; /* this one may be able to */
- /* progress */
-
- half_con->flags |= HCON_BUSY; /* don't try this hcon */
- /* while we already doing */
- /* it. Could happen if */
- /* we queue new ops */
- half_con->flags &= ~HCON_PROGRESS; /* gdb_move_data will */
- /* indicate progress here*/
- if (gdb_Debug & GDB_LOG)
- fprintf(gdb_log, "LOOPING\n");
-
- /*----------------------------------------------------------*/
- /*
- /* Loop through the operations queued on this half con
- /* trying to make progress on them, in order.
- /*
- /*----------------------------------------------------------*/
-
- while (!done &&
- op != (OPERATION)half_con) {
-
- if (gdb_Debug & GDB_LOG)
- fprintf(gdb_log, "\top=0x%x status%d...",
- op, OP_STATUS(op));
-
- switch (op->status) {
- /*
- * Operation is at head of queue for first time and has
- * never been started. Try to start it up.
- */
- case OP_QUEUED:
- /*
- * Call the initialization routine for this operation
- */
- fcn_result = (*op->fcn.init)(op,half_con,op->arg);
- if (gdb_Debug & GDB_LOG)
- fprintf(gdb_log, "init result=%d\n",
- fcn_result);
-
- switch (fcn_result) {
- case OP_COMPLETE:
- case OP_CANCELLED:
- op->status = fcn_result;
- op = g_op_newhead(half_con);
- progress = COMPLETE;
- break;
- case OP_PREEMPTED:
- op->status = OP_QUEUED;
- /* fall thru */
- case OP_REQUEUED:
- /* important: don't set status on re-queued */
- /* op as it may already have completed in */
- /* its second life ! */
- op = half_con->op_q_first;
- progress = max(progress, PROGRESS);
- break;
- default:
- op->status = fcn_result;
- done = TRUE; /* could not get done */
- }
- break;
- /*
- * Operation is at head of queue and has already
- * started trying to run. The only reason we could be in this
- * state is that the last time we tried to do the requested input
- * or output, all the data could not be moved synchronously.
- * We therefore try to move some more, and if it all goes now,
- * we call the continuation routine.
- */
- case OP_RUNNING:
- /*
- * Try to move some more data. If it won't all
- * go now, we're done with this half connection.
- *
- * If this is a special listening connection which
- * has an operation queued trying to do a listen,
- * then do the listen. Otherwise do an ordinary
- * data move.
- */
- if (half_con->flags & HCON_PENDING_LISTEN) {
- if (gdb_listen(half_con)==FALSE) {
- if (gdb_Debug & GDB_LOG)
- fprintf(gdb_log, "NO LISTEN\n");
- done = TRUE;
- break;
- }
- } else
- if (gdb_move_data(direction, half_con)==FALSE) {
- done = TRUE;
- if (gdb_Debug & GDB_LOG)
- fprintf(gdb_log, "NO DATA\n");
- break;
- }
- /*
- * The pending data transmission has now completed.
- * Call the continuation routine for this operation
- */
- fcn_result = (*op->fcn.cont)(op,half_con,op->arg);
- if (gdb_Debug & GDB_LOG)
- fprintf(gdb_log, "cont result=%d\n",
- fcn_result);
-
- switch (fcn_result) {
- case OP_COMPLETE:
- case OP_CANCELLED:
- op->status = fcn_result;
- op = g_op_newhead(half_con);
- progress = COMPLETE;
- break;
- case OP_PREEMPTED:
- op->status = OP_QUEUED;
- /* fall thru */
- case OP_REQUEUED:
- /* important: don't set status on re-queued */
- /* op as it may already have completed in */
- /* its second life ! */
- op = half_con->op_q_first;
- progress = max(progress, PROGRESS);
- break;
- default:
- op->status = fcn_result;
- done = TRUE; /* could not get done */
- }
- break;
- /*
- * Following cases are all unexpected, at least for the
- * moment. (See explanation of loop invariants for this while
- * loop. Give up if they turn up.
- */
- case OP_COMPLETE:
- GDB_GIVEUP("gdb_hcon_progress: found OP_COMPLETE on q")
- case OP_CANCELLED:
- GDB_GIVEUP("gdb_hcon_progress: found OP_CANCELLED on q")
- case OP_CANCELLING:
- GDB_GIVEUP("gdb_hcon_progress: OP_CANCELLING")
- default:
- GDB_GIVEUP("gdb_hcon_progress: Operation is queued, but is not runnable")
- }
- }
-
- if (progress == NOPROGRESS && (half_con->flags & HCON_PROGRESS))
- progress = PROGRESS;
-
- half_con->flags &= ~HCON_BUSY;
-
- if (gdb_Debug & GDB_LOG)
- fprintf(gdb_log, "hcon_progress: returns %d\n",progress);
-
- return progress; /* NOPROGRESS, PROGRESS */
- /* or COMPLETE */
-}
-\f
-/************************************************************************/
-/*
-/* g_op_newhead
-/*
-/* Dequeues the operation at the head of the queue for the
-/* given half connection and returns the pointer to the
-/* new head of the queue. If the queue is null, then a pointer
-/* to the half_con itself is returned. (The lists are
-/* linked circularly.)
-/*
-/************************************************************************/
-
-OPERATION
-g_op_newhead(hcp)
-HALF_CONNECTION hcp;
-{
- register OPERATION newhead, oldhead;
-
- /*
- * Get old and new heads of chain
- */
- oldhead = hcp->op_q_first;
- newhead = oldhead->next;
- /*
- * Make sure nobody chained a bad one on us
- */
- if (newhead == NULL) {
- if (gdb_Debug & GDB_LOG) {
- fprintf(gdb_log,"\t\tg_op_newhead: found null link, oldhead = 0x%x newhead=0x%x halfcon=0x%x\n\t\t\t hc->first=0x%x hc->last=0x%x\n",
- oldhead, newhead, hcp, hcp->op_q_first,
- hcp->op_q_last);
- }
- GDB_GIVEUP("g_op_newhead: found NULL chain link")
- }
- /*
- * Remove oldhead from chain, fixing up chain pointers
- */
- newhead->prev = oldhead->prev;
- hcp->op_q_first = newhead;
-
- /*
- * Clean up pointers in the newly dequeued operation. This is
- * just for cleanliness and ease of debugging.
- */
- oldhead->next = oldhead->prev = NULL;
- oldhead->halfcon = NULL;
-
- return newhead;
-}
-\f
-/************************************************************************/
-/*
-/* gdb_move_data
-/*
-/* This routine attempts to make further progress on the pending
-/* level transmission operation pending on this half connection.
-/* (Presumes that such an operation is pending.) Returns TRUE
-/* if all the requested data has been moved, else FALSE.
-/*
-/* We assume here that all fd's are set to non-blocking I/O, so
-/* we can safely try reading and writing until they return 0 bytes.
-/*
-/************************************************************************/
-
-int
-gdb_move_data(direction, hc)
-int direction; /* CON_INPUT or CON_OUTPUT */
-struct half_con_data *hc; /* pointer to control struct */
- /* for this half connection */
-{
- register HALF_CONNECTION half_con = hc;
- /* half connection pointer */
- /* fast copy in register */
- register int count; /* number of bytes read */
- /* or written in latest */
- /* attempt */
- fd_set *fdbits; /* the mask we should adjust */
- /* for this direction */
- fd_set tst_bits; /* these are used for */
- /* the select we do prior */
- /* to reading which tells */
- /* us whether 0 byte read */
- /* means empty or closed */
- int selected; /* TRUE iff select says */
- /* we should be able to */
- /* progress */
-
- /*
- * For safety, in case we're called when nothing is pending.
- */
- if (half_con->remaining == 0)
- return TRUE;
- /*
- * Loop until either (1) the connection reported that it could
- * not progress any further or (2) the full count has been
- * satisfied. Some versions of Unix observe the rule that
- * a closed connection, especially when reading, is indicated
- * by select claiming that there is data when read says there
- * isn't. Also, some other versions return errors from the
- * select on that FD. We test for both situations here.
- */
- FD_ZERO(&tst_bits);
-
- while(half_con->remaining>0) {
- FD_SET(half_con->fd,&tst_bits);
- if (direction == CON_INPUT) {
- selected = select(gdb_mfd,&tst_bits, NULL, NULL,
- &gdb_notime);
- /*
- * If selected==(-1), then we know there's something
- * wrong with the socket
- */
- if (selected == (-1)) {
- gdb_conok = FALSE;
- break;
- }
- /*
- * if selected==0, then we know read won't do
- * anything, so save the extraneous system call
- */
- if (selected == 0) {
- count =0;
- break;
- }
- /*
- * Selected is >0. Either the read is going to
- * return 0, or this is one of those versions of
- * Unix that tells us the connection has died by
- * indicating select=1, read=0.
- */
- count = read(half_con->fd, half_con->next_byte,
- half_con->remaining);
- } else {
- selected = select(gdb_mfd, NULL, &tst_bits, NULL,
- &gdb_notime);
- if (selected == (-1)) {
- gdb_conok = FALSE;
- break;
- }
- if (selected == 0) {
- count =0;
- break;
- }
- count = write(half_con->fd, half_con->next_byte,
- min(half_con->remaining,
- GDB_MAX_SOCK_WRITE));
- }
- /*
- * We moved some data
- */
- if (count >0)
- half_con->flags |= HCON_PROGRESS;
- /*
- * rc==0 means we didn't mvoe any data, but if accompanied
- * by select claiming there was data, it really means the
- * connection is dead!
- */
- if (count==0) {
- if (selected == 1)
- gdb_conok = FALSE;
- break; /* no more data available now*/
- }
- if (count<0) {
- count = 0;
- if (errno != EWOULDBLOCK) {
- gdb_conok = FALSE; /* tell callers that */
- /* con has died */
- }
- break;
-
- }
- half_con->remaining -=count;
- half_con->next_byte +=count;
- }
-
- /*
- * The file descriptor masks used for doing selects must be activated
- * when and only when there is a pending operation trying to use
- * the connection. Update the masks for this half connection.
- */
- fdbits = (direction == CON_INPUT)? &gdb_crfds : &gdb_cwfds;
- if (half_con->remaining >0)
- FD_SET(half_con->fd, fdbits);
- else
- FD_CLR(half_con->fd, fdbits);
-
- return (half_con->remaining == 0);
-}
-\f
-/************************************************************************/
-/*
-/* gdb_receive_data (gdb_receive_data)
-/*
-/* This routine is called by an init or continuation routine to
-/* request that a specified amount of data be read, without
-/* blocking, on the supplied connection. This routine returns
-/* OP_COMPLETE if the entire read completed synchronously,
-/* or OP_RUNNING if the read remains ongoing or is cancelling
-/* due to error on the socket.
-/*
-/************************************************************************/
-
-int
-gdb_receive_data(half_con, ptr, len)
-HALF_CONNECTION half_con; /* read on this connection*/
-char *ptr; /* put first byte here */
-int len; /* number of bytes to read */
-{
- /*
- * Fill in the initial state of the attempted receive
- */
- half_con->remaining = len;
- half_con->next_byte = ptr;
-
- /*
- * Now see if we can make some progress on this read, possibly
- * even completing it synchronously. Return appropriate
- * result to our caller. Note: errors are reflected as OP_RUNNING
- * with global variable gdb_cnok set to FALSE.
- */
- if(gdb_move_data(CON_INPUT, half_con))
- return OP_COMPLETE;
- else
- return OP_RUNNING;
-}
-
-/************************************************************************/
-/*
-/* gdb_send_data (gdb_sndat)
-/*
-/* This routine is called by an init or continuation routine to
-/* request that a specified amount of data be written, without
-/* blocking, on the supplied connection. This routine returns
-/* OP_COMPLETE if the entire write completed synchronously,
-/* or OP_RUNNING if the output remains ongoing or there was an error.
-/*
-/************************************************************************/
-
-int
-gdb_sndat(half_con, ptr, len)
-HALF_CONNECTION half_con; /* write on this connection*/
-char *ptr; /* put first byte here */
-int len; /* number of bytes to read */
-{
-
- /*
- * Fill in the initial state of the attempted receive
- */
- half_con->remaining = len;
- half_con->next_byte = ptr;
-
- /*
- * Now see if we can make some progress on this read, possibly
- * even completing it synchronously. Return appropriate
- * result to our caller.
- */
- if(gdb_move_data(CON_OUTPUT, half_con))
- return OP_COMPLETE;
- else
- return OP_RUNNING;
-}
-
-/************************************************************************/
-/*
-/* gdb_start_a_listen
-/*
-/* This routine is called by an init or continuation routine to
-/* request that a connection be done. This routine returns
-/* OP_COMPLETE if the accept completed synchronously,
-/* or OP_RUNNING if the output remains ongoing or there was an error.
-/*
-/************************************************************************/
-
-int
-gdb_start_a_listen(half_con, otherside, lenp, fdp)
-HALF_CONNECTION half_con; /* write on this connection*/
-char *otherside; /* put first byte here */
-int *lenp; /* number of bytes to read */
-int *fdp;
-{
-
- /*
- * Fill in the initial state of the attempted accept
- */
- half_con->accepted_len = lenp;
- half_con->next_byte = otherside;
- half_con->accepted_fdp = fdp;
-
- /*
- * Now see if we can make some progress on this read, possibly
- * even completing it synchronously. Return appropriate
- * result to our caller.
- */
- if(gdb_listen(half_con))
- return OP_COMPLETE;
- else
- return OP_RUNNING;
-}
-\f
-/************************************************************************/
-/*
-/* gdb_listen (gdb_listen)
-/*
-/* This routine is called from gdb_start_a_listen or hcon_progress to attempt
-/* to continue making progress in accepting a connection on a
-/* listening connection.
-/*
-/************************************************************************/
-
-int
-gdb_listen(hc)
-struct half_con_data *hc; /* pointer to control struct */
- /* for this half connection */
-{
- register HALF_CONNECTION half_con = hc;
- /* half connection pointer */
- /* fast copy in register */
-
- fd_set tst_bits; /* these are used for */
- /* the select we do prior */
- /* to reading which tells */
- /* us whether 0 byte read */
- /* means empty or closed */
- int selected; /* TRUE iff select says */
- /* we should be able to */
- /* progress */
-
-
- GDB_INIT_CHECK
-
- half_con->flags &= ~HCON_PENDING_LISTEN;/* in case we succeed */
-
- FD_ZERO(&tst_bits);
- FD_SET(half_con->fd,&tst_bits);
- selected = select(gdb_mfd,&tst_bits, NULL, NULL, &gdb_notime);
- /*
- * If selected==(-1), then we know there's something
- * wrong with the socket
- */
- if (selected == (-1)) {
- gdb_conok = FALSE;
- return FALSE;
- }
- /*
- * if selected==0, then we know accept won't do anything, so
- * don't try.
- */
- if (selected == 0) {
- half_con->flags |= HCON_PENDING_LISTEN;
- FD_SET(half_con->fd, &gdb_crfds); /* we'll be looking for */
- /* this whenever we select*/
- return FALSE;
- }
- /*
- * Selected is >0. The accept SHOULD not hang.
- */
- *(half_con->accepted_fdp) = accept(half_con->fd, half_con->next_byte,
- half_con->accepted_len);
- /*
- * See whether the accept succeeded
- */
- if (*(half_con->accepted_fdp) < 0) {
- perror("gdb: start_listening: error on listen");
- GDB_GIVEUP("Unexpected listen error");
- }
-
- FD_CLR(half_con->fd, &gdb_crfds); /* don't select on this */
- return TRUE;
-}
+++ /dev/null
-/*
- * $Source$
- * $Header$
- */
-
-#ifndef lint
-static char *rcsid_tfsr_c = "$Header$";
-#endif lint
-
-/************************************************************************/
-/*
-/* tfsr (test forking server)
-/* --------------------------
-/*
-/* Author: Noah Mendelsohn (IBM T.J. Watson Research and MIT Project
-/* Athena)
-/*
-/* Copyright: 1986 MIT Project Athena
-/*
-/************************************************************************/
-/*
-/* PURPOSE
-/* -------
-/*
-/* A GDB server program demonstrating techniques for asynchronously
-/* communicating with an arbitrary number of clients by forking
-/* a new server process for each incoming client.
-/*
-/* Each forked child receives a stream of integers,
-/* which it interprets as ASCII characters. The characters are
-/* converted to uppercase, and then sent back to the client from
-/* which they came.
-/*
-/* NOTE
-/* ----
-/*
-/* This program is interface compatible with tsr.c. Clients
-/* cannot tell which style of server they are using.
-/*
-/************************************************************************/
-
-#include <stdio.h>
-#include "gdb.h"
-
-
-int
-main(argc, argv)
-int argc;
-char *argv[];
-{
- /*----------------------------------------------------------*/
- /*
- /* LOCAL VARIABLES
- /*
- /*----------------------------------------------------------*/
-
- CONNECTION client; /* talk on this to client */
-
- int data; /* receive data here */
-
-\f /*----------------------------------------------------------*/
- /*
- /* EXECUTION BEGINS HERE
- /*
- /* Check parameters
- /*
- /*----------------------------------------------------------*/
-
- if (argc != 2) {
- fprintf(stderr,"Correct form is %s <servicename>\n",
- argv[0]);
- exit(4);
- }
-
- /*----------------------------------------------------------*/
- /*
- /* Initialize
- /*
- /*----------------------------------------------------------*/
-
- gdb_init(); /* set up gdb */
-
- /*----------------------------------------------------------*/
- /*
- /* Now, turn ourselves into a forking server.
- /*
- /*----------------------------------------------------------*/
-
- client = create_forking_server(argv[1],NULL);
- fprintf(stderr,"forked\n");
-
- /*----------------------------------------------------------*/
- /*
- /* Here we are in the child process for each client.
- /* Echo the characters.
- /*
- /*----------------------------------------------------------*/
-
- while (TRUE) {
- if (receive_object(client, &data, INTEGER_T) ==
- OP_CANCELLED) {
- fprintf(stderr,"receive error\n");
- exit(4);
- }
- if (data >= 'a' && data <= 'z')
- data += 'A'-'a'; /* upcase the response */
- if (send_object(client, &data, INTEGER_T) ==
- OP_CANCELLED) {
- fprintf(stderr,"send error\n");
- exit(4);
- }
- }
-}
+++ /dev/null
-/*
- * $Source$
- * $Header$
- */
-
-#ifndef lint
-static char *rcsid_tsr_c = "$Header$";
-#endif lint
-
-/************************************************************************/
-/*
-/* tsr (test server)
-/* -----------------
-/*
-/* Author: Noah Mendelsohn (IBM T.J. Watson Research and MIT Project
-/* Athena)
-/*
-/* Copyright: 1986 MIT Project Athena
-/*
-/************************************************************************/
-/*
-/* PURPOSE
-/* -------
-/*
-/* A GDB server program demonstrating techniques for asynchronously
-/* communicating with an arbitrary number of clients from a single
-/* Unix server process. This server accepts GDB connections from
-/* clients as requests come in (up to the arbitrary maximum
-/* MAXCLIENTS.) On each connection, it receives a stream of integers,
-/* which it interprets as ASCII characters. The characters are
-/* converted to uppercase, and then sent back to the client from
-/* which they came.
-/*
-/* All of this is done completely asynchronously. No client is
-/* locked out while characters are being echoed to another, and
-/* new connections are accepted at any time.
-/*
-/* NOTES
-/* -----
-/*
-/* 1) The complete state of each client is kept in the array
-/* named client. The client[i].state variable indicates whether
-/* client i is active, and if so, the client[i].action variable
-/* indicates what kind of asynchronous activity the client is
-/* engaged in. Note that these are local conventions, having
-/* nothing to do with GDB or its interfaces.
-/*
-/* 2) Communication to each client is done over its connection,
-/* named client[i].con.
-/*
-/* 3) There is at most one asynchronous activity pending to
-/* each client at any given time, and its state is tracked
-/* in the variable named client[i].pending_op. The operation
-/* may be a send, a receive, or an accept, depending on
-/* the contents of client[i].action. These operations are
-/* allocated when the server starts up, and then re-used
-/* repeatedly. They are the GDB analog of a lightweight process,
-/* which is activated when queued on a connection.
-/*
-/* 4) A special form of connection and a special listening operation
-/* are used for asynchronously listening for new connection
-/* requests. These are 'listencon' and 'listenop' respectively.
-\f/*
-/* 5) GDB includes a special form of select which waits for
-/* completion of operations as well as for activity on user
-/* specified file descriptors. The list of operations to be
-/* monitored is stored in the variable
-/* named op_list. The call to op_select_any hangs until one
-/* or more of these operations complete, then terminates.
-/*
-/* 6) The main server loop acts on any new connection requests,
-/* processes any newly completed activity on the active
-/* clients, then drops into op_select_any to allow asynchronous
-/* activities to progress.
-/*
-/*
-/************************************************************************/
-
-#include <stdio.h>
-#include "gdb.h"
-
-extern int errno;
-
-\f
-/************************************************************************/
-/*
-/* DECLARATIONS
-/*
-/************************************************************************/
-
-#define MAXCLIENTS 10
-
- /*----------------------------------------------------------*/
- /*
- /* State of each possible client
- /*
- /*----------------------------------------------------------*/
-
-struct client {
- int state; /* state of this client */
- /* descriptor */
-#define CL_DEAD 1 /* client not started */
-#define CL_STARTING 2 /* accepted, reply ongoing */
-#define CL_UP 3 /* ready to go */
- int action; /* what are we doing now */
-#define CL_RECEIVE 4 /* waiting for a packet */
-#define CL_SEND 5 /* sending a packet */
-#define CL_ACCEPT 6 /* sending a reply */
- CONNECTION con; /* connection to this */
- /* client, if any */
- OPERATION pending_op; /* pending operation */
- /* on this connection, */
- /* if any */
- int data; /* the character to echo */
- /* goes here, expressed as */
- /* an int */
-};
-
-struct client client[MAXCLIENTS];
-
- /*----------------------------------------------------------*/
- /*
- /* Connections and operations for listening for
- /* new clients.
- /*
- /*----------------------------------------------------------*/
-
-CONNECTION listencon; /* listen on this */
- /* connection */
-OPERATION listenop; /* this operation is used */
- /* repeatedly for listening */
- /* for new clients */
-
-int nextcl = 0; /* index of the next client */
- /* we'll accept */
-
-\f /*----------------------------------------------------------*/
- /*
- /* Miscellaneous variables used in acquiring connections.
- /* These are ignored in a simple server like this; a
- /* more sophisticated server might want to validate the
- /* names of its clients before accepting connections.
- /*
- /*----------------------------------------------------------*/
-
-TUPLE client_tuple; /* client request goes */
- /* here */
-char otherside[100];
-int othersize;
-
-
-\f
-/************************************************************************/
-/*
-/* MAIN
-/*
-/************************************************************************/
-
-int
-main(argc, argv)
-int argc;
-char *argv[];
-{
- /*----------------------------------------------------------*/
- /*
- /* LOCAL VARIABLES
- /*
- /*----------------------------------------------------------*/
-
- register int i; /* loop index */
- LIST_OF_OPERATIONS op_list; /* for op_select_any */
-
- /*----------------------------------------------------------*/
- /*
- /* Check parameters
- /*
- /*----------------------------------------------------------*/
-
- if (argc != 2) {
- fprintf(stderr,"Correct form is %s <servicename>\n",
- argv[0]);
- exit(4);
- }
-
- /*----------------------------------------------------------*/
- /*
- /* Initialize
- /*
- /*----------------------------------------------------------*/
-
- gdb_init(); /* set up gdb */
- init_clients(); /* null the client states */
- do_listen(argv[1]); /* start the listening */
- /* connection and queue */
- /* a listening operation */
- make_oplist(&op_list); /* create wait list */
-
- /*----------------------------------------------------------*/
- /*
- /* Loop forever taking care of business.
- /*
- /* 1) If any new connection requests have come in,
- /* accept them.
- /*
- /* 2) For each client on which some activity is newly
- /* completed, take care of it.
- /*
- /*----------------------------------------------------------*/
-
- while (TRUE) {
- if (OP_DONE(listenop))
- new_connection();
- for (i=0; i<MAXCLIENTS; i++) {
- if (OP_DONE(client[i].pending_op))
- do_client(i);
- }
- if(op_select_any(op_list, 0, NULL, NULL, NULL, NULL)==(-1)) {
- perror("op_select_any returned error");
- exit(32);
- }
- }
-}
-\f
-/************************************************************************/
-/*
-/* do_client
-/*
-/* An operation has completed on the specified client.
-/*
-/************************************************************************/
-
-int
-do_client(id)
-int id;
-{
- register struct client *cp = &(client[id]);
-
- /*
- * If there has been an error, shutdown the client.
- */
- connection_perror(cp->con, "Unix error on send or receive");
- /* print error if any */
- if (connection_status(cp->con) != CON_UP ||
- OP_STATUS(cp->pending_op) == OP_CANCELLED) {
- sever_connection(cp->con);
- reset_operation(cp->pending_op);
- cp->state = CL_DEAD;
- cp->action = 0;
- return;
- }
- /*
- * The operation completed successfully. Figure out what it was
- * and do the right thing.
- */
- switch (cp->action) {
- case CL_ACCEPT:
- case CL_SEND:
- start_receiving_object(cp->pending_op, cp->con,
- (char *)&cp->data,
- INTEGER_T);
- cp->action = CL_RECEIVE;
- break;
- case CL_RECEIVE:
- if (cp->data >= 'a' && cp->data <= 'z')
- cp->data += 'A'-'a'; /* upcase the response */
- start_sending_object(cp->pending_op, cp->con,
- (char *)&cp->data,
- INTEGER_T);
- cp->action = CL_SEND;
- }
-}
-\f
-/************************************************************************/
-/*
-/* init_clients
-/*
-/************************************************************************/
-
-int
-init_clients()
-{
- register struct client *c;
-
- for (c=client; c<client+MAXCLIENTS; c++){
- c->state = CL_DEAD;
- c->action = 0;
- c->con = NULL;
- c->pending_op = create_operation();
- reset_operation(c->pending_op);
- }
-}
-
-
-
-/************************************************************************/
-/*
-/* make_oplist
-/*
-/************************************************************************/
-
-int
-make_oplist(oplp)
-LIST_OF_OPERATIONS *oplp;
-{
- /*
- * ugh! we've got to fix create_list_of_operations to be
- * more flexible!!
- */
-
- *oplp = create_list_of_operations(MAXCLIENTS+1, listenop,
- client[0].pending_op,
- client[1].pending_op,
- client[2].pending_op,
- client[3].pending_op,
- client[4].pending_op,
- client[5].pending_op,
- client[6].pending_op,
- client[7].pending_op,
- client[8].pending_op,
- client[9].pending_op);
-}
-\f/************************************************************************/
-/*
-/* do_listen
-/*
-/* Do the one time setup for listening for clients, and
-/* also start a listen for an actual client.
-/*
-/************************************************************************/
-
-int
-do_listen(service)
-char *service;
-{
-
- /*----------------------------------------------------------*/
- /*
- /* Make a listening connection
- /*
- /*----------------------------------------------------------*/
-
- fprintf(stderr, "Server creating listening connection\n");
- listencon = create_listening_connection(service);
-
- if (listencon == NULL || connection_status(listencon) != CON_UP) {
- if(connection_status(listencon) == CON_STOPPING) {
- connection_perror(listencon,
- "Unix error creating listening connection");
- }
- fprintf(stderr,"tsr: could not create listening connection\n");
- exit (4);
- }
-
- /*----------------------------------------------------------*/
- /*
- /* On that connection, put up an operation to listen
- /* for our first client.
- /*
- /*----------------------------------------------------------*/
-
- listenop = create_operation();
-
- othersize = sizeof(otherside);
-
- start_accepting_client(listencon, listenop, &(client[nextcl].con),
- (char *)otherside,
- &othersize, &client_tuple);
-
-}
-\f
-/************************************************************************/
-/*
-/* new_connection
-/*
-/* We have just gotten a connection for client nextcl.
-/*
-/************************************************************************/
-
-int
-new_connection()
-{
- register struct client *cp = &client[nextcl];
- /*
- * Make sure there's been no error
- */
- if(connection_status(listencon) != CON_UP) {
- connection_perror(listencon, "Unix error on listening connection");
- fprintf(gdb_log, "Listening connection has died.\n");
- exit(8);
- }
- if(OP_STATUS(listenop) != OP_COMPLETE ||
- cp->con == NULL || connection_status(cp->con) != CON_UP) {
- fprintf(stderr,"Error on listening operation\n");
- if (cp->con != NULL &&
- connection_status(cp->con)==CON_STOPPING) {
- connection_perror(cp->con,
- "Error on newly started client connection.");
- sever_connection(cp->con);
- cp->con = NULL;
- } else
- exit(8);
- } else {
- /*
- * Set up the new connection and reply to the client
- */
- cp->state = CL_STARTING;
- cp->action = CL_ACCEPT;
- start_replying_to_client(cp->pending_op, cp->con, GDB_ACCEPTED,
- "", "");
- /*
- * Find a new free connection descriptor. Blow up if
- * we've used the last one
- */
- for (nextcl=0; nextcl<MAXCLIENTS; nextcl++)
- if (client[nextcl].state == CL_DEAD)
- break;
-
- if (nextcl == MAXCLIENTS) {
- fprintf(stderr,"Too many clients, giving up\n");
- exit(8);
- }
- }
- /*
- * Start listening again
- */
- reset_operation(listenop);
- othersize = sizeof(otherside);
-
- start_accepting_client(listencon, listenop, &(client[nextcl].con),
- (char *)otherside,
- &othersize, &client_tuple);
-
-
-}
+++ /dev/null
-/*
- * $Source$
- * $Header$
- */
-
-#ifndef lint
-static char *rcsid_tst_c = "$Header$";
-#endif lint
-
-/************************************************************************/
-/*
-/* tst.c
-/*
-/* A test program for the client library interface.
-/*
-/* Author: Noah Mendelsohn
-/*
-/* Copyright: 1986 MIT Project Athena
-/*
-/************************************************************************/
-
-#include <stdio.h>
-#include "gdb.h"
-
-char *field_names[] = {"desc",
- "code",
- "man",
- "cost",
- "count"};
-FIELD_TYPE field_types[] = {STRING_T, /* desc */
- INTEGER_T, /* code */
- STRING_T, /* man */
- REAL_T, /* cost */
- INTEGER_T}; /* count */
-
-FILE *coded_file;
-
-int
-main(argc, argv)
-int argc;
-char *argv[];
-{
-
-\f /************************************************************
- * DECLARATIONS *
- ************************************************************/
-
- /*
- * Declare the names of fields to be retrieved and their types
- */
-
- int field_count = 5;
- int i;
- /*
- * The following defines are for convenience in addressing
- * the fields.
- */
-
-#define DESC 0
-#define CODE 1
-#define MAN 2
-#define COST 3
-#define COUNT 4
-
- /*
- * Declare the relation and related data structures for
- * storing the retrieved data.
- */
-
- TUPLE_DESCRIPTOR tuple_desc;
- RELATION retrieved_data, decoded_rel;
-
- /*
- * Declarations for misc. variables
- */
-
- TUPLE t; /* next tuple to print */
- int coded_len;
- char *coded_dat;
-
- int rc; /* A return code */
-
-\f /************************************************************
- * EXECUTION BEGINS HERE *
- ************************************************************/
-
- /*
- * Build the descriptor describing the layout of the tuples
- * to be retrieved, and create an empty relation into which
- * the retrieval will be done.
- */
-
- gdb_init(); /* initialize the global */
- /* database facility */
-
- printf("tst.c: attempting to create tuple descriptor\n");
-
- tuple_desc = create_tuple_descriptor(field_count, field_names,
- field_types);
-
- printf("tst.c: tuple desc created.. attempting to create relation\n");
- retrieved_data = create_relation(tuple_desc);
-
- printf("tst.c: relation created, formatting descriptor\n");
-
-
- print_tuple_descriptor("Test Tuple Descriptor", tuple_desc);
-
- printf("tst.c: descriptor formatted, formatting relation\n");
-
- print_relation("Test Relation", retrieved_data);
-
- printf("Creating tuples\n");
-
- for (i=0; i<3; i++) {
- t = create_tuple(tuple_desc);
-
- initialize_tuple(t);
-
- fprintf(stderr, "Following tuple should contain null fields:\n\n");
-
- print_tuple("A NULL TUPLE", t);
-
- *(int *)FIELD_FROM_TUPLE(t, CODE) = i+1;
-
- *(double *)FIELD_FROM_TUPLE(t, COST) = 12.34 * (i+1);
- string_alloc((STRING *)FIELD_FROM_TUPLE(t,MAN), 20);
- strcpy(STRING_DATA(*((STRING *)FIELD_FROM_TUPLE(t,MAN))),
- "Manager field data");
- ADD_TUPLE_TO_RELATION(retrieved_data, t);
- }
-
-
- printf("tst.c: relation initialized, formatting relation\n");
-
- print_relation("Test Relation", retrieved_data);
-
-
-/*
- * Try to encode the entire relation!!
- */
-
- printf("Attempting to encode the relation\n");
-
- coded_len = FCN_PROPERTY(RELATION_T, CODED_LENGTH_PROPERTY)
- (&retrieved_data, NULL);
-
- coded_dat = (char *)malloc(coded_len);
-
- FCN_PROPERTY(RELATION_T, ENCODE_PROPERTY)
- (&retrieved_data, NULL, coded_dat);
-
- printf("Relation encoding complete, writing file \n\n");
-
- coded_file = fopen("coded.dat", "w");
-
- fwrite(coded_dat, 1, coded_len, coded_file);
-
- fclose(coded_file);
-
- printf("File written\n");
-
- printf("Decoding relation\n\n");
-
- FCN_PROPERTY(RELATION_T, DECODE_PROPERTY)
- (&decoded_rel, NULL, coded_dat);
-
- printf("Relation decoded!! Printing it\n\n");
-
- print_relation("Decoded Relation", decoded_rel);
-
-
- printf("tst.c: exit\n");
-
- return 0;
-}
# Imakefile for include.
#
-CODE=Imakefile gdb.h mit-copyright.h \
- moira.h moira_site.h mr_proto.h update.h ureg_proto.h
+CODE=Imakefile mit-copyright.h \
+ moira.h moira_site.h update.h ureg_proto.h
SRCDIR=$(SRCTOP)/include
all::
+++ /dev/null
-/*
- * $Header$
- */
-
-/************************************************************************
- *
- * gdb.h
- *
- * Includes for the global database facility (gdb)
- *
- * Author: Noah Mendelsohn
- * Copyright: 1986 MIT Project Athena
- *
- ************************************************************************/
-
-/*
- * Note: following include may safely be done redundantly, so it doesn't
- * matter if caller does it too. We need it for fd_set.
- */
-#include <sys/types.h>
-#include <sys/time.h>
-
-#include <stdio.h>
-
-#ifndef TRUE
-#define TRUE 1
-#endif
-
-#ifndef FALSE
-#define FALSE 0
-#endif
-
-#ifndef max
-#define max(a,b) ((a)>(b)?(a):(b))
-#endif
-
-#ifndef min
-#define min(a,b) ((a)<(b)?(a):(b))
-#endif
-
-#ifndef NFDBITS
-#define NFDBITS 32
-#endif
-#ifndef howmany
-#define howmany(x, y) (((x)+((y)-1))/(y))
-#endif
-#ifndef FD_SETSIZE
-#define FD_SETSIZE 256
-#endif
-#ifndef FD_SET
-#define FD_SET(n, p) ((p)->fds_bits[(n)/NFDBITS] |= (1 << ((n) % NFDBITS)))
-#define FD_CLR(n, p) ((p)->fds_bits[(n)/NFDBITS] &= ~(1 << ((n) % NFDBITS)))
-#define FD_ISSET(n, p) ((p)->fds_bits[(n)/NFDBITS] & (1 << ((n) % NFDBITS)))
-#define FD_ZERO(p) bzero(p, sizeof(*(p)))
-typedef long fd_mask;
-typedef struct fd_set {fd_mask fds_bits[howmany(FD_SETSIZE, NFDBITS)];} fd_set;
-#endif
-
-/* 32-bit type for consistency in data sent across the network */
-#ifdef SIXTYFOUR
-#define int32 int
-#define uint32 u_int
-#else
-#define int32 long
-#define uint32 u_long
-#endif
-
- /*----------------------------------------------------------*
- *
- * GDB_GIVEUP
- *
- * This macro is called with a string argument whenever a
- * fatal error is encounterd. If you re-define this
- * macro, you can control the disposition of fatal gdb
- * errors.
- *
- * The gdb library must be recompiled for the change to
- * take effect. That will have to be fixed sometime.
- *
- *----------------------------------------------------------*/
-
-#define GDB_GIVEUP(errormsg) g_givup(errormsg);
-
- /*----------------------------------------------------------*
- *
- * GDB_ROUNDUP
- *
- * Round a number up to the next specified boundary.
- *
- *----------------------------------------------------------*/
-
-#define GDB_ROUNDUP(n,b) ((((n)+(b)-1)/(b))*(b))
-
-extern int gdb_Options; /* Control optional features */
-#define GDB_OPT_KEEPALIVE 1
-extern int gdb_Debug; /* debugging flags are */
- /* stored here */
-extern FILE *gdb_log; /* file descriptor for */
- /* logging gdb output */
-
-
-/*
- * connection failure indicator
- *
- * This variable is used to communicate between gdb_move_data and
- * g_con_progress without passing an extra parameter through lots
- * of procedure calls. When set to FALSE, it indicates that the
- * connection currently being processed has encountered a fatal error
- * and should be severed.
- */
-extern int gdb_conok;
-/************************************************************************
- *
- * USER IDENTIFICATION
- *
- * gdb_init puts the user's i.d. and hostname as strings here.
- *
- ************************************************************************/
-
-extern char *gdb_uname; /* user's string name */
-extern char *gdb_host; /* name of local host */
- /* goes here */
-
-
-\f
-/************************************************************************
- *
- *
- * TYPE MANAGEMENT
- *
- * Declarations used to control the definition and use of 'types'
- * as supported by the global database system. Most of the
- * declarations for 'System Defined Types' will not be of concern
- * to typical users, with the exception of the type names like
- * INTEGER_T which are defined below.
- *
- * In this implementation, user defined types are added
- * dynamically during execution by calling the appropriate
- * functions. The define GDB_MAX_TYPES below sets the maximum
- * total number of types, including both system and user defined,
- * which the system can support for any one application. When
- * GDB_MAX_TYPES is changed, the libary must be re-built. Space
- * for a two dimensional array, with one word for each property
- * of each possible type, is allocated statically in the library.
- *
- ************************************************************************/
-
-
-/*
- * Maximum number of types we can support, including both system and
- * user defined.
- */
-
-#define GDB_MAX_TYPES 50
-
-typedef int FIELD_TYPE; /* data needed to repre- */
- /* sent a system or user */
- /* defined data type */
- /* This is actualy just */
- /* a row index in the type */
- /* definition table */
-
- /*----------------------------------------------------------
- *
- * System defined types
- *
- * WARNING: Any changes to these type definitions must be
- * carefully matched with the initializations in the
- * gdb_i_stype routine in gdb_stype.c. Mistakes in these
- * tables may be VERY difficult to debug. Be careful!
- *
- *----------------------------------------------------------*/
-
-/*
- * Primitive types for ingres data
- */
-
-#define INTEGER_T (0)
-#define STRING_T (1)
-#define REAL_T (2)
-#define DATE_T (3)
-
-/*
- * Structured types
- */
-
-#define TUPLE_DESCRIPTOR_T (4)
-#define TUPLE_T (5)
-#define TUPLE_DATA_T (6)
-#define RELATION_T (7)
-
-/*
- * Number of system defined types
- *
- * This will always be equal to one more than index of last system type
- */
-
-#define SYSTEM_TYPE_COUNT 8
-
- /*----------------------------------------------------------
- *
- * Type descriptor tables
- *
- *----------------------------------------------------------*/
-
-/*
- * gdb_prop_union
- *
- * Each entry in the type definition table is a union of this form,
- * which allows us to store a choice of an integer, a function
- * pointer, or a pointer to a character string.
- */
-
-union gdb_prop_union {
- int i; /* when we want as an */
- /* integer */
- int (*f)(); /* as a function pointer */
- char *cp; /* character pointer */
- char *(*cpf)(); /* string function pointer */
-};
-
-#define TYPE_PROPERTY_COUNT 8 /* number of properties */
- /* for each type*/
-
-/*
- * Uses of the type properties. Each type has a set of properties.
- * Some are integers, some are functions. The defines below descrive
- * respectively the 0'th,1'st, 2'nd, etc. properties of EACH type.
- *
- * Note: TYPE_PROPERTY_COUNT (above) must be changed when new properties
- * are added. For system defined types, bindings for the properties
- * are done in gdb_i_stype in the gdb_stype.c source file.
- */
-
-#define LENGTH_PROPERTY 0 /* integer */
-#define ALIGNMENT_PROPERTY 1 /* integer */
-#define NULL_PROPERTY 2 /* function */
-#define CODED_LENGTH_PROPERTY 3 /* function */
-#define ENCODE_PROPERTY 4 /* string function */
-#define DECODE_PROPERTY 5 /* string function */
-#define FORMAT_PROPERTY 6 /* function */
-#define NAME_PROPERTY 7 /* char pointer */
-
-/*
- * gdb_type_def
- *
- * Information to describe a single type
- */
-
-typedef union gdb_prop_union gdb_type_def[TYPE_PROPERTY_COUNT];
-
-
-/*
- * g_type_table
- *
- * This is the table where the actual definitions for the types are
- * kept.
- */
-
-extern gdb_type_def g_type_table[GDB_MAX_TYPES];
-extern int gdb_n_types; /* number of entries in */
- /* table */
-
- /*----------------------------------------------------------
- *
- * Macros for accessing properties
- *
- *----------------------------------------------------------*/
-
-#define INT_PROPERTY(type, prop) (g_type_table[type][prop].i)
-#define STR_PROPERTY(type, prop) (g_type_table[type][prop].cp)
-#define FCN_PROPERTY(type, prop) (*g_type_table[type][prop].f)
-
-\f
-/************************************************************************
- *
- * STRUCTURED DATA
- *
- * Stuff needed to declare and manage TUPLES, TUPLE_DESCRIPTORS
- * and RELATIONS.
- *
- ************************************************************************/
-
- /*----------------------------------------------------------
- *
- * TUPLE_DESCRIPTOR
- *
- *----------------------------------------------------------*/
-
-#define GDB_DESC_ID 0x54504400 /* "TPD" */
-
-struct tupld_var { /* the variable length */
- /* stuff in a tuple */
- /* descriptor*/
- char *name; /* string name of field */
- FIELD_TYPE type; /* type of this field */
- int offset; /* byte offset of this field */
- /* relative to first byte of */
- /* data (not start of whole */
- /* tuple) */
- int length; /* Length of the actual data */
- /* for this field. Note that */
- /* alignment requirements of */
- /* following field are NOT */
- /* included in this length */
-};
-struct tupl_desc {
- int32 id; /* this should say TPD\0 */
- int ref_count; /* when this goes to zero, */
- /* the descriptor may really */
- /* be reclaimed */
- int field_count; /* number of fields in */
- /* the tuple*/
- int data_len; /* length of the data in */
- /* the actual tuple */
- int str_len; /* length of the strings */
- /* stored off the end of */
- /* this descriptor*/
- struct tupld_var var[1]; /* one of these for each */
- /* field, but the C compiler */
- /* won't accept the[] decl, */
- /* because it's afraid of */
- /* uncertain length*/
-};
-
-typedef struct tupl_desc *TUPLE_DESCRIPTOR; /* use this to declare a */
- /* tuple descriptor anchor */
-
-#define gdb_descriptor_length(num_flds) (sizeof(struct tupl_desc) + ((num_flds)-1) * sizeof(struct tupld_var))
-
- /*----------------------------------------------------------
- *
- * TUPLE
- *
- * tuple_dat is allocated by the create_tuple routine.
- *
- * TUPLE may be used in user code to declare a handle
- * on a tuple.
- *
- *----------------------------------------------------------*/
-
-#define GDB_TUP_ID 0x54555000
-
-typedef struct tuple_dat *TUPLE; /* handle on a tuple */
-
-struct tuple_dat {
- TUPLE next, prev; /* chain pointers when */
- /* tuples are linked, as in */
- /* a relation. Convention is*/
- /* double linked, circular.*/
- int32 id; /* should say TUP\0 */
- TUPLE_DESCRIPTOR desc; /* pointer to the descriptor */
- char data[1]; /* data goes here, word */
- /* aligned. Should be [] */
- /* if compiler would allow */
-};
-
-
- /*----------------------------------------------------------
- *
- * RELATION
- *
- * rel_dat is allocated by the create_relation
- * routine.
- *
- * RELATION may be used in user code to declare a handle
- * on a relation.
- *
- *----------------------------------------------------------*/
-
-#define GDB_REL_ID 0x52454c00
-
-struct rel_dat {
- TUPLE first, last; /* chain pointers to tuples */
- /* note that first->prev and */
- /* last->next are both == */
- /* &rel-dat. Maintenance is */
- /* simplified by keeping */
- /* as the first fields in */
- /* both rel_dat and tuple_dat*/
- /* a minor non-portability */
- int32 id; /* should contain REL\0 */
- TUPLE_DESCRIPTOR desc; /* descriptor for the tuples */
- /* in this relation. Should */
- /* == that in each tuple */
-
-};
-
-typedef struct rel_dat *RELATION; /* handle on a relation */
-\f
-/************************************************************************
- *
- * transport LAYER DECLARATIONS
- *
- * Declares the state maintenance structures for full duplex
- * connections with asynchronous transmission capability. Most
- * users need only know that the type CONNECTION is defined, and
- * that it may be treated as a pointer for most purposes (i.e. it
- * is compact, and copying it does not actually copy the connection
- * state.)
- *
- ************************************************************************/
-
-#define GDB_PROTOCOL_VERSION 0x01 /* version of the gdb */
- /* protocols that we're */
- /* observing */
-#define GDB_STREAM_BUFFER_SIZE 4096 /* amount to read in */
- /* one chunk from tcp stream*/
-#define GDB_PORT htons(9425) /* temporary until we use */
- /* services properly */
-#define GDB_BIND_RETRY_COUNT 5 /* number of times to */
- /* retry a bind before */
- /* giving up. Used when */
- /* accepting connections */
-#define GDB_BIND_RETRY_INTERVAL 10 /* Number of seconds to wait */
- /* between attempts to bind */
- /* the listening socket */
-#define GDB_MAX_CONNECTIONS 40 /* maximum number of */
- /* connections that */
- /* any one process can */
- /* control simultaneously */
-/*
- * Circumvent bug in ACIS 4.2 socket support
- */
-#ifdef ibm032
-#define GDB_MAX_SOCK_WRITE 2047 /* rt can't do socket */
- /* writes longer than this */
- /* gives errno 40*/
-#else
-#define GDB_MAX_SOCK_WRITE 0x00ffffff
-#endif
-
- /*----------------------------------------------------------
- *
- * Declarations for HALF_CONNECTIONS
- *
- * Each full duplex connection has associated with it
- * two simplex half-connections, each of which
- * has its own queue of pending operations. The
- * following describes the state of a half-connection.
- *
- *----------------------------------------------------------*/
-
-struct half_con_data {
- /*
- * these two must be first to match position in OPERATION
- */
- struct oper_data *op_q_first; /* first item in q of pending*/
- /* operations for this */
- /* half-connection. (chained */
- /* circularly to half con, */
- /* NOT the con. */
- struct oper_data *op_q_last; /* last item in q of pending*/
- /* operations for this */
- /* half-connection*/
- int status; /* values are of type */
- /* OPSTATUS. tells whether */
- /* transmit/receive is */
- /* pending.*/
- int32 flags; /* bit flags */
-#define HCON_PROGRESS 0x00000001 /* used by selection */
- /* routines to detect */
- /* progress */
-#define HCON_LISTEN 0x00000002 /* indicates that this is */
- /* a special half connection */
- /* used only for listenting */
- /* to incoming connection */
- /* requests */
-#define HCON_UNUSED 0x00000004 /* on a one-way connection, */
- /* this flag marks an unused */
- /* half */
-#define HCON_PENDING_LISTEN 0x00000008 /* a queued op on this half */
- /* connection is actually */
- /* trying to listen */
-#define HCON_BUSY 0x00000010 /* we are currently making */
- /* progress on this half */
- /* connection. Used to */
- /* detect re-entrance of */
- /* hcon_progress */
- int fd; /* main half duplex file */
- /* descriptor for this h_conn*/
- int oob_fd; /* file descriptor for */
- /* out of band signals*/
- char *next_byte; /* next byte to send/recv */
- int remaining; /* number of bytes remaining */
- /* to send/receive */
- char *stream_buffer; /* points to a buffer */
- /* used to pre-read/write */
- /* the stream to avoid */
- /* window thrashing */
- int stream_buffer_length; /* length of the stream */
- /* buffer */
- char *stream_buffer_next; /* next byte to read in */
- /* stream buffer */
- int stream_buffer_remaining; /* number of bytes currently */
- /* in use in stream buffer*/
- int *accepted_fdp; /* used only for listening */
- /* connections. latest */
- /* accepted fd is put where*/
- /* this points */
- int *accepted_len; /* ptr to length of 'from' */
- /* data on accept */
-
-};
-
-typedef struct half_con_data *HALF_CONNECTION;
-
-
- /*----------------------------------------------------------
- *
- * Declarations for CONNECTIONS
- *
- *----------------------------------------------------------*/
-
-#define GDB_CON_ID 0x434f4e00 /*"CON"*/
-
-struct con_data {
- int32 id; /* should contain CON\0 */
- int status; /* See definitions below. */
- /* Do not confuse with */
- /* the status sub-fields of */
- /* in and out half-cons. */
- int version; /* the version of the */
- /* protocol being observed */
- /* on this connection */
- int errno; /* the real errno gets */
- /* copied here if it causes */
- /* the connection to die */
- int (*oob_fcn)(); /* pointer to function to */
- /* call when something */
- /* arrives on the out of */
- /* band channel */
- struct half_con_data in, out; /* states of the inbound */
- /* and outbound half */
- /* sessions.*/
-};
-
-typedef struct con_data *CONNECTION; /* the only externally */
- /* visible handle on a */
- /* connection*/
-
-/*
- * Definitions of status fields.
- *
- * WARNING: If you change any of the following, there are coordinated
- * changes to be made in gdb_debug.c
- */
-
-#define CON_STOPPED 1 /* never started, terminated */
-#define CON_UP 2 /* ready to use */
-#define CON_STARTING 3 /* trying to start */
-#define CON_STOPPING 4 /* trying to stop */
-
-/*
- * The following are used as keywords when distinguishing input from output
- * half connections.
- */
-#define CON_INPUT 1 /* this is an input half */
- /* session*/
-#define CON_OUTPUT 2 /* this is an output half */
- /* session*/
-
-
- /*----------------------------------------------------------
- *
- * gdb_cons
- *
- * This is the array of connection control data
- * structures for gdb. Every connection has its
- * structure stored here, but they are in no
- * particular order. Because the connection data
- * itself cannot be moved (due to possible dangling
- * pointers), there may be some unused connections
- * in the middle of this array. gdb_mcons is the
- * 1 based number of the highest connection which is
- * actually in use at this time. This is a considerable
- * optimization for the typical case where very few
- * are in use, and turnover is low.
- *
- * These are externs for globals defined in gdb_lib.h
- * and included by gdb.c.
- *
- *----------------------------------------------------------*/
-
-extern int gdb_mcons; /* one based number of the */
- /* highest connection */
- /* descriptor we're using */
- /* at the moment */
-
-extern int gdb_mfd; /* number of the highest */
- /* file descriptor in use */
- /* for a connection */
-extern struct con_data gdb_cons[GDB_MAX_CONNECTIONS];
- /* actual connection data */
- /* is stored here */
-
-extern fd_set gdb_crfds, gdb_cwfds, gdb_cefds; /* connection related file */
- /* descriptor maps to be */
- /* used in select */
-extern fd_set last_crfds, last_cwfds, last_cefds;/* these file desc. bit */
- /* masks are set up */
- /* for each select call */
- /* to include the user */
- /* supplied and the */
- /* connection related */
- /* fd's */
-
- /*----------------------------------------------------------
- *
- * OPERATIONS
- *
- *----------------------------------------------------------*/
-
-#define GDB_OP_ID 0x4f505200
-
-struct oper_data {
- struct oper_data *next, *prev; /* forward and back chain */
- int32 id; /* should contain OPR\0 */
- int tag; /* unique identifier for */
- /* this operation */
- int status; /* current state of this */
- /* oaperation*/
- int flags;
-#define OPF_MARKED_COMPLETE 0x00000001 /* operation was already */
- /* complete when opsel was */
- /* called*/
-#define OPF_MARKED_CANCELLED 0x00000002 /* operation was already */
- /* cancelled when opsel was */
- /* called*/
- int result; /* when the operation */
- /* completes, this field is */
- /* set to reflect its dispos-*/
- /* ition. Contents of this */
- /* field will depend on the */
- /* operation being performed */
- HALF_CONNECTION halfcon; /* the half connection on */
- /* which this operation is */
- /* queued */
- /* May be meaningless if not */
- /* queued..*/
- char *arg; /* pointer to user data */
- union fcn {
- int (*init)(); /* pointer to routine to */
- /* call to start this */
-
- /* operation*/
- int (*cont)(); /* pointer to function to */
- /* be called when this */
- /* logical operation */
- /* continues*/
- } fcn;
-
- int (*cancel)(); /* this routine gets called */
- /* to handle a cancel request*/
- /* this field is normally */
- /* set to NULL when the */
- /* operation is queued and */
- /* updated as required by */
- /* the init and cont funcs. */
-};
-
-typedef struct oper_data OPERATION_DATA; /* an actual operation */
- /* descritor, creator is */
- /* responsible for making */
- /* sure that memory is not */
- /* freed while in use */
-typedef OPERATION_DATA *OPERATION; /* a handle on an operation */
- /* this is what most */
- /* applications will use */
-
- /*----------------------------------------------------------
- *
- * STATES OF AN OPERATION
- *
- * These represent the state of an asynchronous, queued
- * operation. For convenience of the application programmer,
- * some of these are folded together when queried through the
- * operation_status routine. In particular, operation status
- * returns only one of:
- *
- * OP_NOT_RUNNING, OP_RUNNING, OP_COMPLETE, or
- * OP_CANCELLED.
- *
- * Any other status is reported as OP_RUNNING. This is
- * done on the assumption that it makes correct coding
- * of applications less error-prone, as there are fewer
- * cases to check, and most of them would not be of
- * interest anyway.
- *
- * Note that OP_CANCELLED may be generated by the system
- * even when no explicit request for cancellation has been
- * issued. For example, this may occur when a connection
- * is severed unexpectedly.
- *
- * WARNING: If you change any of the following, be sure
- * to make the equivalent changes to gdb_debug.c.
- *
- * We also define here certain standard values of OP_RESULT,
- * since some return conventions presume that op_status
- * and op_result values are orthogonal.
- *
- *----------------------------------------------------------*/
-
-#define OP_SUCCESS 0 /* this value is actually */
- /* used only in result */
- /* fields, but it is */
- /* sometimes convenient to */
- /* have status and result */
- /* have orthogonal values */
-#define OP_NOT_STARTED 1 /* this operation has been */
- /* initialized but is not on */
- /* any connection's queue */
-#define OP_QUEUED 2 /* this operation is on */
- /* some connection's queue */
- /* but it has not yet */
- /* reached the head of the q */
-#define OP_RUNNING 3 /* op is at head of q trying */
- /* to progress */
-#define OP_COMPLETE 4 /* operation has run to */
- /* completion. result field */
- /* is now valid */
-#define OP_CANCELLING 5 /* we are in the process of */
- /* (trying to) cancel this */
- /* operation */
-#define OP_CANCELLED 6 /* operation was prematurely */
- /* terminated. result field */
- /* is NOT valid. */
-#define OP_MARKED 7 /* used by op_select_all */
-#define OP_REQUEUED 8 /* returned by an init or */
- /* continuation routine to */
- /* indicate that the */
- /* operation has requeued */
- /* itself */
-#define OP_PREEMPTED 9 /* returned by an init or */
- /* continuation routine to */
- /* indicate that the op has */
- /* preempted itself by */
- /* queueing a new operation */
- /* ahead of itself */
-
- /*----------------------------------------------------------
- *
- * LIST_OF_OPERATIONS
- *
- *----------------------------------------------------------*/
-
-struct oper_list {
- int count; /* number of operations */
- /* in the list */
- OPERATION op[1]; /* really op[n], but */
- /* structs must have a */
- /* definite length */
-};
-
-typedef struct oper_list *LIST_OF_OPERATIONS; /* handle on a list */
-
-#define size_of_list_of_operations(n) \
- (sizeof(struct oper_list) + (n-1)*sizeof(OPERATION))
-
- /*----------------------------------------------------------
- *
- * gdb_notime
- *
- * Pass this to select when doing a poll.
- *
- *----------------------------------------------------------*/
-
-extern struct timeval gdb_notime;
-
-\f
-/************************************************************************
- *
- * CHECKING ROUTINES IMPLEMENTED AS MACROS
- *
- ************************************************************************/
-
-extern char g_errstr[150]; /* build emsgs here */
-
-#define GDB_INIT_CHECK g_chk_init(); /* make sure gdb_init */
- /* was called */
-
-#define GDB_CHECK_CON(con, where) if ((con)->id != GDB_CON_ID) \
- { (void) sprintf(g_errstr, "Invalid connection descriptor passed to \"%s\"\n", where); \
- GDB_GIVEUP(g_errstr) }
-
-
-#define GDB_CHECK_TUP(tup, where) if ((tup)->id != GDB_TUP_ID) \
- { (void) sprintf(g_errstr, "Invalid tuple passed to \"%s\"\n", where); \
- GDB_GIVEUP(g_errstr) }
-
-
-#define GDB_CHECK_TPD(tpd, where) if ((tpd)->id != GDB_DESC_ID) \
- { (void) sprintf(g_errstr, "Invalid tuple descriptor passed to \"%s\"\n", where); \
- GDB_GIVEUP(g_errstr) }
-
-
-#define GDB_CHECK_REL(rel, where) if ((rel)->id != GDB_REL_ID) \
- { (void) sprintf(g_errstr, "Invalid relation passed to \"%s\"\n", where); \
- GDB_GIVEUP(g_errstr) }
-
-#define GDB_CHECK_OP(op, where) if ((op)->id != GDB_OP_ID) \
- { (void) sprintf(g_errstr, "Invalid operation passed to \"%s\"\n", where); \
- GDB_GIVEUP(g_errstr) }
-
-#define GDB_CHECK_DB(db, where) if (db->id != GDB_DB_ID) \
- { (void) sprintf(g_errstr, "Invalid database handle passed to \"%s\"\n", where); \
- GDB_GIVEUP(g_errstr) }
-
-
-
-\f
-/************************************************************************
- *
- * TRANSPORT ROUTINES IMPLEMENTED AS MACROS
- *
- ************************************************************************/
-
- /*----------------------------------------------------------
- *
- * connection_status
- *
- * Returns the status of the indicated connection.
- * Possible return values are:
- *
- * CON_STOPPED never started or terminated
- * CON_UP currently usable
- * CON_STARTING transient state on way up
- * CON_STOPPING transient state on way down
- *
- *----------------------------------------------------------*/
-
-#define connection_status(con) ((con)->status)
-
- /*----------------------------------------------------------
- *
- * connection_errno
- *
- * When a connection dies due to an error on a system
- * call, the corresponding errno is recorded in the
- * connection descriptor. This macro returns that value.
- *
- *----------------------------------------------------------*/
-
-#define connection_errno(con) ((con)->errno)
-
-
-\f
-/************************************************************************
- *
- * SERVER/CLIENT MANAGEMENT
- *
- * Definitions used in starting and maintaining communication
- * between servers and clients (as opposed to peers.)
- *
- ************************************************************************/
-
-#define GDB_MAX_SERVER_RETRIES 3 /* maximum number of times */
- /* clients will accept */
- /* forwarding requests from */
- /* a given server */
-
-
-extern TUPLE_DESCRIPTOR gdb_tosrv; /* descriptor for request */
- /* tuples sent to the */
- /* server during negotiation*/
-
-extern TUPLE_DESCRIPTOR gdb_fmsrv; /* descriptor for request */
- /* tuples sent from the */
- /* server during negotiation*/
-
-#define GDB_MAX_SERVER_ID_SIZE 255 /* longest name of a server */
- /* that we can handle */
-#define GDB_MAX_SERVER_PARMS_SIZE 1023 /* longest parm string we */
- /* can exchange between */
- /* server and client*/
-
- /*----------------------------------------------------------
- *
- * The following are values returned in the disposition
- * field of the response tuple to indicate what the
- * server has decided to do about the connection
- * request.
- *
- *----------------------------------------------------------*/
-
-#define GDB_ACCEPTED 1
-#define GDB_REFUSED 2
-#define GDB_FORWARDED 3
-
- /*----------------------------------------------------------
- *
- * Global variables inherited by a child from a server
- * parent.
- *
- *----------------------------------------------------------*/
-
-extern TUPLE gdb_client_tuple; /* request tuple sent from */
- /* the client */
-
-extern char gdb_sockaddr_of_client[100]; /* this should really be */
- /* sockaddr_in, but I don't */
- /* want everyone to have */
- /* to include all those */
- /* big .h files */
-extern int gdb_socklen; /* length of above */
-
-
-\f
-/************************************************************************
- *
- * DATABASE MANAGEMENT
- *
- * This layer of GDB provides access to the services of a relational
- * database from anywhere in a GDB network.
- *
- ************************************************************************/
-
- /*----------------------------------------------------------
- *
- * GDB_DB_SERVICE
- *
- * The name of the service, as found in /etc/services,
- * for GDB database servers.
- *
- *----------------------------------------------------------*/
-
-#define GDB_DB_SERVICE "#9420"
-
- /*----------------------------------------------------------
- *
- * DATABASE
- *
- * Describes a client's active connection to a database.
- *
- *----------------------------------------------------------*/
-
-#define GDB_DB_ID 0x44420000 /* eye catcher */
-
-struct db_struct {
- int32 id; /* eye catcher */
- CONNECTION connection; /* the GDB connection */
- /* used to get at this */
- /* database */
- int status; /* status of this */
- /* database connection */
-#define DB_OPEN 1 /* database opened */
- /* successfully */
-#define DB_CLOSED 2 /* not open */
- char *name; /* pointer to string name */
- /* of the database, for */
- /* debugging */
- char *server; /* pointer to the i.d. */
- /* of the server, for */
- /* debugging */
-};
-
-typedef struct db_struct *DATABASE;
-
- /*----------------------------------------------------------
- *
- * Return codes from DB operations
- *
- *----------------------------------------------------------*/
-
-#define DB_NO_SUCH_OP 3
-
- /*----------------------------------------------------------
- *
- * Parameters which limit sizes of things
- *
- *----------------------------------------------------------*/
-
-#define GDB_MAX_QUERY_SIZE 2048 /* length of the longest */
- /* substituted query */
- /* string we can make */
-#define GDB_MAX_QUERY_FIELDS 100 /* maximum number of fields */
- /* we can retrieve in one */
- /* query */
-#define GDB_SIZE_OF_INGRES_TEXT 2001 /* number of chars in */
- /* largest ingres text */
- /* field */
-#define GDB_MAX_RETRIEVED_TEXT_FIELDS 60 /* maximum number of text */
- /* type fields we can */
- /* retrieve in a single */
- /* query. we hold this */
- /* down because stack space */
- /* is taken for max size of */
- /* each during query. */
-
- /*----------------------------------------------------------
- *
- * Return codes from database operations
- *
- *----------------------------------------------------------*/
-
-#define DB_PARSE_FAIL (-3) /* couldn't parse */
- /* the request string*/
-
-\f
-/************************************************************************
- *
- * DATABASE OPERATIONS IMPLEMENTED
- * AS MACROS
- *
- ************************************************************************/
-
- /*----------------------------------------------------------
- *
- * DB_STATUS
- *
- *----------------------------------------------------------*/
-
-#define DB_STATUS(dbhandle) ((dbhandle)->status)
-
-
-
-\f
-/************************************************************************
- *
- * STRING MANAGEMENT
- *
- * To allow dynamic manipulation of strings in gdb without
- * excessive memory re-allocation, we define a string as a
- * counted byte space. Though this space will frequently be used
- * to store a standard null terminated string, that is not
- * required.
- *
- * Current representation for a string is a pointer followed by
- * an integer length. A null pointer indicates a null string, in
- * which case the length is arbitrary. Any other pointer is to
- * memory which was allocated by db_alloc in which must be free'd
- * eventually with db_free.
- *
- ************************************************************************/
-
-typedef struct str_dat {
- char *ptr; /* pointer to the data */
- int length; /* length of the allocated */
- /* memory (not necessarily */
- /* length of null-term'd */
- /* string stored there) */
-} STRING;
-
- /*----------------------------------------------------------
- *
- * Macros for manipulating strings. These return
- * the actual data from the string and the size of
- * the data space respectively. To get the length of
- * the null terminated string which might be stored
- * there, use strlen(STRING_DATA(string)).
- *
- *----------------------------------------------------------*/
-
-
-#define STRING_DATA(str) ((str).ptr)
-#define MAX_STRING_SIZE(str) ((str).length)
-
-
-/************************************************************************
- *
- * MEMORY ALLOCATION
- *
- * db_alloc and db_free are the externally visible names of
- * the memory allocation services. These actually call the
- * routines pointed to by the vectors gdb_amv and gdb_fmv, which
- * default to the supplied routines gdb_am and gdb_fm. Users
- * may supply their own memory allocation by storing over the
- * vectors. This may be done prior to calling gdb_init to
- * insure that all dynamic memory is controlled by the user.
- *
- ************************************************************************/
-
-#define db_alloc (*gdb_amv)
-#define db_free (*gdb_fmv)
-
-extern char *gdb_am();
-extern int gdb_fm();
-
-extern char *((*gdb_amv)());
-extern int (*gdb_fmv)();
-
-\f
-/************************************************************************
- *
- * STRUCTURED DATA ROUTINES IMPLEMENTED AS MACROS
- *
- ************************************************************************/
-
- /*----------------------------------------------------------
- *
- * ADD_TUPLE_TO_RELATION
- *
- *----------------------------------------------------------*/
-
-#define ADD_TUPLE_TO_RELATION(relation, tuple) \
- { \
- (tuple)->prev = (relation)->last; \
- (tuple)->next = (TUPLE)(relation); \
- (relation)->last = tuple; \
- (tuple)->prev->next = tuple; \
- }
-
- /*----------------------------------------------------------
- *
- * ADD_TUPLE_AFTER_TUPLE
- *
- *----------------------------------------------------------*/
-
-#define ADD_TUPLE_AFTER_TUPLE(relation, tuple, prev_tuple) \
- { \
- (tuple)->prev = (prev_tuple)->next->prev; \
- (tuple)->next = (prev_tuple)->next; \
- (tuple)->next->prev = tuple; \
- (prev_tuple)->next = tuple; \
- }
-
- /*----------------------------------------------------------
- *
- * REMOVE_TUPLE_FROM_RELATION
- *
- *----------------------------------------------------------*/
-
-#define REMOVE_TUPLE_FROM_RELATION(relation, tuple) \
- { \
- (tuple)->prev->next = (tuple)->next; \
- (tuple)->next->prev = (tuple)->prev; \
- }
-
-
- /*----------------------------------------------------------
- *
- * DESCRIPTOR_FROM_TUPLE
- *
- *----------------------------------------------------------*/
-
-#define DESCRIPTOR_FROM_TUPLE(tuple) ((tuple)->desc)
-
- /*----------------------------------------------------------
- *
- * DESCRIPTOR_FROM_RELATION
- *
- *----------------------------------------------------------*/
-
-#define DESCRIPTOR_FROM_RELATION(relation) ((relation)->desc)
-
- /*----------------------------------------------------------
- *
- * REFERENCE_TUPLE_DESCRIPTOR
- *
- * Bumps the reference count for a tuple descriptor.
- * Intended only for internal use of GDB.
- *
- *----------------------------------------------------------*/
-
-#define REFERENCE_TUPLE_DESCRIPTOR(tpd) (++((tpd)->ref_count))
-
- /*----------------------------------------------------------
- *
- * UNREFERENCE_TUPLE_DESCRIPTOR
- *
- * Decrements the reference count for a tuple descriptor.
- * Intended only for internal use of GDB. Warning: it
- * is the user's responsibility to use delete_tuple_descriptor
- * instead of this macro in any situation in which the
- * reference count might go to zero.
- *
- *----------------------------------------------------------*/
-
-#define UNREFERENCE_TUPLE_DESCRIPTOR(tpd) (--((tpd)->ref_count))
-
- /*----------------------------------------------------------
- *
- * FIELD_FROM_TUPLE
- *
- *----------------------------------------------------------*/
-
-#define FIELD_FROM_TUPLE(tuple, field_index) \
- (((tuple)->data) + (((tuple)->desc)->var[field_index].offset))
-
- /*----------------------------------------------------------
- *
- * FIELD_OFFSET_IN_TUPLE
- *
- *----------------------------------------------------------*/
-
-#define FIELD_OFFSET_IN_TUPLE(tuple_descriptor, field_index) \
- ((tuple_descriptor)->var[field_index].offset)
-
- /*----------------------------------------------------------
- *
- * FIELD_TYPE_IN_TUPLE
- *
- *----------------------------------------------------------*/
-
-#define FIELD_TYPE_IN_TUPLE(tuple_descriptor, field_index) \
- ((tuple_descriptor)->var[field_index].type)
-
- /*----------------------------------------------------------
- *
- * FIRST_TUPLE_IN_RELATION
- *
- *----------------------------------------------------------*/
-
-#define FIRST_TUPLE_IN_RELATION(relation) \
- (((relation)->first) == (TUPLE)relation ? NULL : (relation)-> first)
-
- /*----------------------------------------------------------
- *
- * NEXT_TUPLE_IN_RELATION
- *
- *----------------------------------------------------------*/
-
-#define NEXT_TUPLE_IN_RELATION(relation, prev) \
- (((prev)->next) == (TUPLE)relation ? NULL : prev->next )
-
- /*----------------------------------------------------------
- *
- * PREV_TUPLE_IN_RELATION
- *
- *----------------------------------------------------------*/
-
-#define PREV_TUPLE_IN_RELATION(relation, next) \
- (((next)->prev) == (TUPLE) relation ? NULL : next->prev)
-
-
-
-\f
-/************************************************************************
- *
- * TRANSPORT and OPERATION SERVICES IMPLEMENTED AS MACROS
- *
- ************************************************************************/
-
- /*----------------------------------------------------------
- *
- * OPERATION_FROM_DATA
- *
- * Given OPERATION_DATA, return the corresponding handle
- * of type OPERATION. Currently, OPERATION is just
- * implemented as a pointer to OPERATION_DATA.
- *
- *----------------------------------------------------------*/
-
-#define OPERATION_FROM_DATA(op_data) \
- ((OPERATION)&(op_data))
-
- /*----------------------------------------------------------
- *
- * OP_TAG
- *
- * Return the tag for a given operation.
- *
- *----------------------------------------------------------*/
-
-#define OP_TAG(operation) ((operation)->tag)
-
- /*----------------------------------------------------------
- *
- * OP_STATUS
- *
- * Return the status of a given operation. Note that
- * status describes an operations progress through
- * execution. It has the same values for all operations.
- * Result describes the final outcome of an operation.
- * It's values depend on the particular operation which
- * was attempted.
- *
- *----------------------------------------------------------*/
-
-#define OP_STATUS(operation) ((operation)->status)
-
- /*----------------------------------------------------------
- *
- * OP_DONE
- *
- * True iff the operation is either OP_COMPLETE or
- * OP_CANCELLED.
- *
- *----------------------------------------------------------*/
-
-#define OP_DONE(op) ((op)->status == OP_COMPLETE || (op)->status == OP_CANCELLED)
-
- /*----------------------------------------------------------
- *
- * OP_RESULT
- *
- * Return the result of a given operation. Note that
- * status describes an operations progress through
- * execution. It has the same values for all operations.
- * Result describes the final outcome of an operation.
- * It's values depend on the particular operation which
- * was attempted. The special result value -1 is used
- * to indicate an invalid value for result. Generally,
- * -1 is returned when result is accidently queried at
- * a time when OP_STATUS != OPERATION_COMPLETE.
- *
- *----------------------------------------------------------*/
-
-#define OP_RESULT(operation) ((operation)->result)
-
-\f
-/************************************************************************
- *
- * Debugging Interfaces
- *
- ************************************************************************/
-
- /*----------------------------------------------------------
- *
- * The following operation codes my be passed to
- * gdb_debug to set special debugging modes of operation.
- *
- * Note that most of these are toggles
- *
- *----------------------------------------------------------*/
-
-#define GDB_LOG 0x00000001 /* turn on tracing to */
- /* log file */
-#define GDB_NOFORK 0x00000002 /* don't fork forking */
- /* servers */
-
-\f
-/************************************************************************
- *
- * Things which have to be at the end because they require
- * the various types to be defined first.
- *
- ************************************************************************/
-
-
-#ifdef DUMB_7_CHAR_LOADER
- /*----------------------------------------------------------
- *
- * Long Names for Routines
- *
- * External names in Unix must generally be unique
- * within the first 7 characters or so, at least for
- * some versions of ld. To account for this without
- * making all our routine names terribly short and
- * cryptic, we use the following defines.
- *
- *----------------------------------------------------------*/
-
-#define string_alloc gdb_sta
-#define string_free gdb_stf
-
-#define create_tuple_descriptor gdb_ctd
-#define delete_tuple_descriptor gdb_dtd
-#define field_index gdb_fi
-#define create_tuple gdb_ctup
-#define delete_tuple gdb_dtup
-#define initialize_tuple gdb_itup
-#define null_tuple_strings gdb_ntps
-
-#define create_relation gdb_crel
-#define delete_relation gdb_drel
-#define tuples_in_relation gdb_trel
-
-
-/*
- * Transport layer
- */
-#define create_operation gdb_crop
-#define delete_operation gdb_dop
-#define initialize_operation gdb_inop
-#define reset_operation gdb_rsop
-#define cancel_operation gdb_cnop
-
-#define create_list_of_operations gdb_clop
-#define delete_list_of_operations gdb_dlop
-
-#define op_select gdb_opsel
-#define op_select_any gdb_opsel
-#define op_select_all gdb_aopsel
-#define con_select gdb_cnsel
-
-#define gdb_receive_data gdb_rcdat
-#define gdb_send_data gdb_sndat
-#define gdb_start_listening gdb_stl
-#define start_accepting_client gdb_stac
-
-
-#define gdb_listen gdb_lis
-
-/*
- * Connection management
- */
-#define start_peer_connection gdb_spconn
-#define sever_connection gdb_svconn
-#define start_server_connection gdb_stsrv
-#define create_listening_connection gdb_clc
-#define start_replying_to_client gdb_strtc
-#define create_forking_server gdb_cfs
-
-
-/*
- * Asynchronous operations
- */
-#define start_sending_object gdb_snobj
-#define start_receiving_object gdb_rcobj
-#define preempt_and_start_receiving_object gdb_prcobj
-
-#define queue_operation gdb_qop
-
-#define requeue_operation g_req_op
-
-#define complete_operation gdb_cmpo
-/*
- * Synchronous operations
- */
-#define send_object gdb_sobj
-#define receive_object gdb_robj
-/*
- * Database operations
- */
-#define access_db gdb_adb
-#define start_accessing_db gdb_sadb
-#define perform_db_operation gdb_pdbo
-#define db_query gdb_dbq
-#define start_performing_db_operation gdb_spdb
-#define start_db_query gdb_sdbq
-#else
-#define op_select_any op_select
-#endif /* DUMB_7_CHAR_LOADER */
-
-extern char *string_alloc();
-extern int string_free();
-extern TUPLE_DESCRIPTOR create_tuple_descriptor();
-extern int delete_tuple_descriptor();
-extern int field_index();
-extern TUPLE create_tuple();
-extern int delete_tuple();
-extern int initialize_tuple();
-extern int null_tuple_strings();
-extern RELATION create_relation();
-extern int delete_relation();
-extern OPERATION create_operation();
-extern LIST_OF_OPERATIONS create_list_of_operations();
-extern OPERATION g_op_newhead();
-extern CONNECTION start_peer_connection();
-extern CONNECTION sever_connection();
-extern CONNECTION start_server_connection();
-extern CONNECTION create_listening_connection();
-extern CONNECTION create_forking_server();
-extern int start_sending_object();
-extern int start_receiving_object();
-extern int preempt_and_start_receiving_object();
-extern int queue_operation();
-extern int requeue_operation();
-extern int complete_operation();
-extern int send_object();
-extern int receive_object();
-
-
- /*----------------------------------------------------------
- *
- * Other routines requiring extern to avoid forward
- * reference to non integer type functions.
- *
- *----------------------------------------------------------*/
-
-extern CONNECTION g_make_con();
+++ /dev/null
-/* $Id$
- *
- * Copyright (C) 1987-1998 by the Massachusetts Institute of Technology
- *
- */
-
-#include <stdio.h>
-#include <gdb.h>
-#include <moira.h>
-
-typedef struct mr_params {
- uint32 mr_size;
- uint32 mr_version_no;
- union {
- u_long procno; /* for call */
- u_long status; /* for reply */
- } u;
-#define mr_procno u.procno
-#define mr_status u.status
- int mr_argc;
- char **mr_argv;
- int *mr_argl;
- char *mr_flattened;
- int mr_state;
-} mr_params;
-
-int mr_start_send(OPERATION op, HALF_CONNECTION hcon, struct mr_params *arg);
-int mr_start_recv(OPERATION op, HALF_CONNECTION hcon, struct mr_params **argp);
-void mr_destroy_reply(mr_params *reply);
#define log_ERROR 3
#define SERVICE_NAME "moira_update"
-#define UPDATE_BUFSIZ BUFSIZ
+/* For unknown reasons, we're running des_pcbc_encrypt in DEcrypt mode,
+ not ENcrypt, so we need to guarantee that the block size is a multiple
+ of 8 to prevent the data from being zero-padded. */
+#define UPDATE_BUFSIZ ((BUFSIZ + 7) & ~7)
-int send_file(char *pathname, char *target_path, int encrypt);
-int send_auth(char *hostname);
-int execute(char *path);
-void send_quit(void);
+int send_file(int conn, char *pathname, char *target_path, int encrypt);
+int send_auth(int conn, char *hostname);
+int execute(int conn, char *path);
+void send_quit(int conn);
#include <krb.h>
int get_mr_update_ticket(char *host, KTEXT ticket);
+
+extern char *whoami;
SRCS = afs.c ksrvtgt.c
CODE = $(SRCS)
CFLAGS = -I../include -I../lib -I$(AFSDIR)/include -I/usr/athena/include $(DBG)
-LIBS = -L../lib -L../gdb -L/usr/athena/lib -lmoira -lzephyr -lmrgdb -lcom_err -lhesiod -lkrb -ldes
+LIBS = -L../lib -L/usr/athena/lib -lmoira -lzephyr -lcom_err -lhesiod -lkrb -ldes
SRCDIR = $(SRCTOP)/incremental
program(afs.incr,afs.o,,${AFSLIBS} ${LIBS},${PROGDIR})
# Imakefile for GDB library.
#
-SRCS = mr_auth.c mr_call.c mr_connect.c mr_data.c mr_init.c \
- mr_query.c mr_param.c mr_access.c mr_ops.c \
+SRCS = mr_auth.c mr_call.c mr_connect.c mr_init.c \
+ mr_query.c mr_access.c mr_ops.c \
fixname.c strs.c fixhost.c nfsparttype.c sq.c hash.c \
idno.c critical.c gdss_convert.c kname_unparse.c
-OBJECTS = mr_auth.o mr_call.o mr_connect.o mr_data.o mr_init.o \
- mr_query.o mr_param.o mr_access.o mr_ops.o \
+OBJECTS = mr_auth.o mr_call.o mr_connect.o mr_init.o \
+ mr_query.o mr_access.o mr_ops.o \
fixname.o strs.o fixhost.o nfsparttype.o sq.o hash.o \
idno.o critical.o mr_et.o ureg_err.o krb_et.o gdss_et.o \
gdss_convert.o kname_unparse.o
RCSID("$Header$");
-int mr_access_internal(int argc, char **argv);
-
/*
* Check access to a named query.
*/
int mr_access(char *name, int argc, char **argv)
{
- char **nargv = malloc(sizeof(char *) * (argc + 1));
- int status = 0;
-
- if (!nargv)
- return ENOMEM;
- nargv[0] = name;
- memcpy(nargv + 1, argv, sizeof(char *) * argc);
- status = mr_access_internal(argc + 1, nargv);
- free(nargv);
- return status;
-}
-
-/*
- * Check access to a named query, where the query name is argv[0]
- * and the arguments are the rest of argv[].
- */
-int mr_access_internal(int argc, char **argv)
-{
+ mr_params params, reply;
int status;
- mr_params params_st;
- mr_params *params = NULL;
- mr_params *reply = NULL;
CHECK_CONNECTED;
- params = ¶ms_st;
- params->mr_version_no = sending_version_no;
- params->mr_procno = MR_ACCESS;
- params->mr_argc = argc;
- params->mr_argl = NULL;
- params->mr_argv = argv;
+ params.u.mr_procno = MR_ACCESS;
+ params.mr_argc = argc + 1;
+ params.mr_argv = malloc(sizeof(char *) * (argc + 1));
+ if (!params.mr_argv)
+ return ENOMEM;
+ params.mr_argv[0] = name;
+ memcpy(params.mr_argv + 1, argv, sizeof(char *) * argc);
+ params.mr_argl = NULL;
- if ((status = mr_do_call(params, &reply)) == 0)
- status = reply->mr_status;
+ if ((status = mr_do_call(¶ms, &reply)) == MR_SUCCESS)
+ status = reply.u.mr_status;
mr_destroy_reply(reply);
+ free(params.mr_argv);
return status;
}
#include "mr_private.h"
#include <ctype.h>
+#include <stdio.h>
#include <string.h>
#include <krb.h>
int mr_auth(char *prog)
{
int status;
- mr_params params_st;
+ mr_params params, reply;
char *args[2];
int argl[2];
char realm[REALM_SZ], host[BUFSIZ], *p;
- mr_params *params = ¶ms_st;
- mr_params *reply = NULL;
KTEXT_ST auth;
CHECK_CONNECTED;
- /* Build a Kerberos authenticator. */
-
- memset(host, 0, sizeof(host));
if ((status = mr_host(host, sizeof(host) - 1)))
return status;
status += ERROR_TABLE_BASE_krb;
return status;
}
- params->mr_version_no = sending_version_no;
- params->mr_procno = MR_AUTH;
- params->mr_argc = 2;
- params->mr_argv = args;
- params->mr_argl = argl;
- params->mr_argv[0] = (char *)auth.dat;
- params->mr_argl[0] = auth.length;
- params->mr_argv[1] = prog;
- params->mr_argl[1] = strlen(prog) + 1;
-
- if (sending_version_no == MR_VERSION_1)
- params->mr_argc = 1;
+ params.u.mr_procno = MR_AUTH;
+ params.mr_argc = 2;
+ params.mr_argv = args;
+ params.mr_argl = argl;
+ params.mr_argv[0] = (char *)auth.dat;
+ params.mr_argl[0] = auth.length;
+ params.mr_argv[1] = prog;
+ params.mr_argl[1] = strlen(prog) + 1;
- if ((status = mr_do_call(params, &reply)) == 0)
- status = reply->mr_status;
+ if ((status = mr_do_call(¶ms, &reply)) == MR_SUCCESS)
+ status = reply.u.mr_status;
mr_destroy_reply(reply);
#include <moira.h>
#include "mr_private.h"
+#include <errno.h>
+#include <netinet/in.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
RCSID("$Header$");
-int mr_do_call(struct mr_params *params, struct mr_params **reply)
+/* Moira RPC format:
+
+ 4-byte total length (including these 4 bytes)
+ 4-byte version number (MR_VERSION_2 == 2)
+ 4-byte opcode (from client) or status (from server)
+ 4-byte argc
+
+ 4-byte len, followed by null-terminated string, padded to 4-byte boundary
+ (the len doesn't include the padding)
+ ...
+
+ (followed by more packets if status was MR_MORE_DATA)
+
+ All numbers are in network byte order.
+*/
+
+int mr_do_call(struct mr_params *params, struct mr_params *reply)
{
+ int status;
+
CHECK_CONNECTED;
- if (!_mr_send_op)
- _mr_send_op = create_operation();
+ status = mr_send(_mr_conn, params);
+ if (status == MR_SUCCESS)
+ status = mr_receive(_mr_conn, reply);
+
+ if (status)
+ mr_disconnect();
+
+ return status;
+}
+
+int mr_send(int fd, struct mr_params *params)
+{
+ u_long length, written;
+ int i, *argl;
+ char *buf, *p;
+
+ length = 16; /* length + version + opcode/status + argc */
+
+ if (params->mr_argl)
+ {
+ argl = params->mr_argl;
+ for (i = 0; i < params->mr_argc; i++)
+ length += 8 + argl[i];
+ }
+ else
+ {
+ argl = malloc(params->mr_argc * sizeof(int));
+ if (params->mr_argc && !argl)
+ return ENOMEM;
+ for (i = 0; i < params->mr_argc; i++)
+ {
+ argl[i] = strlen(params->mr_argv[i]) + 1;
+ length += 8 + argl[i];
+ }
+ }
+
+ buf = malloc(length);
+ if (!buf)
+ {
+ if (!params->mr_argl)
+ free(argl);
+ return ENOMEM;
+ }
+ memset(buf, 0, length);
+
+ putlong(buf + 4, MR_VERSION_2);
+ putlong(buf + 8, params->u.mr_procno);
+ putlong(buf + 12, params->mr_argc);
- if (!_mr_recv_op)
- _mr_recv_op = create_operation();
+ for (i = 0, p = buf + 16; i < params->mr_argc; i++)
+ {
+ putlong(p, argl[i]);
+ memcpy(p += 4, params->mr_argv[i], argl[i]);
+ p += argl[i] + (4 - argl[i] % 4) % 4;
+ }
+ length = p - buf;
+ putlong(buf, length);
+
+ written = write(fd, buf, length);
+ free(buf);
+ if (!params->mr_argl)
+ free(argl);
- initialize_operation(_mr_send_op, mr_start_send, (char *)params, NULL);
- queue_operation(_mr_conn, CON_OUTPUT, _mr_send_op);
+ if (written != length)
+ return MR_ABORTED;
+ else
+ return MR_SUCCESS;
+}
+
+int mr_receive(int fd, struct mr_params *reply)
+{
+ u_long length, data;
+ ssize_t size, more;
+ char *p;
+ int i;
- initialize_operation(_mr_recv_op, mr_start_recv, (char *)reply, NULL);
- queue_operation(_mr_conn, CON_INPUT, _mr_recv_op);
+ memset(reply, 0, sizeof(struct mr_params));
- /* Block until operation done. */
- complete_operation(_mr_send_op);
- complete_operation(_mr_recv_op);
- /* Look at results */
- if ((OP_STATUS(_mr_send_op) != OP_COMPLETE) ||
- (OP_STATUS(_mr_recv_op) != OP_COMPLETE))
+ size = read(fd, &data, 4);
+ if (size != 4)
+ return size ? MR_ABORTED : MR_NOT_CONNECTED;
+ length = ntohl(data) - 4;
+ reply->mr_flattened = malloc(length);
+ if (!reply->mr_flattened)
+ return ENOMEM;
+
+ for (size = 0; size < length; size += more)
+ {
+ more = read(fd, reply->mr_flattened + size, length - size);
+ if (!more)
+ break;
+ }
+ if (size != length)
{
- mr_disconnect();
+ mr_destroy_reply(*reply);
return MR_ABORTED;
}
- return 0;
+
+ getlong(reply->mr_flattened, data);
+ if (data != MR_VERSION_2)
+ {
+ mr_destroy_reply(*reply);
+ return MR_VERSION_MISMATCH;
+ }
+
+ getlong(reply->mr_flattened + 4, reply->u.mr_status);
+ getlong(reply->mr_flattened + 8, reply->mr_argc);
+ reply->mr_argv = malloc(reply->mr_argc * sizeof(char *));
+ reply->mr_argl = malloc(reply->mr_argc * sizeof(int));
+ if (reply->mr_argc && (!reply->mr_argv || !reply->mr_argl))
+ {
+ mr_destroy_reply(*reply);
+ return ENOMEM;
+ }
+
+ for (i = 0, p = reply->mr_flattened + 12; i < reply->mr_argc; i++)
+ {
+ getlong(p, reply->mr_argl[i]);
+ reply->mr_argv[i] = p + 4;
+ p += 4 + reply->mr_argl[i] + (4 - reply->mr_argl[i] % 4) % 4;
+ }
+
+ return MR_SUCCESS;
+}
+
+void mr_destroy_reply(mr_params reply)
+{
+ free(reply.mr_argl);
+ free(reply.mr_argv);
+ free(reply.mr_flattened);
}
#include <moira_site.h>
#include "mr_private.h"
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <netinet/in.h>
+#include <netdb.h>
+
#include <errno.h>
#include <stdlib.h>
#include <string.h>
+#include <unistd.h>
#include <hesiod.h>
RCSID("$Header$");
-static char *mr_server_host = 0;
+int _mr_conn = 0;
+static char *mr_server_host = NULL;
+
+/* mrgdb compatibility magic
+
+ The data looks like this:
+
+ client -> server
+ 00000036 [length of rest of packet]
+ 00000004 [number of fields]
+ 01 01 01 01 [types of fields: 4 strings]
+ "server_id\0parms\0host\0user\0" [field names]
+ 00000001 [length of null-terminated server_id]
+ "\0" [server_id: ignored anyway]
+ 00000001 [length of null-terminated parms]
+ "\0" [parms: ignored anyway]
+ 00000001 [length of null-terminated client host]
+ "\0" [host: ignored anyway]
+ 00000001 [length of null-terminated client name]
+ "\0" [user: ignored anyway]
+
+ server -> client
+ 00000031 [length of rest of packet]
+ 00000003 [number of fields]
+ 00 01 01 [types of fields: int and 2 strings]
+ "disposition\0server_id\0parms\0" [field names]
+ 00000001 [GDB_ACCEPTED]
+ 00000001 [length of null-terminated server_id]
+ "\0" [server_id: ignored anyway]
+ 00000001 [length of null-terminated parms]
+ "\0" [parms: ignored anyway]
+
+*/
+
+static char challenge[58] = "\0\0\0\066\0\0\0\004\001\001\001\001server_id\0parms\0host\0user\0\0\0\0\001\0\0\0\0\001\0\0\0\0\001\0\0\0\0\001\0";
+static char response[53] = "\0\0\0\061\0\0\0\003\0\001\001disposition\0server_id\0parms\0\0\0\0\001\0\0\0\001\0\0\0\0\001\0";
/*
- * Open a connection to the mr server. Looks for the server name
+ * Open a connection to the moira server. Looks for the server name
* 1) passed as an argument, 2) in environment variable, 3) by hesiod
- * 4) compiled in default (from moira_site.h).
+ * 4) compiled in default
*/
int mr_connect(char *server)
{
- char *p, **pp, *sbuf = NULL;
+ char *port, **pp, *sbuf = NULL;
+ struct hostent *shost;
- if (!mr_inited)
- mr_init();
if (_mr_conn)
return MR_ALREADY_CONNECTED;
+ if (!mr_inited)
+ mr_init();
if (!server || (strlen(server) == 0))
server = getenv("MOIRASERVER");
if (!server || (strlen(server) == 0))
server = MOIRA_SERVER;
- if (!strchr(server, ':'))
+ shost = gethostbyname(server);
+ if (!shost)
+ return MR_CANT_CONNECT;
+
+ if (strchr(server, ':'))
{
- p = strchr(MOIRA_SERVER, ':');
- p++;
- sbuf = malloc(strlen(server) + strlen(p) + 2);
- if (!sbuf)
- return ENOMEM;
- sprintf(sbuf, "%s:%s", server, p);
+ int len = strcspn(server, ":");
+ sbuf = malloc(len + 1);
+ strncpy(sbuf, server, len);
+ sbuf[len - 1] = '\0';
server = sbuf;
+ port = strchr(server, ':') + 1;
+ }
+ else
+ port = strchr(MOIRA_SERVER, ':') + 1;
+
+ _mr_conn = mr_connect_internal(server, port);
+ free(sbuf);
+ if (!_mr_conn)
+ return MR_CANT_CONNECT;
+
+ /* stash hostname for later use */
+ mr_server_host = strdup(shost->h_name);
+ return MR_SUCCESS;
+}
+
+int mr_connect_internal(char *server, char *port)
+{
+ int fd, size, more;
+ struct sockaddr_in target;
+ struct hostent *shost;
+ char actualresponse[53];
+
+ shost = gethostbyname(server);
+ if (!shost)
+ return 0;
+
+ if (port[0] == '#')
+ target.sin_port = atoi(port + 1);
+ else
+ {
+ struct servent *s;
+ s = getservbyname(port, "tcp");
+ if (s)
+ target.sin_port = s->s_port;
+ else
+ return 0;
}
- errno = 0;
- _mr_conn = start_server_connection(server, "");
- if (_mr_conn == NULL)
- return errno;
- if (connection_status(_mr_conn) == CON_STOPPED)
+ memcpy(&target.sin_addr, shost->h_addr, shost->h_length);
+ target.sin_family = shost->h_addrtype;
+
+ fd = socket(AF_INET, SOCK_STREAM, 0);
+ if (fd < 0)
+ return 0;
+
+ if (connect(fd, (struct sockaddr *)&target, sizeof(target)) < 0)
{
- int status = connection_errno(_mr_conn);
- if (!status)
- status = MR_CANT_CONNECT;
- mr_disconnect();
- return status;
+ close(fd);
+ return 0;
}
- /*
- * stash hostname for later use
- */
+ /* Do magic mrgdb initialization */
+ size = write(fd, challenge, sizeof(challenge));
+ if (size != sizeof(challenge))
+ {
+ close(fd);
+ return 0;
+ }
+ for (size = 0; size < sizeof(actualresponse); size += more)
+ {
+ more = read(fd, actualresponse + size, sizeof(actualresponse) - size);
+ if (!more)
+ break;
+ }
+ if (size != sizeof(actualresponse))
+ {
+ close(fd);
+ return 0;
+ }
+ if (memcmp(actualresponse, response, sizeof(actualresponse)))
+ {
+ close(fd);
+ return 0;
+ }
- if (!sbuf)
- sbuf = strdup(server);
- mr_server_host = sbuf;
- if ((p = strchr(mr_server_host, ':')))
- *p = '\0';
- mr_server_host = canonicalize_hostname(mr_server_host);
- return 0;
+ /* You win */
+ return fd;
}
int mr_disconnect(void)
{
CHECK_CONNECTED;
- _mr_conn = sever_connection(_mr_conn);
+ close(_mr_conn);
+ _mr_conn = 0;
free(mr_server_host);
- mr_server_host = 0;
- return 0;
+ mr_server_host = NULL;
+ return MR_SUCCESS;
}
int mr_host(char *host, int size)
/* If we are connected, mr_server_host points to a valid string. */
strncpy(host, mr_server_host, size);
- return 0;
+ host[size - 1] = '\0';
+ return MR_SUCCESS;
}
int mr_noop(void)
{
int status;
- mr_params param_st;
- struct mr_params *params = NULL;
- struct mr_params *reply = NULL;
+ mr_params params, reply;
CHECK_CONNECTED;
- params = ¶m_st;
- params->mr_version_no = sending_version_no;
- params->mr_procno = MR_NOOP;
- params->mr_argc = 0;
- params->mr_argl = NULL;
- params->mr_argv = NULL;
+ params.u.mr_procno = MR_NOOP;
+ params.mr_argc = 0;
+ params.mr_argl = NULL;
+ params.mr_argv = NULL;
- if ((status = mr_do_call(params, &reply)) == 0)
- status = reply->mr_status;
+ if ((status = mr_do_call(¶ms, &reply)) == MR_SUCCESS)
+ status = reply.u.mr_status;
mr_destroy_reply(reply);
return status;
}
+
+
+/* Server side */
+
+int mr_listen(char *port)
+{
+ struct sockaddr_in sin;
+ int s, on = 1;
+
+ memset(&sin, 0, sizeof(sin));
+ if (port[0] == '#')
+ sin.sin_port = atoi(port + 1);
+ else
+ {
+ struct servent *s;
+ s = getservbyname(port, "tcp");
+ if (s)
+ sin.sin_port = s->s_port;
+ else
+ return -1;
+ }
+
+ s = socket(AF_INET, SOCK_STREAM, 0);
+ if (s < 0)
+ return -1;
+ if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(int)) < 0)
+ {
+ close(s);
+ return -1;
+ }
+ if (bind(s, (struct sockaddr *)&sin, sizeof(sin)) < 0)
+ {
+ close(s);
+ return -1;
+ }
+ if (listen(s, 5) < 0)
+ {
+ close(s);
+ return -1;
+ }
+
+ return s;
+}
+
+int mr_accept(int s, struct sockaddr_in *sin)
+{
+ int conn, addrlen = sizeof(struct sockaddr_in);
+ char lbuf[4], *buf;
+ long len, size, more;
+
+ conn = accept(s, (struct sockaddr *)sin, &addrlen);
+ if (conn < 0)
+ return -1;
+
+ /* Now do mrgdb accept protocol */
+ /* XXX timeout */
+
+ if (read(conn, lbuf, 4) != 4)
+ {
+ close(conn);
+ return -1;
+ }
+ getlong(lbuf, len);
+
+ buf = malloc(len);
+ if (!buf || len < 54)
+ {
+ close(conn);
+ free(buf);
+ return -1;
+ }
+
+ for (size = 0; size < len; size += more)
+ {
+ more = read(conn, buf + size, len - size);
+ if (!more)
+ break;
+ }
+ if (size != len)
+ {
+ close(conn);
+ free(buf);
+ return 0;
+ }
+
+ if (memcmp(buf, challenge + 4, 34))
+ {
+ close(conn);
+ free(buf);
+ return 0;
+ }
+
+ /* good enough */
+ free(buf);
+
+ if (write(conn, response, sizeof(response)) != sizeof(response))
+ {
+ close(conn);
+ return -1;
+ }
+ return conn;
+}
+++ /dev/null
-/* $Id$
- *
- * A few variables
- *
- * Copyright (C) 1987-1998 by the Massachusetts Institute of Technology
- * For copying and distribution information, please see the file
- * <mit-copyright.h>.
- */
-
-#include <mit-copyright.h>
-#include <moira.h>
-#include "mr_private.h"
-
-RCSID("$Header$");
-
-CONNECTION _mr_conn;
-
-OPERATION _mr_send_op, _mr_recv_op;
-
-int sending_version_no = MR_VERSION_2;
-
int mr_inited = 0;
-/* the reference to link_against_the_moira_version_of_gdb is to make
- * sure that this is built with the proper libraries.
- */
void mr_init(void)
{
- extern int link_against_the_moira_version_of_gdb;
if (mr_inited)
return;
- gdb_init();
initialize_sms_error_table();
initialize_krb_error_table();
- link_against_the_moira_version_of_gdb = 0;
+ initialize_gdss_error_table();
+ initialize_ureg_error_table();
mr_inited = 1;
}
int mr_do_update(void)
{
int status;
- mr_params param_st;
- struct mr_params *params = NULL;
- struct mr_params *reply = NULL;
+ mr_params params, reply;
CHECK_CONNECTED;
- params = ¶m_st;
- params->mr_version_no = sending_version_no;
- params->mr_procno = MR_DO_UPDATE;
- params->mr_argc = 0;
- params->mr_argl = NULL;
- params->mr_argv = NULL;
+ params.u.mr_procno = MR_DO_UPDATE;
+ params.mr_argc = 0;
+ params.mr_argl = NULL;
+ params.mr_argv = NULL;
- if ((status = mr_do_call(params, &reply)) == 0)
- status = reply->mr_status;
+ if ((status = mr_do_call(¶ms, &reply)) == MR_SUCCESS)
+ status = reply.u.mr_status;
mr_destroy_reply(reply);
int mr_motd(char **motd)
{
int status;
- mr_params param_st;
- struct mr_params *params = NULL;
- struct mr_params *reply = NULL;
+ mr_params params, reply;
static char *buffer = NULL;
*motd = NULL;
CHECK_CONNECTED;
- params = ¶m_st;
- params->mr_version_no = sending_version_no;
- params->mr_procno = MR_MOTD;
- params->mr_argc = 0;
- params->mr_argl = NULL;
- params->mr_argv = NULL;
-
- if ((status = mr_do_call(params, &reply)))
+ params.u.mr_procno = MR_MOTD;
+ params.mr_argc = 0;
+ params.mr_argl = NULL;
+ params.mr_argv = NULL;
+
+ if ((status = mr_do_call(¶ms, &reply)))
goto punt;
- while ((status = reply->mr_status) == MR_MORE_DATA)
+ while ((status = reply.u.mr_status) == MR_MORE_DATA)
{
- if (reply->mr_argc > 0)
+ if (reply.mr_argc > 0)
{
- buffer = realloc(buffer, reply->mr_argl[0] + 1);
+ buffer = realloc(buffer, reply.mr_argl[0] + 1);
if (!buffer)
{
mr_disconnect();
return ENOMEM;
}
- strcpy(buffer, reply->mr_argv[0]);
+ strcpy(buffer, reply.mr_argv[0]);
*motd = buffer;
}
mr_destroy_reply(reply);
- reply = NULL;
-
- initialize_operation(_mr_recv_op, mr_start_recv, &reply, NULL);
- queue_operation(_mr_conn, CON_INPUT, _mr_recv_op);
-
- complete_operation(_mr_recv_op);
- if (OP_STATUS(_mr_recv_op) != OP_COMPLETE)
+ if (mr_receive(_mr_conn, &reply) != MR_SUCCESS)
{
mr_disconnect();
- status = MR_ABORTED;
- return status;
+ return MR_ABORTED;
}
}
punt:
mr_destroy_reply(reply);
- /* for backwards compatability */
- if (status == MR_UNKNOWN_PROC)
- return 0;
- else
- return status;
+
+ return status;
}
+++ /dev/null
-/* $Id$
- *
- * Deal with mrgdb (bleah!)
- *
- * Copyright (C) 1987-1998 by the Massachusetts Institute of Technology
- * For copying and distribution information, please see the file
- * <mit-copyright.h>.
- *
- */
-
-#include <mit-copyright.h>
-#include <moira.h>
-#include "mr_private.h"
-
-#include <sys/types.h>
-#include <netinet/in.h>
-
-#include <stdlib.h>
-#include <string.h>
-
-RCSID("$Header$");
-
-/*
- * GDB operations to send and recieve RPC requests and replies.
- */
-
-/*
- * This doesn't get called until after the actual buffered write completes.
- * In a non-preflattening version of this, this would then queue the
- * write of the next bunch of data.
- */
-
-int mr_cont_send(OPERATION op, HALF_CONNECTION hcon, struct mr_params *arg)
-{
- op->result = OP_SUCCESS;
- free(arg->mr_flattened);
- arg->mr_flattened = NULL;
-
- return OP_COMPLETE;
-}
-
-int mr_start_send(OPERATION op, HALF_CONNECTION hcon,
- struct mr_params *arg)
-{
- int i, len;
- unsigned int mr_size;
- int *argl;
- char *buf, *bp;
-
- /*
- * This should probably be split into several routines.
- * It could also probably be made more efficient (punting most
- * of the argument marshalling stuff) by doing I/O directly
- * from the strings. Anyone for a scatter/gather mr_send_data?
- *
- * that would look a lot like the uio stuff in the kernel.. hmm.
- */
-
- /*
- * Marshall the entire data right now..
- * We are sending the version number,
- * total request size, request number,
- * argument count, and then each argument.
- * At least for now, each argument is a string, which is
- * sent as a count of bytes followed by the bytes
- * (including the trailing '\0'), padded
- * to a 32-bit boundary.
- */
-
- mr_size = 4 * sizeof(int32);
-
- argl = malloc(sizeof(int) * arg->mr_argc);
-
- /*
- * For each argument, figure out how much space is needed.
- */
-
- for (i = 0; i < arg->mr_argc; ++i)
- {
- if (arg->mr_argl)
- argl[i] = len = arg->mr_argl[i];
- else
- argl[i] = len = strlen(arg->mr_argv[i]) + 1;
- mr_size += sizeof(int32) + len;
- /* Round up to next 32-bit boundary.. */
- mr_size = sizeof(int32) * howmany(mr_size, sizeof(int32));
- }
-
- arg->mr_flattened = buf = malloc(mr_size);
-
- memset(arg->mr_flattened, 0, mr_size);
-
- arg->mr_size = mr_size;
-
- ((int32 *)buf)[0] = htonl(mr_size);
- ((int32 *)buf)[1] = htonl(arg->mr_version_no);
- ((int32 *)buf)[2] = htonl(arg->mr_procno);
- ((int32 *)buf)[3] = htonl(arg->mr_argc);
-
- /*
- * bp is a pointer into the point in the buffer to put
- * the next argument.
- */
-
- bp = (char *)(((int32 *)buf) + 4);
-
- for (i = 0; i < arg->mr_argc; ++i)
- {
- len = argl[i];
- *((int32 *)bp) = htonl(len);
- bp += sizeof(int32);
- memcpy(bp, arg->mr_argv[i], len);
- bp += sizeof(int32) * howmany(len, sizeof(int32));
- }
- op->fcn.cont = mr_cont_send;
- arg->mr_size = mr_size;
-
- free(argl);
-
- if (gdb_send_data(hcon, arg->mr_flattened, mr_size) == OP_COMPLETE)
- return mr_cont_send(op, hcon, arg);
- else
- return OP_RUNNING;
-}
-
-int mr_cont_recv(OPERATION op, HALF_CONNECTION hcon, mr_params **argp)
-{
- int done = FALSE;
- char *cp;
- int *ip;
- int i;
- mr_params *arg = *argp;
-
- while (!done)
- {
- switch (arg->mr_state)
- {
- case S_RECV_START:
- arg->mr_state = S_RECV_DATA;
- if (gdb_receive_data(hcon, (caddr_t)&arg->mr_size,
- sizeof(int32)) == OP_COMPLETE)
- continue;
- done = TRUE;
- break;
- case S_RECV_DATA:
- fflush(stdout);
- /* Should validate that length is reasonable */
- arg->mr_size = ntohl(arg->mr_size);
- if (arg->mr_size > 65536)
- return OP_CANCELLED;
- arg->mr_flattened = malloc(arg->mr_size);
- arg->mr_state = S_DECODE_DATA;
- memcpy(arg->mr_flattened, (caddr_t)&arg->mr_size, sizeof(int32));
-
- if (gdb_receive_data(hcon, arg->mr_flattened + sizeof(int32),
- arg->mr_size - sizeof(int32))
- == OP_COMPLETE)
- continue;
- done = TRUE;
- break;
- case S_DECODE_DATA:
- cp = arg->mr_flattened;
- ip = (int *) cp;
- /* we already got the overall length.. */
- for (i = 1; i < 4; i++)
- ip[i] = ntohl(ip[i]);
- arg->mr_version_no = ip[1];
- if (arg->mr_version_no != MR_VERSION_1 &&
- arg->mr_version_no != MR_VERSION_2)
- arg->mr_status = MR_VERSION_MISMATCH;
- else arg->mr_status = ip[2];
- arg->mr_argc = ip[3];
- cp += 4 * sizeof(int);
- arg->mr_argv = malloc(arg->mr_argc * sizeof(char *));
- arg->mr_argl = malloc(arg->mr_argc * sizeof(int));
-
- for (i = 0; i < arg->mr_argc; ++i)
- {
- u_short nlen = ntohl(*(int *)cp);
- cp += sizeof(int32);
- if (cp + nlen > arg->mr_flattened + arg->mr_size)
- {
- free(arg->mr_flattened);
- arg->mr_flattened = NULL;
- return OP_CANCELLED;
- }
- arg->mr_argv[i] = malloc(nlen);
- memcpy(arg->mr_argv[i], cp, nlen);
- arg->mr_argl[i] = nlen;
- cp += sizeof(int32) * howmany(nlen, sizeof(int32));
- }
- free(arg->mr_flattened);
- arg->mr_flattened = NULL;
- return OP_COMPLETE;
- }
- }
- return OP_RUNNING;
-}
-
-
-int mr_start_recv(OPERATION op, HALF_CONNECTION hcon, struct mr_params **argp)
-{
- mr_params *arg = *argp;
- if (!arg)
- {
- *argp = arg = malloc(sizeof(mr_params));
- arg->mr_argl = NULL;
- arg->mr_argv = NULL;
- arg->mr_flattened = NULL;
- }
- arg->mr_state = S_RECV_START;
- op->fcn.cont = mr_cont_recv;
- return mr_cont_recv(op, hcon, argp);
-}
-
-void mr_destroy_reply(mr_params *reply)
-{
- int i;
- if (reply)
- {
- if (reply->mr_argl)
- free(reply->mr_argl);
- reply->mr_argl = NULL;
- if (reply->mr_flattened)
- free(reply->mr_flattened);
- reply->mr_flattened = NULL;
- if (reply->mr_argv)
- {
- for (i = 0; i < reply->mr_argc; i++)
- {
- if (reply->mr_argv[i])
- free (reply->mr_argv[i]);
- reply->mr_argv[i] = NULL;
- }
- free(reply->mr_argv);
- }
- reply->mr_argv = NULL;
- free(reply);
- }
-}
* <mit-copyright.h>.
*/
-#include "mr_proto.h"
+#include <mit-copyright.h>
+#include <moira.h>
-extern CONNECTION _mr_conn;
-extern OPERATION _mr_send_op, _mr_recv_op;
+#include <sys/types.h>
-extern int mr_inited;
-extern int sending_version_no;
+#include <netinet/in.h>
+#include <arpa/inet.h>
-/*
- * You are in a maze of twisty little FSM's, all different.
- */
+extern int _mr_conn, mr_inited;
-#define S_RECV_START 1
-#define S_RECV_DATA 2
-#define S_DECODE_DATA 3
+typedef struct mr_params {
+ union {
+ u_long mr_procno; /* for call */
+ u_long mr_status; /* for reply */
+ } u;
+ int mr_argc;
+ char **mr_argv;
+ int *mr_argl;
+ unsigned char *mr_flattened;
+} mr_params;
-#define EVER (;;)
+#define CHECK_CONNECTED if (!_mr_conn) return MR_NOT_CONNECTED
-#define CHECK_CONNECTED {if (!_mr_conn) return MR_NOT_CONNECTED;}
+#define getlong(cp, l) l = ((((unsigned char *)cp)[0] * 256 + ((unsigned char *)cp)[1]) * 256 + ((unsigned char *)cp)[2]) * 256 + ((unsigned char *)cp)[3]
+#define putlong(cp, l) do { ((unsigned char *)cp)[0] = l >> 24; ((unsigned char *)cp)[1] = l >> 16; ((unsigned char *)cp)[2] = l >> 8; ((unsigned char *)cp)[3] = l; } while(0);
/* prototypes from mr_call.h */
-int mr_do_call(struct mr_params *params, struct mr_params **reply);
+int mr_do_call(struct mr_params *params, struct mr_params *reply);
+int mr_send(int fd, struct mr_params *params);
+int mr_receive(int fd, struct mr_params *params);
+void mr_destroy_reply(mr_params reply);
+
+/* prototypes from mr_connect.h */
+int mr_accept(int s, struct sockaddr_in *sin);
+int mr_connect_internal(char *server, char *port);
+int mr_listen(char *port);
/* prototypes from mr_init.c */
void mr_init(void);
-
-/* prototypes from mr_ops.c */
-int mr_complete_operation(OPERATION op);
-
-/* prototypes from mr_params.c */
-int mr_cont_send(OPERATION op, HALF_CONNECTION hcon, struct mr_params *arg);
-int mr_cont_recv(OPERATION op, HALF_CONNECTION hcon, mr_params **argp);
RCSID("$Header$");
-int mr_query_internal(int argc, char **argv,
- int (*callback)(int, char **, void *), void *callarg);
-
/*
* This routine is the primary external interface to the mr library.
*
int mr_query(char *name, int argc, char **argv,
int (*callproc)(int, char **, void *), void *callarg)
{
- char **nargv = malloc(sizeof(char *) * (argc + 1));
- int status = 0;
-
- if (!nargv)
- return ENOMEM;
- nargv[0] = name;
- memcpy(nargv + 1, argv, sizeof(char *) * argc);
- status = mr_query_internal(argc + 1, nargv, callproc, callarg);
- free(nargv);
- return status;
-}
-
-/*
- * This routine makes a Moira query.
- *
- * argv[0] is the query name.
- * argv[1..argc-1] are the query arguments.
- *
- * callproc is called once for each returned value, with arguments
- * argc, argv, and callarg.
- * If it returns a non-zero value, further calls to it are not done, and
- * all future data from the server is ignored (there should be some
- * way to send it a quench..)
- */
-
-int mr_query_internal(int argc, char **argv,
- int (*callproc)(int, char **, void *), void *callarg)
-{
- int status;
- mr_params params_st;
- mr_params *params = NULL;
- mr_params *reply = NULL;
- int stopcallbacks = 0;
+ int status, stopcallbacks = 0;
+ mr_params params, reply;
+ CHECK_CONNECTED;
if (level)
return MR_QUERY_NOT_REENTRANT;
- CHECK_CONNECTED;
- level++;
-
- params = ¶ms_st;
- params->mr_version_no = sending_version_no;
- params->mr_procno = MR_QUERY;
- params->mr_argc = argc;
- params->mr_argl = NULL;
- params->mr_argv = argv;
+ params.u.mr_procno = MR_QUERY;
+ params.mr_argc = argc + 1;
+ params.mr_argl = NULL;
+ params.mr_argv = malloc(sizeof(char *) * (argc + 1));
+ if (!params.mr_argv)
+ return ENOMEM;
+ params.mr_argv[0] = name;
+ memcpy(params.mr_argv + 1, argv, sizeof(char *) * argc);
- if ((status = mr_do_call(params, &reply)))
+ level++;
+ if ((status = mr_do_call(¶ms, &reply)))
goto punt;
- while ((status = reply->mr_status) == MR_MORE_DATA)
+ while ((status = reply.u.mr_status) == MR_MORE_DATA)
{
- if (!stopcallbacks && callproc)
- stopcallbacks = (*callproc)(reply->mr_argc, reply->mr_argv, callarg);
+ if (!stopcallbacks)
+ stopcallbacks = (*callproc)(reply.mr_argc, reply.mr_argv, callarg);
mr_destroy_reply(reply);
- reply = NULL;
-
- initialize_operation(_mr_recv_op, mr_start_recv, &reply, NULL);
- queue_operation(_mr_conn, CON_INPUT, _mr_recv_op);
- complete_operation(_mr_recv_op);
- if (OP_STATUS(_mr_recv_op) != OP_COMPLETE)
+ if (mr_receive(_mr_conn, &reply) != MR_SUCCESS)
{
mr_disconnect();
status = MR_ABORTED;
goto punt_1;
}
}
+
punt:
mr_destroy_reply(reply);
punt_1:
level--;
+ free(params.mr_argv);
+
return status;
}
directly. Instead, they are called by the routines that are described.
Be sure to link your application against these libraries:
--lmoira -lmrgdb -lcom_err -lkrb -ldes
+-lmoira -lcom_err -lkrb -ldes
.TP
Protocol functions
All protocol routines return 0 on success, or a value from
CODE=$(SRCS)
OBJS=requests.o reg_svr.o
-program(reg_svr, $(OBJS),,../server/libmrglue.a -lkadm $(LIBGDSS) $(CLIBS) $(SQL_LIB), ${PROGDIR})
+program(reg_svr, $(OBJS),,-lkadm $(LIBGDSS) $(CLIBS) $(SQL_LIB), ${PROGDIR})
program(startreg, startreg.o,,,${PROGDIR})
extern char admin_errmsg[];
+FILE *journal;
+char *whoami;
+
int parse_encrypted(struct msg *message, struct db_data *data);
int parse_encrypted(struct msg *message, struct db_data *data);
int db_callproc(int argc, char **argv, void *queue);
GDSS_INC=$(GDSSINC)
SRCS = startmoira.c mr_main.c mr_sauth.c mr_scall.c \
- mr_srvdata.c mr_shutdown.c mr_util.c \
+ mr_shutdown.c mr_util.c \
mr_server.h query.h qrtn.h qrtn.c \
qsupport.c qsubs.c queries2.c increment.c \
cache.c mr_glue.c qvalidate.c qaccess.c qsetup.c \
qfollow.c
CODE= startmoira.c mr_main.c mr_sauth.c mr_scall.c \
- mr_srvdata.c mr_shutdown.c mr_util.c \
+ mr_shutdown.c mr_util.c \
mr_server.h query.h qrtn.h qrtn.pc \
qsupport.pc qsubs.c queries2.c increment.pc \
cache.pc mr_glue.c qvalidate.pc Imakefile \
qaccess.pc qsetup.pc qfollow.pc
-SRVOBJ=mr_main.o mr_sauth.o mr_scall.o mr_srvdata.o mr_shutdown.o \
+SRVOBJ=mr_main.o mr_sauth.o mr_scall.o mr_shutdown.o \
mr_util.o qrtn.o queries2.o qsupport.o qsubs.o \
increment.o cache.o qvalidate.o \
qaccess.o qsetup.o qfollow.o
GLUOBJS=mr_glue.o qrtn.o queries2.o qsupport.o qsubs.o \
- ../lib/mr_et.o mr_sauth.o mr_srvdata.o \
+ ../lib/mr_et.o mr_sauth.o \
../lib/krb_et.o mr_util.o increment.o cache.o \
qvalidate.o qaccess.o qsetup.o qfollow.o
SRCDIR = $(SRCTOP)/server
sqlrule()
-program(moirad, ${SRVOBJ},${MR_LIBDEP} ${GDB_LIBDEP},\
+program(moirad, ${SRVOBJ},${MR_LIBDEP},\
-L/usr/athena/lib .././lib/libmoira.a -lzephyr \
- .././gdb/libmrgdb.a -lcom_err $(LIBGDSS)\
+ -lcom_err $(LIBGDSS)\
-lkrb -ldes -lhesiod ${SQL_LIB}, ${PROGDIR})
-program(startmoira, startmoira.o,${MR_LIBDEP} ${GDB_LIBDEP},\
+program(startmoira, startmoira.o,${MR_LIBDEP},\
-L/usr/athena/lib .././lib/libmoira.a -lzephyr \
- .././gdb/libmrgdb.a -lcom_err -lkrb -ldes -lhesiod,\
+ -lcom_err -lkrb -ldes -lhesiod,\
${PROGDIR})
library_obj_rule()
install_library_target(mrglue,$(GLUOBJS),$(SRCS))
i->next->prev = i->prev;
i->prev->next = i->next;
free(i);
+ break;
}
}
}
#include "qrtn.h"
#include <signal.h>
+#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
int mr_auth(char *prog)
{
struct passwd *pw;
- extern char *krb_realm;
char buf[1024];
CHECK_CONNECTED;
return KDC_PR_UNKNOWN + ERROR_TABLE_BASE_krb;
strcpy(pseudo_client.kname.name, pw->pw_name);
krb_get_lrealm(pseudo_client.kname.realm, 1);
- krb_realm = pseudo_client.kname.realm;
+ krb_get_lrealm(krb_realm, 1);
strcpy(buf, pw->pw_name);
strcat(buf, "@");
name_to_id(pseudo_client.kname.name, USERS_TABLE, &pseudo_client.users_id);
pseudo_client.client_id = pseudo_client.users_id;
strncpy(pseudo_client.entity, prog, 8);
- pseudo_client.args = malloc(sizeof(mr_params));
- pseudo_client.args->mr_version_no = MR_VERSION_2;
return 0;
}
#include <string.h>
#include <unistd.h>
+#include <krb.h>
+
RCSID("$Header$");
extern char *krb_get_lrealm(char *, int);
-extern CONNECTION newconn, listencon;
-
-extern int nclients;
-extern client **clients, *cur_client;
+client *cur_client;
-extern OPERATION listenop;
-extern LIST_OF_OPERATIONS op_list;
+char *whoami;
+char *takedown;
+FILE *journal;
-extern struct sockaddr_in client_addr;
-extern int client_addrlen;
-extern TUPLE client_tuple;
+time_t now;
-extern char *whoami;
-extern char buf1[BUFSIZ];
-extern char *takedown;
-extern FILE *journal;
+char *host;
+char krb_realm[REALM_SZ];
-extern time_t now;
+/* Client array and associated data. This needs to be global for _list_users */
+client **clients;
+int nclients, clientssize;
-char *host;
+int dormant;
void reapchild(int x);
void godormant(int x);
void gowakeup(int x);
-int do_listen(char *port);
-void do_reset_listen(void);
void clist_append(client *cp);
-void oplist_append(LIST_OF_OPERATIONS *oplp, OPERATION op);
-void oplist_delete(LIST_OF_OPERATIONS oplp, OPERATION op);
void mr_setup_signals(void);
-int new_connection(void);
/*
* Main Moira server loop.
int main(int argc, char **argv)
{
- int status, i;
+ int status, i, listener;
time_t tardy;
char *port, *p;
extern char *database;
struct stat stbuf;
struct utsname uts;
+ fd_set readfds, writefds, xreadfds, xwritefds;
+ int nfds, counter = 0;
whoami = argv[0];
/*
* Error handler init.
*/
- initialize_sms_error_table();
- initialize_krb_error_table();
- initialize_gdss_error_table();
+ mr_init();
set_com_err_hook(mr_com_err);
setvbuf(stderr, NULL, _IOLBF, BUFSIZ);
}
}
- /*
- * GDB initialization.
- */
- if (gdb_init() != 0)
- {
- com_err(whoami, 0, "GDB initialization failed.");
- exit(1);
- }
- gdb_debug(0); /* this can be patched, if necessary, to enable */
- /* GDB level debugging .. */
- krb_realm = malloc(REALM_SZ);
krb_get_lrealm(krb_realm, 1);
/*
{
if ((status = mr_open_database()))
{
- com_err(whoami, status, " when trying to open database.");
+ com_err(whoami, status, "trying to open database.");
exit(1);
}
sanity_check_database();
* Set up client array handler.
*/
nclients = 0;
- clients = malloc(0);
+ clientssize = 10;
+ clients = malloc(clientssize * sizeof(client *));
mr_setup_signals();
journal = fopen(JOURNAL, "a");
if (!journal)
{
- com_err(whoami, errno, " while opening journal file");
+ com_err(whoami, errno, "opening journal file");
exit(1);
}
/*
* Establish template connection.
*/
- if ((status = do_listen(port)))
+ if (!(listener = mr_listen(port)))
{
- com_err(whoami, status, " while trying to create listening connection");
+ com_err(whoami, status, "trying to create listening connection");
exit(1);
}
-
- op_list = create_list_of_operations(1, listenop);
+ FD_ZERO(&xreadfds);
+ FD_ZERO(&xwritefds);
+ FD_SET(listener, &xreadfds);
+ nfds = listener + 1;
com_err(whoami, 0, "started (pid %d)", getpid());
com_err(whoami, 0, rcsid);
while (!takedown)
{
int i;
- /*
- * Block until something happens.
- */
- if (dormant == SLEEPY)
+ struct timeval timeout;
+
+ /* If we're supposed to go down and we can, do it */
+ if ((dormant == AWAKE) && (nclients == 0) &&
+ (stat(MOIRA_MOTD_FILE, &stbuf) == 0))
{
mr_close_database();
com_err(whoami, 0, "database closed");
send_zgram("MOIRA", "database closed");
dormant = ASLEEP;
}
- else if (dormant == GROGGY)
- {
- mr_open_database();
- com_err(whoami, 0, "database open");
- mr_setup_signals();
- send_zgram("MOIRA", "database open again");
- dormant = AWAKE;
- }
- errno = 0;
- status = op_select_any(op_list, 0, NULL, NULL, NULL, NULL);
- if (status == -1)
+ /* Block until something happens. */
+ memcpy(&readfds, &xreadfds, sizeof(readfds));
+ memcpy(&writefds, &xwritefds, sizeof(writefds));
+ /* XXX set timeout */
+ if (select(nfds, &readfds, &writefds, NULL, NULL) == -1)
{
if (errno != EINTR)
- com_err(whoami, errno, " error from op_select");
+ com_err(whoami, errno, "in select");
if (!inc_running || now - inc_started > INC_TIMEOUT)
next_incremental();
continue;
}
- else if (status != -2)
- {
- com_err(whoami, 0, " wrong return from op_select_any");
- continue;
- }
+
if (takedown)
break;
time(&now);
if (!inc_running || now - inc_started > INC_TIMEOUT)
next_incremental();
+ tardy = now - 30 * 60;
+
+ /* If we're asleep and we should wake up, do it */
+ if ((dormant == ASLEEP) && (stat(MOIRA_MOTD_FILE, &stbuf) == -1) &&
+ (errno == ENOENT))
+ {
+ mr_open_database();
+ com_err(whoami, 0, "database open");
+ mr_setup_signals();
+ send_zgram("MOIRA", "database open again");
+ dormant = AWAKE;
+ }
- /*
- * Handle any new connections; this comes first so
- * errno isn't tromped on.
- */
- if (OP_DONE(listenop))
+ /* Handle any new connections */
+ if (FD_ISSET(listener, &readfds))
{
- if (OP_STATUS(listenop) == OP_CANCELLED)
+ int newconn;
+ struct sockaddr_in addr;
+ client *cp;
+
+ newconn = mr_accept(listener, &addr);
+ if (newconn == -1)
+ com_err(whoami, errno, "accepting new connection");
+ else if (newconn > 0)
{
- if (errno == EWOULDBLOCK)
- do_reset_listen();
- else
+ if (newconn + 1 > nfds)
+ nfds = newconn + 1;
+ FD_SET(newconn, &xreadfds);
+
+ /* Add a new client to the array */
+ nclients++;
+ if (nclients > clientssize)
{
- static int count = 0;
- com_err(whoami, errno, " error (%d) on listen", count);
- if (count++ > 10)
- exit(1);
+ clientssize = 2 * clientssize;
+ clients = xrealloc(clients, clientssize * sizeof(client *));
}
- }
- else if ((status = new_connection()))
- {
- com_err(whoami, errno, " Error on listening operation.");
- /*
- * Sleep here to prevent hosing?
- */
- }
- /* if the new connection is our only connection,
- * and the server is supposed to be down, then go
- * down now.
- */
- if ((dormant == AWAKE) && (nclients == 1) &&
- (stat(MOIRA_MOTD_FILE, &stbuf) == 0))
- {
- com_err(whoami, 0, "motd file exists, slumbertime");
- dormant = SLEEPY;
- }
- /* on new connection, if we are no longer supposed
- * to be down, then wake up.
- */
- if ((dormant == ASLEEP) && (stat(MOIRA_MOTD_FILE, &stbuf) == -1) &&
- (errno == ENOENT))
- {
- com_err(whoami, 0, "motd file no longer exists, waking up");
- dormant = GROGGY;
+
+ clients[nclients - 1] = cp = xmalloc(sizeof(client));
+ memset(cp, 0, sizeof(client));
+ cp->con = newconn;
+ cp->id = counter++;
+ cp->last_time_used = now;
+ cp->haddr = addr;
+ cp->tuplessize = 1;
+ cp->tuples = xmalloc(sizeof(mr_params));
+ memset(cp->tuples, 0, sizeof(mr_params));
+
+ cur_client = cp;
+ com_err(whoami, 0,
+ "New connection from %s port %d (now %d client%s)",
+ inet_ntoa(cp->haddr.sin_addr),
+ (int)ntohs(cp->haddr.sin_port),
+ nclients, nclients != 1 ? "s" : "");
}
}
- /*
- * Handle any existing connections.
- */
- tardy = now - 30 * 60;
+ /* Handle any existing connections. */
for (i = 0; i < nclients; i++)
{
cur_client = clients[i];
- if (OP_DONE(clients[i]->pending_op))
+
+ if (FD_ISSET(clients[i]->con, &writefds))
+ {
+ client_write(clients[i]);
+ if (!clients[i]->ntuples)
+ {
+ FD_CLR(clients[i]->con, &xwritefds);
+ /* Now that we're done writing we can read again */
+ FD_SET(clients[i]->con, &xreadfds);
+ }
+ clients[i]->last_time_used = now;
+ }
+
+ if (FD_ISSET(clients[i]->con, &readfds))
{
- cur_client->last_time_used = now;
- do_client(cur_client);
+ client_read(clients[i]);
+ if (clients[i]->ntuples)
+ FD_SET(clients[i]->con, &xwritefds);
+ clients[i]->last_time_used = now;
}
- else if (clients[i]->last_time_used < tardy)
+
+ if (clients[i]->last_time_used < tardy)
{
com_err(whoami, 0, "Shutting down connection due to inactivity");
- shutdown(cur_client->con->in.fd, 2);
+ clients[i]->done = 1;
+ }
+
+ if (clients[i]->done)
+ {
+ client *old;
+
+ com_err(whoami, 0, "Closed connection (now %d client%s, "
+ "%d queries)", nclients - 1, nclients != 2 ? "s" : "",
+ newqueries);
+
+ shutdown(clients[i]->con, 2);
+ close(clients[i]->con);
+ FD_CLR(clients[i]->con, &xreadfds);
+ FD_CLR(clients[i]->con, &xwritefds);
+ for (; clients[i]->ntuples; clients[i]->ntuples--)
+ mr_destroy_reply(clients[i]->tuples[clients[i]->ntuples - 1]);
+ free(clients[i]->tuples);
+ old = clients[i];
+ clients[i] = clients[--nclients];
+ free(old);
}
+
cur_client = NULL;
if (takedown)
break;
}
}
+
com_err(whoami, 0, "%s", takedown);
if (dormant != ASLEEP)
mr_close_database();
return 0;
}
-/*
- * Set up the template connection and queue the first accept.
- */
-
-int do_listen(char *port)
-{
- listencon = create_listening_connection(port);
-
- if (!listencon)
- return errno;
-
- listenop = create_operation();
- client_addrlen = sizeof(client_addr);
-
- start_accepting_client(listencon, listenop, &newconn, (char *)&client_addr,
- &client_addrlen, &client_tuple);
- return 0;
-}
-
-
-void do_reset_listen(void)
-{
- client_addrlen = sizeof(client_addr);
- start_accepting_client(listencon, listenop, &newconn, (char *)&client_addr,
- &client_addrlen, &client_tuple);
-}
-
-/*
- * This routine is called when a new connection comes in.
- *
- * It sets up a new client and adds it to the list of currently active clients.
- */
-int new_connection(void)
-{
- client *cp;
- static counter = 0;
-
- /*
- * Make sure there's been no error
- */
- if (OP_STATUS(listenop) != OP_COMPLETE)
- return errno;
-
- if (!newconn)
- return MR_NOT_CONNECTED;
-
- /*
- * Set up the new connection and reply to the client
- */
- cp = malloc(sizeof(client));
- memset(cp, 0, sizeof(*cp));
- cp->action = CL_ACCEPT;
- cp->con = newconn;
- cp->id = counter++;
- cp->args = NULL;
- cp->clname[0] = '\0';
- cp->reply.mr_argv = NULL;
- cp->first = NULL;
- cp->last = NULL;
- cp->last_time_used = now;
- newconn = NULL;
-
- cp->pending_op = create_operation();
- reset_operation(cp->pending_op);
- oplist_append(&op_list, cp->pending_op);
- cur_client = cp;
-
- /*
- * Add a new client to the array..
- */
- clist_append(cp);
-
- /*
- * Let him know we heard him.
- */
- start_replying_to_client(cp->pending_op, cp->con, GDB_ACCEPTED, "", "");
-
- cp->haddr = client_addr;
-
- /*
- * Log new connection.
- */
- com_err(whoami, 0, "New connection from %s port %d (now %d client%s)",
- inet_ntoa(cp->haddr.sin_addr), (int)ntohs(cp->haddr.sin_port),
- nclients, nclients != 1 ? "s" : "");
-
- /*
- * Get ready to accept the next connection.
- */
- reset_operation(listenop);
- client_addrlen = sizeof(client_addr);
-
- start_accepting_client(listencon, listenop, &newconn, (char *)&client_addr,
- &client_addrlen, &client_tuple);
- return 0;
-}
-
-/*
- * Add a new client to the known clients.
- */
-void clist_append(client *cp)
-{
- client **clients_n;
-
- nclients++;
- clients_n = malloc(nclients * sizeof(client *));
- memcpy(clients_n, clients, (nclients - 1) * sizeof(cp));
- clients_n[nclients - 1] = cp;
- free(clients);
- clients = clients_n;
- clients_n = NULL;
-}
-
-
-void clist_delete(client *cp)
-{
- client **clients_n, **scpp, **dcpp; /* source and dest client ptr ptr */
-
- int found_it = 0;
-
- clients_n = malloc((nclients - 1) * sizeof(client *));
- for (scpp = clients, dcpp = clients_n; scpp < clients + nclients; )
- {
- if (*scpp != cp)
- *dcpp++ = *scpp++;
- else
- {
- scpp++;
- if (found_it)
- abort();
- found_it = 1;
- }
- }
- --nclients;
- free(clients);
- clients = clients_n;
- clients_n = NULL;
- oplist_delete(op_list, cp->pending_op);
- reset_operation(cp->pending_op);
- delete_operation(cp->pending_op);
- sever_connection(cp->con);
- free(cp);
-}
-
-/*
- * Add a new operation to a list of operations.
- *
- * This should be rewritten to use realloc instead, since in most
- * cases it won't have to copy the array.
- */
-
-void oplist_append(LIST_OF_OPERATIONS *oplp, OPERATION op)
-{
- int count = (*oplp)->count + 1;
- LIST_OF_OPERATIONS newlist = (LIST_OF_OPERATIONS)
- db_alloc(size_of_list_of_operations(count));
- memcpy(newlist, *oplp, size_of_list_of_operations((*oplp)->count));
- newlist->count++;
- newlist->op[count - 1] = op;
- db_free(*oplp, size_of_list_of_operations(count - 1));
- *oplp = newlist;
-}
-
-void oplist_delete(LIST_OF_OPERATIONS oplp, OPERATION op)
-{
- OPERATION *s;
- int c;
-
- for (s = oplp->op, c = oplp->count; c; --c, ++s)
- {
- if (*s == op)
- {
- while (c > 0)
- {
- *s = *(s + 1);
- ++s;
- --c;
- }
- oplp->count--;
- return;
- }
- }
- abort();
-}
-
-
void reapchild(int x)
{
int status, pid;
* cl->cl_name.
*/
-void do_auth(client *cl)
+void do_auth(client *cl, mr_params req)
{
KTEXT_ST auth;
AUTH_DAT ad;
replay_cache *rc, *rcnew;
time_t now;
- auth.length = cl->args->mr_argl[0];
- memcpy(auth.dat, cl->args->mr_argv[0], auth.length);
+ auth.length = req.mr_argl[0];
+ memcpy(auth.dat, req.mr_argv[0], auth.length);
auth.mbz = 0;
- if ((status = krb_rd_req (&auth, MOIRA_SNAME, host,
- cl->haddr.sin_addr.s_addr, &ad, "")))
+ if ((status = krb_rd_req(&auth, MOIRA_SNAME, host,
+ cl->haddr.sin_addr.s_addr, &ad, "")))
{
status += ERROR_TABLE_BASE_krb;
- cl->reply.mr_status = status;
- if (log_flags & LOG_RES)
- com_err(whoami, status, " (authentication failed)");
+ client_reply(cl, status);
+ com_err(whoami, status, " (authentication failed)");
return;
}
inet_ntoa(cl->haddr.sin_addr),
kname_unparse(ad.pname, ad.pinst, ad.prealm));
com_err(whoami, KE_RD_AP_REPEAT, " (authentication failed)");
- cl->reply.mr_status = KE_RD_AP_REPEAT;
+ client_reply(cl, KE_RD_AP_REPEAT);
return;
}
}
status = set_krb_mapping(cl->clname, ad.pname, ok,
&cl->client_id, &cl->users_id);
- if (cl->args->mr_version_no == MR_VERSION_2)
- {
- strncpy(cl->entity, cl->args->mr_argv[1], 8);
- cl->entity[8] = 0;
- }
- else
- strcpy(cl->entity, "???");
+ strncpy(cl->entity, req.mr_argv[1], 8);
+ cl->entity[8] = 0;
+
memset(&ad, 0, sizeof(ad)); /* Clean up session key, etc. */
- if (log_flags & LOG_RES)
- {
- com_err(whoami, 0, "Auth to %s using %s, uid %d cid %d",
- cl->clname, cl->entity, cl->users_id, cl->client_id);
- }
- if (status != MR_SUCCESS)
- cl->reply.mr_status = status;
- else if (cl->users_id == 0)
- cl->reply.mr_status = MR_USER_AUTH;
+ com_err(whoami, 0, "Auth to %s using %s, uid %d cid %d",
+ cl->clname, cl->entity, cl->users_id, cl->client_id);
+
+ if (status != MR_SUCCESS || cl->users_id != 0)
+ client_reply(cl, status);
+ else
+ client_reply(cl, MR_USER_AUTH);
}
RCSID("$Header$");
-extern char buf1[];
extern int nclients;
+extern client **clients;
extern char *whoami;
extern int dbms_errno, mr_errcode;
-static int row_count;
void do_call(client *cl);
void free_rtn_tuples(client *cp);
-int retr_callback(int argc, char **argv, void *p_cp);
-int list_users(int (*callbk)(int, char **, void *), char *callarg);
-void do_retr(client *cl);
-void do_access(client *cl);
+int retr_callback(int argc, char **argv, void *p_cl);
+int list_users(client *cl);
+void do_retr(client *cl, mr_params req);
+void do_access(client *cl, mr_params req);
void get_motd(client *cl);
/* Put this in a variable so that we can patch it if necessary */
int max_row_count = 4096;
-/*
- * Welcome to the (finite state) machine (highest level).
- */
-void do_client(client *cp)
-{
- struct stat stbuf;
-
- free_rtn_tuples(cp);
- if (OP_STATUS(cp->pending_op) == OP_CANCELLED)
- {
- com_err(whoami, 0,
- "Closed connection (now %d client%s, %d new queries, %d old)",
- nclients - 1, nclients != 2 ? "s" : "", newqueries, oldqueries);
- clist_delete(cp);
- /* if we no longer have any clients, and we're supposed to
- * go down, then go down now.
- */
- if ((dormant == AWAKE) && (nclients == 0) &&
- (stat(MOIRA_MOTD_FILE, &stbuf) == 0))
- {
- com_err(whoami, 0, "motd file exists, slumbertime");
- dormant = SLEEPY;
- }
- return;
- }
- switch (cp->action)
- {
- case CL_ACCEPT:
- case CL_SEND:
- /* Start recieving next request */
- initialize_operation(cp->pending_op, mr_start_recv,
- (char *)&cp->args, NULL);
- queue_operation(cp->con, CON_INPUT, cp->pending_op);
- cp->action = CL_RECEIVE;
- break;
- case CL_RECEIVE:
- /* Data is here. Process it & start it heading back */
- do_call(cp); /* This may block for a while. */
- mr_destroy_reply(cp->args);
- cp->args = NULL;
- initialize_operation(cp->pending_op, mr_start_send,
- (char *)&cp->reply, NULL);
- queue_operation(cp->con, CON_OUTPUT, cp->pending_op);
- cp->action = CL_SEND;
- break;
- }
-}
-
char *procnames[] = {
"noop",
"auth",
"motd",
};
+int newqueries;
-void do_call(client *cl)
+void client_read(client *cl)
{
- int pn;
- cl->reply.mr_argc = 0;
- cl->reply.mr_status = 0;
- cl->reply.mr_version_no = cl->args->mr_version_no;
- if (((pn = cl->args->mr_procno) < 0) || (pn > MR_MAX_PROC))
+ mr_params req;
+ int status, pn;
+
+ status = mr_receive(cl->con, &req);
+ if (status != MR_SUCCESS)
{
- com_err(whoami, 0, "procno out of range");
- cl->reply.mr_status = MR_UNKNOWN_PROC;
+ cl->done = 1;
+ if (status != MR_NOT_CONNECTED)
+ com_err(whoami, status, "while reading from socket");
return;
}
- if (log_flags & LOG_ARGS)
+
+ pn = req.u.mr_procno;
+ if (pn < 0 || pn > MR_MAX_PROC)
{
- log_args(procnames[pn], cl->args->mr_version_no,
- cl->args->mr_argc, cl->args->mr_argv);
+ com_err(whoami, 0, "procno out of range");
+ client_reply(cl, MR_UNKNOWN_PROC);
+ return;
}
- else if (log_flags & LOG_REQUESTS)
- com_err(whoami, 0, "%s", procnames[pn]);
+ log_args(procnames[pn], 2, req.mr_argc, req.mr_argv);
- if ((dormant == ASLEEP || dormant == GROGGY) &&
- pn != MR_NOOP && pn != MR_MOTD)
+ if (dormant == ASLEEP && pn != MR_NOOP && pn != MR_MOTD)
{
- cl->reply.mr_status = MR_DOWN;
- if (log_flags & LOG_RES)
- com_err(whoami, MR_DOWN, "(query refused)");
+ client_reply(cl, MR_DOWN);
+ com_err(whoami, MR_DOWN, "(query refused)");
return;
}
switch (pn)
{
case MR_NOOP:
- cl->reply.mr_status = 0;
+ client_reply(cl, MR_SUCCESS);
return;
case MR_AUTH:
- do_auth(cl);
+ do_auth(cl, req);
return;
case MR_QUERY:
- do_retr(cl);
+ do_retr(cl, req);
return;
case MR_ACCESS:
- do_access(cl);
+ do_access(cl, req);
return;
case MR_SHUTDOWN:
return;
case MR_DO_UPDATE:
- trigger_dcm(NULL, NULL, cl);
+ client_reply(cl, MR_PERM);
return;
case MR_MOTD:
}
}
-void free_rtn_tuples(client *cp)
+/* Set the final return status for a query. We always keep one more
+ free slot in cl->tuples[] than we're using so that this can't fail */
+void client_reply(client *cl, long status)
{
- returned_tuples *temp;
- for (temp = cp->first; temp && OP_DONE(temp->op); )
- {
- returned_tuples *t1 = temp;
- temp = t1->next;
- if (t1 == cp->last)
- cp->last = NULL;
-
- mr_destroy_reply(t1->retval);
- delete_operation(t1->op);
- free(t1);
- }
- cp->first = temp;
+ cl->tuples[cl->ntuples].u.mr_status = status;
+ cl->tuples[cl->ntuples].mr_argc = 0;
+ cl->tuples[cl->ntuples].mr_argl = NULL;
+ cl->tuples[cl->ntuples].mr_argv = NULL;
+ cl->ntuples++;
}
-int retr_callback(int argc, char **argv, void *p_cp)
+void client_return_tuple(client *cl, int argc, char **argv)
{
- client *cp = p_cp;
- mr_params *arg_tmp;
- returned_tuples *tp;
- OPERATION op_tmp;
- char **nargv;
- int i;
+ if (cl->done || dbms_errno)
+ return;
- if (row_count++ >= max_row_count)
+ if (cl->ntuples == max_row_count)
{
dbms_errno = mr_errcode = MR_NO_MEM;
- return MR_ABORT;
+ return;
}
- /*
- * This takes too much advantage of the fact that
- * serialization of the data happens during the queue operation.
- */
- arg_tmp = malloc(sizeof(mr_params));
- tp = malloc(sizeof(returned_tuples));
- nargv = malloc(argc * sizeof(char *));
-
- op_tmp = create_operation();
-
- if (mr_trim_args(argc, argv) == MR_NO_MEM)
- com_err(whoami, MR_NO_MEM, "while trimming args");
- if (log_flags & LOG_RESP)
- log_args("return: ", cp->args->mr_version_no, argc, argv);
-
- tp->op = op_tmp;
- tp->retval = arg_tmp;
- tp->next = NULL;
-
- arg_tmp->mr_status = MR_MORE_DATA;
- arg_tmp->mr_version_no = cp->args->mr_version_no;
- arg_tmp->mr_argc = argc;
- arg_tmp->mr_argv = nargv;
- for (i = 0; i < argc; i++)
+ if (cl->ntuples == cl->tuplessize - 1)
{
- int len = strlen(argv[i]) + 1;
- nargv[i] = malloc(len);
- memcpy(nargv[i], argv[i], len);
+ int newsize = (cl->tuplessize + 4) * 2;
+ mr_params *newtuples;
+
+ newtuples = realloc(cl->tuples, newsize * sizeof(mr_params));
+ if (!newtuples)
+ {
+ free_rtn_tuples(cl);
+ dbms_errno = mr_errcode = MR_NO_MEM;
+ return;
+ }
+ cl->tuplessize = newsize;
+ cl->tuples = newtuples;
}
- arg_tmp->mr_flattened = NULL;
- arg_tmp->mr_argl = NULL;
- if (cp->last)
- {
- cp->last->next = tp;
- cp->last = tp;
- } else
- cp->last = cp->first = tp;
-
- reset_operation(op_tmp);
- initialize_operation(op_tmp, mr_start_send, (char *)arg_tmp, NULL);
- queue_operation(cp->con, CON_OUTPUT, op_tmp);
- return MR_CONT;
+ cl->tuples[cl->ntuples].u.mr_status = MR_MORE_DATA;
+ cl->tuples[cl->ntuples].mr_argc = argc;
+ cl->tuples[cl->ntuples].mr_argl = NULL;
+ cl->tuples[cl->ntuples].mr_argv = mr_copy_args(argv, argc);
+ cl->ntuples++;
}
-int list_users(int (*callbk)(int, char **, void *), char *callarg)
+void client_write(client *cl)
{
- char *argv[6];
- char buf[30];
- char buf1[30];
- int i;
- extern client **clients;
- char *cp;
+ int status;
- for (i = 0; i < nclients; i++)
+ status = mr_send(cl->con, &cl->tuples[cl->nexttuple]);
+ if (status)
{
- client *cl = clients[i];
- if (cl->clname)
- argv[0] = cl->clname;
- else argv[0] = "unauthenticated";
-
- argv[1] = inet_ntoa(cl->haddr.sin_addr);
- argv[2] = buf;
- sprintf(buf, "port %d", ntohs(cl->haddr.sin_port));
- argv[3] = ctime(&cl->last_time_used);
- cp = strchr(argv[3], '\n');
- if (cp)
- *cp = '\0';
- argv[4] = buf1;
- sprintf(buf1, "[#%d]", cl->id);
- (*callbk)(5, argv, callarg);
+ com_err(whoami, status, "writing to socket");
+ cl->done = 1;
+ }
+ else
+ {
+ cl->nexttuple++;
+ if (cl->nexttuple == cl->ntuples)
+ free_rtn_tuples(cl);
}
- return 0;
}
-void do_retr(client *cl)
+void free_rtn_tuples(client *cl)
{
- char *queryname;
+ for (cl->ntuples--; cl->ntuples >= 0; cl->ntuples--)
+ free_argv(cl->tuples[cl->ntuples].mr_argv,
+ cl->tuples[cl->ntuples].mr_argc);
+ free(cl->tuples);
+
+ cl->tuples = xmalloc(sizeof(mr_params));
+ cl->tuplessize = 1;
+ cl->ntuples = cl->nexttuple = 0;
+}
- cl->reply.mr_argc = 0;
- cl->reply.mr_status = 0;
- row_count = 0;
+void do_retr(client *cl, mr_params req)
+{
+ char *queryname;
+ int status;
- if (cl->args->mr_argc < 1)
+ if (req.mr_argc < 1)
{
- cl->reply.mr_status = MR_ARGS;
+ client_reply(cl, MR_ARGS);
com_err(whoami, MR_ARGS, "got nameless query");
return;
}
- queryname = cl->args->mr_argv[0];
-
- if (cl->args->mr_version_no == MR_VERSION_2)
- newqueries++;
- else
- oldqueries++;
+ queryname = req.mr_argv[0];
+ newqueries++;
if (!strcmp(queryname, "_list_users"))
- cl->reply.mr_status = list_users(retr_callback, (char *)cl);
+ status = list_users(cl);
else
+ status = mr_process_query(cl, queryname, req.mr_argc - 1, req.mr_argv + 1,
+ retr_callback, cl);
+
+ client_reply(cl, status);
+
+ if (cl->ntuples >= max_row_count)
{
- cl->reply.mr_status = mr_process_query(cl, queryname,
- cl->args->mr_argc - 1,
- cl->args->mr_argv + 1,
- retr_callback, cl);
- }
- if (row_count >= max_row_count)
- {
- critical_alert("moirad", "attempted query %s with %d rows\n",
- queryname, row_count);
+ critical_alert("moirad", "attempted query %s with too many rows\n",
+ queryname);
}
- if (log_flags & LOG_RES)
- com_err(whoami, 0, "Query complete.");
+ com_err(whoami, 0, "Query complete.");
+}
+
+int retr_callback(int argc, char **argv, void *p_cl)
+{
+ client *cl = p_cl;
+
+ mr_trim_args(argc, argv);
+ client_return_tuple(cl, argc, argv);
}
-void do_access(client *cl)
+void do_access(client *cl, mr_params req)
{
- if (cl->args->mr_argc < 1)
+ int status;
+
+ if (req.mr_argc < 1)
{
- cl->reply.mr_status = MR_ARGS;
+ client_reply(cl, MR_ARGS);
com_err(whoami, MR_ARGS, "got nameless access");
return;
}
- cl->reply.mr_argc = 0;
- cl->reply.mr_status = mr_check_access(cl, cl->args->mr_argv[0],
- cl->args->mr_argc - 1,
- cl->args->mr_argv + 1);
+ status = mr_check_access(cl, req.mr_argv[0], req. mr_argc - 1,
+ req.mr_argv + 1);
+ client_reply(cl, status);
com_err(whoami, 0, "Access check complete.");
}
-
-/* trigger_dcm is also used as a followup routine to the
- * set_server_host_override query, hence the two dummy arguments.
- */
-
-struct query pseudo_query = {
- "trigger_dcm",
- "tdcm",
-};
-
-int trigger_dcm(struct query *q, char *argv[], client *cl)
+void get_motd(client *cl)
{
- int pid;
- char prog[128];
-
- cl->reply.mr_argc = 0;
-
- if ((cl->reply.mr_status = check_query_access(&pseudo_query, 0, cl)))
- return cl->reply.mr_status;
+ int motd;
+ char *buffer;
+ struct stat statb;
- sprintf(prog, "%s/startdcm", BIN_DIR);
- pid = vfork();
- switch (pid)
+ if (stat(MOIRA_MOTD_FILE, &statb) == -1)
{
- case 0:
- execl(prog, "startdcm", 0);
- exit(1);
-
- case -1:
- cl->reply.mr_status = errno;
- return 0;
+ client_reply(cl, MR_SUCCESS);
+ return;
+ }
+
+ buffer = malloc(statb.st_size + 1);
+ if (!buffer)
+ {
+ client_reply(cl, MR_NO_MEM);
+ return;
+ }
- default:
- return 0;
+ motd = open(MOIRA_MOTD_FILE, 0, O_RDONLY);
+ if (motd)
+ {
+ read(motd, buffer, statb.st_size);
+ close(motd);
+ buffer[statb.st_size] = '\0';
+ client_return_tuple(cl, 1, &buffer);
+ client_reply(cl, MR_SUCCESS);
}
-}
+ else
+ client_reply(cl, errno);
+ free(buffer);
+}
-void get_motd(client *cl)
+int list_users(client *cl)
{
- int motd, len;
- char buffer[1024];
- char *arg[1];
+ char *argv[5];
+ char buf[30];
+ char buf1[30];
+ int i;
+ char *cp;
- arg[0] = buffer;
- cl->reply.mr_status = 0;
- motd = open(MOIRA_MOTD_FILE, 0, O_RDONLY);
- if (motd < 0)
- return;
- len = read(motd, buffer, sizeof(buffer) - 1);
- close(motd);
- buffer[len] = 0;
- row_count = 0;
- retr_callback(1, arg, cl);
- cl->reply.mr_status = 0;
+ for (i = 0; i < nclients; i++)
+ {
+ client *c = clients[i];
+ argv[0] = c->clname;
+ argv[1] = inet_ntoa(c->haddr.sin_addr);
+ argv[2] = buf;
+ sprintf(buf, "port %d", ntohs(c->haddr.sin_port));
+ argv[3] = ctime(&c->last_time_used);
+ cp = strchr(argv[3], '\n');
+ if (cp)
+ *cp = '\0';
+ argv[4] = buf1;
+ sprintf(buf1, "[#%d]", c->id);
+ client_return_tuple(cl, 5, argv);
+ }
+ return MR_SUCCESS;
}
+
*/
#include <moira.h>
-#include <mr_proto.h>
+#include <mr_private.h>
#include <moira_site.h>
#include <netinet/in.h>
#include <krb.h>
-typedef struct returned_tuples {
- struct returned_tuples *next;
- OPERATION op;
- mr_params *retval;
-} returned_tuples;
-
-/*
- * This should be in the kerberos header file.
- */
+/* This should be in the kerberos header file. */
struct krbname {
char name[ANAME_SZ];
*/
typedef struct _client {
- OPERATION pending_op; /* Primary pending operation */
- CONNECTION con; /* Connection to the client */
- int action; /* what action is pending? */
- mr_params *args, reply;
+ int con; /* Connection to the client */
int id; /* Unique id of client */
struct sockaddr_in haddr; /* IP address of client */
char clname[MAX_K_NAME_SZ]; /* Name client authenticated to */
struct krbname kname; /* Parsed version of the above */
+ char entity[9]; /* client program being used */
int users_id; /* Moira-internal ID of authenticated user */
- int client_id; /* Moira-internal ID of client for modby field */
- returned_tuples *first, *last;
+ int client_id; /* Moira-internal ID of client */
time_t last_time_used; /* Last time connection used */
- char entity[9]; /* entity on other end of the connection */
+ mr_params *tuples; /* Tuples waiting to send back to client */
+ int ntuples; /* Number of tuples waiting */
+ int tuplessize; /* Current size of tuple array */
+ int nexttuple; /* Next tuple to return */
+ int done; /* Close up next time through loop */
} client;
-/*
- * States
- */
-
-#define CL_DEAD 0
-#define CL_STARTING 1
-
-/*
- * Actions.
- */
-
-#define CL_ACCEPT 0
-#define CL_RECEIVE 1
-#define CL_SEND 2
-
-extern char *krb_realm;
-
-/*
- * Debugging options.
- */
-
-extern int log_flags;
-
-#define LOG_CONNECT 0x0001
-#define LOG_REQUESTS 0x0002
-#define LOG_ARGS 0x0004
-#define LOG_RESP 0x0008
-#define LOG_RES 0x0010
-#define LOG_VALID 0x0020
-#define LOG_SQL 0x0040
-#define LOG_GDSS 0x0080
-
+extern char krb_realm[REALM_SZ];
/* max length of query argument allowed */
#define ARGLEN 257
#define QMAXARGS 22
/* statistics on number of queries by version number */
-extern int newqueries, oldqueries;
+extern int newqueries;
/* Maximum and minimum values that will ever be chosen for IDs */
#define MAX_ID_VALUE 31999
struct validate;
struct valobj;
-/* prototypes from gdb */
-int gdb_init(void);
-int gdb_debug(int flag);
-void start_accepting_client(CONNECTION, OPERATION, CONNECTION *,
- char *, int *, TUPLE *);
-int initialize_operation(OPERATION, int (*init_function)(OPERATION, HALF_CONNECTION, void *),
- char *, int (*cancel_function)(HALF_CONNECTION, void *));
-int reset_operation(OPERATION);
-int delete_operation(OPERATION);
-int start_replying_to_client(OPERATION, CONNECTION, int, char *, char *);
-int op_select(LIST_OF_OPERATIONS, int, fd_set *, fd_set *, fd_set *,
- struct timeval *);
-
/* prototypes from increment.dc */
void incremental_init(void);
void next_incremental(void);
void clist_delete(client *cp);
/* prototypes from mr_sauth.c */
-void do_auth(client *cl);
+void do_auth(client *cl, mr_params req);
/* prototypes from mr_scall.c */
-void do_client(client *cp);
-int trigger_dcm(struct query *q, char *argv[], client *cl);
+void do_client(client *cl);
+void client_reply(client *cl, long status);
+void client_return_tuple(client *cl, int argc, char **argv);
+void client_read(client *cl);
+void client_write(client *cl);
/* prototypes from mr_shutdown.c */
void sigshut(int);
void mr_com_err(const char *whoami, long code, const char *fmt, va_list pvar);
int mr_trim_args(int argc, char **argv);
char **mr_copy_args(char **argv, int argc);
+void *xmalloc(size_t);
+void *xrealloc(void *, size_t);
/* prototypes from qaccess.pc */
int access_user(struct query *q, char *argv[], client *cl);
int set_filesys_modtime(struct query *q, char *argv[], client *cl);
int set_zephyr_modtime(struct query *q, char *argv[], client *cl);
int _sdl_followup(struct query *q, char *argv[], client *cl);
+int trigger_dcm(struct query *q, char *argv[], client *cl);
/* prototypes from qsetup.pc */
int prefetch_value(struct query *q, char *argv[], client *cl);
* This feature is no longer supported. Sorry.
*/
com_err(whoami, 0, "Shutdown request by %s rejected", cl->clname);
- cl->reply.mr_status = EACCES;
+ client_reply(cl, EACCES);
}
+++ /dev/null
-/* $Id$
- *
- * Global variables inside the Moira server.
- *
- * Copyright (C) 1987-1998 by the Massachusetts Institute of Technology
- * For copying and distribution information, please see the file
- * <mit-copyright.h>.
- */
-
-#include <mit-copyright.h>
-#include "mr_server.h"
-
-RCSID("$Header$");
-
-/*
- * Connections & clients:
- *
- * The template and most recently connected connection:
- */
-CONNECTION listencon, newconn;
-/*
- * The template listen operation.
- */
-OPERATION listenop;
-/*
- * The list of operations corresponding to the I/O being done by the
- * currently connected clients.
- */
-LIST_OF_OPERATIONS op_list;
-/*
- * The current number of connected clients, an array of them, and the
- * "current" client, if any.
- */
-int nclients = 0;
-client **clients, *cur_client;
-/*
- * Socket address of the most recently connected client.
- */
-struct sockaddr_in client_addr;
-int client_addrlen;
-/*
- * Additional data sent at connect time by the client
- * (provided by GDB; ignored by Moira)
- */
-TUPLE client_tuple;
-
-/*
- * Name server was invoked as.
- */
-char *whoami;
-/*
- * Buffer for use in error messages.
- */
-char buf1[BUFSIZ];
-
-/*
- * If non-null, reason for shutdown. (Moira will be going down shortly
- * if this is non-null)
- */
-char *takedown = NULL;
-
-/* States for putting the server to sleep & waking it up again. */
-int dormant = AWAKE;
-
-/*
- * The name of the local Kerberos realm
- */
-
-char *krb_realm = NULL;
-/*
- * Logging levels.
- */
-
-int log_flags = LOG_CONNECT|LOG_REQUESTS|LOG_ARGS|LOG_RES;
-
-/*
- * Time of last time through main loop.
- */
-time_t now;
-
-
-/*
- * Statistics on number of queries of each version that have been attempted
- */
-
-int newqueries = 0;
-int oldqueries = 0;
-
-
-/* Journalling file */
-FILE *journal = NULL;
-
#include "mr_server.h"
#include <ctype.h>
+#include <stdio.h>
#include <stdlib.h>
#include <string.h>
fprintf(stderr, "[#%d]", cur_client->id);
fputs(": ", stderr);
}
- if (code)
+ if (code) {
fputs(error_message(code), stderr);
+ fputs(" ", stderr);
+ }
if (fmt)
vfprintf(stderr, fmt, pvar);
putc('\n', stderr);
a[i] = strdup(argv[i]);
return a;
}
+
+
+/* malloc or die! */
+void *xmalloc(size_t bytes)
+{
+ void *buf = malloc(bytes);
+
+ if (buf)
+ return buf;
+
+ critical_alert("moirad", "Out of memory");
+ exit(1);
+}
+
+void *xrealloc(void *ptr, size_t bytes)
+{
+ void *buf = realloc(ptr, bytes);
+
+ if (buf)
+ return buf;
+
+ critical_alert("moirad", "Out of memory");
+ exit(1);
+}
#include "query.h"
#include "qrtn.h"
+#include <errno.h>
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
kname_parse(si.pname, si.pinst, si.prealm, kname);
free(kname);
si.rawsig = (unsigned char *)strdup(rsig);
- if (log_flags & LOG_GDSS)
- com_err(whoami, 0, "rawsig length = %d, sig=\"%s\"",
- strlen(si.rawsig), si.rawsig);
GDSS_Recompose(&si, sigbuf);
free(si.rawsig);
free(argv[U_SIGNATURE]);
argv[U_SIGNATURE] = strdup(sigbuf);
- if (log_flags & LOG_GDSS)
- com_err(whoami, 0, "generated signature length %d",
- strlen(sigbuf));
}
#endif /* GDSS */
(*action)(q->vcnt, argv, actarg);
timestamp = si.timestamp;
}
else
- {
- if (log_flags & LOG_GDSS)
- hex_dump(argv[U_SIGNATURE]);
- return gdss2et(status);
- }
+ return gdss2et(status);
}
else
{
timestamp = si.timestamp;
}
else
- {
- if (log_flags & LOG_GDSS)
- hex_dump(argv[U_SIGNATURE + 1]);
- return gdss2et(status);
- }
+ return gdss2et(status);
}
else
{
free(argv[idx]);
argv[idx] = malloc(256);
name = argv[idx];
+ memset(name, 0, 256);
if (id == 0)
{
label = argv[0];
int _sdl_followup(struct query *q, char *argv[], client *cl)
{
- int i;
- i = atoi(argv[0]);
- log_flags = i;
-
- if (i & LOG_SQL)
+ if (atoi(argv[0]))
EXEC SQL ALTER SESSION SET SQL_TRACE TRUE;
else
EXEC SQL ALTER SESSION SET SQL_TRACE FALSE;
}
-static void hex_dump(unsigned char *p)
+int trigger_dcm(struct query *q, char *argv[], client *cl)
{
- fprintf(stderr, "Size: %d\n", strlen(p));
- while (strlen(p) >= 8)
- {
- fprintf(stderr, "%02x %02x %02x %02x %02x %02x %02x %02x\n",
- p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7]);
- p += 8;
- }
- switch (strlen(p))
+ pid_t pid;
+ char prog[128];
+
+ sprintf(prog, "%s/startdcm", BIN_DIR);
+ pid = vfork();
+ switch (pid)
{
- case 7:
- fprintf(stderr, "%02x %02x %02x %02x %02x %02x %02x\n",
- p[0], p[1], p[2], p[3], p[4], p[5], p[6]);
- break;
- case 6:
- fprintf(stderr, "%02x %02x %02x %02x %02x %02x\n",
- p[0], p[1], p[2], p[3], p[4], p[5]);
- break;
- case 5:
- fprintf(stderr, "%02x %02x %02x %02x %02x\n",
- p[0], p[1], p[2], p[3], p[4]);
- break;
- case 4:
- fprintf(stderr, "%02x %02x %02x %02x\n",
- p[0], p[1], p[2], p[3]);
- break;
- case 3:
- fprintf(stderr, "%02x %02x %02x\n",
- p[0], p[1], p[2]);
- break;
- case 2:
- fprintf(stderr, "%02x %02x\n",
- p[0], p[1]);
- break;
- case 1:
- fprintf(stderr, "%02x\n",
- p[0]);
- break;
+ case 0:
+ execl(prog, "startdcm", 0);
+ exit(1);
+
+ case -1:
+ return errno;
+
default:
- return;
+ return MR_SUCCESS;
}
}
#include "qrtn.h"
#include "query.h"
+#include <stdio.h>
#include <stdlib.h>
#include <string.h>
dbms_errno = 0;
mr_errcode = 0;
- q = get_query_by_name(name, cl->args->mr_version_no);
+ q = get_query_by_name(name);
if (!q)
return MR_NO_HANDLE;
/* list queries command */
if (!strcmp(name, "_list_queries"))
{
- list_queries(cl->args->mr_version_no, action, actarg);
+ list_queries(action, actarg);
return MR_SUCCESS;
}
{
if (argc < 1)
return MR_ARGS;
- q = get_query_by_name(argv_ro[0], cl->args->mr_version_no);
+ q = get_query_by_name(argv_ro[0]);
if (!q)
return MR_NO_HANDLE;
help_query(q, action, actarg);
}
/* get query structure, return error if named query does not exist */
- q = get_query_by_name(name, cl->args->mr_version_no);
+ q = get_query_by_name(name);
if (!q)
return MR_NO_HANDLE;
v = q->validate;
fprintf(journal, "%% %s %s %s",
cl->clname, cl->entity, ctime(&now));
- fprintf(journal, "%s[%ld] ", q->name, cl->args->mr_version_no);
+ fprintf(journal, "%s ", q->name);
for (i = 0; i < argc; i++)
{
if (i != 0)
}
cache_commit(); /* commit following abort is safe */
- if (status != MR_SUCCESS && log_flags & LOG_RES)
+ if (status != MR_SUCCESS)
com_err(whoami, status, " (Query failed)");
return status;
}
return MR_ARG_TOO_LONG;
*to = '\0';
- if (*--to == '\\')
+ if (to > Argv[i] && *--to == '\\')
return MR_BAD_CHAR;
}
}
}
- if (LOG_RES)
- com_err(whoami, 0, "setting ID %s to %d", object, value);
+ com_err(whoami, 0, "setting ID %s to %d", object, value);
EXEC SQL UPDATE numvalues SET value = :value WHERE name = :obj;
return MR_SUCCESS;
}
int setup_ahst(struct query *q, char **argv, client *cl)
{
EXEC SQL BEGIN DECLARE SECTION;
- char *name;
+ char *name, oldname[41], vendor[17], model[25], os[17];
int value, id, saddr, mask, high, low, cnt;
EXEC SQL END DECLARE SECTION;
int row;
struct in_addr addr;
if (!strcmp(q->shortname, "uhst"))
- row = 1;
+ {
+ row = 1;
+ EXEC SQL SELECT name, vendor, model, os
+ INTO :oldname, :vendor, :model, :os
+ FROM machine WHERE mach_id = :id;
+ }
else
row = 0;
+ id = *(int *)argv[0];
+
/* sanity check name: must start with a letter, contain only
* letters, numerals, and hyphen, and not end with a hyphen.
*/
- if (row == 0 || strcmp(argv[1], cl->args->mr_argv[1]))
+ if (row == 0 || strcmp(argv[1], oldname))
{
char *p = argv[row];
/* sanity check host vendor: must start with a letter, contain only
* letters, numerals, and hyphen, and end with an alphanumeric.
*/
- if (*argv[row + 1] && (row == 0 || strcmp(argv[2], cl->args->mr_argv[2])))
+ if (*argv[row + 1] && (row == 0 || strcmp(argv[2], vendor)))
{
char *p = argv[row + 1];
/* sanity check host type: must start with a letter, contain only
* letters, numerals, and hyphen, and end with an alphanumeric.
*/
- if (*argv[row + 2] && (row == 0 || strcmp(argv[3], cl->args->mr_argv[3])))
+ if (*argv[row + 2] && (row == 0 || strcmp(argv[3], model)))
{
char *p = argv[row + 2];
return MR_BAD_CHAR;
}
- /* sanity check host vendor: must start with a letter, contain only
+ /* sanity check host os: must start with a letter, contain only
* letters, numerals, and hyphen, and end with an hyphen alphanumeric.
*/
- if (*argv[row + 3] && (row == 0 || strcmp(argv[4], cl->args->mr_argv[4])))
+ if (*argv[row + 3] && (row == 0 || strcmp(argv[4], os)))
{
char *p = argv[row + 3];
int qcmp(const void *q1, const void *q2);
-struct query *get_query_by_name(char *name, int version)
+struct query *get_query_by_name(char *name)
{
struct query *q;
int i;
return NULL;
}
-void list_queries(int version, int (*action)(int, char *[], void *),
- void *actarg)
+void list_queries(int (*action)(int, char *[], void *), void *actarg)
{
struct query *q;
int i;
{V_NAME, 0, SERVERS_TABLE, NAME, 0, MR_SERVICE},
{V_LOCK, 0, SERVERHOSTS_TABLE, 0, MACH_ID, MR_DEADLOCK},
{V_ID, 1, MACHINE_TABLE, NAME, MACH_ID, MR_MACHINE},
- {V_CHAR, 0, SERVERHOSTS_TABLE, NAME},
+ {V_CHAR, 0, SERVERHOSTS_TABLE, SERVICE},
{V_LEN, 5, SERVERHOSTS_TABLE, "value3"},
};
void incremental_after(enum tables table, char *qualx, char **argv);
void incremental_clear_after(void);
-/* prototypes from qfollow.dc */
-int set_pobox_modtime(struct query *q, char *argv[], client *cl);
-
/* prototypes from qrtn.dc */
int check_query_access(struct query *q, char *argv[], client *cl);
int set_next_object_id(char *objectx, enum tables table, int limit);
/* prototypes from qsubs.c */
-void list_queries(int version, int (*action)(int, char *[], void *),
+void list_queries(int (*action)(int, char *[], void *),
void *actarg);
void help_query(struct query *q, int (*action)(int, char *[], void *),
void *actarg);
-struct query *get_query_by_name(char *name, int version);
+struct query *get_query_by_name(char *name);
/* build where clause */
build_qual(v->qual, v->argc, argv, qual);
- if (log_flags & LOG_VALID)
- /* tell the logfile what we're doing */
- com_err(whoami, 0, "validating row: %s", qual);
-
/* look for the record */
sprintf(stmt_buf, "SELECT COUNT (*) FROM %s WHERE %s",
table_name[q->rtable], qual);
switch (vo->type)
{
case V_NAME:
- if (log_flags & LOG_VALID)
- {
- com_err(whoami, 0, "validating %s in %s: %s",
- vo->namefield, table_name[vo->table], argv[vo->index]);
- }
status = validate_name(argv, vo);
break;
case V_ID:
- if (log_flags & LOG_VALID)
- {
- com_err(whoami, 0, "validating %s in %s: %s",
- vo->idfield, table_name[vo->table], argv[vo->index]);
- }
status = validate_id(q, argv, vo);
break;
case V_TYPE:
- if (log_flags & LOG_VALID)
- {
- com_err(whoami, 0, "validating %s type: %s",
- table_name[vo->table], argv[vo->index]);
- }
status = validate_type(argv, vo);
break;
case V_TYPEDATA:
- if (log_flags & LOG_VALID)
- {
- com_err(whoami, 0, "validating typed data (%s): %s",
- argv[vo->index - 1], argv[vo->index]);
- }
status = validate_typedata(q, argv, vo);
break;
case V_RENAME:
- if (log_flags & LOG_VALID)
- {
- com_err(whoami, 0, "validating rename %s in %s",
- argv[vo->index], table_name[vo->table]);
- }
status = validate_rename(argv, vo);
break;
case V_CHAR:
- if (log_flags & LOG_VALID)
- com_err(whoami, 0, "validating chars: %s", argv[vo->index]);
status = validate_chars(argv, vo);
break;
case V_LEN:
- if (log_flags & LOG_VALID)
- com_err(whoami, 0, "validating length: %s", argv[vo->index]);
status = validate_len(argv, vo);
break;
#
SRCS= auth_002.c checksum.c client.c config.c exec_002.c \
- get_file.c log.c send_file.c ticket.c \
+ get_file.c sendrecv.c send_file.c ticket.c \
update_server.c update_test.c xfer_002.c xfer_003.c
SRCDIR=${SRCTOP}/update
CODE=${SRCS} Imakefile
SOBJS = auth_002.o checksum.o config.o exec_002.o get_file.o \
- log.o update_server.o xfer_002.o xfer_003.o
-COBJS= checksum.o client.o send_file.o ticket.o
+ update_server.o xfer_002.o xfer_003.o sendrecv.o
+COBJS= checksum.o client.o send_file.o ticket.o sendrecv.o
#ifdef HAS_VSPRINTF
DEFINES= -DHAS_VSPRINTF
${LD} -r -o moira_update.o ${COBJS}
#endif
-program(update_server, ${SOBJS},${MR_LIBDEP} ${GDB_LIBDEP}, ${CLIBS},${ETCDIR})
-program(update_test, update_test.o ${COBJS}, ${MR_LIBDEP} ${GDB_LIBDEP}, ${CLIBS},${BINDIR})
+program(update_server, ${SOBJS},${MR_LIBDEP}, ${CLIBS},${ETCDIR})
+program(update_test, update_test.o ${COBJS}, ${MR_LIBDEP}, ${CLIBS},${BINDIR})
#include <errno.h>
#include <stdio.h>
-#include <gdb.h>
#include <krb.h>
RCSID("$Header$");
-extern char buf[BUFSIZ];
-extern int have_authorization;
-extern CONNECTION conn;
-extern int code;
static char service[] = "rcmd";
static char master[] = "sms";
static char qmark[] = "???";
-extern C_Block session;
+extern des_cblock session;
/*
* authentication request auth_002:
*
*/
-int auth_002(char *str)
+void auth_002(int conn, char *str)
{
- STRING data;
char aname[ANAME_SZ], ainst[INST_SZ], arealm[REALM_SZ];
AUTH_DAT ad;
- char *p, *first;
+ char *p, *first, *data;
+ size_t size;
KTEXT_ST ticket_st;
- struct utsname name;
des_key_schedule sched;
- C_Block nonce, nonce2;
+ des_cblock nonce, nonce2;
+ long code;
- if (send_ok())
- lose("sending okay for authorization (auth_002)");
- code = receive_object(conn, (char *)&data, STRING_T);
- if (code)
+ send_ok(conn);
+
+ recv_string(conn, &data, &size);
+ if (size > sizeof(ticket_st.dat))
{
- code = connection_errno(conn);
- lose("awaiting Kerberos authenticators");
+ code = KE_RD_AP_UNDEC;
+ com_err(whoami, code, ": authenticator too large");
+ send_int(conn, code);
+ return;
}
- uname(&name);
+ memcpy(ticket_st.dat, data, size);
+ free(data);
ticket_st.mbz = 0;
- ticket_st.length = MAX_STRING_SIZE(data);
- memcpy(ticket_st.dat, STRING_DATA(data), MAX_STRING_SIZE(data));
- code = krb_rd_req(&ticket_st, service, krb_get_phost(name.nodename), 0,
+ ticket_st.length = size;
+ code = krb_rd_req(&ticket_st, service, krb_get_phost(hostname), 0,
&ad, KEYFILE);
if (code)
{
strcmp(arealm, ad.prealm))
goto auth_failed;
- if (send_ok())
- lose("sending preliminary approval of authorization");
+ send_ok(conn);
/* replay protection */
des_random_key(&nonce);
- STRING_DATA(data) = (char *)nonce;
- MAX_STRING_SIZE(data) = 8;
- if (send_object(conn, (char *)&data, STRING_T))
- lose("sending nonce");
- code = receive_object(conn, (char *)&data, STRING_T);
- if (code)
- {
- code = connection_errno(conn);
- goto auth_failed;
- }
+ send_string(conn, (char *)nonce, sizeof(nonce));
+ recv_string(conn, &data, &size);
des_key_sched(ad.session, sched);
- des_ecb_encrypt(STRING_DATA(data), nonce2, sched, 0);
+ des_ecb_encrypt(data, nonce2, sched, 0);
+ free(data);
if (memcmp(nonce, nonce2, sizeof(nonce)))
goto auth_failed;
+ send_ok(conn);
- if (send_ok())
- lose("sending approval of authorization");
have_authorization = 1;
/* Stash away session key */
memcpy(session, ad.session, sizeof(session));
- return 0;
+ return;
+
auth_failed:
- sprintf(buf, "auth for %s.%s@%s failed: %s",
- ad.pname, ad.pinst, ad.prealm, error_message(code));
- {
- int rc;
- rc = send_object(conn, (char *)&code, INTEGER_T);
- code = rc;
- }
- if (code)
- lose("sending rejection of authenticator");
- return EPERM;
+ com_err(whoami, code, "auth for %s.%s@%s failed",
+ ad.pname, ad.pinst, ad.prealm);
+ send_int(conn, code);
}
* returns 24-bit checksum of bytes in file
*/
-int checksum_file(char *path)
+long checksum_file(char *path)
{
- int sum;
+ long sum;
int ch;
FILE *f;
#include <moira.h>
#include "update.h"
+#include <errno.h>
#include <stdio.h>
+#include <stdlib.h>
#include <des.h>
-#include <gdb.h>
#include <krb.h>
RCSID("$Header$");
-extern int dbg;
-extern C_Block session;
+extern des_cblock session;
-CONNECTION conn;
-
-int send_auth(char *host_name)
+int send_auth(int conn, char *host_name)
{
KTEXT_ST ticket_st;
- KTEXT ticket = &ticket_st;
- STRING data;
- int code;
- int response;
- int auth_version = 2;
+ int code, auth_version = 2;
+ long response;
- code = get_mr_update_ticket(host_name, ticket);
+ code = get_mr_update_ticket(host_name, &ticket_st);
if (code)
return code;
- STRING_DATA(data) = "AUTH_002";
- MAX_STRING_SIZE(data) = 9;
- code = send_object(conn, (char *)&data, STRING_T);
+ code = send_string(conn, "AUTH_002", 9);
if (code)
- return connection_errno(conn);
- code = receive_object(conn, (char *)&response, INTEGER_T);
+ return code;
+ code = recv_int(conn, &response);
if (code)
- return connection_errno(conn);
+ return code;
if (response)
{
- STRING_DATA(data) = "AUTH_001";
- MAX_STRING_SIZE(data) = 9;
- code = send_object(conn, (char *)&data, STRING_T);
+ code = send_string(conn, "AUTH_001", 9);
if (code)
- return connection_errno(conn);
- code = receive_object(conn, (char *)&response, INTEGER_T);
+ return code;
+ code = recv_int(conn, &response);
if (code)
- return connection_errno(conn);
+ return code;
if (response)
return response;
auth_version = 1;
}
- STRING_DATA(data) = (char *)ticket->dat;
- MAX_STRING_SIZE(data) = ticket->length;
- code = send_object(conn, (char *)&data, STRING_T);
+ code = send_string(conn, (char *)ticket_st.dat, ticket_st.length);
if (code)
- return connection_errno(conn);
- code = receive_object(conn, (char *)&response, INTEGER_T);
+ return code;
+ code = recv_int(conn, &response);
if (code)
- return connection_errno(conn);
+ return code;
if (response)
return response;
{
des_key_schedule sched;
C_Block enonce;
+ char *data;
+ size_t size;
- code = receive_object(conn, (char *)&data, STRING_T);
+ code = recv_string(conn, &data, &size);
if (code)
- return connection_errno(conn);
+ return code;
des_key_sched(session, sched);
- des_ecb_encrypt(STRING_DATA(data), enonce, sched, 1);
- STRING_DATA(data) = enonce;
- code = send_object(conn, (char *)&data, STRING_T);
+ des_ecb_encrypt(data, enonce, sched, 1);
+ free(data);
+ code = send_string(conn, (char *)enonce, sizeof(enonce));
if (code)
- return connection_errno(conn);
- code = receive_object(conn, (char *)&response, INTEGER_T);
+ return code;
+ code = recv_int(conn, &response);
if (code)
- return connection_errno(conn);
+ return code;
if (response)
return response;
}
return MR_SUCCESS;
}
-int execute(char *path)
+int execute(int conn, char *path)
{
- int response;
- STRING data;
+ long response;
+ char *data;
int code;
- string_alloc(&data, BUFSIZ);
- sprintf(STRING_DATA(data), "EXEC_002 %s", path);
- code = send_object(conn, (char *)&data, STRING_T);
+ data = malloc(10 + strlen(path));
+ if (!data)
+ return ENOMEM;
+ sprintf(data, "EXEC_002 %s", path);
+ code = send_string(conn, data, strlen(data) + 1);
+ free(data);
if (code)
- return connection_errno(conn);
- code = receive_object(conn, (char *)&response, INTEGER_T);
+ return code;
+ code = recv_int(conn, &response);
if (code)
- return connection_errno(conn);
+ return code;
if (response)
return response;
+
return MR_SUCCESS;
}
-void send_quit(void)
+void send_quit(int conn)
+{
+ send_string(conn, "quit", 5);
+}
+
+void fail(int conn, int err, char *msg)
{
- STRING str;
- if (!conn)
- return;
- string_alloc(&str, 5);
- strcpy(STRING_DATA(str), "quit");
- send_object(conn, (char *)&str, STRING_T);
- string_free(&str);
+ return;
}
#include <sys/stat.h>
#include <ctype.h>
+#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
static char *config_buf = NULL;
static char **config_keys, **config_values;
-
static int init(void)
{
int fd, count = 0;
struct stat st;
- char *p;
+ char *p, *start;
/* Only execute once */
if (config_buf)
{
config_buf = "";
config_keys = malloc(sizeof(char *) * 2);
+ if (!config_keys)
+ return ENOMEM;
config_keys[0] = config_keys[1] = NULL;
return MR_SUCCESS;
}
+
if (fstat(fd, &st) < 0)
return MR_INTERNAL;
+
config_buf = malloc(st.st_size + 2);
if (!config_buf)
- return MR_NO_MEM;
+ return ENOMEM;
+
if (read(fd, config_buf, st.st_size) < st.st_size)
{
free(config_buf);
if (!config_keys || !config_values)
{
free(config_buf);
+ free(config_keys);
+ free(config_values);
config_buf = NULL;
- return MR_NO_MEM;
+ return ENOMEM;
}
+
count = 0;
for (p = strtok(config_buf, "\n"); p; p = strtok(NULL, "\n"))
config_keys[count++] = p;
config_keys[count] = NULL;
+
for (count = 0; config_keys[count]; count++)
{
config_values[count] = "";
#include <stdio.h>
#include <unistd.h>
-#include <gdb.h>
-
RCSID("$Header$");
-extern CONNECTION conn;
-extern int code, uid, log_priority, have_authorization;
-extern char *whoami;
-
-int exec_002(char *str)
+void exec_002(int conn, char *str)
{
- int waitb;
+ int waitb, n;
sigset_t mask, oldmask;
- int n, pid;
+ pid_t pid;
+ long code;
if (!have_authorization)
{
- reject_call(MR_PERM);
- return 0;
+ send_int(conn, MR_PERM);
+ return;
}
if (config_lookup("noexec"))
{
- code = EPERM;
- send_object(conn, (char *)&code, INTEGER_T);
- com_err(whoami, code, "Not allowed to execute");
- return 0;
+ send_int(conn, EPERM);
+ com_err(whoami, EPERM, "Not allowed to execute");
+ return;
}
+
str += 8;
while (*str == ' ')
str++;
case -1:
n = errno;
sigprocmask(SIG_UNBLOCK, &oldmask, &mask);
- log_priority = log_ERROR;
- com_err(whoami, errno, ": can't fork to run install script");
- code = send_object(conn, (char *)&n, INTEGER_T);
+ com_err(whoami, n, ": can't fork to run install script");
+ code = send_int(conn, n);
if (code)
exit(1);
- return 0;
+ return;
+
case 0:
if (setuid(uid) < 0)
{
execlp(str, str, NULL);
n = errno;
sigprocmask(SIG_UNBLOCK, &oldmask, &mask);
- log_priority = log_ERROR;
com_err(whoami, n, ": %s", str);
- send_object(conn, (char *)&n, INTEGER_T);
+ send_int(conn, n);
exit(1);
+
default:
do
n = wait(&waitb);
while (n != -1 && n != pid);
+
sigprocmask(SIG_UNBLOCK, &oldmask, &mask);
if ((WIFEXITED(waitb) && (WEXITSTATUS(waitb) != 0)) ||
WIFSIGNALED(waitb))
{
- log_priority = log_ERROR;
if (WIFSIGNALED(waitb))
{
n = MR_COREDUMP;
com_err(whoami, n, " child exited with status %d",
WEXITSTATUS(waitb));
}
- code = send_object(conn, (char *)&n, INTEGER_T);
+ code = send_int(conn, n);
if (code)
exit(1);
}
else
{
- code = send_ok();
+ code = send_ok(conn);
if (code)
exit(1);
}
}
- return 0;
+ return;
}
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
+#include <stdlib.h>
#include <unistd.h>
#include <des.h>
-#include <gdb.h>
RCSID("$Header$");
#define MIN(a, b) (((a) < (b)) ? (a) : (b))
#endif /* MIN */
-extern CONNECTION conn;
-extern char buf[BUFSIZ];
-
-extern int code, uid;
-extern char *whoami;
-
-extern int have_authorization, have_file, done;
-extern C_Block session;
static des_key_schedule sched;
static des_cblock ivec;
+extern des_cblock session;
-static int get_block(int fd, int max_size, int encrypt);
+static int get_block(int conn, int fd, int max_size, int encrypt);
/*
* get_file()
*
*/
-int get_file(char *pathname, int file_size, int checksum,
+int get_file(int conn, char *pathname, int file_size, int checksum,
int mode, int encrypt)
{
- int fd, n_written;
+ int fd, n_written, code;
int found_checksum;
+ char buf[BUFSIZ];
if (!have_authorization)
{
- reject_call(MR_PERM);
+ send_int(conn, MR_PERM);
return 1;
}
- if (done) /* re-initialize data */
- initialize();
if (setuid(uid) < 0)
{
com_err(whoami, errno, "Unable to setuid to %d\n", uid);
exit(1);
}
+
/* unlink old file */
if (!config_lookup("noclobber"))
unlink(pathname);
if (fd == -1)
{
code = errno;
- sprintf(buf, "%s: creating file %s (get_file)",
- error_message(code), pathname);
- mr_log_error(buf);
- report_error("reporting file creation error (get_file)");
+ com_err(whoami, errno, "creating file %s (get_file)", pathname);
+ send_int(conn, code);
if (setuid(0) < 0)
{
com_err(whoami, errno, "Unable to setuid back to %d\n", 0);
}
return 1;
}
+
/* check to see if we've got the disk space */
n_written = 0;
while (n_written < file_size)
if (n_wrote == -1)
{
code = errno;
- sprintf(buf, "%s: verifying free disk space for %s (get_file)",
- error_message(code), pathname);
- mr_log_error(buf);
+ com_err(whoami, code, "verifying free disk space for %s (get_file)",
+ pathname);
+ send_int(conn, code);
+
/* do all we can to free the space */
unlink(pathname);
ftruncate(fd, 0);
close(fd);
- report_error("reporting test-write error (get_file)");
+
if (setuid(0) < 0)
{
com_err(whoami, errno, "Unable to setuid back to %d\n", 0);
}
n_written += n_wrote;
}
+
lseek(fd, 0, SEEK_SET);
- if (send_ok())
- lose("sending okay for file transfer (get_file)");
+ send_ok(conn);
+
if (encrypt)
{
des_key_sched(session, sched);
memcpy(ivec, session, sizeof(ivec));
}
+
n_written = 0;
- while (n_written < file_size && code == 0)
+ while (n_written < file_size)
{
- int n_got = get_block(fd, file_size - n_written, encrypt);
+ int n_got = get_block(conn, fd, file_size - n_written, encrypt);
+
if (n_got == -1)
{
/* get_block has already printed a message */
}
n_written += n_got;
if (n_written != file_size)
- {
- if (send_ok())
- lose("receiving data");
- }
- }
- if (code)
- {
- code = connection_errno(conn);
- report_error("reading file (get_file)");
- if (setuid(0) < 0)
- {
- com_err(whoami, errno, "Unable to setuid back to %d\n", 0);
- exit(1);
- }
- return 1;
+ send_ok(conn);
}
+
fsync(fd);
ftruncate(fd, file_size);
fsync(fd);
close(fd);
+
if (setuid(0) < 0)
{
com_err(whoami, errno, "Unable to setuid back to %d\n", 0);
exit(1);
}
+
/* validate checksum */
found_checksum = checksum_file(pathname);
if (checksum != found_checksum)
code = MR_MISSINGFILE;
com_err(whoami, code, ": expected = %d, found = %d",
checksum, found_checksum);
- report_error("checksum error");
- return 1;
- }
- /* send ack or nack */
- have_file = 1;
- if (send_ok())
- {
- code = connection_errno(conn);
- unlink(pathname);
- lose("sending ok after file transfer (get_file)");
+ send_int(conn, code);
return 1;
}
+
+ send_ok(conn);
return 0;
}
-static int get_block(int fd, int max_size, int encrypt)
+static int get_block(int conn, int fd, int max_size, int encrypt)
{
- STRING data;
- unsigned char dst[UPDATE_BUFSIZ + 8], *src;
- int n_read, n, i;
+ char *data;
+ size_t len;
+ int n_read, n, i, code;
- code = receive_object(conn, (char *)&data, STRING_T);
- if (code)
- {
- code = connection_errno(conn);
- lose("receiving data file (get_file)");
- }
+ recv_string(conn, &data, &len);
if (encrypt)
{
- src = (unsigned char *)STRING_DATA(data);
- n = MAX_STRING_SIZE(data);
- des_pcbc_encrypt(src, dst, n, sched, ivec, 1);
+ char *unenc = malloc(len);
+
+ if (!unenc)
+ {
+ send_int(conn, ENOMEM);
+ return -1;
+ }
+
+ des_pcbc_encrypt(data, unenc, len, sched, ivec, 1);
for (i = 0; i < 8; i++)
- ivec[i] = src[n - 8 + i] ^ dst[n - 8 + i];
- memcpy(STRING_DATA(data), dst, n);
+ ivec[i] = data[len - 8 + i] ^ unenc[len - 8 + i];
+ free(data);
+ data = unenc;
}
- n_read = MIN(MAX_STRING_SIZE(data), max_size);
+ n_read = MIN(len, max_size);
n = 0;
while (n < n_read)
{
int n_wrote;
- n_wrote = write(fd, STRING_DATA(data) + n, n_read - n);
+ n_wrote = write(fd, data + n, n_read - n);
if (n_wrote == -1)
{
code = errno;
- sprintf(buf, "%s: writing file (get_file)", error_message(code));
- mr_log_error(buf);
- string_free(&data);
- report_error("reporting write error (get_file)");
+ com_err(whoami, errno, "writing file (get_file)");
+ send_int(conn, code);
+ free(data);
close(fd);
return -1;
}
n += n_wrote;
}
- string_free(&data);
+ free(data);
return n;
}
+++ /dev/null
-/* $Id$
- *
- * handle logging for dcm and update server
- *
- * Copyright (C) 1988-1998 by the Massachusetts Institute of Technology.
- * For copying and distribution information, please see the file
- * <mit-copyright.h>.
- */
-
-#include <mit-copyright.h>
-#include <moira.h>
-#include "update_server.h"
-
-#include <stdio.h>
-#include <stdarg.h>
-
-#include "update.h"
-
-#ifdef use_syslog
-#include <syslog.h>
-#else
-#define use_tty
-#endif
-
-RCSID("$Header$");
-
-void mr_update_com_err_hook(const char *whoami, long code,
- const char *fmt, va_list args);
-
-#ifdef use_syslog
-int syslog_prio[] = {
- LOG_DEBUG,
- LOG_INFO,
- LOG_WARNING,
- LOG_ERR
-};
-#endif
-extern int log_priority;
-extern char *whoami;
-
-void mr_update_com_err_hook(const char *whoami, long code,
- const char *fmt, va_list args)
-{
- char buf[BUFSIZ], *cp;
-
-#ifndef use_syslog
- strcpy(buf, whoami);
- for (cp = buf; *cp; cp++)
- ;
- *cp++ = ':';
- *cp++ = ' ';
-#else
- cp = buf;
- *cp = '\0';
-#endif
- if (code)
- {
- strcpy(cp, error_message(code));
- while (*cp)
- cp++;
- }
- vsprintf(cp, fmt, args);
-#ifdef use_syslog
- syslog(syslog_prio[log_priority], "%s", buf);
-#endif
-#ifdef use_tty
- puts(buf);
-#endif
-}
-
-void mr_update_initialize(void)
-{
- static int initialized = 0;
- if (initialized)
- return;
-#ifdef use_syslog
- openlog(whoami, LOG_PID, LOG_DAEMON);
-#endif
- set_com_err_hook(mr_update_com_err_hook);
- log_priority = log_INFO;
- initialized = 1;
-}
-
-
-static char fmt[] = "[%s] %s";
-
-#define def(name, level, prio) \
-void name(char *msg)\
-{\
- int old_prio; \
- old_prio = log_priority; \
- mr_update_initialize(); \
- com_err(whoami, 0, fmt, level, msg); \
- log_priority = old_prio; \
-}
-
-def(mr_log_error, "error", log_ERROR)
-def(mr_log_warning, "warning", log_WARNING)
-def(mr_log_info, "info", log_INFO)
-def(mr_debug, "debug", log_DEBUG)
#include <unistd.h>
#include <des.h>
-#include <gdb.h>
#include <update.h>
RCSID("$Header$");
-extern CONNECTION conn;
-char buf[BUFSIZ];
-extern C_Block session;
-extern char *whoami;
+extern des_cblock session;
/*
* syntax:
* 1 on error (file not found, etc)
*/
-int send_file(char *pathname, char *target_path, int encrypt)
+int send_file(int conn, char *pathname, char *target_path, int encrypt)
{
int n, fd, code, n_to_send, i;
- STRING data;
- unsigned char dst[UPDATE_BUFSIZ + 8], *src;
+ char data[UPDATE_BUFSIZ], enc[UPDATE_BUFSIZ];
+ long response;
struct stat statb;
des_key_schedule sched;
des_cblock ivec;
}
n_to_send = statb.st_size;
- string_alloc(&data, UPDATE_BUFSIZ);
- sprintf(STRING_DATA(data), "XFER_00%c %d %d %s",
- (encrypt ? '3' : '2'), n_to_send,
+ sprintf(data, "XFER_00%c %d %d %s", (encrypt ? '3' : '2'), n_to_send,
checksum_file(pathname), target_path);
- code = send_object(conn, (char *)&data, STRING_T);
+ code = send_string(conn, data, strlen(data) + 1);
if (code)
{
- com_err(whoami, connection_errno(conn), " sending XFER request");
+ com_err(whoami, code, "sending XFER request");
close(fd);
- return connection_errno(conn);
+ return code;
}
- code = receive_object(conn, (char *)&n, INTEGER_T);
+ code = recv_int(conn, &response);
if (code)
{
- com_err(whoami, connection_errno(conn),
- " getting reply from XFER request");
+ com_err(whoami, code, "getting reply from XFER request");
close(fd);
- return connection_errno(conn);
+ return code;
}
- if (n)
+ if (response)
{
- com_err(whoami, n, " transfer request (XFER) rejected");
+ com_err(whoami, response, "transfer request (XFER) rejected");
close(fd);
- return n;
+ return response;
}
- code = receive_object(conn, (char *)&n, INTEGER_T);
+ code = recv_int(conn, &response);
if (code)
{
- com_err(whoami, connection_errno(conn), ": lost connection");
+ com_err(whoami, code, ": lost connection");
close(fd);
- return connection_errno(conn);
+ return code;
}
- if (n)
+ if (response)
{
- com_err(whoami, n, " from remote server: can't update %s", pathname);
+ com_err(whoami, response, " from remote server: can't update %s",
+ pathname);
close(fd);
- return n;
+ return response;
}
if (encrypt)
while (n_to_send > 0)
{
- n = read(fd, STRING_DATA(data), UPDATE_BUFSIZ);
+ n = read(fd, data, sizeof(data));
if (n < 0)
{
- com_err(whoami, errno, " reading %s for transmission", pathname);
+ com_err(whoami, errno, "reading %s for transmission", pathname);
close(fd);
return MR_ABORTED;
}
- MAX_STRING_SIZE(data) = n;
if (encrypt)
{
- src = (unsigned char *)STRING_DATA(data);
- memmove(dst, src, n);
- memset(dst + n, 0, 7);
- /* encrypt! */
- des_pcbc_encrypt(dst, src, n, sched, ivec, 0);
+ memset(data + n, 0, sizeof(data) -n);
+ des_pcbc_encrypt(data, enc, n, sched, ivec, 0);
/* save vector to continue chaining */
for (i = 0; i < 8; i++)
- ivec[i] = dst[n - 8 + i] ^ src[n - 8 + i];
+ ivec[i] = data[n - 8 + i] ^ enc[n - 8 + i];
/* round up to multiple of 8 */
- data.length = (data.length + 7) & 0xfffffff8;
+ n = (n + 7) & ~7;
+ code = send_string(conn, enc, n);
}
- code = send_object(conn, (char *)&data, STRING_T);
+ else
+ code = send_string(conn, data, n);
if (code)
{
- com_err(whoami, connection_errno(conn), " transmitting file %s",
- pathname);
+ com_err(whoami, code, "transmitting file %s", pathname);
close(fd);
- return connection_errno(conn);
+ return code;
}
+
n_to_send -= n;
- code = receive_object(conn, (char *)&n, INTEGER_T);
+ code = recv_int(conn, &response);
if (code)
{
- com_err(whoami, connection_errno(conn),
- " awaiting ACK remote server during transmission of %s",
+ com_err(whoami, code, "awaiting ACK during transmission of %s",
pathname);
close(fd);
- return connection_errno(conn);
+ return code;
}
- if (n)
+ if (response)
{
- com_err(whoami, n, " from remote server during transmission of %s",
+ com_err(whoami, response,
+ "from remote server during transmission of %s",
pathname);
close(fd);
- return n;
+ return response;
}
}
+
if (statb.st_size == 0)
{
- code = receive_object(conn, (char *)&n, INTEGER_T);
+ code = recv_int(conn, &response);
if (code)
{
- com_err(whoami, connection_errno(conn),
- " awaiting ACK remote server after transmission of %s",
+ com_err(whoami, code, "awaiting ACK after transmission of %s",
pathname);
close(fd);
- return connection_errno(conn);
+ return code;
}
- if (n)
+ if (response)
{
- com_err(whoami, n, " from remote server after transmission of %s",
+ com_err(whoami, response,
+ "from remote server after transmission of %s",
pathname);
close(fd);
- return n;
+ return response;
}
}
close(fd);
--- /dev/null
+/* $Id$
+ *
+ * socket layer for update_server
+ *
+ * Copyright (C) 1997-1998 by the Massachusetts Institute of Technology
+ * For copying and distribution information, please see the file
+ * <mit-copyright.h>.
+ */
+
+#include <mit-copyright.h>
+#include <moira.h>
+
+#include <sys/uio.h>
+
+#include <errno.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+RCSID("$Header$");
+
+#define putlong(cp, l) { cp[0] = l >> 24; cp[1] = l >> 16; cp[2] = l >> 8; cp[3] = l; }
+#define getlong(cp, l) l = ((cp[0] * 256 + cp[1]) * 256 + cp[2]) * 256 + cp[3]
+
+extern void fail(int conn, int err, char *msg);
+
+int send_int(int conn, long data)
+{
+ char buf[8];
+
+ putlong(buf, 4);
+ putlong((buf + 4), data);
+ if (write(conn, buf, 8) == 8)
+ return 0;
+ else
+ {
+ fail(conn, errno, "sending integer");
+ return errno;
+ }
+}
+
+int recv_int(int conn, long *data)
+{
+ char buf[8];
+
+ if (read(conn, buf, 8) != 8)
+ {
+ fail(conn, errno, "reading integer");
+ return errno;
+ }
+ getlong((buf + 4), *data);
+ return 0;
+}
+
+
+int send_string(int conn, char *buf, size_t len)
+{
+ char fulllen[4], stringlen[4];
+ struct iovec iov[3];
+
+ putlong(fulllen, (len + 4));
+ putlong(stringlen, len);
+ iov[0].iov_base = fulllen;
+ iov[0].iov_len = 4;
+ iov[1].iov_base = stringlen;
+ iov[1].iov_len = 4;
+ iov[2].iov_base = buf;
+ iov[2].iov_len = len;
+
+ if (writev(conn, iov, 3) == -1)
+ {
+ fail(conn, errno, "sending string");
+ return errno;
+ }
+ else
+ return 0;
+}
+
+int recv_string(int conn, char **buf, size_t *len)
+{
+ char tmp[4];
+
+ if (read(conn, tmp, 4) != 4)
+ {
+ fail(conn, errno, "reading string");
+ return errno;
+ }
+ if (read(conn, tmp, 4) != 4)
+ {
+ fail(conn, errno, "reading string");
+ return errno;
+ }
+ getlong(tmp, *len);
+
+ *buf = malloc(*len);
+ if (!*buf)
+ {
+ fail(conn, ENOMEM, "reading string");
+ return ENOMEM;
+ }
+ if (read(conn, *buf, *len) != *len)
+ {
+ free(buf);
+ fail(conn, errno, "reading string");
+ return errno;
+ }
+
+ return 0;
+}
int main(int argc, char **argv)
{
int fd;
- C_Block key;
+ des_cblock key;
char *tty;
tty = ttyname(0);
if (!tty || strcmp(tty, "/dev/console"))
RCSID("$Header$");
-extern char *whoami;
-
-/* too bad we can't set the pathname easily */
static char *srvtab = KEYFILE; /* default == /etc/srvtab */
static char realm[REALM_SZ];
static char master[INST_SZ] = "sms";
static char service[ANAME_SZ] = "rcmd";
-C_Block session;
-
-int get_mr_tgt(void);
-
-static void init(void)
-{
- static int initialized = 0;
-
- if (!initialized)
- {
- if (krb_get_lrealm(realm, 1))
- strcpy(realm, KRB_REALM);
- initialize_krb_error_table();
- initialized = 1;
- }
-}
+des_cblock session;
+static int get_mr_tgt(void);
int get_mr_update_ticket(char *host, KTEXT ticket)
{
- int code;
- int pass;
+ int code, pass;
char phost[BUFSIZ];
CREDENTIALS cr;
pass = 1;
- init();
+ if (krb_get_lrealm(realm, 1))
+ strcpy(realm, KRB_REALM);
strcpy(phost, (char *)krb_get_phost(host));
+
try_it:
code = krb_mk_req(ticket, service, phost, realm, (long)0);
if (code)
return code;
}
-int get_mr_tgt(void)
+static int get_mr_tgt(void)
{
int code;
char linst[INST_SZ], kinst[INST_SZ];
- init();
linst[0] = '\0';
strcpy(kinst, "krbtgt");
code = krb_get_svc_in_tkt(master, linst, realm, kinst, realm,
#include "update_server.h"
#include <sys/stat.h>
+#include <sys/utsname.h>
+#include <arpa/inet.h>
+#include <netinet/in.h>
+
+#include <errno.h>
#include <pwd.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <des.h>
-#include <gdb.h>
#include "update.h"
RCSID("$Header$");
-extern int errno;
-
-CONNECTION conn;
-int code, log_priority;
-char *whoami;
+char *whoami, *hostname;
int have_authorization = 0;
-C_Block session;
-int have_file = 0;
-int done = 0;
+des_cblock session;
int uid = 0;
-#define send_int(n) \
- (_send_int = (n), send_object(conn, (char *)&_send_int, INTEGER_T))
-int _send_int;
-
struct _dt {
char *str;
- int (*proc)(char *);
+ void (*proc)(int, char *);
} dispatch_table[] = {
{ "AUTH_002", auth_002 },
{ "XFER_002", xfer_002 },
{ "XFER_003", xfer_003 },
{ "EXEC_002", exec_002 },
{ "quit", quit },
- { NULL, (int (*)(char *))abort }
+ { NULL, (void (*)(int, char *))abort }
};
-/* general scratch space -- useful for building error messages et al... */
-char buf[BUFSIZ];
-
int main(int argc, char **argv)
{
- STRING str;
+ char *str, *p;
+ size_t len;
struct _dt *d;
- char *p;
+ struct utsname name;
+ int s, conn;
whoami = strrchr(argv[0], '/');
if (whoami)
exit(0);
setsid();
}
- else
- gdb_debug(GDB_NOFORK);
- umask(0022);
- initialize_sms_error_table();
- initialize_krb_error_table();
- mr_update_initialize();
+ uname(&name);
+ hostname = name.nodename;
- /* wait for connection */
- gdb_init();
- /* If the config file contains a line "port portname", the daemon
- * will listen on the named port rather than SERVICE_NAME "moira_update"
- */
- if (!(p = config_lookup("port")))
- p = SERVICE_NAME;
- conn = create_forking_server(p, 0);
+ umask(0022);
+ mr_init();
/* If the config file contains a line "user username", the
* daemon will run with that user's UID.
uid = pw->pw_uid;
}
+ /* If the config file contains a line "port portname", the daemon
+ * will listen on the named port rather than SERVICE_NAME ("moira_update")
+ */
+ if (!(p = config_lookup("port")))
+ p = SERVICE_NAME;
+
+ s = mr_listen(p);
+ if (s == -1)
+ {
+ com_err(whoami, errno, "creating listening socket");
+ exit(1);
+ }
+
+ /* now loop waiting for connections */
+ while (1)
+ {
+ struct sockaddr_in client;
+ long len;
+ char *buf;
+
+ conn = mr_accept(s, &client);
+ if (conn == -1)
+ {
+ com_err(whoami, errno, "accepting on listening socket");
+ exit(1);
+ }
+ else if (conn == 0)
+ continue;
+
+ if (config_lookup("nofork") || (fork() <= 0))
+ break;
+ }
+
/* If the config file contains a line "chroot /dir/name", the
* daemon will run chrooted to that directory.
*/
}
}
- if (!conn)
- {
- com_err(whoami, errno, "can't get connection");
- exit(1);
- }
- if (connection_status(conn) == CON_STOPPED)
- {
- com_err(whoami, connection_errno(conn), ": can't get connection");
- exit(1);
- }
+ com_err(whoami, 0, "got connection");
- mr_log_info("got connection");
- /* got a connection; loop forever */
while (1)
{
- char *cp;
- code = receive_object(conn, (char *)&str, STRING_T);
+ char *cp, *str;
+ size_t len;
+ int code;
+
+ code = recv_string(conn, &str, &len);
if (code)
{
- com_err(whoami, connection_errno(conn), "receiving command");
- sever_connection(conn);
+ com_err(whoami, code, "receiving command");
+ close(conn);
exit(1);
}
- cp = strchr(STRING_DATA(str), ' ');
+
+ cp = strchr(str, ' ');
if (cp)
*cp = '\0';
for (d = dispatch_table; d->str; d++)
{
- if (!strcmp(d->str, STRING_DATA(str)))
+ if (!strcmp(d->str, str))
{
if (cp)
*cp = ' ';
- (d->proc)(STRING_DATA(str));
+ (d->proc)(conn, str);
goto ok;
}
}
- sprintf(buf, "unknown request received: %s\n", STRING_DATA(str));
- mr_log_error(buf);
- code = send_int(MR_UNKNOWN_PROC);
+ com_err(whoami, 0, "unknown request received: %s", str);
+ code = send_int(conn, MR_UNKNOWN_PROC);
if (code)
- com_err(whoami, connection_errno(conn), "sending UNKNOWN_PROC");
+ com_err(whoami, code, "sending UNKNOWN_PROC");
ok:
- string_free(&str);
+ free(str);
}
}
-int send_ok(void)
-{
- static int zero = 0;
- return code = send_object(conn, (char *)&zero, INTEGER_T);
-}
-
-
-void initialize(void)
+int send_ok(int conn)
{
- /* keep have_authorization around */
- have_file = 0;
- done = 0;
+ return send_int(conn, 0);
}
-
/*
* quit request:
*
* function:
* closes connection from MR
*/
-int quit(char *str)
+
+void quit(int conn, char *str)
{
- send_ok();
- sever_connection(conn);
- mr_log_info("Closing connection.");
+ send_ok(conn);
+ close(conn);
+ com_err(whoami, 0, "Closing connection.");
exit(0);
}
-
-/*
- * lose(msg)
- *
- * put <msg> to log as error, break connection, and exit
- */
-
-void lose(char *msg)
+void fail(int conn, int err, char *msg)
{
- com_err(whoami, code, msg);
- if (conn)
- sever_connection(conn);
+ com_err(whoami, err, msg);
+ close(conn);
exit(1);
}
-
-/*
- * report_error(msg)
- *
- * send back (external) <code>; if error, punt big with <lose(msg)>
- */
-
-void report_error(char *msg)
-{
- code = send_object(conn, (char *)&code, INTEGER_T);
- if (code)
- {
- code = connection_errno(conn);
- lose(msg);
- }
-}
-
-/*
- * reject_call(c)
- *
- * set (external) <code> to <c> and call <report_error>
- */
-
-void reject_call(int c)
-{
- code = c;
- report_error("call rejected");
-}
/* prototypes for the update_server */
-int auth_002(char *str);
-int exec_002(char *str);
-int xfer_002(char *str);
-int xfer_003(char *str);
+void auth_002(int conn, char *str);
+void exec_002(int conn, char *str);
+void xfer_002(int conn, char *str);
+void xfer_003(int conn, char *str);
/* from checksum.c */
-int checksum_file(char *path);
+long checksum_file(char *path);
/* from config.c */
char *config_lookup(char *key);
/* from get_file.c */
-int get_file(char *pathname, int file_size, int checksum,
+int get_file(int conn, char *pathname, int file_size, int checksum,
int mode, int encrypt);
/* from log.c */
/* from update_server.c */
void initialize(void);
-int send_ok(void);
-int quit(char *str);
+int send_ok(int conn);
+void quit(int conn, char *str);
void lose(char *msg);
void report_error(char *msg);
void reject_call(int c);
+
+extern char *whoami, *hostname;
+extern int have_authorization, uid;
#include <moira.h>
#include <update.h>
+#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
-#include <gdb.h>
-
void usage(void);
-CONNECTION conn;
char *whoami;
int main(int argc, char **argv)
{
- char *host, service_address[256], *file, *rfile, buf[256];
- int code, i;
+ char *host, *file, *rfile, *ibuf = NULL;
+ int code, i, count = 0, conn;
whoami = argv[0];
- initialize_sms_error_table();
- initialize_krb_error_table();
- gdb_init();
+ mr_init();
if (argc < 2)
usage();
host = argv[1];
- sprintf(service_address, "%s:%s", host, SERVICE_NAME);
- conn = start_server_connection(service_address, "");
- if (!conn || (connection_status(conn) == CON_STOPPED))
+ conn = mr_connect_internal(host, SERVICE_NAME);
+ if (!conn)
{
- com_err(whoami, connection_errno(conn),
- " can't connect to update %s", service_address);
- return MR_CANT_CONNECT;
+ com_err(whoami, errno, ": can't connect to %s:%s", host, SERVICE_NAME);
+ exit(1);
}
- code = send_auth(host);
+
+ code = send_auth(conn, host);
if (code)
- com_err(whoami, code, " authorization attempt failed");
+ com_err(whoami, code, "attempting authorization");
for (i = 2; i < argc; i++)
{
file = argv[++i];
rfile = argv[++i];
fprintf(stderr, "Sending file %s to %s as %s\n", file, host, rfile);
- send_file(file, rfile, 0);
+ send_file(conn, file, rfile, 0);
break;
case 'S':
if (i + 2 >= argc)
rfile = argv[++i];
fprintf(stderr, "Sending (encrypted) file %s to %s as %s\n",
file, host, rfile);
- send_file(file, rfile, 1);
+ send_file(conn, file, rfile, 1);
break;
case 'i':
if (i + 1 >= argc)
usage();
file = argv[++i];
- strcpy(buf, "/tmp/moira-updateXXXXX");
- mktemp(buf);
+ ibuf = strdup("/tmp/moira-updateXXXXX");
+ if (!ibuf)
+ {
+ com_err(whoami, ENOMEM, "sending instructions");
+ exit(1);
+ }
+ mktemp(ibuf);
fprintf(stderr, "Sending instructions %s to %s as %s\n",
- file, host, buf);
- send_file(file, buf, 0);
+ file, host, ibuf);
+ send_file(conn, file, ibuf, 0);
break;
case 'I':
if (i + 2 >= argc)
usage();
file = argv[++i];
- rfile = argv[++i];
- strcpy(buf, rfile);
+ ibuf = argv[++i];
+ strcpy(ibuf, rfile);
fprintf(stderr, "Sending instructions %s to %s as %s\n",
- file, host, buf);
- send_file(file, buf, 0);
+ file, host, ibuf);
+ send_file(conn, file, ibuf, 0);
break;
case 'x':
- fprintf(stderr, "Executing instructions %s on %s\n", buf, host);
- code = execute(buf);
+ if (!ibuf)
+ {
+ fprintf(stderr, "No instructions sent.");
+ usage();
+ }
+ fprintf(stderr, "Executing instructions %s on %s\n", ibuf, host);
+ code = execute(conn, ibuf);
if (code)
com_err(whoami, code, "executing");
break;
usage();
file = argv[++i];
fprintf(stderr, "Executing instructions %s on %s\n", file, host);
- code = execute(file);
+ code = execute(conn, file);
if (code)
com_err(whoami, code, "executing");
break;
usage();
}
}
- send_quit();
- conn = sever_connection(conn);
+ send_quit(conn);
+ close(conn);
exit(code);
}
#include <stdlib.h>
#include <string.h>
-#include <gdb.h>
-
RCSID("$Header$");
-extern CONNECTION conn;
-extern char buf[BUFSIZ];
-
-extern int code;
-
-extern int have_authorization, have_file, done;
-
/*
*
* syntax:
* of all at once; use checksums
*/
-int xfer_002(char *str)
+void xfer_002(int conn, char *str)
{
- int file_size;
- int checksum;
- char *pathname;
+ int file_size, checksum, code;
+ char *pathname, *p;
str += 8;
while (*str == ' ')
if (!*str)
{
failure:
- reject_call(MR_ARGS);
- return 0;
+ send_int(conn, MR_ARGS);
+ return;
}
- file_size = atoi(str);
- while (isdigit(*str))
- str++;
+
+ file_size = strtol(str, &p, 10);
+ if (p == str)
+ {
+ send_int(conn, MR_ARGS);
+ return;
+ }
+ else
+ str = p;
while (*str == ' ')
str++;
- checksum = atoi(str);
- while (isdigit(*str))
- str++;
+
+ checksum = strtol(str, &p, 10);
+ if (p == str)
+ {
+ send_int(conn, MR_ARGS);
+ return;
+ }
+ else
+ str = p;
while (*str == ' ')
str++;
+
if (*str != '/')
goto failure;
pathname = str;
+
if (!have_authorization)
{
- reject_call(MR_PERM);
- return 0;
+ send_int(conn, MR_PERM);
+ return;
}
- if (done) /* re-initialize data */
- initialize();
- code = send_ok();
- if (code)
- lose("sending ok for file xfer (2)");
- code = get_file(pathname, file_size, checksum, 0700, 0);
+
+ send_ok(conn);
+ code = get_file(conn, pathname, file_size, checksum, 0700, 0);
if (!code)
- {
- char buf[BUFSIZ];
- have_file = 1;
- strcpy(buf, "transferred file ");
- strcat(buf, pathname);
- mr_log_info(buf);
- }
- return 0;
+ com_err(whoami, 0, "Transferred file %s", pathname);
+
+ return;
}
#include <stdlib.h>
#include <string.h>
-#include <gdb.h>
-
RCSID("$Header$");
-extern CONNECTION conn;
-extern char buf[BUFSIZ];
-
-extern int code;
-
-extern int have_authorization, have_file, done;
-
/*
*
* syntax:
* this version of transfer encrypts the file being transferred.
*/
-int xfer_003(char *str)
+void xfer_003(int conn, char *str)
{
- int file_size;
- int checksum;
- char *pathname;
+ int file_size, checksum, code;
+ char *pathname, *p;
str += 8;
while (*str == ' ')
if (!*str)
{
failure:
- reject_call(MR_ARGS);
- return 0;
+ send_int(conn, MR_ARGS);
+ return;
}
- file_size = atoi(str);
- while (isdigit(*str))
- str++;
+
+ file_size = strtol(str, &p, 10);
+ if (p == str)
+ {
+ send_int(conn, MR_ARGS);
+ return;
+ }
+ else
+ str = p;
while (*str == ' ')
str++;
- checksum = atoi(str);
- while (isdigit(*str))
- str++;
+
+ checksum = strtol(str, &p, 10);
+ if (p == str)
+ {
+ send_int(conn, MR_ARGS);
+ return;
+ }
+ else
+ str = p;
while (*str == ' ')
str++;
+
if (*str != '/')
goto failure;
pathname = str;
+
if (!have_authorization)
{
- reject_call(MR_PERM);
- return 0;
+ send_int(conn, MR_PERM);
+ return;
}
- if (done) /* re-initialize data */
- initialize();
- code = send_ok();
- if (code)
- lose("sending ok for file xfer (2)");
- code = get_file(pathname, file_size, checksum, 0444, 1);
+
+ send_ok(conn);
+ code = get_file(conn, pathname, file_size, checksum, 0444, 1);
if (!code)
- {
- char buf[BUFSIZ];
- have_file = 1;
- strcpy(buf, "transferred file ");
- strcat(buf, pathname);
- mr_log_info(buf);
- }
- return 0;
+ com_err(whoami, 0, "Transferred file %s", pathname);
+
+ return;
}
MR_LIB=$(BUILDTOP)/lib/libmoira.a
MR_LIBDEP=$(MR_LIB)
-GDB_LIB=$(BUILDTOP)/gdb/libmrgdb.a
-GDB_LIBDEP=$(GDB_LIB)
-
/*
* The name of a command which compiles error tables.
/* libraries most Moira programs use */
#ifdef HESIOD
-CLIBS= $(MR_LIB) $(GDB_LIB) $(LLIB) $(LZEPHYR) $(COM_ERR) -lkrb -ldes -lhesiod
+CLIBS= $(MR_LIB) $(LLIB) $(LZEPHYR) $(COM_ERR) -lkrb -ldes -lhesiod
#else
-CLIBS= $(MR_LIB) $(GDB_LIB) $(LLIB) $(LZEPHYR) $(COM_ERR) -lkrb -ldes
+CLIBS= $(MR_LIB) $(LLIB) $(LZEPHYR) $(COM_ERR) -lkrb -ldes
#endif
/* libraries SQL programs need */