summaryrefslogtreecommitdiffstats
path: root/client
diff options
context:
space:
mode:
authorBrian Cully <bjc@kublai.com>2008-04-14 21:52:55 -0400
committerBrian Cully <github.20.shmit@spamgourmet.com>2008-04-14 21:52:55 -0400
commit6ba98a9f9f48e13738d9736cba9c45b5e94f42f2 (patch)
tree86d7c281bcdbf67eb53cee064aa905e740ec5ccf /client
downloadnastd-6ba98a9f9f48e13738d9736cba9c45b5e94f42f2.tar.gz
nastd-6ba98a9f9f48e13738d9736cba9c45b5e94f42f2.zip
Initial import
Diffstat (limited to 'client')
-rw-r--r--client/.cvsignore7
-rw-r--r--client/.pure0
-rw-r--r--client/.svn/README.txt2
-rw-r--r--client/.svn/empty-file0
-rw-r--r--client/.svn/entries67
-rw-r--r--client/.svn/format1
-rw-r--r--client/.svn/prop-base/.cvsignore.svn-base1
-rw-r--r--client/.svn/prop-base/.pure.svn-base1
-rw-r--r--client/.svn/prop-base/Makefile.svn-base1
-rw-r--r--client/.svn/prop-base/nastapi.c.svn-base1
-rw-r--r--client/.svn/prop-base/thread.c.svn-base1
-rw-r--r--client/.svn/prop-base/thread.h.svn-base1
-rw-r--r--client/.svn/props/.cvsignore.svn-work1
-rw-r--r--client/.svn/props/.pure.svn-work1
-rw-r--r--client/.svn/props/Makefile.svn-work1
-rw-r--r--client/.svn/props/nastapi.c.svn-work1
-rw-r--r--client/.svn/props/thread.c.svn-work1
-rw-r--r--client/.svn/props/thread.h.svn-work1
-rw-r--r--client/.svn/text-base/.cvsignore.svn-base7
-rw-r--r--client/.svn/text-base/.pure.svn-base0
-rw-r--r--client/.svn/text-base/Makefile.svn-base26
-rw-r--r--client/.svn/text-base/nastapi.c.svn-base885
-rw-r--r--client/.svn/text-base/thread.c.svn-base75
-rw-r--r--client/.svn/text-base/thread.h.svn-base19
-rw-r--r--client/Makefile26
-rw-r--r--client/nastapi.c885
-rw-r--r--client/thread.c75
-rw-r--r--client/thread.h19
28 files changed, 2106 insertions, 0 deletions
diff --git a/client/.cvsignore b/client/.cvsignore
new file mode 100644
index 0000000..35f79a6
--- /dev/null
+++ b/client/.cvsignore
@@ -0,0 +1,7 @@
+.pure
+*.o
+*.so
+*.a
+*.core
+*.gmon
+tags
diff --git a/client/.pure b/client/.pure
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/client/.pure
diff --git a/client/.svn/README.txt b/client/.svn/README.txt
new file mode 100644
index 0000000..271a8ce
--- /dev/null
+++ b/client/.svn/README.txt
@@ -0,0 +1,2 @@
+This is a Subversion working copy administrative directory.
+Visit http://subversion.tigris.org/ for more information.
diff --git a/client/.svn/empty-file b/client/.svn/empty-file
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/client/.svn/empty-file
diff --git a/client/.svn/entries b/client/.svn/entries
new file mode 100644
index 0000000..273f9c8
--- /dev/null
+++ b/client/.svn/entries
@@ -0,0 +1,67 @@
+<?xml version="1.0" encoding="utf-8"?>
+<wc-entries
+ xmlns="svn:">
+<entry
+ committed-rev="1"
+ name=""
+ committed-date="2005-12-08T17:32:07.572093Z"
+ url="svn+ssh://coleridge/svn/nastd/trunk/client"
+ last-author="bjc"
+ kind="dir"
+ uuid="2641c99b-6c07-0410-920d-927397d2d5d0"
+ revision="8"/>
+<entry
+ committed-rev="1"
+ name="nastapi.c"
+ text-time="2005-12-24T00:01:04.000000Z"
+ committed-date="2005-12-08T17:32:07.572093Z"
+ checksum="58742753ba1da3456bab621d9ce1b743"
+ last-author="bjc"
+ kind="file"
+ prop-time="2005-12-24T00:01:03.000000Z"/>
+<entry
+ committed-rev="1"
+ name="thread.c"
+ text-time="2005-12-24T00:01:04.000000Z"
+ committed-date="2005-12-08T17:32:07.572093Z"
+ checksum="b2c15aef2f6f15a0c7d005692aab8988"
+ last-author="bjc"
+ kind="file"
+ prop-time="2005-12-24T00:01:03.000000Z"/>
+<entry
+ committed-rev="1"
+ name=".pure"
+ text-time="2005-12-24T00:01:04.000000Z"
+ committed-date="2005-12-08T17:32:07.572093Z"
+ checksum="d41d8cd98f00b204e9800998ecf8427e"
+ last-author="bjc"
+ kind="file"
+ prop-time="2005-12-24T00:01:04.000000Z"/>
+<entry
+ committed-rev="1"
+ name=".cvsignore"
+ text-time="2005-12-24T00:01:04.000000Z"
+ committed-date="2005-12-08T17:32:07.572093Z"
+ checksum="d3ab4aa9b2ce78dcf3467cf1a8ddb6d1"
+ last-author="bjc"
+ kind="file"
+ prop-time="2005-12-24T00:01:04.000000Z"/>
+<entry
+ committed-rev="1"
+ name="Makefile"
+ text-time="2005-12-24T00:01:04.000000Z"
+ committed-date="2005-12-08T17:32:07.572093Z"
+ checksum="e8640e9f9f184a80e9bed5d894578c58"
+ last-author="bjc"
+ kind="file"
+ prop-time="2005-12-24T00:01:04.000000Z"/>
+<entry
+ committed-rev="1"
+ name="thread.h"
+ text-time="2005-12-24T00:01:04.000000Z"
+ committed-date="2005-12-08T17:32:07.572093Z"
+ checksum="a548e3157e8dba41d2ab2bb83a20aafe"
+ last-author="bjc"
+ kind="file"
+ prop-time="2005-12-24T00:01:04.000000Z"/>
+</wc-entries>
diff --git a/client/.svn/format b/client/.svn/format
new file mode 100644
index 0000000..b8626c4
--- /dev/null
+++ b/client/.svn/format
@@ -0,0 +1 @@
+4
diff --git a/client/.svn/prop-base/.cvsignore.svn-base b/client/.svn/prop-base/.cvsignore.svn-base
new file mode 100644
index 0000000..dce2c1d
--- /dev/null
+++ b/client/.svn/prop-base/.cvsignore.svn-base
@@ -0,0 +1 @@
+END
diff --git a/client/.svn/prop-base/.pure.svn-base b/client/.svn/prop-base/.pure.svn-base
new file mode 100644
index 0000000..dce2c1d
--- /dev/null
+++ b/client/.svn/prop-base/.pure.svn-base
@@ -0,0 +1 @@
+END
diff --git a/client/.svn/prop-base/Makefile.svn-base b/client/.svn/prop-base/Makefile.svn-base
new file mode 100644
index 0000000..dce2c1d
--- /dev/null
+++ b/client/.svn/prop-base/Makefile.svn-base
@@ -0,0 +1 @@
+END
diff --git a/client/.svn/prop-base/nastapi.c.svn-base b/client/.svn/prop-base/nastapi.c.svn-base
new file mode 100644
index 0000000..dce2c1d
--- /dev/null
+++ b/client/.svn/prop-base/nastapi.c.svn-base
@@ -0,0 +1 @@
+END
diff --git a/client/.svn/prop-base/thread.c.svn-base b/client/.svn/prop-base/thread.c.svn-base
new file mode 100644
index 0000000..dce2c1d
--- /dev/null
+++ b/client/.svn/prop-base/thread.c.svn-base
@@ -0,0 +1 @@
+END
diff --git a/client/.svn/prop-base/thread.h.svn-base b/client/.svn/prop-base/thread.h.svn-base
new file mode 100644
index 0000000..dce2c1d
--- /dev/null
+++ b/client/.svn/prop-base/thread.h.svn-base
@@ -0,0 +1 @@
+END
diff --git a/client/.svn/props/.cvsignore.svn-work b/client/.svn/props/.cvsignore.svn-work
new file mode 100644
index 0000000..dce2c1d
--- /dev/null
+++ b/client/.svn/props/.cvsignore.svn-work
@@ -0,0 +1 @@
+END
diff --git a/client/.svn/props/.pure.svn-work b/client/.svn/props/.pure.svn-work
new file mode 100644
index 0000000..dce2c1d
--- /dev/null
+++ b/client/.svn/props/.pure.svn-work
@@ -0,0 +1 @@
+END
diff --git a/client/.svn/props/Makefile.svn-work b/client/.svn/props/Makefile.svn-work
new file mode 100644
index 0000000..dce2c1d
--- /dev/null
+++ b/client/.svn/props/Makefile.svn-work
@@ -0,0 +1 @@
+END
diff --git a/client/.svn/props/nastapi.c.svn-work b/client/.svn/props/nastapi.c.svn-work
new file mode 100644
index 0000000..dce2c1d
--- /dev/null
+++ b/client/.svn/props/nastapi.c.svn-work
@@ -0,0 +1 @@
+END
diff --git a/client/.svn/props/thread.c.svn-work b/client/.svn/props/thread.c.svn-work
new file mode 100644
index 0000000..dce2c1d
--- /dev/null
+++ b/client/.svn/props/thread.c.svn-work
@@ -0,0 +1 @@
+END
diff --git a/client/.svn/props/thread.h.svn-work b/client/.svn/props/thread.h.svn-work
new file mode 100644
index 0000000..dce2c1d
--- /dev/null
+++ b/client/.svn/props/thread.h.svn-work
@@ -0,0 +1 @@
+END
diff --git a/client/.svn/text-base/.cvsignore.svn-base b/client/.svn/text-base/.cvsignore.svn-base
new file mode 100644
index 0000000..35f79a6
--- /dev/null
+++ b/client/.svn/text-base/.cvsignore.svn-base
@@ -0,0 +1,7 @@
+.pure
+*.o
+*.so
+*.a
+*.core
+*.gmon
+tags
diff --git a/client/.svn/text-base/.pure.svn-base b/client/.svn/text-base/.pure.svn-base
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/client/.svn/text-base/.pure.svn-base
diff --git a/client/.svn/text-base/Makefile.svn-base b/client/.svn/text-base/Makefile.svn-base
new file mode 100644
index 0000000..e92feed
--- /dev/null
+++ b/client/.svn/text-base/Makefile.svn-base
@@ -0,0 +1,26 @@
+# $Id: Makefile,v 1.10 2001/01/19 02:54:37 shmit Exp $
+
+LIBOBJS= nastapi.o thread.o
+
+all-lib:
+ @rm -f *.o
+ @make "THREADFLAGS=-UTHREADSAFECLIENT" libnast.a
+ @rm -f *.o
+ @make "THREADFLAGS=-DTHREADSAFECLIENT" libnast_r.a
+
+libnast.a: ${LIBOBJS}
+ ar r $@ ${LIBOBJS}
+ ranlib $@
+
+libnast_r.a: ${LIBOBJS}
+ ar r $@ ${LIBOBJS}
+ ranlib $@
+
+#
+# Dependencies
+#
+nastapi.o: ../include/nastd.h ../include/nastipc.h thread.h
+thread.o: thread.h
+
+MKDIR= ../Makefiles
+include ${MKDIR}/build
diff --git a/client/.svn/text-base/nastapi.c.svn-base b/client/.svn/text-base/nastapi.c.svn-base
new file mode 100644
index 0000000..3e5a3b9
--- /dev/null
+++ b/client/.svn/text-base/nastapi.c.svn-base
@@ -0,0 +1,885 @@
+#include "conf.h"
+#include "nastd.h"
+#include "nastipc.h"
+#include "thread.h"
+
+#include <errno.h>
+#include <thread.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/uio.h>
+#include <sys/un.h>
+#include <signal.h>
+#include <unistd.h>
+
+RCSID("$Id: nastapi.c,v 1.13 2001/10/29 11:17:11 shmit Exp $");
+
+char *nast_errmsgs[] = {
+ "No errors",
+ "The NASTD server has gone away",
+ "Couldn't allocate enough memory",
+ "Server response is unknown",
+ "Connection to server timed out",
+ "Unknown option returned",
+ NULL
+};
+
+static nast_string_t *
+nast_string_new(int slen, const char *data)
+{
+ nast_string_t *tmp;
+
+ tmp = malloc(sizeof(nast_string_t));
+ if (tmp == NULL)
+ return NULL;
+
+ tmp->strdata = malloc((slen+1) * sizeof(char *));
+ if (tmp == NULL)
+ return NULL;
+ memcpy(tmp->strdata, data, slen);
+ tmp->strdata[slen] = '\0';
+ tmp->strlen = slen;
+
+ return tmp;
+}
+
+static void
+nast_string_delete(nast_string_t *string)
+{
+ string->strlen = 0;
+ free(string->strdata);
+ string->strdata = NULL;
+ free(string);
+}
+
+nast_array *
+nast_array_new()
+{
+ nast_array *aa;
+
+ aa = malloc(sizeof(nast_array));
+ if (aa == NULL)
+ return NULL;
+
+ aa->nitems = 0;
+ aa->items = NULL;
+ return aa;
+}
+
+int
+nast_array_add(nast_array *array, short len, const char *data)
+{
+ const int GRANULARITY = 10;
+
+ if (array->nitems % GRANULARITY == 0) {
+ nast_string_t **tmp;
+ tmp = realloc(array->items, sizeof(char *) *
+ (GRANULARITY + array->nitems));
+ if (tmp == NULL)
+ return -1;
+ array->items = tmp;
+ }
+ array->nitems++;
+
+ array->items[array->nitems-1] = nast_string_new(len, data);
+ if (array->items[array->nitems-1] == NULL)
+ return -1;
+
+ return 0;
+}
+
+void
+nast_array_delete(nast_array *array)
+{
+ nast_free_result(array);
+}
+
+static nast_response *
+response_new()
+{
+ nast_response *tmp;
+
+ tmp = malloc(sizeof(nast_response));
+ if (tmp == NULL)
+ return NULL;
+
+ tmp->buffer = NULL;
+ tmp->bufflen = 0;
+ tmp->errcode = NAST_OK;
+ tmp->errmsg = NULL;
+ tmp->reqid = -1;
+
+ return tmp;
+}
+
+static nast_response *
+getmyresponse(nasth *s, unsigned short reqid)
+{
+ if (s->nthreads < reqid)
+ return NULL;
+
+ return s->responses[reqid-1];
+}
+
+static void
+nast_set_error(nasth *s, unsigned short reqid, errcodes code)
+{
+ nast_response *ar;
+
+ ar = getmyresponse(s, reqid);
+ if (ar == NULL)
+ return;
+
+ ar->errcode = code;
+}
+
+static int
+grow_responses(nasth *s, unsigned short maxitems)
+{
+ nast_response **tmp_resp;
+ int i;
+
+ if (s->nthreads >= maxitems)
+ return 0;
+
+ tmp_resp = realloc(s->responses, maxitems * sizeof(nast_response *));
+ if (tmp_resp == NULL)
+ return -1;
+
+ s->responses = tmp_resp;
+ for (i = s->nthreads; i < maxitems; i++) {
+ s->responses[i] = response_new();
+ if (s->responses[i] == NULL)
+ return -1;
+ }
+
+ s->nthreads = maxitems;
+ return 0;
+}
+
+static nast_array *
+build_result(nasth *s, const char *buff, short bufflen)
+{
+ nast_array *aa;
+ const char *s_p, *e_p;
+ short l;
+
+ aa = nast_array_new();
+ if (aa == NULL) {
+ nast_set_error(s, thread_id(), NAST_NOMEM);
+ return NULL;
+ }
+
+ /* Parse the buff into an array. */
+ l = 0;
+ aa->nitems = 0;
+ aa->items = NULL;
+ s_p = buff+l;
+ for (e_p = s_p; e_p <= buff+bufflen; e_p++) {
+ if (e_p == buff+bufflen || *e_p == NASTSEP) {
+ if (nast_array_add(aa, e_p - s_p, s_p) == -1) {
+ nast_set_error(s, thread_id(), NAST_NOMEM);
+ return NULL;
+ }
+ s_p = e_p + 1;
+ }
+ }
+
+ return aa;
+}
+
+static int
+addresponse(nasth *s, unsigned short reqid, char *buffer, short len)
+{
+ nast_response *ar;
+
+ switch (buffer[0]) {
+ case NASTOK:
+ nast_set_error(s, reqid, NAST_OK);
+ break;
+ case NASTERR:
+ nast_set_error(s, reqid, NAST_SERVER_ERR);
+ break;
+ default:
+ nast_set_error(s, reqid, NAST_UNKNOWN_RESPONSE);
+ }
+
+ ar = getmyresponse(s, reqid);
+ if (ar == NULL) {
+ /*
+ * Somehow we got a response for something we didn't
+ * request.
+ */
+ return -1;
+ }
+
+ ar->reqid = reqid;
+ ar->buffer = malloc(len-1);
+ if (ar->buffer == NULL) {
+ nast_set_error(s, reqid, NAST_NOMEM);
+ return -1;
+ }
+ memcpy(ar->buffer, buffer+1, len-1);
+ ar->bufflen = len-1;
+ return 0;
+}
+
+static void
+delresponse(nasth *s, unsigned short reqid)
+{
+ nast_response *ar;
+
+ ar = getmyresponse(s, reqid);
+ if (ar == NULL)
+ return;
+
+ ar->reqid = -1;
+ if (ar->buffer)
+ free(ar->buffer);
+ ar->bufflen = 0;
+}
+
+static int
+checkresponse(nasth *s, unsigned short reqid)
+{
+ nast_response *ar;
+
+ if (reqid > s->nthreads)
+ return -1;
+
+ ar = getmyresponse(s, reqid);
+ if (ar == NULL || (short)ar->reqid == -1)
+ return -1;
+
+ return 0;
+}
+
+static int
+sendcmd(nasth *s, const char *buff, short len)
+{
+ ssize_t wrote;
+
+ /* Make sure there's room in the response array. */
+ if (grow_responses(s, thread_id()) == -1) {
+ /*
+ * XXX: This is a fatal error.
+ * Find some better way to handle this.
+ */
+ exit(1);
+ }
+
+ /* If we're sending a command, make sure we get rid of old data. */
+ delresponse(s, thread_id());
+
+ /* Send the command. */
+ wrote = 0;
+ while (wrote < len) {
+ ssize_t rc;
+
+ rc = write(s->socket, buff + wrote, len - wrote);
+ if (rc == -1) {
+ if (errno == EINTR || errno == EAGAIN)
+ continue;
+ nast_set_error(s, thread_id(), NAST_SERVER_GONE);
+ return -1;
+ }
+ wrote += rc;
+ }
+
+ /* Check status. */
+ return 0;
+}
+
+static int
+getresponse(nasth *s)
+{
+ char buffer[1024];
+ nast_response *ar;
+ int ntries, rc;
+ unsigned short tid;
+ short nbytes;
+
+ ntries = 0;
+ tid = thread_id();
+reread:
+ _nast_mutex_lock(s->lock);
+
+ /*
+ * See if we already have the response from another thread which
+ * may have found it first.
+ */
+ rc = checkresponse(s, tid);
+ if (rc == 0) {
+ _nast_mutex_unlock(s->lock);
+ } else {
+ /* Not in the already read stuff, check the network. */
+ char *p;
+ short bufflen;
+ unsigned short reqid;
+ fd_set readfds;
+ struct timeval timeout;
+
+ FD_ZERO(&readfds);
+ FD_SET(s->socket, &readfds);
+ timeout.tv_sec = 0;
+ timeout.tv_usec = 10000;
+ switch(select(s->socket+1, &readfds, NULL, NULL, &timeout)) {
+ case -1:
+ _nast_mutex_unlock(s->lock);
+ nast_set_error(s, tid, NAST_SERVER_GONE);
+ return -1;
+ case 0:
+ /* Timeout expired. */
+ _nast_mutex_unlock(s->lock);
+ if (ntries++ < 50)
+ goto reread;
+ nast_set_error(s, tid, NAST_TIMEDOUT);
+ return -1;
+ }
+ nbytes = recv(s->socket, buffer, sizeof(buffer), 0);
+ if (nbytes == -1) {
+ _nast_mutex_unlock(s->lock);
+ nast_set_error(s, tid, NAST_SERVER_GONE);
+ return -1;
+ } else if (nbytes == 0) {
+ /* No response from server. That's bad. Terminate. */
+ _nast_mutex_unlock(s->lock);
+ nast_set_error(s, tid, NAST_SERVER_GONE);
+ return -1;
+ } else if (nbytes <
+ sizeof(bufflen) + sizeof(reqid) + sizeof(char)) {
+ /* Response is too short. */
+ _nast_mutex_unlock(s->lock);
+ nast_set_error(s, tid, NAST_UNKNOWN_RESPONSE);
+ return -1;
+ }
+
+ for (p = buffer; p < buffer+nbytes; p += bufflen) {
+ int l;
+
+ memcpy(&bufflen, p, sizeof(bufflen));
+ bufflen = ntohs(bufflen);
+ l = sizeof(bufflen);
+
+ /* Sanity check, just in case data gets munged. */
+ if (bufflen <= 0 || bufflen > nbytes) {
+ _nast_mutex_unlock(s->lock);
+ nast_set_error(s, tid, NAST_UNKNOWN_RESPONSE);
+ return -1;
+ }
+
+ memcpy(&reqid, p+l, sizeof(reqid));
+ reqid = ntohs(reqid);
+ l += sizeof(reqid);
+
+ /* Save this response on the response array. */
+ addresponse(s, reqid, p+l, bufflen-l);
+ }
+ /* Check the response array to see if we got our response. */
+ _nast_mutex_unlock(s->lock);
+ goto reread;
+ }
+
+ ar = getmyresponse(s, tid);
+ if (ar->errcode == NAST_OK)
+ return 0;
+ else
+ return -1;
+}
+
+nasth *
+nast_sphincter_new(const char *sock_path)
+{
+ nasth *tmp_h;
+ struct sockaddr_un sunix;
+ int rc;
+
+ tmp_h = malloc(sizeof(nasth));
+ if (tmp_h == NULL) {
+ fprintf(stderr,
+ "ERROR: Couldn't make space for sphincter: %s.\n",
+ strerror(errno));
+ return NULL;
+ }
+
+ tmp_h->nthreads = 0;
+ tmp_h->responses = NULL;
+
+ tmp_h->lock = malloc(sizeof(_nast_mutex_t));
+ if (tmp_h->lock == NULL) {
+ fprintf(stderr,
+ "ERROR: Couldn't create NAST lock: %s.\n",
+ strerror(errno));
+ nast_sphincter_close(tmp_h);
+ return NULL;
+ }
+ rc = _nast_mutex_new(tmp_h->lock);
+ if (rc) {
+ fprintf(stderr,
+ "ERROR: Couldn't initialise NAST lock: %s.\n",
+ strerror(rc));
+ nast_sphincter_close(tmp_h);
+ return NULL;
+ }
+
+ tmp_h->socket = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (tmp_h->socket == -1) {
+ fprintf(stderr,
+ "ERROR: Couldn't initialise socket: %s.\n",
+ strerror(errno));
+ nast_sphincter_close(tmp_h);
+ return NULL;
+ }
+
+ memset(&sunix, 0, sizeof(sunix));
+ if (sock_path == NULL)
+ snprintf(sunix.sun_path, sizeof(sunix.sun_path), NASTHOLE);
+ else
+ strncpy(sunix.sun_path, sock_path, sizeof(sunix.sun_path));
+ sunix.sun_family = AF_UNIX;
+
+ if (connect(tmp_h->socket, (struct sockaddr *)&sunix,
+ sizeof(sunix)) == -1) {
+ fprintf(stderr,
+ "ERROR: Couldn't connect to server: %s.\n",
+ strerror(errno));
+ nast_sphincter_close(tmp_h);
+ return NULL;
+ }
+ return tmp_h;
+}
+
+void
+nast_sphincter_close(nasth *s)
+{
+ int i;
+
+ if (s == NULL)
+ return;
+
+ s->nthreads = 0;
+
+ if (s->lock != NULL) {
+ _nast_mutex_delete(s->lock);
+ free(s->lock);
+ s->lock = NULL;
+ }
+
+ if (s->socket != -1) {
+ close(s->socket);
+ s->socket = -1;
+ }
+
+ if (s->responses != NULL) {
+ for (i = 0; i < s->nthreads; i++) {
+ if (s->responses[i]->buffer != NULL)
+ free(s->responses[i]->buffer);
+ free(s->responses[i]);
+ s->responses[i] = NULL;
+ }
+ free(s->responses);
+ s->responses = NULL;
+ }
+
+ free(s);
+}
+
+nast_array *
+nast_get_result(nasth *s)
+{
+ nast_response *ar;
+
+ ar = getmyresponse(s, thread_id());
+ if (ar == NULL)
+ return NULL;
+
+ return build_result(s, ar->buffer, ar->bufflen);
+}
+
+void
+nast_free_result(nast_array *aa)
+{
+ int i;
+
+ if (aa->items) {
+ for (i = 0; i < aa->nitems; i++)
+ nast_string_delete(aa->items[i]);
+ free(aa->items);
+ aa->items = NULL;
+ }
+ aa->nitems = 0;
+ free(aa);
+}
+
+static int
+add_reqid(char *buffer)
+{
+ unsigned short tid;
+
+ tid = thread_id();
+ memcpy(buffer, &htons(tid), sizeof(tid));
+
+ return sizeof(tid);
+}
+
+int
+nast_options_get(nasth *s, nast_options *opts)
+{
+ nast_array *aa;
+ char buffer[512];
+ short bufflen, i;
+
+ if (s == NULL) {
+ fprintf(stderr, "ERROR: Can't get options: no sphincter.\n");
+ return -1;
+ }
+
+ bufflen = sizeof(short);
+ bufflen += add_reqid(buffer+bufflen);
+
+ snprintf(buffer+bufflen, sizeof(buffer)-bufflen, "%c%c",
+ NASTCMD, NASTOPTGET);
+ bufflen += 2 * sizeof(char);
+ memcpy(buffer, &htons(bufflen), sizeof(short));
+ sendcmd(s, buffer, bufflen);
+ if (getresponse(s) == -1)
+ return -1;
+
+ /* Copy out results and free them. */
+ aa = nast_get_result(s);
+
+ if (aa->nitems != 1) {
+ nast_set_error(s, thread_id(), NAST_UNKNOWN_RESPONSE);
+ return -1;
+ }
+
+ if (sizeof(buffer) < aa->items[0]->strlen)
+ bufflen = sizeof(buffer);
+ else
+ bufflen = aa->items[0]->strlen;
+ memcpy(buffer, aa->items[0]->strdata, bufflen);
+ nast_free_result(aa);
+
+ /* Parse return into options. */
+ for (i = 0; i < bufflen; i+=2) {
+ switch (buffer[i]) {
+ case OPTQCACHE:
+ if (buffer[i+1] == OPTFALSE)
+ opts->use_qcache = NASTFALSE;
+ else
+ opts->use_qcache = NASTTRUE;
+ break;
+ case OPTLOCALDB:
+ if (buffer[i+1] == OPTFALSE)
+ opts->use_localdb = NASTFALSE;
+ else
+ opts->use_localdb = NASTTRUE;
+ break;
+ case OPTFALLASYNC:
+ if (buffer[i+1] == OPTFALSE)
+ opts->fallthrough_async = NASTFALSE;
+ else
+ opts->fallthrough_async = NASTTRUE;
+ break;
+ case OPTALWAYSFALL:
+ if (buffer[i+1] == OPTFALSE)
+ opts->always_fallthrough = NASTFALSE;
+ else
+ opts->always_fallthrough = NASTTRUE;
+ break;
+ case OPTFAILONCE:
+ if (buffer[i+1] == OPTFALSE)
+ opts->fail_once = NASTFALSE;
+ else
+ opts->fail_once = NASTTRUE;
+ break;
+ case OPTNOFALLTHROUGH:
+ if (buffer[i+1] == OPTFALSE)
+ opts->no_fallthrough = NASTFALSE;
+ else
+ opts->no_fallthrough = NASTTRUE;
+ break;
+ default:
+ nast_set_error(s, thread_id(), NAST_UNKNOWN_OPT);
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+int
+nast_options_set(nasth *s, nast_options *opts)
+{
+ char buffer[512];
+ short bufflen;
+
+ if (s == NULL) {
+ fprintf(stderr, "ERROR: Can't set options: no sphincter.\n");
+ return -1;
+ }
+
+ bufflen = sizeof(short);
+ bufflen += add_reqid(buffer+bufflen);
+
+ snprintf(buffer+bufflen, sizeof(buffer)-bufflen, "%c%c",
+ NASTCMD, NASTOPTSET);
+ bufflen += 2*sizeof(char);
+
+ buffer[bufflen] = OPTQCACHE;
+ if (opts->use_qcache)
+ buffer[bufflen+1] = OPTTRUE;
+ else
+ buffer[bufflen+1] = OPTFALSE;
+ bufflen += 2;
+
+ buffer[bufflen] = OPTLOCALDB;
+ if (opts->use_localdb)
+ buffer[bufflen+1] = OPTTRUE;
+ else
+ buffer[bufflen+1] = OPTFALSE;
+ bufflen += 2;
+
+ buffer[bufflen] = OPTFALLASYNC;
+ if (opts->fallthrough_async)
+ buffer[bufflen+1] = OPTTRUE;
+ else
+ buffer[bufflen+1] = OPTFALSE;
+ bufflen += 2;
+
+ buffer[bufflen] = OPTALWAYSFALL;
+ if (opts->always_fallthrough)
+ buffer[bufflen+1] = OPTTRUE;
+ else
+ buffer[bufflen+1] = OPTFALSE;
+ bufflen += 2;
+
+ buffer[bufflen] = OPTFAILONCE;
+ if (opts->fail_once)
+ buffer[bufflen+1] = OPTTRUE;
+ else
+ buffer[bufflen+1] = OPTFALSE;
+ bufflen += 2;
+
+ buffer[bufflen] = OPTNOFALLTHROUGH;
+ if (opts->no_fallthrough)
+ buffer[bufflen+1] = OPTTRUE;
+ else
+ buffer[bufflen+1] = OPTFALSE;
+ bufflen += 2;
+
+ memcpy(buffer, &htons(bufflen), sizeof(short));
+ sendcmd(s, buffer, bufflen);
+ return getresponse(s);
+}
+
+int
+nast_add(nasth *s, const char *query)
+{
+ char buffer[512];
+ short bufflen;
+
+ if (s == NULL) {
+ fprintf(stderr, "ERROR: Can't add: no sphincter.\n");
+ return -1;
+ }
+
+ bufflen = sizeof(short);
+ bufflen += add_reqid(buffer+bufflen);
+
+ snprintf(buffer+bufflen, sizeof(buffer)-bufflen, "%c%c%s",
+ NASTCMD, NASTADD, query);
+ bufflen += (2 + strlen(query))*sizeof(char);
+
+ memcpy(buffer, &htons(bufflen), sizeof(short));
+ sendcmd(s, buffer, bufflen);
+ return getresponse(s);
+}
+
+int
+nast_del(nasth *s, const char *query)
+{
+ char buffer[512];
+ short bufflen;
+
+ if (s == NULL) {
+ fprintf(stderr, "ERROR: Can't delete: no sphincter.\n");
+ return -1;
+ }
+
+ bufflen = sizeof(short);
+ bufflen += add_reqid(buffer+bufflen);
+
+ snprintf(buffer+bufflen, sizeof(buffer)-bufflen, "%c%c%s",
+ NASTCMD, NASTDEL, query);
+ bufflen += (2 + strlen(query))*sizeof(char);
+
+ memcpy(buffer, &htons(bufflen), sizeof(short));
+ sendcmd(s, buffer, bufflen);
+ return getresponse(s);
+}
+
+int
+nast_get(nasth *s, const char *query)
+{
+ char buffer[512];
+ short bufflen;
+
+ if (s == NULL) {
+ fprintf(stderr, "ERROR: Can't get: no sphincter.\n");
+ return -1;
+ }
+
+ bufflen = sizeof(short);
+ bufflen += add_reqid(buffer+bufflen);
+
+ snprintf(buffer+bufflen, sizeof(buffer)-bufflen, "%c%c%s",
+ NASTCMD, NASTGET, query);
+ bufflen += (2 + strlen(query))*sizeof(char);
+
+ memcpy(buffer, &htons(bufflen), sizeof(short));
+ sendcmd(s, buffer, bufflen);
+ return getresponse(s);
+}
+
+void
+nast_die(nasth *s)
+{
+ char buffer[512];
+ short bufflen;
+
+ if (s == NULL) {
+ fprintf(stderr, "ERROR: Can't kill nast: no sphincter.\n");
+ return;
+ }
+
+ bufflen = sizeof(short);
+ bufflen += add_reqid(buffer+bufflen);
+
+ snprintf(buffer+bufflen, sizeof(buffer)-bufflen, "%c%c",
+ NASTCMD, NASTDIE);
+ bufflen += 2 * sizeof(char);
+
+ memcpy(buffer, &htons(bufflen), sizeof(short));
+ sendcmd(s, buffer, bufflen);
+ return;
+}
+
+int
+nast_upd(nasth *s, const char *key, nast_array *valarray)
+{
+ char buffer[512];
+ int i;
+ short bufflen;
+
+ if (s == NULL) {
+ fprintf(stderr, "ERROR: Can't update: no sphincter.\n");
+ return -1;
+ }
+
+ bufflen = sizeof(short);
+ bufflen += add_reqid(buffer+bufflen);
+
+ snprintf(buffer+bufflen, sizeof(buffer)-bufflen, "%c%c%s%c",
+ NASTCMD, NASTUPD, key, NASTSEP);
+ bufflen += (3 + strlen(key))*sizeof(char);
+
+ for (i = 0; i < valarray->nitems; i++) {
+ char *str;
+ short slen;
+
+ str = valarray->items[i]->strdata;
+ slen = valarray->items[i]->strlen;
+ if (bufflen + slen > sizeof(buffer)) {
+ nast_set_error(s, thread_id(), NAST_NOMEM);
+ return -1;
+ }
+
+ memcpy(buffer+bufflen, str, slen);
+ bufflen += slen;
+
+ if (i < valarray->nitems - 1) {
+ buffer[bufflen] = NASTSEP;
+ bufflen += sizeof(char);
+ }
+ }
+
+ memcpy(buffer, &htons(bufflen), sizeof(short));
+ sendcmd(s, buffer, bufflen);
+ return getresponse(s);
+}
+
+int
+nast_stats(nasth *s)
+{
+ char buffer[512];
+ short bufflen;
+
+ if (s == NULL) {
+ fprintf(stderr, "ERROR: Can't get stats: no sphincter.\n");
+ return -1;
+ }
+
+ bufflen = sizeof(short);
+ bufflen += add_reqid(buffer+bufflen);
+
+ snprintf(buffer+bufflen, sizeof(buffer)-bufflen, "%c%c",
+ NASTCMD, NASTSTATS);
+ bufflen += 2 * sizeof(char);
+ memcpy(buffer, &htons(bufflen), sizeof(short));
+ sendcmd(s, buffer, bufflen);
+ return getresponse(s);
+}
+
+errcodes
+nast_geterr(nasth *s)
+{
+ nast_response *ar;
+
+ if (s == NULL)
+ return NAST_SERVER_GONE;
+
+ ar = getmyresponse(s, thread_id());
+ if (ar == NULL)
+ return NAST_OK;
+
+ return ar->errcode;
+}
+
+char *
+nast_errmsg(nasth *s)
+{
+ nast_response *ar;
+ errcodes ec;
+
+ ec = nast_geterr(s);
+ if (ec == NAST_SERVER_ERR) {
+ nast_array *aa;
+
+ ar = getmyresponse(s, thread_id());
+ if (ar == NULL)
+ return nast_errmsgs[NAST_UNKNOWN_RESPONSE];
+
+ aa = build_result(s, ar->buffer, ar->bufflen);
+ if (aa == NULL || aa->nitems == 0)
+ return nast_errmsgs[NAST_UNKNOWN_RESPONSE];
+
+ if (ar->errmsg != NULL)
+ free(ar->errmsg);
+
+ ar->errmsg = malloc(aa->items[0]->strlen);
+ if (ar->errmsg == NULL)
+ return nast_errmsgs[NAST_UNKNOWN_RESPONSE];
+
+ memcpy(ar->errmsg, aa->items[0]->strdata, aa->items[0]->strlen);
+ nast_free_result(aa);
+
+ return ar->errmsg;
+ }
+
+ return nast_errmsgs[ec];
+}
diff --git a/client/.svn/text-base/thread.c.svn-base b/client/.svn/text-base/thread.c.svn-base
new file mode 100644
index 0000000..bdd613a
--- /dev/null
+++ b/client/.svn/text-base/thread.c.svn-base
@@ -0,0 +1,75 @@
+#include "conf.h"
+#include "thread.h"
+
+#include <unistd.h>
+
+#ifdef THREADSAFECLIENT
+#include <pthread.h>
+#endif
+
+RCSID("$Id: thread.c,v 1.6 2000/09/13 20:21:30 shmit Exp $");
+
+#ifdef THREADSAFECLIENT
+short
+thread_id()
+{
+ short i;
+
+ i = (pthread_self() & 0xff) | (getpid() << 8);
+ return i;
+}
+
+int
+_nast_mutex_new(_nast_mutex_t *lock)
+{
+ return pthread_mutex_init(lock, NULL);
+}
+
+void
+_nast_mutex_delete(_nast_mutex_t *lock)
+{
+ (void)pthread_mutex_destroy(lock);
+}
+
+int
+_nast_mutex_lock(_nast_mutex_t *lock)
+{
+ return pthread_mutex_lock(lock);
+}
+
+int
+_nast_mutex_unlock(_nast_mutex_t *lock)
+{
+ return pthread_mutex_unlock(lock);
+}
+#else /* THREADSAFECLIENT */
+short
+thread_id()
+{
+ return getpid();
+}
+
+int
+_nast_mutex_new(_nast_mutex_t *lock)
+{
+ return 0;
+}
+
+void
+_nast_mutex_delete(_nast_mutex_t *lock)
+{
+ return;
+}
+
+int
+_nast_mutex_lock(_nast_mutex_t *lock)
+{
+ return 0;
+}
+
+int
+_nast_mutex_unlock(_nast_mutex_t *lock)
+{
+ return 0;
+}
+#endif /* THREADSAFECLIENT */
diff --git a/client/.svn/text-base/thread.h.svn-base b/client/.svn/text-base/thread.h.svn-base
new file mode 100644
index 0000000..171fde1
--- /dev/null
+++ b/client/.svn/text-base/thread.h.svn-base
@@ -0,0 +1,19 @@
+/* $Id: thread.h,v 1.5 2000/09/13 20:21:30 shmit Exp $ */
+
+#ifndef THREAD_H
+# define THREAD_H
+
+#ifdef THREADSAFECLIENT
+#include <pthread.h>
+
+typedef pthread_mutex_t _nast_mutex_t;
+#else
+typedef int _nast_mutex_t;
+#endif
+
+short thread_id();
+int _nast_mutex_new(_nast_mutex_t *lock);
+void _nast_mutex_delete(_nast_mutex_t *lock);
+int _nast_mutex_lock(_nast_mutex_t *lock);
+int _nast_mutex_unlock(_nast_mutex_t *lock);
+#endif
diff --git a/client/Makefile b/client/Makefile
new file mode 100644
index 0000000..e92feed
--- /dev/null
+++ b/client/Makefile
@@ -0,0 +1,26 @@
+# $Id: Makefile,v 1.10 2001/01/19 02:54:37 shmit Exp $
+
+LIBOBJS= nastapi.o thread.o
+
+all-lib:
+ @rm -f *.o
+ @make "THREADFLAGS=-UTHREADSAFECLIENT" libnast.a
+ @rm -f *.o
+ @make "THREADFLAGS=-DTHREADSAFECLIENT" libnast_r.a
+
+libnast.a: ${LIBOBJS}
+ ar r $@ ${LIBOBJS}
+ ranlib $@
+
+libnast_r.a: ${LIBOBJS}
+ ar r $@ ${LIBOBJS}
+ ranlib $@
+
+#
+# Dependencies
+#
+nastapi.o: ../include/nastd.h ../include/nastipc.h thread.h
+thread.o: thread.h
+
+MKDIR= ../Makefiles
+include ${MKDIR}/build
diff --git a/client/nastapi.c b/client/nastapi.c
new file mode 100644
index 0000000..3e5a3b9
--- /dev/null
+++ b/client/nastapi.c
@@ -0,0 +1,885 @@
+#include "conf.h"
+#include "nastd.h"
+#include "nastipc.h"
+#include "thread.h"
+
+#include <errno.h>
+#include <thread.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/uio.h>
+#include <sys/un.h>
+#include <signal.h>
+#include <unistd.h>
+
+RCSID("$Id: nastapi.c,v 1.13 2001/10/29 11:17:11 shmit Exp $");
+
+char *nast_errmsgs[] = {
+ "No errors",
+ "The NASTD server has gone away",
+ "Couldn't allocate enough memory",
+ "Server response is unknown",
+ "Connection to server timed out",
+ "Unknown option returned",
+ NULL
+};
+
+static nast_string_t *
+nast_string_new(int slen, const char *data)
+{
+ nast_string_t *tmp;
+
+ tmp = malloc(sizeof(nast_string_t));
+ if (tmp == NULL)
+ return NULL;
+
+ tmp->strdata = malloc((slen+1) * sizeof(char *));
+ if (tmp == NULL)
+ return NULL;
+ memcpy(tmp->strdata, data, slen);
+ tmp->strdata[slen] = '\0';
+ tmp->strlen = slen;
+
+ return tmp;
+}
+
+static void
+nast_string_delete(nast_string_t *string)
+{
+ string->strlen = 0;
+ free(string->strdata);
+ string->strdata = NULL;
+ free(string);
+}
+
+nast_array *
+nast_array_new()
+{
+ nast_array *aa;
+
+ aa = malloc(sizeof(nast_array));
+ if (aa == NULL)
+ return NULL;
+
+ aa->nitems = 0;
+ aa->items = NULL;
+ return aa;
+}
+
+int
+nast_array_add(nast_array *array, short len, const char *data)
+{
+ const int GRANULARITY = 10;
+
+ if (array->nitems % GRANULARITY == 0) {
+ nast_string_t **tmp;
+ tmp = realloc(array->items, sizeof(char *) *
+ (GRANULARITY + array->nitems));
+ if (tmp == NULL)
+ return -1;
+ array->items = tmp;
+ }
+ array->nitems++;
+
+ array->items[array->nitems-1] = nast_string_new(len, data);
+ if (array->items[array->nitems-1] == NULL)
+ return -1;
+
+ return 0;
+}
+
+void
+nast_array_delete(nast_array *array)
+{
+ nast_free_result(array);
+}
+
+static nast_response *
+response_new()
+{
+ nast_response *tmp;
+
+ tmp = malloc(sizeof(nast_response));
+ if (tmp == NULL)
+ return NULL;
+
+ tmp->buffer = NULL;
+ tmp->bufflen = 0;
+ tmp->errcode = NAST_OK;
+ tmp->errmsg = NULL;
+ tmp->reqid = -1;
+
+ return tmp;
+}
+
+static nast_response *
+getmyresponse(nasth *s, unsigned short reqid)
+{
+ if (s->nthreads < reqid)
+ return NULL;
+
+ return s->responses[reqid-1];
+}
+
+static void
+nast_set_error(nasth *s, unsigned short reqid, errcodes code)
+{
+ nast_response *ar;
+
+ ar = getmyresponse(s, reqid);
+ if (ar == NULL)
+ return;
+
+ ar->errcode = code;
+}
+
+static int
+grow_responses(nasth *s, unsigned short maxitems)
+{
+ nast_response **tmp_resp;
+ int i;
+
+ if (s->nthreads >= maxitems)
+ return 0;
+
+ tmp_resp = realloc(s->responses, maxitems * sizeof(nast_response *));
+ if (tmp_resp == NULL)
+ return -1;
+
+ s->responses = tmp_resp;
+ for (i = s->nthreads; i < maxitems; i++) {
+ s->responses[i] = response_new();
+ if (s->responses[i] == NULL)
+ return -1;
+ }
+
+ s->nthreads = maxitems;
+ return 0;
+}
+
+static nast_array *
+build_result(nasth *s, const char *buff, short bufflen)
+{
+ nast_array *aa;
+ const char *s_p, *e_p;
+ short l;
+
+ aa = nast_array_new();
+ if (aa == NULL) {
+ nast_set_error(s, thread_id(), NAST_NOMEM);
+ return NULL;
+ }
+
+ /* Parse the buff into an array. */
+ l = 0;
+ aa->nitems = 0;
+ aa->items = NULL;
+ s_p = buff+l;
+ for (e_p = s_p; e_p <= buff+bufflen; e_p++) {
+ if (e_p == buff+bufflen || *e_p == NASTSEP) {
+ if (nast_array_add(aa, e_p - s_p, s_p) == -1) {
+ nast_set_error(s, thread_id(), NAST_NOMEM);
+ return NULL;
+ }
+ s_p = e_p + 1;
+ }
+ }
+
+ return aa;
+}
+
+static int
+addresponse(nasth *s, unsigned short reqid, char *buffer, short len)
+{
+ nast_response *ar;
+
+ switch (buffer[0]) {
+ case NASTOK:
+ nast_set_error(s, reqid, NAST_OK);
+ break;
+ case NASTERR:
+ nast_set_error(s, reqid, NAST_SERVER_ERR);
+ break;
+ default:
+ nast_set_error(s, reqid, NAST_UNKNOWN_RESPONSE);
+ }
+
+ ar = getmyresponse(s, reqid);
+ if (ar == NULL) {
+ /*
+ * Somehow we got a response for something we didn't
+ * request.
+ */
+ return -1;
+ }
+
+ ar->reqid = reqid;
+ ar->buffer = malloc(len-1);
+ if (ar->buffer == NULL) {
+ nast_set_error(s, reqid, NAST_NOMEM);
+ return -1;
+ }
+ memcpy(ar->buffer, buffer+1, len-1);
+ ar->bufflen = len-1;
+ return 0;
+}
+
+static void
+delresponse(nasth *s, unsigned short reqid)
+{
+ nast_response *ar;
+
+ ar = getmyresponse(s, reqid);
+ if (ar == NULL)
+ return;
+
+ ar->reqid = -1;
+ if (ar->buffer)
+ free(ar->buffer);
+ ar->bufflen = 0;
+}
+
+static int
+checkresponse(nasth *s, unsigned short reqid)
+{
+ nast_response *ar;
+
+ if (reqid > s->nthreads)
+ return -1;
+
+ ar = getmyresponse(s, reqid);
+ if (ar == NULL || (short)ar->reqid == -1)
+ return -1;
+
+ return 0;
+}
+
+static int
+sendcmd(nasth *s, const char *buff, short len)
+{
+ ssize_t wrote;
+
+ /* Make sure there's room in the response array. */
+ if (grow_responses(s, thread_id()) == -1) {
+ /*
+ * XXX: This is a fatal error.
+ * Find some better way to handle this.
+ */
+ exit(1);
+ }
+
+ /* If we're sending a command, make sure we get rid of old data. */
+ delresponse(s, thread_id());
+
+ /* Send the command. */
+ wrote = 0;
+ while (wrote < len) {
+ ssize_t rc;
+
+ rc = write(s->socket, buff + wrote, len - wrote);
+ if (rc == -1) {
+ if (errno == EINTR || errno == EAGAIN)
+ continue;
+ nast_set_error(s, thread_id(), NAST_SERVER_GONE);
+ return -1;
+ }
+ wrote += rc;
+ }
+
+ /* Check status. */
+ return 0;
+}
+
+static int
+getresponse(nasth *s)
+{
+ char buffer[1024];
+ nast_response *ar;
+ int ntries, rc;
+ unsigned short tid;
+ short nbytes;
+
+ ntries = 0;
+ tid = thread_id();
+reread:
+ _nast_mutex_lock(s->lock);
+
+ /*
+ * See if we already have the response from another thread which
+ * may have found it first.
+ */
+ rc = checkresponse(s, tid);
+ if (rc == 0) {
+ _nast_mutex_unlock(s->lock);
+ } else {
+ /* Not in the already read stuff, check the network. */
+ char *p;
+ short bufflen;
+ unsigned short reqid;
+ fd_set readfds;
+ struct timeval timeout;
+
+ FD_ZERO(&readfds);
+ FD_SET(s->socket, &readfds);
+ timeout.tv_sec = 0;
+ timeout.tv_usec = 10000;
+ switch(select(s->socket+1, &readfds, NULL, NULL, &timeout)) {
+ case -1:
+ _nast_mutex_unlock(s->lock);
+ nast_set_error(s, tid, NAST_SERVER_GONE);
+ return -1;
+ case 0:
+ /* Timeout expired. */
+ _nast_mutex_unlock(s->lock);
+ if (ntries++ < 50)
+ goto reread;
+ nast_set_error(s, tid, NAST_TIMEDOUT);
+ return -1;
+ }
+ nbytes = recv(s->socket, buffer, sizeof(buffer), 0);
+ if (nbytes == -1) {
+ _nast_mutex_unlock(s->lock);
+ nast_set_error(s, tid, NAST_SERVER_GONE);
+ return -1;
+ } else if (nbytes == 0) {
+ /* No response from server. That's bad. Terminate. */
+ _nast_mutex_unlock(s->lock);
+ nast_set_error(s, tid, NAST_SERVER_GONE);
+ return -1;
+ } else if (nbytes <
+ sizeof(bufflen) + sizeof(reqid) + sizeof(char)) {
+ /* Response is too short. */
+ _nast_mutex_unlock(s->lock);
+ nast_set_error(s, tid, NAST_UNKNOWN_RESPONSE);
+ return -1;
+ }
+
+ for (p = buffer; p < buffer+nbytes; p += bufflen) {
+ int l;
+
+ memcpy(&bufflen, p, sizeof(bufflen));
+ bufflen = ntohs(bufflen);
+ l = sizeof(bufflen);
+
+ /* Sanity check, just in case data gets munged. */
+ if (bufflen <= 0 || bufflen > nbytes) {
+ _nast_mutex_unlock(s->lock);
+ nast_set_error(s, tid, NAST_UNKNOWN_RESPONSE);
+ return -1;
+ }
+
+ memcpy(&reqid, p+l, sizeof(reqid));
+ reqid = ntohs(reqid);
+ l += sizeof(reqid);
+
+ /* Save this response on the response array. */
+ addresponse(s, reqid, p+l, bufflen-l);
+ }
+ /* Check the response array to see if we got our response. */
+ _nast_mutex_unlock(s->lock);
+ goto reread;
+ }
+
+ ar = getmyresponse(s, tid);
+ if (ar->errcode == NAST_OK)
+ return 0;
+ else
+ return -1;
+}
+
+nasth *
+nast_sphincter_new(const char *sock_path)
+{
+ nasth *tmp_h;
+ struct sockaddr_un sunix;
+ int rc;
+
+ tmp_h = malloc(sizeof(nasth));
+ if (tmp_h == NULL) {
+ fprintf(stderr,
+ "ERROR: Couldn't make space for sphincter: %s.\n",
+ strerror(errno));
+ return NULL;
+ }
+
+ tmp_h->nthreads = 0;
+ tmp_h->responses = NULL;
+
+ tmp_h->lock = malloc(sizeof(_nast_mutex_t));
+ if (tmp_h->lock == NULL) {
+ fprintf(stderr,
+ "ERROR: Couldn't create NAST lock: %s.\n",
+ strerror(errno));
+ nast_sphincter_close(tmp_h);
+ return NULL;
+ }
+ rc = _nast_mutex_new(tmp_h->lock);
+ if (rc) {
+ fprintf(stderr,
+ "ERROR: Couldn't initialise NAST lock: %s.\n",
+ strerror(rc));
+ nast_sphincter_close(tmp_h);
+ return NULL;
+ }
+
+ tmp_h->socket = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (tmp_h->socket == -1) {
+ fprintf(stderr,
+ "ERROR: Couldn't initialise socket: %s.\n",
+ strerror(errno));
+ nast_sphincter_close(tmp_h);
+ return NULL;
+ }
+
+ memset(&sunix, 0, sizeof(sunix));
+ if (sock_path == NULL)
+ snprintf(sunix.sun_path, sizeof(sunix.sun_path), NASTHOLE);
+ else
+ strncpy(sunix.sun_path, sock_path, sizeof(sunix.sun_path));
+ sunix.sun_family = AF_UNIX;
+
+ if (connect(tmp_h->socket, (struct sockaddr *)&sunix,
+ sizeof(sunix)) == -1) {
+ fprintf(stderr,
+ "ERROR: Couldn't connect to server: %s.\n",
+ strerror(errno));
+ nast_sphincter_close(tmp_h);
+ return NULL;
+ }
+ return tmp_h;
+}
+
+void
+nast_sphincter_close(nasth *s)
+{
+ int i;
+
+ if (s == NULL)
+ return;
+
+ s->nthreads = 0;
+
+ if (s->lock != NULL) {
+ _nast_mutex_delete(s->lock);
+ free(s->lock);
+ s->lock = NULL;
+ }
+
+ if (s->socket != -1) {
+ close(s->socket);
+ s->socket = -1;
+ }
+
+ if (s->responses != NULL) {
+ for (i = 0; i < s->nthreads; i++) {
+ if (s->responses[i]->buffer != NULL)
+ free(s->responses[i]->buffer);
+ free(s->responses[i]);
+ s->responses[i] = NULL;
+ }
+ free(s->responses);
+ s->responses = NULL;
+ }
+
+ free(s);
+}
+
+nast_array *
+nast_get_result(nasth *s)
+{
+ nast_response *ar;
+
+ ar = getmyresponse(s, thread_id());
+ if (ar == NULL)
+ return NULL;
+
+ return build_result(s, ar->buffer, ar->bufflen);
+}
+
+void
+nast_free_result(nast_array *aa)
+{
+ int i;
+
+ if (aa->items) {
+ for (i = 0; i < aa->nitems; i++)
+ nast_string_delete(aa->items[i]);
+ free(aa->items);
+ aa->items = NULL;
+ }
+ aa->nitems = 0;
+ free(aa);
+}
+
+static int
+add_reqid(char *buffer)
+{
+ unsigned short tid;
+
+ tid = thread_id();
+ memcpy(buffer, &htons(tid), sizeof(tid));
+
+ return sizeof(tid);
+}
+
+int
+nast_options_get(nasth *s, nast_options *opts)
+{
+ nast_array *aa;
+ char buffer[512];
+ short bufflen, i;
+
+ if (s == NULL) {
+ fprintf(stderr, "ERROR: Can't get options: no sphincter.\n");
+ return -1;
+ }
+
+ bufflen = sizeof(short);
+ bufflen += add_reqid(buffer+bufflen);
+
+ snprintf(buffer+bufflen, sizeof(buffer)-bufflen, "%c%c",
+ NASTCMD, NASTOPTGET);
+ bufflen += 2 * sizeof(char);
+ memcpy(buffer, &htons(bufflen), sizeof(short));
+ sendcmd(s, buffer, bufflen);
+ if (getresponse(s) == -1)
+ return -1;
+
+ /* Copy out results and free them. */
+ aa = nast_get_result(s);
+
+ if (aa->nitems != 1) {
+ nast_set_error(s, thread_id(), NAST_UNKNOWN_RESPONSE);
+ return -1;
+ }
+
+ if (sizeof(buffer) < aa->items[0]->strlen)
+ bufflen = sizeof(buffer);
+ else
+ bufflen = aa->items[0]->strlen;
+ memcpy(buffer, aa->items[0]->strdata, bufflen);
+ nast_free_result(aa);
+
+ /* Parse return into options. */
+ for (i = 0; i < bufflen; i+=2) {
+ switch (buffer[i]) {
+ case OPTQCACHE:
+ if (buffer[i+1] == OPTFALSE)
+ opts->use_qcache = NASTFALSE;
+ else
+ opts->use_qcache = NASTTRUE;
+ break;
+ case OPTLOCALDB:
+ if (buffer[i+1] == OPTFALSE)
+ opts->use_localdb = NASTFALSE;
+ else
+ opts->use_localdb = NASTTRUE;
+ break;
+ case OPTFALLASYNC:
+ if (buffer[i+1] == OPTFALSE)
+ opts->fallthrough_async = NASTFALSE;
+ else
+ opts->fallthrough_async = NASTTRUE;
+ break;
+ case OPTALWAYSFALL:
+ if (buffer[i+1] == OPTFALSE)
+ opts->always_fallthrough = NASTFALSE;
+ else
+ opts->always_fallthrough = NASTTRUE;
+ break;
+ case OPTFAILONCE:
+ if (buffer[i+1] == OPTFALSE)
+ opts->fail_once = NASTFALSE;
+ else
+ opts->fail_once = NASTTRUE;
+ break;
+ case OPTNOFALLTHROUGH:
+ if (buffer[i+1] == OPTFALSE)
+ opts->no_fallthrough = NASTFALSE;
+ else
+ opts->no_fallthrough = NASTTRUE;
+ break;
+ default:
+ nast_set_error(s, thread_id(), NAST_UNKNOWN_OPT);
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+int
+nast_options_set(nasth *s, nast_options *opts)
+{
+ char buffer[512];
+ short bufflen;
+
+ if (s == NULL) {
+ fprintf(stderr, "ERROR: Can't set options: no sphincter.\n");
+ return -1;
+ }
+
+ bufflen = sizeof(short);
+ bufflen += add_reqid(buffer+bufflen);
+
+ snprintf(buffer+bufflen, sizeof(buffer)-bufflen, "%c%c",
+ NASTCMD, NASTOPTSET);
+ bufflen += 2*sizeof(char);
+
+ buffer[bufflen] = OPTQCACHE;
+ if (opts->use_qcache)
+ buffer[bufflen+1] = OPTTRUE;
+ else
+ buffer[bufflen+1] = OPTFALSE;
+ bufflen += 2;
+
+ buffer[bufflen] = OPTLOCALDB;
+ if (opts->use_localdb)
+ buffer[bufflen+1] = OPTTRUE;
+ else
+ buffer[bufflen+1] = OPTFALSE;
+ bufflen += 2;
+
+ buffer[bufflen] = OPTFALLASYNC;
+ if (opts->fallthrough_async)
+ buffer[bufflen+1] = OPTTRUE;
+ else
+ buffer[bufflen+1] = OPTFALSE;
+ bufflen += 2;
+
+ buffer[bufflen] = OPTALWAYSFALL;
+ if (opts->always_fallthrough)
+ buffer[bufflen+1] = OPTTRUE;
+ else
+ buffer[bufflen+1] = OPTFALSE;
+ bufflen += 2;
+
+ buffer[bufflen] = OPTFAILONCE;
+ if (opts->fail_once)
+ buffer[bufflen+1] = OPTTRUE;
+ else
+ buffer[bufflen+1] = OPTFALSE;
+ bufflen += 2;
+
+ buffer[bufflen] = OPTNOFALLTHROUGH;
+ if (opts->no_fallthrough)
+ buffer[bufflen+1] = OPTTRUE;
+ else
+ buffer[bufflen+1] = OPTFALSE;
+ bufflen += 2;
+
+ memcpy(buffer, &htons(bufflen), sizeof(short));
+ sendcmd(s, buffer, bufflen);
+ return getresponse(s);
+}
+
+int
+nast_add(nasth *s, const char *query)
+{
+ char buffer[512];
+ short bufflen;
+
+ if (s == NULL) {
+ fprintf(stderr, "ERROR: Can't add: no sphincter.\n");
+ return -1;
+ }
+
+ bufflen = sizeof(short);
+ bufflen += add_reqid(buffer+bufflen);
+
+ snprintf(buffer+bufflen, sizeof(buffer)-bufflen, "%c%c%s",
+ NASTCMD, NASTADD, query);
+ bufflen += (2 + strlen(query))*sizeof(char);
+
+ memcpy(buffer, &htons(bufflen), sizeof(short));
+ sendcmd(s, buffer, bufflen);
+ return getresponse(s);
+}
+
+int
+nast_del(nasth *s, const char *query)
+{
+ char buffer[512];
+ short bufflen;
+
+ if (s == NULL) {
+ fprintf(stderr, "ERROR: Can't delete: no sphincter.\n");
+ return -1;
+ }
+
+ bufflen = sizeof(short);
+ bufflen += add_reqid(buffer+bufflen);
+
+ snprintf(buffer+bufflen, sizeof(buffer)-bufflen, "%c%c%s",
+ NASTCMD, NASTDEL, query);
+ bufflen += (2 + strlen(query))*sizeof(char);
+
+ memcpy(buffer, &htons(bufflen), sizeof(short));
+ sendcmd(s, buffer, bufflen);
+ return getresponse(s);
+}
+
+int
+nast_get(nasth *s, const char *query)
+{
+ char buffer[512];
+ short bufflen;
+
+ if (s == NULL) {
+ fprintf(stderr, "ERROR: Can't get: no sphincter.\n");
+ return -1;
+ }
+
+ bufflen = sizeof(short);
+ bufflen += add_reqid(buffer+bufflen);
+
+ snprintf(buffer+bufflen, sizeof(buffer)-bufflen, "%c%c%s",
+ NASTCMD, NASTGET, query);
+ bufflen += (2 + strlen(query))*sizeof(char);
+
+ memcpy(buffer, &htons(bufflen), sizeof(short));
+ sendcmd(s, buffer, bufflen);
+ return getresponse(s);
+}
+
+void
+nast_die(nasth *s)
+{
+ char buffer[512];
+ short bufflen;
+
+ if (s == NULL) {
+ fprintf(stderr, "ERROR: Can't kill nast: no sphincter.\n");
+ return;
+ }
+
+ bufflen = sizeof(short);
+ bufflen += add_reqid(buffer+bufflen);
+
+ snprintf(buffer+bufflen, sizeof(buffer)-bufflen, "%c%c",
+ NASTCMD, NASTDIE);
+ bufflen += 2 * sizeof(char);
+
+ memcpy(buffer, &htons(bufflen), sizeof(short));
+ sendcmd(s, buffer, bufflen);
+ return;
+}
+
+int
+nast_upd(nasth *s, const char *key, nast_array *valarray)
+{
+ char buffer[512];
+ int i;
+ short bufflen;
+
+ if (s == NULL) {
+ fprintf(stderr, "ERROR: Can't update: no sphincter.\n");
+ return -1;
+ }
+
+ bufflen = sizeof(short);
+ bufflen += add_reqid(buffer+bufflen);
+
+ snprintf(buffer+bufflen, sizeof(buffer)-bufflen, "%c%c%s%c",
+ NASTCMD, NASTUPD, key, NASTSEP);
+ bufflen += (3 + strlen(key))*sizeof(char);
+
+ for (i = 0; i < valarray->nitems; i++) {
+ char *str;
+ short slen;
+
+ str = valarray->items[i]->strdata;
+ slen = valarray->items[i]->strlen;
+ if (bufflen + slen > sizeof(buffer)) {
+ nast_set_error(s, thread_id(), NAST_NOMEM);
+ return -1;
+ }
+
+ memcpy(buffer+bufflen, str, slen);
+ bufflen += slen;
+
+ if (i < valarray->nitems - 1) {
+ buffer[bufflen] = NASTSEP;
+ bufflen += sizeof(char);
+ }
+ }
+
+ memcpy(buffer, &htons(bufflen), sizeof(short));
+ sendcmd(s, buffer, bufflen);
+ return getresponse(s);
+}
+
+int
+nast_stats(nasth *s)
+{
+ char buffer[512];
+ short bufflen;
+
+ if (s == NULL) {
+ fprintf(stderr, "ERROR: Can't get stats: no sphincter.\n");
+ return -1;
+ }
+
+ bufflen = sizeof(short);
+ bufflen += add_reqid(buffer+bufflen);
+
+ snprintf(buffer+bufflen, sizeof(buffer)-bufflen, "%c%c",
+ NASTCMD, NASTSTATS);
+ bufflen += 2 * sizeof(char);
+ memcpy(buffer, &htons(bufflen), sizeof(short));
+ sendcmd(s, buffer, bufflen);
+ return getresponse(s);
+}
+
+errcodes
+nast_geterr(nasth *s)
+{
+ nast_response *ar;
+
+ if (s == NULL)
+ return NAST_SERVER_GONE;
+
+ ar = getmyresponse(s, thread_id());
+ if (ar == NULL)
+ return NAST_OK;
+
+ return ar->errcode;
+}
+
+char *
+nast_errmsg(nasth *s)
+{
+ nast_response *ar;
+ errcodes ec;
+
+ ec = nast_geterr(s);
+ if (ec == NAST_SERVER_ERR) {
+ nast_array *aa;
+
+ ar = getmyresponse(s, thread_id());
+ if (ar == NULL)
+ return nast_errmsgs[NAST_UNKNOWN_RESPONSE];
+
+ aa = build_result(s, ar->buffer, ar->bufflen);
+ if (aa == NULL || aa->nitems == 0)
+ return nast_errmsgs[NAST_UNKNOWN_RESPONSE];
+
+ if (ar->errmsg != NULL)
+ free(ar->errmsg);
+
+ ar->errmsg = malloc(aa->items[0]->strlen);
+ if (ar->errmsg == NULL)
+ return nast_errmsgs[NAST_UNKNOWN_RESPONSE];
+
+ memcpy(ar->errmsg, aa->items[0]->strdata, aa->items[0]->strlen);
+ nast_free_result(aa);
+
+ return ar->errmsg;
+ }
+
+ return nast_errmsgs[ec];
+}
diff --git a/client/thread.c b/client/thread.c
new file mode 100644
index 0000000..bdd613a
--- /dev/null
+++ b/client/thread.c
@@ -0,0 +1,75 @@
+#include "conf.h"
+#include "thread.h"
+
+#include <unistd.h>
+
+#ifdef THREADSAFECLIENT
+#include <pthread.h>
+#endif
+
+RCSID("$Id: thread.c,v 1.6 2000/09/13 20:21:30 shmit Exp $");
+
+#ifdef THREADSAFECLIENT
+short
+thread_id()
+{
+ short i;
+
+ i = (pthread_self() & 0xff) | (getpid() << 8);
+ return i;
+}
+
+int
+_nast_mutex_new(_nast_mutex_t *lock)
+{
+ return pthread_mutex_init(lock, NULL);
+}
+
+void
+_nast_mutex_delete(_nast_mutex_t *lock)
+{
+ (void)pthread_mutex_destroy(lock);
+}
+
+int
+_nast_mutex_lock(_nast_mutex_t *lock)
+{
+ return pthread_mutex_lock(lock);
+}
+
+int
+_nast_mutex_unlock(_nast_mutex_t *lock)
+{
+ return pthread_mutex_unlock(lock);
+}
+#else /* THREADSAFECLIENT */
+short
+thread_id()
+{
+ return getpid();
+}
+
+int
+_nast_mutex_new(_nast_mutex_t *lock)
+{
+ return 0;
+}
+
+void
+_nast_mutex_delete(_nast_mutex_t *lock)
+{
+ return;
+}
+
+int
+_nast_mutex_lock(_nast_mutex_t *lock)
+{
+ return 0;
+}
+
+int
+_nast_mutex_unlock(_nast_mutex_t *lock)
+{
+ return 0;
+}
+#endif /* THREADSAFECLIENT */
diff --git a/client/thread.h b/client/thread.h
new file mode 100644
index 0000000..171fde1
--- /dev/null
+++ b/client/thread.h
@@ -0,0 +1,19 @@
+/* $Id: thread.h,v 1.5 2000/09/13 20:21:30 shmit Exp $ */
+
+#ifndef THREAD_H
+# define THREAD_H
+
+#ifdef THREADSAFECLIENT
+#include <pthread.h>
+
+typedef pthread_mutex_t _nast_mutex_t;
+#else
+typedef int _nast_mutex_t;
+#endif
+
+short thread_id();
+int _nast_mutex_new(_nast_mutex_t *lock);
+void _nast_mutex_delete(_nast_mutex_t *lock);
+int _nast_mutex_lock(_nast_mutex_t *lock);
+int _nast_mutex_unlock(_nast_mutex_t *lock);
+#endif