diff options
Diffstat (limited to 'server')
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 |