summaryrefslogtreecommitdiffstats
path: root/server
diff options
context:
space:
mode:
Diffstat (limited to 'server')
-rw-r--r--server/.cvsignore8
-rw-r--r--server/.pure0
-rw-r--r--server/.svn/README.txt2
-rw-r--r--server/.svn/empty-file0
-rw-r--r--server/.svn/entries265
-rw-r--r--server/.svn/format1
-rw-r--r--server/.svn/prop-base/.cvsignore.svn-base1
-rw-r--r--server/.svn/prop-base/.pure.svn-base1
-rw-r--r--server/.svn/prop-base/Makefile.svn-base1
-rw-r--r--server/.svn/prop-base/array.c.svn-base1
-rw-r--r--server/.svn/prop-base/array.h.svn-base1
-rw-r--r--server/.svn/prop-base/cdb.c.svn-base1
-rw-r--r--server/.svn/prop-base/cdb.h.svn-base1
-rw-r--r--server/.svn/prop-base/cdb_find.c.svn-base1
-rw-r--r--server/.svn/prop-base/cdb_hash.c.svn-base1
-rw-r--r--server/.svn/prop-base/cdb_unpack.c.svn-base1
-rw-r--r--server/.svn/prop-base/cdbpriv.h.svn-base1
-rw-r--r--server/.svn/prop-base/fqm.c.svn-base1
-rw-r--r--server/.svn/prop-base/fqm.h.svn-base1
-rw-r--r--server/.svn/prop-base/log.c.svn-base1
-rw-r--r--server/.svn/prop-base/log.h.svn-base1
-rw-r--r--server/.svn/prop-base/md5.c.svn-base1
-rw-r--r--server/.svn/prop-base/md5.h.svn-base1
-rw-r--r--server/.svn/prop-base/memdb.c.svn-base1
-rw-r--r--server/.svn/prop-base/memdb.h.svn-base1
-rw-r--r--server/.svn/prop-base/mysqldb.c.svn-base1
-rw-r--r--server/.svn/prop-base/mysqldb.h.svn-base1
-rw-r--r--server/.svn/prop-base/nastd.c.svn-base1
-rw-r--r--server/.svn/prop-base/nastdio.c.svn-base1
-rw-r--r--server/.svn/prop-base/nastdio.h.svn-base1
-rw-r--r--server/.svn/prop-base/periodic.c.svn-base1
-rw-r--r--server/.svn/prop-base/periodic.h.svn-base1
-rw-r--r--server/.svn/prop-base/thread.c.svn-base1
-rw-r--r--server/.svn/prop-base/thread.h.svn-base1
-rw-r--r--server/.svn/props/.cvsignore.svn-work1
-rw-r--r--server/.svn/props/.pure.svn-work1
-rw-r--r--server/.svn/props/Makefile.svn-work1
-rw-r--r--server/.svn/props/array.c.svn-work1
-rw-r--r--server/.svn/props/array.h.svn-work1
-rw-r--r--server/.svn/props/cdb.c.svn-work1
-rw-r--r--server/.svn/props/cdb.h.svn-work1
-rw-r--r--server/.svn/props/cdb_find.c.svn-work1
-rw-r--r--server/.svn/props/cdb_hash.c.svn-work1
-rw-r--r--server/.svn/props/cdb_unpack.c.svn-work1
-rw-r--r--server/.svn/props/cdbpriv.h.svn-work1
-rw-r--r--server/.svn/props/fqm.c.svn-work1
-rw-r--r--server/.svn/props/fqm.h.svn-work1
-rw-r--r--server/.svn/props/log.c.svn-work1
-rw-r--r--server/.svn/props/log.h.svn-work1
-rw-r--r--server/.svn/props/md5.c.svn-work1
-rw-r--r--server/.svn/props/md5.h.svn-work1
-rw-r--r--server/.svn/props/memdb.c.svn-work1
-rw-r--r--server/.svn/props/memdb.h.svn-work1
-rw-r--r--server/.svn/props/mysqldb.c.svn-work1
-rw-r--r--server/.svn/props/mysqldb.h.svn-work1
-rw-r--r--server/.svn/props/nastd.c.svn-work1
-rw-r--r--server/.svn/props/nastdio.c.svn-work1
-rw-r--r--server/.svn/props/nastdio.h.svn-work1
-rw-r--r--server/.svn/props/periodic.c.svn-work1
-rw-r--r--server/.svn/props/periodic.h.svn-work1
-rw-r--r--server/.svn/props/thread.c.svn-work1
-rw-r--r--server/.svn/props/thread.h.svn-work1
-rw-r--r--server/.svn/text-base/.cvsignore.svn-base8
-rw-r--r--server/.svn/text-base/.pure.svn-base0
-rw-r--r--server/.svn/text-base/Makefile.svn-base32
-rw-r--r--server/.svn/text-base/array.c.svn-base149
-rw-r--r--server/.svn/text-base/array.h.svn-base31
-rw-r--r--server/.svn/text-base/cdb.c.svn-base324
-rw-r--r--server/.svn/text-base/cdb.h.svn-base15
-rw-r--r--server/.svn/text-base/cdb_find.c.svn-base121
-rw-r--r--server/.svn/text-base/cdb_hash.c.svn-base18
-rw-r--r--server/.svn/text-base/cdb_unpack.c.svn-base16
-rw-r--r--server/.svn/text-base/cdbpriv.h.svn-base15
-rw-r--r--server/.svn/text-base/fqm.c.svn-base448
-rw-r--r--server/.svn/text-base/fqm.h.svn-base50
-rw-r--r--server/.svn/text-base/log.c.svn-base99
-rw-r--r--server/.svn/text-base/log.h.svn-base11
-rw-r--r--server/.svn/text-base/md5.c.svn-base308
-rw-r--r--server/.svn/text-base/md5.h.svn-base48
-rw-r--r--server/.svn/text-base/memdb.c.svn-base501
-rw-r--r--server/.svn/text-base/memdb.h.svn-base17
-rw-r--r--server/.svn/text-base/mysqldb.c.svn-base163
-rw-r--r--server/.svn/text-base/mysqldb.h.svn-base17
-rw-r--r--server/.svn/text-base/nastd.c.svn-base224
-rw-r--r--server/.svn/text-base/nastdio.c.svn-base701
-rw-r--r--server/.svn/text-base/nastdio.h.svn-base14
-rw-r--r--server/.svn/text-base/periodic.c.svn-base118
-rw-r--r--server/.svn/text-base/periodic.h.svn-base8
-rw-r--r--server/.svn/text-base/thread.c.svn-base216
-rw-r--r--server/.svn/text-base/thread.h.svn-base49
-rw-r--r--server/Makefile32
-rw-r--r--server/array.c149
-rw-r--r--server/array.h31
-rw-r--r--server/cdb.c324
-rw-r--r--server/cdb.h15
-rw-r--r--server/cdb_find.c121
-rw-r--r--server/cdb_hash.c18
-rw-r--r--server/cdb_unpack.c16
-rw-r--r--server/cdbpriv.h15
-rw-r--r--server/fqm.c448
-rw-r--r--server/fqm.h50
-rw-r--r--server/log.c99
-rw-r--r--server/log.h11
-rw-r--r--server/md5.c308
-rw-r--r--server/md5.h48
-rw-r--r--server/memdb.c501
-rw-r--r--server/memdb.h17
-rw-r--r--server/mysqldb.c163
-rw-r--r--server/mysqldb.h17
-rw-r--r--server/nastd.c224
-rw-r--r--server/nastdio.c701
-rw-r--r--server/nastdio.h14
-rw-r--r--server/periodic.c118
-rw-r--r--server/periodic.h8
-rw-r--r--server/thread.c216
-rw-r--r--server/thread.h49
116 files changed, 7766 insertions, 0 deletions
diff --git a/server/.cvsignore b/server/.cvsignore
new file mode 100644
index 0000000..c656c21
--- /dev/null
+++ b/server/.cvsignore
@@ -0,0 +1,8 @@
+nastd
+.pure
+*.o
+*.so
+*.a
+*.core
+*.gmon
+tags
diff --git a/server/.pure b/server/.pure
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/server/.pure
diff --git a/server/.svn/README.txt b/server/.svn/README.txt
new file mode 100644
index 0000000..271a8ce
--- /dev/null
+++ b/server/.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/server/.svn/empty-file b/server/.svn/empty-file
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/server/.svn/empty-file
diff --git a/server/.svn/entries b/server/.svn/entries
new file mode 100644
index 0000000..75a456a
--- /dev/null
+++ b/server/.svn/entries
@@ -0,0 +1,265 @@
+<?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/server"
+ last-author="bjc"
+ kind="dir"
+ uuid="2641c99b-6c07-0410-920d-927397d2d5d0"
+ revision="8"/>
+<entry
+ committed-rev="1"
+ name="periodic.c"
+ text-time="2005-12-24T00:00:52.000000Z"
+ committed-date="2005-12-08T17:32:07.572093Z"
+ checksum="9ab6f8fb29a2ab22ad6c371b670aaa78"
+ last-author="bjc"
+ kind="file"
+ prop-time="2005-12-24T00:00:49.000000Z"/>
+<entry
+ committed-rev="1"
+ name="cdbpriv.h"
+ text-time="2005-12-24T00:00:52.000000Z"
+ committed-date="2005-12-08T17:32:07.572093Z"
+ checksum="b04ca2fe0d69d678bbd954d5ca1046c6"
+ last-author="bjc"
+ kind="file"
+ prop-time="2005-12-24T00:00:49.000000Z"/>
+<entry
+ committed-rev="1"
+ name="nastdio.c"
+ text-time="2005-12-24T00:00:52.000000Z"
+ committed-date="2005-12-08T17:32:07.572093Z"
+ checksum="14fde9ce0366ccada683bc6bfad47a03"
+ last-author="bjc"
+ kind="file"
+ prop-time="2005-12-24T00:00:49.000000Z"/>
+<entry
+ committed-rev="1"
+ name="periodic.h"
+ text-time="2005-12-24T00:00:53.000000Z"
+ committed-date="2005-12-08T17:32:07.572093Z"
+ checksum="aa6705635a8d4265cbd6348823b538c2"
+ last-author="bjc"
+ kind="file"
+ prop-time="2005-12-24T00:00:49.000000Z"/>
+<entry
+ committed-rev="1"
+ name="nastdio.h"
+ text-time="2005-12-24T00:00:53.000000Z"
+ committed-date="2005-12-08T17:32:07.572093Z"
+ checksum="9df20501cc13a633b8c393c72649546d"
+ last-author="bjc"
+ kind="file"
+ prop-time="2005-12-24T00:00:49.000000Z"/>
+<entry
+ committed-rev="1"
+ name="thread.c"
+ text-time="2005-12-24T00:00:53.000000Z"
+ committed-date="2005-12-08T17:32:07.572093Z"
+ checksum="4cd24cd9f6877f80b7921efdfb03751c"
+ last-author="bjc"
+ kind="file"
+ prop-time="2005-12-24T00:00:50.000000Z"/>
+<entry
+ committed-rev="1"
+ name=".pure"
+ text-time="2005-12-24T00:00:53.000000Z"
+ committed-date="2005-12-08T17:32:07.572093Z"
+ checksum="d41d8cd98f00b204e9800998ecf8427e"
+ last-author="bjc"
+ kind="file"
+ prop-time="2005-12-24T00:00:50.000000Z"/>
+<entry
+ committed-rev="1"
+ name="nastd.c"
+ text-time="2005-12-24T00:00:53.000000Z"
+ committed-date="2005-12-08T17:32:07.572093Z"
+ checksum="d09727d31e0c616e3b50f138a98a7c6f"
+ last-author="bjc"
+ kind="file"
+ prop-time="2005-12-24T00:00:50.000000Z"/>
+<entry
+ committed-rev="1"
+ name="mysqldb.c"
+ text-time="2005-12-24T00:00:53.000000Z"
+ committed-date="2005-12-08T17:32:07.572093Z"
+ checksum="9c95871bbe8737d0ea68f0a4fac84629"
+ last-author="bjc"
+ kind="file"
+ prop-time="2005-12-24T00:00:50.000000Z"/>
+<entry
+ committed-rev="1"
+ name="thread.h"
+ text-time="2005-12-24T00:00:53.000000Z"
+ committed-date="2005-12-08T17:32:07.572093Z"
+ checksum="f50e611ab3f72f79ad5d952781376665"
+ last-author="bjc"
+ kind="file"
+ prop-time="2005-12-24T00:00:50.000000Z"/>
+<entry
+ committed-rev="1"
+ name="array.c"
+ text-time="2005-12-24T00:00:53.000000Z"
+ committed-date="2005-12-08T17:32:07.572093Z"
+ checksum="b517ceff3a424b967e6d38e8b325b6aa"
+ last-author="bjc"
+ kind="file"
+ prop-time="2005-12-24T00:00:50.000000Z"/>
+<entry
+ committed-rev="1"
+ name="mysqldb.h"
+ text-time="2005-12-24T00:00:53.000000Z"
+ committed-date="2005-12-08T17:32:07.572093Z"
+ checksum="f9765376cccfa9f80c82f7a7b5c747c6"
+ last-author="bjc"
+ kind="file"
+ prop-time="2005-12-24T00:00:50.000000Z"/>
+<entry
+ committed-rev="1"
+ name="log.c"
+ text-time="2005-12-24T00:00:53.000000Z"
+ committed-date="2005-12-08T17:32:07.572093Z"
+ checksum="ae49c6ca92752738c2a0f6dd81666817"
+ last-author="bjc"
+ kind="file"
+ prop-time="2005-12-24T00:00:50.000000Z"/>
+<entry
+ committed-rev="1"
+ name="array.h"
+ text-time="2005-12-24T00:00:54.000000Z"
+ committed-date="2005-12-08T17:32:07.572093Z"
+ checksum="9ad3ee2195e9917b98173825d0ddf53a"
+ last-author="bjc"
+ kind="file"
+ prop-time="2005-12-24T00:00:51.000000Z"/>
+<entry
+ committed-rev="1"
+ name="fqm.c"
+ text-time="2005-12-24T00:00:53.000000Z"
+ committed-date="2005-12-08T17:32:07.572093Z"
+ checksum="e5f212af7ef0ce67764a5017f22c43af"
+ last-author="bjc"
+ kind="file"
+ prop-time="2005-12-24T00:00:50.000000Z"/>
+<entry
+ committed-rev="1"
+ name="memdb.c"
+ text-time="2005-12-24T00:00:54.000000Z"
+ committed-date="2005-12-08T17:32:07.572093Z"
+ checksum="b82f28b318f365f9b9289328a21aef76"
+ last-author="bjc"
+ kind="file"
+ prop-time="2005-12-24T00:00:51.000000Z"/>
+<entry
+ committed-rev="1"
+ name="md5.c"
+ text-time="2005-12-24T00:00:54.000000Z"
+ committed-date="2005-12-08T17:32:07.572093Z"
+ checksum="8e1191d8f06f198585e0c025232a5411"
+ last-author="bjc"
+ kind="file"
+ prop-time="2005-12-24T00:00:51.000000Z"/>
+<entry
+ committed-rev="1"
+ name="log.h"
+ text-time="2005-12-24T00:00:54.000000Z"
+ committed-date="2005-12-08T17:32:07.572093Z"
+ checksum="1acee0ec3cf339b45df070838a7229c1"
+ last-author="bjc"
+ kind="file"
+ prop-time="2005-12-24T00:00:51.000000Z"/>
+<entry
+ committed-rev="1"
+ name="fqm.h"
+ text-time="2005-12-24T00:00:54.000000Z"
+ committed-date="2005-12-08T17:32:07.572093Z"
+ checksum="1b4fc62462465934914fcf7614cb37b1"
+ last-author="bjc"
+ kind="file"
+ prop-time="2005-12-24T00:00:51.000000Z"/>
+<entry
+ committed-rev="1"
+ name="cdb_find.c"
+ text-time="2005-12-24T00:00:54.000000Z"
+ committed-date="2005-12-08T17:32:07.572093Z"
+ checksum="8b7a134f62e6b2ca0823a26b9e055358"
+ last-author="bjc"
+ kind="file"
+ prop-time="2005-12-24T00:00:51.000000Z"/>
+<entry
+ committed-rev="1"
+ name="cdb.c"
+ text-time="2005-12-24T00:00:54.000000Z"
+ committed-date="2005-12-08T17:32:07.572093Z"
+ checksum="6ae6b86a75220cd0334cd23b8ffbf8fd"
+ last-author="bjc"
+ kind="file"
+ prop-time="2005-12-24T00:00:51.000000Z"/>
+<entry
+ committed-rev="1"
+ name="cdb_unpack.c"
+ text-time="2005-12-24T00:00:54.000000Z"
+ committed-date="2005-12-08T17:32:07.572093Z"
+ checksum="72a688b528b8ec040f755a6a24f48f23"
+ last-author="bjc"
+ kind="file"
+ prop-time="2005-12-24T00:00:51.000000Z"/>
+<entry
+ committed-rev="1"
+ name="memdb.h"
+ text-time="2005-12-24T00:00:54.000000Z"
+ committed-date="2005-12-08T17:32:07.572093Z"
+ checksum="6312c1468cf623288611a981e173ef5f"
+ last-author="bjc"
+ kind="file"
+ prop-time="2005-12-24T00:00:52.000000Z"/>
+<entry
+ committed-rev="1"
+ name="md5.h"
+ text-time="2005-12-24T00:00:54.000000Z"
+ committed-date="2005-12-08T17:32:07.572093Z"
+ checksum="c6837ec020c3d9e7c2792b2a114853fe"
+ last-author="bjc"
+ kind="file"
+ prop-time="2005-12-24T00:00:52.000000Z"/>
+<entry
+ committed-rev="1"
+ name="cdb_hash.c"
+ text-time="2005-12-24T00:00:55.000000Z"
+ committed-date="2005-12-08T17:32:07.572093Z"
+ checksum="2f63db3059a51e03db8d2b6502edaf0a"
+ last-author="bjc"
+ kind="file"
+ prop-time="2005-12-24T00:00:52.000000Z"/>
+<entry
+ committed-rev="1"
+ name="Makefile"
+ text-time="2005-12-24T00:00:55.000000Z"
+ committed-date="2005-12-08T17:32:07.572093Z"
+ checksum="fe3c112570d1215c4b5aafe76042f53c"
+ last-author="bjc"
+ kind="file"
+ prop-time="2005-12-24T00:00:52.000000Z"/>
+<entry
+ committed-rev="1"
+ name=".cvsignore"
+ text-time="2005-12-24T00:00:55.000000Z"
+ committed-date="2005-12-08T17:32:07.572093Z"
+ checksum="b3345144ac86fa19fdfc3f4845fded00"
+ last-author="bjc"
+ kind="file"
+ prop-time="2005-12-24T00:00:52.000000Z"/>
+<entry
+ committed-rev="1"
+ name="cdb.h"
+ text-time="2005-12-24T00:00:55.000000Z"
+ committed-date="2005-12-08T17:32:07.572093Z"
+ checksum="bea51b513470729ab998b007e2ea760f"
+ last-author="bjc"
+ kind="file"
+ prop-time="2005-12-24T00:00:52.000000Z"/>
+</wc-entries>
diff --git a/server/.svn/format b/server/.svn/format
new file mode 100644
index 0000000..b8626c4
--- /dev/null
+++ b/server/.svn/format
@@ -0,0 +1 @@
+4
diff --git a/server/.svn/prop-base/.cvsignore.svn-base b/server/.svn/prop-base/.cvsignore.svn-base
new file mode 100644
index 0000000..dce2c1d
--- /dev/null
+++ b/server/.svn/prop-base/.cvsignore.svn-base
@@ -0,0 +1 @@
+END
diff --git a/server/.svn/prop-base/.pure.svn-base b/server/.svn/prop-base/.pure.svn-base
new file mode 100644
index 0000000..dce2c1d
--- /dev/null
+++ b/server/.svn/prop-base/.pure.svn-base
@@ -0,0 +1 @@
+END
diff --git a/server/.svn/prop-base/Makefile.svn-base b/server/.svn/prop-base/Makefile.svn-base
new file mode 100644
index 0000000..dce2c1d
--- /dev/null
+++ b/server/.svn/prop-base/Makefile.svn-base
@@ -0,0 +1 @@
+END
diff --git a/server/.svn/prop-base/array.c.svn-base b/server/.svn/prop-base/array.c.svn-base
new file mode 100644
index 0000000..dce2c1d
--- /dev/null
+++ b/server/.svn/prop-base/array.c.svn-base
@@ -0,0 +1 @@
+END
diff --git a/server/.svn/prop-base/array.h.svn-base b/server/.svn/prop-base/array.h.svn-base
new file mode 100644
index 0000000..dce2c1d
--- /dev/null
+++ b/server/.svn/prop-base/array.h.svn-base
@@ -0,0 +1 @@
+END
diff --git a/server/.svn/prop-base/cdb.c.svn-base b/server/.svn/prop-base/cdb.c.svn-base
new file mode 100644
index 0000000..dce2c1d
--- /dev/null
+++ b/server/.svn/prop-base/cdb.c.svn-base
@@ -0,0 +1 @@
+END
diff --git a/server/.svn/prop-base/cdb.h.svn-base b/server/.svn/prop-base/cdb.h.svn-base
new file mode 100644
index 0000000..dce2c1d
--- /dev/null
+++ b/server/.svn/prop-base/cdb.h.svn-base
@@ -0,0 +1 @@
+END
diff --git a/server/.svn/prop-base/cdb_find.c.svn-base b/server/.svn/prop-base/cdb_find.c.svn-base
new file mode 100644
index 0000000..dce2c1d
--- /dev/null
+++ b/server/.svn/prop-base/cdb_find.c.svn-base
@@ -0,0 +1 @@
+END
diff --git a/server/.svn/prop-base/cdb_hash.c.svn-base b/server/.svn/prop-base/cdb_hash.c.svn-base
new file mode 100644
index 0000000..dce2c1d
--- /dev/null
+++ b/server/.svn/prop-base/cdb_hash.c.svn-base
@@ -0,0 +1 @@
+END
diff --git a/server/.svn/prop-base/cdb_unpack.c.svn-base b/server/.svn/prop-base/cdb_unpack.c.svn-base
new file mode 100644
index 0000000..dce2c1d
--- /dev/null
+++ b/server/.svn/prop-base/cdb_unpack.c.svn-base
@@ -0,0 +1 @@
+END
diff --git a/server/.svn/prop-base/cdbpriv.h.svn-base b/server/.svn/prop-base/cdbpriv.h.svn-base
new file mode 100644
index 0000000..dce2c1d
--- /dev/null
+++ b/server/.svn/prop-base/cdbpriv.h.svn-base
@@ -0,0 +1 @@
+END
diff --git a/server/.svn/prop-base/fqm.c.svn-base b/server/.svn/prop-base/fqm.c.svn-base
new file mode 100644
index 0000000..dce2c1d
--- /dev/null
+++ b/server/.svn/prop-base/fqm.c.svn-base
@@ -0,0 +1 @@
+END
diff --git a/server/.svn/prop-base/fqm.h.svn-base b/server/.svn/prop-base/fqm.h.svn-base
new file mode 100644
index 0000000..dce2c1d
--- /dev/null
+++ b/server/.svn/prop-base/fqm.h.svn-base
@@ -0,0 +1 @@
+END
diff --git a/server/.svn/prop-base/log.c.svn-base b/server/.svn/prop-base/log.c.svn-base
new file mode 100644
index 0000000..dce2c1d
--- /dev/null
+++ b/server/.svn/prop-base/log.c.svn-base
@@ -0,0 +1 @@
+END
diff --git a/server/.svn/prop-base/log.h.svn-base b/server/.svn/prop-base/log.h.svn-base
new file mode 100644
index 0000000..dce2c1d
--- /dev/null
+++ b/server/.svn/prop-base/log.h.svn-base
@@ -0,0 +1 @@
+END
diff --git a/server/.svn/prop-base/md5.c.svn-base b/server/.svn/prop-base/md5.c.svn-base
new file mode 100644
index 0000000..dce2c1d
--- /dev/null
+++ b/server/.svn/prop-base/md5.c.svn-base
@@ -0,0 +1 @@
+END
diff --git a/server/.svn/prop-base/md5.h.svn-base b/server/.svn/prop-base/md5.h.svn-base
new file mode 100644
index 0000000..dce2c1d
--- /dev/null
+++ b/server/.svn/prop-base/md5.h.svn-base
@@ -0,0 +1 @@
+END
diff --git a/server/.svn/prop-base/memdb.c.svn-base b/server/.svn/prop-base/memdb.c.svn-base
new file mode 100644
index 0000000..dce2c1d
--- /dev/null
+++ b/server/.svn/prop-base/memdb.c.svn-base
@@ -0,0 +1 @@
+END
diff --git a/server/.svn/prop-base/memdb.h.svn-base b/server/.svn/prop-base/memdb.h.svn-base
new file mode 100644
index 0000000..dce2c1d
--- /dev/null
+++ b/server/.svn/prop-base/memdb.h.svn-base
@@ -0,0 +1 @@
+END
diff --git a/server/.svn/prop-base/mysqldb.c.svn-base b/server/.svn/prop-base/mysqldb.c.svn-base
new file mode 100644
index 0000000..dce2c1d
--- /dev/null
+++ b/server/.svn/prop-base/mysqldb.c.svn-base
@@ -0,0 +1 @@
+END
diff --git a/server/.svn/prop-base/mysqldb.h.svn-base b/server/.svn/prop-base/mysqldb.h.svn-base
new file mode 100644
index 0000000..dce2c1d
--- /dev/null
+++ b/server/.svn/prop-base/mysqldb.h.svn-base
@@ -0,0 +1 @@
+END
diff --git a/server/.svn/prop-base/nastd.c.svn-base b/server/.svn/prop-base/nastd.c.svn-base
new file mode 100644
index 0000000..dce2c1d
--- /dev/null
+++ b/server/.svn/prop-base/nastd.c.svn-base
@@ -0,0 +1 @@
+END
diff --git a/server/.svn/prop-base/nastdio.c.svn-base b/server/.svn/prop-base/nastdio.c.svn-base
new file mode 100644
index 0000000..dce2c1d
--- /dev/null
+++ b/server/.svn/prop-base/nastdio.c.svn-base
@@ -0,0 +1 @@
+END
diff --git a/server/.svn/prop-base/nastdio.h.svn-base b/server/.svn/prop-base/nastdio.h.svn-base
new file mode 100644
index 0000000..dce2c1d
--- /dev/null
+++ b/server/.svn/prop-base/nastdio.h.svn-base
@@ -0,0 +1 @@
+END
diff --git a/server/.svn/prop-base/periodic.c.svn-base b/server/.svn/prop-base/periodic.c.svn-base
new file mode 100644
index 0000000..dce2c1d
--- /dev/null
+++ b/server/.svn/prop-base/periodic.c.svn-base
@@ -0,0 +1 @@
+END
diff --git a/server/.svn/prop-base/periodic.h.svn-base b/server/.svn/prop-base/periodic.h.svn-base
new file mode 100644
index 0000000..dce2c1d
--- /dev/null
+++ b/server/.svn/prop-base/periodic.h.svn-base
@@ -0,0 +1 @@
+END
diff --git a/server/.svn/prop-base/thread.c.svn-base b/server/.svn/prop-base/thread.c.svn-base
new file mode 100644
index 0000000..dce2c1d
--- /dev/null
+++ b/server/.svn/prop-base/thread.c.svn-base
@@ -0,0 +1 @@
+END
diff --git a/server/.svn/prop-base/thread.h.svn-base b/server/.svn/prop-base/thread.h.svn-base
new file mode 100644
index 0000000..dce2c1d
--- /dev/null
+++ b/server/.svn/prop-base/thread.h.svn-base
@@ -0,0 +1 @@
+END
diff --git a/server/.svn/props/.cvsignore.svn-work b/server/.svn/props/.cvsignore.svn-work
new file mode 100644
index 0000000..dce2c1d
--- /dev/null
+++ b/server/.svn/props/.cvsignore.svn-work
@@ -0,0 +1 @@
+END
diff --git a/server/.svn/props/.pure.svn-work b/server/.svn/props/.pure.svn-work
new file mode 100644
index 0000000..dce2c1d
--- /dev/null
+++ b/server/.svn/props/.pure.svn-work
@@ -0,0 +1 @@
+END
diff --git a/server/.svn/props/Makefile.svn-work b/server/.svn/props/Makefile.svn-work
new file mode 100644
index 0000000..dce2c1d
--- /dev/null
+++ b/server/.svn/props/Makefile.svn-work
@@ -0,0 +1 @@
+END
diff --git a/server/.svn/props/array.c.svn-work b/server/.svn/props/array.c.svn-work
new file mode 100644
index 0000000..dce2c1d
--- /dev/null
+++ b/server/.svn/props/array.c.svn-work
@@ -0,0 +1 @@
+END
diff --git a/server/.svn/props/array.h.svn-work b/server/.svn/props/array.h.svn-work
new file mode 100644
index 0000000..dce2c1d
--- /dev/null
+++ b/server/.svn/props/array.h.svn-work
@@ -0,0 +1 @@
+END
diff --git a/server/.svn/props/cdb.c.svn-work b/server/.svn/props/cdb.c.svn-work
new file mode 100644
index 0000000..dce2c1d
--- /dev/null
+++ b/server/.svn/props/cdb.c.svn-work
@@ -0,0 +1 @@
+END
diff --git a/server/.svn/props/cdb.h.svn-work b/server/.svn/props/cdb.h.svn-work
new file mode 100644
index 0000000..dce2c1d
--- /dev/null
+++ b/server/.svn/props/cdb.h.svn-work
@@ -0,0 +1 @@
+END
diff --git a/server/.svn/props/cdb_find.c.svn-work b/server/.svn/props/cdb_find.c.svn-work
new file mode 100644
index 0000000..dce2c1d
--- /dev/null
+++ b/server/.svn/props/cdb_find.c.svn-work
@@ -0,0 +1 @@
+END
diff --git a/server/.svn/props/cdb_hash.c.svn-work b/server/.svn/props/cdb_hash.c.svn-work
new file mode 100644
index 0000000..dce2c1d
--- /dev/null
+++ b/server/.svn/props/cdb_hash.c.svn-work
@@ -0,0 +1 @@
+END
diff --git a/server/.svn/props/cdb_unpack.c.svn-work b/server/.svn/props/cdb_unpack.c.svn-work
new file mode 100644
index 0000000..dce2c1d
--- /dev/null
+++ b/server/.svn/props/cdb_unpack.c.svn-work
@@ -0,0 +1 @@
+END
diff --git a/server/.svn/props/cdbpriv.h.svn-work b/server/.svn/props/cdbpriv.h.svn-work
new file mode 100644
index 0000000..dce2c1d
--- /dev/null
+++ b/server/.svn/props/cdbpriv.h.svn-work
@@ -0,0 +1 @@
+END
diff --git a/server/.svn/props/fqm.c.svn-work b/server/.svn/props/fqm.c.svn-work
new file mode 100644
index 0000000..dce2c1d
--- /dev/null
+++ b/server/.svn/props/fqm.c.svn-work
@@ -0,0 +1 @@
+END
diff --git a/server/.svn/props/fqm.h.svn-work b/server/.svn/props/fqm.h.svn-work
new file mode 100644
index 0000000..dce2c1d
--- /dev/null
+++ b/server/.svn/props/fqm.h.svn-work
@@ -0,0 +1 @@
+END
diff --git a/server/.svn/props/log.c.svn-work b/server/.svn/props/log.c.svn-work
new file mode 100644
index 0000000..dce2c1d
--- /dev/null
+++ b/server/.svn/props/log.c.svn-work
@@ -0,0 +1 @@
+END
diff --git a/server/.svn/props/log.h.svn-work b/server/.svn/props/log.h.svn-work
new file mode 100644
index 0000000..dce2c1d
--- /dev/null
+++ b/server/.svn/props/log.h.svn-work
@@ -0,0 +1 @@
+END
diff --git a/server/.svn/props/md5.c.svn-work b/server/.svn/props/md5.c.svn-work
new file mode 100644
index 0000000..dce2c1d
--- /dev/null
+++ b/server/.svn/props/md5.c.svn-work
@@ -0,0 +1 @@
+END
diff --git a/server/.svn/props/md5.h.svn-work b/server/.svn/props/md5.h.svn-work
new file mode 100644
index 0000000..dce2c1d
--- /dev/null
+++ b/server/.svn/props/md5.h.svn-work
@@ -0,0 +1 @@
+END
diff --git a/server/.svn/props/memdb.c.svn-work b/server/.svn/props/memdb.c.svn-work
new file mode 100644
index 0000000..dce2c1d
--- /dev/null
+++ b/server/.svn/props/memdb.c.svn-work
@@ -0,0 +1 @@
+END
diff --git a/server/.svn/props/memdb.h.svn-work b/server/.svn/props/memdb.h.svn-work
new file mode 100644
index 0000000..dce2c1d
--- /dev/null
+++ b/server/.svn/props/memdb.h.svn-work
@@ -0,0 +1 @@
+END
diff --git a/server/.svn/props/mysqldb.c.svn-work b/server/.svn/props/mysqldb.c.svn-work
new file mode 100644
index 0000000..dce2c1d
--- /dev/null
+++ b/server/.svn/props/mysqldb.c.svn-work
@@ -0,0 +1 @@
+END
diff --git a/server/.svn/props/mysqldb.h.svn-work b/server/.svn/props/mysqldb.h.svn-work
new file mode 100644
index 0000000..dce2c1d
--- /dev/null
+++ b/server/.svn/props/mysqldb.h.svn-work
@@ -0,0 +1 @@
+END
diff --git a/server/.svn/props/nastd.c.svn-work b/server/.svn/props/nastd.c.svn-work
new file mode 100644
index 0000000..dce2c1d
--- /dev/null
+++ b/server/.svn/props/nastd.c.svn-work
@@ -0,0 +1 @@
+END
diff --git a/server/.svn/props/nastdio.c.svn-work b/server/.svn/props/nastdio.c.svn-work
new file mode 100644
index 0000000..dce2c1d
--- /dev/null
+++ b/server/.svn/props/nastdio.c.svn-work
@@ -0,0 +1 @@
+END
diff --git a/server/.svn/props/nastdio.h.svn-work b/server/.svn/props/nastdio.h.svn-work
new file mode 100644
index 0000000..dce2c1d
--- /dev/null
+++ b/server/.svn/props/nastdio.h.svn-work
@@ -0,0 +1 @@
+END
diff --git a/server/.svn/props/periodic.c.svn-work b/server/.svn/props/periodic.c.svn-work
new file mode 100644
index 0000000..dce2c1d
--- /dev/null
+++ b/server/.svn/props/periodic.c.svn-work
@@ -0,0 +1 @@
+END
diff --git a/server/.svn/props/periodic.h.svn-work b/server/.svn/props/periodic.h.svn-work
new file mode 100644
index 0000000..dce2c1d
--- /dev/null
+++ b/server/.svn/props/periodic.h.svn-work
@@ -0,0 +1 @@
+END
diff --git a/server/.svn/props/thread.c.svn-work b/server/.svn/props/thread.c.svn-work
new file mode 100644
index 0000000..dce2c1d
--- /dev/null
+++ b/server/.svn/props/thread.c.svn-work
@@ -0,0 +1 @@
+END
diff --git a/server/.svn/props/thread.h.svn-work b/server/.svn/props/thread.h.svn-work
new file mode 100644
index 0000000..dce2c1d
--- /dev/null
+++ b/server/.svn/props/thread.h.svn-work
@@ -0,0 +1 @@
+END
diff --git a/server/.svn/text-base/.cvsignore.svn-base b/server/.svn/text-base/.cvsignore.svn-base
new file mode 100644
index 0000000..c656c21
--- /dev/null
+++ b/server/.svn/text-base/.cvsignore.svn-base
@@ -0,0 +1,8 @@
+nastd
+.pure
+*.o
+*.so
+*.a
+*.core
+*.gmon
+tags
diff --git a/server/.svn/text-base/.pure.svn-base b/server/.svn/text-base/.pure.svn-base
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/server/.svn/text-base/.pure.svn-base
diff --git a/server/.svn/text-base/Makefile.svn-base b/server/.svn/text-base/Makefile.svn-base
new file mode 100644
index 0000000..0d26efe
--- /dev/null
+++ b/server/.svn/text-base/Makefile.svn-base
@@ -0,0 +1,32 @@
+# $Id: Makefile,v 1.17 2001/10/04 23:57:09 shmit Exp $
+
+OBJS= array.o nastd.o nastdio.o cdb.o cdb_find.o cdb_hash.o \
+ cdb_unpack.o fqm.o log.o md5.o memdb.o mysqldb.o periodic.o \
+ thread.o ../libconfig/libconfig.a
+
+nastd: ${OBJS}
+ ${PURIFY} ${CC} -o $@ ${OBJS} ${LIBS} ${MYSQL_LIBS}
+
+#
+# Dependencies
+#
+array.o: array.h log.h
+nastd.o: ../include/nastipc.h ../include/config.h nastdio.h \
+ cdb.h log.h memdb.h mysqldb.h periodic.h
+nastdio.o: ../include/nastd.h ../include/nastipc.h array.h \
+ nastdio.h cdb.h fqm.h log.h memdb.h mysqldb.h thread.h
+cdb.o: ../include/config.h array.h nastdio.h cdbpriv.h log.h \
+ memdb.h thread.h
+cdb_find.o: cdbpriv.h
+cdb_hash.o: cdbpriv.h
+cdb_unpack.o: cdbpriv.h
+fqm.o: fqm.h log.h thread.h
+log.o: log.h
+md5.o: log.h md5.h
+memdb.o: ../include/config.h array.h log.h md5.h memdb.h thread.h
+mysqldb.o: array.h nastdio.h log.h mysqldb.h
+periodic.o: cdb.h log.h thread.h periodic.h
+thread.o: thread.h
+
+MKDIR= ../Makefiles
+include ${MKDIR}/build
diff --git a/server/.svn/text-base/array.c.svn-base b/server/.svn/text-base/array.c.svn-base
new file mode 100644
index 0000000..65766de
--- /dev/null
+++ b/server/.svn/text-base/array.c.svn-base
@@ -0,0 +1,149 @@
+#include "conf.h"
+#include "array.h"
+#include "log.h"
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+RCSID("$Id: array.c,v 1.10 2000/10/17 23:38:27 shmit Exp $");
+
+string_t *
+string_new(int slen, char *strdata)
+{
+ string_t *tmp;
+
+ tmp = malloc(sizeof(string_t));
+ if (tmp == NULL) {
+ log_err("Couldn't allocate data for string: %s.",
+ strerror(errno));
+ return NULL;
+ }
+
+ tmp->str = malloc(slen * sizeof(char *));
+ if (tmp->str == NULL) {
+ log_err("Couldn't allocate string data: %s.",
+ strerror(errno));
+ return NULL;
+ }
+ memcpy(tmp->str, strdata, slen);
+ tmp->strlen = slen;
+
+ return tmp;
+}
+
+void
+string_delete(string_t *string)
+{
+ if (string == NULL)
+ return;
+
+ if (string->str != NULL)
+ free(string->str);
+ string->strlen = 0;
+ string->str = NULL;
+ free(string);
+}
+
+array_t *
+array_new()
+{
+ array_t *tmp;
+
+ tmp = malloc(sizeof(array_t));
+ if (tmp == NULL)
+ return NULL;
+
+ tmp->nitems = 0;
+ tmp->items = NULL;
+ return tmp;
+}
+
+void
+array_delete(array_t *aa)
+{
+ int i;
+
+ if (aa == NULL)
+ return;
+
+ for (i = 0; i < aa->nitems; i++)
+ string_delete(aa->items[i]);
+ free(aa->items);
+ aa->items = NULL;
+ free(aa);
+}
+
+int
+va_array_add(array_t *aa, va_list ap)
+{
+ const int GRANULARITY = 10;
+ int slen;
+ char *s;
+
+ slen = va_arg(ap, int);
+ if (slen != ARRTERM)
+ s = va_arg(ap, char *);
+ else
+ s = NULL;
+ while (s) {
+ if (aa->nitems % GRANULARITY == 0) {
+ aa->items = realloc(aa->items, sizeof(string_t *) *
+ (GRANULARITY + aa->nitems));
+ if (aa->items == NULL)
+ return -1;
+ }
+ aa->nitems++;
+ aa->items[aa->nitems-1] = string_new(slen, s);
+ if (aa->items[aa->nitems-1] == NULL)
+ return -1;
+ slen = va_arg(ap, int);
+ if (slen != ARRTERM)
+ s = va_arg(ap, char *);
+ else
+ s = NULL;
+ }
+
+ return 0;
+}
+
+int
+array_add(array_t *aa, ...)
+{
+ va_list ap;
+ int rc;
+
+ va_start(ap, aa);
+ rc = va_array_add(aa, ap);
+ va_end(ap);
+
+ return rc;
+}
+
+int
+array_dup(array_t *dst, array_t *src)
+{
+ int i;
+
+ if (dst == NULL)
+ return -1;
+
+ dst->nitems = src->nitems;
+ dst->items = malloc(dst->nitems * sizeof(string_t *));
+ if (dst->items == NULL) {
+ log_err("Couldn't allocate dup array items list: %s.",
+ strerror(errno));
+ return -1;
+ }
+
+ for (i = 0; i < dst->nitems; i++) {
+ dst->items[i] = string_new(src->items[i]->strlen,
+ src->items[i]->str);
+ if (dst->items[i] == NULL) {
+ dst->nitems = i;
+ return -1;
+ }
+ }
+
+ return 0;
+}
diff --git a/server/.svn/text-base/array.h.svn-base b/server/.svn/text-base/array.h.svn-base
new file mode 100644
index 0000000..83b4ee1
--- /dev/null
+++ b/server/.svn/text-base/array.h.svn-base
@@ -0,0 +1,31 @@
+/* $Id: array.h,v 1.4 2000/10/17 23:38:27 shmit Exp $ */
+
+#ifndef ARRAY_H
+#define ARRAY_H
+
+#include <stdarg.h>
+
+#define ARRTERM -1
+
+struct _string_t {
+ char *str;
+ int strlen;
+};
+typedef struct _string_t string_t;
+
+struct _array_t {
+ int nitems;
+ string_t **items;
+};
+typedef struct _array_t array_t;
+
+string_t *string_new(int slen, char *strdata);
+void string_delete(string_t *string);
+
+array_t *array_new();
+void array_delete(array_t *array);
+int va_array_add(array_t *aa, va_list ap);
+int array_add(array_t *aa, ...);
+int array_dup(array_t *dst, array_t *src);
+
+#endif
diff --git a/server/.svn/text-base/cdb.c.svn-base b/server/.svn/text-base/cdb.c.svn-base
new file mode 100644
index 0000000..4f55d03
--- /dev/null
+++ b/server/.svn/text-base/cdb.c.svn-base
@@ -0,0 +1,324 @@
+#include "conf.h"
+#include "config.h"
+#include "array.h"
+#include "nastdio.h"
+#include "cdbpriv.h"
+#include "log.h"
+#include "memdb.h"
+#include "thread.h"
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <unistd.h>
+
+RCSID("$Id: cdb.c,v 1.64 2001/10/19 22:28:07 shmit Exp $");
+
+time_t cdb_mtime;
+
+static char *memcdb = NULL;
+static rw_mutex_t *cdb_lk = NULL;
+static uint32_t cdb_len = 0;
+
+/*
+ * Exportable symbols - the CDB file is authoritative for them, so they
+ * come from here, but nastdio.c is their consumer.
+ */
+char db_key[1024];
+char db_dbn[1024];
+char db_tbl[1024];
+fieldent *db_fields = NULL;
+char db_sep;
+short db_fieldcount = 0;
+
+static int sec_c;
+static float onemin_c, fivemin_c, fifteenmin_c;
+
+static int
+cdb_findbykey(char *mcdb, int len, const char *key, int keylen,
+ char **data, int *dlen)
+{
+ if (cdb_find(mcdb, len, key, keylen, data, dlen) != 1)
+ return -1;
+
+ return 0;
+}
+
+int
+cdb_new()
+{
+ char *newcdb;
+ char *p, *s_p, *e_p;
+ fieldent *newfields;
+ char buffer[1024];
+ char cdbfields_s[1024], newkey[1024], newtbl[1024], newdbn[1024];
+ char newsep;
+ struct stat sb;
+ int i, fieldslen, newfieldcount;
+ int fd;
+ size_t len;
+
+ log_info("Initialising CDB interface.");
+
+ sec_c = 0;
+ onemin_c = fivemin_c = fifteenmin_c = 0;
+
+ if (!cdb_lk) {
+ cdb_lk = malloc(sizeof(rw_mutex_t));
+ if (!cdb_lk) {
+ log_err("Couldn't allocate CDB mutex: %s.",
+ strerror(errno));
+ return -1;
+ }
+ if (rw_mutex_new(cdb_lk)) {
+ log_err("Couldn't initialse CDB mutex: %s.",
+ strerror(errno));
+ return -1;
+ }
+ }
+
+ snprintf(buffer, sizeof(buffer), "%s/%s",
+ config.nast_dir, config.nast_cdb_file);
+ fd = open(buffer, O_RDONLY);
+ if (fd == -1) {
+ log_err("Couldn't open %s: %s.", buffer, strerror(errno));
+ return -1;
+ }
+
+ if (fstat(fd, &sb) == -1) {
+ log_err("Couldn't stat %s: %s.", buffer, strerror(errno));
+ return -1;
+ }
+ len = sb.st_size;
+
+ newcdb = mmap(NULL, len, PROT_READ, MAP_SHARED, fd, 0);
+ if (newcdb == MAP_FAILED) {
+ log_err("Couldn't mmap %s: %s.", buffer, strerror(errno));
+ return -1;
+ }
+
+ (void)close(fd);
+
+ /* Get the key out of the CDB, so clients know how to search. */
+ if (cdb_findbykey(newcdb, len, "_KEY_", strlen("_KEY_"),
+ &p, &i) == -1) {
+ log_err("Couldn't find `_KEY_' in the CDB.");
+ munmap(newcdb, len);
+ return -1;
+ }
+
+ if (i > sizeof(newkey)) {
+ log_err("Couldn't copy key information from the CDB.");
+ munmap(newcdb, len);
+ return -1;
+ }
+ memcpy(newkey, p, i);
+ newkey[i] = '\0';
+
+ /* Get the dbname out of the CDB, so mysql knows which db to use. */
+ if (cdb_findbykey(newcdb, len, "_DB_", strlen("_DB_"), &p, &i) == -1) {
+ log_err("Warning: Couldn't find `_DB_' in the CDB.");
+ strncpy(newdbn, DBNAME, sizeof(newdbn));
+ } else {
+ if (i > sizeof(newdbn)) {
+ log_err("Couldn't copy database information "
+ "from the CDB.");
+ munmap(newcdb, len);
+ return -1;
+ }
+ memcpy(newdbn, p, i);
+ newdbn[i] = '\0';
+ }
+
+ /* Get the table out of the CDB, so mysql knows which to use. */
+ if (cdb_findbykey(newcdb, len, "_TABLE_", strlen("_TABLE_"),
+ &p, &i) == -1) {
+ log_err("Warning: Couldn't find `_TABLE_' in the CDB.");
+ strncpy(newtbl, DBTBL, sizeof(newtbl));
+ } else {
+ if (i > sizeof(newtbl)) {
+ log_err("Couldn't copy table information "
+ "from the CDB.");
+ munmap(newcdb, len);
+ return -1;
+ }
+ memcpy(newtbl, p, i);
+ newtbl[i] = '\0';
+ }
+
+ /* Get the delimiter out of the CDB file. */
+ if (cdb_findbykey(newcdb, len, "_DELIM_", strlen("_DELIM_"),
+ &p, &i) == -1) {
+ log_info("Couldn't find `_DELIM_' in the CDB. Using default.");
+ newsep = ':';
+ } else
+ newsep = *p;
+
+ /* Now get the column names. */
+ if (cdb_findbykey(newcdb, len, "_VALUES_", strlen("_VALUES_"),
+ &p, &i) == -1) {
+ log_err("Couldn't find `_VALUES_' in the CDB.");
+ munmap(newcdb, len);
+ return -1;
+ }
+ if (i >= sizeof(cdbfields_s)) {
+ log_err("Couldn't copy value information from the CDB.");
+ munmap(newcdb, len);
+ return -1;
+ }
+ memcpy(cdbfields_s, p, i);
+ cdbfields_s[i] = '\0';
+ fieldslen = strlen(cdbfields_s);
+
+ /* Fill out cdbfields with an array of field names. */
+ newfieldcount = 0;
+ for (i = 0; i <= fieldslen; i++) {
+ if (cdbfields_s[i] == newsep || i == fieldslen)
+ newfieldcount++;
+ }
+
+ newfields = malloc(sizeof(fieldent) * newfieldcount);
+ if (newfields == NULL) {
+ log_err("Couldn't allocate space for field array: %s.",
+ strerror(errno));
+ munmap(newcdb, len);
+ return -1;
+ }
+
+ s_p = cdbfields_s;
+ for (i = 0; i < newfieldcount; i++) {
+ for (e_p = s_p; *e_p != newsep && *e_p != '\0'; e_p++);
+
+ newfields[i].index = i;
+ newfields[i].name = malloc(e_p - s_p + 1*sizeof(char));
+ if (newfields[i].name == NULL) {
+ /* XXX: clean up. */
+ log_err("Couldn't allocate space for field: %s.",
+ strerror(errno));
+ munmap(newcdb, len);
+ return -1;
+ }
+ memcpy(newfields[i].name, s_p, e_p - s_p);
+ newfields[i].name[e_p - s_p] = '\0';
+ s_p = e_p + 1;
+ }
+
+ rw_mutex_write_lock(cdb_lk);
+ if (memcdb)
+ munmap(memcdb, cdb_len);
+ memcdb = newcdb;
+ cdb_len = (uint32_t)len;
+ cdb_mtime = sb.st_mtime;
+
+ memcpy(db_dbn, newdbn, sizeof(db_dbn));
+ memcpy(db_tbl, newtbl, sizeof(db_tbl));
+ memcpy(db_key, newkey, sizeof(db_key));
+ if (db_fields)
+ free(db_fields);
+ db_fields = newfields;
+ db_sep = newsep;
+ db_fieldcount = newfieldcount;
+ rw_mutex_unlock(cdb_lk);
+
+ log_info("CDB interface initialised.");
+ return 0;
+}
+
+int
+cdb_get(const char *key, int keylen, array_t *aa)
+{
+ char *s_p, *e_p;
+ char *data;
+ int dlen;
+
+ rw_mutex_read_lock(cdb_lk);
+ if (cdb_findbykey(memcdb, cdb_len, key, keylen, &data, &dlen) == -1) {
+ rw_mutex_unlock(cdb_lk);
+ return -1;
+ }
+
+ sec_c++;
+
+ s_p = data; e_p = data;
+ while (e_p <= data+dlen) {
+ if (*e_p == db_sep || e_p == data+dlen) {
+ if (array_add(aa, e_p-s_p, s_p, ARRTERM) == -1) {
+ rw_mutex_unlock(cdb_lk);
+ return -1;
+ }
+ s_p = e_p + 1;
+ }
+ e_p++;
+ }
+
+ rw_mutex_unlock(cdb_lk);
+ return 0;
+}
+
+void
+cdb_periodic()
+{
+ char buffer[1024];
+ struct stat sb;
+
+ snprintf(buffer, sizeof(buffer), "%s/%s",
+ config.nast_dir, config.nast_cdb_file);
+ if (stat(buffer, &sb) == -1) {
+ log_err("PERIODIC: Couldn't stat %s: %s.\n", buffer,
+ strerror(errno));
+ return;
+ }
+
+ if (sb.st_size == 0) {
+ log_err("PERIODIC: WARNING! CDB file is empty!");
+ return;
+ }
+
+ if (cdb_mtime < sb.st_mtime) {
+ log_info("PERIODIC: CDB file changed, reloading.\n");
+ (void)cdb_new();
+
+ /*
+ * Turned off, as now entries are time stamped and
+ * auto-expire when the cdb file is updated.
+ */
+ /* (void)memdb_new(); */
+ return;
+ }
+}
+
+int
+cdb_stats(array_t *statarr)
+{
+ char buffer[512];
+ char tbuff[512];
+ struct tm res;
+
+ snprintf(buffer, sizeof(buffer), "CDB: %.2f, %.2f, %.2f",
+ onemin_c, fivemin_c, fifteenmin_c);
+ if (array_add(statarr, strlen(buffer), buffer, ARRTERM) == -1)
+ return -1;
+
+ /* Convert CDB mtime into a string. */
+ (void)localtime_r(&cdb_mtime, &res);
+ strftime(tbuff, sizeof(tbuff), "%Y%m%d %H:%M:%S", &res);
+
+ snprintf(buffer, sizeof(buffer), "CDB: last updated: %s",
+ tbuff);
+ return array_add(statarr, strlen(buffer), buffer, ARRTERM);
+}
+
+void
+cdb_collate()
+{
+ onemin_c = ((onemin_c * 59) + sec_c) / 60;
+ fivemin_c = ((fivemin_c * 299) + sec_c) / 300;
+ fifteenmin_c = ((fifteenmin_c * 899) + sec_c) / 900;
+ sec_c = 0;
+}
diff --git a/server/.svn/text-base/cdb.h.svn-base b/server/.svn/text-base/cdb.h.svn-base
new file mode 100644
index 0000000..0da14df
--- /dev/null
+++ b/server/.svn/text-base/cdb.h.svn-base
@@ -0,0 +1,15 @@
+/* $Id: cdb.h,v 1.11 2000/05/17 19:32:58 shmit Exp $ */
+
+#ifndef CDB_H
+#define CDB_H
+
+#include "array.h"
+
+int cdb_new();
+int cdb_get(const char *key, int keylen, array_t *dstarr);
+void cdb_periodic();
+
+int cdb_stats(array_t *statarr);
+void cdb_collate();
+
+#endif
diff --git a/server/.svn/text-base/cdb_find.c.svn-base b/server/.svn/text-base/cdb_find.c.svn-base
new file mode 100644
index 0000000..2fc2bcf
--- /dev/null
+++ b/server/.svn/text-base/cdb_find.c.svn-base
@@ -0,0 +1,121 @@
+#include "cdbpriv.h"
+
+#include <errno.h>
+#include <stdio.h>
+#include <sys/types.h>
+
+#ifndef SEEK_SET
+#define SEEK_SET 0
+#endif
+
+RCSID("$Id: cdb_find.c,v 1.2 2000/02/29 19:31:33 shmit Exp $");
+
+static int
+cdb_bread(char **ptr, char *endptr, char *buf, unsigned int len)
+{
+ if ((*ptr)+len > endptr) {
+ errno = EIO;
+ return -1;
+ }
+
+ memcpy(buf, *ptr, len);
+ *ptr += len;
+ return 0;
+}
+
+static int
+match(char **ptr, char *endptr, const char *key, unsigned int len)
+{
+ char buf[32];
+ int n;
+ int i;
+
+ n = sizeof(buf);
+ if (n > len)
+ n = len;
+
+ while (len > 0) {
+ if (cdb_bread(ptr, endptr, buf, n) == -1)
+ return -1;
+
+ for (i = 0; i < n; ++i)
+ if (buf[i] != key[i])
+ return 0;
+ key += n;
+ len -= n;
+ }
+
+ return 1;
+}
+
+int
+cdb_find(char *buff, off_t bufflen, const char *key, int len,
+ char **ret, uint32_t *retlen)
+{
+ char *cur, *end;
+ char packbuf[8];
+ uint32_t pos;
+ uint32_t h;
+ uint32_t lenhash;
+ uint32_t h2;
+ uint32_t loop;
+ uint32_t poskd;
+
+ cur = buff;
+ end = buff + bufflen;
+
+ h = cdb_hash(key, len);
+
+ pos = 8 * (h & 255);
+ cur += pos;
+ if (cur > end) {
+ errno = EIO;
+ return -1;
+ }
+
+ if (cdb_bread(&cur, end, packbuf, 8) == -1)
+ return -1;
+
+ pos = cdb_unpack(packbuf);
+ lenhash = cdb_unpack(packbuf + 4);
+
+ if (!lenhash) return 0;
+ h2 = (h >> 8) % lenhash;
+
+ for (loop = 0; loop < lenhash; ++loop) {
+ cur = buff + (pos + 8 * h2);
+ if (cur > end) {
+ errno = EIO;
+ return -1;
+ }
+ if (cdb_bread(&cur, end, packbuf, 8) == -1)
+ return -1;
+ poskd = cdb_unpack(packbuf + 4);
+ if (!poskd)
+ return 0;
+
+ if (cdb_unpack(packbuf) == h) {
+ cur = buff + poskd;
+ if (cur > end) {
+ errno = EIO;
+ return -1;
+ }
+ if (cdb_bread(&cur, end, packbuf, 8) == -1)
+ return -1;
+ if (cdb_unpack(packbuf) == len) {
+ switch(match(&cur, end, key, len)) {
+ case -1:
+ return -1;
+ case 1:
+ *retlen = cdb_unpack(&packbuf[4]);
+ *ret = cur;
+ return 1;
+ }
+ }
+ }
+ if (++h2 == lenhash)
+ h2 = 0;
+ }
+
+ return 0;
+}
diff --git a/server/.svn/text-base/cdb_hash.c.svn-base b/server/.svn/text-base/cdb_hash.c.svn-base
new file mode 100644
index 0000000..b664ba9
--- /dev/null
+++ b/server/.svn/text-base/cdb_hash.c.svn-base
@@ -0,0 +1,18 @@
+#include "conf.h"
+#include "cdbpriv.h"
+
+RCSID("$Id: cdb_hash.c,v 1.2 2000/02/29 19:31:33 shmit Exp $");
+
+uint32_t
+cdb_hash(const unsigned char *buf, unsigned int len)
+{
+ uint32_t h;
+
+ h = 5381;
+ while (len) {
+ --len;
+ h += (h << 5);
+ h ^= (uint32_t)*buf++;
+ }
+ return h;
+}
diff --git a/server/.svn/text-base/cdb_unpack.c.svn-base b/server/.svn/text-base/cdb_unpack.c.svn-base
new file mode 100644
index 0000000..14a71b9
--- /dev/null
+++ b/server/.svn/text-base/cdb_unpack.c.svn-base
@@ -0,0 +1,16 @@
+#include "conf.h"
+#include "cdbpriv.h"
+
+RCSID("$Id: cdb_unpack.c,v 1.2 2000/02/29 19:31:33 shmit Exp $");
+
+uint32_t
+cdb_unpack(unsigned char *buf)
+{
+ uint32_t num;
+
+ num = buf[3]; num <<= 8;
+ num += buf[2]; num <<= 8;
+ num += buf[1]; num <<= 8;
+ num += buf[0];
+ return num;
+}
diff --git a/server/.svn/text-base/cdbpriv.h.svn-base b/server/.svn/text-base/cdbpriv.h.svn-base
new file mode 100644
index 0000000..cc398a8
--- /dev/null
+++ b/server/.svn/text-base/cdbpriv.h.svn-base
@@ -0,0 +1,15 @@
+/* $Id: cdbpriv.h,v 1.2 2000/02/29 19:15:28 shmit Exp $ */
+
+#ifndef CDBPRIV_H
+#define CDBPRIV_H
+
+#include "conf.h"
+
+#include <sys/types.h>
+
+int cdb_find(char *buff, off_t bufflen, const char *key, int len,
+ char **ret, uint32_t *retlen);
+uint32_t cdb_hash(const unsigned char *buff, unsigned int len);
+uint32_t cdb_unpack(unsigned char *buff);
+
+#endif
diff --git a/server/.svn/text-base/fqm.c.svn-base b/server/.svn/text-base/fqm.c.svn-base
new file mode 100644
index 0000000..73c9e54
--- /dev/null
+++ b/server/.svn/text-base/fqm.c.svn-base
@@ -0,0 +1,448 @@
+#include "conf.h"
+#include "fqm.h"
+#include "log.h"
+#include "mysqldb.h"
+#include "thread.h"
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+RCSID("$Id: fqm.c,v 1.33 2001/10/29 11:17:13 shmit Exp $");
+
+/* Global Variables */
+fqm_t *fqm;
+
+request_t *
+req_new(int sock, short reqid, REQHANDLER((*req_handler)),
+ const char *reqstr, int reqlen)
+{
+ request_t *tmp;
+
+ tmp = malloc(sizeof(request_t));
+ if (tmp == NULL) {
+ log_err("Couldn't allocate request: %s.", strerror(errno));
+ return NULL;
+ }
+
+ tmp->req = malloc((reqlen + 1) * sizeof(char));
+ if (tmp->req == NULL) {
+ log_err("Couldn't allocate request string: %s.",
+ strerror(errno));
+ req_delete(tmp);
+ return NULL;
+ }
+ memcpy(tmp->req, reqstr, reqlen);
+ tmp->req[reqlen] = '\0';
+ tmp->sock = sock;
+ tmp->reqid = reqid;
+ tmp->req_handler = req_handler;
+
+ return tmp;
+}
+
+void
+req_delete(request_t *req)
+{
+ if (req) {
+ if (req->req)
+ free(req->req);
+ free(req);
+ }
+}
+
+static request_t *
+fqm_pop(reqthread_t *req)
+{
+ fqm_t *fqm;
+ request_t *p;
+
+ if (req == NULL || req->fqm == NULL) {
+ mutex_unlock(&req->fqm->q_cond->lock);
+ return NULL;
+ }
+ fqm = req->fqm;
+
+ /* Wait for something to arrive on the queue. */
+ mutex_lock(&req->fqm->q_cond->lock);
+ req->status = IDLE;
+
+ if (fqm->sreq == NULL)
+ cond_wait(fqm->q_cond);
+
+ /* Something waiting on the queue. Scarf it off and unlock. */
+ p = fqm->sreq;
+ if (p == NULL) {
+ mutex_unlock(&fqm->q_cond->lock);
+ return NULL;
+ }
+ fqm->sreq = fqm->sreq->next;
+ fqm->nitems--;
+ req->status = BUSY;
+ mutex_unlock(&fqm->q_cond->lock);
+
+ p->next = NULL;
+ return p;
+}
+
+static void *
+fqm_popper(void *arg)
+{
+ reqthread_t *self;
+ request_t *req;
+
+ /* Detach this thread. */
+ (void)pthread_detach(pthread_self());
+
+ self = (reqthread_t *)arg;
+ for (;;) {
+ req = fqm_pop(self);
+
+ /*
+ * Check to see if we were told to die. If we were,
+ * do so.
+ * Note: there's the possibility for the dropping of
+ * a request here. Que sera sera.
+ */
+ if (self->dieflag)
+ break;
+ if (req == NULL)
+ continue;
+ if (req->req_handler(req, self) == -1)
+ log_info("Couldn't handle request");
+ req_delete(req);
+ }
+
+ log_info("Got a kill flag; dying off.");
+ mutex_lock(&self->fqm->t_cond->lock);
+ self->status = NOALLOC;
+ cond_signal(self->fqm->t_cond);
+ mutex_unlock(&self->fqm->t_cond->lock);
+ return NULL;
+}
+
+static reqthread_t *
+fqm_thr_new(fqm_t *fqm)
+{
+ reqthread_t *tmp;
+
+ tmp = malloc(sizeof(reqthread_t));
+ if (tmp == NULL) {
+ log_err("Couldn't allocate fallthrough thread: %s.",
+ strerror(errno));
+ return NULL;
+ }
+
+ tmp->arg = NULL;
+ tmp->fqm = fqm;
+ tmp->dieflag = 0;
+ tmp->status = NOALLOC;
+
+ tmp->tid = malloc(sizeof(thread_t));
+ if (tmp->tid == NULL) {
+ log_err("Couldn't allocate fallthrough thread: %s.",
+ strerror(errno));
+ free(tmp);
+ return NULL;
+ }
+
+ if (thread_new(tmp->tid, fqm_popper, tmp)) {
+ log_err("Couldn't start fallthrough thread: %s.",
+ strerror(errno));
+ free(tmp->tid);
+ tmp->tid = NULL;
+ free(tmp);
+ return NULL;
+ }
+ tmp->status = IDLE;
+
+ return tmp;
+}
+
+static int
+fqm_thr_wait(fqm_t *fqm)
+{
+ int deadthreads;
+ int i;
+
+ deadthreads = 0;
+
+ for (i = 0; i < fqm->maxitems; i++) {
+ if (fqm->tids[i] && fqm->tids[i]->status == NOALLOC) {
+ mysqldb_connect_close(fqm->tids[i]->arg);
+ if (fqm->tids[i]->tid != NULL)
+ free(fqm->tids[i]->tid);
+ free(fqm->tids[i]);
+ fqm->tids[i] = NULL;
+ deadthreads++;
+ }
+ }
+
+ return deadthreads;
+}
+
+fqm_t *
+fqm_new(int maxitems)
+{
+ fqm_t *tmp;
+ int i;
+
+ tmp = malloc(sizeof(fqm_t));
+ if (tmp == NULL) {
+ log_err("Couldn't create fallthrough queue manager: %s.",
+ strerror(errno));
+ return NULL;
+ }
+
+ tmp->q_cond = NULL;
+ tmp->t_cond = NULL;
+ tmp->tids = NULL;
+ tmp->sreq = NULL; tmp->ereq = NULL;
+ tmp->maxitems = 0;
+ tmp->nitems = 0;
+
+ tmp->q_cond = malloc(sizeof(cond_t));
+ if (tmp->q_cond == NULL) {
+ log_err("Couldn't allocate queue condition variable: %s.",
+ strerror(errno));
+ fqm_delete(tmp);
+ return NULL;
+ }
+
+ if (cond_new(tmp->q_cond)) {
+ log_err("Couldn't initialise queue condition variable: %s.",
+ strerror(errno));
+ fqm_delete(tmp);
+ return NULL;
+ }
+
+ tmp->t_cond = malloc(sizeof(cond_t));
+ if (tmp->t_cond == NULL) {
+ log_err("Couldn't allocate queue destroy condition: %s.",
+ strerror(errno));
+ fqm_delete(tmp);
+ return NULL;
+ }
+
+ if (cond_new(tmp->t_cond)) {
+ log_err("Couldn't initialise queue destroy condition: %s.",
+ strerror(errno));
+ fqm_delete(tmp);
+ return NULL;
+ }
+
+ tmp->tids = malloc(maxitems * sizeof(reqthread_t *));
+ if (tmp->tids == NULL) {
+ log_err("Couldn't allocate fallthrough queue threads: %s.",
+ strerror(errno));
+ fqm_delete(tmp);
+ return NULL;
+ }
+
+ /* Init these so fqm_delete won't break. */
+ for (i = 0; i < maxitems; i++)
+ tmp->tids[i] = NULL;
+
+ /* We cannot adjust max items while threads are busy, so lock. */
+ mutex_lock(&tmp->q_cond->lock);
+ for (i = 0; i < maxitems; i++) {
+ tmp->tids[i] = fqm_thr_new(tmp);
+ if (tmp->tids[i] == NULL) {
+ mutex_unlock(&tmp->q_cond->lock);
+ fqm_delete(tmp);
+ return NULL;
+ }
+ tmp->maxitems++;
+ }
+ mutex_unlock(&tmp->q_cond->lock);
+
+ /* XXX: this may need a lock. */
+ fqm = tmp;
+ return tmp;
+}
+
+/*
+ * Make sure all threads in this fqm are idle. This is an expensive
+ * operation, but that's okay, since this code should only be run when
+ * a client has closed the connection, so we can take as long as we want.
+ *
+ * The queue is locked when coming in to this routine and leaving it.
+ */
+void
+verify_idle_threads(fqm_t *fqm)
+{
+ int i, busy_thread;
+
+ do {
+ busy_thread = 0;
+ for (i = 0; i < fqm->maxitems; i++) {
+ if (fqm->tids[i]->status == BUSY)
+ busy_thread = 1;
+ }
+
+ /*
+ * This is the shitty part.
+ * We unlock the queue and sleep for a bit to give other
+ * threads a chance to finish what they're doing.
+ */
+ if (busy_thread) {
+ mutex_unlock(&fqm->q_cond->lock);
+ sleep(1);
+ mutex_lock(&fqm->q_cond->lock);
+ }
+ } while (busy_thread);
+}
+
+void
+fqm_delete(fqm_t *fqm)
+{
+ int i;
+
+ if (fqm->tids) {
+ /* Tell all threads to quit. */
+ for (i = 0; i < fqm->maxitems; i++) {
+ if (fqm->tids[i] != NULL)
+ fqm->tids[i]->dieflag = 1;
+ }
+
+ /*
+ * Lock queue waiting condition to ensure that no
+ * threads miss this signal. The threads MUST be idle
+ * in order to guarantee this signal is recieved.
+ */
+ mutex_lock(&fqm->q_cond->lock);
+ verify_idle_threads(fqm);
+ pthread_cond_broadcast(&fqm->q_cond->id);
+ mutex_unlock(&fqm->q_cond->lock);
+
+ /* Now wait for them to die off. */
+ i = 0;
+ while (i < fqm->maxitems) {
+ mutex_lock(&fqm->t_cond->lock);
+ i += fqm_thr_wait(fqm);
+ if (i < fqm->maxitems)
+ cond_wait(fqm->t_cond);
+ mutex_unlock(&fqm->t_cond->lock);
+ }
+ free(fqm->tids);
+ fqm->tids = NULL;
+ }
+
+ /*
+ * At this point all fqm threads should be dead.
+ */
+ if (fqm->t_cond) {
+ (void)cond_destroy(fqm->t_cond);
+ free(fqm->t_cond);
+ fqm->t_cond = NULL;
+ }
+
+ if (fqm->q_cond) {
+ (void)cond_destroy(fqm->q_cond);
+ free(fqm->q_cond);
+ fqm->q_cond = NULL;
+ }
+
+ free(fqm);
+}
+
+int
+fqm_changemaxitems(fqm_t *fqm, int maxitems)
+{
+ reqthread_t **new_tidpool;
+ int i, j;
+
+ /*
+ * This code broken. Silently fail here.
+ */
+ return 0;
+
+ if (fqm->maxitems == maxitems)
+ return 0;
+
+ mutex_lock(&fqm->q_cond->lock);
+ /*
+ * Make sure all threads are busy. We can't go changing
+ * data under them.
+ */
+ verify_idle_threads(fqm);
+ new_tidpool = malloc(maxitems * sizeof(reqthread_t *));
+ if (new_tidpool == NULL) {
+ mutex_unlock(&fqm->q_cond->lock);
+ log_err("Couldn't allocate new FQM thread pool: %s.",
+ strerror(errno));
+ return -1;
+ }
+
+ /* Compress old TID pool. */
+ for (j = 0, i = 0; i < fqm->maxitems && j < maxitems; i++) {
+ if (fqm->tids[i]->status != NOALLOC) {
+ new_tidpool[j] = fqm->tids[i];
+ j++;
+ }
+ }
+
+ if (fqm->maxitems < maxitems) {
+ /* Add more threads. */
+ mutex_lock(&fqm->q_cond->lock);
+ for (i = fqm->maxitems; i < maxitems; i++) {
+ new_tidpool[i] = fqm_thr_new(fqm);
+ if (new_tidpool[i] == NULL) {
+ free(new_tidpool);
+ mutex_unlock(&fqm->q_cond->lock);
+ return -1;
+ }
+ }
+ mutex_unlock(&fqm->q_cond->lock);
+ } else if (fqm->maxitems > maxitems) {
+ /* Kill some threads. */
+ int deadthreads;
+
+ /* Tell them to die, then wake 'em up. */
+ for (i = maxitems; i < fqm->maxitems; i++)
+ fqm->tids[i]->dieflag = 1;
+ pthread_cond_broadcast(&fqm->q_cond->id);
+
+ deadthreads = 0;
+ while (deadthreads < fqm->maxitems - maxitems) {
+ mutex_lock(&fqm->t_cond->lock);
+ deadthreads += fqm_thr_wait(fqm);
+ if (deadthreads < fqm->maxitems - maxitems)
+ cond_wait(fqm->t_cond);
+ mutex_unlock(&fqm->t_cond->lock);
+ }
+ }
+
+ free(fqm->tids);
+ fqm->tids = new_tidpool;
+ fqm->maxitems = maxitems;
+ mutex_unlock(&fqm->q_cond->lock);
+ return 0;
+}
+
+int
+fqm_push(fqm_t *fqm, request_t *req)
+{
+ if (fqm->nitems == fqm->maxitems) {
+ log_err("Too many items on the request queue.");
+ return -1;
+ }
+
+ /* Lock the queue and add the item. */
+ mutex_lock(&fqm->q_cond->lock);
+
+ req->next = NULL;
+ if (fqm->sreq == NULL)
+ fqm->sreq = req;
+ else
+ fqm->ereq->next = req;
+ fqm->ereq = req;
+ fqm->nitems++;
+
+ /* Unlock the queue and signal that there's an item on it. */
+ cond_signal(fqm->q_cond);
+ mutex_unlock(&fqm->q_cond->lock);
+ return 0;
+}
diff --git a/server/.svn/text-base/fqm.h.svn-base b/server/.svn/text-base/fqm.h.svn-base
new file mode 100644
index 0000000..c3c27df
--- /dev/null
+++ b/server/.svn/text-base/fqm.h.svn-base
@@ -0,0 +1,50 @@
+/* $Id: fqm.h,v 1.9 2000/11/08 19:50:24 shmit Exp $ */
+
+#ifndef FQM_H
+#define FQM_H
+
+#include "thread.h"
+
+#define REQHANDLER(func) int func(struct _request_t *req, reqthread_t *self)
+
+enum _status_t { IDLE, BUSY, NOALLOC };
+typedef enum _status_t status_t;
+
+struct _reqthread_t {
+ struct _fqm_t *fqm;
+ thread_t *tid;
+ void *arg;
+ int dieflag;
+ status_t status;
+};
+typedef struct _reqthread_t reqthread_t;
+
+struct _request_t {
+ REQHANDLER((*req_handler));
+ char *req;
+ int sock;
+ short reqid;
+ struct _request_t *next;
+};
+typedef struct _request_t request_t;
+
+struct _fqm_t {
+ cond_t *q_cond;
+ cond_t *t_cond;
+ request_t *sreq, *ereq;
+ reqthread_t **tids;
+ int maxitems;
+ int nitems;
+};
+typedef struct _fqm_t fqm_t;
+
+request_t *req_new(int sock, short reqid, REQHANDLER((*req_handler)),
+ const char *reqstr, int reqlen);
+void req_delete(request_t *req);
+
+fqm_t *fqm_new(int maxitems);
+void fqm_delete(fqm_t *fqm);
+int fqm_changemaxitems(fqm_t *fqm, int maxitems);
+int fqm_push(fqm_t *fqm, request_t *req);
+
+#endif
diff --git a/server/.svn/text-base/log.c.svn-base b/server/.svn/text-base/log.c.svn-base
new file mode 100644
index 0000000..612156d
--- /dev/null
+++ b/server/.svn/text-base/log.c.svn-base
@@ -0,0 +1,99 @@
+#include "conf.h"
+#include "log.h"
+
+#include <pthread.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <sys/time.h>
+#include <syslog.h>
+#include <time.h>
+#include <unistd.h>
+
+RCSID("$Id: log.c,v 1.8 2000/11/13 18:47:17 shmit Exp $");
+
+extern char *progname;
+
+#if 0
+FILE *logf = NULL;
+#endif
+
+static void log_priority(int priority, const char *fmt, va_list args);
+
+/*
+ * Log a message with a given priority.
+ */
+void
+log_priority(int priority, const char *fmt, va_list args)
+{
+ char lbuff[1024];
+#if 0
+ char tbuff[256];
+ struct tm timeptr;
+ time_t tloc;
+#endif
+
+ (void)snprintf(lbuff, sizeof(lbuff),
+ "[tid: %d] %s", (int)pthread_self(), fmt);
+ vsyslog(priority, lbuff, args);
+
+#if 0
+ tloc = time(0);
+ (void)localtime_r(&tloc, &timeptr);
+ (void)strftime(tbuff, sizeof(tbuff), "%Y-%h-%d %H:%M:%S", &timeptr);
+ (void)snprintf(lbuff, sizeof(lbuff), "%s %s[%d:%d] %s\n",
+ tbuff, progname, getpid(), (int)pthread_self(), fmt);
+ (void)vfprintf(logf, lbuff, args);
+#endif
+}
+
+/*
+ * Log an error message.
+ */
+void
+log_err(const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ log_priority(LOG_ERR, fmt, ap);
+ va_end(ap);
+}
+
+/*
+ * Log a warning message.
+ */
+void
+log_warn(const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ log_priority(LOG_WARNING, fmt, ap);
+ va_end(ap);
+}
+
+/*
+ * Log an informational message.
+ */
+void
+log_info(const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ log_priority(LOG_INFO, fmt, ap);
+ va_end(ap);
+}
+
+/*
+ * Initialize logging facilites.
+ */
+void
+log_open()
+{
+ openlog(progname, LOG_PID|LOG_PERROR, LOG_DAEMON);
+
+#if 0
+ logf = fopen("/home/shmit/var/log/nastd.log", "a+");
+#endif
+}
diff --git a/server/.svn/text-base/log.h.svn-base b/server/.svn/text-base/log.h.svn-base
new file mode 100644
index 0000000..9aff53a
--- /dev/null
+++ b/server/.svn/text-base/log.h.svn-base
@@ -0,0 +1,11 @@
+/* $Id: log.h,v 1.1 2000/02/29 00:32:12 shmit Exp $ */
+
+#ifndef LOG_H
+# define LOG_H
+
+void log_open();
+void log_err(const char *fmt, ...);
+void log_warn(const char *fmt, ...);
+void log_info(const char *fmt, ...);
+
+#endif /* LOG_H */
diff --git a/server/.svn/text-base/md5.c.svn-base b/server/.svn/text-base/md5.c.svn-base
new file mode 100644
index 0000000..b24f966
--- /dev/null
+++ b/server/.svn/text-base/md5.c.svn-base
@@ -0,0 +1,308 @@
+/*
+ * Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991.
+ * All rights reserved.
+ *
+ * License to copy and use this software is granted provided that it
+ * is identified as the "RSA Data Security, Inc. MD5 Message-Digest
+ * Algorithm" in all material mentioning or referencing this software
+ * or this function.
+ *
+ * License is also granted to make and use derivative works provided
+ * that such works are identified as "derived from the RSA Data
+ * Security, Inc. MD5 Message-Digest Algorithm" in all material
+ * mentioning or referencing the derived work.
+ *
+ * RSA Data Security, Inc. makes no representations concerning either
+ * the merchantability of this software or the suitability of this
+ * software for any particular purpose. It is provided "as is"
+ * without express or implied warranty of any kind.
+ *
+ * These notices must be retained in any copies of any part of this
+ * documentation and/or software.
+ */
+
+#include "conf.h"
+#include "log.h"
+#include "md5.h"
+
+#include <string.h>
+
+RCSID("$Id: md5.c,v 1.2 2000/03/27 22:23:25 shmit Exp $");
+
+/* Constants for MD5Transform routine. */
+#define S11 7
+#define S12 12
+#define S13 17
+#define S14 22
+#define S21 5
+#define S22 9
+#define S23 14
+#define S24 20
+#define S31 4
+#define S32 11
+#define S33 16
+#define S34 23
+#define S41 6
+#define S42 10
+#define S43 15
+#define S44 21
+
+static void MD5Transform(uint32_t *, const unsigned char *);
+static void Encode(unsigned char *, uint32_t *, unsigned int, int ouputlen);
+static void Decode(uint32_t *, const unsigned char *, unsigned int);
+
+static unsigned char PADDING[64] = {
+ 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+/* F, G, H and I are basic MD5 functions. */
+#define F(x, y, z) (((x) & (y)) | ((~x) & (z)))
+#define G(x, y, z) (((x) & (z)) | ((y) & (~z)))
+#define H(x, y, z) ((x) ^ (y) ^ (z))
+#define I(x, y, z) ((y) ^ ((x) | (~z)))
+
+/* ROTATE_LEFT rotates x left n bits. */
+#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n))))
+
+/*
+ * FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4.
+ * Rotation is separate from addition to prevent recomputation.
+ */
+#define FF(a, b, c, d, x, s, ac) { \
+ (a) += F ((b), (c), (d)) + (x) + (uint32_t)(ac); \
+ (a) = ROTATE_LEFT ((a), (s)); \
+ (a) += (b); \
+ }
+#define GG(a, b, c, d, x, s, ac) { \
+ (a) += G ((b), (c), (d)) + (x) + (uint32_t)(ac); \
+ (a) = ROTATE_LEFT ((a), (s)); \
+ (a) += (b); \
+ }
+#define HH(a, b, c, d, x, s, ac) { \
+ (a) += H ((b), (c), (d)) + (x) + (uint32_t)(ac); \
+ (a) = ROTATE_LEFT ((a), (s)); \
+ (a) += (b); \
+ }
+#define II(a, b, c, d, x, s, ac) { \
+ (a) += I ((b), (c), (d)) + (x) + (uint32_t)(ac); \
+ (a) = ROTATE_LEFT ((a), (s)); \
+ (a) += (b); \
+ }
+
+void
+md5_calc(unsigned char *output, const unsigned char *input, unsigned int inlen,
+ int outputlen)
+{
+ MD5_CTX context;
+
+ MD5Init(&context);
+ MD5Update(&context, input, inlen);
+ MD5Final(output, &context, outputlen);
+}
+
+/*
+ * MD5 initialization. Begins an MD5 operation, writing a new context.
+ */
+void
+MD5Init(MD5_CTX *context)
+{
+ context->count[0] = context->count[1] = 0;
+ /* Load magic initialization constants. */
+ context->state[0] = 0x67452301;
+ context->state[1] = 0xefcdab89;
+ context->state[2] = 0x98badcfe;
+ context->state[3] = 0x10325476;
+}
+
+/*
+ * MD5 block update operation. Continues an MD5 message-digest
+ * operation, processing another message block, and updating the
+ * context.
+ */
+void
+MD5Update(MD5_CTX *context, const unsigned char *input, unsigned int inputLen)
+{
+ unsigned int i, index, partLen;
+
+ /* Compute number of bytes mod 64 */
+ index = (unsigned int)((context->count[0] >> 3) & 0x3F);
+
+ /* Update number of bits */
+ if ((context->count[0] += ((uint32_t)inputLen << 3)) <
+ ((uint32_t)inputLen << 3))
+ context->count[1]++;
+ context->count[1] += ((uint32_t)inputLen >> 29);
+
+ partLen = 64 - index;
+
+ /* Transform as many times as possible. */
+ if (inputLen >= partLen) {
+ memcpy(&context->buffer[index], input, partLen);
+ MD5Transform (context->state, context->buffer);
+
+ for (i = partLen; i + 63 < inputLen; i += 64)
+ MD5Transform (context->state, &input[i]);
+
+ index = 0;
+ } else
+ i = 0;
+
+ /* Buffer remaining input */
+ memcpy(&context->buffer[index], &input[i], inputLen-i);
+}
+
+/*
+ * MD5 finalization. Ends an MD5 message-digest operation, writing the
+ * the message digest and zeroizing the context.
+ */
+void
+MD5Final(unsigned char *digest, MD5_CTX *context, int digestlen)
+{
+ unsigned char bits[8];
+ unsigned int index, padLen;
+
+ /* Save number of bits */
+ Encode(bits, context->count, 8, sizeof(bits));
+
+ /* Pad out to 56 mod 64. */
+ index = (context->count[0] >> 3) & 0x3f;
+ padLen = (index < 56) ? (56 - index) : (120 - index);
+ MD5Update(context, PADDING, padLen);
+
+ /* Append length (before padding) */
+ MD5Update(context, bits, 8);
+
+ /* Store state in digest */
+ Encode(digest, context->state, 16, digestlen);
+
+ /* Zeroize sensitive information. */
+ memset(context, 0, sizeof(MD5_CTX));
+}
+
+/*
+ * MD5 basic transformation. Transforms state based on block.
+ */
+static void
+MD5Transform(uint32_t *state, const unsigned char *block)
+{
+ uint32_t a = state[0], b = state[1], c = state[2], d = state[3], x[16];
+
+ Decode (x, block, 64);
+
+ /* Round 1 */
+ FF(a, b, c, d, x[ 0], S11, 0xd76aa478); /* 1 */
+ FF(d, a, b, c, x[ 1], S12, 0xe8c7b756); /* 2 */
+ FF(c, d, a, b, x[ 2], S13, 0x242070db); /* 3 */
+ FF(b, c, d, a, x[ 3], S14, 0xc1bdceee); /* 4 */
+ FF(a, b, c, d, x[ 4], S11, 0xf57c0faf); /* 5 */
+ FF(d, a, b, c, x[ 5], S12, 0x4787c62a); /* 6 */
+ FF(c, d, a, b, x[ 6], S13, 0xa8304613); /* 7 */
+ FF(b, c, d, a, x[ 7], S14, 0xfd469501); /* 8 */
+ FF(a, b, c, d, x[ 8], S11, 0x698098d8); /* 9 */
+ FF(d, a, b, c, x[ 9], S12, 0x8b44f7af); /* 10 */
+ FF(c, d, a, b, x[10], S13, 0xffff5bb1); /* 11 */
+ FF(b, c, d, a, x[11], S14, 0x895cd7be); /* 12 */
+ FF(a, b, c, d, x[12], S11, 0x6b901122); /* 13 */
+ FF(d, a, b, c, x[13], S12, 0xfd987193); /* 14 */
+ FF(c, d, a, b, x[14], S13, 0xa679438e); /* 15 */
+ FF(b, c, d, a, x[15], S14, 0x49b40821); /* 16 */
+
+ /* Round 2 */
+ GG(a, b, c, d, x[ 1], S21, 0xf61e2562); /* 17 */
+ GG(d, a, b, c, x[ 6], S22, 0xc040b340); /* 18 */
+ GG(c, d, a, b, x[11], S23, 0x265e5a51); /* 19 */
+ GG(b, c, d, a, x[ 0], S24, 0xe9b6c7aa); /* 20 */
+ GG(a, b, c, d, x[ 5], S21, 0xd62f105d); /* 21 */
+ GG(d, a, b, c, x[10], S22, 0x2441453); /* 22 */
+ GG(c, d, a, b, x[15], S23, 0xd8a1e681); /* 23 */
+ GG(b, c, d, a, x[ 4], S24, 0xe7d3fbc8); /* 24 */
+ GG(a, b, c, d, x[ 9], S21, 0x21e1cde6); /* 25 */
+ GG(d, a, b, c, x[14], S22, 0xc33707d6); /* 26 */
+ GG(c, d, a, b, x[ 3], S23, 0xf4d50d87); /* 27 */
+ GG(b, c, d, a, x[ 8], S24, 0x455a14ed); /* 28 */
+ GG(a, b, c, d, x[13], S21, 0xa9e3e905); /* 29 */
+ GG(d, a, b, c, x[ 2], S22, 0xfcefa3f8); /* 30 */
+ GG(c, d, a, b, x[ 7], S23, 0x676f02d9); /* 31 */
+ GG(b, c, d, a, x[12], S24, 0x8d2a4c8a); /* 32 */
+
+ /* Round 3 */
+ HH(a, b, c, d, x[ 5], S31, 0xfffa3942); /* 33 */
+ HH(d, a, b, c, x[ 8], S32, 0x8771f681); /* 34 */
+ HH(c, d, a, b, x[11], S33, 0x6d9d6122); /* 35 */
+ HH(b, c, d, a, x[14], S34, 0xfde5380c); /* 36 */
+ HH(a, b, c, d, x[ 1], S31, 0xa4beea44); /* 37 */
+ HH(d, a, b, c, x[ 4], S32, 0x4bdecfa9); /* 38 */
+ HH(c, d, a, b, x[ 7], S33, 0xf6bb4b60); /* 39 */
+ HH(b, c, d, a, x[10], S34, 0xbebfbc70); /* 40 */
+ HH(a, b, c, d, x[13], S31, 0x289b7ec6); /* 41 */
+ HH(d, a, b, c, x[ 0], S32, 0xeaa127fa); /* 42 */
+ HH(c, d, a, b, x[ 3], S33, 0xd4ef3085); /* 43 */
+ HH(b, c, d, a, x[ 6], S34, 0x4881d05); /* 44 */
+ HH(a, b, c, d, x[ 9], S31, 0xd9d4d039); /* 45 */
+ HH(d, a, b, c, x[12], S32, 0xe6db99e5); /* 46 */
+ HH(c, d, a, b, x[15], S33, 0x1fa27cf8); /* 47 */
+ HH(b, c, d, a, x[ 2], S34, 0xc4ac5665); /* 48 */
+
+ /* Round 4 */
+ II(a, b, c, d, x[ 0], S41, 0xf4292244); /* 49 */
+ II(d, a, b, c, x[ 7], S42, 0x432aff97); /* 50 */
+ II(c, d, a, b, x[14], S43, 0xab9423a7); /* 51 */
+ II(b, c, d, a, x[ 5], S44, 0xfc93a039); /* 52 */
+ II(a, b, c, d, x[12], S41, 0x655b59c3); /* 53 */
+ II(d, a, b, c, x[ 3], S42, 0x8f0ccc92); /* 54 */
+ II(c, d, a, b, x[10], S43, 0xffeff47d); /* 55 */
+ II(b, c, d, a, x[ 1], S44, 0x85845dd1); /* 56 */
+ II(a, b, c, d, x[ 8], S41, 0x6fa87e4f); /* 57 */
+ II(d, a, b, c, x[15], S42, 0xfe2ce6e0); /* 58 */
+ II(c, d, a, b, x[ 6], S43, 0xa3014314); /* 59 */
+ II(b, c, d, a, x[13], S44, 0x4e0811a1); /* 60 */
+ II(a, b, c, d, x[ 4], S41, 0xf7537e82); /* 61 */
+ II(d, a, b, c, x[11], S42, 0xbd3af235); /* 62 */
+ II(c, d, a, b, x[ 2], S43, 0x2ad7d2bb); /* 63 */
+ II(b, c, d, a, x[ 9], S44, 0xeb86d391); /* 64 */
+
+ state[0] += a;
+ state[1] += b;
+ state[2] += c;
+ state[3] += d;
+
+ /* Zeroize sensitive information. */
+ memset(x, 0, sizeof(x));
+}
+
+/*
+ * Encodes input (uint32_t) into output (unsigned char). Assumes len is
+ * a multiple of 4.
+ */
+static void
+Encode(unsigned char *output, uint32_t *input, unsigned int len, int outputlen)
+{
+ unsigned int i, j;
+
+ for (i = 0, j = 0; j < len && j <= outputlen-4; i++, j += 4) {
+ output[j] = (unsigned char)(input[i] & 0xff);
+ output[j+1] = (unsigned char)((input[i] >> 8) & 0xff);
+ output[j+2] = (unsigned char)((input[i] >> 16) & 0xff);
+ output[j+3] = (unsigned char)((input[i] >> 24) & 0xff);
+ }
+
+ if (j > outputlen-4 && j < len)
+ log_warn("Digest would be too big, truncated.");
+}
+
+/*
+ * Decodes input (unsigned char) into output (uint32_t). Assumes len is
+ * a multiple of 4.
+ */
+static void
+Decode(uint32_t *output, const unsigned char *input, unsigned int len)
+{
+ unsigned int i, j;
+
+ for (i = 0, j = 0; j < len; i++, j += 4)
+ output[i] = ((uint32_t)input[j]) | (((uint32_t)input[j+1]) << 8) |
+ (((uint32_t)input[j+2]) << 16) |
+ (((uint32_t)input[j+3]) << 24);
+}
diff --git a/server/.svn/text-base/md5.h.svn-base b/server/.svn/text-base/md5.h.svn-base
new file mode 100644
index 0000000..5f48d41
--- /dev/null
+++ b/server/.svn/text-base/md5.h.svn-base
@@ -0,0 +1,48 @@
+/* $Id: md5.h,v 1.2 2000/03/27 22:23:25 shmit Exp $ */
+
+/*
+ * Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991.
+ * All rights reserved.
+ *
+ * License to copy and use this software is granted provided that it
+ * is identified as the "RSA Data Security, Inc. MD5 Message-Digest
+ * Algorithm" in all material mentioning or referencing this software
+ * or this function.
+ *
+ * License is also granted to make and use derivative works provided
+ * that such works are identified as "derived from the RSA Data
+ * Security, Inc. MD5 Message-Digest Algorithm" in all material
+ * mentioning or referencing the derived work.
+ *
+ * RSA Data Security, Inc. makes no representations concerning either
+ * the merchantability of this software or the suitability of this
+ * software for any particular purpose. It is provided "as is"
+ * without express or implied warranty of any kind.
+ *
+ * These notices must be retained in any copies of any part of this
+ * documentation and/or software.
+ */
+
+#ifndef MD5_H
+# define MD5_H
+
+#include "compat.h"
+
+/* POINTER defines a generic pointer type */
+typedef unsigned char *POINTER;
+
+/* MD5 context. */
+typedef struct {
+ uint32_t state[4]; /* state (ABCD) */
+ uint32_t count[2]; /* number of bits, modulo 2^64 */
+ /* (lsb first) */
+ unsigned char buffer[64]; /* input buffer */
+} MD5_CTX;
+
+void MD5Init(MD5_CTX *);
+void MD5Update(MD5_CTX *, const unsigned char *, unsigned int);
+void MD5Final(unsigned char *, MD5_CTX *, int outputlen);
+void md5_calc(unsigned char *output, const unsigned char *input,
+ unsigned int inlen, int outputlen);
+
+#endif /* MD5_H */
diff --git a/server/.svn/text-base/memdb.c.svn-base b/server/.svn/text-base/memdb.c.svn-base
new file mode 100644
index 0000000..501d0ef
--- /dev/null
+++ b/server/.svn/text-base/memdb.c.svn-base
@@ -0,0 +1,501 @@
+#include "conf.h"
+#include "config.h"
+#include "array.h"
+#include "log.h"
+#include "md5.h"
+#include "memdb.h"
+#include "thread.h"
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+RCSID("$Id: memdb.c,v 1.27 2001/10/04 23:57:10 shmit Exp $");
+
+#define HASHSIZE 1024
+
+extern time_t cdb_mtime;
+
+enum _cachetype_t { C_DEL, C_UPD, C_TRYVAL, C_EXPIRE };
+typedef enum _cachetype_t cachetype_t;
+
+struct _mement_t {
+ char *key;
+ int keylen;
+ array_t *vals;
+ time_t *upd_time;
+ cachetype_t type;
+ void *typeval;
+};
+typedef struct _mement_t mement_t;
+
+struct _cachent_t {
+ mement_t ent;
+ struct _cachent_t *next;
+};
+typedef struct _cachent_t cachent_t;
+
+struct _counter_t {
+ float onemin, fivemin, fifteenmin;
+};
+typedef struct _counter_t counter_t;
+
+extern short db_fieldcount;
+
+static cachent_t **memdb = NULL;
+static int memdbsize;
+static rw_mutex_t *memdb_lk = NULL;
+
+static int query_sec, add_sec, del_sec, upd_sec;
+static counter_t queryrate;
+static counter_t addrate;
+static counter_t delrate;
+static counter_t updrate;
+
+static unsigned int
+hashkey(const char *key, int keylen)
+{
+ char md5hash[32];
+ unsigned int rc;
+
+ memset(md5hash, 0, sizeof(md5hash));
+ md5_calc(md5hash, key, keylen, sizeof(md5hash));
+ memcpy(&rc, md5hash, sizeof(rc));
+ return (rc % HASHSIZE);
+}
+
+static void
+ent_delete(mement_t *ent)
+{
+ if (ent == NULL)
+ return;
+
+ if (ent->key != NULL)
+ free(ent->key);
+ if (ent->vals != NULL)
+ array_delete(ent->vals);
+ if (ent->upd_time != NULL)
+ free(ent->upd_time);
+ if (ent->typeval != NULL)
+ free(ent->typeval);
+}
+
+int
+memdb_new()
+{
+ cachent_t **newdb;
+ int i;
+ int rc;
+
+ log_info("Initialising memdb interface.");
+
+ if (memdb_lk == NULL) {
+ memdb_lk = malloc(sizeof(rw_mutex_t));
+ if (memdb_lk == NULL) {
+ log_err("Couldn't allocate memdb mutex: %s.",
+ strerror(errno));
+ return -1;
+ }
+ rc = rw_mutex_new(memdb_lk);
+ if (rc) {
+ log_err("Couldn't initialise memdb mutex: %s.",
+ strerror(rc));
+ return -1;
+ }
+ }
+
+ newdb = malloc(HASHSIZE * sizeof(cachent_t *));
+ if (newdb == NULL) {
+ log_err("Couldn't allocate new memdb: %s.", strerror(errno));
+ return -1;
+ }
+
+ for (i = 0; i < HASHSIZE; i++)
+ newdb[i] = NULL;
+
+ rw_mutex_write_lock(memdb_lk);
+ if (memdb != NULL)
+ memdb_delete(memdb);
+ memdb = newdb;
+ rw_mutex_unlock(memdb_lk);
+
+ query_sec = add_sec = del_sec = upd_sec = 0;
+ queryrate.onemin = queryrate.fivemin = queryrate.fifteenmin = 0;
+ addrate.onemin = addrate.fivemin = addrate.fifteenmin = 0;
+ delrate.onemin = delrate.fivemin = delrate.fifteenmin = 0;
+ updrate.onemin = updrate.fivemin = updrate.fifteenmin = 0;
+ memdbsize = 0;
+
+ log_info("memdb interface initialised.");
+ return 0;
+}
+
+void
+memdb_delete()
+{
+ int i;
+
+ if (memdb == NULL)
+ return;
+
+ for (i = 0; i < HASHSIZE; i++) {
+ cachent_t *p;
+
+ p = memdb[i];
+ while (p != NULL) {
+ cachent_t *q;
+
+ q = p->next;
+ ent_delete(&p->ent);
+ free(p);
+ p = q;
+ }
+ }
+ free(memdb);
+ memdb = NULL;
+}
+
+int
+memdb_add(const char *key, int keylen, array_t *vals)
+{
+ cachent_t *ent;
+ int hkey;
+ time_t curtime;
+
+ ent = malloc(sizeof(cachent_t));
+ if (ent == NULL) {
+ log_err("Couldn't create memdb entry for `%s': %s.",
+ key, strerror(errno));
+ return -1;
+ }
+ ent->next = NULL;
+ ent->ent.key = NULL;
+ ent->ent.type = C_UPD;
+ ent->ent.typeval = NULL;
+ ent->ent.upd_time = NULL;
+
+ ent->ent.key = malloc((keylen + 1) * sizeof(char));
+ if (ent->ent.key == NULL) {
+ log_err("Couldn't create entry key for `%s': %s.",
+ key, strerror(errno));
+ ent_delete(&ent->ent);
+ return -1;
+ }
+ memcpy(ent->ent.key, key, keylen);
+ ent->ent.keylen = keylen;
+
+ ent->ent.vals = array_new();
+ if (ent->ent.vals == NULL) {
+ ent_delete(&ent->ent);
+ return -1;
+ }
+ if (array_dup(ent->ent.vals, vals) == -1) {
+ ent_delete(&ent->ent);
+ return -1;
+ }
+
+ ent->ent.upd_time = malloc(sizeof(time_t));
+ if (ent->ent.upd_time == NULL) {
+ ent_delete(&ent->ent);
+ return -1;
+ }
+ curtime = time(NULL);
+ memcpy(ent->ent.upd_time, &curtime, sizeof(time_t));
+
+ /* Cache non-results for a minute. */
+ if (vals->nitems == 0) {
+ time_t expire;
+
+ ent->ent.type = C_EXPIRE;
+ ent->ent.typeval = malloc(sizeof(time_t));
+ expire = time(NULL);
+ expire += config.null_cache_timeout;
+ memcpy(ent->ent.typeval, &expire, sizeof(time_t));
+ }
+
+ hkey = hashkey(key, keylen);
+ rw_mutex_write_lock(memdb_lk);
+ if (memdb[hkey] == NULL) {
+ memdb[hkey] = ent;
+ memdbsize++;
+ } else {
+ cachent_t *p, *q;
+
+ p = memdb[hkey];
+ q = NULL;
+ while (p->next != NULL &&
+ !(keylen == p->ent.keylen &&
+ memcmp(p->ent.key, key, p->ent.keylen) == 0)) {
+ q = p;
+ p = p->next;
+ }
+ if (keylen == p->ent.keylen &&
+ memcmp(p->ent.key, key, keylen) == 0) {
+ /* Already in memdb. */
+
+ log_info("(add) %s already exists.", key);
+
+ switch (p->ent.type) {
+ case C_EXPIRE:
+ case C_DEL:
+ /*
+ * Deletable entries. Trash 'em and
+ * add 'em.
+ */
+
+ if (q == NULL)
+ memdb[hkey] = ent;
+ else
+ q->next = ent;
+ ent->next = p->next;
+
+ ent_delete(&p->ent);
+ free(p);
+ break;
+ default:
+ /* XXX: Do an update instead of an add. */
+ }
+ } else {
+ p->next = ent;
+ memdbsize++;
+ }
+ }
+
+ add_sec++;
+ rw_mutex_unlock(memdb_lk);
+ return 0;
+}
+
+int
+memdb_del(const char *key, int keylen)
+{
+ cachent_t *p;
+ int hkey;
+
+ hkey = hashkey(key, keylen);
+
+ rw_mutex_read_lock(memdb_lk);
+ p = memdb[hkey];
+ while (p != NULL &&
+ (keylen != p->ent.keylen || memcmp(p->ent.key, key, keylen)))
+ p = p->next;
+
+ if (p == NULL) {
+ array_t *aa;
+
+ /*
+ * Entry not found. We have to add it ourselves.
+ */
+ rw_mutex_unlock(memdb_lk);
+
+ aa = array_new();
+ if (aa == NULL)
+ return -1;
+ if (memdb_add(key, keylen, aa) == -1)
+ return -1;
+
+ /*
+ * The entry has been added - now go back and mark it
+ * for deletion.
+ */
+ return memdb_del(key, keylen);
+ }
+
+ rw_mutex_write_lock(memdb_lk);
+ if (p->ent.typeval != NULL) {
+ free(p->ent.typeval);
+ p->ent.typeval = NULL;
+ }
+
+ p->ent.type = C_DEL;
+ if (p->ent.vals != NULL)
+ array_delete(p->ent.vals);
+
+ del_sec++;
+ rw_mutex_unlock(memdb_lk);
+
+ return 0;
+}
+
+int
+memdb_get(const char *key, int keylen, array_t *vals)
+{
+ cachent_t *p, *q;
+ int hkey;
+
+ hkey = hashkey(key, keylen);
+ rw_mutex_read_lock(memdb_lk);
+ p = memdb[hkey];
+ q = NULL;
+ while (p != NULL &&
+ (keylen != p->ent.keylen || memcmp(p->ent.key, key, keylen))) {
+ q = p;
+ p = p->next;
+ }
+
+ if (p == NULL) {
+ rw_mutex_unlock(memdb_lk);
+ return -1;
+ }
+
+ switch (p->ent.type) {
+ case C_DEL:
+ rw_mutex_unlock(memdb_lk);
+ query_sec++;
+ return 0;
+ case C_EXPIRE:
+ if (p->ent.typeval != NULL) {
+ time_t now, expire;
+
+ now = time(NULL);
+ expire = *((time_t *)p->ent.typeval);
+ if (now >= expire) {
+ ent_delete(&p->ent);
+ if (q != NULL)
+ q->next = p->next;
+ else
+ memdb[hkey] = p->next;
+ free(p);
+ rw_mutex_unlock(memdb_lk);
+ log_info("DEBUG: (get) %s has expired", key);
+ return -1;
+ }
+ }
+ break;
+ default:
+ if (p->ent.upd_time != NULL) {
+ time_t upd_time;
+
+ upd_time = *p->ent.upd_time;
+ if (cdb_mtime > upd_time) {
+ ent_delete(&p->ent);
+ if (q != NULL)
+ q->next = p->next;
+ else
+ memdb[hkey] = p->next;
+ free(p);
+ rw_mutex_unlock(memdb_lk);
+ log_info("DEBUG: (get) %s is out of date", key);
+ return -1;
+ }
+ }
+ break;
+ }
+
+ if (array_dup(vals, p->ent.vals) == -1) {
+ rw_mutex_unlock(memdb_lk);
+ return -1;
+ }
+
+ rw_mutex_unlock(memdb_lk);
+ query_sec++;
+ return 0;
+}
+
+int
+memdb_upd(const char *key, int keylen, array_t *vals)
+{
+ cachent_t *p;
+ int hkey;
+ time_t curtime;
+
+ /* First make sure that we have enough values. */
+ if (vals->nitems != db_fieldcount) {
+ log_err("Update tried with wrong value count.");
+ return -1;
+ }
+
+ hkey = hashkey(key, keylen);
+ rw_mutex_read_lock(memdb_lk);
+ p = memdb[hkey];
+ while (p != NULL &&
+ (keylen != p->ent.keylen || memcmp(p->ent.key, key, keylen)))
+ p = p->next;
+
+ if (p == NULL) {
+ /* Entry not found. Add it ourselves. */
+ rw_mutex_unlock(memdb_lk);
+ return memdb_add(key, keylen, vals);
+ }
+ rw_mutex_unlock(memdb_lk);
+
+ /* Entry found, lets replace it with our copy. */
+
+ rw_mutex_write_lock(memdb_lk);
+ p->ent.type = C_UPD;
+ p->ent.typeval = NULL;
+ curtime = time(NULL);
+ memcpy(p->ent.upd_time, &curtime, sizeof(time_t));
+ array_delete(p->ent.vals);
+
+ p->ent.vals = array_new();
+ if (p->ent.vals == NULL) {
+ rw_mutex_unlock(memdb_lk);
+ return -1;
+ }
+
+ if (array_dup(p->ent.vals, vals) == -1) {
+ rw_mutex_unlock(memdb_lk);
+ return -1;
+ }
+
+ upd_sec++;
+ rw_mutex_unlock(memdb_lk);
+
+ return 0;
+}
+
+int
+memdb_stats(array_t *statarr)
+{
+ char buffer[512];
+
+ /* Convert q/m, q/5m, and q/15m into q/s. */
+ snprintf(buffer, sizeof(buffer), "MEMDB get: %.2f, %.2f, %.2f",
+ queryrate.onemin, queryrate.fivemin, queryrate.fifteenmin);
+ if (array_add(statarr, strlen(buffer), buffer, ARRTERM) == -1)
+ return -1;
+
+ snprintf(buffer, sizeof(buffer), "MEMDB add: %.2f, %.2f, %.2f",
+ addrate.onemin, addrate.fivemin, addrate.fifteenmin);
+ if (array_add(statarr, strlen(buffer), buffer, ARRTERM) == -1)
+ return -1;
+
+ snprintf(buffer, sizeof(buffer), "MEMDB del: %.2f, %.2f, %.2f",
+ delrate.onemin, delrate.fivemin, delrate.fifteenmin);
+ if (array_add(statarr, strlen(buffer), buffer, ARRTERM) == -1)
+ return -1;
+
+ snprintf(buffer, sizeof(buffer), "MEMDB upd: %.2f, %.2f, %.2f",
+ updrate.onemin, updrate.fivemin, updrate.fifteenmin);
+ if (array_add(statarr, strlen(buffer), buffer, ARRTERM) == -1)
+ return -1;
+
+ snprintf(buffer, sizeof(buffer), "MEMDB total entries: %d", memdbsize);
+ return array_add(statarr, strlen(buffer), buffer, ARRTERM);
+}
+
+void
+memdb_collate()
+{
+ queryrate.onemin = ((queryrate.onemin * 59) + query_sec) / 60;
+ queryrate.fivemin = ((queryrate.fivemin * 299) + query_sec) / 300;
+ queryrate.fifteenmin = ((queryrate.fifteenmin * 899) + query_sec) / 900;
+ query_sec = 0;
+
+ addrate.onemin = ((addrate.onemin * 59) + add_sec) / 60;
+ addrate.fivemin = ((addrate.fivemin * 299) + add_sec) / 300;
+ addrate.fifteenmin = ((addrate.fifteenmin * 899) + add_sec) / 900;
+ add_sec = 0;
+
+ delrate.onemin = ((delrate.onemin * 59) + del_sec) / 60;
+ delrate.fivemin = ((delrate.fivemin * 299) + del_sec) / 300;
+ delrate.fifteenmin = ((delrate.fifteenmin * 899) + del_sec) / 900;
+ del_sec = 0;
+
+ updrate.onemin = ((updrate.onemin * 59) + upd_sec) / 60;
+ updrate.fivemin = ((updrate.fivemin * 299) + upd_sec) / 300;
+ updrate.fifteenmin = ((updrate.fifteenmin * 899) + upd_sec) / 900;
+ upd_sec = 0;
+}
diff --git a/server/.svn/text-base/memdb.h.svn-base b/server/.svn/text-base/memdb.h.svn-base
new file mode 100644
index 0000000..0822e61
--- /dev/null
+++ b/server/.svn/text-base/memdb.h.svn-base
@@ -0,0 +1,17 @@
+/* $Id: memdb.h,v 1.8 2000/05/17 19:32:58 shmit Exp $ */
+
+#ifndef MEMDB_H
+#define MEMDB_H
+
+int memdb_new();
+void memdb_delete();
+
+int memdb_add(const char *key, int keylen, array_t *vals);
+int memdb_del(const char *key, int keylen);
+int memdb_get(const char *key, int keylen, array_t *vals);
+int memdb_upd(const char *key, int keylen, array_t *vals);
+
+int memdb_stats(array_t *statarr);
+void memdb_collate();
+
+#endif
diff --git a/server/.svn/text-base/mysqldb.c.svn-base b/server/.svn/text-base/mysqldb.c.svn-base
new file mode 100644
index 0000000..517c15b
--- /dev/null
+++ b/server/.svn/text-base/mysqldb.c.svn-base
@@ -0,0 +1,163 @@
+#include "conf.h"
+#include "array.h"
+#include "config.h"
+#include "nastdio.h"
+#include "log.h"
+#include "mysqldb.h"
+#include "thread.h"
+
+#include <errno.h>
+#include <errmsg.h>
+#include <mysql.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+RCSID("$Id: mysqldb.c,v 1.30 2001/10/10 20:29:47 shmit Exp $");
+
+extern fieldent *db_fields;
+extern char db_key[1024];
+extern char db_dbn[1024];
+extern char db_tbl[1024];
+extern short db_fieldcount;
+
+static char cols[512];
+
+static int sec_c;
+static float onemin_c, fivemin_c, fifteenmin_c;
+
+int
+mysqldb_new()
+{
+ int i;
+
+ sec_c = 0;
+ onemin_c = fivemin_c = fifteenmin_c = 0;
+
+ cols[0] = '\0';
+ for (i = 0; i < db_fieldcount; i++) {
+ strncat(cols, db_fields[i].name, sizeof(cols));
+ if (i < db_fieldcount-1)
+ strncat(cols, ",", sizeof(cols));
+ }
+
+ return 0;
+}
+
+void *
+mysqldb_connect_new()
+{
+ MYSQL *dbh;
+
+ log_info("Initialising MySQL database.");
+
+ dbh = mysql_init(NULL);
+ if (dbh == NULL) {
+ log_err("Couldn't allocate mysql handle: %s.",
+ strerror(errno));
+ return NULL;
+ }
+
+ if (!mysql_connect(dbh, config.mysql_host,
+ config.mysql_user, config.mysql_pass)) {
+ log_err("Couldn't open connection to database: %s.",
+ mysql_error(dbh));
+ mysqldb_connect_close(dbh);
+ return NULL;
+ }
+
+ if (mysql_select_db(dbh, db_dbn)) {
+ log_err("Couldn't open database: %s.",
+ mysql_error(dbh));
+ mysqldb_connect_close(dbh);
+ return NULL;
+ }
+
+ log_info("MySQL database interface initialised.");
+ return (void *)dbh;
+}
+
+void
+mysqldb_connect_close(void *dbh)
+{
+ if (dbh != NULL) {
+ log_info("MySQL connection shutting down.");
+ mysql_close(dbh);
+ free(dbh);
+ }
+}
+
+int
+mysqldb_get(reqthread_t *self, const char *key, int keylen, array_t *aa)
+{
+ MYSQL *dbh;
+ MYSQL_RES *result;
+ char buffer[1024];
+ MYSQL_ROW row;
+ int i, rc;
+
+ snprintf(buffer, sizeof(buffer), DBSELECT, cols, db_tbl, db_key, key);
+
+ if (self->arg == NULL) {
+ self->arg = mysqldb_connect_new();
+ if (self->arg == NULL)
+ return -1;
+ }
+ dbh = (MYSQL *)self->arg;
+
+ rc = mysql_query(dbh, buffer);
+ if (rc) {
+ log_err("Error performing query: %s.", mysql_error(dbh));
+ mysqldb_connect_close(dbh);
+ self->arg = NULL;
+ return -1;
+ }
+
+ result = mysql_use_result(dbh);
+ row = mysql_fetch_row(result);
+ if (row == NULL) {
+ log_info("Couldn't find %s in MySQL database.",
+ key);
+ mysql_free_result(result);
+ sec_c++;
+ return 1;
+ }
+
+ if (mysql_num_fields(result) < db_fieldcount) {
+ log_err("MySQL server didn't return all fields.");
+ mysql_free_result(result);
+ return 0;
+ }
+
+ for (i = 0; i < db_fieldcount; i++) {
+ if (array_add(aa, strlen(row[i]), row[i], ARRTERM) == -1) {
+ mysql_free_result(result);
+ return -1;
+ }
+ }
+ while (mysql_fetch_row(result));
+ mysql_free_result(result);
+
+ sec_c++;
+
+ return 0;
+}
+
+int
+mysqldb_stats(array_t *statarr)
+{
+ char buffer[512];
+
+ snprintf(buffer, sizeof(buffer), "MySQL: %.2f, %.2f, %.2f",
+ onemin_c, fivemin_c, fifteenmin_c);
+ return array_add(statarr, strlen(buffer), buffer, ARRTERM);
+}
+
+void
+mysqldb_collate()
+{
+ onemin_c = ((onemin_c * 59) + sec_c) / 60;
+ fivemin_c = ((fivemin_c * 299) + sec_c) / 300;
+ fifteenmin_c = ((fifteenmin_c * 899) + sec_c) / 900;
+ sec_c = 0;
+}
diff --git a/server/.svn/text-base/mysqldb.h.svn-base b/server/.svn/text-base/mysqldb.h.svn-base
new file mode 100644
index 0000000..c5c703c
--- /dev/null
+++ b/server/.svn/text-base/mysqldb.h.svn-base
@@ -0,0 +1,17 @@
+/* $Id: mysqldb.h,v 1.7 2000/11/08 20:04:55 shmit Exp $ */
+
+#ifndef MYSQLDB_H
+#define MYSQLDB_H
+
+#include "array.h"
+#include "fqm.h"
+
+int mysqldb_new();
+void *mysqldb_connect_new();
+void mysqldb_connect_close(void *dbh);
+int mysqldb_get(reqthread_t *self, const char *key, int keylen, array_t *aa);
+
+int mysqldb_stats(array_t *statarr);
+void mysqldb_collate();
+
+#endif
diff --git a/server/.svn/text-base/nastd.c.svn-base b/server/.svn/text-base/nastd.c.svn-base
new file mode 100644
index 0000000..3c015a6
--- /dev/null
+++ b/server/.svn/text-base/nastd.c.svn-base
@@ -0,0 +1,224 @@
+#include "conf.h"
+#include "config.h"
+#include "nastdio.h"
+#include "nastipc.h"
+#include "cdb.h"
+#include "fqm.h"
+#include "log.h"
+#include "memdb.h"
+#include "mysqldb.h"
+#include "periodic.h"
+
+#include <errno.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <time.h>
+#include <unistd.h>
+
+RCSID("$Id: nastd.c,v 1.8 2001/11/09 15:54:38 shmit Exp $");
+
+char *progname;
+time_t start_time;
+
+static int
+make_u_csock()
+{
+ struct sockaddr_un sunix;
+ int sock;
+
+ sock = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (sock == -1) {
+ log_err("Couldn't create unix socket: %s.", strerror(errno));
+ return -1;
+ }
+
+ memset(&sunix, 0, sizeof(sunix));
+ snprintf(sunix.sun_path, sizeof(sunix.sun_path), config.nast_sock);
+ sunix.sun_family = AF_UNIX;
+ (void)unlink(sunix.sun_path);
+
+ if (bind(sock, (struct sockaddr *)&sunix, sizeof(sunix)) == -1) {
+ log_err("Couldn't bind to unix socket: %s.", strerror(errno));
+ return -1;
+ }
+ (void)listen(sock, 10);
+
+ return sock;
+}
+
+static int
+make_i_csock()
+{
+ struct sockaddr_in sinet;
+ int sock;
+
+ sock = socket(AF_INET, SOCK_STREAM, 0);
+ if (sock == -1) {
+ log_err("Couldn't create inet socket: %S.", strerror(errno));
+ return -1;
+ }
+
+ memset(&sinet, 0, sizeof(sinet));
+ sinet.sin_family = AF_INET;
+ sinet.sin_addr.s_addr = INADDR_ANY;
+ sinet.sin_port = htons(config.tcp_port);
+ if (bind(sock, (struct sockaddr *)&sinet, sizeof(sinet)) == -1) {
+ log_err("Couldn't bind to inet socket: %s.", strerror(errno));
+ return -1;
+ }
+ (void)listen(sock, 10);
+
+ return sock;
+}
+
+static int
+do_client_connect(int sock)
+{
+ struct sockaddr_un saremote;
+ int addrlen;
+ int s;
+
+ addrlen = sizeof(saremote);
+ s = accept(sock, (struct sockaddr *)&saremote, &addrlen);
+ if (s == -1) {
+ log_err("Couldn't accept new connection: %s.",
+ strerror(errno));
+ return -1;
+ }
+ io_new(s);
+
+ return 0;
+}
+
+static int
+init_daemon()
+{
+ sigset_t sigmask;
+
+ log_open();
+ if (cdb_new())
+ return -1;
+ mysqldb_new();
+ if (memdb_new())
+ return -1;
+
+ /*
+ * Turn off SIGPIPE. The calls to write() will return fine with
+ * it off, and this means we don't have to do any signal mojo.
+ */
+ (void)sigemptyset(&sigmask);
+ (void)sigaddset(&sigmask, SIGPIPE);
+ (void)sigprocmask(SIG_BLOCK, &sigmask, NULL);
+
+ /* Now we daemonise. */
+ switch (fork()) {
+ case 0:
+ setsid();
+ close(STDIN_FILENO);
+ close(STDOUT_FILENO);
+ break;
+ case -1:
+ log_err("Couldn't fork into daemon: %s.", strerror(errno));
+ return -1;
+ default:
+ exit(0);
+ }
+
+ return 0;
+}
+
+void
+usage()
+{
+ fprintf(stderr, "Usage: %s [options]\n"
+ "\t-d directory\tSpecify directory for nast files.\n",
+ progname);
+ exit(1);
+}
+
+int
+main(int argc, char *argv[])
+{
+ char ch;
+ int u_csock, i_csock;
+
+ progname = strrchr(argv[0], '/');
+ if (!progname)
+ progname = argv[0];
+ else
+ progname++;
+
+ /* Initialise configuration values from file. */
+ config_setdefaults();
+ while ((ch = getopt(argc, argv, "d:")) != -1) {
+ switch (ch) {
+ case 'd':
+ config.nast_dir = optarg;
+ break;
+ default:
+ usage();
+ /* NOTREACHED */
+ }
+ }
+ if (config_init())
+ return 1;
+
+ if (init_daemon()) {
+ log_err("Couldn't initialise nastd. Exiting.");
+ return 1;
+ }
+
+ u_csock = make_u_csock();
+ if (u_csock == -1)
+ return 1;
+
+ i_csock = make_i_csock();
+ if (i_csock == -1)
+ return 1;
+
+ /* Create the FQM threads. */
+ (void)fqm_new(10);
+
+ /*
+ * Creat a thread that runs periodically.
+ */
+ if (periodic_new() == -1) {
+ log_err("Couldn't start periodic thread. Exiting.");
+ return 1;
+ }
+
+ start_time = time(NULL);
+
+ /*
+ * Main control loop. Sit on the socket and wait for a client to
+ * connect. As soon as it does, make another thread to handle it.
+ */
+ for (;;) {
+ fd_set read_fds;
+ int rc;
+
+ FD_ZERO(&read_fds);
+ FD_SET(i_csock, &read_fds);
+ FD_SET(u_csock, &read_fds);
+
+ rc = select(10, &read_fds, NULL, NULL, NULL);
+ if (rc == -1) {
+ log_err("Couldn't select on sockets: %s.",
+ strerror(errno));
+ sleep(30);
+ }
+
+ if (FD_ISSET(i_csock, &read_fds))
+ (void)do_client_connect(i_csock);
+
+ if (FD_ISSET(u_csock, &read_fds))
+ (void)do_client_connect(u_csock);
+ }
+ return 0;
+}
diff --git a/server/.svn/text-base/nastdio.c.svn-base b/server/.svn/text-base/nastdio.c.svn-base
new file mode 100644
index 0000000..dd7ccd3
--- /dev/null
+++ b/server/.svn/text-base/nastdio.c.svn-base
@@ -0,0 +1,701 @@
+#include "conf.h"
+#include "array.h"
+#include "nastd.h"
+#include "nastdio.h"
+#include "nastipc.h"
+#include "cdb.h"
+#include "fqm.h"
+#include "log.h"
+#include "memdb.h"
+#include "mysqldb.h"
+#include "thread.h"
+
+#include <errno.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <unistd.h>
+
+RCSID("$Id: nastdio.c,v 1.13 2001/10/29 11:17:13 shmit Exp $");
+
+extern fqm_t *fqm;
+
+extern fieldent *db_fields;
+extern char db_key[1024];
+extern short db_fieldcount;
+
+extern time_t start_time;
+
+struct _client_opts {
+ nast_options *opts;
+ rw_mutex_t *opt_lk;
+ thread_t *tid;
+ int sock;
+};
+typedef struct _client_opts client_opts;
+
+static int
+arr_send_response(int sock, short reqid, char r_code, array_t *aa)
+{
+ char *s;
+ char buffer[1024];
+ short l, i;
+ ssize_t wrote;
+
+ /* Save space for buffer length. */
+ l = sizeof(short);
+
+ /* Add request ID. */
+ memcpy(buffer+l, &htons(reqid), sizeof(reqid));
+ l += sizeof(reqid);
+
+ /* Add OK or ERR. */
+ buffer[l] = r_code;
+ l += sizeof(char);
+
+ /*
+ * Build the string to be sent back to the client.
+ */
+ for (i = 0; i < aa->nitems; i++) {
+ int slen;
+
+ s = aa->items[i]->str;
+ slen = aa->items[i]->strlen;
+ if (l+slen > sizeof(buffer)) {
+ log_err("Buffer isn't big enough for all data.");
+ return -1;
+ }
+ memcpy(buffer+l, s, slen);
+ l += slen;
+
+ if (i < aa->nitems-1) {
+ buffer[l] = NASTSEP;
+ l++;
+ }
+ }
+
+ /* Fill in buffer length. */
+ memcpy(buffer, &htons(l), sizeof(short));
+
+ wrote = 0;
+ while (wrote < l) {
+ ssize_t rc;
+
+ rc = write(sock, buffer + wrote, l - wrote);
+ if (rc == -1) {
+ if (errno == EINTR || errno == EAGAIN)
+ continue;
+ log_err("Couldn't write response: %s.",
+ strerror(errno));
+ return -1;
+ }
+ wrote += rc;
+ }
+
+ return 0;
+}
+
+static int
+send_response(int sock, short reqid, char r_code, ...)
+{
+ array_t *aa;
+ int rc;
+ va_list ap;
+
+ aa = array_new();
+ va_start(ap, r_code);
+ if (va_array_add(aa, ap) == -1) {
+ va_end(ap);
+ array_delete(aa);
+ return -1;
+ }
+ va_end(ap);
+
+ rc = arr_send_response(sock, reqid, r_code, aa);
+ array_delete(aa);
+ return rc;
+}
+
+static int
+do_opt_set(int s, short reqid, client_opts *cops, const char *buffer,
+ size_t bufflen)
+{
+ int i;
+
+ rw_mutex_write_lock(cops->opt_lk);
+ for (i = 0; i < bufflen; i += 2) {
+ switch (buffer[i]) {
+ case OPTQCACHE:
+ if (buffer[i+1] == OPTFALSE)
+ cops->opts->use_qcache = NASTFALSE;
+ else
+ cops->opts->use_qcache = NASTTRUE;
+ break;
+ case OPTLOCALDB:
+ if (buffer[i+1] == OPTFALSE)
+ cops->opts->use_localdb = NASTFALSE;
+ else
+ cops->opts->use_localdb = NASTTRUE;
+ break;
+ case OPTFALLASYNC:
+ if (buffer[i+1] == OPTFALSE)
+ cops->opts->fallthrough_async = NASTFALSE;
+ else
+ cops->opts->fallthrough_async = NASTTRUE;
+ break;
+ case OPTALWAYSFALL:
+ if (buffer[i+1] == OPTFALSE)
+ cops->opts->always_fallthrough = NASTFALSE;
+ else
+ cops->opts->always_fallthrough = NASTTRUE;
+ break;
+ case OPTFAILONCE:
+ if (buffer[i+1] == OPTFALSE)
+ cops->opts->fail_once = NASTFALSE;
+ else
+ cops->opts->fail_once = NASTTRUE;
+ break;
+ case OPTNOFALLTHROUGH:
+ if (buffer[i+1] == OPTFALSE)
+ cops->opts->no_fallthrough = NASTFALSE;
+ else
+ cops->opts->no_fallthrough = NASTTRUE;
+ break;
+ default:
+ rw_mutex_unlock(cops->opt_lk);
+ log_err("Unknown option: `0x%x'.\n", buffer[i]);
+ (void)send_response(s, reqid, NASTERR,
+ strlen("Sent unknown option"),
+ "Sent unknown option", ARRTERM);
+ return -1;
+ }
+ }
+ rw_mutex_unlock(cops->opt_lk);
+
+ if (send_response(s, reqid, NASTOK, ARRTERM) == -1)
+ return -1;
+ return 0;
+}
+
+static int
+do_opt_get(int s, short reqid, client_opts *cops)
+{
+ char buffer[512];
+ int bufflen;
+
+ bufflen = 0;
+ rw_mutex_read_lock(cops->opt_lk);
+
+ buffer[bufflen] = OPTQCACHE;
+ if (cops->opts->use_qcache)
+ buffer[bufflen+1] = OPTTRUE;
+ else
+ buffer[bufflen+1] = OPTFALSE;
+ bufflen += 2;
+
+ buffer[bufflen] = OPTLOCALDB;
+ if (cops->opts->use_localdb)
+ buffer[bufflen+1] = OPTTRUE;
+ else
+ buffer[bufflen+1] = OPTFALSE;
+ bufflen += 2;
+
+ buffer[bufflen] = OPTFALLASYNC;
+ if (cops->opts->fallthrough_async)
+ buffer[bufflen+1] = OPTTRUE;
+ else
+ buffer[bufflen+1] = OPTFALSE;
+ bufflen += 2;
+
+ buffer[bufflen] = OPTALWAYSFALL;
+ if (cops->opts->always_fallthrough)
+ buffer[bufflen+1] = OPTTRUE;
+ else
+ buffer[bufflen+1] = OPTFALSE;
+ bufflen += 2;
+
+ buffer[bufflen] = OPTFAILONCE;
+ if (cops->opts->fail_once)
+ buffer[bufflen+1] = OPTTRUE;
+ else
+ buffer[bufflen+1] = OPTFALSE;
+ bufflen += 2;
+
+ buffer[bufflen] = OPTNOFALLTHROUGH;
+ if (cops->opts->no_fallthrough)
+ buffer[bufflen+1] = OPTTRUE;
+ else
+ buffer[bufflen+1] = OPTFALSE;
+ bufflen += 2;
+
+ buffer[bufflen] = '\0';
+
+ rw_mutex_unlock(cops->opt_lk);
+
+ if (send_response(s, reqid, NASTOK, bufflen, buffer, ARRTERM) == -1)
+ return -1;
+ return 0;
+}
+
+static
+REQHANDLER(do_fallthrough)
+{
+ array_t *aa;
+ int rc;
+
+ aa = array_new();
+ if (aa == NULL) {
+ log_info("Couldn't allocate array: %s.", strerror(errno));
+ (void)send_response(req->sock, req->reqid, NASTERR,
+ strlen("Couldn't allocate return buffer"),
+ "Couldn't allocate return buffer", ARRTERM);
+ return -1;
+ }
+
+ rc = mysqldb_get(self, req->req, strlen(req->req), aa);
+ if (rc == -1) {
+ log_info("Couldn't find %s in MySQL DB.", req->req);
+ rc = send_response(req->sock, req->reqid, NASTOK, ARRTERM);
+ array_delete(aa);
+ return -1;
+ }
+
+ if (arr_send_response(req->sock, req->reqid, NASTOK, aa) == -1) {
+ array_delete(aa);
+ return -1;
+ }
+
+ /* If we're this far, the memdb doesn't have this key. Add it. */
+ (void)memdb_add(req->req, strlen(req->req), aa);
+
+ array_delete(aa);
+
+ return 0;
+}
+
+static int
+do_get(int s, short reqid, client_opts *cops, const char *buffer,
+ size_t bufflen)
+{
+ int fallthrough_flag;
+ array_t *aa;
+
+ aa = array_new();
+ if (aa == NULL) {
+ log_info("Couldn't allocate array: %s.", strerror(errno));
+ (void)send_response(s, reqid, NASTERR,
+ strlen("Couldn't allocate return buffer"),
+ "Couldn't allocate return buffer", ARRTERM);
+ return -1;
+ }
+
+ /* Check memdb first. */
+ if (cops->opts->use_qcache && !cops->opts->always_fallthrough) {
+ if (memdb_get(buffer, bufflen, aa) == 0) {
+ log_info("Found `%s' in memdb.", buffer);
+ if (arr_send_response(s, reqid, NASTOK, aa) == -1) {
+ array_delete(aa);
+ return -1;
+ }
+ array_delete(aa);
+ return 0;
+ }
+ }
+
+ /* Just do a CDB lookup for now. */
+ if (!cops->opts->always_fallthrough && cops->opts->use_localdb) {
+ fallthrough_flag = cdb_get(buffer, bufflen, aa);
+ if (fallthrough_flag == -1)
+ log_info("Couldn't find `%s' in CDB file.", buffer);
+ } else {
+ fallthrough_flag = -1;
+ }
+
+ /*
+ * If fallthrough_flag is -1, then the CDB query failed, so
+ * we should check fallthrough.
+ */
+ if (fallthrough_flag == -1 && !cops->opts->no_fallthrough) {
+ request_t *req;
+
+ array_delete(aa);
+
+ log_info("Checking fallthrough for `%s'.", buffer);
+ req = req_new(s, reqid, do_fallthrough,
+ buffer, bufflen);
+ if (req == NULL) {
+ log_err("Couldn't build request for FQM.");
+ send_response(s, reqid, NASTERR,
+ strlen("Couldn't build FQM req"),
+ "Couldn't build FQM req", ARRTERM);
+ return -1;
+ }
+
+ if (fqm_push(fqm, req) == -1) {
+ send_response(s, reqid, NASTERR,
+ strlen("Couldn't add req to FQM"),
+ "Couldn't add req to FQM", ARRTERM);
+ req_delete(req);
+ return -1;
+ }
+ return 0;
+ }
+
+ if (arr_send_response(s, reqid, NASTOK, aa) == -1) {
+ array_delete(aa);
+ return -1;
+ }
+ array_delete(aa);
+
+ return 0;
+}
+
+static array_t *
+build_array(const char *buff, size_t bufflen)
+{
+ array_t *aa;
+ const char *ep, *sp;
+
+ aa = array_new();
+ if (aa == NULL) {
+ log_err("Couldn't allocate array for input buffer.");
+ return NULL;
+ }
+
+ sp = buff;
+ for (ep = sp; ep <= buff+bufflen; ep++) {
+ if ((ep == buff+bufflen || *ep == NASTSEP) &&
+ ep - sp > 0) {
+ if (array_add(aa, ep-sp, sp, ARRTERM) == -1) {
+ log_err("Couldn't add item to input array.");
+ array_delete(aa);
+ return NULL;
+ }
+ sp = ep+1;
+ }
+ }
+
+ return aa;
+}
+
+static int
+do_update(int s, short reqid, client_opts *cops, const char *buffer,
+ size_t bufflen)
+{
+ array_t *aa;
+ char *key;
+ int keylen;
+
+ for (keylen = 0; keylen < bufflen; keylen++) {
+ if (buffer[keylen] == NASTSEP)
+ break;
+ }
+
+ if (keylen == bufflen) {
+ /* Request only contains key. That's bad. */
+ return send_response(s, reqid, NASTERR,
+ strlen("Need values for update"),
+ "Need values for update", ARRTERM);
+ }
+
+ key = malloc(sizeof(char) * keylen + 1);
+ if (key == NULL) {
+ log_err("Couldn't allocate key for update: %s.",
+ strerror(errno));
+ send_response(s, reqid, NASTERR,
+ strlen("Couldn't allocate key"),
+ "Couldn't allocate key", ARRTERM);
+ return -1;
+ }
+ memcpy(key, buffer, keylen);
+ key[keylen + 1] = '\0';
+
+ aa = build_array(buffer+keylen + 1, bufflen-keylen - 1);
+ if (aa == NULL) {
+ send_response(s, reqid, NASTERR,
+ strlen("Couldn't build your input array"),
+ "Couldn't build your input array", ARRTERM);
+ return -1;
+ }
+
+ if (memdb_upd(key, keylen, aa) == -1) {
+ array_delete(aa);
+ return send_response(s, reqid, NASTERR,
+ strlen("Couldn't update cache"),
+ "Couldn't update cache", ARRTERM);
+ }
+ array_delete(aa);
+
+ return send_response(s, reqid, NASTOK, ARRTERM);
+}
+
+static int
+do_stats(int s, int reqid)
+{
+ array_t *aa;
+ char buffer[512];
+ int up_day, up_hour, up_min, up_sec;
+ time_t uptime;
+
+ aa = array_new();
+
+ /* Gather stats from the various databases. */
+ if (mysqldb_stats(aa) == -1) {
+ send_response(s, reqid, NASTERR,
+ strlen("Couldn't get stats from MySQL"),
+ "Couldn't get stats from MySQL", ARRTERM);
+ array_delete(aa);
+ return -1;
+ }
+
+ if (cdb_stats(aa) == -1) {
+ send_response(s, reqid, NASTERR,
+ strlen("Couldn't get stats from cdb"),
+ "Couldn't get stats from cdb", ARRTERM);
+ array_delete(aa);
+ return -1;
+ }
+
+ if (memdb_stats(aa) == -1) {
+ send_response(s, reqid, NASTERR,
+ strlen("Couldn't get stats from memdb"),
+ "Couldn't get stats from memdb", ARRTERM);
+ array_delete(aa);
+ return -1;
+ }
+
+ uptime = time(NULL) - start_time;
+ up_day = uptime / 86400; uptime -= up_day * 86400;
+ up_hour = uptime / 3600; uptime -= up_hour * 3600;
+ up_min = uptime / 60; uptime -= up_min * 60;
+ up_sec = uptime;
+
+ snprintf(buffer, sizeof(buffer),
+ "Uptime: %d day(s), %02d:%02d:%02d",
+ up_day, up_hour, up_min, up_sec);
+ if (array_add(aa, strlen(buffer), buffer, ARRTERM) == -1) {
+ send_response(s, reqid, NASTERR,
+ strlen("Couldn't return uptime"),
+ "Couldn't return uptime", ARRTERM);
+ array_delete(aa);
+ return -1;
+ }
+
+ snprintf(buffer, sizeof(buffer),
+ "Version: %s", VERSION);
+ if (array_add(aa, strlen(buffer), buffer, ARRTERM) == -1) {
+ send_response(s, reqid, NASTERR,
+ strlen("Couldn't return version"),
+ "Couldn't return version", ARRTERM);
+ array_delete(aa);
+ return -1;
+ }
+
+ /* Now send 'em off. */
+ if (arr_send_response(s, reqid, NASTOK, aa) == -1) {
+ array_delete(aa);
+ return -1;
+ }
+ array_delete(aa);
+ return 0;
+}
+
+static int
+process_cmd(int s, client_opts *cops, const char *buffer, size_t bufflen)
+{
+ const char *p;
+ int l;
+ short len, reqid;
+
+ /*
+ * bufflen must be at least six bytes or we have a screwed up
+ * command.
+ */
+ if (bufflen < sizeof(len) + sizeof(reqid) + 2*sizeof(char)) {
+ log_err("Command sent is too short.");
+ return -1;
+ }
+
+ for (p = buffer; p < buffer+bufflen; p += len) {
+ /* All requests start with a length. */
+ memcpy(&len, p, sizeof(reqid));
+ len = ntohs(len);
+ l = sizeof(len);
+
+ /* Follow with a request ID. */
+ memcpy(&reqid, p+l, sizeof(reqid));
+ reqid = ntohs(reqid);
+ l += sizeof(reqid);
+
+ /* Then have NASTCMD. Make sure it's there. */
+ if (p[l] != NASTCMD) {
+ log_err("Command doesn't start with NASTCMD.");
+ return -1;
+ }
+ l += sizeof(char);
+
+ /* The next byte says what kind of command it is. */
+ switch (p[l++]) {
+ case NASTDIE:
+ return 1;
+ break;
+ case NASTOPTSET:
+ if (do_opt_set(s, reqid, cops, p+l, len-l) == -1)
+ return -1;
+ break;
+ case NASTOPTGET:
+ if (do_opt_get(s, reqid, cops) == -1)
+ return -1;
+ break;
+ case NASTGET:
+ if (do_get(s, reqid, cops, p+l, len-l) == -1)
+ return -1;
+ break;
+ case NASTADD:
+ log_err("Add command not supported yet.");
+ break;
+ case NASTDEL:
+ log_err("Delete command not supported yet.");
+ break;
+ case NASTUPD:
+ if (do_update(s, reqid, cops, p+l, len-l) == -1)
+ return -1;
+ break;
+ case NASTSTATS:
+ if (do_stats(s, reqid) == -1)
+ return -1;
+ break;
+ default:
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+static void
+free_cops(client_opts *cops)
+{
+ if (cops == NULL)
+ return;
+
+ close(cops->sock);
+ if (cops->opts != NULL)
+ free(cops->opts);
+ if (cops->opt_lk != NULL)
+ free(cops->opt_lk);
+ if (cops->tid != NULL)
+ free(cops->tid);
+ free(cops);
+}
+
+static void *
+io_looper(void *arg)
+{
+ client_opts *cops;
+
+ (void)pthread_detach(pthread_self());
+ cops = (client_opts *)arg;
+
+ for (;;) {
+ ssize_t nbytes;
+ char buffer[1024];
+
+ nbytes = recv(cops->sock, buffer, sizeof(buffer), 0);
+ if (nbytes == -1) {
+ log_err("Couldn't read from socket: %s.",
+ strerror(errno));
+ break;
+ }
+ if (nbytes == 0) {
+ /* Connection has been closed. Terminate. */
+ log_info("Connection closed.");
+ break;
+ }
+ buffer[nbytes] = '\0';
+
+ /* Do command processing on the buffer. */
+ if (process_cmd(cops->sock, cops, buffer, nbytes) == 1)
+ break;
+ }
+
+ free_cops(cops);
+ return NULL;
+}
+
+static void
+set_default_opts(nast_options *opts)
+{
+ opts->use_qcache = NASTTRUE;
+ opts->use_localdb = NASTTRUE;
+ opts->fallthrough_async = NASTFALSE;
+ opts->always_fallthrough = NASTFALSE;
+ opts->fail_once = NASTFALSE;
+ opts->no_fallthrough = NASTFALSE;
+}
+
+int
+io_new(int s)
+{
+ client_opts *cops;
+
+ log_info("Connection opened.");
+
+ cops = malloc(sizeof(client_opts));
+ if (cops == NULL) {
+ log_err("Couldn't allocate client option structure: %s.",
+ strerror(errno));
+ return -1;
+ }
+
+ /* Pre-set these. free_cops() relies on them being set. */
+ cops->opts = NULL;
+ cops->opt_lk = NULL;
+ cops->sock = s;
+ cops->tid = NULL;
+
+ cops->opts = malloc(sizeof(nast_options));
+ if (cops->opts == NULL) {
+ log_err("Couldn't allocate client options structure: %s.",
+ strerror(errno));
+ free_cops(cops);
+ return -1;
+ }
+ set_default_opts(cops->opts);
+
+ cops->opt_lk = malloc(sizeof(rw_mutex_t));
+ if (cops->opt_lk == NULL) {
+ log_err("Couldn't allocate client options mutex: %s.",
+ strerror(errno));
+ free_cops(cops);
+ return -1;
+ }
+
+ if (rw_mutex_new(cops->opt_lk)) {
+ log_err("Couldn't initialise client options mutex: %s.",
+ strerror(errno));
+ free_cops(cops);
+ return -1;
+ }
+
+ cops->tid = malloc(sizeof(thread_t));
+ if (cops->tid == NULL) {
+ log_err("Couldn't allocate initial client thread: %s.",
+ strerror(errno));
+ free_cops(cops);
+ return -1;
+ }
+
+ if (thread_new(cops->tid, io_looper, cops)) {
+ log_err("Couldn't start initial looper thread: %s.",
+ strerror(errno));
+ free_cops(cops);
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/server/.svn/text-base/nastdio.h.svn-base b/server/.svn/text-base/nastdio.h.svn-base
new file mode 100644
index 0000000..e3375f6
--- /dev/null
+++ b/server/.svn/text-base/nastdio.h.svn-base
@@ -0,0 +1,14 @@
+/* $Id: nastdio.h,v 1.1 2000/09/13 20:21:34 shmit Exp $ */
+
+#ifndef NASTDIO_H
+#define NASTDIO_H
+
+struct _fieldent {
+ char *name;
+ short index;
+};
+typedef struct _fieldent fieldent;
+
+int io_new(int sock);
+
+#endif
diff --git a/server/.svn/text-base/periodic.c.svn-base b/server/.svn/text-base/periodic.c.svn-base
new file mode 100644
index 0000000..9a94edb
--- /dev/null
+++ b/server/.svn/text-base/periodic.c.svn-base
@@ -0,0 +1,118 @@
+#include "conf.h"
+#include "cdb.h"
+#include "log.h"
+#include "memdb.h"
+#include "mysqldb.h"
+#include "thread.h"
+#include "periodic.h"
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+RCSID("$Id: periodic.c,v 1.9 2000/05/17 21:52:34 shmit Exp $");
+
+struct _per_thread_t {
+ cond_t *cond;
+ thread_t *tid;
+};
+typedef struct _per_thread_t per_thread_t;
+
+static void *
+periodic_looper(void *arg)
+{
+ per_thread_t *self;
+ int count;
+
+ self = (per_thread_t *)arg;
+ pthread_detach(pthread_self());
+
+ count = 0;
+ for (;;) {
+ struct timespec ts;
+
+ clock_gettime(CLOCK_REALTIME, &ts);
+ ts.tv_sec += 1;
+ (void)cond_timedwait(self->cond, &ts);
+
+ count++;
+ if (count == PERIODICITY) {
+ cdb_periodic();
+ count = 0;
+ }
+
+ cdb_collate();
+ mysqldb_collate();
+ memdb_collate();
+ }
+ return NULL;
+}
+
+void
+periodic_delete(per_thread_t *thr)
+{
+ if (thr == NULL)
+ return;
+
+ if (thr->cond) {
+ cond_destroy(thr->cond);
+ free(thr->cond);
+ thr->cond = NULL;
+ }
+
+ if (thr->tid) {
+ free(thr->tid);
+ thr->tid = NULL;
+ }
+ free(thr);
+}
+
+int
+periodic_new()
+{
+ per_thread_t *per_thread;
+ int rc;
+
+ per_thread = malloc(sizeof(per_thread_t));
+ if (per_thread == NULL) {
+ log_err("Couldn't allocate periodic thread: %s.",
+ strerror(errno));
+ return -1;
+ }
+ per_thread->cond = NULL;
+ per_thread->tid = NULL;
+
+ per_thread->cond = malloc(sizeof(cond_t));
+ if (per_thread->cond == NULL) {
+ log_err("Couldn't allocate periodic condition: %s.",
+ strerror(errno));
+ periodic_delete(per_thread);
+ return -1;
+ }
+
+ rc = cond_new(per_thread->cond);
+ if (rc) {
+ log_err("Couldn't initialise periodic condition: %s.",
+ strerror(rc));
+ periodic_delete(per_thread);
+ return -1;
+ }
+
+ per_thread->tid = malloc(sizeof(thread_t));
+ if (per_thread->tid == NULL) {
+ log_err("Couldn't allocate periodic thread: %s.",
+ strerror(rc));
+ periodic_delete(per_thread);
+ return -1;
+ }
+
+ rc = thread_new(per_thread->tid, periodic_looper, per_thread);
+ if (rc) {
+ log_err("Couldn't start periodic thread: %s.",
+ strerror(rc));
+ periodic_delete(per_thread);
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/server/.svn/text-base/periodic.h.svn-base b/server/.svn/text-base/periodic.h.svn-base
new file mode 100644
index 0000000..835fd4a
--- /dev/null
+++ b/server/.svn/text-base/periodic.h.svn-base
@@ -0,0 +1,8 @@
+/* $Id: periodic.h,v 1.2 2000/09/13 20:21:35 shmit Exp $ */
+
+#ifndef NAST_PERIODIC_H
+#define NAST_PERIODIC_H
+
+int periodic_new();
+
+#endif
diff --git a/server/.svn/text-base/thread.c.svn-base b/server/.svn/text-base/thread.c.svn-base
new file mode 100644
index 0000000..95737d6
--- /dev/null
+++ b/server/.svn/text-base/thread.c.svn-base
@@ -0,0 +1,216 @@
+#include "conf.h"
+#include "log.h"
+#include "thread.h"
+
+#include <errno.h>
+#include <pthread.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+
+RCSID("$Id: thread.c,v 1.6 2000/03/21 19:18:24 shmit Exp $");
+
+int
+thread_new(thread_t *thread, void *(*thread_func)(void *), void *arg)
+{
+ int rc;
+
+ thread->thread_func = thread_func;
+ rc = pthread_create(&thread->id, NULL, thread_func, arg);
+ if (rc != 0) {
+ log_err("Couldn't create thread: %s.\n", strerror(rc));
+ return -1;
+ }
+ return 0;
+}
+
+void
+thread_kill(thread_t *thread)
+{
+}
+
+int
+thread_reload(thread_t *thread)
+{
+ return 0;
+}
+
+int
+mutex_new(mutex_t *lock)
+{
+ return pthread_mutex_init(lock, NULL);
+}
+
+int
+mutex_delete(mutex_t *lock)
+{
+ return pthread_mutex_destroy(lock);
+}
+
+int
+mutex_lock(mutex_t *lock)
+{
+ return pthread_mutex_lock(lock);
+}
+
+int
+mutex_unlock(mutex_t *lock)
+{
+ return pthread_mutex_unlock(lock);
+}
+
+int
+rw_mutex_new(rw_mutex_t *lock)
+{
+ int rc;
+
+ rc = mutex_new(&lock->lock);
+ if (rc)
+ return rc;
+ rc = pthread_cond_init(&lock->read_sig, NULL);
+ if (rc) {
+ mutex_delete(&lock->lock);
+ return rc;
+ }
+ rc = pthread_cond_init(&lock->write_sig, NULL);
+ if (rc) {
+ mutex_delete(&lock->lock);
+ pthread_cond_destroy(&lock->read_sig);
+ return rc;
+ }
+ lock->state = 0;
+ lock->blocked_writers = 0;
+
+ return 0;
+}
+
+int
+rw_mutex_read_lock(rw_mutex_t *lock)
+{
+ int rc;
+
+ if (lock == NULL)
+ return EINVAL;
+
+ rc = mutex_lock(&lock->lock);
+ if (rc)
+ return rc;
+
+ /* Make sure the writers go before the readers. */
+ while (lock->blocked_writers || lock->state < 0) {
+ rc = pthread_cond_wait(&lock->read_sig, &lock->lock);
+ if (rc) {
+ mutex_unlock(&lock->lock);
+ return rc;
+ }
+ }
+ lock->state++;
+ mutex_unlock(&lock->lock);
+
+ return rc;
+}
+
+int
+rw_mutex_write_lock(rw_mutex_t *lock)
+{
+ int rc;
+
+ if (lock == NULL)
+ return EINVAL;
+
+ rc = mutex_lock(&lock->lock);
+ if (rc)
+ return rc;
+
+ /* Wait for no readers on the lock. */
+ while (lock->state != 0) {
+ lock->blocked_writers++;
+ rc = pthread_cond_wait(&lock->write_sig, &lock->lock);
+ lock->blocked_writers--;
+ if (rc) {
+ mutex_unlock(&lock->lock);
+ return rc;
+ }
+ }
+ lock->state = -1;
+
+ mutex_unlock(&lock->lock);
+ return rc;
+}
+
+int
+rw_mutex_unlock(rw_mutex_t *lock)
+{
+ int rc;
+
+ if (lock == NULL)
+ return EINVAL;
+
+ rc = mutex_lock(&lock->lock);
+ if (rc)
+ return rc;
+
+ if (lock->state > 0) {
+ /* We have an open read lock. */
+ if (--lock->state == 0 && lock->blocked_writers)
+ rc = pthread_cond_signal(&lock->write_sig);
+ } else if (lock->state < 0) {
+ /* We have an open writer lock. */
+ lock->state = 0;
+ if (lock->blocked_writers)
+ rc = pthread_cond_signal(&lock->write_sig);
+ else
+ rc = pthread_cond_broadcast(&lock->read_sig);
+ } else
+ rc = EINVAL;
+
+ mutex_unlock(&lock->lock);
+ return rc;
+}
+
+int
+cond_new(cond_t *cond)
+{
+ int rc;
+
+ if (cond == NULL)
+ return EINVAL;
+
+ rc = pthread_cond_init(&cond->id, NULL);
+ if (rc)
+ return rc;
+
+ rc = mutex_new(&cond->lock);
+ if (rc)
+ pthread_cond_destroy(&cond->id);
+
+ return rc;
+}
+
+int
+cond_signal(cond_t *cond)
+{
+ return pthread_cond_signal(&cond->id);
+}
+
+int
+cond_wait(cond_t *cond)
+{
+ return pthread_cond_wait(&cond->id, &cond->lock);
+}
+
+int
+cond_timedwait(cond_t *cond, const struct timespec *abstime)
+{
+ return pthread_cond_timedwait(&cond->id, &cond->lock, abstime);
+}
+
+int
+cond_destroy(cond_t *cond)
+{
+ return pthread_cond_destroy(&cond->id);
+}
diff --git a/server/.svn/text-base/thread.h.svn-base b/server/.svn/text-base/thread.h.svn-base
new file mode 100644
index 0000000..0aadef4
--- /dev/null
+++ b/server/.svn/text-base/thread.h.svn-base
@@ -0,0 +1,49 @@
+/* $Id: thread.h,v 1.5 2000/03/15 00:09:25 shmit Exp $ */
+
+#ifndef THREAD_H
+# define THREAD_H
+
+#include "conf.h"
+
+#include <pthread.h>
+
+typedef pthread_t thread_id;
+typedef pthread_mutex_t mutex_t;
+
+struct _rwlock {
+ mutex_t lock;
+ int state;
+ pthread_cond_t read_sig;
+ pthread_cond_t write_sig;
+ int blocked_writers;
+};
+typedef struct _rwlock rw_mutex_t;
+
+struct thread {
+ void *(*thread_func)(void *arg);
+ pthread_t id;
+};
+typedef struct thread thread_t;
+
+struct cond {
+ pthread_cond_t id;
+ mutex_t lock;
+};
+typedef struct cond cond_t;
+
+int thread_new(thread_t *thread, void *(*thread_func)(void *), void *arg);
+void thread_kill(thread_t *thread);
+int thread_reload(thread_t *thread);
+int mutex_new(mutex_t *lock);
+int mutex_lock(mutex_t *lock);
+int mutex_unlock(mutex_t *lock);
+int rw_mutex_new(rw_mutex_t *lock);
+int rw_mutex_read_lock(rw_mutex_t *lock);
+int rw_mutex_write_lock(rw_mutex_t *lock);
+int rw_mutex_unlock(rw_mutex_t *lock);
+int cond_new(cond_t *cond);
+int cond_signal(cond_t *cond);
+int cond_wait(cond_t *cond);
+int cond_timedwait(cond_t *cond, const struct timespec *abstime);
+int cond_destroy(cond_t *cond);
+#endif
diff --git a/server/Makefile b/server/Makefile
new file mode 100644
index 0000000..0d26efe
--- /dev/null
+++ b/server/Makefile
@@ -0,0 +1,32 @@
+# $Id: Makefile,v 1.17 2001/10/04 23:57:09 shmit Exp $
+
+OBJS= array.o nastd.o nastdio.o cdb.o cdb_find.o cdb_hash.o \
+ cdb_unpack.o fqm.o log.o md5.o memdb.o mysqldb.o periodic.o \
+ thread.o ../libconfig/libconfig.a
+
+nastd: ${OBJS}
+ ${PURIFY} ${CC} -o $@ ${OBJS} ${LIBS} ${MYSQL_LIBS}
+
+#
+# Dependencies
+#
+array.o: array.h log.h
+nastd.o: ../include/nastipc.h ../include/config.h nastdio.h \
+ cdb.h log.h memdb.h mysqldb.h periodic.h
+nastdio.o: ../include/nastd.h ../include/nastipc.h array.h \
+ nastdio.h cdb.h fqm.h log.h memdb.h mysqldb.h thread.h
+cdb.o: ../include/config.h array.h nastdio.h cdbpriv.h log.h \
+ memdb.h thread.h
+cdb_find.o: cdbpriv.h
+cdb_hash.o: cdbpriv.h
+cdb_unpack.o: cdbpriv.h
+fqm.o: fqm.h log.h thread.h
+log.o: log.h
+md5.o: log.h md5.h
+memdb.o: ../include/config.h array.h log.h md5.h memdb.h thread.h
+mysqldb.o: array.h nastdio.h log.h mysqldb.h
+periodic.o: cdb.h log.h thread.h periodic.h
+thread.o: thread.h
+
+MKDIR= ../Makefiles
+include ${MKDIR}/build
diff --git a/server/array.c b/server/array.c
new file mode 100644
index 0000000..65766de
--- /dev/null
+++ b/server/array.c
@@ -0,0 +1,149 @@
+#include "conf.h"
+#include "array.h"
+#include "log.h"
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+RCSID("$Id: array.c,v 1.10 2000/10/17 23:38:27 shmit Exp $");
+
+string_t *
+string_new(int slen, char *strdata)
+{
+ string_t *tmp;
+
+ tmp = malloc(sizeof(string_t));
+ if (tmp == NULL) {
+ log_err("Couldn't allocate data for string: %s.",
+ strerror(errno));
+ return NULL;
+ }
+
+ tmp->str = malloc(slen * sizeof(char *));
+ if (tmp->str == NULL) {
+ log_err("Couldn't allocate string data: %s.",
+ strerror(errno));
+ return NULL;
+ }
+ memcpy(tmp->str, strdata, slen);
+ tmp->strlen = slen;
+
+ return tmp;
+}
+
+void
+string_delete(string_t *string)
+{
+ if (string == NULL)
+ return;
+
+ if (string->str != NULL)
+ free(string->str);
+ string->strlen = 0;
+ string->str = NULL;
+ free(string);
+}
+
+array_t *
+array_new()
+{
+ array_t *tmp;
+
+ tmp = malloc(sizeof(array_t));
+ if (tmp == NULL)
+ return NULL;
+
+ tmp->nitems = 0;
+ tmp->items = NULL;
+ return tmp;
+}
+
+void
+array_delete(array_t *aa)
+{
+ int i;
+
+ if (aa == NULL)
+ return;
+
+ for (i = 0; i < aa->nitems; i++)
+ string_delete(aa->items[i]);
+ free(aa->items);
+ aa->items = NULL;
+ free(aa);
+}
+
+int
+va_array_add(array_t *aa, va_list ap)
+{
+ const int GRANULARITY = 10;
+ int slen;
+ char *s;
+
+ slen = va_arg(ap, int);
+ if (slen != ARRTERM)
+ s = va_arg(ap, char *);
+ else
+ s = NULL;
+ while (s) {
+ if (aa->nitems % GRANULARITY == 0) {
+ aa->items = realloc(aa->items, sizeof(string_t *) *
+ (GRANULARITY + aa->nitems));
+ if (aa->items == NULL)
+ return -1;
+ }
+ aa->nitems++;
+ aa->items[aa->nitems-1] = string_new(slen, s);
+ if (aa->items[aa->nitems-1] == NULL)
+ return -1;
+ slen = va_arg(ap, int);
+ if (slen != ARRTERM)
+ s = va_arg(ap, char *);
+ else
+ s = NULL;
+ }
+
+ return 0;
+}
+
+int
+array_add(array_t *aa, ...)
+{
+ va_list ap;
+ int rc;
+
+ va_start(ap, aa);
+ rc = va_array_add(aa, ap);
+ va_end(ap);
+
+ return rc;
+}
+
+int
+array_dup(array_t *dst, array_t *src)
+{
+ int i;
+
+ if (dst == NULL)
+ return -1;
+
+ dst->nitems = src->nitems;
+ dst->items = malloc(dst->nitems * sizeof(string_t *));
+ if (dst->items == NULL) {
+ log_err("Couldn't allocate dup array items list: %s.",
+ strerror(errno));
+ return -1;
+ }
+
+ for (i = 0; i < dst->nitems; i++) {
+ dst->items[i] = string_new(src->items[i]->strlen,
+ src->items[i]->str);
+ if (dst->items[i] == NULL) {
+ dst->nitems = i;
+ return -1;
+ }
+ }
+
+ return 0;
+}
diff --git a/server/array.h b/server/array.h
new file mode 100644
index 0000000..83b4ee1
--- /dev/null
+++ b/server/array.h
@@ -0,0 +1,31 @@
+/* $Id: array.h,v 1.4 2000/10/17 23:38:27 shmit Exp $ */
+
+#ifndef ARRAY_H
+#define ARRAY_H
+
+#include <stdarg.h>
+
+#define ARRTERM -1
+
+struct _string_t {
+ char *str;
+ int strlen;
+};
+typedef struct _string_t string_t;
+
+struct _array_t {
+ int nitems;
+ string_t **items;
+};
+typedef struct _array_t array_t;
+
+string_t *string_new(int slen, char *strdata);
+void string_delete(string_t *string);
+
+array_t *array_new();
+void array_delete(array_t *array);
+int va_array_add(array_t *aa, va_list ap);
+int array_add(array_t *aa, ...);
+int array_dup(array_t *dst, array_t *src);
+
+#endif
diff --git a/server/cdb.c b/server/cdb.c
new file mode 100644
index 0000000..4f55d03
--- /dev/null
+++ b/server/cdb.c
@@ -0,0 +1,324 @@
+#include "conf.h"
+#include "config.h"
+#include "array.h"
+#include "nastdio.h"
+#include "cdbpriv.h"
+#include "log.h"
+#include "memdb.h"
+#include "thread.h"
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <unistd.h>
+
+RCSID("$Id: cdb.c,v 1.64 2001/10/19 22:28:07 shmit Exp $");
+
+time_t cdb_mtime;
+
+static char *memcdb = NULL;
+static rw_mutex_t *cdb_lk = NULL;
+static uint32_t cdb_len = 0;
+
+/*
+ * Exportable symbols - the CDB file is authoritative for them, so they
+ * come from here, but nastdio.c is their consumer.
+ */
+char db_key[1024];
+char db_dbn[1024];
+char db_tbl[1024];
+fieldent *db_fields = NULL;
+char db_sep;
+short db_fieldcount = 0;
+
+static int sec_c;
+static float onemin_c, fivemin_c, fifteenmin_c;
+
+static int
+cdb_findbykey(char *mcdb, int len, const char *key, int keylen,
+ char **data, int *dlen)
+{
+ if (cdb_find(mcdb, len, key, keylen, data, dlen) != 1)
+ return -1;
+
+ return 0;
+}
+
+int
+cdb_new()
+{
+ char *newcdb;
+ char *p, *s_p, *e_p;
+ fieldent *newfields;
+ char buffer[1024];
+ char cdbfields_s[1024], newkey[1024], newtbl[1024], newdbn[1024];
+ char newsep;
+ struct stat sb;
+ int i, fieldslen, newfieldcount;
+ int fd;
+ size_t len;
+
+ log_info("Initialising CDB interface.");
+
+ sec_c = 0;
+ onemin_c = fivemin_c = fifteenmin_c = 0;
+
+ if (!cdb_lk) {
+ cdb_lk = malloc(sizeof(rw_mutex_t));
+ if (!cdb_lk) {
+ log_err("Couldn't allocate CDB mutex: %s.",
+ strerror(errno));
+ return -1;
+ }
+ if (rw_mutex_new(cdb_lk)) {
+ log_err("Couldn't initialse CDB mutex: %s.",
+ strerror(errno));
+ return -1;
+ }
+ }
+
+ snprintf(buffer, sizeof(buffer), "%s/%s",
+ config.nast_dir, config.nast_cdb_file);
+ fd = open(buffer, O_RDONLY);
+ if (fd == -1) {
+ log_err("Couldn't open %s: %s.", buffer, strerror(errno));
+ return -1;
+ }
+
+ if (fstat(fd, &sb) == -1) {
+ log_err("Couldn't stat %s: %s.", buffer, strerror(errno));
+ return -1;
+ }
+ len = sb.st_size;
+
+ newcdb = mmap(NULL, len, PROT_READ, MAP_SHARED, fd, 0);
+ if (newcdb == MAP_FAILED) {
+ log_err("Couldn't mmap %s: %s.", buffer, strerror(errno));
+ return -1;
+ }
+
+ (void)close(fd);
+
+ /* Get the key out of the CDB, so clients know how to search. */
+ if (cdb_findbykey(newcdb, len, "_KEY_", strlen("_KEY_"),
+ &p, &i) == -1) {
+ log_err("Couldn't find `_KEY_' in the CDB.");
+ munmap(newcdb, len);
+ return -1;
+ }
+
+ if (i > sizeof(newkey)) {
+ log_err("Couldn't copy key information from the CDB.");
+ munmap(newcdb, len);
+ return -1;
+ }
+ memcpy(newkey, p, i);
+ newkey[i] = '\0';
+
+ /* Get the dbname out of the CDB, so mysql knows which db to use. */
+ if (cdb_findbykey(newcdb, len, "_DB_", strlen("_DB_"), &p, &i) == -1) {
+ log_err("Warning: Couldn't find `_DB_' in the CDB.");
+ strncpy(newdbn, DBNAME, sizeof(newdbn));
+ } else {
+ if (i > sizeof(newdbn)) {
+ log_err("Couldn't copy database information "
+ "from the CDB.");
+ munmap(newcdb, len);
+ return -1;
+ }
+ memcpy(newdbn, p, i);
+ newdbn[i] = '\0';
+ }
+
+ /* Get the table out of the CDB, so mysql knows which to use. */
+ if (cdb_findbykey(newcdb, len, "_TABLE_", strlen("_TABLE_"),
+ &p, &i) == -1) {
+ log_err("Warning: Couldn't find `_TABLE_' in the CDB.");
+ strncpy(newtbl, DBTBL, sizeof(newtbl));
+ } else {
+ if (i > sizeof(newtbl)) {
+ log_err("Couldn't copy table information "
+ "from the CDB.");
+ munmap(newcdb, len);
+ return -1;
+ }
+ memcpy(newtbl, p, i);
+ newtbl[i] = '\0';
+ }
+
+ /* Get the delimiter out of the CDB file. */
+ if (cdb_findbykey(newcdb, len, "_DELIM_", strlen("_DELIM_"),
+ &p, &i) == -1) {
+ log_info("Couldn't find `_DELIM_' in the CDB. Using default.");
+ newsep = ':';
+ } else
+ newsep = *p;
+
+ /* Now get the column names. */
+ if (cdb_findbykey(newcdb, len, "_VALUES_", strlen("_VALUES_"),
+ &p, &i) == -1) {
+ log_err("Couldn't find `_VALUES_' in the CDB.");
+ munmap(newcdb, len);
+ return -1;
+ }
+ if (i >= sizeof(cdbfields_s)) {
+ log_err("Couldn't copy value information from the CDB.");
+ munmap(newcdb, len);
+ return -1;
+ }
+ memcpy(cdbfields_s, p, i);
+ cdbfields_s[i] = '\0';
+ fieldslen = strlen(cdbfields_s);
+
+ /* Fill out cdbfields with an array of field names. */
+ newfieldcount = 0;
+ for (i = 0; i <= fieldslen; i++) {
+ if (cdbfields_s[i] == newsep || i == fieldslen)
+ newfieldcount++;
+ }
+
+ newfields = malloc(sizeof(fieldent) * newfieldcount);
+ if (newfields == NULL) {
+ log_err("Couldn't allocate space for field array: %s.",
+ strerror(errno));
+ munmap(newcdb, len);
+ return -1;
+ }
+
+ s_p = cdbfields_s;
+ for (i = 0; i < newfieldcount; i++) {
+ for (e_p = s_p; *e_p != newsep && *e_p != '\0'; e_p++);
+
+ newfields[i].index = i;
+ newfields[i].name = malloc(e_p - s_p + 1*sizeof(char));
+ if (newfields[i].name == NULL) {
+ /* XXX: clean up. */
+ log_err("Couldn't allocate space for field: %s.",
+ strerror(errno));
+ munmap(newcdb, len);
+ return -1;
+ }
+ memcpy(newfields[i].name, s_p, e_p - s_p);
+ newfields[i].name[e_p - s_p] = '\0';
+ s_p = e_p + 1;
+ }
+
+ rw_mutex_write_lock(cdb_lk);
+ if (memcdb)
+ munmap(memcdb, cdb_len);
+ memcdb = newcdb;
+ cdb_len = (uint32_t)len;
+ cdb_mtime = sb.st_mtime;
+
+ memcpy(db_dbn, newdbn, sizeof(db_dbn));
+ memcpy(db_tbl, newtbl, sizeof(db_tbl));
+ memcpy(db_key, newkey, sizeof(db_key));
+ if (db_fields)
+ free(db_fields);
+ db_fields = newfields;
+ db_sep = newsep;
+ db_fieldcount = newfieldcount;
+ rw_mutex_unlock(cdb_lk);
+
+ log_info("CDB interface initialised.");
+ return 0;
+}
+
+int
+cdb_get(const char *key, int keylen, array_t *aa)
+{
+ char *s_p, *e_p;
+ char *data;
+ int dlen;
+
+ rw_mutex_read_lock(cdb_lk);
+ if (cdb_findbykey(memcdb, cdb_len, key, keylen, &data, &dlen) == -1) {
+ rw_mutex_unlock(cdb_lk);
+ return -1;
+ }
+
+ sec_c++;
+
+ s_p = data; e_p = data;
+ while (e_p <= data+dlen) {
+ if (*e_p == db_sep || e_p == data+dlen) {
+ if (array_add(aa, e_p-s_p, s_p, ARRTERM) == -1) {
+ rw_mutex_unlock(cdb_lk);
+ return -1;
+ }
+ s_p = e_p + 1;
+ }
+ e_p++;
+ }
+
+ rw_mutex_unlock(cdb_lk);
+ return 0;
+}
+
+void
+cdb_periodic()
+{
+ char buffer[1024];
+ struct stat sb;
+
+ snprintf(buffer, sizeof(buffer), "%s/%s",
+ config.nast_dir, config.nast_cdb_file);
+ if (stat(buffer, &sb) == -1) {
+ log_err("PERIODIC: Couldn't stat %s: %s.\n", buffer,
+ strerror(errno));
+ return;
+ }
+
+ if (sb.st_size == 0) {
+ log_err("PERIODIC: WARNING! CDB file is empty!");
+ return;
+ }
+
+ if (cdb_mtime < sb.st_mtime) {
+ log_info("PERIODIC: CDB file changed, reloading.\n");
+ (void)cdb_new();
+
+ /*
+ * Turned off, as now entries are time stamped and
+ * auto-expire when the cdb file is updated.
+ */
+ /* (void)memdb_new(); */
+ return;
+ }
+}
+
+int
+cdb_stats(array_t *statarr)
+{
+ char buffer[512];
+ char tbuff[512];
+ struct tm res;
+
+ snprintf(buffer, sizeof(buffer), "CDB: %.2f, %.2f, %.2f",
+ onemin_c, fivemin_c, fifteenmin_c);
+ if (array_add(statarr, strlen(buffer), buffer, ARRTERM) == -1)
+ return -1;
+
+ /* Convert CDB mtime into a string. */
+ (void)localtime_r(&cdb_mtime, &res);
+ strftime(tbuff, sizeof(tbuff), "%Y%m%d %H:%M:%S", &res);
+
+ snprintf(buffer, sizeof(buffer), "CDB: last updated: %s",
+ tbuff);
+ return array_add(statarr, strlen(buffer), buffer, ARRTERM);
+}
+
+void
+cdb_collate()
+{
+ onemin_c = ((onemin_c * 59) + sec_c) / 60;
+ fivemin_c = ((fivemin_c * 299) + sec_c) / 300;
+ fifteenmin_c = ((fifteenmin_c * 899) + sec_c) / 900;
+ sec_c = 0;
+}
diff --git a/server/cdb.h b/server/cdb.h
new file mode 100644
index 0000000..0da14df
--- /dev/null
+++ b/server/cdb.h
@@ -0,0 +1,15 @@
+/* $Id: cdb.h,v 1.11 2000/05/17 19:32:58 shmit Exp $ */
+
+#ifndef CDB_H
+#define CDB_H
+
+#include "array.h"
+
+int cdb_new();
+int cdb_get(const char *key, int keylen, array_t *dstarr);
+void cdb_periodic();
+
+int cdb_stats(array_t *statarr);
+void cdb_collate();
+
+#endif
diff --git a/server/cdb_find.c b/server/cdb_find.c
new file mode 100644
index 0000000..2fc2bcf
--- /dev/null
+++ b/server/cdb_find.c
@@ -0,0 +1,121 @@
+#include "cdbpriv.h"
+
+#include <errno.h>
+#include <stdio.h>
+#include <sys/types.h>
+
+#ifndef SEEK_SET
+#define SEEK_SET 0
+#endif
+
+RCSID("$Id: cdb_find.c,v 1.2 2000/02/29 19:31:33 shmit Exp $");
+
+static int
+cdb_bread(char **ptr, char *endptr, char *buf, unsigned int len)
+{
+ if ((*ptr)+len > endptr) {
+ errno = EIO;
+ return -1;
+ }
+
+ memcpy(buf, *ptr, len);
+ *ptr += len;
+ return 0;
+}
+
+static int
+match(char **ptr, char *endptr, const char *key, unsigned int len)
+{
+ char buf[32];
+ int n;
+ int i;
+
+ n = sizeof(buf);
+ if (n > len)
+ n = len;
+
+ while (len > 0) {
+ if (cdb_bread(ptr, endptr, buf, n) == -1)
+ return -1;
+
+ for (i = 0; i < n; ++i)
+ if (buf[i] != key[i])
+ return 0;
+ key += n;
+ len -= n;
+ }
+
+ return 1;
+}
+
+int
+cdb_find(char *buff, off_t bufflen, const char *key, int len,
+ char **ret, uint32_t *retlen)
+{
+ char *cur, *end;
+ char packbuf[8];
+ uint32_t pos;
+ uint32_t h;
+ uint32_t lenhash;
+ uint32_t h2;
+ uint32_t loop;
+ uint32_t poskd;
+
+ cur = buff;
+ end = buff + bufflen;
+
+ h = cdb_hash(key, len);
+
+ pos = 8 * (h & 255);
+ cur += pos;
+ if (cur > end) {
+ errno = EIO;
+ return -1;
+ }
+
+ if (cdb_bread(&cur, end, packbuf, 8) == -1)
+ return -1;
+
+ pos = cdb_unpack(packbuf);
+ lenhash = cdb_unpack(packbuf + 4);
+
+ if (!lenhash) return 0;
+ h2 = (h >> 8) % lenhash;
+
+ for (loop = 0; loop < lenhash; ++loop) {
+ cur = buff + (pos + 8 * h2);
+ if (cur > end) {
+ errno = EIO;
+ return -1;
+ }
+ if (cdb_bread(&cur, end, packbuf, 8) == -1)
+ return -1;
+ poskd = cdb_unpack(packbuf + 4);
+ if (!poskd)
+ return 0;
+
+ if (cdb_unpack(packbuf) == h) {
+ cur = buff + poskd;
+ if (cur > end) {
+ errno = EIO;
+ return -1;
+ }
+ if (cdb_bread(&cur, end, packbuf, 8) == -1)
+ return -1;
+ if (cdb_unpack(packbuf) == len) {
+ switch(match(&cur, end, key, len)) {
+ case -1:
+ return -1;
+ case 1:
+ *retlen = cdb_unpack(&packbuf[4]);
+ *ret = cur;
+ return 1;
+ }
+ }
+ }
+ if (++h2 == lenhash)
+ h2 = 0;
+ }
+
+ return 0;
+}
diff --git a/server/cdb_hash.c b/server/cdb_hash.c
new file mode 100644
index 0000000..b664ba9
--- /dev/null
+++ b/server/cdb_hash.c
@@ -0,0 +1,18 @@
+#include "conf.h"
+#include "cdbpriv.h"
+
+RCSID("$Id: cdb_hash.c,v 1.2 2000/02/29 19:31:33 shmit Exp $");
+
+uint32_t
+cdb_hash(const unsigned char *buf, unsigned int len)
+{
+ uint32_t h;
+
+ h = 5381;
+ while (len) {
+ --len;
+ h += (h << 5);
+ h ^= (uint32_t)*buf++;
+ }
+ return h;
+}
diff --git a/server/cdb_unpack.c b/server/cdb_unpack.c
new file mode 100644
index 0000000..14a71b9
--- /dev/null
+++ b/server/cdb_unpack.c
@@ -0,0 +1,16 @@
+#include "conf.h"
+#include "cdbpriv.h"
+
+RCSID("$Id: cdb_unpack.c,v 1.2 2000/02/29 19:31:33 shmit Exp $");
+
+uint32_t
+cdb_unpack(unsigned char *buf)
+{
+ uint32_t num;
+
+ num = buf[3]; num <<= 8;
+ num += buf[2]; num <<= 8;
+ num += buf[1]; num <<= 8;
+ num += buf[0];
+ return num;
+}
diff --git a/server/cdbpriv.h b/server/cdbpriv.h
new file mode 100644
index 0000000..cc398a8
--- /dev/null
+++ b/server/cdbpriv.h
@@ -0,0 +1,15 @@
+/* $Id: cdbpriv.h,v 1.2 2000/02/29 19:15:28 shmit Exp $ */
+
+#ifndef CDBPRIV_H
+#define CDBPRIV_H
+
+#include "conf.h"
+
+#include <sys/types.h>
+
+int cdb_find(char *buff, off_t bufflen, const char *key, int len,
+ char **ret, uint32_t *retlen);
+uint32_t cdb_hash(const unsigned char *buff, unsigned int len);
+uint32_t cdb_unpack(unsigned char *buff);
+
+#endif
diff --git a/server/fqm.c b/server/fqm.c
new file mode 100644
index 0000000..73c9e54
--- /dev/null
+++ b/server/fqm.c
@@ -0,0 +1,448 @@
+#include "conf.h"
+#include "fqm.h"
+#include "log.h"
+#include "mysqldb.h"
+#include "thread.h"
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+RCSID("$Id: fqm.c,v 1.33 2001/10/29 11:17:13 shmit Exp $");
+
+/* Global Variables */
+fqm_t *fqm;
+
+request_t *
+req_new(int sock, short reqid, REQHANDLER((*req_handler)),
+ const char *reqstr, int reqlen)
+{
+ request_t *tmp;
+
+ tmp = malloc(sizeof(request_t));
+ if (tmp == NULL) {
+ log_err("Couldn't allocate request: %s.", strerror(errno));
+ return NULL;
+ }
+
+ tmp->req = malloc((reqlen + 1) * sizeof(char));
+ if (tmp->req == NULL) {
+ log_err("Couldn't allocate request string: %s.",
+ strerror(errno));
+ req_delete(tmp);
+ return NULL;
+ }
+ memcpy(tmp->req, reqstr, reqlen);
+ tmp->req[reqlen] = '\0';
+ tmp->sock = sock;
+ tmp->reqid = reqid;
+ tmp->req_handler = req_handler;
+
+ return tmp;
+}
+
+void
+req_delete(request_t *req)
+{
+ if (req) {
+ if (req->req)
+ free(req->req);
+ free(req);
+ }
+}
+
+static request_t *
+fqm_pop(reqthread_t *req)
+{
+ fqm_t *fqm;
+ request_t *p;
+
+ if (req == NULL || req->fqm == NULL) {
+ mutex_unlock(&req->fqm->q_cond->lock);
+ return NULL;
+ }
+ fqm = req->fqm;
+
+ /* Wait for something to arrive on the queue. */
+ mutex_lock(&req->fqm->q_cond->lock);
+ req->status = IDLE;
+
+ if (fqm->sreq == NULL)
+ cond_wait(fqm->q_cond);
+
+ /* Something waiting on the queue. Scarf it off and unlock. */
+ p = fqm->sreq;
+ if (p == NULL) {
+ mutex_unlock(&fqm->q_cond->lock);
+ return NULL;
+ }
+ fqm->sreq = fqm->sreq->next;
+ fqm->nitems--;
+ req->status = BUSY;
+ mutex_unlock(&fqm->q_cond->lock);
+
+ p->next = NULL;
+ return p;
+}
+
+static void *
+fqm_popper(void *arg)
+{
+ reqthread_t *self;
+ request_t *req;
+
+ /* Detach this thread. */
+ (void)pthread_detach(pthread_self());
+
+ self = (reqthread_t *)arg;
+ for (;;) {
+ req = fqm_pop(self);
+
+ /*
+ * Check to see if we were told to die. If we were,
+ * do so.
+ * Note: there's the possibility for the dropping of
+ * a request here. Que sera sera.
+ */
+ if (self->dieflag)
+ break;
+ if (req == NULL)
+ continue;
+ if (req->req_handler(req, self) == -1)
+ log_info("Couldn't handle request");
+ req_delete(req);
+ }
+
+ log_info("Got a kill flag; dying off.");
+ mutex_lock(&self->fqm->t_cond->lock);
+ self->status = NOALLOC;
+ cond_signal(self->fqm->t_cond);
+ mutex_unlock(&self->fqm->t_cond->lock);
+ return NULL;
+}
+
+static reqthread_t *
+fqm_thr_new(fqm_t *fqm)
+{
+ reqthread_t *tmp;
+
+ tmp = malloc(sizeof(reqthread_t));
+ if (tmp == NULL) {
+ log_err("Couldn't allocate fallthrough thread: %s.",
+ strerror(errno));
+ return NULL;
+ }
+
+ tmp->arg = NULL;
+ tmp->fqm = fqm;
+ tmp->dieflag = 0;
+ tmp->status = NOALLOC;
+
+ tmp->tid = malloc(sizeof(thread_t));
+ if (tmp->tid == NULL) {
+ log_err("Couldn't allocate fallthrough thread: %s.",
+ strerror(errno));
+ free(tmp);
+ return NULL;
+ }
+
+ if (thread_new(tmp->tid, fqm_popper, tmp)) {
+ log_err("Couldn't start fallthrough thread: %s.",
+ strerror(errno));
+ free(tmp->tid);
+ tmp->tid = NULL;
+ free(tmp);
+ return NULL;
+ }
+ tmp->status = IDLE;
+
+ return tmp;
+}
+
+static int
+fqm_thr_wait(fqm_t *fqm)
+{
+ int deadthreads;
+ int i;
+
+ deadthreads = 0;
+
+ for (i = 0; i < fqm->maxitems; i++) {
+ if (fqm->tids[i] && fqm->tids[i]->status == NOALLOC) {
+ mysqldb_connect_close(fqm->tids[i]->arg);
+ if (fqm->tids[i]->tid != NULL)
+ free(fqm->tids[i]->tid);
+ free(fqm->tids[i]);
+ fqm->tids[i] = NULL;
+ deadthreads++;
+ }
+ }
+
+ return deadthreads;
+}
+
+fqm_t *
+fqm_new(int maxitems)
+{
+ fqm_t *tmp;
+ int i;
+
+ tmp = malloc(sizeof(fqm_t));
+ if (tmp == NULL) {
+ log_err("Couldn't create fallthrough queue manager: %s.",
+ strerror(errno));
+ return NULL;
+ }
+
+ tmp->q_cond = NULL;
+ tmp->t_cond = NULL;
+ tmp->tids = NULL;
+ tmp->sreq = NULL; tmp->ereq = NULL;
+ tmp->maxitems = 0;
+ tmp->nitems = 0;
+
+ tmp->q_cond = malloc(sizeof(cond_t));
+ if (tmp->q_cond == NULL) {
+ log_err("Couldn't allocate queue condition variable: %s.",
+ strerror(errno));
+ fqm_delete(tmp);
+ return NULL;
+ }
+
+ if (cond_new(tmp->q_cond)) {
+ log_err("Couldn't initialise queue condition variable: %s.",
+ strerror(errno));
+ fqm_delete(tmp);
+ return NULL;
+ }
+
+ tmp->t_cond = malloc(sizeof(cond_t));
+ if (tmp->t_cond == NULL) {
+ log_err("Couldn't allocate queue destroy condition: %s.",
+ strerror(errno));
+ fqm_delete(tmp);
+ return NULL;
+ }
+
+ if (cond_new(tmp->t_cond)) {
+ log_err("Couldn't initialise queue destroy condition: %s.",
+ strerror(errno));
+ fqm_delete(tmp);
+ return NULL;
+ }
+
+ tmp->tids = malloc(maxitems * sizeof(reqthread_t *));
+ if (tmp->tids == NULL) {
+ log_err("Couldn't allocate fallthrough queue threads: %s.",
+ strerror(errno));
+ fqm_delete(tmp);
+ return NULL;
+ }
+
+ /* Init these so fqm_delete won't break. */
+ for (i = 0; i < maxitems; i++)
+ tmp->tids[i] = NULL;
+
+ /* We cannot adjust max items while threads are busy, so lock. */
+ mutex_lock(&tmp->q_cond->lock);
+ for (i = 0; i < maxitems; i++) {
+ tmp->tids[i] = fqm_thr_new(tmp);
+ if (tmp->tids[i] == NULL) {
+ mutex_unlock(&tmp->q_cond->lock);
+ fqm_delete(tmp);
+ return NULL;
+ }
+ tmp->maxitems++;
+ }
+ mutex_unlock(&tmp->q_cond->lock);
+
+ /* XXX: this may need a lock. */
+ fqm = tmp;
+ return tmp;
+}
+
+/*
+ * Make sure all threads in this fqm are idle. This is an expensive
+ * operation, but that's okay, since this code should only be run when
+ * a client has closed the connection, so we can take as long as we want.
+ *
+ * The queue is locked when coming in to this routine and leaving it.
+ */
+void
+verify_idle_threads(fqm_t *fqm)
+{
+ int i, busy_thread;
+
+ do {
+ busy_thread = 0;
+ for (i = 0; i < fqm->maxitems; i++) {
+ if (fqm->tids[i]->status == BUSY)
+ busy_thread = 1;
+ }
+
+ /*
+ * This is the shitty part.
+ * We unlock the queue and sleep for a bit to give other
+ * threads a chance to finish what they're doing.
+ */
+ if (busy_thread) {
+ mutex_unlock(&fqm->q_cond->lock);
+ sleep(1);
+ mutex_lock(&fqm->q_cond->lock);
+ }
+ } while (busy_thread);
+}
+
+void
+fqm_delete(fqm_t *fqm)
+{
+ int i;
+
+ if (fqm->tids) {
+ /* Tell all threads to quit. */
+ for (i = 0; i < fqm->maxitems; i++) {
+ if (fqm->tids[i] != NULL)
+ fqm->tids[i]->dieflag = 1;
+ }
+
+ /*
+ * Lock queue waiting condition to ensure that no
+ * threads miss this signal. The threads MUST be idle
+ * in order to guarantee this signal is recieved.
+ */
+ mutex_lock(&fqm->q_cond->lock);
+ verify_idle_threads(fqm);
+ pthread_cond_broadcast(&fqm->q_cond->id);
+ mutex_unlock(&fqm->q_cond->lock);
+
+ /* Now wait for them to die off. */
+ i = 0;
+ while (i < fqm->maxitems) {
+ mutex_lock(&fqm->t_cond->lock);
+ i += fqm_thr_wait(fqm);
+ if (i < fqm->maxitems)
+ cond_wait(fqm->t_cond);
+ mutex_unlock(&fqm->t_cond->lock);
+ }
+ free(fqm->tids);
+ fqm->tids = NULL;
+ }
+
+ /*
+ * At this point all fqm threads should be dead.
+ */
+ if (fqm->t_cond) {
+ (void)cond_destroy(fqm->t_cond);
+ free(fqm->t_cond);
+ fqm->t_cond = NULL;
+ }
+
+ if (fqm->q_cond) {
+ (void)cond_destroy(fqm->q_cond);
+ free(fqm->q_cond);
+ fqm->q_cond = NULL;
+ }
+
+ free(fqm);
+}
+
+int
+fqm_changemaxitems(fqm_t *fqm, int maxitems)
+{
+ reqthread_t **new_tidpool;
+ int i, j;
+
+ /*
+ * This code broken. Silently fail here.
+ */
+ return 0;
+
+ if (fqm->maxitems == maxitems)
+ return 0;
+
+ mutex_lock(&fqm->q_cond->lock);
+ /*
+ * Make sure all threads are busy. We can't go changing
+ * data under them.
+ */
+ verify_idle_threads(fqm);
+ new_tidpool = malloc(maxitems * sizeof(reqthread_t *));
+ if (new_tidpool == NULL) {
+ mutex_unlock(&fqm->q_cond->lock);
+ log_err("Couldn't allocate new FQM thread pool: %s.",
+ strerror(errno));
+ return -1;
+ }
+
+ /* Compress old TID pool. */
+ for (j = 0, i = 0; i < fqm->maxitems && j < maxitems; i++) {
+ if (fqm->tids[i]->status != NOALLOC) {
+ new_tidpool[j] = fqm->tids[i];
+ j++;
+ }
+ }
+
+ if (fqm->maxitems < maxitems) {
+ /* Add more threads. */
+ mutex_lock(&fqm->q_cond->lock);
+ for (i = fqm->maxitems; i < maxitems; i++) {
+ new_tidpool[i] = fqm_thr_new(fqm);
+ if (new_tidpool[i] == NULL) {
+ free(new_tidpool);
+ mutex_unlock(&fqm->q_cond->lock);
+ return -1;
+ }
+ }
+ mutex_unlock(&fqm->q_cond->lock);
+ } else if (fqm->maxitems > maxitems) {
+ /* Kill some threads. */
+ int deadthreads;
+
+ /* Tell them to die, then wake 'em up. */
+ for (i = maxitems; i < fqm->maxitems; i++)
+ fqm->tids[i]->dieflag = 1;
+ pthread_cond_broadcast(&fqm->q_cond->id);
+
+ deadthreads = 0;
+ while (deadthreads < fqm->maxitems - maxitems) {
+ mutex_lock(&fqm->t_cond->lock);
+ deadthreads += fqm_thr_wait(fqm);
+ if (deadthreads < fqm->maxitems - maxitems)
+ cond_wait(fqm->t_cond);
+ mutex_unlock(&fqm->t_cond->lock);
+ }
+ }
+
+ free(fqm->tids);
+ fqm->tids = new_tidpool;
+ fqm->maxitems = maxitems;
+ mutex_unlock(&fqm->q_cond->lock);
+ return 0;
+}
+
+int
+fqm_push(fqm_t *fqm, request_t *req)
+{
+ if (fqm->nitems == fqm->maxitems) {
+ log_err("Too many items on the request queue.");
+ return -1;
+ }
+
+ /* Lock the queue and add the item. */
+ mutex_lock(&fqm->q_cond->lock);
+
+ req->next = NULL;
+ if (fqm->sreq == NULL)
+ fqm->sreq = req;
+ else
+ fqm->ereq->next = req;
+ fqm->ereq = req;
+ fqm->nitems++;
+
+ /* Unlock the queue and signal that there's an item on it. */
+ cond_signal(fqm->q_cond);
+ mutex_unlock(&fqm->q_cond->lock);
+ return 0;
+}
diff --git a/server/fqm.h b/server/fqm.h
new file mode 100644
index 0000000..c3c27df
--- /dev/null
+++ b/server/fqm.h
@@ -0,0 +1,50 @@
+/* $Id: fqm.h,v 1.9 2000/11/08 19:50:24 shmit Exp $ */
+
+#ifndef FQM_H
+#define FQM_H
+
+#include "thread.h"
+
+#define REQHANDLER(func) int func(struct _request_t *req, reqthread_t *self)
+
+enum _status_t { IDLE, BUSY, NOALLOC };
+typedef enum _status_t status_t;
+
+struct _reqthread_t {
+ struct _fqm_t *fqm;
+ thread_t *tid;
+ void *arg;
+ int dieflag;
+ status_t status;
+};
+typedef struct _reqthread_t reqthread_t;
+
+struct _request_t {
+ REQHANDLER((*req_handler));
+ char *req;
+ int sock;
+ short reqid;
+ struct _request_t *next;
+};
+typedef struct _request_t request_t;
+
+struct _fqm_t {
+ cond_t *q_cond;
+ cond_t *t_cond;
+ request_t *sreq, *ereq;
+ reqthread_t **tids;
+ int maxitems;
+ int nitems;
+};
+typedef struct _fqm_t fqm_t;
+
+request_t *req_new(int sock, short reqid, REQHANDLER((*req_handler)),
+ const char *reqstr, int reqlen);
+void req_delete(request_t *req);
+
+fqm_t *fqm_new(int maxitems);
+void fqm_delete(fqm_t *fqm);
+int fqm_changemaxitems(fqm_t *fqm, int maxitems);
+int fqm_push(fqm_t *fqm, request_t *req);
+
+#endif
diff --git a/server/log.c b/server/log.c
new file mode 100644
index 0000000..612156d
--- /dev/null
+++ b/server/log.c
@@ -0,0 +1,99 @@
+#include "conf.h"
+#include "log.h"
+
+#include <pthread.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <sys/time.h>
+#include <syslog.h>
+#include <time.h>
+#include <unistd.h>
+
+RCSID("$Id: log.c,v 1.8 2000/11/13 18:47:17 shmit Exp $");
+
+extern char *progname;
+
+#if 0
+FILE *logf = NULL;
+#endif
+
+static void log_priority(int priority, const char *fmt, va_list args);
+
+/*
+ * Log a message with a given priority.
+ */
+void
+log_priority(int priority, const char *fmt, va_list args)
+{
+ char lbuff[1024];
+#if 0
+ char tbuff[256];
+ struct tm timeptr;
+ time_t tloc;
+#endif
+
+ (void)snprintf(lbuff, sizeof(lbuff),
+ "[tid: %d] %s", (int)pthread_self(), fmt);
+ vsyslog(priority, lbuff, args);
+
+#if 0
+ tloc = time(0);
+ (void)localtime_r(&tloc, &timeptr);
+ (void)strftime(tbuff, sizeof(tbuff), "%Y-%h-%d %H:%M:%S", &timeptr);
+ (void)snprintf(lbuff, sizeof(lbuff), "%s %s[%d:%d] %s\n",
+ tbuff, progname, getpid(), (int)pthread_self(), fmt);
+ (void)vfprintf(logf, lbuff, args);
+#endif
+}
+
+/*
+ * Log an error message.
+ */
+void
+log_err(const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ log_priority(LOG_ERR, fmt, ap);
+ va_end(ap);
+}
+
+/*
+ * Log a warning message.
+ */
+void
+log_warn(const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ log_priority(LOG_WARNING, fmt, ap);
+ va_end(ap);
+}
+
+/*
+ * Log an informational message.
+ */
+void
+log_info(const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ log_priority(LOG_INFO, fmt, ap);
+ va_end(ap);
+}
+
+/*
+ * Initialize logging facilites.
+ */
+void
+log_open()
+{
+ openlog(progname, LOG_PID|LOG_PERROR, LOG_DAEMON);
+
+#if 0
+ logf = fopen("/home/shmit/var/log/nastd.log", "a+");
+#endif
+}
diff --git a/server/log.h b/server/log.h
new file mode 100644
index 0000000..9aff53a
--- /dev/null
+++ b/server/log.h
@@ -0,0 +1,11 @@
+/* $Id: log.h,v 1.1 2000/02/29 00:32:12 shmit Exp $ */
+
+#ifndef LOG_H
+# define LOG_H
+
+void log_open();
+void log_err(const char *fmt, ...);
+void log_warn(const char *fmt, ...);
+void log_info(const char *fmt, ...);
+
+#endif /* LOG_H */
diff --git a/server/md5.c b/server/md5.c
new file mode 100644
index 0000000..b24f966
--- /dev/null
+++ b/server/md5.c
@@ -0,0 +1,308 @@
+/*
+ * Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991.
+ * All rights reserved.
+ *
+ * License to copy and use this software is granted provided that it
+ * is identified as the "RSA Data Security, Inc. MD5 Message-Digest
+ * Algorithm" in all material mentioning or referencing this software
+ * or this function.
+ *
+ * License is also granted to make and use derivative works provided
+ * that such works are identified as "derived from the RSA Data
+ * Security, Inc. MD5 Message-Digest Algorithm" in all material
+ * mentioning or referencing the derived work.
+ *
+ * RSA Data Security, Inc. makes no representations concerning either
+ * the merchantability of this software or the suitability of this
+ * software for any particular purpose. It is provided "as is"
+ * without express or implied warranty of any kind.
+ *
+ * These notices must be retained in any copies of any part of this
+ * documentation and/or software.
+ */
+
+#include "conf.h"
+#include "log.h"
+#include "md5.h"
+
+#include <string.h>
+
+RCSID("$Id: md5.c,v 1.2 2000/03/27 22:23:25 shmit Exp $");
+
+/* Constants for MD5Transform routine. */
+#define S11 7
+#define S12 12
+#define S13 17
+#define S14 22
+#define S21 5
+#define S22 9
+#define S23 14
+#define S24 20
+#define S31 4
+#define S32 11
+#define S33 16
+#define S34 23
+#define S41 6
+#define S42 10
+#define S43 15
+#define S44 21
+
+static void MD5Transform(uint32_t *, const unsigned char *);
+static void Encode(unsigned char *, uint32_t *, unsigned int, int ouputlen);
+static void Decode(uint32_t *, const unsigned char *, unsigned int);
+
+static unsigned char PADDING[64] = {
+ 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+/* F, G, H and I are basic MD5 functions. */
+#define F(x, y, z) (((x) & (y)) | ((~x) & (z)))
+#define G(x, y, z) (((x) & (z)) | ((y) & (~z)))
+#define H(x, y, z) ((x) ^ (y) ^ (z))
+#define I(x, y, z) ((y) ^ ((x) | (~z)))
+
+/* ROTATE_LEFT rotates x left n bits. */
+#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n))))
+
+/*
+ * FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4.
+ * Rotation is separate from addition to prevent recomputation.
+ */
+#define FF(a, b, c, d, x, s, ac) { \
+ (a) += F ((b), (c), (d)) + (x) + (uint32_t)(ac); \
+ (a) = ROTATE_LEFT ((a), (s)); \
+ (a) += (b); \
+ }
+#define GG(a, b, c, d, x, s, ac) { \
+ (a) += G ((b), (c), (d)) + (x) + (uint32_t)(ac); \
+ (a) = ROTATE_LEFT ((a), (s)); \
+ (a) += (b); \
+ }
+#define HH(a, b, c, d, x, s, ac) { \
+ (a) += H ((b), (c), (d)) + (x) + (uint32_t)(ac); \
+ (a) = ROTATE_LEFT ((a), (s)); \
+ (a) += (b); \
+ }
+#define II(a, b, c, d, x, s, ac) { \
+ (a) += I ((b), (c), (d)) + (x) + (uint32_t)(ac); \
+ (a) = ROTATE_LEFT ((a), (s)); \
+ (a) += (b); \
+ }
+
+void
+md5_calc(unsigned char *output, const unsigned char *input, unsigned int inlen,
+ int outputlen)
+{
+ MD5_CTX context;
+
+ MD5Init(&context);
+ MD5Update(&context, input, inlen);
+ MD5Final(output, &context, outputlen);
+}
+
+/*
+ * MD5 initialization. Begins an MD5 operation, writing a new context.
+ */
+void
+MD5Init(MD5_CTX *context)
+{
+ context->count[0] = context->count[1] = 0;
+ /* Load magic initialization constants. */
+ context->state[0] = 0x67452301;
+ context->state[1] = 0xefcdab89;
+ context->state[2] = 0x98badcfe;
+ context->state[3] = 0x10325476;
+}
+
+/*
+ * MD5 block update operation. Continues an MD5 message-digest
+ * operation, processing another message block, and updating the
+ * context.
+ */
+void
+MD5Update(MD5_CTX *context, const unsigned char *input, unsigned int inputLen)
+{
+ unsigned int i, index, partLen;
+
+ /* Compute number of bytes mod 64 */
+ index = (unsigned int)((context->count[0] >> 3) & 0x3F);
+
+ /* Update number of bits */
+ if ((context->count[0] += ((uint32_t)inputLen << 3)) <
+ ((uint32_t)inputLen << 3))
+ context->count[1]++;
+ context->count[1] += ((uint32_t)inputLen >> 29);
+
+ partLen = 64 - index;
+
+ /* Transform as many times as possible. */
+ if (inputLen >= partLen) {
+ memcpy(&context->buffer[index], input, partLen);
+ MD5Transform (context->state, context->buffer);
+
+ for (i = partLen; i + 63 < inputLen; i += 64)
+ MD5Transform (context->state, &input[i]);
+
+ index = 0;
+ } else
+ i = 0;
+
+ /* Buffer remaining input */
+ memcpy(&context->buffer[index], &input[i], inputLen-i);
+}
+
+/*
+ * MD5 finalization. Ends an MD5 message-digest operation, writing the
+ * the message digest and zeroizing the context.
+ */
+void
+MD5Final(unsigned char *digest, MD5_CTX *context, int digestlen)
+{
+ unsigned char bits[8];
+ unsigned int index, padLen;
+
+ /* Save number of bits */
+ Encode(bits, context->count, 8, sizeof(bits));
+
+ /* Pad out to 56 mod 64. */
+ index = (context->count[0] >> 3) & 0x3f;
+ padLen = (index < 56) ? (56 - index) : (120 - index);
+ MD5Update(context, PADDING, padLen);
+
+ /* Append length (before padding) */
+ MD5Update(context, bits, 8);
+
+ /* Store state in digest */
+ Encode(digest, context->state, 16, digestlen);
+
+ /* Zeroize sensitive information. */
+ memset(context, 0, sizeof(MD5_CTX));
+}
+
+/*
+ * MD5 basic transformation. Transforms state based on block.
+ */
+static void
+MD5Transform(uint32_t *state, const unsigned char *block)
+{
+ uint32_t a = state[0], b = state[1], c = state[2], d = state[3], x[16];
+
+ Decode (x, block, 64);
+
+ /* Round 1 */
+ FF(a, b, c, d, x[ 0], S11, 0xd76aa478); /* 1 */
+ FF(d, a, b, c, x[ 1], S12, 0xe8c7b756); /* 2 */
+ FF(c, d, a, b, x[ 2], S13, 0x242070db); /* 3 */
+ FF(b, c, d, a, x[ 3], S14, 0xc1bdceee); /* 4 */
+ FF(a, b, c, d, x[ 4], S11, 0xf57c0faf); /* 5 */
+ FF(d, a, b, c, x[ 5], S12, 0x4787c62a); /* 6 */
+ FF(c, d, a, b, x[ 6], S13, 0xa8304613); /* 7 */
+ FF(b, c, d, a, x[ 7], S14, 0xfd469501); /* 8 */
+ FF(a, b, c, d, x[ 8], S11, 0x698098d8); /* 9 */
+ FF(d, a, b, c, x[ 9], S12, 0x8b44f7af); /* 10 */
+ FF(c, d, a, b, x[10], S13, 0xffff5bb1); /* 11 */
+ FF(b, c, d, a, x[11], S14, 0x895cd7be); /* 12 */
+ FF(a, b, c, d, x[12], S11, 0x6b901122); /* 13 */
+ FF(d, a, b, c, x[13], S12, 0xfd987193); /* 14 */
+ FF(c, d, a, b, x[14], S13, 0xa679438e); /* 15 */
+ FF(b, c, d, a, x[15], S14, 0x49b40821); /* 16 */
+
+ /* Round 2 */
+ GG(a, b, c, d, x[ 1], S21, 0xf61e2562); /* 17 */
+ GG(d, a, b, c, x[ 6], S22, 0xc040b340); /* 18 */
+ GG(c, d, a, b, x[11], S23, 0x265e5a51); /* 19 */
+ GG(b, c, d, a, x[ 0], S24, 0xe9b6c7aa); /* 20 */
+ GG(a, b, c, d, x[ 5], S21, 0xd62f105d); /* 21 */
+ GG(d, a, b, c, x[10], S22, 0x2441453); /* 22 */
+ GG(c, d, a, b, x[15], S23, 0xd8a1e681); /* 23 */
+ GG(b, c, d, a, x[ 4], S24, 0xe7d3fbc8); /* 24 */
+ GG(a, b, c, d, x[ 9], S21, 0x21e1cde6); /* 25 */
+ GG(d, a, b, c, x[14], S22, 0xc33707d6); /* 26 */
+ GG(c, d, a, b, x[ 3], S23, 0xf4d50d87); /* 27 */
+ GG(b, c, d, a, x[ 8], S24, 0x455a14ed); /* 28 */
+ GG(a, b, c, d, x[13], S21, 0xa9e3e905); /* 29 */
+ GG(d, a, b, c, x[ 2], S22, 0xfcefa3f8); /* 30 */
+ GG(c, d, a, b, x[ 7], S23, 0x676f02d9); /* 31 */
+ GG(b, c, d, a, x[12], S24, 0x8d2a4c8a); /* 32 */
+
+ /* Round 3 */
+ HH(a, b, c, d, x[ 5], S31, 0xfffa3942); /* 33 */
+ HH(d, a, b, c, x[ 8], S32, 0x8771f681); /* 34 */
+ HH(c, d, a, b, x[11], S33, 0x6d9d6122); /* 35 */
+ HH(b, c, d, a, x[14], S34, 0xfde5380c); /* 36 */
+ HH(a, b, c, d, x[ 1], S31, 0xa4beea44); /* 37 */
+ HH(d, a, b, c, x[ 4], S32, 0x4bdecfa9); /* 38 */
+ HH(c, d, a, b, x[ 7], S33, 0xf6bb4b60); /* 39 */
+ HH(b, c, d, a, x[10], S34, 0xbebfbc70); /* 40 */
+ HH(a, b, c, d, x[13], S31, 0x289b7ec6); /* 41 */
+ HH(d, a, b, c, x[ 0], S32, 0xeaa127fa); /* 42 */
+ HH(c, d, a, b, x[ 3], S33, 0xd4ef3085); /* 43 */
+ HH(b, c, d, a, x[ 6], S34, 0x4881d05); /* 44 */
+ HH(a, b, c, d, x[ 9], S31, 0xd9d4d039); /* 45 */
+ HH(d, a, b, c, x[12], S32, 0xe6db99e5); /* 46 */
+ HH(c, d, a, b, x[15], S33, 0x1fa27cf8); /* 47 */
+ HH(b, c, d, a, x[ 2], S34, 0xc4ac5665); /* 48 */
+
+ /* Round 4 */
+ II(a, b, c, d, x[ 0], S41, 0xf4292244); /* 49 */
+ II(d, a, b, c, x[ 7], S42, 0x432aff97); /* 50 */
+ II(c, d, a, b, x[14], S43, 0xab9423a7); /* 51 */
+ II(b, c, d, a, x[ 5], S44, 0xfc93a039); /* 52 */
+ II(a, b, c, d, x[12], S41, 0x655b59c3); /* 53 */
+ II(d, a, b, c, x[ 3], S42, 0x8f0ccc92); /* 54 */
+ II(c, d, a, b, x[10], S43, 0xffeff47d); /* 55 */
+ II(b, c, d, a, x[ 1], S44, 0x85845dd1); /* 56 */
+ II(a, b, c, d, x[ 8], S41, 0x6fa87e4f); /* 57 */
+ II(d, a, b, c, x[15], S42, 0xfe2ce6e0); /* 58 */
+ II(c, d, a, b, x[ 6], S43, 0xa3014314); /* 59 */
+ II(b, c, d, a, x[13], S44, 0x4e0811a1); /* 60 */
+ II(a, b, c, d, x[ 4], S41, 0xf7537e82); /* 61 */
+ II(d, a, b, c, x[11], S42, 0xbd3af235); /* 62 */
+ II(c, d, a, b, x[ 2], S43, 0x2ad7d2bb); /* 63 */
+ II(b, c, d, a, x[ 9], S44, 0xeb86d391); /* 64 */
+
+ state[0] += a;
+ state[1] += b;
+ state[2] += c;
+ state[3] += d;
+
+ /* Zeroize sensitive information. */
+ memset(x, 0, sizeof(x));
+}
+
+/*
+ * Encodes input (uint32_t) into output (unsigned char). Assumes len is
+ * a multiple of 4.
+ */
+static void
+Encode(unsigned char *output, uint32_t *input, unsigned int len, int outputlen)
+{
+ unsigned int i, j;
+
+ for (i = 0, j = 0; j < len && j <= outputlen-4; i++, j += 4) {
+ output[j] = (unsigned char)(input[i] & 0xff);
+ output[j+1] = (unsigned char)((input[i] >> 8) & 0xff);
+ output[j+2] = (unsigned char)((input[i] >> 16) & 0xff);
+ output[j+3] = (unsigned char)((input[i] >> 24) & 0xff);
+ }
+
+ if (j > outputlen-4 && j < len)
+ log_warn("Digest would be too big, truncated.");
+}
+
+/*
+ * Decodes input (unsigned char) into output (uint32_t). Assumes len is
+ * a multiple of 4.
+ */
+static void
+Decode(uint32_t *output, const unsigned char *input, unsigned int len)
+{
+ unsigned int i, j;
+
+ for (i = 0, j = 0; j < len; i++, j += 4)
+ output[i] = ((uint32_t)input[j]) | (((uint32_t)input[j+1]) << 8) |
+ (((uint32_t)input[j+2]) << 16) |
+ (((uint32_t)input[j+3]) << 24);
+}
diff --git a/server/md5.h b/server/md5.h
new file mode 100644
index 0000000..5f48d41
--- /dev/null
+++ b/server/md5.h
@@ -0,0 +1,48 @@
+/* $Id: md5.h,v 1.2 2000/03/27 22:23:25 shmit Exp $ */
+
+/*
+ * Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991.
+ * All rights reserved.
+ *
+ * License to copy and use this software is granted provided that it
+ * is identified as the "RSA Data Security, Inc. MD5 Message-Digest
+ * Algorithm" in all material mentioning or referencing this software
+ * or this function.
+ *
+ * License is also granted to make and use derivative works provided
+ * that such works are identified as "derived from the RSA Data
+ * Security, Inc. MD5 Message-Digest Algorithm" in all material
+ * mentioning or referencing the derived work.
+ *
+ * RSA Data Security, Inc. makes no representations concerning either
+ * the merchantability of this software or the suitability of this
+ * software for any particular purpose. It is provided "as is"
+ * without express or implied warranty of any kind.
+ *
+ * These notices must be retained in any copies of any part of this
+ * documentation and/or software.
+ */
+
+#ifndef MD5_H
+# define MD5_H
+
+#include "compat.h"
+
+/* POINTER defines a generic pointer type */
+typedef unsigned char *POINTER;
+
+/* MD5 context. */
+typedef struct {
+ uint32_t state[4]; /* state (ABCD) */
+ uint32_t count[2]; /* number of bits, modulo 2^64 */
+ /* (lsb first) */
+ unsigned char buffer[64]; /* input buffer */
+} MD5_CTX;
+
+void MD5Init(MD5_CTX *);
+void MD5Update(MD5_CTX *, const unsigned char *, unsigned int);
+void MD5Final(unsigned char *, MD5_CTX *, int outputlen);
+void md5_calc(unsigned char *output, const unsigned char *input,
+ unsigned int inlen, int outputlen);
+
+#endif /* MD5_H */
diff --git a/server/memdb.c b/server/memdb.c
new file mode 100644
index 0000000..501d0ef
--- /dev/null
+++ b/server/memdb.c
@@ -0,0 +1,501 @@
+#include "conf.h"
+#include "config.h"
+#include "array.h"
+#include "log.h"
+#include "md5.h"
+#include "memdb.h"
+#include "thread.h"
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+RCSID("$Id: memdb.c,v 1.27 2001/10/04 23:57:10 shmit Exp $");
+
+#define HASHSIZE 1024
+
+extern time_t cdb_mtime;
+
+enum _cachetype_t { C_DEL, C_UPD, C_TRYVAL, C_EXPIRE };
+typedef enum _cachetype_t cachetype_t;
+
+struct _mement_t {
+ char *key;
+ int keylen;
+ array_t *vals;
+ time_t *upd_time;
+ cachetype_t type;
+ void *typeval;
+};
+typedef struct _mement_t mement_t;
+
+struct _cachent_t {
+ mement_t ent;
+ struct _cachent_t *next;
+};
+typedef struct _cachent_t cachent_t;
+
+struct _counter_t {
+ float onemin, fivemin, fifteenmin;
+};
+typedef struct _counter_t counter_t;
+
+extern short db_fieldcount;
+
+static cachent_t **memdb = NULL;
+static int memdbsize;
+static rw_mutex_t *memdb_lk = NULL;
+
+static int query_sec, add_sec, del_sec, upd_sec;
+static counter_t queryrate;
+static counter_t addrate;
+static counter_t delrate;
+static counter_t updrate;
+
+static unsigned int
+hashkey(const char *key, int keylen)
+{
+ char md5hash[32];
+ unsigned int rc;
+
+ memset(md5hash, 0, sizeof(md5hash));
+ md5_calc(md5hash, key, keylen, sizeof(md5hash));
+ memcpy(&rc, md5hash, sizeof(rc));
+ return (rc % HASHSIZE);
+}
+
+static void
+ent_delete(mement_t *ent)
+{
+ if (ent == NULL)
+ return;
+
+ if (ent->key != NULL)
+ free(ent->key);
+ if (ent->vals != NULL)
+ array_delete(ent->vals);
+ if (ent->upd_time != NULL)
+ free(ent->upd_time);
+ if (ent->typeval != NULL)
+ free(ent->typeval);
+}
+
+int
+memdb_new()
+{
+ cachent_t **newdb;
+ int i;
+ int rc;
+
+ log_info("Initialising memdb interface.");
+
+ if (memdb_lk == NULL) {
+ memdb_lk = malloc(sizeof(rw_mutex_t));
+ if (memdb_lk == NULL) {
+ log_err("Couldn't allocate memdb mutex: %s.",
+ strerror(errno));
+ return -1;
+ }
+ rc = rw_mutex_new(memdb_lk);
+ if (rc) {
+ log_err("Couldn't initialise memdb mutex: %s.",
+ strerror(rc));
+ return -1;
+ }
+ }
+
+ newdb = malloc(HASHSIZE * sizeof(cachent_t *));
+ if (newdb == NULL) {
+ log_err("Couldn't allocate new memdb: %s.", strerror(errno));
+ return -1;
+ }
+
+ for (i = 0; i < HASHSIZE; i++)
+ newdb[i] = NULL;
+
+ rw_mutex_write_lock(memdb_lk);
+ if (memdb != NULL)
+ memdb_delete(memdb);
+ memdb = newdb;
+ rw_mutex_unlock(memdb_lk);
+
+ query_sec = add_sec = del_sec = upd_sec = 0;
+ queryrate.onemin = queryrate.fivemin = queryrate.fifteenmin = 0;
+ addrate.onemin = addrate.fivemin = addrate.fifteenmin = 0;
+ delrate.onemin = delrate.fivemin = delrate.fifteenmin = 0;
+ updrate.onemin = updrate.fivemin = updrate.fifteenmin = 0;
+ memdbsize = 0;
+
+ log_info("memdb interface initialised.");
+ return 0;
+}
+
+void
+memdb_delete()
+{
+ int i;
+
+ if (memdb == NULL)
+ return;
+
+ for (i = 0; i < HASHSIZE; i++) {
+ cachent_t *p;
+
+ p = memdb[i];
+ while (p != NULL) {
+ cachent_t *q;
+
+ q = p->next;
+ ent_delete(&p->ent);
+ free(p);
+ p = q;
+ }
+ }
+ free(memdb);
+ memdb = NULL;
+}
+
+int
+memdb_add(const char *key, int keylen, array_t *vals)
+{
+ cachent_t *ent;
+ int hkey;
+ time_t curtime;
+
+ ent = malloc(sizeof(cachent_t));
+ if (ent == NULL) {
+ log_err("Couldn't create memdb entry for `%s': %s.",
+ key, strerror(errno));
+ return -1;
+ }
+ ent->next = NULL;
+ ent->ent.key = NULL;
+ ent->ent.type = C_UPD;
+ ent->ent.typeval = NULL;
+ ent->ent.upd_time = NULL;
+
+ ent->ent.key = malloc((keylen + 1) * sizeof(char));
+ if (ent->ent.key == NULL) {
+ log_err("Couldn't create entry key for `%s': %s.",
+ key, strerror(errno));
+ ent_delete(&ent->ent);
+ return -1;
+ }
+ memcpy(ent->ent.key, key, keylen);
+ ent->ent.keylen = keylen;
+
+ ent->ent.vals = array_new();
+ if (ent->ent.vals == NULL) {
+ ent_delete(&ent->ent);
+ return -1;
+ }
+ if (array_dup(ent->ent.vals, vals) == -1) {
+ ent_delete(&ent->ent);
+ return -1;
+ }
+
+ ent->ent.upd_time = malloc(sizeof(time_t));
+ if (ent->ent.upd_time == NULL) {
+ ent_delete(&ent->ent);
+ return -1;
+ }
+ curtime = time(NULL);
+ memcpy(ent->ent.upd_time, &curtime, sizeof(time_t));
+
+ /* Cache non-results for a minute. */
+ if (vals->nitems == 0) {
+ time_t expire;
+
+ ent->ent.type = C_EXPIRE;
+ ent->ent.typeval = malloc(sizeof(time_t));
+ expire = time(NULL);
+ expire += config.null_cache_timeout;
+ memcpy(ent->ent.typeval, &expire, sizeof(time_t));
+ }
+
+ hkey = hashkey(key, keylen);
+ rw_mutex_write_lock(memdb_lk);
+ if (memdb[hkey] == NULL) {
+ memdb[hkey] = ent;
+ memdbsize++;
+ } else {
+ cachent_t *p, *q;
+
+ p = memdb[hkey];
+ q = NULL;
+ while (p->next != NULL &&
+ !(keylen == p->ent.keylen &&
+ memcmp(p->ent.key, key, p->ent.keylen) == 0)) {
+ q = p;
+ p = p->next;
+ }
+ if (keylen == p->ent.keylen &&
+ memcmp(p->ent.key, key, keylen) == 0) {
+ /* Already in memdb. */
+
+ log_info("(add) %s already exists.", key);
+
+ switch (p->ent.type) {
+ case C_EXPIRE:
+ case C_DEL:
+ /*
+ * Deletable entries. Trash 'em and
+ * add 'em.
+ */
+
+ if (q == NULL)
+ memdb[hkey] = ent;
+ else
+ q->next = ent;
+ ent->next = p->next;
+
+ ent_delete(&p->ent);
+ free(p);
+ break;
+ default:
+ /* XXX: Do an update instead of an add. */
+ }
+ } else {
+ p->next = ent;
+ memdbsize++;
+ }
+ }
+
+ add_sec++;
+ rw_mutex_unlock(memdb_lk);
+ return 0;
+}
+
+int
+memdb_del(const char *key, int keylen)
+{
+ cachent_t *p;
+ int hkey;
+
+ hkey = hashkey(key, keylen);
+
+ rw_mutex_read_lock(memdb_lk);
+ p = memdb[hkey];
+ while (p != NULL &&
+ (keylen != p->ent.keylen || memcmp(p->ent.key, key, keylen)))
+ p = p->next;
+
+ if (p == NULL) {
+ array_t *aa;
+
+ /*
+ * Entry not found. We have to add it ourselves.
+ */
+ rw_mutex_unlock(memdb_lk);
+
+ aa = array_new();
+ if (aa == NULL)
+ return -1;
+ if (memdb_add(key, keylen, aa) == -1)
+ return -1;
+
+ /*
+ * The entry has been added - now go back and mark it
+ * for deletion.
+ */
+ return memdb_del(key, keylen);
+ }
+
+ rw_mutex_write_lock(memdb_lk);
+ if (p->ent.typeval != NULL) {
+ free(p->ent.typeval);
+ p->ent.typeval = NULL;
+ }
+
+ p->ent.type = C_DEL;
+ if (p->ent.vals != NULL)
+ array_delete(p->ent.vals);
+
+ del_sec++;
+ rw_mutex_unlock(memdb_lk);
+
+ return 0;
+}
+
+int
+memdb_get(const char *key, int keylen, array_t *vals)
+{
+ cachent_t *p, *q;
+ int hkey;
+
+ hkey = hashkey(key, keylen);
+ rw_mutex_read_lock(memdb_lk);
+ p = memdb[hkey];
+ q = NULL;
+ while (p != NULL &&
+ (keylen != p->ent.keylen || memcmp(p->ent.key, key, keylen))) {
+ q = p;
+ p = p->next;
+ }
+
+ if (p == NULL) {
+ rw_mutex_unlock(memdb_lk);
+ return -1;
+ }
+
+ switch (p->ent.type) {
+ case C_DEL:
+ rw_mutex_unlock(memdb_lk);
+ query_sec++;
+ return 0;
+ case C_EXPIRE:
+ if (p->ent.typeval != NULL) {
+ time_t now, expire;
+
+ now = time(NULL);
+ expire = *((time_t *)p->ent.typeval);
+ if (now >= expire) {
+ ent_delete(&p->ent);
+ if (q != NULL)
+ q->next = p->next;
+ else
+ memdb[hkey] = p->next;
+ free(p);
+ rw_mutex_unlock(memdb_lk);
+ log_info("DEBUG: (get) %s has expired", key);
+ return -1;
+ }
+ }
+ break;
+ default:
+ if (p->ent.upd_time != NULL) {
+ time_t upd_time;
+
+ upd_time = *p->ent.upd_time;
+ if (cdb_mtime > upd_time) {
+ ent_delete(&p->ent);
+ if (q != NULL)
+ q->next = p->next;
+ else
+ memdb[hkey] = p->next;
+ free(p);
+ rw_mutex_unlock(memdb_lk);
+ log_info("DEBUG: (get) %s is out of date", key);
+ return -1;
+ }
+ }
+ break;
+ }
+
+ if (array_dup(vals, p->ent.vals) == -1) {
+ rw_mutex_unlock(memdb_lk);
+ return -1;
+ }
+
+ rw_mutex_unlock(memdb_lk);
+ query_sec++;
+ return 0;
+}
+
+int
+memdb_upd(const char *key, int keylen, array_t *vals)
+{
+ cachent_t *p;
+ int hkey;
+ time_t curtime;
+
+ /* First make sure that we have enough values. */
+ if (vals->nitems != db_fieldcount) {
+ log_err("Update tried with wrong value count.");
+ return -1;
+ }
+
+ hkey = hashkey(key, keylen);
+ rw_mutex_read_lock(memdb_lk);
+ p = memdb[hkey];
+ while (p != NULL &&
+ (keylen != p->ent.keylen || memcmp(p->ent.key, key, keylen)))
+ p = p->next;
+
+ if (p == NULL) {
+ /* Entry not found. Add it ourselves. */
+ rw_mutex_unlock(memdb_lk);
+ return memdb_add(key, keylen, vals);
+ }
+ rw_mutex_unlock(memdb_lk);
+
+ /* Entry found, lets replace it with our copy. */
+
+ rw_mutex_write_lock(memdb_lk);
+ p->ent.type = C_UPD;
+ p->ent.typeval = NULL;
+ curtime = time(NULL);
+ memcpy(p->ent.upd_time, &curtime, sizeof(time_t));
+ array_delete(p->ent.vals);
+
+ p->ent.vals = array_new();
+ if (p->ent.vals == NULL) {
+ rw_mutex_unlock(memdb_lk);
+ return -1;
+ }
+
+ if (array_dup(p->ent.vals, vals) == -1) {
+ rw_mutex_unlock(memdb_lk);
+ return -1;
+ }
+
+ upd_sec++;
+ rw_mutex_unlock(memdb_lk);
+
+ return 0;
+}
+
+int
+memdb_stats(array_t *statarr)
+{
+ char buffer[512];
+
+ /* Convert q/m, q/5m, and q/15m into q/s. */
+ snprintf(buffer, sizeof(buffer), "MEMDB get: %.2f, %.2f, %.2f",
+ queryrate.onemin, queryrate.fivemin, queryrate.fifteenmin);
+ if (array_add(statarr, strlen(buffer), buffer, ARRTERM) == -1)
+ return -1;
+
+ snprintf(buffer, sizeof(buffer), "MEMDB add: %.2f, %.2f, %.2f",
+ addrate.onemin, addrate.fivemin, addrate.fifteenmin);
+ if (array_add(statarr, strlen(buffer), buffer, ARRTERM) == -1)
+ return -1;
+
+ snprintf(buffer, sizeof(buffer), "MEMDB del: %.2f, %.2f, %.2f",
+ delrate.onemin, delrate.fivemin, delrate.fifteenmin);
+ if (array_add(statarr, strlen(buffer), buffer, ARRTERM) == -1)
+ return -1;
+
+ snprintf(buffer, sizeof(buffer), "MEMDB upd: %.2f, %.2f, %.2f",
+ updrate.onemin, updrate.fivemin, updrate.fifteenmin);
+ if (array_add(statarr, strlen(buffer), buffer, ARRTERM) == -1)
+ return -1;
+
+ snprintf(buffer, sizeof(buffer), "MEMDB total entries: %d", memdbsize);
+ return array_add(statarr, strlen(buffer), buffer, ARRTERM);
+}
+
+void
+memdb_collate()
+{
+ queryrate.onemin = ((queryrate.onemin * 59) + query_sec) / 60;
+ queryrate.fivemin = ((queryrate.fivemin * 299) + query_sec) / 300;
+ queryrate.fifteenmin = ((queryrate.fifteenmin * 899) + query_sec) / 900;
+ query_sec = 0;
+
+ addrate.onemin = ((addrate.onemin * 59) + add_sec) / 60;
+ addrate.fivemin = ((addrate.fivemin * 299) + add_sec) / 300;
+ addrate.fifteenmin = ((addrate.fifteenmin * 899) + add_sec) / 900;
+ add_sec = 0;
+
+ delrate.onemin = ((delrate.onemin * 59) + del_sec) / 60;
+ delrate.fivemin = ((delrate.fivemin * 299) + del_sec) / 300;
+ delrate.fifteenmin = ((delrate.fifteenmin * 899) + del_sec) / 900;
+ del_sec = 0;
+
+ updrate.onemin = ((updrate.onemin * 59) + upd_sec) / 60;
+ updrate.fivemin = ((updrate.fivemin * 299) + upd_sec) / 300;
+ updrate.fifteenmin = ((updrate.fifteenmin * 899) + upd_sec) / 900;
+ upd_sec = 0;
+}
diff --git a/server/memdb.h b/server/memdb.h
new file mode 100644
index 0000000..0822e61
--- /dev/null
+++ b/server/memdb.h
@@ -0,0 +1,17 @@
+/* $Id: memdb.h,v 1.8 2000/05/17 19:32:58 shmit Exp $ */
+
+#ifndef MEMDB_H
+#define MEMDB_H
+
+int memdb_new();
+void memdb_delete();
+
+int memdb_add(const char *key, int keylen, array_t *vals);
+int memdb_del(const char *key, int keylen);
+int memdb_get(const char *key, int keylen, array_t *vals);
+int memdb_upd(const char *key, int keylen, array_t *vals);
+
+int memdb_stats(array_t *statarr);
+void memdb_collate();
+
+#endif
diff --git a/server/mysqldb.c b/server/mysqldb.c
new file mode 100644
index 0000000..517c15b
--- /dev/null
+++ b/server/mysqldb.c
@@ -0,0 +1,163 @@
+#include "conf.h"
+#include "array.h"
+#include "config.h"
+#include "nastdio.h"
+#include "log.h"
+#include "mysqldb.h"
+#include "thread.h"
+
+#include <errno.h>
+#include <errmsg.h>
+#include <mysql.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+RCSID("$Id: mysqldb.c,v 1.30 2001/10/10 20:29:47 shmit Exp $");
+
+extern fieldent *db_fields;
+extern char db_key[1024];
+extern char db_dbn[1024];
+extern char db_tbl[1024];
+extern short db_fieldcount;
+
+static char cols[512];
+
+static int sec_c;
+static float onemin_c, fivemin_c, fifteenmin_c;
+
+int
+mysqldb_new()
+{
+ int i;
+
+ sec_c = 0;
+ onemin_c = fivemin_c = fifteenmin_c = 0;
+
+ cols[0] = '\0';
+ for (i = 0; i < db_fieldcount; i++) {
+ strncat(cols, db_fields[i].name, sizeof(cols));
+ if (i < db_fieldcount-1)
+ strncat(cols, ",", sizeof(cols));
+ }
+
+ return 0;
+}
+
+void *
+mysqldb_connect_new()
+{
+ MYSQL *dbh;
+
+ log_info("Initialising MySQL database.");
+
+ dbh = mysql_init(NULL);
+ if (dbh == NULL) {
+ log_err("Couldn't allocate mysql handle: %s.",
+ strerror(errno));
+ return NULL;
+ }
+
+ if (!mysql_connect(dbh, config.mysql_host,
+ config.mysql_user, config.mysql_pass)) {
+ log_err("Couldn't open connection to database: %s.",
+ mysql_error(dbh));
+ mysqldb_connect_close(dbh);
+ return NULL;
+ }
+
+ if (mysql_select_db(dbh, db_dbn)) {
+ log_err("Couldn't open database: %s.",
+ mysql_error(dbh));
+ mysqldb_connect_close(dbh);
+ return NULL;
+ }
+
+ log_info("MySQL database interface initialised.");
+ return (void *)dbh;
+}
+
+void
+mysqldb_connect_close(void *dbh)
+{
+ if (dbh != NULL) {
+ log_info("MySQL connection shutting down.");
+ mysql_close(dbh);
+ free(dbh);
+ }
+}
+
+int
+mysqldb_get(reqthread_t *self, const char *key, int keylen, array_t *aa)
+{
+ MYSQL *dbh;
+ MYSQL_RES *result;
+ char buffer[1024];
+ MYSQL_ROW row;
+ int i, rc;
+
+ snprintf(buffer, sizeof(buffer), DBSELECT, cols, db_tbl, db_key, key);
+
+ if (self->arg == NULL) {
+ self->arg = mysqldb_connect_new();
+ if (self->arg == NULL)
+ return -1;
+ }
+ dbh = (MYSQL *)self->arg;
+
+ rc = mysql_query(dbh, buffer);
+ if (rc) {
+ log_err("Error performing query: %s.", mysql_error(dbh));
+ mysqldb_connect_close(dbh);
+ self->arg = NULL;
+ return -1;
+ }
+
+ result = mysql_use_result(dbh);
+ row = mysql_fetch_row(result);
+ if (row == NULL) {
+ log_info("Couldn't find %s in MySQL database.",
+ key);
+ mysql_free_result(result);
+ sec_c++;
+ return 1;
+ }
+
+ if (mysql_num_fields(result) < db_fieldcount) {
+ log_err("MySQL server didn't return all fields.");
+ mysql_free_result(result);
+ return 0;
+ }
+
+ for (i = 0; i < db_fieldcount; i++) {
+ if (array_add(aa, strlen(row[i]), row[i], ARRTERM) == -1) {
+ mysql_free_result(result);
+ return -1;
+ }
+ }
+ while (mysql_fetch_row(result));
+ mysql_free_result(result);
+
+ sec_c++;
+
+ return 0;
+}
+
+int
+mysqldb_stats(array_t *statarr)
+{
+ char buffer[512];
+
+ snprintf(buffer, sizeof(buffer), "MySQL: %.2f, %.2f, %.2f",
+ onemin_c, fivemin_c, fifteenmin_c);
+ return array_add(statarr, strlen(buffer), buffer, ARRTERM);
+}
+
+void
+mysqldb_collate()
+{
+ onemin_c = ((onemin_c * 59) + sec_c) / 60;
+ fivemin_c = ((fivemin_c * 299) + sec_c) / 300;
+ fifteenmin_c = ((fifteenmin_c * 899) + sec_c) / 900;
+ sec_c = 0;
+}
diff --git a/server/mysqldb.h b/server/mysqldb.h
new file mode 100644
index 0000000..c5c703c
--- /dev/null
+++ b/server/mysqldb.h
@@ -0,0 +1,17 @@
+/* $Id: mysqldb.h,v 1.7 2000/11/08 20:04:55 shmit Exp $ */
+
+#ifndef MYSQLDB_H
+#define MYSQLDB_H
+
+#include "array.h"
+#include "fqm.h"
+
+int mysqldb_new();
+void *mysqldb_connect_new();
+void mysqldb_connect_close(void *dbh);
+int mysqldb_get(reqthread_t *self, const char *key, int keylen, array_t *aa);
+
+int mysqldb_stats(array_t *statarr);
+void mysqldb_collate();
+
+#endif
diff --git a/server/nastd.c b/server/nastd.c
new file mode 100644
index 0000000..3c015a6
--- /dev/null
+++ b/server/nastd.c
@@ -0,0 +1,224 @@
+#include "conf.h"
+#include "config.h"
+#include "nastdio.h"
+#include "nastipc.h"
+#include "cdb.h"
+#include "fqm.h"
+#include "log.h"
+#include "memdb.h"
+#include "mysqldb.h"
+#include "periodic.h"
+
+#include <errno.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <time.h>
+#include <unistd.h>
+
+RCSID("$Id: nastd.c,v 1.8 2001/11/09 15:54:38 shmit Exp $");
+
+char *progname;
+time_t start_time;
+
+static int
+make_u_csock()
+{
+ struct sockaddr_un sunix;
+ int sock;
+
+ sock = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (sock == -1) {
+ log_err("Couldn't create unix socket: %s.", strerror(errno));
+ return -1;
+ }
+
+ memset(&sunix, 0, sizeof(sunix));
+ snprintf(sunix.sun_path, sizeof(sunix.sun_path), config.nast_sock);
+ sunix.sun_family = AF_UNIX;
+ (void)unlink(sunix.sun_path);
+
+ if (bind(sock, (struct sockaddr *)&sunix, sizeof(sunix)) == -1) {
+ log_err("Couldn't bind to unix socket: %s.", strerror(errno));
+ return -1;
+ }
+ (void)listen(sock, 10);
+
+ return sock;
+}
+
+static int
+make_i_csock()
+{
+ struct sockaddr_in sinet;
+ int sock;
+
+ sock = socket(AF_INET, SOCK_STREAM, 0);
+ if (sock == -1) {
+ log_err("Couldn't create inet socket: %S.", strerror(errno));
+ return -1;
+ }
+
+ memset(&sinet, 0, sizeof(sinet));
+ sinet.sin_family = AF_INET;
+ sinet.sin_addr.s_addr = INADDR_ANY;
+ sinet.sin_port = htons(config.tcp_port);
+ if (bind(sock, (struct sockaddr *)&sinet, sizeof(sinet)) == -1) {
+ log_err("Couldn't bind to inet socket: %s.", strerror(errno));
+ return -1;
+ }
+ (void)listen(sock, 10);
+
+ return sock;
+}
+
+static int
+do_client_connect(int sock)
+{
+ struct sockaddr_un saremote;
+ int addrlen;
+ int s;
+
+ addrlen = sizeof(saremote);
+ s = accept(sock, (struct sockaddr *)&saremote, &addrlen);
+ if (s == -1) {
+ log_err("Couldn't accept new connection: %s.",
+ strerror(errno));
+ return -1;
+ }
+ io_new(s);
+
+ return 0;
+}
+
+static int
+init_daemon()
+{
+ sigset_t sigmask;
+
+ log_open();
+ if (cdb_new())
+ return -1;
+ mysqldb_new();
+ if (memdb_new())
+ return -1;
+
+ /*
+ * Turn off SIGPIPE. The calls to write() will return fine with
+ * it off, and this means we don't have to do any signal mojo.
+ */
+ (void)sigemptyset(&sigmask);
+ (void)sigaddset(&sigmask, SIGPIPE);
+ (void)sigprocmask(SIG_BLOCK, &sigmask, NULL);
+
+ /* Now we daemonise. */
+ switch (fork()) {
+ case 0:
+ setsid();
+ close(STDIN_FILENO);
+ close(STDOUT_FILENO);
+ break;
+ case -1:
+ log_err("Couldn't fork into daemon: %s.", strerror(errno));
+ return -1;
+ default:
+ exit(0);
+ }
+
+ return 0;
+}
+
+void
+usage()
+{
+ fprintf(stderr, "Usage: %s [options]\n"
+ "\t-d directory\tSpecify directory for nast files.\n",
+ progname);
+ exit(1);
+}
+
+int
+main(int argc, char *argv[])
+{
+ char ch;
+ int u_csock, i_csock;
+
+ progname = strrchr(argv[0], '/');
+ if (!progname)
+ progname = argv[0];
+ else
+ progname++;
+
+ /* Initialise configuration values from file. */
+ config_setdefaults();
+ while ((ch = getopt(argc, argv, "d:")) != -1) {
+ switch (ch) {
+ case 'd':
+ config.nast_dir = optarg;
+ break;
+ default:
+ usage();
+ /* NOTREACHED */
+ }
+ }
+ if (config_init())
+ return 1;
+
+ if (init_daemon()) {
+ log_err("Couldn't initialise nastd. Exiting.");
+ return 1;
+ }
+
+ u_csock = make_u_csock();
+ if (u_csock == -1)
+ return 1;
+
+ i_csock = make_i_csock();
+ if (i_csock == -1)
+ return 1;
+
+ /* Create the FQM threads. */
+ (void)fqm_new(10);
+
+ /*
+ * Creat a thread that runs periodically.
+ */
+ if (periodic_new() == -1) {
+ log_err("Couldn't start periodic thread. Exiting.");
+ return 1;
+ }
+
+ start_time = time(NULL);
+
+ /*
+ * Main control loop. Sit on the socket and wait for a client to
+ * connect. As soon as it does, make another thread to handle it.
+ */
+ for (;;) {
+ fd_set read_fds;
+ int rc;
+
+ FD_ZERO(&read_fds);
+ FD_SET(i_csock, &read_fds);
+ FD_SET(u_csock, &read_fds);
+
+ rc = select(10, &read_fds, NULL, NULL, NULL);
+ if (rc == -1) {
+ log_err("Couldn't select on sockets: %s.",
+ strerror(errno));
+ sleep(30);
+ }
+
+ if (FD_ISSET(i_csock, &read_fds))
+ (void)do_client_connect(i_csock);
+
+ if (FD_ISSET(u_csock, &read_fds))
+ (void)do_client_connect(u_csock);
+ }
+ return 0;
+}
diff --git a/server/nastdio.c b/server/nastdio.c
new file mode 100644
index 0000000..dd7ccd3
--- /dev/null
+++ b/server/nastdio.c
@@ -0,0 +1,701 @@
+#include "conf.h"
+#include "array.h"
+#include "nastd.h"
+#include "nastdio.h"
+#include "nastipc.h"
+#include "cdb.h"
+#include "fqm.h"
+#include "log.h"
+#include "memdb.h"
+#include "mysqldb.h"
+#include "thread.h"
+
+#include <errno.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <unistd.h>
+
+RCSID("$Id: nastdio.c,v 1.13 2001/10/29 11:17:13 shmit Exp $");
+
+extern fqm_t *fqm;
+
+extern fieldent *db_fields;
+extern char db_key[1024];
+extern short db_fieldcount;
+
+extern time_t start_time;
+
+struct _client_opts {
+ nast_options *opts;
+ rw_mutex_t *opt_lk;
+ thread_t *tid;
+ int sock;
+};
+typedef struct _client_opts client_opts;
+
+static int
+arr_send_response(int sock, short reqid, char r_code, array_t *aa)
+{
+ char *s;
+ char buffer[1024];
+ short l, i;
+ ssize_t wrote;
+
+ /* Save space for buffer length. */
+ l = sizeof(short);
+
+ /* Add request ID. */
+ memcpy(buffer+l, &htons(reqid), sizeof(reqid));
+ l += sizeof(reqid);
+
+ /* Add OK or ERR. */
+ buffer[l] = r_code;
+ l += sizeof(char);
+
+ /*
+ * Build the string to be sent back to the client.
+ */
+ for (i = 0; i < aa->nitems; i++) {
+ int slen;
+
+ s = aa->items[i]->str;
+ slen = aa->items[i]->strlen;
+ if (l+slen > sizeof(buffer)) {
+ log_err("Buffer isn't big enough for all data.");
+ return -1;
+ }
+ memcpy(buffer+l, s, slen);
+ l += slen;
+
+ if (i < aa->nitems-1) {
+ buffer[l] = NASTSEP;
+ l++;
+ }
+ }
+
+ /* Fill in buffer length. */
+ memcpy(buffer, &htons(l), sizeof(short));
+
+ wrote = 0;
+ while (wrote < l) {
+ ssize_t rc;
+
+ rc = write(sock, buffer + wrote, l - wrote);
+ if (rc == -1) {
+ if (errno == EINTR || errno == EAGAIN)
+ continue;
+ log_err("Couldn't write response: %s.",
+ strerror(errno));
+ return -1;
+ }
+ wrote += rc;
+ }
+
+ return 0;
+}
+
+static int
+send_response(int sock, short reqid, char r_code, ...)
+{
+ array_t *aa;
+ int rc;
+ va_list ap;
+
+ aa = array_new();
+ va_start(ap, r_code);
+ if (va_array_add(aa, ap) == -1) {
+ va_end(ap);
+ array_delete(aa);
+ return -1;
+ }
+ va_end(ap);
+
+ rc = arr_send_response(sock, reqid, r_code, aa);
+ array_delete(aa);
+ return rc;
+}
+
+static int
+do_opt_set(int s, short reqid, client_opts *cops, const char *buffer,
+ size_t bufflen)
+{
+ int i;
+
+ rw_mutex_write_lock(cops->opt_lk);
+ for (i = 0; i < bufflen; i += 2) {
+ switch (buffer[i]) {
+ case OPTQCACHE:
+ if (buffer[i+1] == OPTFALSE)
+ cops->opts->use_qcache = NASTFALSE;
+ else
+ cops->opts->use_qcache = NASTTRUE;
+ break;
+ case OPTLOCALDB:
+ if (buffer[i+1] == OPTFALSE)
+ cops->opts->use_localdb = NASTFALSE;
+ else
+ cops->opts->use_localdb = NASTTRUE;
+ break;
+ case OPTFALLASYNC:
+ if (buffer[i+1] == OPTFALSE)
+ cops->opts->fallthrough_async = NASTFALSE;
+ else
+ cops->opts->fallthrough_async = NASTTRUE;
+ break;
+ case OPTALWAYSFALL:
+ if (buffer[i+1] == OPTFALSE)
+ cops->opts->always_fallthrough = NASTFALSE;
+ else
+ cops->opts->always_fallthrough = NASTTRUE;
+ break;
+ case OPTFAILONCE:
+ if (buffer[i+1] == OPTFALSE)
+ cops->opts->fail_once = NASTFALSE;
+ else
+ cops->opts->fail_once = NASTTRUE;
+ break;
+ case OPTNOFALLTHROUGH:
+ if (buffer[i+1] == OPTFALSE)
+ cops->opts->no_fallthrough = NASTFALSE;
+ else
+ cops->opts->no_fallthrough = NASTTRUE;
+ break;
+ default:
+ rw_mutex_unlock(cops->opt_lk);
+ log_err("Unknown option: `0x%x'.\n", buffer[i]);
+ (void)send_response(s, reqid, NASTERR,
+ strlen("Sent unknown option"),
+ "Sent unknown option", ARRTERM);
+ return -1;
+ }
+ }
+ rw_mutex_unlock(cops->opt_lk);
+
+ if (send_response(s, reqid, NASTOK, ARRTERM) == -1)
+ return -1;
+ return 0;
+}
+
+static int
+do_opt_get(int s, short reqid, client_opts *cops)
+{
+ char buffer[512];
+ int bufflen;
+
+ bufflen = 0;
+ rw_mutex_read_lock(cops->opt_lk);
+
+ buffer[bufflen] = OPTQCACHE;
+ if (cops->opts->use_qcache)
+ buffer[bufflen+1] = OPTTRUE;
+ else
+ buffer[bufflen+1] = OPTFALSE;
+ bufflen += 2;
+
+ buffer[bufflen] = OPTLOCALDB;
+ if (cops->opts->use_localdb)
+ buffer[bufflen+1] = OPTTRUE;
+ else
+ buffer[bufflen+1] = OPTFALSE;
+ bufflen += 2;
+
+ buffer[bufflen] = OPTFALLASYNC;
+ if (cops->opts->fallthrough_async)
+ buffer[bufflen+1] = OPTTRUE;
+ else
+ buffer[bufflen+1] = OPTFALSE;
+ bufflen += 2;
+
+ buffer[bufflen] = OPTALWAYSFALL;
+ if (cops->opts->always_fallthrough)
+ buffer[bufflen+1] = OPTTRUE;
+ else
+ buffer[bufflen+1] = OPTFALSE;
+ bufflen += 2;
+
+ buffer[bufflen] = OPTFAILONCE;
+ if (cops->opts->fail_once)
+ buffer[bufflen+1] = OPTTRUE;
+ else
+ buffer[bufflen+1] = OPTFALSE;
+ bufflen += 2;
+
+ buffer[bufflen] = OPTNOFALLTHROUGH;
+ if (cops->opts->no_fallthrough)
+ buffer[bufflen+1] = OPTTRUE;
+ else
+ buffer[bufflen+1] = OPTFALSE;
+ bufflen += 2;
+
+ buffer[bufflen] = '\0';
+
+ rw_mutex_unlock(cops->opt_lk);
+
+ if (send_response(s, reqid, NASTOK, bufflen, buffer, ARRTERM) == -1)
+ return -1;
+ return 0;
+}
+
+static
+REQHANDLER(do_fallthrough)
+{
+ array_t *aa;
+ int rc;
+
+ aa = array_new();
+ if (aa == NULL) {
+ log_info("Couldn't allocate array: %s.", strerror(errno));
+ (void)send_response(req->sock, req->reqid, NASTERR,
+ strlen("Couldn't allocate return buffer"),
+ "Couldn't allocate return buffer", ARRTERM);
+ return -1;
+ }
+
+ rc = mysqldb_get(self, req->req, strlen(req->req), aa);
+ if (rc == -1) {
+ log_info("Couldn't find %s in MySQL DB.", req->req);
+ rc = send_response(req->sock, req->reqid, NASTOK, ARRTERM);
+ array_delete(aa);
+ return -1;
+ }
+
+ if (arr_send_response(req->sock, req->reqid, NASTOK, aa) == -1) {
+ array_delete(aa);
+ return -1;
+ }
+
+ /* If we're this far, the memdb doesn't have this key. Add it. */
+ (void)memdb_add(req->req, strlen(req->req), aa);
+
+ array_delete(aa);
+
+ return 0;
+}
+
+static int
+do_get(int s, short reqid, client_opts *cops, const char *buffer,
+ size_t bufflen)
+{
+ int fallthrough_flag;
+ array_t *aa;
+
+ aa = array_new();
+ if (aa == NULL) {
+ log_info("Couldn't allocate array: %s.", strerror(errno));
+ (void)send_response(s, reqid, NASTERR,
+ strlen("Couldn't allocate return buffer"),
+ "Couldn't allocate return buffer", ARRTERM);
+ return -1;
+ }
+
+ /* Check memdb first. */
+ if (cops->opts->use_qcache && !cops->opts->always_fallthrough) {
+ if (memdb_get(buffer, bufflen, aa) == 0) {
+ log_info("Found `%s' in memdb.", buffer);
+ if (arr_send_response(s, reqid, NASTOK, aa) == -1) {
+ array_delete(aa);
+ return -1;
+ }
+ array_delete(aa);
+ return 0;
+ }
+ }
+
+ /* Just do a CDB lookup for now. */
+ if (!cops->opts->always_fallthrough && cops->opts->use_localdb) {
+ fallthrough_flag = cdb_get(buffer, bufflen, aa);
+ if (fallthrough_flag == -1)
+ log_info("Couldn't find `%s' in CDB file.", buffer);
+ } else {
+ fallthrough_flag = -1;
+ }
+
+ /*
+ * If fallthrough_flag is -1, then the CDB query failed, so
+ * we should check fallthrough.
+ */
+ if (fallthrough_flag == -1 && !cops->opts->no_fallthrough) {
+ request_t *req;
+
+ array_delete(aa);
+
+ log_info("Checking fallthrough for `%s'.", buffer);
+ req = req_new(s, reqid, do_fallthrough,
+ buffer, bufflen);
+ if (req == NULL) {
+ log_err("Couldn't build request for FQM.");
+ send_response(s, reqid, NASTERR,
+ strlen("Couldn't build FQM req"),
+ "Couldn't build FQM req", ARRTERM);
+ return -1;
+ }
+
+ if (fqm_push(fqm, req) == -1) {
+ send_response(s, reqid, NASTERR,
+ strlen("Couldn't add req to FQM"),
+ "Couldn't add req to FQM", ARRTERM);
+ req_delete(req);
+ return -1;
+ }
+ return 0;
+ }
+
+ if (arr_send_response(s, reqid, NASTOK, aa) == -1) {
+ array_delete(aa);
+ return -1;
+ }
+ array_delete(aa);
+
+ return 0;
+}
+
+static array_t *
+build_array(const char *buff, size_t bufflen)
+{
+ array_t *aa;
+ const char *ep, *sp;
+
+ aa = array_new();
+ if (aa == NULL) {
+ log_err("Couldn't allocate array for input buffer.");
+ return NULL;
+ }
+
+ sp = buff;
+ for (ep = sp; ep <= buff+bufflen; ep++) {
+ if ((ep == buff+bufflen || *ep == NASTSEP) &&
+ ep - sp > 0) {
+ if (array_add(aa, ep-sp, sp, ARRTERM) == -1) {
+ log_err("Couldn't add item to input array.");
+ array_delete(aa);
+ return NULL;
+ }
+ sp = ep+1;
+ }
+ }
+
+ return aa;
+}
+
+static int
+do_update(int s, short reqid, client_opts *cops, const char *buffer,
+ size_t bufflen)
+{
+ array_t *aa;
+ char *key;
+ int keylen;
+
+ for (keylen = 0; keylen < bufflen; keylen++) {
+ if (buffer[keylen] == NASTSEP)
+ break;
+ }
+
+ if (keylen == bufflen) {
+ /* Request only contains key. That's bad. */
+ return send_response(s, reqid, NASTERR,
+ strlen("Need values for update"),
+ "Need values for update", ARRTERM);
+ }
+
+ key = malloc(sizeof(char) * keylen + 1);
+ if (key == NULL) {
+ log_err("Couldn't allocate key for update: %s.",
+ strerror(errno));
+ send_response(s, reqid, NASTERR,
+ strlen("Couldn't allocate key"),
+ "Couldn't allocate key", ARRTERM);
+ return -1;
+ }
+ memcpy(key, buffer, keylen);
+ key[keylen + 1] = '\0';
+
+ aa = build_array(buffer+keylen + 1, bufflen-keylen - 1);
+ if (aa == NULL) {
+ send_response(s, reqid, NASTERR,
+ strlen("Couldn't build your input array"),
+ "Couldn't build your input array", ARRTERM);
+ return -1;
+ }
+
+ if (memdb_upd(key, keylen, aa) == -1) {
+ array_delete(aa);
+ return send_response(s, reqid, NASTERR,
+ strlen("Couldn't update cache"),
+ "Couldn't update cache", ARRTERM);
+ }
+ array_delete(aa);
+
+ return send_response(s, reqid, NASTOK, ARRTERM);
+}
+
+static int
+do_stats(int s, int reqid)
+{
+ array_t *aa;
+ char buffer[512];
+ int up_day, up_hour, up_min, up_sec;
+ time_t uptime;
+
+ aa = array_new();
+
+ /* Gather stats from the various databases. */
+ if (mysqldb_stats(aa) == -1) {
+ send_response(s, reqid, NASTERR,
+ strlen("Couldn't get stats from MySQL"),
+ "Couldn't get stats from MySQL", ARRTERM);
+ array_delete(aa);
+ return -1;
+ }
+
+ if (cdb_stats(aa) == -1) {
+ send_response(s, reqid, NASTERR,
+ strlen("Couldn't get stats from cdb"),
+ "Couldn't get stats from cdb", ARRTERM);
+ array_delete(aa);
+ return -1;
+ }
+
+ if (memdb_stats(aa) == -1) {
+ send_response(s, reqid, NASTERR,
+ strlen("Couldn't get stats from memdb"),
+ "Couldn't get stats from memdb", ARRTERM);
+ array_delete(aa);
+ return -1;
+ }
+
+ uptime = time(NULL) - start_time;
+ up_day = uptime / 86400; uptime -= up_day * 86400;
+ up_hour = uptime / 3600; uptime -= up_hour * 3600;
+ up_min = uptime / 60; uptime -= up_min * 60;
+ up_sec = uptime;
+
+ snprintf(buffer, sizeof(buffer),
+ "Uptime: %d day(s), %02d:%02d:%02d",
+ up_day, up_hour, up_min, up_sec);
+ if (array_add(aa, strlen(buffer), buffer, ARRTERM) == -1) {
+ send_response(s, reqid, NASTERR,
+ strlen("Couldn't return uptime"),
+ "Couldn't return uptime", ARRTERM);
+ array_delete(aa);
+ return -1;
+ }
+
+ snprintf(buffer, sizeof(buffer),
+ "Version: %s", VERSION);
+ if (array_add(aa, strlen(buffer), buffer, ARRTERM) == -1) {
+ send_response(s, reqid, NASTERR,
+ strlen("Couldn't return version"),
+ "Couldn't return version", ARRTERM);
+ array_delete(aa);
+ return -1;
+ }
+
+ /* Now send 'em off. */
+ if (arr_send_response(s, reqid, NASTOK, aa) == -1) {
+ array_delete(aa);
+ return -1;
+ }
+ array_delete(aa);
+ return 0;
+}
+
+static int
+process_cmd(int s, client_opts *cops, const char *buffer, size_t bufflen)
+{
+ const char *p;
+ int l;
+ short len, reqid;
+
+ /*
+ * bufflen must be at least six bytes or we have a screwed up
+ * command.
+ */
+ if (bufflen < sizeof(len) + sizeof(reqid) + 2*sizeof(char)) {
+ log_err("Command sent is too short.");
+ return -1;
+ }
+
+ for (p = buffer; p < buffer+bufflen; p += len) {
+ /* All requests start with a length. */
+ memcpy(&len, p, sizeof(reqid));
+ len = ntohs(len);
+ l = sizeof(len);
+
+ /* Follow with a request ID. */
+ memcpy(&reqid, p+l, sizeof(reqid));
+ reqid = ntohs(reqid);
+ l += sizeof(reqid);
+
+ /* Then have NASTCMD. Make sure it's there. */
+ if (p[l] != NASTCMD) {
+ log_err("Command doesn't start with NASTCMD.");
+ return -1;
+ }
+ l += sizeof(char);
+
+ /* The next byte says what kind of command it is. */
+ switch (p[l++]) {
+ case NASTDIE:
+ return 1;
+ break;
+ case NASTOPTSET:
+ if (do_opt_set(s, reqid, cops, p+l, len-l) == -1)
+ return -1;
+ break;
+ case NASTOPTGET:
+ if (do_opt_get(s, reqid, cops) == -1)
+ return -1;
+ break;
+ case NASTGET:
+ if (do_get(s, reqid, cops, p+l, len-l) == -1)
+ return -1;
+ break;
+ case NASTADD:
+ log_err("Add command not supported yet.");
+ break;
+ case NASTDEL:
+ log_err("Delete command not supported yet.");
+ break;
+ case NASTUPD:
+ if (do_update(s, reqid, cops, p+l, len-l) == -1)
+ return -1;
+ break;
+ case NASTSTATS:
+ if (do_stats(s, reqid) == -1)
+ return -1;
+ break;
+ default:
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+static void
+free_cops(client_opts *cops)
+{
+ if (cops == NULL)
+ return;
+
+ close(cops->sock);
+ if (cops->opts != NULL)
+ free(cops->opts);
+ if (cops->opt_lk != NULL)
+ free(cops->opt_lk);
+ if (cops->tid != NULL)
+ free(cops->tid);
+ free(cops);
+}
+
+static void *
+io_looper(void *arg)
+{
+ client_opts *cops;
+
+ (void)pthread_detach(pthread_self());
+ cops = (client_opts *)arg;
+
+ for (;;) {
+ ssize_t nbytes;
+ char buffer[1024];
+
+ nbytes = recv(cops->sock, buffer, sizeof(buffer), 0);
+ if (nbytes == -1) {
+ log_err("Couldn't read from socket: %s.",
+ strerror(errno));
+ break;
+ }
+ if (nbytes == 0) {
+ /* Connection has been closed. Terminate. */
+ log_info("Connection closed.");
+ break;
+ }
+ buffer[nbytes] = '\0';
+
+ /* Do command processing on the buffer. */
+ if (process_cmd(cops->sock, cops, buffer, nbytes) == 1)
+ break;
+ }
+
+ free_cops(cops);
+ return NULL;
+}
+
+static void
+set_default_opts(nast_options *opts)
+{
+ opts->use_qcache = NASTTRUE;
+ opts->use_localdb = NASTTRUE;
+ opts->fallthrough_async = NASTFALSE;
+ opts->always_fallthrough = NASTFALSE;
+ opts->fail_once = NASTFALSE;
+ opts->no_fallthrough = NASTFALSE;
+}
+
+int
+io_new(int s)
+{
+ client_opts *cops;
+
+ log_info("Connection opened.");
+
+ cops = malloc(sizeof(client_opts));
+ if (cops == NULL) {
+ log_err("Couldn't allocate client option structure: %s.",
+ strerror(errno));
+ return -1;
+ }
+
+ /* Pre-set these. free_cops() relies on them being set. */
+ cops->opts = NULL;
+ cops->opt_lk = NULL;
+ cops->sock = s;
+ cops->tid = NULL;
+
+ cops->opts = malloc(sizeof(nast_options));
+ if (cops->opts == NULL) {
+ log_err("Couldn't allocate client options structure: %s.",
+ strerror(errno));
+ free_cops(cops);
+ return -1;
+ }
+ set_default_opts(cops->opts);
+
+ cops->opt_lk = malloc(sizeof(rw_mutex_t));
+ if (cops->opt_lk == NULL) {
+ log_err("Couldn't allocate client options mutex: %s.",
+ strerror(errno));
+ free_cops(cops);
+ return -1;
+ }
+
+ if (rw_mutex_new(cops->opt_lk)) {
+ log_err("Couldn't initialise client options mutex: %s.",
+ strerror(errno));
+ free_cops(cops);
+ return -1;
+ }
+
+ cops->tid = malloc(sizeof(thread_t));
+ if (cops->tid == NULL) {
+ log_err("Couldn't allocate initial client thread: %s.",
+ strerror(errno));
+ free_cops(cops);
+ return -1;
+ }
+
+ if (thread_new(cops->tid, io_looper, cops)) {
+ log_err("Couldn't start initial looper thread: %s.",
+ strerror(errno));
+ free_cops(cops);
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/server/nastdio.h b/server/nastdio.h
new file mode 100644
index 0000000..e3375f6
--- /dev/null
+++ b/server/nastdio.h
@@ -0,0 +1,14 @@
+/* $Id: nastdio.h,v 1.1 2000/09/13 20:21:34 shmit Exp $ */
+
+#ifndef NASTDIO_H
+#define NASTDIO_H
+
+struct _fieldent {
+ char *name;
+ short index;
+};
+typedef struct _fieldent fieldent;
+
+int io_new(int sock);
+
+#endif
diff --git a/server/periodic.c b/server/periodic.c
new file mode 100644
index 0000000..9a94edb
--- /dev/null
+++ b/server/periodic.c
@@ -0,0 +1,118 @@
+#include "conf.h"
+#include "cdb.h"
+#include "log.h"
+#include "memdb.h"
+#include "mysqldb.h"
+#include "thread.h"
+#include "periodic.h"
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+RCSID("$Id: periodic.c,v 1.9 2000/05/17 21:52:34 shmit Exp $");
+
+struct _per_thread_t {
+ cond_t *cond;
+ thread_t *tid;
+};
+typedef struct _per_thread_t per_thread_t;
+
+static void *
+periodic_looper(void *arg)
+{
+ per_thread_t *self;
+ int count;
+
+ self = (per_thread_t *)arg;
+ pthread_detach(pthread_self());
+
+ count = 0;
+ for (;;) {
+ struct timespec ts;
+
+ clock_gettime(CLOCK_REALTIME, &ts);
+ ts.tv_sec += 1;
+ (void)cond_timedwait(self->cond, &ts);
+
+ count++;
+ if (count == PERIODICITY) {
+ cdb_periodic();
+ count = 0;
+ }
+
+ cdb_collate();
+ mysqldb_collate();
+ memdb_collate();
+ }
+ return NULL;
+}
+
+void
+periodic_delete(per_thread_t *thr)
+{
+ if (thr == NULL)
+ return;
+
+ if (thr->cond) {
+ cond_destroy(thr->cond);
+ free(thr->cond);
+ thr->cond = NULL;
+ }
+
+ if (thr->tid) {
+ free(thr->tid);
+ thr->tid = NULL;
+ }
+ free(thr);
+}
+
+int
+periodic_new()
+{
+ per_thread_t *per_thread;
+ int rc;
+
+ per_thread = malloc(sizeof(per_thread_t));
+ if (per_thread == NULL) {
+ log_err("Couldn't allocate periodic thread: %s.",
+ strerror(errno));
+ return -1;
+ }
+ per_thread->cond = NULL;
+ per_thread->tid = NULL;
+
+ per_thread->cond = malloc(sizeof(cond_t));
+ if (per_thread->cond == NULL) {
+ log_err("Couldn't allocate periodic condition: %s.",
+ strerror(errno));
+ periodic_delete(per_thread);
+ return -1;
+ }
+
+ rc = cond_new(per_thread->cond);
+ if (rc) {
+ log_err("Couldn't initialise periodic condition: %s.",
+ strerror(rc));
+ periodic_delete(per_thread);
+ return -1;
+ }
+
+ per_thread->tid = malloc(sizeof(thread_t));
+ if (per_thread->tid == NULL) {
+ log_err("Couldn't allocate periodic thread: %s.",
+ strerror(rc));
+ periodic_delete(per_thread);
+ return -1;
+ }
+
+ rc = thread_new(per_thread->tid, periodic_looper, per_thread);
+ if (rc) {
+ log_err("Couldn't start periodic thread: %s.",
+ strerror(rc));
+ periodic_delete(per_thread);
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/server/periodic.h b/server/periodic.h
new file mode 100644
index 0000000..835fd4a
--- /dev/null
+++ b/server/periodic.h
@@ -0,0 +1,8 @@
+/* $Id: periodic.h,v 1.2 2000/09/13 20:21:35 shmit Exp $ */
+
+#ifndef NAST_PERIODIC_H
+#define NAST_PERIODIC_H
+
+int periodic_new();
+
+#endif
diff --git a/server/thread.c b/server/thread.c
new file mode 100644
index 0000000..95737d6
--- /dev/null
+++ b/server/thread.c
@@ -0,0 +1,216 @@
+#include "conf.h"
+#include "log.h"
+#include "thread.h"
+
+#include <errno.h>
+#include <pthread.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+
+RCSID("$Id: thread.c,v 1.6 2000/03/21 19:18:24 shmit Exp $");
+
+int
+thread_new(thread_t *thread, void *(*thread_func)(void *), void *arg)
+{
+ int rc;
+
+ thread->thread_func = thread_func;
+ rc = pthread_create(&thread->id, NULL, thread_func, arg);
+ if (rc != 0) {
+ log_err("Couldn't create thread: %s.\n", strerror(rc));
+ return -1;
+ }
+ return 0;
+}
+
+void
+thread_kill(thread_t *thread)
+{
+}
+
+int
+thread_reload(thread_t *thread)
+{
+ return 0;
+}
+
+int
+mutex_new(mutex_t *lock)
+{
+ return pthread_mutex_init(lock, NULL);
+}
+
+int
+mutex_delete(mutex_t *lock)
+{
+ return pthread_mutex_destroy(lock);
+}
+
+int
+mutex_lock(mutex_t *lock)
+{
+ return pthread_mutex_lock(lock);
+}
+
+int
+mutex_unlock(mutex_t *lock)
+{
+ return pthread_mutex_unlock(lock);
+}
+
+int
+rw_mutex_new(rw_mutex_t *lock)
+{
+ int rc;
+
+ rc = mutex_new(&lock->lock);
+ if (rc)
+ return rc;
+ rc = pthread_cond_init(&lock->read_sig, NULL);
+ if (rc) {
+ mutex_delete(&lock->lock);
+ return rc;
+ }
+ rc = pthread_cond_init(&lock->write_sig, NULL);
+ if (rc) {
+ mutex_delete(&lock->lock);
+ pthread_cond_destroy(&lock->read_sig);
+ return rc;
+ }
+ lock->state = 0;
+ lock->blocked_writers = 0;
+
+ return 0;
+}
+
+int
+rw_mutex_read_lock(rw_mutex_t *lock)
+{
+ int rc;
+
+ if (lock == NULL)
+ return EINVAL;
+
+ rc = mutex_lock(&lock->lock);
+ if (rc)
+ return rc;
+
+ /* Make sure the writers go before the readers. */
+ while (lock->blocked_writers || lock->state < 0) {
+ rc = pthread_cond_wait(&lock->read_sig, &lock->lock);
+ if (rc) {
+ mutex_unlock(&lock->lock);
+ return rc;
+ }
+ }
+ lock->state++;
+ mutex_unlock(&lock->lock);
+
+ return rc;
+}
+
+int
+rw_mutex_write_lock(rw_mutex_t *lock)
+{
+ int rc;
+
+ if (lock == NULL)
+ return EINVAL;
+
+ rc = mutex_lock(&lock->lock);
+ if (rc)
+ return rc;
+
+ /* Wait for no readers on the lock. */
+ while (lock->state != 0) {
+ lock->blocked_writers++;
+ rc = pthread_cond_wait(&lock->write_sig, &lock->lock);
+ lock->blocked_writers--;
+ if (rc) {
+ mutex_unlock(&lock->lock);
+ return rc;
+ }
+ }
+ lock->state = -1;
+
+ mutex_unlock(&lock->lock);
+ return rc;
+}
+
+int
+rw_mutex_unlock(rw_mutex_t *lock)
+{
+ int rc;
+
+ if (lock == NULL)
+ return EINVAL;
+
+ rc = mutex_lock(&lock->lock);
+ if (rc)
+ return rc;
+
+ if (lock->state > 0) {
+ /* We have an open read lock. */
+ if (--lock->state == 0 && lock->blocked_writers)
+ rc = pthread_cond_signal(&lock->write_sig);
+ } else if (lock->state < 0) {
+ /* We have an open writer lock. */
+ lock->state = 0;
+ if (lock->blocked_writers)
+ rc = pthread_cond_signal(&lock->write_sig);
+ else
+ rc = pthread_cond_broadcast(&lock->read_sig);
+ } else
+ rc = EINVAL;
+
+ mutex_unlock(&lock->lock);
+ return rc;
+}
+
+int
+cond_new(cond_t *cond)
+{
+ int rc;
+
+ if (cond == NULL)
+ return EINVAL;
+
+ rc = pthread_cond_init(&cond->id, NULL);
+ if (rc)
+ return rc;
+
+ rc = mutex_new(&cond->lock);
+ if (rc)
+ pthread_cond_destroy(&cond->id);
+
+ return rc;
+}
+
+int
+cond_signal(cond_t *cond)
+{
+ return pthread_cond_signal(&cond->id);
+}
+
+int
+cond_wait(cond_t *cond)
+{
+ return pthread_cond_wait(&cond->id, &cond->lock);
+}
+
+int
+cond_timedwait(cond_t *cond, const struct timespec *abstime)
+{
+ return pthread_cond_timedwait(&cond->id, &cond->lock, abstime);
+}
+
+int
+cond_destroy(cond_t *cond)
+{
+ return pthread_cond_destroy(&cond->id);
+}
diff --git a/server/thread.h b/server/thread.h
new file mode 100644
index 0000000..0aadef4
--- /dev/null
+++ b/server/thread.h
@@ -0,0 +1,49 @@
+/* $Id: thread.h,v 1.5 2000/03/15 00:09:25 shmit Exp $ */
+
+#ifndef THREAD_H
+# define THREAD_H
+
+#include "conf.h"
+
+#include <pthread.h>
+
+typedef pthread_t thread_id;
+typedef pthread_mutex_t mutex_t;
+
+struct _rwlock {
+ mutex_t lock;
+ int state;
+ pthread_cond_t read_sig;
+ pthread_cond_t write_sig;
+ int blocked_writers;
+};
+typedef struct _rwlock rw_mutex_t;
+
+struct thread {
+ void *(*thread_func)(void *arg);
+ pthread_t id;
+};
+typedef struct thread thread_t;
+
+struct cond {
+ pthread_cond_t id;
+ mutex_t lock;
+};
+typedef struct cond cond_t;
+
+int thread_new(thread_t *thread, void *(*thread_func)(void *), void *arg);
+void thread_kill(thread_t *thread);
+int thread_reload(thread_t *thread);
+int mutex_new(mutex_t *lock);
+int mutex_lock(mutex_t *lock);
+int mutex_unlock(mutex_t *lock);
+int rw_mutex_new(rw_mutex_t *lock);
+int rw_mutex_read_lock(rw_mutex_t *lock);
+int rw_mutex_write_lock(rw_mutex_t *lock);
+int rw_mutex_unlock(rw_mutex_t *lock);
+int cond_new(cond_t *cond);
+int cond_signal(cond_t *cond);
+int cond_wait(cond_t *cond);
+int cond_timedwait(cond_t *cond, const struct timespec *abstime);
+int cond_destroy(cond_t *cond);
+#endif