From f0974784902d7d8d7a997f76893214a1c2648ce2 Mon Sep 17 00:00:00 2001 From: Matthew Mondor Date: Sat, 22 Jul 2006 04:47:44 +0000 Subject: [PATCH] This commit was manufactured by cvs2svn to create tag 'before-appserv-tag'. --- Xisop/LICENSE | 34 - Xisop/README | 53 - Xisop/TODO | 163 - Xisop/doc/clean.sh | 3 - Xisop/doc/make.sh | 11 - Xisop/doc/xisop.lyx | 10041 ------------------- Xisop/src/clean.sh | 16 - Xisop/src/common/clean.sh | 11 - Xisop/src/common/kernel/clean.sh | 8 - Xisop/src/common/kernel/debug.c | 408 - Xisop/src/common/kernel/debug.h | 104 - Xisop/src/common/kernel/device.c | 525 - Xisop/src/common/kernel/device.h | 176 - Xisop/src/common/kernel/exception.c | 222 - Xisop/src/common/kernel/exception.h | 84 - Xisop/src/common/kernel/main.c | 234 - Xisop/src/common/kernel/main.h | 155 - Xisop/src/common/kernel/make.sh | 9 - Xisop/src/common/kernel/memory.c | 1425 --- Xisop/src/common/kernel/memory.h | 260 - Xisop/src/common/kernel/object.h | 87 - Xisop/src/common/kernel/port.c | 337 - Xisop/src/common/kernel/port.h | 103 - Xisop/src/common/kernel/scheduler.c | 382 - Xisop/src/common/kernel/scheduler.h | 90 - Xisop/src/common/kernel/signal.c | 195 - Xisop/src/common/kernel/signal.h | 67 - Xisop/src/common/kernel/statistic.c | 168 - Xisop/src/common/kernel/statistic.h | 163 - Xisop/src/common/kernel/syscall.c | 204 - Xisop/src/common/kernel/syscall.h | 76 - Xisop/src/common/kernel/task.c | 432 - Xisop/src/common/kernel/task.h | 164 - Xisop/src/common/kernlib/clean.sh | 9 - Xisop/src/common/kernlib/fifo.h | 165 - Xisop/src/common/kernlib/hash.c | 381 - Xisop/src/common/kernlib/hash.h | 96 - Xisop/src/common/kernlib/lifo.h | 107 - Xisop/src/common/kernlib/list.h | 178 - Xisop/src/common/kernlib/make.sh | 13 - Xisop/src/common/kernlib/rand.c | 108 - Xisop/src/common/kernlib/rand.h | 53 - Xisop/src/common/kernlib/setjmp.h | 64 - Xisop/src/common/kernlib/string.h | 106 - Xisop/src/common/kernlib/string/_strcat.c | 52 - Xisop/src/common/kernlib/string/_strcpy.c | 53 - Xisop/src/common/kernlib/string/_strdup.c | 59 - Xisop/src/common/kernlib/string/_strncat.c | 59 - Xisop/src/common/kernlib/string/_strncpy.c | 62 - Xisop/src/common/kernlib/string/_strndup.c | 61 - Xisop/src/common/kernlib/string/_strrev.c | 57 - Xisop/src/common/kernlib/string/bstr_alloc.c | 59 - Xisop/src/common/kernlib/string/bstr_free.c | 50 - Xisop/src/common/kernlib/string/bstr_new.c | 67 - Xisop/src/common/kernlib/string/case.c | 241 - Xisop/src/common/kernlib/string/htol.c | 71 - Xisop/src/common/kernlib/string/memcmp.c | 120 - Xisop/src/common/kernlib/string/memcpy.c | 164 - Xisop/src/common/kernlib/string/memhash32.c | 72 - Xisop/src/common/kernlib/string/memmove.c | 305 - Xisop/src/common/kernlib/string/memset.c | 179 - Xisop/src/common/kernlib/string/pageclr.c | 96 - Xisop/src/common/kernlib/string/straspl.c | 68 - Xisop/src/common/kernlib/string/strchr.c | 48 - Xisop/src/common/kernlib/string/strcmp.c | 48 - Xisop/src/common/kernlib/string/strlen.c | 56 - Xisop/src/common/kernlib/string/strnchr.c | 58 - Xisop/src/common/kernlib/string/strncmp.c | 57 - Xisop/src/common/kernlib/string/strnlen.c | 51 - Xisop/src/common/kernlib/string/strnrchr.c | 54 - Xisop/src/common/kernlib/string/strrchr.c | 53 - Xisop/src/common/kernlib/string/strspl.c | 65 - Xisop/src/common/make.sh | 15 - Xisop/src/common/types.h | 133 - Xisop/src/config.h | 50 - Xisop/src/generic_makedefs.sh | 40 - Xisop/src/make.sh | 68 - Xisop/src/ports/amiga/boot/DOTuaerc | 27 - Xisop/src/ports/amiga/boot/README | 12 - Xisop/src/ports/amiga/boot/bootf/bootf_c.c | 139 - Xisop/src/ports/amiga/boot/bootf/bootf_s.s | 118 - Xisop/src/ports/amiga/boot/bootf/bootf_script.ld | 14 - Xisop/src/ports/amiga/boot/clean.sh | 10 - Xisop/src/ports/amiga/boot/config.h | 72 - Xisop/src/ports/amiga/boot/image_script.ld | 13 - Xisop/src/ports/amiga/boot/init.c | 590 -- Xisop/src/ports/amiga/boot/make.sh | 33 - Xisop/src/ports/amiga/boot/tools/aosbblock.c | 107 - Xisop/src/ports/amiga/boot/tools/config.c | 64 - Xisop/src/ports/amiga/boot/tools/dumpkern.c | 80 - Xisop/src/ports/amiga/clean.sh | 7 - Xisop/src/ports/amiga/make.sh | 10 - Xisop/src/ports/amiga/makedefs.sh | 71 - Xisop/src/ports/amiga/support.h | 467 - Xisop/src/ports/amiga/support_c.c | 77 - Xisop/src/ports/amiga/support_s.s | 865 -- Xisop/src/processors/i386/make.sh | 11 - Xisop/src/processors/i386/support.h | 83 - Xisop/src/processors/i386/support.s | 143 - Xisop/src/processors/m68k/clean.sh | 8 - Xisop/src/processors/m68k/make.sh | 10 - Xisop/src/processors/m68k/math/README | 3 - Xisop/src/processors/m68k/math/divsi3.s | 59 - Xisop/src/processors/m68k/math/modsi3.s | 60 - Xisop/src/processors/m68k/math/mulsi3.s | 55 - Xisop/src/processors/m68k/math/udivsi3.s | 114 - Xisop/src/processors/m68k/math/umodsi3.s | 51 - Xisop/src/processors/m68k/support.h | 138 - Xisop/src/processors/m68k/support.s | 321 - mmsoftware/BUGS | 5 - mmsoftware/LICENSE | 30 - mmsoftware/TODO | 387 - mmsoftware/apache-mmstat/GNUmakefile | 24 - mmsoftware/apache-mmstat/apache-mmstat.8 | 296 - mmsoftware/apache-mmstat/apache-mmstat.c | 400 - mmsoftware/apache-mmstat/makepart.sh | 25 - mmsoftware/bot/Makefile | 36 - mmsoftware/bot/irc.txt | 399 - mmsoftware/bot/newbot.c | 1259 --- mmsoftware/bot/zenbot.c | 1106 -- mmsoftware/clean.sh | 33 - mmsoftware/install.sh | 199 - mmsoftware/js/classes/js_errno.c | 222 - mmsoftware/js/classes/js_errno.h | 13 - mmsoftware/js/classes/js_fd.c | 1971 ---- mmsoftware/js/classes/js_fd.h | 15 - mmsoftware/js/classes/js_global.c | 150 - mmsoftware/js/classes/js_global.h | 15 - mmsoftware/js/classes/js_mysql.c | 6 - mmsoftware/js/classes/js_mysql.h | 13 - mmsoftware/js/classes/js_pgsql.c | 3493 ------- mmsoftware/js/classes/js_pgsql.h | 15 - mmsoftware/js/classes/js_signal.c | 231 - mmsoftware/js/classes/js_signal.h | 13 - mmsoftware/js/js-sh/src/GNUmakefile | 29 - mmsoftware/js/js-sh/src/js-sh.c | 328 - mmsoftware/js/util/spidermonkey-config | 51 - mmsoftware/make.sh | 40 - mmsoftware/mmanoncvs/GNUmakefile | 26 - mmsoftware/mmanoncvs/mmanoncvs.8 | 720 -- mmsoftware/mmanoncvs/mmanoncvs.c | 1425 --- mmsoftware/mmanoncvs/mmanoncvs.conf.5 | 293 - mmsoftware/mmanoncvs/mmanoncvs.list.5 | 130 - mmsoftware/mmftpd/ChangeLog | 692 -- mmsoftware/mmftpd/GNUmakefile | 30 - mmsoftware/mmftpd/README | 284 - mmsoftware/mmftpd/clean.sh | 20 - mmsoftware/mmftpd/etc/mmftpd.conf | 146 - mmsoftware/mmftpd/etc/mmftpdpasswd | 16 - mmsoftware/mmftpd/install.sh | 154 - mmsoftware/mmftpd/make.sh | 28 - mmsoftware/mmftpd/scripts/mmftpd.sh | 32 - mmsoftware/mmftpd/src/Makefile | 35 - mmsoftware/mmftpd/src/makepart.sh | 27 - mmsoftware/mmftpd/src/mmftpd.8 | 436 - mmsoftware/mmftpd/src/mmftpd.c | 4840 --------- mmsoftware/mmftpd/src/mmftpd.conf.5 | 459 - mmsoftware/mmftpd/src/mmftpd.h | 444 - mmsoftware/mmftpd/src/mmftpdpasswd.5 | 218 - mmsoftware/mmidentd/GNUmakefile | 21 - mmsoftware/mmidentd/mmidentd.c | 323 - mmsoftware/mmlib/Makefile | 91 - mmsoftware/mmlib/makedefs.sh | 18 - mmsoftware/mmlib/makefuncs.sh | 138 - mmsoftware/mmlib/makepart.sh | 29 - mmsoftware/mmlib/mm_pth_pool.c | 383 - mmsoftware/mmlib/mm_pth_pool.h | 104 - mmsoftware/mmlib/mm_thread_abstraction.h | 46 - mmsoftware/mmlib/mmalarm.3 | 245 - mmsoftware/mmlib/mmalarm.c | 218 - mmsoftware/mmlib/mmalarm.h | 91 - mmsoftware/mmlib/mmarch.c | 74 - mmsoftware/mmlib/mmarch.h | 124 - mmsoftware/mmlib/mmbit.h | 172 - mmsoftware/mmlib/mmbstring.c | 332 - mmsoftware/mmlib/mmbstring.h | 92 - mmsoftware/mmlib/mmfd.3 | 979 -- mmsoftware/mmlib/mmfd.c | 1167 --- mmsoftware/mmlib/mmfd.h | 236 - mmsoftware/mmlib/mmfifo.3 | 278 - mmsoftware/mmlib/mmfifo.c | 146 - mmsoftware/mmlib/mmfifo.h | 163 - mmsoftware/mmlib/mmhash.3 | 523 - mmsoftware/mmlib/mmhash.c | 436 - mmsoftware/mmlib/mmhash.h | 116 - mmsoftware/mmlib/mmheap.3 | 485 - mmsoftware/mmlib/mmheap.c | 603 -- mmsoftware/mmlib/mmheap.h | 67 - mmsoftware/mmlib/mmlifo.3 | 218 - mmsoftware/mmlib/mmlifo.h | 107 - mmsoftware/mmlib/mmlimitrate.3 | 179 - mmsoftware/mmlib/mmlimitrate.c | 73 - mmsoftware/mmlib/mmlimitrate.h | 101 - mmsoftware/mmlib/mmlist.3 | 205 - mmsoftware/mmlib/mmlist.h | 179 - mmsoftware/mmlib/mmloadarray.c | 115 - mmsoftware/mmlib/mmloadarray.h | 70 - mmsoftware/mmlib/mmlog.c | 99 - mmsoftware/mmlib/mmlog.h | 102 - mmsoftware/mmlib/mmpath.3 | 351 - mmsoftware/mmlib/mmpath.c | 501 - mmsoftware/mmlib/mmpath.h | 92 - mmsoftware/mmlib/mmpool.3 | 320 - mmsoftware/mmlib/mmpool.c | 493 - mmsoftware/mmlib/mmpool.h | 111 - mmsoftware/mmlib/mmrc4.c | 173 - mmsoftware/mmlib/mmrc4.h | 77 - mmsoftware/mmlib/mmrc4util.3 | 324 - mmsoftware/mmlib/mmrc4util.c | 449 - mmsoftware/mmlib/mmrc4util.h | 61 - mmsoftware/mmlib/mmreadcfg.c | 491 - mmsoftware/mmlib/mmreadcfg.h | 143 - mmsoftware/mmlib/mmserver.c | 1413 --- mmsoftware/mmlib/mmserver.h | 238 - mmsoftware/mmlib/mmserver2.3 | 1190 --- mmsoftware/mmlib/mmserver2.c | 2976 ------ mmsoftware/mmlib/mmserver2.h | 222 - mmsoftware/mmlib/mmsql.c | 336 - mmsoftware/mmlib/mmsql.h | 87 - mmsoftware/mmlib/mmstat.3 | 242 - mmsoftware/mmlib/mmstat.c | 430 - mmsoftware/mmlib/mmstat.h | 148 - mmsoftware/mmlib/mmstr.c | 163 - mmsoftware/mmlib/mmstr.h | 76 - mmsoftware/mmlib/mmstring.c | 1405 --- mmsoftware/mmlib/mmstring.h | 106 - mmsoftware/mmlib/mmtypes.h | 97 - mmsoftware/mmmail/ChangeLog | 887 -- mmsoftware/mmmail/GNUmakefile | 50 - mmsoftware/mmmail/README | 322 - mmsoftware/mmmail/clean.sh | 22 - mmsoftware/mmmail/etc/mmpop3d.conf | 115 - mmsoftware/mmmail/etc/mmsmtpd.conf | 175 - mmsoftware/mmmail/install.sh | 158 - mmsoftware/mmmail/make.sh | 31 - mmsoftware/mmmail/scripts/mmpop3d.sh | 32 - mmsoftware/mmmail/scripts/mmsmtpd.sh | 32 - mmsoftware/mmmail/scripts/tables.sql | 148 - mmsoftware/mmmail/scripts/upgrade-0.0.13.sql | 33 - mmsoftware/mmmail/scripts/upgrade-0.0.21.sql | 11 - mmsoftware/mmmail/scripts/upgrade-0.0.24.sql | 87 - mmsoftware/mmmail/src/mmmail.8 | 477 - mmsoftware/mmmail/src/mmpop3d/Makefile | 36 - mmsoftware/mmmail/src/mmpop3d/makepart.sh | 29 - mmsoftware/mmmail/src/mmpop3d/mmpop3d.8 | 86 - mmsoftware/mmmail/src/mmpop3d/mmpop3d.c | 1860 ---- mmsoftware/mmmail/src/mmpop3d/mmpop3d.conf.5 | 349 - mmsoftware/mmmail/src/mmpop3d/mmpop3d.h | 242 - mmsoftware/mmmail/src/mmrelayd/mmrelayd.c | 565 -- mmsoftware/mmmail/src/mmrelayd/mmrelayd.h | 51 - mmsoftware/mmmail/src/mmsmtpd/Makefile | 36 - mmsoftware/mmmail/src/mmsmtpd/makepart.sh | 29 - mmsoftware/mmmail/src/mmsmtpd/mmsmtpd.8 | 91 - mmsoftware/mmmail/src/mmsmtpd/mmsmtpd.c | 3001 ------ mmsoftware/mmmail/src/mmsmtpd/mmsmtpd.conf.5 | 578 -- mmsoftware/mmmail/src/mmsmtpd/mmsmtpd.h | 340 - mmsoftware/mmmail2/ChangeLog | 4 - mmsoftware/mmmail2/README | 14 - mmsoftware/mmmail2/notes/convert.sh | 7 - mmsoftware/mmmail2/notes/mmmail-design.lyx | 875 -- mmsoftware/mmmail2/notes/mmmail-tables.dia | Bin 3913 -> 0 bytes mmsoftware/mmpasswd/GNUmakefile | 23 - mmsoftware/mmpasswd/Makefile | 36 - mmsoftware/mmpasswd/make.sh | 8 - mmsoftware/mmpasswd/makepart.sh | 25 - mmsoftware/mmpasswd/mmpasswd.8 | 93 - mmsoftware/mmpasswd/mmpasswd.c | 169 - mmsoftware/mmsendmail/GNUmakefile | 22 - mmsoftware/mmsendmail/Makefile | 34 - mmsoftware/mmsendmail/mmsendmail.c | 377 - mmsoftware/mmspawnd/GNUmakefile | 22 - mmsoftware/mmspawnd/mmspawnd.8 | 160 - mmsoftware/mmspawnd/mmspawnd.c | 1709 ---- mmsoftware/mmspawnd/mmspawnd.conf.5 | 361 - mmsoftware/mmspawnd/new/GNUmakefile | 17 - mmsoftware/mmspawnd/new/README | 122 - mmsoftware/mmspawnd/new/mmspawnd.c | 1498 --- mmsoftware/mmspawnd2/GNUmakefile | 22 - mmsoftware/mmspawnd2/mmspawnd.8 | 160 - mmsoftware/mmspawnd2/mmspawnd.c | 794 -- mmsoftware/mmspawnd2/mmspawnd.conf.5 | 361 - mmsoftware/mmstatd/ChangeLog | 197 - mmsoftware/mmstatd/GNUmakefile | 34 - mmsoftware/mmstatd/README | 165 - mmsoftware/mmstatd/clean.sh | 16 - mmsoftware/mmstatd/etc/mmstatd.conf | 72 - mmsoftware/mmstatd/future.txt | 70 - mmsoftware/mmstatd/install.sh | 124 - mmsoftware/mmstatd/make.sh | 23 - mmsoftware/mmstatd/src/Makefile | 39 - mmsoftware/mmstatd/src/makepart.sh | 27 - mmsoftware/mmstatd/src/mmstat.8 | 225 - mmsoftware/mmstatd/src/mmstat.c | 415 - mmsoftware/mmstatd/src/mmstatd.8 | 235 - mmsoftware/mmstatd/src/mmstatd.c | 1991 ---- mmsoftware/mmstatd/src/mmstatd.conf.5 | 213 - mmsoftware/mmstatd/src/mmstatd.h | 128 - mmsoftware/mmsucom/GNUmakefile | 28 - mmsoftware/mmsucom/Makefile | 37 - mmsoftware/mmsucom/README | 28 - mmsoftware/mmsucom/mmsucom.c | 101 - mmsoftware/mmsucom/mmsucomd.c | 453 - mmsoftware/mmsucom/mmsucomd.conf | 5 - mmsoftware/stringtest/GNUmakefile | 19 - mmsoftware/stringtest/stringtest.c | 706 -- site/contributors.html | 95 - site/cvs.html | 89 - site/donations.html | 115 - site/favicon.ico | Bin 318 -> 0 bytes site/images/CVS.jpg | Bin 1366 -> 0 bytes site/images/key.jpg | Bin 1977 -> 0 bytes site/images/sigil.jpg | Bin 3311 -> 0 bytes site/index.html | 91 - site/mirrors.html | 92 - site/new/TODO | 93 - site/new/build/GNUmakefile | 20 - site/new/build/README | 16 - site/new/build/TODO | 10 - site/new/build/english/faq_mmftpd.txt | 17 - site/new/build/english/faq_mmmail.txt | 0 site/new/build/english/faq_mmstatd.txt | 0 site/new/build/english/menu_languages.txt | 6 - site/new/build/english/menu_main.txt | 10 - site/new/build/english/menu_mirrors.txt | 7 - site/new/build/english/page_contact.txt | 0 site/new/build/english/page_contributors.txt | 0 site/new/build/english/page_cvs.txt | 0 site/new/build/english/page_donations.txt | 0 site/new/build/english/page_index.txt | 22 - site/new/build/english/page_mirrors.txt | 0 site/new/build/english/page_philosophy.txt | 0 site/new/build/english/page_projects.txt | 0 site/new/build/english/page_software.txt | 0 site/new/build/english/soft_descriptions.txt | 15 - site/new/build/mmsite.c | 557 - site/new/build/mmsite.conf | 12 - site/new/build/site_faq.txt | 6 - site/new/build/site_languages.txt | 5 - site/new/build/site_mirrors.txt | 6 - site/new/build/site_pages.txt | 12 - site/new/build/site_software.txt | 6 - site/new/build/soft_development.txt | 4 - site/new/build/soft_old.txt | 4 - site/new/build/soft_stable.txt | 4 - site/philosophy.html | 133 - site/projects.html | 146 - site/software.html | 342 - tests/js-test/README | 7 - tests/js-test/js/copy.js | 37 - tests/js-test/js/fd.js | 35 - tests/js-test/js/game/objects.js | 341 - tests/js-test/js/httpd/httpd.js | 1679 ---- tests/js-test/js/httpd/ml_clean.js | 192 - tests/js-test/js/httpd/ml_machine.js | 146 - tests/js-test/js/httpd/options.js | 82 - tests/js-test/js/httpd/root.js | 197 - tests/js-test/js/httpd/string.js | 34 - tests/js-test/js/ml.js | 176 - tests/js-test/js/ml2.js | 209 - tests/js-test/js/ml3.js | 209 - tests/js-test/js/ml4.js | 203 - tests/js-test/js/ml5.js | 213 - tests/js-test/js/ml6.js | 182 - tests/js-test/js/parse.js | 318 - tests/js-test/js/poll_test.js | 93 - tests/js-test/js/sock_httpd.js | 292 - tests/js-test/js/sock_listen.js | 52 - tests/js-test/js/test.js | 84 - tests/js-test/js/test2.js | 15 - tests/js-test/js/test3.js | 46 - tests/js-test/src/GNUmakefile | 29 - tests/js-test/src/classes/js_errno.c | 222 - tests/js-test/src/classes/js_errno.h | 13 - tests/js-test/src/classes/js_fd.c | 1971 ---- tests/js-test/src/classes/js_fd.h | 15 - tests/js-test/src/classes/js_global.c | 150 - tests/js-test/src/classes/js_global.h | 15 - tests/js-test/src/classes/js_mysql.c | 6 - tests/js-test/src/classes/js_mysql.h | 13 - tests/js-test/src/classes/js_pgsql.c | 3493 ------- tests/js-test/src/classes/js_pgsql.h | 15 - tests/js-test/src/classes/js_signal.c | 231 - tests/js-test/src/classes/js_signal.h | 13 - tests/js-test/src/js-server.c | 328 - tests/js-test/util/spidermonkey-config | 49 - tests/kqueue/GNUmakefile | 22 - tests/kqueue/README | 48 - tests/kqueue/client.c | 152 - tests/kqueue/client.h | 59 - tests/kqueue/conf.h | 39 - tests/kqueue/daemon.c | 124 - tests/kqueue/daemon.h | 19 - tests/kqueue/kqueue.c | 329 - tests/kqueue/kqueue.h | 31 - tests/kqueue/main.c | 62 - tests/kqueue/net.c | 131 - tests/kqueue/net.h | 23 - tests/kqueue/packets.c | 327 - tests/kqueue/packets.h | 160 - tests/kqueue/recvq.c | 84 - tests/kqueue/recvq.h | 35 - tests/kqueue/sendq.c | 148 - tests/kqueue/sendq.h | 37 - tests/memory/README | 42 - tests/pthread_utils/GNUmakefile | 35 - tests/pthread_utils/README | 197 - tests/pthread_utils/mm_pthread_debug.h | 63 - tests/pthread_utils/mm_pthread_msg.c | 466 - tests/pthread_utils/mm_pthread_msg.h | 110 - tests/pthread_utils/mm_pthread_poll.c | 1116 --- tests/pthread_utils/mm_pthread_poll.h | 63 - tests/pthread_utils/mm_pthread_pool.c | 504 - tests/pthread_utils/mm_pthread_pool.h | 97 - tests/pthread_utils/mm_pthread_sleep.c | 285 - tests/pthread_utils/mm_pthread_sleep.h | 57 - tests/pthread_utils/tests/msg_test.c | 269 - tests/pthread_utils/tests/poll_test.c | 73 - tests/pthread_utils/tests/polltest.c | 153 - tests/pthread_utils/tests/sigtest.c | 146 - tests/rotate/GNUmakefile | 57 - tests/rotate/main.c | 195 - tests/sdl-client/GNUmakefile | 90 - tests/sdl-client/README | 190 - tests/sdl-client/bmp/FedCA.bmp | Bin 4854 -> 0 bytes tests/sdl-client/bmp/README | 1 - tests/sdl-client/bmp/RomDD.bmp | Bin 4854 -> 0 bytes tests/sdl-client/bmp/fedship.bmp | Bin 103478 -> 0 bytes tests/sdl-client/bmp/indship.bmp | Bin 87098 -> 0 bytes tests/sdl-client/bmp/kliship.bmp | Bin 103478 -> 0 bytes ...-bold-r-normal--13-120-75-75-c-80-iso8859-1.bmp | Bin 3390 -> 0 bytes ...old-r-normal--15-120-100-100-c-90-iso8859-1.bmp | Bin 4862 -> 0 bytes ...edium-r-normal--13-120-75-75-c-80-iso8859-1.bmp | Bin 3390 -> 0 bytes ...ium-r-normal--15-120-100-100-c-90-iso8859-1.bmp | Bin 4862 -> 0 bytes tests/sdl-client/bmp/oriship.bmp | Bin 103478 -> 0 bytes tests/sdl-client/bmp/romship.bmp | Bin 103478 -> 0 bytes tests/sdl-client/bmp/sbexpl.bmp | Bin 29050 -> 0 bytes tests/sdl-client/bmp/shexpl.bmp | Bin 9832 -> 0 bytes tests/sdl-client/conf.h | 13 - tests/sdl-client/debug.c | 42 - tests/sdl-client/debug.h | 44 - tests/sdl-client/decode.c | 38 - tests/sdl-client/decode.h | 23 - tests/sdl-client/dlist.h | 174 - tests/sdl-client/encode.c | 107 - tests/sdl-client/fnt/10x20.fnt | Bin 10240 -> 0 bytes tests/sdl-client/fnt/5x7.fnt | Bin 1792 -> 0 bytes tests/sdl-client/fnt/5x8.fnt | Bin 2048 -> 0 bytes tests/sdl-client/fnt/6x10.fnt | Bin 2560 -> 0 bytes tests/sdl-client/fnt/6x12.fnt | Bin 3072 -> 0 bytes tests/sdl-client/fnt/6x13.fnt | Bin 3328 -> 0 bytes tests/sdl-client/fnt/6x13B.fnt | Bin 3328 -> 0 bytes tests/sdl-client/fnt/6x13O.fnt | Bin 3328 -> 0 bytes tests/sdl-client/fnt/6x9.fnt | Bin 2304 -> 0 bytes tests/sdl-client/fnt/7x13.fnt | Bin 3328 -> 0 bytes tests/sdl-client/fnt/7x13B.fnt | Bin 3328 -> 0 bytes tests/sdl-client/fnt/7x13O.fnt | Bin 3328 -> 0 bytes tests/sdl-client/fnt/7x14.fnt | Bin 3584 -> 0 bytes tests/sdl-client/fnt/7x14B.fnt | Bin 3584 -> 0 bytes tests/sdl-client/fnt/8x13.fnt | Bin 3328 -> 0 bytes tests/sdl-client/fnt/8x13B.fnt | Bin 3328 -> 0 bytes tests/sdl-client/fnt/8x13O.fnt | Bin 3328 -> 0 bytes tests/sdl-client/fnt/9x15.fnt | Bin 7680 -> 0 bytes tests/sdl-client/fnt/9x15B.fnt | Bin 7680 -> 0 bytes tests/sdl-client/fnt/9x18.fnt | Bin 9216 -> 0 bytes tests/sdl-client/fnt/9x18B.fnt | Bin 9216 -> 0 bytes tests/sdl-client/main.c | 872 -- tests/sdl-client/main.h | 29 - tests/sdl-client/ogg/README | 5 - tests/sdl-client/packets.c | 327 - tests/sdl-client/packets.h | 151 - tests/sdl-client/pool.c | 417 - tests/sdl-client/pool.h | 122 - tests/sdl-client/rawobjs.h | 79 - tests/sdl-client/screen.c | 65 - tests/sdl-client/screen.h | 34 - tests/sdl-client/tests/draw.c | 948 -- tests/sdl-client/tests/draw.h | 75 - tests/sdl-client/tests/fonts.c | 318 - tests/sdl-client/tests/fonts.h | 46 - tests/sdl-client/tests/msg.c | 189 - tests/sdl-client/tests/netrek-like.c | 124 - tests/sdl-client/tests/rot.c | 182 - tests/sdl-client/tests/snd.c | 159 - tests/sdl-client/thread_msg.c | 432 - tests/sdl-client/thread_msg.h | 86 - tests/sdl-client/thread_net_recv.c | 191 - tests/sdl-client/thread_net_recv.h | 65 - tests/sdl-client/thread_net_send.c | 71 - tests/sdl-client/thread_net_send.h | 35 - tests/sdl-client/wav/README | 1 - tests/sdl-client/wav/nt_cloaked.wav | Bin 198704 -> 0 bytes tests/sdl-client/wav/nt_explosion_other.wav | Bin 276878 -> 0 bytes tests/sdl-client/wav/nt_fire_torp_other.wav | Bin 52940 -> 0 bytes tests/sdl-client/wav/nt_plasma_hit.wav | Bin 108878 -> 0 bytes tests/sdl-client/wav/nt_shield_down.wav | Bin 89732 -> 0 bytes tests/sdl-client/wav/nt_shield_up.wav | Bin 66922 -> 0 bytes tests/sdl-client/wav/nt_uncloak.wav | Bin 253180 -> 0 bytes tests/zlib/README | 3 - tests/zlib/deflate.c | 87 - tests/zlib/inflate.c | 91 - 500 files changed, 114496 deletions(-) delete mode 100644 Xisop/LICENSE delete mode 100644 Xisop/README delete mode 100644 Xisop/TODO delete mode 100755 Xisop/doc/clean.sh delete mode 100755 Xisop/doc/make.sh delete mode 100644 Xisop/doc/xisop.lyx delete mode 100755 Xisop/src/clean.sh delete mode 100755 Xisop/src/common/clean.sh delete mode 100755 Xisop/src/common/kernel/clean.sh delete mode 100644 Xisop/src/common/kernel/debug.c delete mode 100644 Xisop/src/common/kernel/debug.h delete mode 100644 Xisop/src/common/kernel/device.c delete mode 100644 Xisop/src/common/kernel/device.h delete mode 100644 Xisop/src/common/kernel/exception.c delete mode 100644 Xisop/src/common/kernel/exception.h delete mode 100644 Xisop/src/common/kernel/main.c delete mode 100644 Xisop/src/common/kernel/main.h delete mode 100755 Xisop/src/common/kernel/make.sh delete mode 100644 Xisop/src/common/kernel/memory.c delete mode 100644 Xisop/src/common/kernel/memory.h delete mode 100644 Xisop/src/common/kernel/object.h delete mode 100644 Xisop/src/common/kernel/port.c delete mode 100644 Xisop/src/common/kernel/port.h delete mode 100644 Xisop/src/common/kernel/scheduler.c delete mode 100644 Xisop/src/common/kernel/scheduler.h delete mode 100644 Xisop/src/common/kernel/signal.c delete mode 100644 Xisop/src/common/kernel/signal.h delete mode 100644 Xisop/src/common/kernel/statistic.c delete mode 100644 Xisop/src/common/kernel/statistic.h delete mode 100644 Xisop/src/common/kernel/syscall.c delete mode 100644 Xisop/src/common/kernel/syscall.h delete mode 100644 Xisop/src/common/kernel/task.c delete mode 100644 Xisop/src/common/kernel/task.h delete mode 100755 Xisop/src/common/kernlib/clean.sh delete mode 100644 Xisop/src/common/kernlib/fifo.h delete mode 100644 Xisop/src/common/kernlib/hash.c delete mode 100644 Xisop/src/common/kernlib/hash.h delete mode 100644 Xisop/src/common/kernlib/lifo.h delete mode 100644 Xisop/src/common/kernlib/list.h delete mode 100755 Xisop/src/common/kernlib/make.sh delete mode 100644 Xisop/src/common/kernlib/rand.c delete mode 100644 Xisop/src/common/kernlib/rand.h delete mode 100644 Xisop/src/common/kernlib/setjmp.h delete mode 100644 Xisop/src/common/kernlib/string.h delete mode 100644 Xisop/src/common/kernlib/string/_strcat.c delete mode 100644 Xisop/src/common/kernlib/string/_strcpy.c delete mode 100644 Xisop/src/common/kernlib/string/_strdup.c delete mode 100644 Xisop/src/common/kernlib/string/_strncat.c delete mode 100644 Xisop/src/common/kernlib/string/_strncpy.c delete mode 100644 Xisop/src/common/kernlib/string/_strndup.c delete mode 100644 Xisop/src/common/kernlib/string/_strrev.c delete mode 100644 Xisop/src/common/kernlib/string/bstr_alloc.c delete mode 100644 Xisop/src/common/kernlib/string/bstr_free.c delete mode 100644 Xisop/src/common/kernlib/string/bstr_new.c delete mode 100644 Xisop/src/common/kernlib/string/case.c delete mode 100644 Xisop/src/common/kernlib/string/htol.c delete mode 100644 Xisop/src/common/kernlib/string/memcmp.c delete mode 100644 Xisop/src/common/kernlib/string/memcpy.c delete mode 100644 Xisop/src/common/kernlib/string/memhash32.c delete mode 100644 Xisop/src/common/kernlib/string/memmove.c delete mode 100644 Xisop/src/common/kernlib/string/memset.c delete mode 100644 Xisop/src/common/kernlib/string/pageclr.c delete mode 100644 Xisop/src/common/kernlib/string/straspl.c delete mode 100644 Xisop/src/common/kernlib/string/strchr.c delete mode 100644 Xisop/src/common/kernlib/string/strcmp.c delete mode 100644 Xisop/src/common/kernlib/string/strlen.c delete mode 100644 Xisop/src/common/kernlib/string/strnchr.c delete mode 100644 Xisop/src/common/kernlib/string/strncmp.c delete mode 100644 Xisop/src/common/kernlib/string/strnlen.c delete mode 100644 Xisop/src/common/kernlib/string/strnrchr.c delete mode 100644 Xisop/src/common/kernlib/string/strrchr.c delete mode 100644 Xisop/src/common/kernlib/string/strspl.c delete mode 100755 Xisop/src/common/make.sh delete mode 100644 Xisop/src/common/types.h delete mode 100644 Xisop/src/config.h delete mode 100644 Xisop/src/generic_makedefs.sh delete mode 100755 Xisop/src/make.sh delete mode 100644 Xisop/src/ports/amiga/boot/DOTuaerc delete mode 100644 Xisop/src/ports/amiga/boot/README delete mode 100644 Xisop/src/ports/amiga/boot/bootf/bootf_c.c delete mode 100644 Xisop/src/ports/amiga/boot/bootf/bootf_s.s delete mode 100644 Xisop/src/ports/amiga/boot/bootf/bootf_script.ld delete mode 100755 Xisop/src/ports/amiga/boot/clean.sh delete mode 100644 Xisop/src/ports/amiga/boot/config.h delete mode 100644 Xisop/src/ports/amiga/boot/image_script.ld delete mode 100644 Xisop/src/ports/amiga/boot/init.c delete mode 100755 Xisop/src/ports/amiga/boot/make.sh delete mode 100644 Xisop/src/ports/amiga/boot/tools/aosbblock.c delete mode 100644 Xisop/src/ports/amiga/boot/tools/config.c delete mode 100644 Xisop/src/ports/amiga/boot/tools/dumpkern.c delete mode 100755 Xisop/src/ports/amiga/clean.sh delete mode 100755 Xisop/src/ports/amiga/make.sh delete mode 100644 Xisop/src/ports/amiga/makedefs.sh delete mode 100644 Xisop/src/ports/amiga/support.h delete mode 100644 Xisop/src/ports/amiga/support_c.c delete mode 100644 Xisop/src/ports/amiga/support_s.s delete mode 100755 Xisop/src/processors/i386/make.sh delete mode 100644 Xisop/src/processors/i386/support.h delete mode 100644 Xisop/src/processors/i386/support.s delete mode 100755 Xisop/src/processors/m68k/clean.sh delete mode 100755 Xisop/src/processors/m68k/make.sh delete mode 100644 Xisop/src/processors/m68k/math/README delete mode 100644 Xisop/src/processors/m68k/math/divsi3.s delete mode 100644 Xisop/src/processors/m68k/math/modsi3.s delete mode 100644 Xisop/src/processors/m68k/math/mulsi3.s delete mode 100644 Xisop/src/processors/m68k/math/udivsi3.s delete mode 100644 Xisop/src/processors/m68k/math/umodsi3.s delete mode 100644 Xisop/src/processors/m68k/support.h delete mode 100644 Xisop/src/processors/m68k/support.s delete mode 100644 mmsoftware/BUGS delete mode 100644 mmsoftware/LICENSE delete mode 100644 mmsoftware/TODO delete mode 100644 mmsoftware/apache-mmstat/GNUmakefile delete mode 100644 mmsoftware/apache-mmstat/apache-mmstat.8 delete mode 100644 mmsoftware/apache-mmstat/apache-mmstat.c delete mode 100755 mmsoftware/apache-mmstat/makepart.sh delete mode 100644 mmsoftware/bot/Makefile delete mode 100644 mmsoftware/bot/irc.txt delete mode 100644 mmsoftware/bot/newbot.c delete mode 100644 mmsoftware/bot/zenbot.c delete mode 100755 mmsoftware/clean.sh delete mode 100755 mmsoftware/install.sh delete mode 100644 mmsoftware/js/classes/js_errno.c delete mode 100644 mmsoftware/js/classes/js_errno.h delete mode 100644 mmsoftware/js/classes/js_fd.c delete mode 100644 mmsoftware/js/classes/js_fd.h delete mode 100644 mmsoftware/js/classes/js_global.c delete mode 100644 mmsoftware/js/classes/js_global.h delete mode 100644 mmsoftware/js/classes/js_mysql.c delete mode 100644 mmsoftware/js/classes/js_mysql.h delete mode 100644 mmsoftware/js/classes/js_pgsql.c delete mode 100644 mmsoftware/js/classes/js_pgsql.h delete mode 100644 mmsoftware/js/classes/js_signal.c delete mode 100644 mmsoftware/js/classes/js_signal.h delete mode 100644 mmsoftware/js/js-sh/src/GNUmakefile delete mode 100644 mmsoftware/js/js-sh/src/js-sh.c delete mode 100755 mmsoftware/js/util/spidermonkey-config delete mode 100755 mmsoftware/make.sh delete mode 100644 mmsoftware/mmanoncvs/GNUmakefile delete mode 100644 mmsoftware/mmanoncvs/mmanoncvs.8 delete mode 100644 mmsoftware/mmanoncvs/mmanoncvs.c delete mode 100644 mmsoftware/mmanoncvs/mmanoncvs.conf.5 delete mode 100644 mmsoftware/mmanoncvs/mmanoncvs.list.5 delete mode 100644 mmsoftware/mmftpd/ChangeLog delete mode 100644 mmsoftware/mmftpd/GNUmakefile delete mode 100644 mmsoftware/mmftpd/README delete mode 100755 mmsoftware/mmftpd/clean.sh delete mode 100644 mmsoftware/mmftpd/etc/mmftpd.conf delete mode 100644 mmsoftware/mmftpd/etc/mmftpdpasswd delete mode 100755 mmsoftware/mmftpd/install.sh delete mode 100755 mmsoftware/mmftpd/make.sh delete mode 100755 mmsoftware/mmftpd/scripts/mmftpd.sh delete mode 100644 mmsoftware/mmftpd/src/Makefile delete mode 100755 mmsoftware/mmftpd/src/makepart.sh delete mode 100644 mmsoftware/mmftpd/src/mmftpd.8 delete mode 100644 mmsoftware/mmftpd/src/mmftpd.c delete mode 100644 mmsoftware/mmftpd/src/mmftpd.conf.5 delete mode 100644 mmsoftware/mmftpd/src/mmftpd.h delete mode 100644 mmsoftware/mmftpd/src/mmftpdpasswd.5 delete mode 100644 mmsoftware/mmidentd/GNUmakefile delete mode 100644 mmsoftware/mmidentd/mmidentd.c delete mode 100644 mmsoftware/mmlib/Makefile delete mode 100644 mmsoftware/mmlib/makedefs.sh delete mode 100755 mmsoftware/mmlib/makefuncs.sh delete mode 100755 mmsoftware/mmlib/makepart.sh delete mode 100644 mmsoftware/mmlib/mm_pth_pool.c delete mode 100644 mmsoftware/mmlib/mm_pth_pool.h delete mode 100644 mmsoftware/mmlib/mm_thread_abstraction.h delete mode 100644 mmsoftware/mmlib/mmalarm.3 delete mode 100644 mmsoftware/mmlib/mmalarm.c delete mode 100644 mmsoftware/mmlib/mmalarm.h delete mode 100644 mmsoftware/mmlib/mmarch.c delete mode 100644 mmsoftware/mmlib/mmarch.h delete mode 100644 mmsoftware/mmlib/mmbit.h delete mode 100644 mmsoftware/mmlib/mmbstring.c delete mode 100644 mmsoftware/mmlib/mmbstring.h delete mode 100644 mmsoftware/mmlib/mmfd.3 delete mode 100644 mmsoftware/mmlib/mmfd.c delete mode 100644 mmsoftware/mmlib/mmfd.h delete mode 100644 mmsoftware/mmlib/mmfifo.3 delete mode 100644 mmsoftware/mmlib/mmfifo.c delete mode 100644 mmsoftware/mmlib/mmfifo.h delete mode 100644 mmsoftware/mmlib/mmhash.3 delete mode 100644 mmsoftware/mmlib/mmhash.c delete mode 100644 mmsoftware/mmlib/mmhash.h delete mode 100644 mmsoftware/mmlib/mmheap.3 delete mode 100644 mmsoftware/mmlib/mmheap.c delete mode 100644 mmsoftware/mmlib/mmheap.h delete mode 100644 mmsoftware/mmlib/mmlifo.3 delete mode 100644 mmsoftware/mmlib/mmlifo.h delete mode 100644 mmsoftware/mmlib/mmlimitrate.3 delete mode 100644 mmsoftware/mmlib/mmlimitrate.c delete mode 100644 mmsoftware/mmlib/mmlimitrate.h delete mode 100644 mmsoftware/mmlib/mmlist.3 delete mode 100644 mmsoftware/mmlib/mmlist.h delete mode 100644 mmsoftware/mmlib/mmloadarray.c delete mode 100644 mmsoftware/mmlib/mmloadarray.h delete mode 100644 mmsoftware/mmlib/mmlog.c delete mode 100644 mmsoftware/mmlib/mmlog.h delete mode 100644 mmsoftware/mmlib/mmpath.3 delete mode 100644 mmsoftware/mmlib/mmpath.c delete mode 100644 mmsoftware/mmlib/mmpath.h delete mode 100644 mmsoftware/mmlib/mmpool.3 delete mode 100644 mmsoftware/mmlib/mmpool.c delete mode 100644 mmsoftware/mmlib/mmpool.h delete mode 100644 mmsoftware/mmlib/mmrc4.c delete mode 100644 mmsoftware/mmlib/mmrc4.h delete mode 100644 mmsoftware/mmlib/mmrc4util.3 delete mode 100644 mmsoftware/mmlib/mmrc4util.c delete mode 100644 mmsoftware/mmlib/mmrc4util.h delete mode 100644 mmsoftware/mmlib/mmreadcfg.c delete mode 100644 mmsoftware/mmlib/mmreadcfg.h delete mode 100644 mmsoftware/mmlib/mmserver.c delete mode 100644 mmsoftware/mmlib/mmserver.h delete mode 100644 mmsoftware/mmlib/mmserver2.3 delete mode 100644 mmsoftware/mmlib/mmserver2.c delete mode 100644 mmsoftware/mmlib/mmserver2.h delete mode 100644 mmsoftware/mmlib/mmsql.c delete mode 100644 mmsoftware/mmlib/mmsql.h delete mode 100644 mmsoftware/mmlib/mmstat.3 delete mode 100644 mmsoftware/mmlib/mmstat.c delete mode 100644 mmsoftware/mmlib/mmstat.h delete mode 100644 mmsoftware/mmlib/mmstr.c delete mode 100644 mmsoftware/mmlib/mmstr.h delete mode 100644 mmsoftware/mmlib/mmstring.c delete mode 100644 mmsoftware/mmlib/mmstring.h delete mode 100644 mmsoftware/mmlib/mmtypes.h delete mode 100644 mmsoftware/mmmail/ChangeLog delete mode 100644 mmsoftware/mmmail/GNUmakefile delete mode 100644 mmsoftware/mmmail/README delete mode 100755 mmsoftware/mmmail/clean.sh delete mode 100644 mmsoftware/mmmail/etc/mmpop3d.conf delete mode 100644 mmsoftware/mmmail/etc/mmsmtpd.conf delete mode 100755 mmsoftware/mmmail/install.sh delete mode 100755 mmsoftware/mmmail/make.sh delete mode 100755 mmsoftware/mmmail/scripts/mmpop3d.sh delete mode 100755 mmsoftware/mmmail/scripts/mmsmtpd.sh delete mode 100644 mmsoftware/mmmail/scripts/tables.sql delete mode 100644 mmsoftware/mmmail/scripts/upgrade-0.0.13.sql delete mode 100644 mmsoftware/mmmail/scripts/upgrade-0.0.21.sql delete mode 100644 mmsoftware/mmmail/scripts/upgrade-0.0.24.sql delete mode 100644 mmsoftware/mmmail/src/mmmail.8 delete mode 100644 mmsoftware/mmmail/src/mmpop3d/Makefile delete mode 100755 mmsoftware/mmmail/src/mmpop3d/makepart.sh delete mode 100644 mmsoftware/mmmail/src/mmpop3d/mmpop3d.8 delete mode 100644 mmsoftware/mmmail/src/mmpop3d/mmpop3d.c delete mode 100644 mmsoftware/mmmail/src/mmpop3d/mmpop3d.conf.5 delete mode 100644 mmsoftware/mmmail/src/mmpop3d/mmpop3d.h delete mode 100644 mmsoftware/mmmail/src/mmrelayd/mmrelayd.c delete mode 100644 mmsoftware/mmmail/src/mmrelayd/mmrelayd.h delete mode 100644 mmsoftware/mmmail/src/mmsmtpd/Makefile delete mode 100755 mmsoftware/mmmail/src/mmsmtpd/makepart.sh delete mode 100644 mmsoftware/mmmail/src/mmsmtpd/mmsmtpd.8 delete mode 100644 mmsoftware/mmmail/src/mmsmtpd/mmsmtpd.c delete mode 100644 mmsoftware/mmmail/src/mmsmtpd/mmsmtpd.conf.5 delete mode 100644 mmsoftware/mmmail/src/mmsmtpd/mmsmtpd.h delete mode 100644 mmsoftware/mmmail2/ChangeLog delete mode 100644 mmsoftware/mmmail2/README delete mode 100755 mmsoftware/mmmail2/notes/convert.sh delete mode 100644 mmsoftware/mmmail2/notes/mmmail-design.lyx delete mode 100644 mmsoftware/mmmail2/notes/mmmail-tables.dia delete mode 100644 mmsoftware/mmpasswd/GNUmakefile delete mode 100644 mmsoftware/mmpasswd/Makefile delete mode 100755 mmsoftware/mmpasswd/make.sh delete mode 100755 mmsoftware/mmpasswd/makepart.sh delete mode 100644 mmsoftware/mmpasswd/mmpasswd.8 delete mode 100644 mmsoftware/mmpasswd/mmpasswd.c delete mode 100644 mmsoftware/mmsendmail/GNUmakefile delete mode 100644 mmsoftware/mmsendmail/Makefile delete mode 100644 mmsoftware/mmsendmail/mmsendmail.c delete mode 100644 mmsoftware/mmspawnd/GNUmakefile delete mode 100644 mmsoftware/mmspawnd/mmspawnd.8 delete mode 100644 mmsoftware/mmspawnd/mmspawnd.c delete mode 100644 mmsoftware/mmspawnd/mmspawnd.conf.5 delete mode 100644 mmsoftware/mmspawnd/new/GNUmakefile delete mode 100644 mmsoftware/mmspawnd/new/README delete mode 100644 mmsoftware/mmspawnd/new/mmspawnd.c delete mode 100644 mmsoftware/mmspawnd2/GNUmakefile delete mode 100644 mmsoftware/mmspawnd2/mmspawnd.8 delete mode 100644 mmsoftware/mmspawnd2/mmspawnd.c delete mode 100644 mmsoftware/mmspawnd2/mmspawnd.conf.5 delete mode 100644 mmsoftware/mmstatd/ChangeLog delete mode 100644 mmsoftware/mmstatd/GNUmakefile delete mode 100644 mmsoftware/mmstatd/README delete mode 100755 mmsoftware/mmstatd/clean.sh delete mode 100644 mmsoftware/mmstatd/etc/mmstatd.conf delete mode 100644 mmsoftware/mmstatd/future.txt delete mode 100755 mmsoftware/mmstatd/install.sh delete mode 100755 mmsoftware/mmstatd/make.sh delete mode 100644 mmsoftware/mmstatd/src/Makefile delete mode 100755 mmsoftware/mmstatd/src/makepart.sh delete mode 100644 mmsoftware/mmstatd/src/mmstat.8 delete mode 100644 mmsoftware/mmstatd/src/mmstat.c delete mode 100644 mmsoftware/mmstatd/src/mmstatd.8 delete mode 100644 mmsoftware/mmstatd/src/mmstatd.c delete mode 100644 mmsoftware/mmstatd/src/mmstatd.conf.5 delete mode 100644 mmsoftware/mmstatd/src/mmstatd.h delete mode 100644 mmsoftware/mmsucom/GNUmakefile delete mode 100644 mmsoftware/mmsucom/Makefile delete mode 100644 mmsoftware/mmsucom/README delete mode 100644 mmsoftware/mmsucom/mmsucom.c delete mode 100644 mmsoftware/mmsucom/mmsucomd.c delete mode 100644 mmsoftware/mmsucom/mmsucomd.conf delete mode 100644 mmsoftware/stringtest/GNUmakefile delete mode 100644 mmsoftware/stringtest/stringtest.c delete mode 100644 site/contributors.html delete mode 100644 site/cvs.html delete mode 100644 site/donations.html delete mode 100644 site/favicon.ico delete mode 100644 site/images/CVS.jpg delete mode 100644 site/images/key.jpg delete mode 100644 site/images/sigil.jpg delete mode 100644 site/index.html delete mode 100644 site/mirrors.html delete mode 100644 site/new/TODO delete mode 100644 site/new/build/GNUmakefile delete mode 100644 site/new/build/README delete mode 100644 site/new/build/TODO delete mode 100644 site/new/build/english/faq_mmftpd.txt delete mode 100644 site/new/build/english/faq_mmmail.txt delete mode 100644 site/new/build/english/faq_mmstatd.txt delete mode 100644 site/new/build/english/menu_languages.txt delete mode 100644 site/new/build/english/menu_main.txt delete mode 100644 site/new/build/english/menu_mirrors.txt delete mode 100644 site/new/build/english/page_contact.txt delete mode 100644 site/new/build/english/page_contributors.txt delete mode 100644 site/new/build/english/page_cvs.txt delete mode 100644 site/new/build/english/page_donations.txt delete mode 100644 site/new/build/english/page_index.txt delete mode 100644 site/new/build/english/page_mirrors.txt delete mode 100644 site/new/build/english/page_philosophy.txt delete mode 100644 site/new/build/english/page_projects.txt delete mode 100644 site/new/build/english/page_software.txt delete mode 100644 site/new/build/english/soft_descriptions.txt delete mode 100644 site/new/build/mmsite.c delete mode 100644 site/new/build/mmsite.conf delete mode 100644 site/new/build/site_faq.txt delete mode 100644 site/new/build/site_languages.txt delete mode 100644 site/new/build/site_mirrors.txt delete mode 100644 site/new/build/site_pages.txt delete mode 100644 site/new/build/site_software.txt delete mode 100644 site/new/build/soft_development.txt delete mode 100644 site/new/build/soft_old.txt delete mode 100644 site/new/build/soft_stable.txt delete mode 100644 site/philosophy.html delete mode 100644 site/projects.html delete mode 100644 site/software.html delete mode 100644 tests/js-test/README delete mode 100644 tests/js-test/js/copy.js delete mode 100644 tests/js-test/js/fd.js delete mode 100644 tests/js-test/js/game/objects.js delete mode 100644 tests/js-test/js/httpd/httpd.js delete mode 100644 tests/js-test/js/httpd/ml_clean.js delete mode 100644 tests/js-test/js/httpd/ml_machine.js delete mode 100644 tests/js-test/js/httpd/options.js delete mode 100644 tests/js-test/js/httpd/root.js delete mode 100644 tests/js-test/js/httpd/string.js delete mode 100644 tests/js-test/js/ml.js delete mode 100644 tests/js-test/js/ml2.js delete mode 100644 tests/js-test/js/ml3.js delete mode 100644 tests/js-test/js/ml4.js delete mode 100644 tests/js-test/js/ml5.js delete mode 100644 tests/js-test/js/ml6.js delete mode 100644 tests/js-test/js/parse.js delete mode 100644 tests/js-test/js/poll_test.js delete mode 100644 tests/js-test/js/sock_httpd.js delete mode 100644 tests/js-test/js/sock_listen.js delete mode 100644 tests/js-test/js/test.js delete mode 100644 tests/js-test/js/test2.js delete mode 100644 tests/js-test/js/test3.js delete mode 100644 tests/js-test/src/GNUmakefile delete mode 100644 tests/js-test/src/classes/js_errno.c delete mode 100644 tests/js-test/src/classes/js_errno.h delete mode 100644 tests/js-test/src/classes/js_fd.c delete mode 100644 tests/js-test/src/classes/js_fd.h delete mode 100644 tests/js-test/src/classes/js_global.c delete mode 100644 tests/js-test/src/classes/js_global.h delete mode 100644 tests/js-test/src/classes/js_mysql.c delete mode 100644 tests/js-test/src/classes/js_mysql.h delete mode 100644 tests/js-test/src/classes/js_pgsql.c delete mode 100644 tests/js-test/src/classes/js_pgsql.h delete mode 100644 tests/js-test/src/classes/js_signal.c delete mode 100644 tests/js-test/src/classes/js_signal.h delete mode 100644 tests/js-test/src/js-server.c delete mode 100755 tests/js-test/util/spidermonkey-config delete mode 100644 tests/kqueue/GNUmakefile delete mode 100644 tests/kqueue/README delete mode 100644 tests/kqueue/client.c delete mode 100644 tests/kqueue/client.h delete mode 100644 tests/kqueue/conf.h delete mode 100644 tests/kqueue/daemon.c delete mode 100644 tests/kqueue/daemon.h delete mode 100644 tests/kqueue/kqueue.c delete mode 100644 tests/kqueue/kqueue.h delete mode 100644 tests/kqueue/main.c delete mode 100644 tests/kqueue/net.c delete mode 100644 tests/kqueue/net.h delete mode 100644 tests/kqueue/packets.c delete mode 100644 tests/kqueue/packets.h delete mode 100644 tests/kqueue/recvq.c delete mode 100644 tests/kqueue/recvq.h delete mode 100644 tests/kqueue/sendq.c delete mode 100644 tests/kqueue/sendq.h delete mode 100644 tests/memory/README delete mode 100644 tests/pthread_utils/GNUmakefile delete mode 100644 tests/pthread_utils/README delete mode 100644 tests/pthread_utils/mm_pthread_debug.h delete mode 100644 tests/pthread_utils/mm_pthread_msg.c delete mode 100644 tests/pthread_utils/mm_pthread_msg.h delete mode 100644 tests/pthread_utils/mm_pthread_poll.c delete mode 100644 tests/pthread_utils/mm_pthread_poll.h delete mode 100644 tests/pthread_utils/mm_pthread_pool.c delete mode 100644 tests/pthread_utils/mm_pthread_pool.h delete mode 100644 tests/pthread_utils/mm_pthread_sleep.c delete mode 100644 tests/pthread_utils/mm_pthread_sleep.h delete mode 100644 tests/pthread_utils/tests/msg_test.c delete mode 100644 tests/pthread_utils/tests/poll_test.c delete mode 100644 tests/pthread_utils/tests/polltest.c delete mode 100644 tests/pthread_utils/tests/sigtest.c delete mode 100644 tests/rotate/GNUmakefile delete mode 100644 tests/rotate/main.c delete mode 100644 tests/sdl-client/GNUmakefile delete mode 100644 tests/sdl-client/README delete mode 100644 tests/sdl-client/bmp/FedCA.bmp delete mode 100644 tests/sdl-client/bmp/README delete mode 100644 tests/sdl-client/bmp/RomDD.bmp delete mode 100755 tests/sdl-client/bmp/fedship.bmp delete mode 100755 tests/sdl-client/bmp/indship.bmp delete mode 100755 tests/sdl-client/bmp/kliship.bmp delete mode 100644 tests/sdl-client/bmp/misc-fixed-bold-r-normal--13-120-75-75-c-80-iso8859-1.bmp delete mode 100644 tests/sdl-client/bmp/misc-fixed-bold-r-normal--15-120-100-100-c-90-iso8859-1.bmp delete mode 100644 tests/sdl-client/bmp/misc-fixed-medium-r-normal--13-120-75-75-c-80-iso8859-1.bmp delete mode 100644 tests/sdl-client/bmp/misc-fixed-medium-r-normal--15-120-100-100-c-90-iso8859-1.bmp delete mode 100755 tests/sdl-client/bmp/oriship.bmp delete mode 100755 tests/sdl-client/bmp/romship.bmp delete mode 100755 tests/sdl-client/bmp/sbexpl.bmp delete mode 100755 tests/sdl-client/bmp/shexpl.bmp delete mode 100644 tests/sdl-client/conf.h delete mode 100644 tests/sdl-client/debug.c delete mode 100644 tests/sdl-client/debug.h delete mode 100644 tests/sdl-client/decode.c delete mode 100644 tests/sdl-client/decode.h delete mode 100644 tests/sdl-client/dlist.h delete mode 100644 tests/sdl-client/encode.c delete mode 100644 tests/sdl-client/fnt/10x20.fnt delete mode 100644 tests/sdl-client/fnt/5x7.fnt delete mode 100644 tests/sdl-client/fnt/5x8.fnt delete mode 100644 tests/sdl-client/fnt/6x10.fnt delete mode 100644 tests/sdl-client/fnt/6x12.fnt delete mode 100644 tests/sdl-client/fnt/6x13.fnt delete mode 100644 tests/sdl-client/fnt/6x13B.fnt delete mode 100644 tests/sdl-client/fnt/6x13O.fnt delete mode 100644 tests/sdl-client/fnt/6x9.fnt delete mode 100644 tests/sdl-client/fnt/7x13.fnt delete mode 100644 tests/sdl-client/fnt/7x13B.fnt delete mode 100644 tests/sdl-client/fnt/7x13O.fnt delete mode 100644 tests/sdl-client/fnt/7x14.fnt delete mode 100644 tests/sdl-client/fnt/7x14B.fnt delete mode 100644 tests/sdl-client/fnt/8x13.fnt delete mode 100644 tests/sdl-client/fnt/8x13B.fnt delete mode 100644 tests/sdl-client/fnt/8x13O.fnt delete mode 100644 tests/sdl-client/fnt/9x15.fnt delete mode 100644 tests/sdl-client/fnt/9x15B.fnt delete mode 100644 tests/sdl-client/fnt/9x18.fnt delete mode 100644 tests/sdl-client/fnt/9x18B.fnt delete mode 100644 tests/sdl-client/main.c delete mode 100644 tests/sdl-client/main.h delete mode 100644 tests/sdl-client/ogg/README delete mode 100644 tests/sdl-client/packets.c delete mode 100644 tests/sdl-client/packets.h delete mode 100644 tests/sdl-client/pool.c delete mode 100644 tests/sdl-client/pool.h delete mode 100644 tests/sdl-client/rawobjs.h delete mode 100644 tests/sdl-client/screen.c delete mode 100644 tests/sdl-client/screen.h delete mode 100644 tests/sdl-client/tests/draw.c delete mode 100644 tests/sdl-client/tests/draw.h delete mode 100644 tests/sdl-client/tests/fonts.c delete mode 100644 tests/sdl-client/tests/fonts.h delete mode 100644 tests/sdl-client/tests/msg.c delete mode 100644 tests/sdl-client/tests/netrek-like.c delete mode 100644 tests/sdl-client/tests/rot.c delete mode 100644 tests/sdl-client/tests/snd.c delete mode 100644 tests/sdl-client/thread_msg.c delete mode 100644 tests/sdl-client/thread_msg.h delete mode 100644 tests/sdl-client/thread_net_recv.c delete mode 100644 tests/sdl-client/thread_net_recv.h delete mode 100644 tests/sdl-client/thread_net_send.c delete mode 100644 tests/sdl-client/thread_net_send.h delete mode 100644 tests/sdl-client/wav/README delete mode 100755 tests/sdl-client/wav/nt_cloaked.wav delete mode 100755 tests/sdl-client/wav/nt_explosion_other.wav delete mode 100755 tests/sdl-client/wav/nt_fire_torp_other.wav delete mode 100755 tests/sdl-client/wav/nt_plasma_hit.wav delete mode 100755 tests/sdl-client/wav/nt_shield_down.wav delete mode 100755 tests/sdl-client/wav/nt_shield_up.wav delete mode 100755 tests/sdl-client/wav/nt_uncloak.wav delete mode 100644 tests/zlib/README delete mode 100644 tests/zlib/deflate.c delete mode 100644 tests/zlib/inflate.c diff --git a/Xisop/LICENSE b/Xisop/LICENSE deleted file mode 100644 index aadb1b9..0000000 --- a/Xisop/LICENSE +++ /dev/null @@ -1,34 +0,0 @@ -/* $Id: LICENSE,v 1.1 2003/10/26 21:19:57 mmondor Exp $ */ - -/* - * Copyright (c) 2003, Matthew Mondor - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Matthew Mondor. - * 4. The name of Matthew Mondor may not be used to endorse or promote - * products derived from this software without specific prior written - * permission. - * 5. Redistribution of source code may not be released under the terms of - * any GNU Public License derivate. - * - * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ diff --git a/Xisop/README b/Xisop/README deleted file mode 100644 index 7681f89..0000000 --- a/Xisop/README +++ /dev/null @@ -1,53 +0,0 @@ -$Id: README,v 1.7 2004/06/04 02:31:51 mmondor Exp $ - - - -Xisop Copyright 2001-2003, Matthew Mondor, -All rights reserved. - -Currently under development. The only current port is Amiga, and an .uaerc -is provided for the UAE emulator to test it and possibly enhance it :) - Xisop/src/ports/amiga/boot/DOTuaerc -It is recommended to run the UAE emulator using the -T command line parameter -when on unix systems (or setting the x11.use_mitshm=true in the ~/.uaerc file). -Note that kickstart 3.0 or later is required, and that I will not provide this -file to anyone, as it is copyrighted material. You can buy a kickstart kit, or -mirror the one of your amiga to file. The models which used to ship with 3.0+ -natively were the A1200 and A4000. It is possible to make Xisop kickstart 1.3 -compatible if it was to be used to make games, since only the bootloader is -AmigaOS dependent. - -The compiled kernel is 30k in size if compiled with statistics support, or -19k otherwise. - -The source of the port-dependent initialization function (which currently -launches tasks displaying colors for testing and demonstrating) is located at - Xisop/src/ports/amiga/boot/init.c - -Some development notes are also being worked on according to the internal -implementation details, programming style notes and how to port Xisop -(also under development as the kernel interfaces are being stabilized). - Xisop/doc/xisop.pdf -This file is built from xisop.lyx which is maintained using the LyX utility. - -Almost all the current code and documentation were re-written from scratch in -Febuary and March 2003. The old Xisop code somewhat served as reference -when needed. - -Please read TODO on what currently needs to be worked on to continue the -project, what was done and what needs debugging. The project is to eventually -be fully released under BSD-style license when at least one port works fine. -Make sure to also read the documentation in doc/. - -To build the amiga port you should specify the location of the cross building -tools in Xisop/src/ports/amiga/makedefs.sh, or use the defaults if you have -a NetBSD 1.6.1 source, in which case you can simply use the build.sh script -provided in /usr/src to build the netbsdelf-m68k target toolchain (which only -needs to be done once): - cd /usr/src - ./build.sh -a m68k -t -Then: - cd Xisop/src - ./make.sh -t amiga - -Matthew Mondor diff --git a/Xisop/TODO b/Xisop/TODO deleted file mode 100644 index 9c2d318..0000000 --- a/Xisop/TODO +++ /dev/null @@ -1,163 +0,0 @@ -$Id: TODO,v 1.20 2004/10/14 15:13:20 mmondor Exp $ - - -- Import modifications from mmsoftware/mmlib: - - New more efficient pool allocator, with enhanced debugging capability. - This could imply reworking some of the whole system. - - Maybe incorporate C byteorder library, so that less processor-specific - code is required (of course, they can override C ones with asm ones). - - -Working: -======= -- Memory management system and ANSI-C related functions -- Syscalls, including one which allows to execute arbitrary code in supervisor - mode -- Exclusive, recursive and read/write locks -- User interrupt facilities -- Preemptive multitasking, _yield() and task_sleep()/task_wakeup() -- CPU saving ideling - When no tasks are on the ready queue to run, the _scontext _ctx_t - defined in scheduler.c is resumed, which in fact returns to main.c's - main() function, which sole purpose is to sys_idle(), in a loop, - which suspends the processor until the next interrupt occurs. -- Serious exceptions generate a display showing a number of lines which - corresponds to the actual error (_ecatch() parameter), useful to debug - the current Amiga port. -- Useful statistics book-keeping of global counters -- Inter-task signals -- Inter-task communication reliable ports and messages -- The init and task reaper system tasks -- Operations which need fast lookup tables to use kernlib/hash.[ch]. - This is done for some system lists, such as for port_find(). -- Multiple tasks shareing a common mpool_t created with TF_SHARED - - -Bugs to work out: -================ -- There again is some problem with memory or stack sizes. When DEBUG and - STATISTICS are enabled in config.h, things do not work as intended. - This seems to simply arise now that the kernel image is about a k larger. - For some reason, the ports/amiga/boot/config.h values do not seem to - always generate proper kernel images, for instance when I increase the - stack size to 16384 I get a guru meditation... -- The scheduler which takes task priorities into account is currently not - working. There is a temporary replacement scheduler which works now and - only performs round-robin when preempting tasks. The old one which has - some bugs is found in src/common/kernel/scheduler.c and is called - old_schedule(). -- Signals and message ports - Some hack should be found a more appropriate solution: signal_send() - needs to _yield() twice (See src/common/kernel/signal.c). - For some not yet determined reason, tasks would all eventually be - stuck on the wait queue if this was not done. - - -Remaining to complete: -===================== -- Add system similar to mmlib/mmalarm(3) to Xisop, to go with the existing - MI interrupt hook management functions -- Perhaps use hashtables in exceptions hooks management instead of linked - lists? Is this needed or wanted? Running through a hash table is slower, - but lookups are faster. Think about it and see which is best to use. -- Fix issue with amiga port init stack size... m68k usermode() could - be specified a stack size for instance, at least. We also could - reserve some memory for the stack in advance and supply it... -- Implement setjmp()/longjmp() for i386 -- CPU specific _bfill16 and _bfill32 etc (at least _bfillint or such), - to efficiently fill a native word size with an 8-bit pattern. This - could be used by the string library in memset() for instance... -- Probably that the cases of usecount (devicenode_t, mpool_t) could use - an _rlock_t for more efficiency. It is implemented in assembler, and - is sure to be atomic. -- Finish dprintf() FIFO operation optimizations, and write a generic - vsnprintf() implementation with stdarg. We want to enhance DPRINTF() - to print module and line numbers, etc. -- _panic() -- Console and printf() (along with corresponding console.device) -- Map remaining exceptions to output messages on console - For the console.device to eventually exist, the port primitives are - essencial. So until they work reliably it's not really possible to - implement right now (at least on Amiga). - It would be easy to use the video hardware memory on a i386 port - however, so a i386 port could perhaps actually help in development - for the rest :) - Ports implementation is now working reliably. Revise. -- Probably that the statistics would also be nice to have on a per-task basis. - Moreover, they are not as useful as they would if a console existed :< - currently, the DPRINTF() system logs to a FIFO buffer. -- Xisop shared libraries -- Xisop devices as a synchronization abstraction over the message ports system -- Xisop handlers as a higher-level (filesystem/file) abstraction over devices - libraries and/or hardware -- Relocatable binary loader - This unfortunately either implies writing a full fledged ELF or a.out - loader, or a BFD library for GCC ld to output in a new desired format. - The ELF loader was started but was not completed (not included in the - current sources). ELF unfortunately seems really bloated for the - actual needs of this project, and it's state is uncertain. - The loader would be used to load shared libraries, and executable - tasks, including devices and handlers. - For the moment, the system is monolithic and soon a simple interface - will be provided to allow the port-specific code to provide a list - of tasks to be launched by Xisop init. - That alone, even monolithic, allows Xisop to stand as a nice C - interface which most code is portable among 32-bit systems, to abstract - interrupt facilities, shared memory access and multitasking for - various applications. Very small, it can fit the application on a - floppy (which includes the kernel). -- timer.device -- input.device -- console.device -- Add necessary functions to allow linking in the common/kernlib/hash.c module - - - -NEW MEMORY MANAGEMENT SYSTEM NOTES: - -As before, we need several types of memory. -For each memory type, we should still attach/detach memory chunks, like now. - -For each memory chunk, a new system should be implemented for page-level -contiguous memory management and freeing, as well as a new pool allocator, -based on the idea of the new mmlib/mmpool(3) one. - -Because Xisop does not rely on MMU/PMMU, we can simplify the allocator to a -simple block allocator, without even worrying about page boundaries, if we -wanted. This would also mean that a pool page could be virtual, that is, could -not be dependent on the system page size at all, if we wanted. If that was the -case, the current mmlib/mmpool(3) allocator could be used as-is. - -Not to say that, even if we respected page alignment, we still could use a -better allocator. - -A good idea would be to maintain a list of contiguous page (or memory bytes) -chunks. Nodes of this list would be split as necessary to provide the -requested size in the allocation functions. The freeing functions would need -to attempt to coalesce the contigious chunks together when possible as well. - -Perhaps that we also would like a best-fit strategy rather than first-fit when -allocating, so that we could favor contiguous memory chunks that are closest -in size to the requested amount, rather than always splitting large chunks -unnecessarily. This would reduce fragmentation, although being slower at -allocation. However, because this would affect the page allocator, to which -calls would be reduced by the pool allocators, this could be reasonable. - -Perhaps that to observe the best-fit strategy it would be possible to somewhat -maintain a sorted index or such, of the smaller to larger available chunks. -It needs to be verified that the code and memory overhead for such an index -is negligeable, however. Moreover, would keeping a sorted list of free chunks -useful for performance? How would the allocator efficiently perform jumps in -the list? Wouldn't it need to be a tree, instead? If we used one, wouldn't we -need recursion or special iteration for both node insertion and lookup? I will -need to check that out. What I should do is count the iterations in my similar -sorted tree system in dnamed, to get an idea of the actual code overhead -involved. - -struct contiguous_chunk { - node_t chunks_node; /* To link in free/allocated chunks list */ - node_t sorted_node; /* To link in free chunks sorted list */ - void *address; /* Starting address of free memory */ - size_t length; /* Size of contiguous free memory */ - u_int32_t pages; /* Or, could be number of free pages */ -}; diff --git a/Xisop/doc/clean.sh b/Xisop/doc/clean.sh deleted file mode 100755 index f4bf49d..0000000 --- a/Xisop/doc/clean.sh +++ /dev/null @@ -1,3 +0,0 @@ -# $Id: clean.sh,v 1.2 2004/06/06 04:21:45 mmondor Exp $ - -rm -f xisop.dvi xisop.tex xisop.lyx~ xisop.pdf xisop.ps xisop.toc .log diff --git a/Xisop/doc/make.sh b/Xisop/doc/make.sh deleted file mode 100755 index 2300408..0000000 --- a/Xisop/doc/make.sh +++ /dev/null @@ -1,11 +0,0 @@ -# $Id: make.sh,v 1.3 2004/06/06 04:21:05 mmondor Exp $ - -# First export LyX document to LaTeX then run this script - -lyx -e latex xisop.lyx -latex xisop.tex -latex xisop.tex -rm -f xisop.ps xisop.aux xisop.log .log -dvips -Pcmz -Pamz -o xisop.ps -t letter -D 300 -Z xisop.dvi -ps2pdf xisop.ps -rm -f *~ diff --git a/Xisop/doc/xisop.lyx b/Xisop/doc/xisop.lyx deleted file mode 100644 index ad156ca..0000000 --- a/Xisop/doc/xisop.lyx +++ /dev/null @@ -1,10041 +0,0 @@ -#LyX 1.2 created this file. For more info see http://www.lyx.org/ -\lyxformat 220 -\textclass article -\language english -\inputencoding auto -\fontscheme default -\graphics default -\paperfontsize default -\spacing single -\papersize letterpaper -\paperpackage a4 -\use_geometry 1 -\use_amsmath 0 -\use_natbib 0 -\use_numerical_citations 0 -\paperorientation portrait -\topmargin 0.5in -\bottommargin 0.5in -\secnumdepth 3 -\tocdepth 3 -\paragraph_separation indent -\defskip medskip -\quotes_language english -\quotes_times 2 -\papercolumns 1 -\papersides 1 -\paperpagestyle default - -\layout Title - -Xisop kernel development notes -\layout Author -\pagebreak_bottom -Copyright (c) 2001-2003, Matthew Mondor -\newline -All rights reserved. -\layout Standard - - -\begin_inset LatexCommand \tableofcontents{} - -\end_inset - - -\layout Section -\pagebreak_top -General development notes -\layout Standard - -Before attempting to write software for Xisop to expand it's funtionality, - or before porting Xisop to a new architecture, it is recommended to carefully - read this manual entirely. - It attempts to answer all questions one could have about it's organization - and build process, as well as style and conventions one must follow for - code consistancy with the rest of the project. -\layout Subsection - -Development software used -\layout Standard - -To develop Xisop, the GNU GCC suite of compiler, assembler, linker and binutils - software was chosen. - The main reasons for this consists of cost savings, and portability. - GCC has support for many different CPU types was a main factor. - Moreover, the AT&T assembly syntax allows to somewhat obtain some consistency. -\layout Standard - -An effort was made to try to not support GCC-specific functions however, - for portability reasons. - For instance, the various -\emph on -_spl -\emph default -* -\emph on -() -\emph default - functions are implemented as macros around a separate assembly function, - located in the assembler -\emph on -.s -\emph default - files, rather than embedded inside the C code using inline assembly directives. - ( -\emph on -XXX -\emph default - -\emph on -actually they may be fixed to be GCC-dependent soon heh -\emph default -). -\layout Standard - -A choice was made to not use GNU or BSD make. - Instead, -\emph on -/bin/sh -\emph default - is assumed to exist. - This usually consists of a POSIX compliant shell, on GNU systems (and Linux) - the -\emph on -bash -\emph default - shell usually emulates it's behavior and -\emph on -/bin/sh -\emph default - then consists of a symbolic link to -\emph on -/bin/bash -\emph default -. - On BSD systems only POSIX 1003.2 and 1003.2a functionality is usually present - in their -\emph on -/bin/sh -\emph default -, which is what Xisop build scripts are making use of. - As -\emph on - -\emph default -such, -\emph on - bash -\emph default - is therefore not a requirement. -\layout Standard - -This document is written and maintained using LyX. - The UAE (Amiga emulator) and Bochs (i386 emulator) utilities were useful - to develop the current ports. - The original host development system consists of an i386 compatible system - running NetBSD 1.6_STABLE. - This operating system is ideal for programming and cross compiling. - The current Xisop building system was tuned to the NetBSD 1.6.1 toolchain. - To compile the amiga port, for instance, you only should need to use the - build.sh script to build the suite of netbsdelf-m68k tools. - Using the Xisop -\begin_inset Quotes eld -\end_inset - - -\emph on -./make.sh -t amiga -\emph default - -\begin_inset Quotes erd -\end_inset - - command should then build the amiga target. -\layout Standard - -The software was written using the VIm editor, with -\emph on -ts=8 -\emph default -, -\emph on -sw=4 -\emph default - and -\emph on -cindent -\emph default -. -\layout Standard - -If you compile your own gcc with your intended compiling target, you simply - need to change a file to tell the locations of the various building tools. - See the section about -\begin_inset Quotes eld -\end_inset - -the build process -\begin_inset Quotes erd -\end_inset - - later on for more information. -\layout Subsection - -Xisop compatibility with other systems, and where it fits best -\layout Subsubsection - -UNIX, POSIX, BSD, Linux -\layout Standard - -Xisop is definitely not POSIX, although it's functions are simpler than - POSIX is, requireing a small learning curve only to use. - It was not designed as a general-purpose operating system to replace Unix - and provide all it's capabilities. - It's an entity of it's own, simpler and smaller, mostly made to suit the - requirements of restricted embedded systems. - To implement most POSIX requirements a larger system is required, which - generally also results in slower code. - For instance, Xisop addresses ports and tasks as addresses, rather than - IDs. - This solves the problem of allocation and reuse of process IDs. -\layout Standard - -A Xisop task also is lightweight compared to a UNIX-style process. - Moreover, the need for more custom user signals than POSIX environment - provides made it unsuitable to reserve almost all 32 signals to reserved - semantics. - The concept of file descriptors and select()/poll() is also different. - However, SIGPOLL signal was reserved in Xisop for the implementation of - message-based signals and events using a single message port and signal. - This can be used when there can be a large number of entities a task may - be monitoring, and the user signals would not be appropriate. - It would be possible to work a system out using this facility for the 32 - standard Unix signals if the need existed. -\layout Standard - -Although it would be possible to implement POSIX compatibility libraries, - it was not a main goal in Xisop development. -\layout Subsubsection - -Memory and process protection -\layout Standard - -Xisop was not designed to support Memory Management Unit (MMU) coprocessors - and provide page-grained memory protection. - This helped to implement message passing in a very efficient manner, only - passing pointers around, and reduced considerably kernel size. - Moreover, it allows to provide most user-access system functions in a shared - library, reducing considerably the amount of system calls an application - needs to make (accessed through traps). - The number of traps being reduced, the preemtive scheduler is less clobbered. - Calling functions of a shared library happen entirely in the caller's task - allocated CPU time slice, which means that the kernel load is not affected. - Not requireing MMU is also an advantage with Xisop, as it can run with - a single MC68000L8 for instance, and a little RAM. -\layout Standard - -However, we all know that without appropriate memory protection, Xisop cannot - efficiently protect the kernel from user tasks, which can take full control - on the system at any time. - Clean interfaces were however implemented, which internally perform various - sanity checking to ensure the validity of the supplied objects and arguments, - so that common software bugs do not automatically corrupt memory and the - system. - Xisop knows the difference between a valid task, port, device handle, etc, - and invalid ones. - It also knows when to not attempt to free memory if a wrong pointer is - supplied, since -\emph on -mnode_t -\emph default - nodes are also validated using a unique magic cookie. - Such steps permit to minimize system crashes in the event of a software - bug. -\layout Standard - -The kernel comports special macros to aid in realizing object validity sceals - and dependancies checking, which are defined in < -\emph on -common/kernel/object.h -\emph default ->: -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -void\SpecialChar ~ -OBJECT_VALIDATE(objptr,\SpecialChar ~ -objtype) -\emph default - Sets the -\emph on -objptr -\emph default -->object_magic field to -\emph on -objtype -\emph default -. - This type corresponds to one of the -\emph on -OBJECT_ -\emph default -* names which are defined in the same headerfile. -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -void\SpecialChar ~ -OBJECT_INVALIDATE(objptr) -\emph default - Sets the -\emph on -objptr -\emph default -->object_magic field to 0. -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -void\SpecialChar ~ -OBJECT_REGISTER(objptr) -\emph default - Registers -\emph on -objptr -\emph default - object by setting the -\emph on -objptr -\emph default -->object_id field to a unique number. - This number will be used for matching when verifying for proper dependancy - link. - Only objects which may be required as dependancy to others need to use - this. -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -void\SpecialChar ~ -OBJECT_SETDEP(objptr,\SpecialChar ~ -depobjptr) -\emph default - Associates -\emph on -objptr -\emph default - object as requireing the -\emph on -depobjptr -\emph default - object as dependancy for proper function. - What this does is internally set -\emph on -objptr -\emph default -->objdep_magic to -\emph on -depobjptr -\emph default -->magic and -\emph on -objptr -\emph default -->objdep_id to -\emph on -depobjptr -\emph default -->object_id. - As a result, it becomes possible to refuse to perform operations related - to this object if the dependancy link ever dies. -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -bool\SpecialChar ~ -OBJECT_VALID(objptr,\SpecialChar ~ -objtype) -\emph default - First verifies if objptr is non-NULL, and then evaluates if the object - associated with -\emph on -objptr -\emph default - truely corresponds to -\emph on -objtype -\emph default - ( -\emph on -objptr -\emph default - != NULL && -\emph on -objptr -\emph default -->object_magic == -\emph on -objtype -\emph default -). - This consists of the reason why the various -\emph on -OBJECT_ -\emph default -* definitions in the headerfile should consist of unique values which are - unlikely to occur randomly. - Returns TRUE on success, or FALSE otherwise. -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -bool\SpecialChar ~ -OBJECT_DEPENDS(objptr,\SpecialChar ~ -depobjptr) -\emph default - Verifies the integrity of a dependancy link, which was previously linked - using -\emph on -OBJECT_SETDEP() -\emph default - on -\emph on -objptr -\emph default -. - This in fact makes sure that the object it should relate to ( -\emph on -depobjptr -\emph default -) is still valid, and consists of the same one ( -\emph on -depobjptr -\emph default - != NULL && -\emph on -depobjptr -\emph default -->object_magic == -\emph on -objptr -\emph default -->objdep_magic && -\emph on -depobjptr -\emph default -->object_id == -\emph on -objptr -\emph default -->objdep_id). - Returns TRUE on success, or FALSE otherwise. -\layout Standard - -Obviously, the structure of an object only requires to hold the fields which - are required for the functionality it requires according to these macros. - Those fields are all expected to be of type -\emph on -u_int32_t -\emph default -. - This system allows relatively lightweight validity checking of object credentia -ls, without the need for memory protection. - The kernel uses them wherever needed for safety. - Using this system also allows to use efficient dynamic memory allocation - techniques ( -\emph on -pool_t -\emph default - functions) to create and destroy critical system objects, and reference - can be made using pointers through the system, rather than using less efficient - or fixed-sized arrays and object ID allocation. - Programming using these macros also yields in a clean interface to result - in consistant, clean C code. -\layout Subsubsection - -Multitasking -\layout Standard - -While Xisop provides preemptive multitasking (more information provided - in the next section), it can be disabled by user applications if need be. - This means that Xisop can be used to create single-tasking applications - where a multitasking environment is not wanted, while still taking advantage - of the abstraction of CPU and hardware access Xisop allows. - This for instance allows games to be written almost entirely in C, and - be independent of an operating system such as Windows or AmigaOS, using - Xisop instead. - Such a small game can be booted from a single floppy, which would comport - all necessary components of the game, including Xisop itself. - Disabling multitasking also allows direct access to the hardware resources - by the main application, in a safe manner. - When multitasking is enabled, such safe access to hardware resources is - also provided, although using the -\emph on -device -\emph default - concept is required to remain multitasking friendly and access those. - More information about Xisop devices is given later on. -\layout Standard - -This means that multitasking was provided as a useful optional facility - rather than to force limits over the applications. - It is great to develop some kinds of systems using multitasking, while - some other applications are much better without it. - This was an important consideration when designing Xisop. - Whether multitasking is enabled or disabled, the public interrupt facilities - interface works as expected. - More information is provided on this interface in a later section. -\layout Subsubsection - -AmigaOS -\layout Standard - -Although some ideas were admitedly borrowed from the Amiga Operating System, - the compatibility stops there. - The AmigaOS headerfiles, although publically available, or source code - (which is unavailable publically) were not used as a reference to write - it, nor can Xisop run AmigaOS native object files and binaries. - Xisop is a clean-room kernel implementation, written from scratch and uses - it's own set of ideas and conventions, which are described in this document. -\layout Standard - -The Xisop signals, message ports, devices and handlers concepts were however - borrowed from AmigaOS, as it was a great proof of feasibility using a very - simple underlaying structure. - The interface and internals do not behave identically but anyone who programmed - using the AmigaOS inter-task communication and synchronization features - will feel at ease with Xisop. -\layout Subsubsection - -Where Xisop fits best -\layout Standard - -The best application Xisop suits for consists of an embedded system, dedicated - to provide specific services or tasks, running with low-cost hardware and - restricted physical memory. - There are however a wide range of applications which fit in this category, - cell phones, microwave ovens, PDAs, clocks, industrial microcontrollers - or event loggers, alarm systems, etc. - An example of low-cost hardware Xisop runs great on is a Motorola MC68000L8 - microprocessor based board with a restricted 512 kilobytes of memory, and - an RS-232 interface with UART controler chip. - Xisop can also be used to develop games on 32-bit consoles or arcade hardware. -\layout Standard - -Xisop consists of a nice component when simplifying hardware design of a - project, where a simple CPU unit, little RAM and Xisop-based software can - provide the tasks of more complex hardware-only solutions. - This also implies that the simpler hardware may then be reused, since it - now basically consists of a general purpose programmable system rather - than single-purpose hardware units which can only serve for a single project. -\layout Standard - -It does not consist of a full multipurpose operating system but rather of - a collection of primitives to aid a C programmer to write his applications - on a variety of hardware, and benefit from memory management including - support for runtime dynamic memory addition and removal, as well as multiple - memory types, inter-task communication primitives, a fair number of user - signals, abstracted interrupt facilities, and a microkernel design allowing - to add future functionality modularized as userspace tasks, shared libraries, - devices and handlers. -\layout Standard - -It's weak license (BSDL-style) makes it especially suitable for commercial - embedded applications when a technical team can build a custom application, - where royalties for use, or redistribution of custom changes in the code - are not required. - An effort is provided to make the CPU-specific interface abstract so that - most of the code can be written in C, for code clarity, consistency, developmen -t speed and ease. - The assembly code is restricted to the minimum, and always consists of - a backend to use C. - The same is true for architecture-specific assembly low-level code. - Those sections are described later on. -\layout Standard - -As such, very little time is generally required to port the basics of Xisop - to a new architecture (other 32-bit ones, at least). - Engineers can then develop custom low-cost hardware and a programmer can - write most of the software for it using C and Xisop rather than having - to write all of it in assembly, or starting from nothing and having to - re-invent the assembly to C support primitives. -\layout Standard - -Very low cost hardware helps when many units are being deployed. - However, in the cases where the hardware costs are higher (i.e. - microprosessor with MMU support and 8 or more megabytes of memory), Xisop - may seem too rudimentary, but there are then alternatives such as NetBSD - which can be ported quite fast (when required, as it already consists of - the most portable OS), and POSIX functionalities, with full unix features, - networking, protected memory, etc all become available. - It is also released under the BSD unrestrictive license and is available - at -\emph on -http://www.netbsd.org -\emph default -. -\layout Subsection - -Xisop coding standards -\layout Subsubsection - -Licencing issues and censorship -\layout Standard - -All code which is to be publically released within the standard Xisop distributi -on should be licensed under a BSD-style license, and should not be released - under the GNU Public License (GPL) or GNU Lesser Public License (LGPL). - It however is obvious that companion packages be released and distributed - separately with the Xisop main archive, as long as it be clearly isolated - and labeled as such, so that it can easily be stripped when code under - such strict a license is not wanted within a project. - It is allowed for authors to include their advertizing clause as wanted - in the BSD license advertizing at the top of their files. - A main file can then easily be created using a script of advertizement - clauses and related files whcih one can append in the documentation of - a commercial or closed source product using Xisop. -\layout Standard - -This allows anyone to use Xisop for open source or closed source projects - as wanted. - Although it is encouraged to publically donate the new processor and architectu -re specific modules one develops, as well as machine-independent new modules - of interest for inclusion into the main Xisop tree, released under BSD - license, noone is obliged to do so. - This also allows the main tree to not become too tainted with alot of code - everyone develops which does not serve the other Xisop users much and mostly - bloat the project. - The mirokernel design easy allows third party supplied libraries, devices - and handlers to be developed, and these may also be closed source, and - commercial. - There of course is no problem to release such optional kernel-independent - modules under one of the GNU licenses. -\layout Standard - -If you port Xisop to a new processor or architecture, and are willing to - contribute the code to the tree, I suggest that the source be obtained - via CVS from the main Xisop source repository, and that -\emph on -cvs diff -u -\emph default - be used to create a patch. - This patch should be sent via email to Matthew Modor, at -\emph on -mmondor@gobot.ca -\emph default -. - I then will attempt to merge it into the main Xisop CVS tree, after performing - basic sanity checking on the code, and fixing required bugs if possible - (if any). - I may also reply back if there is any reason why the changes cannot be - applied, in which case I could help to make it conform with the requirements. -\layout Subsubsection - -Standard data types and when to use them -\layout Standard - -Xisop uses a defined set of data types in it's kernel, and attempts to remain - consistant when using them for code clarity and obviousness. - The following C standard data types are used: -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -char -\emph default - The standard C character type, used for C strings only in Xisop. - -\emph on -int8_t -\emph default -and -\emph on - u_int8_t -\emph default - should be used for other byte-related types. -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -char\SpecialChar ~ -* -\emph default - Obvious, also only used in direct relation with C strings. -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -void -\emph default - When a function returns nothing, or has no arguments, for both prototype - and function declaration. -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -void\SpecialChar ~ -* -\emph default - This consists as usual, of a pointer to an abstract type, and is used where - required. -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -int -\emph default - This is used as a convenient format for various return codes and variables - where bit-size is not a concern, or should match the one which is assumed - for the processor (although it is highly recommended to use the various - defined types below for these). -\layout Standard - -The other standard C data types are replaced by the following ones, which - are defined in the Xisop machine-independent < -\emph on -common/types.h> -\emph default - headerfile: -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -bool -\emph default - Corresponds to -\emph on -int -\emph default -, but specifically denotes that this variable is used for boolean operations - and conditionals for code clarity it may only hold TRUE (1) or FALSE (0) - values. -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -int8_t -\emph default - Is used everywhere byte-sized signed elements are explicitely required, - from -127 to +128, except for strings where the standard C -\emph on -char -\emph default - type should be used. -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -u_int8_t -\emph default - Should be used where unsigned byte-sized elements are required, for 0-255 - values. -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -int16_t -\emph default - Is the data type used for 16-bit signed integers values, -32768 to +32767. -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -u_int16_t -\emph default - The unsigned 16-bit value data type which can old 0 to 65535. -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -int32_t -\emph default - For use with 32-bit signed data types -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -u_int32_t -\emph default - 32-bit unsigned data type -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -int64_t -\emph default - signed 64-bit data type (usually typedef with -\emph on -long long -\emph default -) -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -u_int64_t -\emph default - unsigned 64-bit data type. - It is to be noted that operations on 64-bit objects is generally slow as - it requires multiple instructions on 32-bit architectures. - Currently, no 64-bit data types are beign used anymore, the few ones which - used it were converted to use 32-bit ones instead. -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -size_t -\emph default - Used to represent a size in bytes, this in fact is equivalent to -\emph on -u_int32_t -\emph default -. - -\emph on -size_t -\emph default - should not be used to represent arbitary types amounts, but byte amounts - only (corresponding to a number of -\emph on -char -\emph default -, -\emph on -u_int8_t -\emph default - or -\emph on -int8_t -\emph default - types). -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -ssize_t -\emph default - Purpose is the same as for -\emph on -size_t -\emph default -, but this can hold -1 for error. -\layout Standard - -And other frequently used standard Xisop types, which are still defined - in < -\emph on -common/types.h -\emph default ->, but have their corresponding structures defined at their respective locations - (mentionned in parentheses): -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -node_t -\emph default - A doubly linked list node to be used in -\emph on -list_t -\emph default - type lists. - (common/kernlib/list.h) -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -list_t -\emph default - A doubly linked list (common/kernlib/list.h) -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -mchunk_t -\emph default - A chunk of contiguous memory pages, part of a -\emph on -ppool_t -\emph default - (common/kernel/memory.h) -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -page_t -\emph default - A physical memory page, or a number of contiguous physical memory pages, - holding the necessary information for freeing back. - (common/kernel/memory.h) -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -ppool_t -\emph default - A system pool of pages, that is, what other pools allocate pages from. - (common/kernel/memory.h) -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -pool_t -\emph default - A efficient fixed sized objects ( -\emph on -pnode_t -\emph default -) memory pool with internal statistical page cacheing, which can optionally - dynamically grow and shrink, or be static in size (common/kernel/memory.h) -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -pnode_t -\emph default - A pool object node, which must prefix any pool object. - This type internally begins with a -\emph on -node_t -\emph default - and can therefore be used directly for queuing in -\emph on -list_t -\emph default -. - (common/kernel/memory.h) -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -mpool_t -\emph default - A multiple -\emph on -pool_t -\emph default - memory pool used to serve multiple sized blocks requests. - Of various memory types. - Bocks which are too large are rounded at page boundaries. - (common/kernel/memory.h) -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -mnode_t -\emph default - Internally used by the -\emph on -mpool_t -\emph default - related allocation primitives (common/kernel/memory.h) -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -facility_t -\emph default - A public interrupt facility which the port-specific code initializes calling - the common -\emph on -facility_init() -\emph default - function. - (common/kernel/exception.h) -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -hook_t -\emph default - An interrupt, trap or exception code vector attached to a -\emph on -facility_t -\emph default -. - (common/kernel/exception.h) -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -hookid_t -\emph default - An -\emph on -hook_t -\emph default - ID, internally a -\emph on -u_int32_t -\emph default -, which is guarranteed to be unique for the facility it belongs to. - (common/kernel/exception.h) -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -task_t -\emph default - Task structure (common/kernel/task.h) -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -priority_t -\emph default - A task priority number (common/kernel/task.h) -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -signum_t -\emph default - Signal number (common/kernel/signal.h) -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -sigmask_t -\emph default - Signal mask which can represent one or more -\emph on -signum_t -\emph default -. - (common/kernel/signal.h) -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -port_t -\emph default - Message port (common/kernel/port.h) -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -message_t -\emph default - A message header structure which comports the glue to queue messages in - -\emph on -port_t -\emph default - (common/kernel/port.h) -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -device_t -\emph default - An open device handler (common/kernel/device.h) -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -devicenode_t -\emph default - A linked device hook (common/kernel/device.h) -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -iorequest_t -\emph default - An I/O request message type used for -\emph on -device_t -\emph default - requests (common/kernel/device.h) -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -fifo8_t -\emph default - An 8-bit elements FIFO buffer. - -\emph on -fifo -\emph default -* -\emph on -_t -\emph default - types are implemented as a fixed bytes buffer once initialized, rather - than using linked lists. - It is possible to easily create new -\emph on -fifo -\emph default -* -\emph on -_t -\emph default - types of arbitrary object sizes using a macro. - (common/kernlib/fifo.h) -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -fifo16_t -\emph default - A 16-bit elements FIFO buffer (common/kernlib/fifo.h) -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -fifo32_t -\emph default - A 32-bit elements FIFO buffer (common/kernlib/fifo.h) -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -lifo8_t -\emph default - A 8-bit elements LIFO buffer. - -\emph on -lifo -\emph default -* -\emph on -_t -\emph default - types operate like stacks, and are internally implemented as a fixed bytes - buffer once initialized, rather than using linked lists. - A macro is provided to easily create new types of -\emph on -lifo -\emph default -* -\emph on -_t -\emph default - for objects of arbitrary size. - (common/kernlib/lifo.h) -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -lifo16_t -\emph default - A 16-bit elements LIFO buffer (common/kernlib/lifo.h) -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -lifo32_t -\emph default - A 32-bit elements LIFO buffer (common/kernlib/lifo.h) -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -hashtable_t -\emph default - A hash lookup table (common/kernlib/hash.h) -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -hashnode_t -\emph default - A hash lookup table node (common/kernlib/hash.h> -\layout Standard - -And here are the processor and architecture specific opaque data types they - provide: -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -_ipl_t -\emph default - Abstract Interrupt Priority Level type, associated with the -\emph on -_spl -\emph default -n -\emph on -() -\emph default - and -\emph on -_splx() -\emph default - functions. - (processor/support.h) -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -_ctx_t -\emph default - Abstract processor-specific supplied context handle, associated to -\emph on -_ctx_init() -\emph default - function which is used by -\emph on -task_alloc() -\emph default - and internal kernel context switching. - (processor/support.h) -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -_lock_t -\emph default - Abstract processor-specific supplied atomic simple lock handle. - Associated to the -\emph on -_lock_ -\emph default -* -\emph on -() -\emph default - functions. - (processor/support.h) -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -_rlock_t -\emph default - An abstract atomic recursive lock handle (needs to be unlocked the same - number of times it was locked to free the -\emph on -_rlock_t -\emph default -). - Associated to the -\emph on -_rlock_ -\emph default -* -\emph on -() -\emph default - functions. - (processor/support.h) -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -XXX -\emph default - The 64-bit related types are currently unused. - On most processors the GCC compiler requires additional libraries to support - these, which are currently not part of Xisop. - There however exist such implementations fully written in C, like the one - used on BSD systems which would not be hard to include if it was necessary. - An effort was made to also not include software which requires the use - of floating point, for code size and efficiency. -\layout Subsubsection - -Programming style and conventions -\layout Standard - -It is a fact that consistency is a must for readibility, especially in an - open source project which many others may need to modify to suit their - particular needs. - The following programming conventions are used, and are defined for assembly - and C code. -\layout Paragraph - -Function names -\layout Standard - -All functions which are not behaving identically to ANSI-C or POSIX or other - widely standarized ones, but have the same name should have their name - prefixed by an underscore ('_'). - Others which behave exactly like the standards should have the same name - the standard expects. - Other functions which have nothing in common with standard names should - not be prefixed by underscores, except for the defined processor and port-speci -fic functions, which names should also begin with an underscore. - Of course, if a hardware-specific function is provided as a replacement - to a common portable one for performance considerations, it obviously should - bear the same name, however. -\layout Paragraph - -Formatting -\layout Standard - -Line termination should consist of -\begin_inset Quotes eld -\end_inset - - -\backslash -n -\begin_inset Quotes erd -\end_inset - - (linefeed, as is used on Amiga and Unix), rather than -\begin_inset Quotes eld -\end_inset - - -\backslash -r -\backslash -n -\begin_inset Quotes erd -\end_inset - - which is used on MS-DOS and Windows for text file storage. - Other than tab ( -\begin_inset Quotes eld -\end_inset - - -\backslash -t -\begin_inset Quotes erd -\end_inset - -) and linefeed ( -\begin_inset Quotes eld -\end_inset - - -\backslash -n -\begin_inset Quotes erd -\end_inset - -), no other control characters should be found within the source files. - Moreover, every line of the source files should not exceed 79 characters. -\layout Paragraph - -C language sections -\layout Standard - -It is important that C files be using the standard eight (8) tab size for - real tabs. - However, the software tab should consist of four (4) characters. - Tabs should be used where 8 space tabs are needed, and spaces used to fill - the 4 character tabs. - When using the VIm editor, these should be used: -\emph on -ts=8 -\emph default -, -\emph on -sw=4 -\emph default -, and -\emph on -cindent -\emph default -. - This allows the files to be printable using text file viewers properly - at all times, and to also be printable as-is on most printers. - -\emph on -XXX -\emph default - Add settings for emacs -\layout Itemize - -C program suffix should consist of lower case -\emph on -.c -\layout Itemize - -C headerfiles should use the lower case suffix -\emph on -.h -\layout Itemize - -ANSI-C programming norms should be observed as much as possible. -\layout Itemize - -ANSI convention for function arguments specification rather than K&R. -\layout Itemize - -BSD programming style used rather than GNU style, especially for opening - and closing braces convention. - This corresponds to the formatting performed using GNU indent program with - the following parameters: -\emph on -gindent -kr -ncs .c -\layout Itemize - -Functions and global variables which are specific to the current module - should be declared -\emph on -static -\emph default -, and their prototypes should be in a private headerfile or in the current - C source file, not in a public headerfile which is included by any other - C source module. -\layout Itemize - -Macros and variables directly refering to hardware architecture-specific - registers should use the -\emph on -volatile -\emph default - keyword. -\layout Itemize - -Every function must have a corresponding prototype, defined either at the - top of the current C file (if static) or in a corresponding headerfile - (public ones). -\layout Itemize - -For kernel code, the conventions should be followed about the choice of - variable types to use (described in the previous section). -\layout Itemize - -No C++ or other language should be used within the kernel code. - Of course there is no such restriction for any user program. -\layout Itemize - -Code should obviously be commented as required for clarity, but -\emph on - -\begin_inset Quotes eld -\end_inset - -/* -\begin_inset Quotes erd -\end_inset - - -\emph default - and -\emph on - -\begin_inset Quotes eld -\end_inset - -*/ -\begin_inset Quotes erd -\end_inset - - -\emph default - C-style delimited comments only are allowed, not -\emph on - -\begin_inset Quotes eld -\end_inset - -// -\begin_inset Quotes erd -\end_inset - - -\emph default - C++ ones. -\layout Itemize - -Code should not invoke general purpose memory allocation routines when a - special fast access pool was provided already to allocate and free these - types of items. - Where appropriate, the general kernel objects pool system should be expanded - when a new object is frequently allocated and freed, rather than using - the general purpose management functions. - These obviously should be common, machine-independent objects (which can - possibly use machine-specific definitions, if they become standard and - documented in this document so all ports provide them). - See the memory management section later on for more information. -\layout Itemize - -General purpose libraries supplied in -\emph on -src/common/kernlib/ -\emph default - should normally have each function implemented as a separate C file into - the library's directory. - During the build process they will get compiled independently, and then - archived together using -\emph on -ar -\emph default -. - -\emph on -ranlib -\emph default - will then be executed on the archive. - This ensures that unused functions do not be included into the end kernel - result, reducing kernel size considerably when full multipurpose libraries - are used (such as complete string libraries). -\layout Paragraph - -Assembly sections -\layout Standard - -For assembly sections, the AT&T style should be observed, and the assembly - should not be embedded into the C code using inline assembly routines. - Often a library requireing both assembly and C will at build time assemble - it's assembly section file ( -\emph on -.s -\emph default -), compile it's C part ( -\emph on -.c -\emph default -), and link the two together into an object file ( -\emph on -.o -\emph default -) using the -\emph on --r -\emph default - linker switch, which then gets linked with the kernel. - In other situations one may want to instead compile all modules to objects - ( -\emph on -.o -\emph default -), create an -\emph on -ar -\emph default - archive ( -\emph on -.a -\emph default -), run -\emph on -ranlib -\emph default - on it, and link it to the kernel (in which case all unused functionality - which was isolated into it's own module does not get included by the linker, - reducing kernel size). - This applies to both processor-specific and architecture-specific backends. - The rest is written in C. - It is encouraged to write most of the architecture-specific boot loader - code in C and provide a companion assembly file to interface and call the - C loader main function. - This minimizes assembly code, and C is more consistant and readable, it - can be more suitable to sometimes write tricky sections like relocators, - etc. -\layout Standard - -The assembly sections should ensure to be reeintrant, that is, using the - stack to save registers rather than using temporary registers to save other - registers, and using the stack as well for temporary variables for which - registers cannot be used, rather than static memory locations. - These are usually indended to be called from C, and because of the way - Xisop shared libraries work, is a main reason to follow these directives. - An assembly section which is known to execute uninterruptably may at it's - discretion use static memory if needed. -\layout Itemize - -Assembly files should have the following lowercase suffix: -\emph on -.s -\layout Itemize - -Tabulation should be set to use real tabs (not spaces), with a standard - tab width of 8. -\layout Itemize - -Assembler opcodes should be located after the first tab. -\layout Itemize - -Operands should be located after the second tabulation. -\layout Itemize - -Where more than an operand is required and are separated by a comma, a space - is required after the comma. -\layout Itemize - -Opcode names should be lowercase, and must match those shown by objdump - when dissassembling. - This allows consistancy between all code for a particular processor. - Although this may sound tidious at first, it is not as hard as it sounds - to learn the specific mnemonic names objdump displays, with some little - practice. - Adventages results in consistency, where it also becomes possible to locate - specific instructions in the code with regular expressions, etc. -\layout Itemize - - -\emph on -.equ -\emph default - and -\emph on -= -\emph default - directives, if any, should be located at the top of the assembly file. -\layout Itemize - - -\emph on -.globl -\emph default - directives to declare public functions should also be located at the top - of the file. -\layout Itemize - -All code should be within the -\emph on -.text -\emph default - section, along with any static memory variables if need be (although use - of these is discouraged, except for instance in the kernel loader bootblock - where they can be useful, or in special context code where one knows what - he's doing rather than only using the stack). -\layout Itemize - -A comment on a line or two should be placed before each function, especially - the ones which are intended to be called from C, along with the C prototype. -\layout Itemize - -Other comments should be found along the code where required for obviousness. -\layout Itemize - -Obviously, registers which a function modifies should be saved in the stack, - and restored upon function return, except (if any) the register corresponding - to the return code expected in C for that processor. -\layout Itemize - -Interrupt, trap and exception assembly sections should normally save all - registers which are commonly used for the particular processor, and restore - them before returning. - It is wrong to assume that C compiled functions always save all registers - they modify other than the expected result register. - This was learned from experience using GCC. -\layout Subsection - -Xisop scheduler and inter-task communication -\layout Subsubsection - -The multitasking scheduler -\layout Standard - -The scheduler currently allows to set priorities for each task, between - -128 and 127. - It is preemptive and will make sure to allow other tasks to run even when - a particular task hugs the CPU by not going to sleep. - A task can be in two main states: running, and sleeping. -\layout Standard - -When a task sleeps, it has a mask of signals that will awake it as soon - as possible if it receives any signal it waits for, and leaves more opportuniti -es for other tasks to execute their shores when it is sleeping. - When a task runs, it usually executes it's shores and goes back to sleep - for the next event to process, when running in a multitasking system. - However, if it does not return to sleep fast enough, the scheduler preemtive - nature will suspend it and allow other tasks to also get a fair CPU time - share, depending on the priority of the running tasks, unless the task - has disabled scheduling, in which case it will remain running until it - decides to delegate control to the system again. -\layout Standard - -The task priority controls the speed and frequency at which a running task - awakes, or runs. - What the scheduler does is assign a set of credits to all tasks in the - run queue, according to their priority one another. - Then using round-robin at each scheduler round, the task with the highest - amount of credits is given some CPU time, and a credit is taken out from - it. - If a task expired it's credits, it is no longer given a round, until all - other tasks also expired their credits. - When this happens, all tasks are given credits according to their priority - again. - Some care is taken to not constantly give a turn to the last task even - if it has more credits, to allow to make the best out of Xisop asynchroneous - facilities, but such a high priority task will have more turns within the - run still, observing it's relative priority to the others. -\layout Standard - -When a task goes to sleep, it is immediately taken out of the run queue - and will not be awaken until a signal it awaits for is received. - When it does, it will be moved in the running queue as fast as possible, - at which point the time elapsing before it can act to the signal is directly - dependent on the priority of the task related to the other ready to run - tasks. - However, when the task is moved in the run queue, it is immediately given - it's credits at the maximum it's priority permits, thus increasing the - chances it can answer in a fast manner among the other tasks in the run - queue, which may already have been there before, and elapsed their credits. -\layout Standard - -This means that if all tasks run with the default priority of 0, they all - get a fair share of the CPU, and are naturally awaking very fast when one - get a signal. - In the case where one of the tasks remains running, the speed of signal - responsiveness will ususally consist of the frequency of the scheduler. - This frequency is usually configurable, and depends on the capacity of - the timers available for the particular architecture (and of course on - CPU speed). -\layout Standard - -In the case where the scheduler detects that all tasks are sleeping, it - attempts to reduce it's CPU usage by calling the -\emph on -_idle() -\emph default - processor-specific function, which will only awaken the scheduler again - when the next interrupt, trap or exception occurs. - It is possible in Xisop to have dead locked tasks, if they decide to wait - for no signals, or if they wait for a signal to occur which never does. -\layout Standard - -Following are described all functions which are related to tasks and scheduler, - or which are useful to synchronize resources, other than the -\emph on -signal_t -\emph default - and -\emph on -port_t -\emph default - based systems, which are described in a further section. -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -task_t\SpecialChar ~ -*task_alloc(int\SpecialChar ~ -(*start)(void\SpecialChar ~ -*,\SpecialChar ~ -void\SpecialChar ~ -*), void\SpecialChar ~ -*res,\SpecialChar ~ -void\SpecialChar ~ -*args,\SpecialChar ~ -priority_t\SpecialChar ~ -priorit -y,\SpecialChar ~ -size_t\SpecialChar ~ -stacksize,\SpecialChar ~ -u_int8_t\SpecialChar ~ -flags) -\emph default - Allocates a task which can then be started or freed back. - -\emph on -start -\emph default - specifies the entry point function, -\emph on -res -\emph default - an optional pointer to a buffer for results which the task may need to - use or NULL, and -\emph on -args -\emph default - an optional pointer to a buffer which may be used by the task to obtain - it's argument, or NULL. - When -\emph on -start -\emph default - is called, -\emph on -res -\emph default - is passed as the first argument and -\emph on -args -\emph default - as the second one. - -\emph on -priority -\emph default - consists of the task initial running priority which may be in the range - of -128 to 127, 0 being the normal running priority. - -\emph on -stacksize -\emph default - specifies the size of the stack in bytes which should be dedicated to the - task, a common size being 4096 bytes. - -\emph on -flags -\emph default - may be 0, or ORed -\emph on -TS_ -\emph default -* flags which are defined in < -\emph on -src/common/kernel/task.h -\emph default ->. - The task is not yet launched by Xisop. - If the -\emph on -TS_SHARED -\emph default - flag is supplied, this task will use the -\emph on -mpool_t -\emph default - of it's parent (the current task which allocates it). - This means that all memory allocated by one of the tasks sharing a memory - pool is inherently shared. - One task ending will not cause the allocated regions to be freed unless - explicitely freed. - When all tasks shareing a memory pool exit, all allocated memory by any - of them is automatically freed back. - This feature can be used when memory should inherently be shared among - the tasks, or when memory resources are very scarce. - However, unlike using Xisop messages which provide synchronization to share - arbitrary memory regions among tasks, implicit synchronization should be - used by the tasks when accessing the same shared memory locations which - they should access concurrently. - For this, -\emph on -rwlock_t -\emph default - and -\emph on -_lock_t -\emph default - based systems can be used, or a custom system based around Xisop signals - and/or messages. -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -task_t\SpecialChar ~ -*task_free(task_t\SpecialChar ~ -*task) -\emph default - Allows to free -\emph on -task -\emph default - which was previously allocated by -\emph on -task_alloc() -\emph default -. - Only tasks which have been allocated but which have not been launched may - be freed using this function, or it internally does nothing. - NULL is returned. - The kernel automatically frees tasks which have been launched when they - exit. - When a task is freed, all the resources it allocated using standard Xisop - functions are automatically freed back to the system as well. - The exception is when the task was setup with the -\emph on -TS_SHARED -\emph default - flag, where the memory is only freed when all tasks sharing that memory - pool ended. - This does not affect message ports, signals, devices, libraries or handlers, - however, which are released by each task, always, as they exit. -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -bool\SpecialChar ~ -task_start(task_t\SpecialChar ~ -*task) -\emph default - Launches -\emph on -task -\emph default - which should have previously been allocated using -\emph on -task_alloc() -\emph default -. - The task is moved to the ready queue and will start executing as soon as - the current task yields calling -\emph on -_yield() -\emph default - or is preempted by the scheduler. - The delay for it to actually execute also depends on the relative priority - of the tasks on the ready queue and their current credits. - If synchronization is needed with it's startup, signals or messages can - be used. - TRUE is returned on success, or FALSE if the task is invalid, or was already - launched. -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -bool\SpecialChar ~ -task_end(task_t\SpecialChar ~ -*task) -\emph default - Immediately forces termination of the specified -\emph on -task -\emph default -, which may consist of the current task, or of another task. - The task is cleanly freed as if it exited itself. - Can also be used on a task which is locked on the waiting queue, perhaps - waiting for events it will never receive. - When a task ends and needs to be freed, it is moved in a queue for a dedicated - task called the reaper, which chores consist of freeing all resources related - to each task and the tasks themselves on it's own scheduler CPU time slices. - Other tasks and the kernel are then releaved from that work. - When a task ends it will soon automatically be freed by Xisop, and the - resources it allocated are automatically freed as well. -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -void\SpecialChar ~ -_yield(task_t\SpecialChar ~ -*task) -\emph default - Permits a currently running task to immediately yield control back to the - scheduler to allow other tasks to have an immediate opportunity to execute, - rather than leaving the preemtive scheduler interrupt it. - The task is not moved to the wait queue, and will have an opportunity to - resume again very soon. - -\emph on -task -\emph default - consists of a suggestion to which task to delegate control to, which should - also currently be in the ready queue. - When -\emph on -task -\emph default - is invalid or NULL, the scheduler is left to decide which task to execute - next. -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -void\SpecialChar ~ -task_sleep(task_t\SpecialChar ~ -*task,\SpecialChar ~ -u_int32_t\SpecialChar ~ -flags,\SpecialChar ~ -task_t\SpecialChar ~ -*yield) -\emph default - Allows to immediately switch the specified task (which may also be the - currently running task) to the waiting queue. - The task will be unable to execute again until it is specifically moved - back again on the ready queue by calling -\emph on -task_wakeup() -\emph default - on it with at least one of the same bits in -\emph on -flags -\emph default -. - This can be useful when custom interrupt attached code hooks need to synchroniz -e tasks and such and that signals and message ports are not desired. - If the task being put to sleep consists of the current task, -\emph on -yield -\emph default - can specify another optional ready task to run immediately, if non-NULL. -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -bool\SpecialChar ~ -task_wakeup(task_t\SpecialChar ~ -*task,\SpecialChar ~ -u_int32_t\SpecialChar ~ -flags) -\emph default - Immediately awakes the specified task if it currently is sleeping in the - waiting queue. - This is normally used after a -\emph on -task_sleep() -\emph default - call was made on the task. - However, it is possible to use this function to wake a task which is waiting - for a signal as well if -\emph on -TSF_SIGNAL -\emph default - flag is specified. - This is normally used by custom interrupt hooks which need to synchronize - tasks, when they cannot use signals or message ports. - They then can share a -\emph on -fifo_t -\emph default - buffer, or custom -\emph on -list_t -\emph default - queue, with the wanted tasks and cause them to awake and sleep at will - for instance. - The task is only awaken if at least one bit supplied in -\emph on -flags -\emph default - matches one of the bits of the -\emph on -flags -\emph default - which were used at -\emph on -task_sleep() -\emph default -. - TRUE is returned if the task could be awaken, or FALSE if the flags do - not permit to awake it or another problem occurs. -\layout Standard - -The current sleeping flags are described as follows (as defined in < -\emph on -src/common/kernel/scheduler.h -\emph default ->) : -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -TSF_KERNEL -\emph default - Kernel-reserved, used by the task reaper for instance. -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -TSF_SIGNAL -\emph default - Task is waiting for the reception of at least of one of the signal bits - in -\emph on -task_t->sigwait -\emph default -. - Internally used bu the signal subsystem. -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -TSF_CUSTOM -\emph default - As the name implies, may be used by user tasks. -\layout Standard - -Here are then described the scheduler control functions: -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -priority_t\SpecialChar ~ -task_getpriority(task_t\SpecialChar ~ -*task) -\emph default - Obtains the current running priority level of -\emph on -task -\emph default -. -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -priority_t\SpecialChar ~ -task_setpriority(task_t\SpecialChar ~ -*task,\SpecialChar ~ -priority_t\SpecialChar ~ -p) -\emph default - Permits to change the running priority of -\emph on -task -\emph default - to -\emph on -p -\emph default -. - Returned is the previous priority of -\emph on -task -\emph default - which could be used to restore it. -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -void\SpecialChar ~ -sched_disable(void) -\emph default - Allows to disable the preemptive scheduler, which will only be re-enabled - when a corresponding number of -\emph on -sched_enable() -\emph default - have been called, as the scheduler lock consists of a recursive -\emph on -_rlock_t -\emph default -. - It is important to not call -\emph on -yield() -\emph default -, -\emph on -port_wait(), port_send(), signal_send() -\emph default - or -\emph on -signal_wait() -\emph default - when the scheduler is disabled, because it would prevent the task from - ever being awaken again. - Obviously, this call should be used with care, if any. -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -void\SpecialChar ~ -sched_enable(void) -\emph default - Re-enables the scheduler if the same number of instances of this function - matched with the previous -\emph on -sched_disable() -\emph default - calls. -\layout Subsubsection - -Locks and synchronization primitives -\layout Standard - -Other synchronization systems such as signals and message ports are later - described in a next section. - However, these are also very useful synchronization primitives which Xisop - provides to both kernelspace and userspace software: -\layout Paragraph - -Exclusive access locks -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -void\SpecialChar ~ -_lock_init(_lock_t\SpecialChar ~ -*lock) -\emph default - Initializes an exclusive access a lock. - This has to be used before using other functions -\emph on -lock -\emph default -. -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -void\SpecialChar ~ -_lock_acquire(_lock_t\SpecialChar ~ -*lock) -\emph default - This function is not multitasking-friendly in that the current task loops - in an endless loop until exclusive obtention of the -\emph on -_lock_t -\emph default - is acquired. - Tasks should normally use -\emph on -lock_acquire() -\emph default - instead. -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -void\SpecialChar ~ -_lock_release(_lock_t\SpecialChar ~ -*lock) -\emph default - Immediately releases the exclusive -\emph on -_lock_t -\emph default - -\emph on -lock -\emph default - to allow another locker to obtain it. -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -bool\SpecialChar ~ -_lock_try(_lock_t\SpecialChar ~ -*lock) -\emph default - Attempts to exclusively acquire -\emph on -lock -\emph default -, but returns immediately with FALSE if it cannot. - TRUE is returned on success. -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -bool\SpecialChar ~ -_lock_available(_lock_t\SpecialChar ~ -*lock) -\emph default - Returns TRUE if there currently are no locker on -\emph on -lock -\emph default -, but does not attempt to lock it. - It is unsafe to attempt to implement -\emph on -_lock_try() -\emph default - using this function, obviously. -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -void\SpecialChar ~ -lock_acquire(_lock_t\SpecialChar ~ -*lock) -\emph default - Similarily to -\emph on -_lock_acquire() -\emph default -, locks execution of the current task until -\emph on -lock -\emph default - is exclusively obtained. - This function is however better according to multitasking as it immediately - yields CPU time to allow the current locker to free it as soon as possible - for the current task to own it. -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -void\SpecialChar ~ -lock_release(_lock_t\SpecialChar ~ -*lock) -\emph default - Identical to -\emph on -_lock_release() -\emph default -, made to match -\emph on -lock_acquire() -\emph default - for consistency. -\layout Paragraph - -Recursive locks -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -void\SpecialChar ~ -_rlock_init(_rlock_t\SpecialChar ~ -*lock) -\emph default - Initializes a recursive lock of type -\emph on -_rlock_t -\emph default -. - This needs to be performed before calling other recursive lock operations - on -\emph on -lock -\emph default -. - Recursive locks are different from exclusive locks in that there can be - any number of concurrent lockers at the same time. - They are usually used for systems which execute at intervals, like the - Xisop scheduler and interrupt facilities which also make use of this system. - They can then make sure to be the only locker before performing their critical - work. - This way arbitrary tasks may disable the facilities safely by locking the - lock, and unlocking it when they are done with their critical section. -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -void\SpecialChar ~ -_rlock_acquire(_rlock_t\SpecialChar ~ -*lock) -\emph default - Locks recusively the -\emph on -lock -\emph default - -\emph on -_rlock_t -\emph default -. - This basically atomically increases the lockers counter of the lock, and - never fails. - It always returns immediately. -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -void\SpecialChar ~ -_rlock_release(_rlock_t\SpecialChar ~ -*lock) -\emph default - Releases the -\emph on -lock -\emph default - -\emph on -_rlock_t -\emph default -. - Note that the same number of release operations must be performed on -\emph on -lock -\emph default - for it to become available again. - This only atomically decreases the lockers counter of the lock, and returns - immediately. -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -bool\SpecialChar ~ -_rlock_trylock(_rlock_t\SpecialChar ~ -*lock) -\emph default - If the lock is free/available, that is, no current lockers exist on -\emph on -lock -\emph default -, this function atomically locks it and returns TRUE immediately. - Otherwise it returns FALSE and returns immediately, in which case the lock - is left in it's original state before the call. - This can be very useful for an event-driven system which needs to make - sure that the lock is free to perform it's tasks, but also needs to prevent - it's own recursion. - Obviously, if it returned TRUE, -\emph on -_rlock_release() -\emph default - is expected to be called when done. -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -bool\SpecialChar ~ -_rlock_available(_rlock_t\SpecialChar ~ -*lock) -\emph default - Returns FALSE if there are any lockers on -\emph on -lock -\emph default -, or TRUE if the lock is currently free from any lockers. - This however only performs the check, and does not change the lock state. - It is unsafe to use this function along with -\emph on -_rlock_acquire() -\emph default - to implement a -\emph on -_rlock_trylock() -\emph default - replacement as it would not be atomic. -\layout Paragraph - -Read/Write locks -\layout Standard - -These locks internally consist of a combination of exclusive and recursive - locks. - They are ideal for instance to protect synchronization of resources where - multiple readers are allowed but that exclusive access is required for - write operations. - There currently is provided no way to upgrade a currently held shared access - lock to an exclusive lock, or to downgrade an exclusively held lock to - a shared access lock, other than releasing the lock and re-locking it. - However, if this proves necessary in the future, we shall implement these - features. - These locks are especially adequate to protect resources which are frequently - accessed in read-only mode, but which occasionally need to be updated, - which obviously requires write/exclusive access, which is the case with - several Xisop system lists, and these locks are then used by the involved - kernel functions as well. -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -void\SpecialChar ~ -rwlock_init(rwlock_t\SpecialChar ~ -*lock) -\emph default - Initializes -\emph on -lock -\emph default -, which is necessary before using any other -\emph on -rwlock_ -\emph default -* -\emph on -() -\emph default - function on it. -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -void\SpecialChar ~ -rwlock_acquire(rwlock_t\SpecialChar ~ -*lock,\SpecialChar ~ -bool\SpecialChar ~ -exclusive) -\emph default - Locks the current task until the -\emph on -lock -\emph default - -\emph on -rwlock_t -\emph default - can be exclusively accessed (for read and/or writing access) if -\emph on -exclusive -\emph default - is TRUE, or until a shared recursive access is obtained (for read-only - access) if -\emph on -exclusive -\emph default - is FALSE. - Is multitasking-safe. -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -void\SpecialChar ~ -rwlock_release(rwlock_t\SpecialChar ~ -*lock) -\emph default - Releases the access on -\emph on -lock -\emph default - which was obtained using -\emph on -rwlock_acquire() -\emph default - or -\emph on -rwlock_try() -\emph default -. - Whether this access was exclusive or shared is internally remembered. -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -bool\SpecialChar ~ -rwlock_try(rwlock_t\SpecialChar ~ -*lock,\SpecialChar ~ -bool\SpecialChar ~ -exclusive) -\emph default - Very similar to -\emph on -rwlock_acquire() -\emph default - but always immediately returns with FALSE if the access cannot be obtained - immediately. - TRUE is returned with the lock held on success. -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -void\SpecialChar ~ -rwlock_upgrade(rwlock_t\SpecialChar ~ -*lock) -\emph default - Permits to upgrade an already obtained shared lock to an exclusive lock. - The current task may sleep as required until all shared lockers free their - lock. - The caller -\emph on -MUST -\emph default - already be holding the lock in shared mode. - This function is most useful for operations such as check-and-modify, when - read-only access is generally required to a resource, but that under certain - conditions write access is needed to modify the resource after the check. -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -void\SpecialChar ~ -rwlock_downgrade(rwlock_t\SpecialChar ~ -*lock) -\emph default - Conversely to -\emph on -rwlock_upgrade() -\emph default -, allows to downgrade an already obtained exclusive lock to a shared lock. - The caller -\emph on -MUST -\emph default - already be holding this lock in exclusive mode. - The usefulness of this function is questionable. - Although provided, the kernel does not use it. -\layout Paragraph - -System lists access -\layout Standard - -Several system lists require internal -\emph on -rwlock_t -\emph default - for synchronization against corruption. - In < -\emph on -common/kernel/main.h -\emph default -> are defined several macros which can be used to access those securely - by kernel functions. - The -\emph on -enum systables -\emph default - specifies various system lists which can be accessed using these methods, - called -\emph on -SYSTABLE_ -\emph default -*. -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -void\SpecialChar ~ -SYSTABLE_RLOCK(enum\SpecialChar ~ -systables\SpecialChar ~ -table) -\emph default - Locks the -\emph on -rwlock_t -\emph default - of the specified -\emph on -table -\emph default -, in shared mode (read-only access). -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -void\SpecialChar ~ -SYSTABLE_WLOCK(enum\SpecialChar ~ -systables\SpecialChar ~ -table) -\emph default - Locks the -\emph on -rwlock_t -\emph default - of the specified -\emph on -table -\emph default -, in exclusive mode (read/write access). -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -void\SpecialChar ~ -SYSTABLE_UNLOCK(enum\SpecialChar ~ -systables\SpecialChar ~ -table) -\emph default - Unlocks the -\emph on -rwlock_t -\emph default - of the specified -\emph on -table -\emph default -, which should have previously been locked using -\emph on -SYSTABLE_RLOCK() -\emph default - or -\emph on -SYSTABLE_WLOCK() -\emph default -. -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -void\SpecialChar ~ -SYSTABLE_UPGRADE(enum\SpecialChar ~ -systables\SpecialChar ~ -table) -\emph default - Allows a caller which -\emph on -MUST -\emph default - hold a shared read-only access lock (after using -\emph on -SYSTABLE_RLOCK() -\emph default -) to upgrade the lock to exclusive mode. -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -hashtable_t\SpecialChar ~ -*SYSTABLE(enum\SpecialChar ~ -systables\SpecialChar ~ -table) -\emph default - Returns the -\emph on -hashtable_t -\emph default - pointer associated with the system list -\emph on -table -\emph default -. - Obviously, this should only be used when the lock is held, and should only - be used for the access corresponding to the currently obtained lock type. -\layout Standard - -The following two functions, as opposed to macros, are implemented as C - functions and perform boundary checking on the arguments, as they are provided - for user tasks rather than for the kernel, for which the previous macros - were designed: -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -hashtable_t\SpecialChar ~ -*systable_lock(u_int32_t\SpecialChar ~ -systable,\SpecialChar ~ -bool\SpecialChar ~ -exclusive) -\emph default - Attempts to lock access to the system list -\emph on -systable -\emph default -, in exclusive or shared mode. - A pointer to the -\emph on -hashtable_t -\emph default - is returned on success, or NULL otherwise. - As usual, shared locks should be obtained when read-only access is performed - on a list, but an exclusive lock is required if there is any need to modify - a system list. -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -void\SpecialChar ~ -systable_unlock(u_int32_t\SpecialChar ~ -systable) -\emph default - Unlocks a previously held lock for -\emph on -systable -\emph default -, which should have previously been obtained using -\emph on -systable_lock() -\emph default -. -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -void\SpecialChar ~ -systable_upgrade(u_int32_t\SpecialChar ~ -systable) -\emph default - Upgrades a previously held lock for systable in shared mode to exclusive - mode. - The lock -\emph on -MUST -\emph default - previously be obtained in shared mode. -\layout Subsubsection - -Signals -\layout Standard - -Very few signals are reserved by the system, SIGTERM and SIGPOLL, respectively. - As Xisop currently supports 32 different signals per task, this results - in 30 user signals the applications programmer may play with. - Signals are internally implemented as bits, and are not reliably queued, - which means that although a task will always know that a particular signal - was received, it cannot know if it occured more than once before it had - the chance to process it. - It ressembles alot to a hardware bus line, which can be activated by the - other end, although our capacity to monitor a single signal state is dependent - on the rate at which we run. -\layout Standard - -Note that sending a SIGTERM signal to a task does not force it to exit. - It merely consists of a request to exit to the task, and the task may ignore - it or respect it. -\layout Standard - - -\emph on -XXX 32 is no longer a fixed value now that signum_t and sigmask_t are abstracted - and could be set by the port-specific code. -\layout Standard - -Message ports are used when the need for reliable event queuing is met. - The signals merely allow a task to sleep and be awaken on specified events, - like a message arriving on a port, which internally uses a signal bit. -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -signum_t\SpecialChar ~ -signal_alloc(void) -\emph default - Attempts to allocate an available signal bit from the current task. - Returns the signal number allocated, or -1 if no more signals available. -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -void\SpecialChar ~ -signal_free(sigmask_t\SpecialChar ~ -sigmask) -\emph default - Frees all specified signals in -\emph on -sigmask -\emph default -, which should previously have been obtained using -\emph on -signal_alloc() -\emph default -. - If reserved signals are part of the supplied -\emph on -sigmask -\emph default -, those are ignored. -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -sigmask_t\SpecialChar ~ -signal_wait(sigmask_t\SpecialChar ~ -sigmask,\SpecialChar ~ -task_t\SpecialChar ~ -*task) -\emph default - Suspends the current task until at least one of the supplied signals in - -\emph on -sigmask -\emph default -, or a system reserved signal occurs. - Returned is the mask of signals we received which caused an awakening. - Multitasking-friendly applications usually spend most of their time suspended - by this call, and are awakening to perform their operations asynchroneously, - and go back to sleep as soon as possible, to allow other tasks to also - respond and execute efficienty. - However, because of the nature of the preemptive scheduler, a task which - remains in running state will not prevent others from executing. - -\emph on -task -\emph default -, which may be NULL, optionally specifies which task should be favored as - a suggestion to the scheduler, which allows to implement cooperative tasks - more efficiently. - The current task is guaranteed to awake at the occurance of the specified - signals, but will not be able to determine how many occurances of each - signal occurred as no queueing is performed. -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -void\SpecialChar ~ -signal_send(task_t\SpecialChar ~ -*task,\SpecialChar ~ -sigmask_t\SpecialChar ~ -sigmask) -\emph default - Atomically sends the supplied signals in -\emph on -sigmask -\emph default - to the specified -\emph on -task -\emph default -. - The task, if suspended waiting for signals, is awaken if a signal it waits - for is included in -\emph on -sigmask -\emph default -, and will have a chance to run. - The delay elapsing between -\emph on -signal_send() -\emph default - and the task being able to process the signal depends on three factors: -\begin_deeper -\layout Itemize - -The speed at which the current task returns to sleep (using -\emph on -yield() -\emph default -, -\emph on -port_wait() -\emph default - or -\emph on -signal_wait() -\emph default -). -\layout Itemize - -If it does not return to sleep, the current task will continue running until - it is preempted by the scheduler, which is dependent on the scheduler timer - interrupt frequency. -\layout Itemize - -The priority of the tasks in the ready queue, that is, which are currently - awake. - This factor takes place after the other end was awaken, and before it gets - an opportunity to actually execute. -\end_deeper -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -sigmask_t\SpecialChar ~ -SIGMASK(signum_t\SpecialChar ~ -signum) -\emph default - Consists of a useful macro to convert a signal number ( -\emph on -signum_t -\emph default -) to a signal mask ( -\emph on -sigmask_t -\emph default -). - Those may then be ORed together (using C '|' bitwise operator character) - to represent multiple signals. -\layout Subsubsection - -Message ports -\layout Standard - -In the UNIX world, this is called Inter Process Communication (IPC). - In the case of Xisop, we communicate between light-weight tasks, rather - than between full fledged processes, using signals, datagram messages and - connectionless, unidirectional ports. - The Xisop port functions internally operate using signal primitives and - message queues (in FIFO order). - These are safe to call from userspace as well as kernelspace tasks. - However, it is not safe to transfer messages among ports in an interrupt - handler context without using -\emph on -_splhigh() -\emph default - for synchronization. - -\emph on -XXX -\layout Standard - -A Xisop message consists of an arbitrary sized object prefixed with a -\emph on -message_t -\emph default - structure, which is internally used for message queueing and replying informati -on. - Because the message is internally -\begin_inset Quotes eld -\end_inset - -moved -\begin_inset Quotes erd -\end_inset - - by only swapping pointers efficiently, memory copy operations are minimized. - However, this also means that the original creator of the message, as well - as the other end to which the message is passed, are responsible to synchronize - operations properly among eachother on the message memory area (more informatio -n on this below). - The -\emph on -message_t -\emph default - header starts with a -\emph on -pnode_t -\emph default - internally, which means that the user can use -\emph on -pool_t -\emph default - primitives to efficiently and arbitrarily create and destroy messages. - It also means that at their discretion the ends are able to internally - queue the message in custom -\emph on -list_t -\emph default - lists when necessary (after -\emph on -port_get() -\emph default -, and taking care to unlink it before -\emph on -port_send() -\emph default -/ -\emph on -port_reply() -\emph default -, obviously). -\layout Standard - -Some care was made when implementing Xisop message ports to take in consideratio -n tasks and or ports which may be destroyed before a message is replied - back by the other end, or for cases where a public port address, after - being obtained once, becomes invalid as the public service dies. - Instead of implementing a higher overhead or less versatile unique port - ID allocation, validation and lookup system, or using expensive string - operations with a -\emph on -hashtable_t -\emph default - using -\emph on -hashtable_lookup() -\emph default - for every message send, the following techniques were implemented: -\layout Itemize - -A special magic cookie is set on a valid, existing port. - This means that when attempting to perform any operation on a -\emph on -port_t -\emph default - pointer which does not resolve to an actual, currently valid port, operation - is refused to take place. - When a message port is destroyed, that magic number is reset to zero, which - renders it unusable. -\layout Itemize - -Because an efficient -\emph on -pool_t -\emph default - is used to allocate and free back -\emph on -port_t -\emph default - objects, it would be possible for an intended reply port to be destroyed, - and for another port, belonging to any task, to be created at the same - address. - To solve this issue, each port also comports an internal unique ID. - When a message is sent to a port, from which a reply is expected, the -\emph on -message_t -\emph default - is made to remember both the reply -\emph on -port_t -\emph default - address and unique ID. - When attempting to reply, the -\emph on -port_t -\emph default - validity is performed as usual via the magic number, and the unique ID - is then evaluated for a match. - This way, a reply message will never be queued back on an unexpected port, - or to a nonexisting one which may just have died. -\layout Itemize - -The unique ID supplied to a port only consists of an unsigned 32-bit integer, - which is obtained from a global shared counter, which is incremented and - used whenever required. - This means that the odds of another -\emph on -port_t -\emph default - using the same address and ID within the delay of a send/reply are probably - always none. -\layout Itemize - -Using this method prevented restrictions on the maximum number of ports - and tasks which can exist in Xisop. - The overhead is also smaller than having to run through an array looking - for an empty slot, and having to re-allocate the memory area to dynamically - grow or shrink it, because without MMU support alot of memory copying would - be required for those operations. - Using a -\emph on -pool_t -\emph default - then is ideal for speed. - Using a relatively fast lookup hash table would require a lookup to be - performed before every message send, which was considered suboptimal when - designing Xisop. - Although -\emph on -port_find() -\emph default - uses this, it would have been absurd to need to perform this lookup before - sending any message. - Especially considering that locking must be used when accessing the system - hash table for synchronization. -\layout Standard - -Usually, the tasks should be written to be reliable and to synchronize well - within eachother. - However, with these precautions, supported by well written applications, - the -\emph on -port_t -\emph default - system is always very reliable and predictable. - Here are described the functions: -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -port_t\SpecialChar ~ -*port_create(const\SpecialChar ~ -char\SpecialChar ~ -*name) -\emph default - Allows to create a message port on the current task. - This internally also needs to allocate a signal bit using -\emph on -signal_alloc() -\emph default -, which is associated to the -\emph on -port_t -\emph default - for the -\emph on -port_wait() -\emph default - internals. - NULL is returned on failure, or a pointer to the new -\emph on -port_t -\emph default - on success. - -\emph on -name -\emph default - is optional and should be NULL if the port does not need to be advertized - to the whole system. - If other tasks need to find our port by name, then -\emph on -name -\emph default - will be usable with -\emph on -port_find() -\emph default - to locate it, and listing the system public ports would advertize it as - well. - Ports are generally private, except for special public services. - The length of -\emph on -name -\emph default - will be truncated to 32 characters if it is longer. - If a public port is created, but that another public port bears the same - name alerady, the operation fails. - The port name is case-sensitive. - If the task exists without closing it's ports, they are automatically destroyed - by the kernel. -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -port_t\SpecialChar ~ -*port_destroy(port_t\SpecialChar ~ -*port) -\emph default - Destroys a message port of the current process, which was created using - -\emph on -port_create() -\emph default -. - The internally allocated signal bit and memory are released back to the - system. - If any queued messages exist, the queue is lost, but the -\emph on -message_t -\emph default - objects are untouched, as they remain the responsibility of the application - who created them. - NULL is returned. -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -port_t\SpecialChar ~ -*port_find(const\SpecialChar ~ -char\SpecialChar ~ -*name) -\emph default - Allows to locate a public message port by name. - If the port can be found, it's address is returned. - NULL is returned otherwise. - Because this needs to lookup through a system's public ports hash table - it needs to lock it internally using a -\emph on -rwlock_t -\emph default - in shared access mode. - The port name is case-sensitive. -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -bool\SpecialChar ~ -port_send(port_t\SpecialChar ~ -*port,\SpecialChar ~ -port_t\SpecialChar ~ -*replyport,\SpecialChar ~ -message_t\SpecialChar ~ -*message) -\emph default - Queues the supplied -\emph on -message_t -\emph default - to the specified -\emph on -port_t -\emph default - in FIFO order, and internally -\emph on -send_signal() -\emph default - the associated process with the internal port signal. - This means that the other process could be sleeping using -\emph on -signal_wait() -\emph default - or -\emph on -port_wait() -\emph default - and will be awakened so that it may process the message. - The supplied message then becomes owned by the other end, and should be - left alone by the current task until a reply be obtained from the other - side on the -\emph on -message_t -\emph default -'s -\emph on -replyport -\emph default -. - This -\emph on -port_send() -\emph default - and -\emph on -port_reply() -\emph default - mechanism serves for synchronization. - This means that normally, -\emph on -replyport -\emph default - is supplied the address of a local -\emph on -port_t -\emph default - used to receive results from the task we send messages to via it's own - -\emph on -port_t -\emph default -. - TRUE is returned on success, or FALSE if the message could not be delivered - (in which case the supplied -\emph on -port_t -\emph default - address is probably invalid and a -\emph on -port_find() -\emph default - operation can be used again to attempt to locate the port, if it consists - of a public port which should exist). -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -message_t\SpecialChar ~ -*port_get(port_t\SpecialChar ~ -*port) -\emph default - If at least one message is available in the specified -\emph on -port_t -\emph default -, the first one is unqueued from it in FIFO order and the address to it - is returned. - Otherwise, NULL is returned. -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -bool\SpecialChar ~ -port_reply(message_t\SpecialChar ~ -*msg) -\emph default - Sends back the supplied -\emph on -message_t -\emph default - to the reply -\emph on -port_t -\emph default - of the task that previously sent it to us. - This port corresponds to the -\emph on -replyport -\emph default - argument that was used at -\emph on -port_send() -\emph default -. - Normally, the other end waits until we are done with the message, and we - use this function to notify that it can safely continue to do whatever - it wants with the -\emph on -message_t -\emph default - data. - TRUE is returned on success, or FALSE if no -\emph on -replyport -\emph default - was set for the -\emph on -message_t -\emph default -, or that the reply port is no longer valid. -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -port_t\SpecialChar ~ -*port_wait(port_t\SpecialChar ~ -**ports,\SpecialChar ~ -u_int32_t\SpecialChar ~ -num,\SpecialChar ~ -task_t\SpecialChar ~ -*task) -\emph default - Suspends the current task until a message is received through one of the - supplied port(s) specified in the array of -\emph on -port_t -\emph default - pointers -\emph on -ports -\emph default -. - -\emph on -num -\emph default - specifies the number of -\emph on -port_t -\emph default - pointers supplied in the ports array. - -\emph on -task -\emph default - specifies a suggestion preference of which task to switch to, or NULL to - let the scheduler choose. - Returned is the pointer to the -\emph on -port_t -\emph default - which received the message, or NULL if a reserved signal was the awakening - cause. - If one of the ports already has queued messages the task will not be set - to sleep and the aforementionned port will be returned immediately. - If it is necessary to monitor other signals as well as port message arrivals, - it is advised to manually assemble the -\emph on -sigmask_t -\emph default - from all monitored signals, including the ones associated to the monitored - ports, and to use -\emph on -signal_wait() -\emph default - instead. - Evaluating the resulting -\emph on -sigmask_t -\emph default - will permit to know which ports received messages, if any. - Use of the -\emph on -PORT_SIGMASK() -\emph default - macro will then be useful. -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -bool\SpecialChar ~ -port_flush(port_t\SpecialChar ~ -*port) -\emph default - Unqueues all pending messages in the supplied -\emph on -port_t -\emph default -, if any. - This can be useful to discard unused -\emph on -port_reply() -\emph default - results which are obtained where no result codes are needed, rather than - using a -\emph on -while() -\emph default - loop of -\emph on -port_get() -\emph default - statements. - Also useful before destroying ports when desired. - Returns TRUE on success, or FALSE if the port is invalid. - Should obviously not be used on other tasks' ports, only ours. -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -sigmask_t\SpecialChar ~ -PORT_SIGMASK(port_t\SpecialChar ~ -*port) -\emph default - Is useful when it is necessary to know which signal bit is associated with - a particular port. - The returned -\emph on -sigmask_t -\emph default - can be ORed with the other masks to provide to -\emph on -signal_wait() -\emph default -. - This way, it is possible for a task to monitor the state of other signals - while at the same time monitoring message ports for events. - This macro, just like -\emph on -SIGMASK() -\emph default -, should be used instead of expressions such as -\emph on -(1L << n) -\emph default - both for code obviousness and backwards compatibility, because the number - of available signals could eventually grow. -\layout Subsubsection - -The special SIGPOLL signal and the poll port -\layout Standard - - -\emph on -XXX needs to be rethinked and re-written :) -\layout Standard - -As there only are 30 user signals, and that a message port is usually associated - with a signal, it may be useful to assign more than one message port to - a single signal, or process more than one event using a single signal and - message port. -\layout Standard - -The SIGPOLL signal is reserved for just that, and each task always has a - message port associated with this signal. - Through this port can be sent various type of messages for more than one - event the task may want to be awakened for. - This provides message multiplexing, while the method for multiple message - ports using the same signal would provide port multiplexing. - The reason why message multiplexing was chosen as a standard in Xisop was - because it requires less resources. - The more ports there are on the system, the larger the system ports pool - grows and it will never shrink for considerations described in the previous - subsection. - -\emph on -XXX -\layout Standard - -This special system-reserved port can be used to transfer reliable signals, - possibly emulating POSIX semantics, or to transfer many other types of - events a task may all be waiting for. - It also is useful to have a high number of concurrently opened high-level - files, and implement filedescriptors. - A special set of functions is provided by Xisop machine-independent layer - to transfer messages through this port, and to evaluate the type of event - that originated the message, along with message size. - Because unlike for standard message ports where messages of a fixed size - are usually transfered, the message size changes here, depending on the - event type and message. -\layout Standard - - -\emph on -XXX -\layout Subsection - -Xisop memory management system -\layout Standard - -Xisop provides three types of memory management primitives for the kernel. - These are provided by the Xisop machine-independent layer. - It also provides distinction between the various types of memory, when - an architecture has more than one (i.e. - Amiga CHIP and FAST RAM, peripheral memory, etc). - Special care was used to prevent the memory management system from having - to disable interrupts (which may be very useful when the -\emph on -_FACILITY_SCHEDTIMER -\emph default - consists of the only timing source for an architecture and needs to be - as reliable as possible). - A -\emph on -_lock_t -\emph default - is internally used by the system page primitives to sychronize access to - the system page pools. - This also avoids having to export them as system calls via traps. -\layout Subsubsection - -Page primitives -\layout Standard - -The first level system consists of page allocation and freeing. - The page size is dependent on architecture when MMU is concerned, however - within Xisop which provides it's own memory management, a page is typically - 4096 bytes. - Primitives are provided to allocate a single page, or number of physically - contiguous pages, and to free back one page, or a number of contiguous - pages. - At system initialization, the page pools are initiated to provide access - to the physical memory areas, and are classified by memory type. - There are a few functions also defined by Xisop which allow to add physical - memory areas to the page pools, and specify their type, so that the architectur -e-dependent initialization code be simpler, and also that at runtime it - be possible to attach new RAM which was mapped from external devices, possibly - hot-pluggable ones such as PCMCIA memory cards, etc. - Here are described the dynamic memory linking functions. - Note that these functions internally use a -\emph on -_lock_t -\emph default - which is acquired to access the system pages pools. - This means that they are generally safe to call anytime, but from interrupt - handler context. - -\emph on -XXX -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -mchunk_t\SpecialChar ~ -*mchunk_init(void\SpecialChar ~ -*mem,\SpecialChar ~ -size_t\SpecialChar ~ -size) -\emph default - Internally prepares the supplied memory block to pages which can later - be linked into the system. - The internal control structures are setup in hidden data which will not - be added into the public pages. - This is dependent on -\emph on -_PAGE_SIZE -\emph default -, which should be defined by the port-specific < -\emph on -port/support.h -\emph default -> headerfile. - NULL is returned if the supplied memory area was too small to be split - into pages and to store the necessary control data. - Otherwise, a pointer to the -\emph on -mchunk_t -\emph default - is provided. - After using this call, the memory area should never be manipulated anymore, - other than using other Xisop standard calls, unless it is known that the - area is not linked into the system pages. - The supplied memory is expected to be physical contiguous memory. -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -bool\SpecialChar ~ -mchunk_attach(int\SpecialChar ~ -memtype,\SpecialChar ~ -mchunk_t\SpecialChar ~ -*mchunk) -\emph default - Allows to link an -\emph on -mchunk_t -\emph default - which was previously prepared using -\emph on -mchunk_init() -\emph default -, to the system -\emph on -ppool_t -\emph default - associated with the specified memory type. - After this is performed, it is possible to allocate memory pages from this - memory chunk until it be unlinked using -\emph on -mchunk_detach() -\emph default -. - This depends on -\emph on -_MEM_MAX -\emph default -, which is defined by the port specific < -\emph on -port/support.h -\emph default -> headerfile. - TRUE is normally returned, unless the chunk is seen to already have been - attached, or that the supplied memory type is invalid, in which case FALSE - is returned. - It is possible to setup and append as many -\emph on -mchunk_t -\emph default - as wanted. - For instance, the port-specific code which sets up the kernel pages for - available memory may need to call -\emph on -mchunk_init() -\emph default - and -\emph on -mchunk_attach() -\emph default - multiple times, one for each contiguous memory block. - It is safe to call this function to attach new memory at system runtime, - anytime. -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -bool\SpecialChar ~ -mchunk_detach(int\SpecialChar ~ -memtype,\SpecialChar ~ -mchunk_t\SpecialChar ~ -*mchunk) -\emph default - Permits to safely detach from the system an -\emph on -mchunk_t -\emph default - which previously was attached using -\emph on -mchunk_attach() -\emph default -. - TRUE is returned on success, or FALSE if the memory type is invalid, if - the supplied -\emph on -mchunk_t -\emph default - was not currently attached, or if any memory from that -\emph on -mchunk_t -\emph default - is still currently allocated, in which case the chunk is not unliked. -\layout Standard - -The first level of allocation primitives allow to allocate and free one - or multiple contiguous pages of physical memory to and from the system - pools ( -\emph on -ppool_t -\emph default - of each memory type). - Internally, the list of linked -\emph on -mchunk_t -\emph default - for a memory type is ran through. - These functions also acquire the system pools safety lock while working - on the system memory pools: -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -page_t\SpecialChar ~ -*pages_alloc(int\SpecialChar ~ -memtype,\SpecialChar ~ -u_int32_t\SpecialChar ~ -many,\SpecialChar ~ -bool\SpecialChar ~ -zero) -\emph default - Allocates -\emph on -many -\emph default - contiguous memory pages of -\emph on -_PAGE_SIZE -\emph default - bytes, of the supplied -\emph on -memtype -\emph default -, which can be -\emph on -_MEM_ANY -\emph default - in which case all memory types are tried sequencially, favoring the first - memory type to the last. - A -\emph on -page_t -\emph default - pointer is returned which can be used to access the memory area via -\emph on -page_t->address -\emph default -, or to free back the memory using -\emph on -pages_free() -\emph default -. - NULL is returned if not enough memory can be found to satisfy the request. - If -\emph on -zero -\emph default - is TRUE and pages could be allocated, the memory area is zeroed using -\emph on -pageclr() -\emph default -. -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -bool\SpecialChar ~ -pages_free(page_t\SpecialChar ~ -*pages) -\emph default - Releases to the system one or more contiguous memory pages which previously - were allocated using -\emph on -pages_alloc() -\emph default -. - TRUE is returned on success, or FALSE if the supplied pointer was NULL, - or if the -\emph on -page_t -\emph default - was already freed back. -\layout Subsubsection - -Simple object pool primitives -\layout Standard - -The second level consists of a pool of fixed sized entities, the -\emph on -pool_t -\emph default -. - A pool can be pre-allocated once, with a fixed maximum number of elements, - setup to grow automatically when new elements are needed, internally allocating - and preparing new pages as required, and can optionally also shrink smaller - automatically, releasing back to the system the unused pages. - When a pool is about to shrink, it evaluates statistics about the number - of pages the pool normally holds on average, so that primitives to free - the page are not called too often unnecessarily. - Such pages are buffered by the pool for future use, if they are expected - to be reclaimed back soon. - Using these functions is more efficient in speed and memory than using - page allocation primitives, calls to which it attempts to minimize, while - filling each page of memory with the most objects possible. -\layout Standard - -A C structure should be defined for the type of object which is to be allocated, - and the first element of that structure should consist of a -\emph on -pnode_t -\emph default - element. - These functions, unlike page allocation ones, do not synchronize if multiple - tasks or interrupt handlers share a -\emph on -pool_t -\emph default -. - The caller should therefore provide synchronization whenever necessary. - However, if the -\emph on -pool_t -\emph default - allocates or frees system pages, the -\emph on -pages_alloc() -\emph default - and -\emph on -pages_free() -\emph default - functions are used which internally use synchronization to ensure that - the system pools do not corrupt. - These functions are mostly for kernel use. - If used from userspace, these pools will not be automatically freed back - to the system unless the tasks specifically destroys them before exiting. -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -bool\SpecialChar ~ -pool_init(pool_t\SpecialChar ~ -*p, u_int32_t\SpecialChar ~ -stepp, u_int32_t\SpecialChar ~ -minp, u_int32_t\SpecialChar ~ -maxp, size_t\SpecialChar ~ -node -size, int\SpecialChar ~ -memtype) -\emph default - Initializes the supplied -\emph on -pool_t -\emph default - object -\emph on -p -\emph default - to be used to allocate objects of -\emph on -nodesize -\emph default - length (size which should include the -\emph on -pnode_t -\emph default - element). - This size should be equal or smaller than -\emph on -_PAGE_SIZE -\emph default - * -\emph on -stepp -\emph default - / 2. - -\emph on -stepp -\emph default - specifies how many contiguous pages should be allocated and freed at once - using -\emph on -pages_ -\emph default -* -\emph on -() -\emph default - primitives, when required. - If -\emph on -stepp -\emph default - appears too small to fit a useful number of nodes in a -\emph on -page_t -\emph default -, it will be automatically grown until it reaches a maximum of 8, after - which FALSE will be returned for failure (although there is no such limit - for the -\emph on -stepp -\emph default - argument itself when manually set high enough). - Each -\emph on -page_t -\emph default - is then internally split into objects. - -\emph on -minp -\emph default - tells the minimum number of -\emph on -page_t -\emph default - which should always remain into the -\emph on -pool_t -\emph default -, or 0 if it is allowed to shrink totally when it needs no memory. - The pool will initially allocate those at initialization, and it will never - shrink smaller during it's lifetime, if non-zero. - -\emph on -maxp -\emph default - similarily specifies the maximum number of -\emph on -page_t -\emph default - which the -\emph on -pool_t -\emph default - should eventually grow to, or 0 for no limit. - For instance, using -\emph on -minp -\emph default - and -\emph on -maxp -\emph default - of 0 allows the pool_t to dynamically grow and shrink as necessary and - it could eventually use all available RAM. - Using a -\emph on -minp -\emph default - and -\emph on -maxp -\emph default - of 2 ensures that at least two -\emph on -page_t -\emph default - be initially allocated, which will never be freed back unless the -\emph on -pool_t -\emph default - is destroyed, and also tells that the pool should never allocate more pages. - This can be useful in interrupt context or some situations where we do - not want the system pool pages to be accessed. - A fixed number of maximum object nodes will be available to allocate and - free efficiently then. - -\emph on -memtype -\emph default - specifies the memory type which this pool will use to allocate the pages. - It is invalid to specify -\emph on -_MEM_ANY -\emph default - here. - TRUE is returned on success, or FALSE on failure (invalid memory type or - not enough available memory). -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -bool\SpecialChar ~ -pool_destroy(pool_t\SpecialChar ~ -*pool) -\emph default - Frees all memory currently being used by the specified -\emph on -pool_t -\emph default - and destroys the pool object, which can no longer be used by pool object - allocation primitives unless it is re-initialized. - Any currently allocated objects from the -\emph on -pool_t -\emph default - become invalid immediately and should therefore not be accessed anymore, - they get freed as well. - TRUE is returned on success, or FALSE if the supplied -\emph on -pool_t -\emph default - was not previously initialized successfully. -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -pnode_t\SpecialChar ~ -*pool_alloc(pool_t\SpecialChar ~ -*pool,\SpecialChar ~ -bool\SpecialChar ~ -zero) -\emph default - Attempts to allocate a single object from the specified -\emph on -pool_t -\emph default -. - The object size depends on the -\emph on -nodesize -\emph default - parameter which was supplied at -\emph on -pool_init() -\emph default -. - A pointer to the object is returned on success, or NULL if not enough memory - is available, either for the -\emph on -pool_t -\emph default -, if -\emph on -maxp -\emph default - was non-zero, or system memory. -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -pnode_t\SpecialChar ~ -*pool_free(pnode_t\SpecialChar ~ -*node) -\emph default - Releases the specified object -\emph on -node -\emph default - which was previously allocated using -\emph on -pool_alloc() -\emph default -, back to the -\emph on -pool_t -\emph default - it belongs to. - NULL is returned. -\layout Subsubsection - -General purpose memory pools -\layout Standard - -The third level memory management system, working with -\emph on -mpool_t -\emph default -, consists of a number of -\emph on -pool_t -\emph default -, adapted to serve most byte requirement requests, through more standard - malloc() and free() like functions. - For a system with a -\emph on -_PAGE_SIZE -\emph default - of 4096, -\emph on -_MPOOLSTEP -\emph default - of 1 and -\emph on -_MPOOLS -\emph default - of 7 and -\emph on -_MPOOLSTART -\emph default - of 16, there are 7 -\emph on -pool_t -\emph default -, deserved to serve element sized as closely possible to the requested number - of bytes (smallest nodesize 16 + sizeof(mnode_t)), and a -\emph on -list_t -\emph default - designed to hold -\emph on -page_t -\emph default - pointers to satisfy larger requests, which get rounded on page boundaries. - An -\emph on -mpool_t -\emph default - is capable of serving requests for various memory types, including -\emph on -_MEM_ANY -\emph default -, where the first memory types are privileged over the last ones. - Like their -\emph on -pool_t -\emph default - counterparts, these functions do not perform any special synchronization - which may be necessary if an -\emph on -mpool_t -\emph default - was shared among several tasks. - Here are the functions related to the -\emph on -mpool_t -\emph default -: -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -bool\SpecialChar ~ -mpool_init(mpool_t\SpecialChar ~ -*mpool) -\emph default - Initializes an -\emph on -mpool_t -\emph default - to accept allocation requests. - TRUE is returned on success, or FALSE if there is a problem initializing - the -\emph on -pool_t -\emph default - elements, or if the supplied -\emph on -mpool -\emph default - pointer is NULL. - This function is dependent on the port-specific -\emph on -_MPOOLS -\emph default -, -\emph on -_MPOOLSTEP, _MPOOLSTART -\emph default - and the -\emph on -enum _memtypes -\emph default -holding -\emph on -_MEM_MAX -\emph default - which should have been defined in < -\emph on -port/support.h -\emph default ->. - -\emph on -_MPOOLS -\emph default - specifies the number of -\emph on -pool_t -\emph default - objects, -\emph on -_MPOOLSTEP -\emph default - the -\emph on -stepp -\emph default - argument to -\emph on -pool_init() -\emph default -, -\emph on -_MPOOLSTART -\emph default - the maximum bytesize request for the first -\emph on -pool_t -\emph default -, and -\emph on -_MEM_MAX -\emph default - the number of memory types supplied by the system. - See the port-dependent section for more information on how to set these. -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -bool\SpecialChar ~ -mpool_destroy(mpool_t\SpecialChar ~ -*mpool) -\emph default - Frees all memory currently associated with the specified -\emph on -mpool_t -\emph default -, which should have previously been initialized by -\emph on -mpool_init() -\emph default -, and disables the -\emph on -mpool_t -\emph default -. - Any pointers to memory which were obtained via -\emph on -_malloc() -\emph default - using this pool become invalid. - No more requests will be allowed on this -\emph on -mpool_t -\emph default - unless re-initialized using -\emph on -mpool_init() -\emph default -. - TRUE is returned, or FALSE if the -\emph on -mpool_t -\emph default - was already destroyed, or that the supplied pointer is NULL. - It is also possible for this function to return FALSE without error, in - the case where at least another task shares this -\emph on -mpool_t -\emph default -. - (See the -\emph on -TS_SHARED -\emph default - flag to -\emph on -task_alloc() -\emph default -). -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -void\SpecialChar ~ -*_malloc(mpool_t\SpecialChar ~ -*mpool,\SpecialChar ~ -int\SpecialChar ~ -memtype,\SpecialChar ~ -size_t\SpecialChar ~ -size,\SpecialChar ~ -bool\SpecialChar ~ -zero) -\emph default - Attempts to allocate the requested -\emph on -size -\emph default - contiguous bytes from the supplied -\emph on -mpool -\emph default -, using memory of type -\emph on -memtype -\emph default -. - _MEM_ANY is valid, and will cause any available memory type to be returned. - A pointer is returned to a memory area holding the number of bytes that - were requested, or NULL if there is not enough memory to satisfy the request, - or if the parameters are wrong. - If -\emph on -zero -\emph default - is TRUE, the memory area is cleared with 0x00 bytes. - Automatic synchronization is performed if the -\emph on -mpool_t -\emph default - is shared by more than one task. -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -void\SpecialChar ~ -*_free(void\SpecialChar ~ -*mem) -\emph default - Frees back the memory to which the supplied -\emph on -mem -\emph default - points, to where it belongs. - NULL is returned. - Automatic synchronization is performed if the -\emph on -mpool_t -\emph default - is shared by more than one task. -\layout Paragraph - -Kernel code -\layout Standard - -Here are variants which can be used by kernel functions. - Note that the kernel can use all the above previously mentionned functions - as well, however for consistency it is good to use the following where - appropriate. - All of these, contrary to the previously described primitives, internally - use exclusive locks as necessary to ensure the integrity of the system - pools, so that disabling the scheduler is unnecessary: -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -void\SpecialChar ~ -*_kmalloc(int\SpecialChar ~ -memtype,\SpecialChar ~ -size_t\SpecialChar ~ -size,\SpecialChar ~ -bool\SpecialChar ~ -zero) -\emph default - Allocates general-purpose memory from the kernel memory pool, which should - be released using -\emph on -kfree() -\emph default -. - When a task uses this function the memory is not restored to the system - automatically when it exists. - Special syncronization is used internally so that it is safe to use by - multiple tasks. -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -void\SpecialChar ~ -*_kfree(void\SpecialChar ~ -*mem) -\emph default - Frees back to the kernel memory pools memory which has previously been - allocated by -\emph on -kmalloc() -\emph default -. - Internal special synchronization is used. -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -void\SpecialChar ~ -*TMALLOC(int\SpecialChar ~ -memtype,\SpecialChar ~ -size_t\SpecialChar ~ -size) -\emph default - Equivalent to calling -\emph on -kmalloc(memtype, size, FALSE); -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -void\SpecialChar ~ -*TCMALLOC(int\SpecialChar ~ -memtype,\SpecialChar ~ -int\SpecialChar ~ -number,\SpecialChar ~ -size_t\SpecialChar ~ -size) -\emph default - Equivalent to calling -\emph on -kmalloc(memtype, number * size, TRUE); -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -void\SpecialChar ~ -*MALLOC(size_t\SpecialChar ~ -size) -\emph default - Identical to calling -\emph on -kmalloc(_MEM_ANY, size, FALSE); -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -void\SpecialChar ~ -*CMALLOC(int\SpecialChar ~ -number,\SpecialChar ~ -size_t\SpecialChar ~ -size) -\emph default - Like calling -\emph on -_kmalloc(_MEM_ANY, number * size, TRUE); -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -void\SpecialChar ~ -*FREE(void\SpecialChar ~ -*mem) -\emph default - Equivalent to -\emph on -_kfree(mem); -\emph default - but made to match all above macros. -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -void\SpecialChar ~ -*kmalloc(size_t\SpecialChar ~ -size) -\emph default - Like -\emph on -MALLOC() -\emph default - but implemented as an ANSI-C compliant function. -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -void\SpecialChar ~ -kfree(void\SpecialChar ~ -*mem) -\emph default - Counterpart to -\emph on -kmalloc() -\emph default -. -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -pnode_t\SpecialChar ~ -*spool_alloc(u_int32_t\SpecialChar ~ -pool) -\emph default - Allows to efficiently allocate a frequently used kernel object for which - a special system pool exists. - -\emph on -pool -\emph default - may consist of one of the -\emph on -POOL_ -\emph default -* names which are defined in the -\emph on -enum _syspools -\emph default - in < -\emph on -src/kernel/memory.h -\emph default ->. - Automatic synchronization is performed. -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -pnode_t\SpecialChar ~ -*spools_free(u_int32_t\SpecialChar ~ -pool,\SpecialChar ~ -pnode_t\SpecialChar ~ -*node) -\emph default - Made to free a system object which was previously allocated using -\emph on -spool_alloc() -\emph default -. - Automatic synchronization is performed. -\layout Paragraph - -User space tasks -\layout Standard - -The following can be called by user tasks to allocate memory using their - own memory pool, which is automatically released back to the system when - they exit. - They behave identically to the standard ANSI-C functions bearing the same - name. - They are implemented as functions rather than macros, for inclusion into - the Xisop shared library. - These are also safe to use in the case where more than one tasks are sharing - an -\emph on -mpool_t -\emph default - (see the -\emph on -TS_SHARED -\emph default - flag to -\emph on -task_alloc() -\emph default -). -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -void\SpecialChar ~ -*malloc(size_t\SpecialChar ~ -size) -\emph default - Behaves identically to the standard ANSI-C -\emph on -malloc() -\emph default -. -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -void\SpecialChar ~ -*calloc(int\SpecialChar ~ -number,\SpecialChar ~ -size_t\SpecialChar ~ -size) -\emph default - Identical to ANSI-C -\emph on -calloc() -\emph default - semantics. -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -void\SpecialChar ~ -*realloc(void\SpecialChar ~ -*ptr,\SpecialChar ~ -size_t\SpecialChar ~ -size) -\emph default - Like ANSI-C -\emph on -realloc() -\emph default - semantics. - Note that like the standard, the returned pointer can point to a new memory - area, in which case the previous contents will have been copied over. - The use of this function is generally discouraged against generally more - efficient dynamic allocation techniques using linked lists. - It is however provided for compatibility with ANSI-C. -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -void\SpecialChar ~ -free(void\SpecialChar ~ -*ptr) -\emph default - Again like the ANSI-C -\emph on -free() -\emph default - function. - Note that this function can also free memory which has been allocated using - -\emph on -tmalloc() -\emph default - and -\emph on -tcmalloc() -\emph default -. -\layout Standard - -ANSI-C however has no concept of multiple memory types, and as such -\emph on -tmalloc() -\emph default - had to be included. - -\emph on -free() -\emph default - can be used to free back memory which they allocate still: -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -void\SpecialChar ~ -*tmalloc(int\SpecialChar ~ -memtype,\SpecialChar ~ -size_t\SpecialChar ~ -size) -\emph default - Like -\emph on -malloc() -\emph default - but allows to specify a Xisop port-dependent memory type. -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -void\SpecialChar ~ -*tcmalloc(int\SpecialChar ~ -memtype,\SpecialChar ~ -int\SpecialChar ~ -number,\SpecialChar ~ -size_t\SpecialChar ~ -size) -\emph default - Like -\emph on -calloc() -\emph default -, but can also be told the type of memory wanted. -\layout Subsubsection - -A note about absolute memory allocation -\layout Standard - -Xisop does not provide an allocation function which can be told the absolute - memory location area desired. - There are several reasons for this which this section attempts to explain. -\layout Standard - -Xisop provides the necessary inter-task communications tools to prevent - the need of using absolute memory addresses for rendez-vous among tasks. - Moreover, it has the concept of devices, which can be left the task to - perform synchronization among tasks which need to share a specific resource. - Additionally, Xisop does not setup the MMU in a way to prevent user tasks - from accessing any memory. -\layout Standard - -This means that if absolute memory locations need to be reserved by the - system, they normally should not be included into the general purpose memory - pools. - A user device task can then specifically handle those locations as necessary - and provide multitasking-friendly access to them. -\layout Standard - -For instance, the video memory should not be attached to the system memory - pools by the port-specific code. - Instead, a device task should be written and provided to allow safe access - to the video hardware, and basic console support. - Optionally, a handler can be provided, which even allows a higher abstraction - to access the device. - For instance, the device could supply basic resource allocation and access - primitives, while the handler could support vt100 emulation over the device. -\layout Standard - -In the case of a single-tasking application being developped around Xisop, - once the scheduler is disabled there is no need for any special handling - to access the wanted memory regions. - The memory allocator is no longer necessary to use, even. -\layout Standard - -Another consideration to realize is that if for instance video memory was - part of the system memory pools, it could automatically be allocated by - another task which was only requesting some memory. - If we had support for reserved pages, then there would be no point in allocatin -g them, also. - Only general-purpose memory should be attached into the system. - There exists the possibility of reserving a specific memory type for some - memory however, if there is a need for specific regions to only be used - by certain applications but that general purpose management primitives - are still desired. -\layout Standard - -Other than the Xisop message port system which allows tasks to share and - synchronize arbitrary memory regions, it is possible for several tasks - to inherently share a common memory pool, using the -\emph on -TS_SHARED -\emph default - flag to -\emph on -task_alloc() -\emph default -. - This can in some circumstances be used if the memory resources are quite - scarce (many tasks which only allocate few but various sized memory blocks - of different memory types can waste quite a large amount of pages. - Using the same pool would then permit the same pages to be used among the - tasks for their similar allocation needs. - Their allocated blocks generally consist of a page which is split in equally - sized blocks). - Moreover, in such a setup, one task which allocates a block of memory does - not implicitly free it on exit, if other tasks are still running sharing - the memory. - Those blocks need to be explicitely freed unless all tasks exit to really - be released back to the system. - Moreover, special synchronization must be used by the tasks if they want - to access the same memory addresses. - This is usually done using an -\emph on -rwlock_t -\emph default -, or disabling the scheduler temporarily. -\layout Subsection - -Xisop public interrupt abstraction facilities -\layout Standard - -To allow Xisop architecture-specific devices to attach interrupt handler - hooks, and for portable code to make use of a few basic interrupt facilities - like timers, it is a good idea to provide a few access functions to allow - this. - The idea is to provide a facility which allows the kernel code, or userspace - tasks, to run a piece of code once, a certain number of times, or indefinitely, - as part of the low-level interrupt handling code. - It thus should be possible to add and delete their code handlers from each - of the wanted interrupt handlers. - To provide this multi-purpose facility, the following system was developed, - and almost entirely consists of portable common code. -\layout Subsubsection - -Internals -\layout Standard - -The following structure is defined in < -\emph on -common/kernel/exception.h -\emph default ->: -\layout Quote - - -\emph on -typedef struct _int_hook { -\newline -\SpecialChar ~ -\SpecialChar ~ -\SpecialChar ~ -\SpecialChar ~ -pnode_t node; -\newline -\SpecialChar ~ -\SpecialChar ~ -\SpecialChar ~ -\SpecialChar ~ -hookid_t id; -\newline -\SpecialChar ~ -\SpecialChar ~ -\SpecialChar ~ -\SpecialChar ~ -u_int32_t skipcount, runcount; -\newline -\SpecialChar ~ -\SpecialChar ~ -\SpecialChar ~ -\SpecialChar ~ -void (*code)(hookid_t, int, void *); -\newline -\SpecialChar ~ -\SpecialChar ~ -\SpecialChar ~ -\SpecialChar ~ -void *data; -\newline -} hook_t; -\layout Standard - -Where -\emph on -node -\emph default - is used for internal linking, -\emph on -id -\emph default -consists of a unique ID for this facility, -\emph on -skipcount -\emph default - of the number of times this handler will execute before calling the -\emph on -code -\emph default - hook, -\emph on -runcount -\emph default - of the number of times the hook -\emph on -code -\emph default - will be called before it gets automatically deleted, or 0 if it should - execute everytime this interrupt occurs. - -\emph on -void\SpecialChar ~ -(*code)(hookid_t, int, void\SpecialChar ~ -*) -\emph default - consists of the C function to invoke when the event occurs. - The abstracted arguments -\emph on -void -\emph default - pointer may serve any purpose the application wants. - -\emph on -void\SpecialChar ~ -*data -\emph default - pointer to abstract user-defined data will be passed as argument to the - called function. - The -\emph on -hookid_t -\emph default - argument will consist of the ID of the -\emph on -hook_t -\emph default - into the facility which caused the call, and is made to be unique. - The supplied -\emph on -int -\emph default - argument may be useless, or can serve to determine the origin of the interrupt, - somewhat like the -\emph on -hookid_t -\emph default -, but will use a facility-specific semantics, which could include a key - being pressed in the case of a keyboard interrupt, etc. - The kernel uses an efficient memory -\emph on -pool_t -\emph default - to internally allocate and free these automatically when hooks are attached - and detached from facilities. -\layout Standard - -The following machine-independent function is also provided so that the - port-specific code (from assembly or C) may easily order an execution of - all attached hooks on a facility. - The hook ids are made to be unique so that it is safe for the one who attached - a hook to try to delete it, assuming that if it exists in the list, it - cannot be any other hook supplied by other code (even if it expired and - another hook replaced it internally). - This consists of an interface for the port, not of the general user interface - to the facilities: -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -void\SpecialChar ~ -facility_exechooks(u_int32_t\SpecialChar ~ -facility,\SpecialChar ~ -int\SpecialChar ~ -origin) -\emph default - executes all attached code hooks of the specified -\emph on -facility -\emph default -, sequencially (if any). - This means that for each existing hook, the corresponding function is called - if it's -\emph on -skipcount -\emph default - is 0, or -\emph on -skipcount -\emph default - is simply decreased by 1 otherwise. - When the hook is to be called, it is passed the corresponding user data - pointer (which can be NULL) to it, the -\emph on -hookid_t -\emph default - id for the hook, and -\emph on -origin -\emph default - into the -\emph on -int -\emph default - argument. - The hook is then evaluated for expiration if -\emph on -runcount -\emph default - was non-zero at insertion with -\emph on -hook_attach() -\emph default -, and automatically destroyed if it expires. - The caller should normally disable the interrupt associated with the facility - temporarily before calling this function, however the associated -\emph on -facility_t -\emph default - internally uses an -\emph on -_rlock_t -\emph default - to prevent self-recursion, lock which is also used by -\emph on -hook_attach() -\emph default - and -\emph on -hook_detach() -\emph default - for safety. - This function is made for the port-specific code to call when the interrupt - this facility is concerned with occurs. - This function is dependent on -\emph on -_FACILITY_MAX -\emph default - and the -\emph on -enum _facilities -\emph default - which are port-specific in < -\emph on -port/support.h -\emph default ->. -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -void\SpecialChar ~ -facilities_init(void) -\emph default - is provided to ease initialization of the system facility arrays from the - port-specific code. - This function depends on the port-specific -\emph on -_FACILITY_MAX -\emph default - and the -\emph on -enum _facilities -\emph default - defined in < -\emph on -port/support.h -\emph default ->. - More informaton on port-specific initialization is provided in a next chapter. -\layout Standard - -Decision was made to provide the above functions even though their functionality - is simple for a few reasons. - It prevents code duplication among ports, minimises assembly sections in - machine-dependent sections, and they are known to work, providing the required - functionality. - Being abstracted, their internals may change over time affecting all ports - simultaneously without requireing changes in the machine-specific sections. - Moreover, they use an internal memory -\emph on -pool_t -\emph default - per -\emph on -facility_t -\emph default - for efficiency. - Once initialized, those will never need to query the system page primitives, - and are thus very efficient, as well as safe to use from interrupt context - without special handling. -\layout Standard - -Because these facilities are transparent to the Xisop microkernel itself, - and are provided by port-specific code, although driven by common portable - code, the interrupt sources are not required to internally correspond to - actual hardware interrupts. - The port-specific code is free to provide the wanted interrupt sources - and facilities in the manner it wishes. - As such they can be used for various hardware and sotfware event types. -\layout Subsubsection - -User interface -\layout Standard - -Here is now described the kernel user interface to manipulate custom interrupt - hooks on the provided facilities. - These are also machine-independent. - They were implemented around -\emph on -_rlock_t -\emph default - and -\emph on -pool_t -\emph default - primitives in a way to make it possible for userland to access their functional -ity without the need for system call traps. -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -hookid_t\SpecialChar ~ -hook_attach(u_int32_t\SpecialChar ~ -facility, u_int32_t\SpecialChar ~ -skipcount, u_int32_t\SpecialChar ~ -runcount, - void\SpecialChar ~ -(*code)(hookid_t,\SpecialChar ~ -int,\SpecialChar ~ -void\SpecialChar ~ -*), void\SpecialChar ~ -*data) -\emph default - Allows to append or insert a user supplied code hook to the wanted interrupt - facility. - The -\emph on -facility -\emph default - argument specifies what type of exception, trap or interrupt is wanted, - and is port-specific. - An example would be -\emph on -_FACILITY_VBLANK -\emph default -. - -\emph on -code -\emph default - specifies which function to call as the user hook handler, which should - never be NULL. - -\emph on -data -\emph default - points to an optional user data block which will be passed back when calling - the function handler, and can be NULL. - The argument of type -\emph on -hookid_t -\emph default - which will be passed will consist of the unique ID representing this -\emph on -hook_t -\emph default - into the -\emph on -facility -\emph default -, while the -\emph on -int -\emph default - argument, which is facility-specific, could serve to determine the origin - of the event if the facility serves several. - The return value is 0 in the case of an error (unknown public facility), - or a unique ID which can be used to eventually delete that particular hook. - This is required to not correspond to the hook function address, since - multiple hooks may call the same function if wanted. - This ID is always unique to this facility. - It is safe to attach a code hook function which can expire and then attempt - to remove it using the supplied -\emph on -hookid_t -\emph default -. - If it expired and another hook now uses the same -\emph on -hook_t -\emph default - address, it's -\emph on -hookid_t -\emph default - will still be different. - The semantics of -\emph on -skipcount -\emph default - and -\emph on -runcount -\emph default - are explained in the internals above. -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -bool\SpecialChar ~ -hook_detach(u_int32_t\SpecialChar ~ -facility,\SpecialChar ~ -hookid_t\SpecialChar ~ -id) -\emph default - Permits to remove a user supplied hook on the wanted -\emph on -facility -\emph default -, with the specfied -\emph on -id -\emph default -. - Returns TRUE if it could find and delete the hook, or FALSE if the hook - did not exist, or no longer does (it may have expired). -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -void\SpecialChar ~ -facility_disable(u_int32_t\SpecialChar ~ -facility) -\emph default - Disables all hooks of the specified facility temporarily. - They cannot expire during the period they are suspended, and none will - be executed, even when the interrupt source occurs. - It does not disable the interrupt source. - This system is recursive, in that the exact same number of calls to -\emph on -facility_enable() -\emph default - must be made to re-enable the facility. - Note that it is safe to attach and detach hooks from a facility at any - time, and that this function is only provided as a feature. -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -void\SpecialChar ~ -facility_enable(u_int32_t\SpecialChar ~ -facility) -\emph default - Re-enables the specified facility which was previously suspended using - -\emph on -facility_disable() -\emph default -, if the -\emph on -facility_t -\emph default - internal -\emph on -_rlock_t -\emph default - reaches 0 (that is all previous calls to -\emph on -facility_disable() -\emph default - were matched by a -\emph on -facility_disable() -\emph default -). -\layout Subsubsection - -Common facilities -\layout Standard - -Not all ports have all these facilities, and some may provide more. - However, this consists of a guide so that facilities which are intended - to provide the same functionality on the various architectures bear the - same name. -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -_FACILITY_SCHEDTIMER -\emph default - This is the only facility which must be available on all ports. - It usually executes at intervals governed by -\emph on -_SCHEDTIMER_HZ -\emph default -, a frequency defined in instances per second (hertz), which is defined - in < -\emph on -port/support.h -\emph default ->. - Systems which only comport one hardware timer source will at least always - have this timer facility available for multiple uses, although internally - used by the scheduler. - Disabling the scheduler does not disable the timer facility, as the scheduler - lock consists of an -\emph on -_rlock_t -\emph default -. -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -_FACILITY_KEYBOARD -\emph default - As the name implies, this facility is concerned with keyboard key presses. - The key code is usually returned in the -\emph on -int -\emph default - argument when calling the attached hooks. -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -_FACILITY_VBLANK -\emph default - This facility is very useful for graphic-oriented software which need to - synchronize operations with the video refresh rate. - The frequency of a vertical blank interrupt varies with the underlaying - hardware, but usually consists of 50Hz for PAL (europe) and 60Hz for NTSC - (american) systems. - This is very useful to implement double buffering, and can also be used - to synchronize audio events like music and sound effects with animations. -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -_FACILITY_TRAP -\emph default -* These type of facilities can be directly tied to user traps which may - remain available, and associated to a related -\emph on -_cause() -\emph default - function to allow user tasks to both attach handlers to receive those events - and call -\emph on -_cause() -\emph default - to trigger such events. - However, because of the Xisop -\emph on -sys_custom() -\emph default - system call, the use of such facilities become questionable. - -\emph on -XXX -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -_FACILITY_FLOPPYSYNC -\emph default - Triggered when a floppy drive notifies that it found the sync code of a - track. -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -_FACILITY_FLOPPYBLOCK -\emph default - Triggered when the floppy drive finished reading a requested block to memory. -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -_FACILITY_AUDIO -\emph default - Notification that an audio channel finished playing a sample, or starts - looping back the supplied sample buffer again. -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -_FACILITY_BLITTERREADY -\emph default - Notification by a parallel hardware blitter that it is done with the requested - operations and may now be ordered new instructions again. -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -_FACILITY_COPPER -\emph default - A hardware raster parrallel blitter originated interrupt -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -_FACILITY_SERIAL -\emph default - A generic serial interrupt -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -_FACILITY_SERIALRBF -\emph default - A serial Read Buffer Full interrupt -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -_FACILITY_SERIALTBE -\emph default - A serial Transmit Buffer Empty interrupt -\layout Standard - -Many other types of facilities may exist, although what should be taken - as an example consists of the clear names that they are given, which directly - reference to their origin as much as possible, while attempting to avoid - cryptic names such as KBD for keyboard, etc. - Where required, the label can be long enough as long as it remains meaningful. -\layout Subsection - -Kernel statistics -\layout Standard - -The -\emph on -src/common/kernel/statistic. -\emph default -( -\emph on -c -\emph default -| -\emph on -h -\emph default -) module defines primitives for statistic counters. -\layout Standard - - -\emph on -XXX -\layout Subsection - -Xisop binaries and executables -\layout Standard - -The Xisop kernel currently has no ELF or a.out relocatable executable loader, - and no custom format was implemented, which would require a GCC ld BFD - backend to be written. - This means that at current time, all the shared libraries, devices, handlers - and user tasks need to be started by the kernel at startup. - This is easily done however, but prevents the microkernel from using all - it's features of attaching required components at runtime as wanted, although - it was designed to eventually be able to do so efficiently. - It however currently allows Xisop to be useable in embedded systems which - have defined components, like for monolithic kernels. -\layout Subsection - -Xisop devices -\layout Standard - -Xisop devices generally consist of a medium-level backend to hardware-assisted - services, such as keyboard input, tty output, RS-232 communication, etc. - As such, they are generally architecture-dependent. - Of course, machine-independent devices may be written as wanted, where - the high-level handler interface is considered unadequate and that shared - access to some kind of ressource is wanted, however. -\layout Standard - -The Xisop devices are implemented around the message port system. - There were two main reasons which determined this decision compared to - using a shared library type system. - First, the message receiving responsiveness and speed can be determined - by the task priority, which allows the administrator to decide which devices - and tasks to prioritize over others. - Secondly, the message passing system already provides reliable FIFO queuing, - and each message/request can be replied to when wanted, allowing a device - to easily serve resources in a multitasking-friendly, asynchroneous manner - to the simultaneous requesters. -\layout Standard - -A device is thus implemented by a task, which decides to attach a system - device node and then takes the responsibility to serve the expected requests. - Each task may only attach one device to the system lists, and has to specify - the device name and version which are used for other tasks to open the - device. - This means that a device name may have several simultaneous versions running - on the system. -\layout Standard - -Of course, there are cases where only a single device may control a specific - hardware resource for instance, and in this case the versionning system - becomes less useful, in which case version 0 is usually used. - However, the version may still be useful if the various versions of the - device had changes to the user interface. - In this case, using the version is still useful, as opening using the wrong - version (the one a task expects) will at least always fail cleanly with - an error, rather than leaving the task to open a device which does not - act as expected when sending requests. -\layout Subsubsection - -User interface -\layout Standard - -Here are described the device interface functions. - Let's first present the functions which are intended for client tasks to - access device server ones: -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -device_t\SpecialChar ~ -*device_open(const\SpecialChar ~ -char\SpecialChar ~ -*name,\SpecialChar ~ -u_int32_t\SpecialChar ~ -version,\SpecialChar ~ -u_int32_t\SpecialChar ~ -unit) -\emph default - Allows the task to open the unit number -\emph on -unit -\emph default - of device -\emph on -name -\emph default - of version -\emph on -version -\emph default -. - NULL is returned on failure, which can occur because of lack of memory, - or if the specified device name of the specified version does not exist. - Otherwise, a -\emph on -device_t -\emph default - handle pointer is returned, which may then be used at -\emph on -iorequest_init() -\emph default -. - Device names are case-sensitive. - If the task exists and that open devices have not been explicitely closed, - the kernel automatically closes them. -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -device_t\SpecialChar ~ -*device_close(device_t\SpecialChar ~ -*handle) -\emph default - Closes a device handle which previously was opened using -\emph on -device_open() -\emph default -. - Always returns NULL. - Any -\emph on -iorequest_t -\emph default - associated to this -\emph on -device_t -\emph default - may not be used anymore, as it becomes invalid, unless it be reinitialized - again using -\emph on -iorequest_init() -\emph default - using a new -\emph on -device_t -\emph default - handle. -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -bool\SpecialChar ~ -iorequest_init(iorequest_t\SpecialChar ~ -*message,\SpecialChar ~ -device_t\SpecialChar ~ -*handle,\SpecialChar ~ -port_t\SpecialChar ~ -*replyport) -\emph default - Initializes an -\emph on -iorequest_t -\emph default - -\emph on -message -\emph default -, which is necessary to use other -\emph on -iorequest_ -\emph default -* -\emph on -() -\emph default - functions to perform device requests. - -\emph on -handle -\emph default - specifies the -\emph on -device_t -\emph default - which will serve requests for this message during future requests, and - -\emph on -replyport -\emph default - of a generally -\emph on -iorequest_t -\emph default --specific private port which was previously created, through which request - result messages will be sent back to us by the device. - The -\emph on -iorequest_t -\emph default - buffer is the responsibility of the task, just like -\emph on -port_t -\emph default - -\emph on -message_t -\emph default - are. - Returns TRUE on success, or FALSE on failure (invalid arguments or out - of memory). - The device may optionally internally allocate device-specific additional - data which will then attach to the -\emph on -iorequest_t -\emph default -. - These will however be allocated on the current task's memory pool, and - are therefore released automatically if the task exists. -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -bool\SpecialChar ~ -iorequest_destroy(iorequest_t\SpecialChar ~ -*message) -\emph default - Invalidates the -\emph on -iorequest_t -\emph default - -\emph on -message -\emph default - which was previously initialized using -\emph on -iorequest_init() -\emph default -. - As devices may internally allocate device-specific additional data and - attach it to an -\emph on -iorequest_t -\emph default - at initialization, a task should call this function when it no longer needs - the -\emph on -iorequest_t -\emph default -. - Of course, if the task exists, the resources are automatically released - back to the system, however. -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -bool\SpecialChar ~ -iorequest_sync(iorequest_t\SpecialChar ~ -*message) -\emph default - Permits to send a synchroneous request to a device, via -\emph on -message -\emph default -. - This means that the task is suspended until the request completes. - The reply result is also automatically extracted from the reply port associated - to the -\emph on -iorequest_t -\emph default -. - Before sending a request, some fields of the -\emph on -iorequest_t -\emph default - message should be set. - When it completes, the result fields will have been set. - Both can have device-dependent semantics, although there is generally a - standard, which is described in the internals section. - Returns TRUE if the request could be sent, or FALSE if there was an internal - problem (invalid -\emph on -message -\emph default -, or no longer existing device). -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -bool\SpecialChar ~ -iorequest_async(iorequest_t\SpecialChar ~ -*message) -\emph default - Very similar to -\emph on -iorequest_sync() -\emph default -, but permits to launch the request without waiting until it completes, - performing an asynchroneous request. - Upon completion, the device will internally -\emph on -port_reply() -\emph default - into the reply port associated with -\emph on -message -\emph default -, and the task is then responsible for extracting the reply message from - the reply port. - This allows to launch several asynchroneous requests and to monitor signals - or ports to detect when they occur. - For instance, a task may launch an asynchroneous request to read one character, - and when the request completes, specifying that data exists to read. - It can then send synchroneous requests to read larger blocks until no more - data is available, in which case it may then again send an asynchroneous - request and resume normal activity. - TRUE is returned if the request could be launched, or FALSE if it failed - (invalid -\emph on -message -\emph default - or no longer existing device). -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -bool\SpecialChar ~ -iorequest_abort(iorequest_t\SpecialChar ~ -*message) -\emph default - If -\emph on -message -\emph default - currently consists of an asynchroneous request which was made using -\emph on -iorequest_async() -\emph default - and is still pending, an abort request is sent to cancel it. - Like usual, the device will reply still as soon as the request could be - aborted, and the task becomes responsible to unlink the reply message from - the reply port associated with -\emph on -message -\emph default -. - TRUE is returned on success, or FALSE if -\emph on -message -\emph default - does not consist of a currently pending asynchroneous request. -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -bool\SpecialChar ~ -iorequest_wait(iorequest_t\SpecialChar ~ -*message) -\emph default - Waits until a currently pending (or aborted which not yet replied) asynchoneous - request terminates, and automatically unlinks the reply message received - through the reply port of -\emph on -message -\emph default -. - This can especially be useful after an -\emph on -iorequest_abort() -\emph default -. - Returns TRUE on success, or FALSE if -\emph on -message -\emph default - is not currently a pending (or aborted which did not yet return) asynchroneous - request. -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -bool\SpecialChar ~ -iorequest_pending(iorequest_t\SpecialChar ~ -*message) -\emph default - Returns TRUE if -\emph on -message -\emph default - currently consists of an asynchroneously pending request, or FALSE otherwise. -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -bool\SpecialChar ~ -iorequest_aborted(iorequest_t\SpecialChar ~ -*message) -\emph default - Returns TRUE if -\emph on -message -\emph default - consists of a request which just completed, but which was an asynchroneous - request and was aborted. -\layout Standard - -These utility functions, although performing the most basic Xisop device - operations, are provided to minimize code duplication, for very simple - synchroneous I/O. - Normally, tasks will address requests using -\emph on -iorequest_sync() -\emph default - and -\emph on -iorequest_async() -\emph default - as needed, but this can be useful to have: -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -ssize_t\SpecialChar ~ -device_read(iorequest_t\SpecialChar ~ -*req,\SpecialChar ~ -void\SpecialChar ~ -*buf,\SpecialChar ~ -size_t\SpecialChar ~ -len) -\emph default - Similarily to unix -\emph on -read() -\emph default -, reads at most -\emph on -len -\emph default - bytes of data from the opened device and unit associated with -\emph on -req -\emph default - into -\emph on -buf -\emph default -, and returns the number of actually read bytes, or -1 on error. - The current task is suspended until the operation completes, since -\emph on -iorequest_sync() -\emph default - is internally used. -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -ssize_t\SpecialChar ~ -device_write(iorequest_t\SpecialChar ~ -*req,\SpecialChar ~ -void\SpecialChar ~ -*buf,\SpecialChar ~ -size_t\SpecialChar ~ -len) -\emph default - Like unix -\emph on -write() -\emph default -, writes at most -\emph on -len -\emph default - bytes of data from -\emph on -buf -\emph default -, to the opened device and unit associated with -\emph on -req -\emph default -, and returns the number of actually written bytes, or -1 on error. - The current task is suspended until the operation completes as it internally - uses -\emph on -iorequest_sync() -\emph default -. -\layout Subsubsection - -Device server interface -\layout Standard - -Here follows functions which are only useful to device server tasks to call: -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -bool\SpecialChar ~ -device_attach(const\SpecialChar ~ -char\SpecialChar ~ -*name, u_int32_t\SpecialChar ~ -version, port_t\SpecialChar ~ -*port, void\SpecialChar ~ -(*clean)(vo -id), bool\SpecialChar ~ -(*open)(void\SpecialChar ~ -**,\SpecialChar ~ -u_int32_t), void\SpecialChar ~ -(*close)(void\SpecialChar ~ -*,\SpecialChar ~ -u_int32_t), void\SpecialChar ~ -*(*iorini -t)(void), void\SpecialChar ~ -(*iordestroy)(void\SpecialChar ~ -*), u_int8_t\SpecialChar ~ -flags) -\emph default - -\newline -Allows the current task to become a system device. - -\emph on -name -\emph default - consists of the unique case-sensitive device name to assign to the system - device node, which will be required to use at -\emph on -device_open() -\emph default -, and will be truncated to 32 characters if longer. - -\emph on -version -\emph default - specifies the version number to use, which will also need to match at -\emph on -device_open() -\emph default -, for this device name. - -\emph on -port -\emph default - consists of the device server's private port, through which requests should - be sent. - -\emph on -flags -\emph default - consists of one or combination of -\emph on -DNF_ -\emph default -* flags described in the header file, like -\emph on -DNF_RESIDENT -\emph default - which tells Xisop to never cause the device task to exit if there exist - no more client open instances. - The various function pointers which must be supplied are explained below: -\begin_deeper -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -void\SpecialChar ~ -(*clean)(void) -\emph default - is a function which the device task can provide for Xisop to call when - no more client open instances exist for this device, unless the device - is resident (and -\emph on -flags -\emph default - comported -\emph on -DNF_RESIDENT -\emph default - at device creation). - This function is responsible to restore the hardware which this device - may have been serving to a consistant and known state. - It will also be called automatically by Xisop when the task exits normally. - Note that -\emph on -clean() -\emph default - should not comport any special function to cause the task to end. - Xisop will send a -\emph on -SIGTERM -\emph default - signal when the task should do so. - If the task exits by itself, -\emph on -clean() -\emph default - will be called automatically nevertheless. - Because this function may be called under the context of any other task, - it should not expect to execute under the device's task memory pool (and - therefore should normally not call allocation functions). - It is possible to specify NULL for this function if the device has no need - for any special cleanup function. -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -bool\SpecialChar ~ -(*open)(void\SpecialChar ~ -**udata,\SpecialChar ~ -u_int32_t\SpecialChar ~ -unit) -\emph default - All devices are required to provide this function. - It's purpose is to validate if -\emph on -unit -\emph default - can be opened (some devices limit the number of open instances of a unit, - to protect their resources), and to optionally allocate any needed device-speci -fic data which it may need to attach to -\emph on -device_t -\emph default - nodes. - -\emph on -open() -\emph default - is called at each -\emph on -device_open() -\emph default - function instance called on this device. - It is expected to return FALSE if it refuses to open the specified -\emph on -unit -\emph default - or if it cannot allocate any required resources, or TRUE on success. - If the task allocates data which is needed to be attached to the -\emph on -device_t -\emph default - handle, it should supply the address of the allocated data block into the - supplied -\emph on -udata -\emph default -. - It should set NULL there otherwise. - Although the device may at it's discretion maintain counters on the number - of currently opened units, etc, Xisop will automatically send a SIGTERM - to the device task if it is non-resident and that there exist no more openers. - Note that this function is called under the context of the task which calls - -\emph on -device_open() -\emph default -. - As such, the memory allocations, such as the optional -\emph on -udata -\emph default - block will be automatically freed when the other task ends, not ours. - For this reason, the function can use -\emph on -malloc() -\emph default - and companions safely, but should not use lower-level Xisop kernel allocation - primitives. -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -void\SpecialChar ~ -(*close)(void\SpecialChar ~ -*udata,\SpecialChar ~ -u_int32_t\SpecialChar ~ -unit) -\emph default - This function is also required for all device tasks to provide, and is - called by -\emph on -device_close() -\emph default - on a -\emph on -device_t -\emph default - handle which was previously associated to this task by -\emph on -device_open() -\emph default -. - The function is responsible for calling -\emph on -free() -\emph default - on the supplied -\emph on -udata -\emph default - pointer if needed, and to perform the necessary device-specific cleanup - required when a device handle closes. - -\emph on -unit -\emph default - specifies the unit which was opened by this handle. -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -void\SpecialChar ~ -*(*iorinit)(void) -\emph default - Devices may optionally provide this function to allocate -\emph on -iorequest_t -\emph default - message specific data which it might need, very similarily to -\emph on -open() -\emph default - which can attach data to a -\emph on -device_t -\emph default - handle. - NULL can be supplied if there is no need for -\emph on -iorequest_t -\emph default - specific extention data. - This function is called under the context of the task calling -\emph on -iorequest_init() -\emph default - and as such no Xisop low-level allocation functions should be called. - The function may allocate the data block with -\emph on -malloc() -\emph default -, initialize it and return a pointer to it, or NULL on failure (out of memory). -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -void\SpecialChar ~ -(iordestroy)(void\SpecialChar ~ -*udata) -\emph default - May also be supplied NULL if NULL was supplied for -\emph on -iorinit() -\emph default -. - Otherwise, this function is responsible to -\emph on -free() -\emph default - the supplied -\emph on -udata -\emph default -, which corresponds to a block of memory which was returned by a previous - -\emph on -iorinit() -\emph default - call. -\end_deeper -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -bool\SpecialChar ~ -iorequest_satisfy(iorequest_t\SpecialChar ~ -*message,\SpecialChar ~ -bool\SpecialChar ~ -result) -\emph default - Is a useful utility function to set the main request success result code - and reply to the task that it has completed. -\layout Standard - -Various macros of interest may be used by device tasks: -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -void\SpecialChar ~ -*DEVICEHANDLE_UDATA(device_t\SpecialChar ~ -*handle) -\emph default - Returns the -\emph on -udata -\emph default - pointer associated with the supplied -\emph on -device_t -\emph default - -\emph on -handle -\emph default -. -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -void\SpecialChar ~ -*IOREQUEST_UDATA(iorequest_t\SpecialChar ~ -*message) -\emph default - Returns the -\emph on -udata -\emph default - pointer associated with the supplied -\emph on -iorequest_t message -\emph default -. -\layout Subsubsection - -Internals -\layout Standard - -This system based upon the Xisop message ports system and the previously - described functions permit to open or provide devices, and to communicate - requests from the client-side and completion from the server-side, through - a special message, the -\emph on -iorequest_t -\emph default -. -\layout Standard - - -\emph on -XXX -\layout Subsection - -Xisop handlers -\layout Standard - -Handlers provide a higher-level abstraction to devices or resources. - It should be noted that unlike devices, these are implemented in the form - of a standard shared library format. - This means that devices are usually a nice abstraction to mount handlers - on, as devices naturally perform the queuing, etc. - -\emph on -XXX -\layout Subsection - -Xisop shared libraries -\layout Standard - -The concept of Xisop shared libraries is both very simple, and unusual. - It is important that all functions a library provides publically be reentrant. - Opening a library basically obtains the pointer to a structure which is - necessary to access it's function pointers. - As such, only one resident copy is required for all applications, and new - applications can -\begin_inset Quotes eld -\end_inset - -attach -\begin_inset Quotes erd -\end_inset - - the libraries they need as required. - The system keeps track of how many times it is currently being open by - various tasks, and can therefore know when the library should be expunged - from memory. - Obviously, when a library is requested which is not currently in RAM, it - should be loaded in the system from disk. -\layout Standard - -Each library may have concurrent versions on the same system, in memory - and on disk. - When an application requests access to a library, it optionally specifies - the expected version, without which the latest is assumed. - This way, it is possible for the administrator to get rid of the obsolete - libraries but only after making sure that no applications require them - anymore. - While software is being developped, it becomes possible to have concurrent - versions of applications each using their respectively related material. -\layout Standard - - -\emph on -XXX -\layout Subsubsection - -The Xisop library -\layout Standard - - -\emph on -XXX -\layout Subsection - -Xisop system calls -\layout Standard - -System calls are different than normal functions in that they allow userspace - tasks to execute instructions which are normally only allowed to call in - supervisor mode, or in kernel space. - Moreover, system calls are currently uninterruptible in Xisop, which means - that the task is guaranteed to not be preempted while executing a system - call function, until it returned. - These are internally implemented using processor traps, by the port-specific - code. - However, the system call functions themselves are portable and part of - the Xisop common code. - Because Xisop does not use MMU facilities, system calls are very fast to - execute compared to on unix systems. - Xisop design attempts to require the less of these possible however, because - they also disable the scheduler when executing. -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -struct\SpecialChar ~ -xisop_root\SpecialChar ~ -*sys_getroot(void) -\emph default - Permits a task to obtain the address of the main Xisop control structure, - where system lists are stored. - Obviously, if a task uses this information in any way, it has to be careful - not to disrupt Xisop activities. - It is recommended to disable the scheduler and/or interrupts where required - to access the information which that structure provides. - It is made for people who know Xisop inside out only. -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -void\SpecialChar ~ -sys_int_disable(void) -\emph default - This internally calls -\emph on -_splhigh() -\emph default - which ensures to mask all interrupts, including that of the preemptive - scheduler. - Precautions should be made about the calls used similarly to when disabling - the scheduler. - However, this call permits a task to completely take control over Xisop. -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -void\SpecialChar ~ -sys_int_enable(void) -\emph default - Internally calling -\emph on -_spl0() -\emph default -, this re-enables all interrupts. - -\emph on -XXX -\emph default - heh actually, can the -\emph on -_syscall() -\emph default - trap actually occur after a -\emph on -sys_int_disable() -\emph default -? Will need to check this out. -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -void\SpecialChar ~ -sys_idle(void) -\emph default - Permits to suspend the processor until the next trap, interrupt or exception - occurs. - This internally calls the -\emph on -_idle() -\emph default - processor-specific function. - This is mostly used by Xisop -\emph on -main() -\emph default - which is returned control to when no tasks are currently on the ready queue. -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -void\SpecialChar ~ -sys_custom(void\SpecialChar ~ -*res,\SpecialChar ~ -void\SpecialChar ~ -(*func)(void\SpecialChar ~ -*,\SpecialChar ~ -void\SpecialChar ~ -*),\SpecialChar ~ -void\SpecialChar ~ -*args) -\emph default - A very special system call, allows user tasks to execute arbitrary code - in supervisor mode, uninterruptibly (scheduler will not preempt, but interrupts - can still take place). - -\emph on -func -\emph default - specifies the function to call, which will be passed -\emph on -res -\emph default - as the first argument and -\emph on -args -\emph default - as the second argument, which can be used by the function to acquire parameters - and return results. - -\emph on -res -\emph default - and -\emph on -args -\emph default - can be NULL when they are not needed. - Because Xisop attempts to be more useful to the programmer than to secure - the kernel against userland, this was beleived to be a very useful function, - where user tasks can create their custom system calls as required. -\layout Subsection - -Xisop general programming interfaces -\layout Standard - -In an attempt to keep the code unified and clean, multipurpose interfaces - were provided. -\layout Subsubsection - -Byte alignment macros -\layout Standard - -In -\emph on - -\emph default - the following macros are provided for byte alignment. -\layout Standard - -These macros permit object size related byte alignment: -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -size_t\SpecialChar ~ -OALIGN_CEIL(size_t\SpecialChar ~ -v,\SpecialChar ~ -o) -\emph default - -\emph on -o -\emph default --aligns -\emph on -v -\emph default -. - This macro rounds -\emph on -v -\emph default - to the nearest larger unit as required. - -\emph on -o -\emph default - should be any native C or custom structure type, to which -\emph on -v -\emph default - should be aligned relative to. - -\emph on -v -\emph default - is not modified, the new value is returned. -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -size_t\SpecialChar ~ -OALIGN_FLOOR(size_t\SpecialChar ~ -v,\SpecialChar ~ -o) -\emph default - -\emph on -o -\emph default --aligns -\emph on -v -\emph default -. - Unlike -\emph on -OALIGN_CEIL() -\emph default - this macro rounds to the nearest smaller unit as required. -\layout Standard - -And these macros permit byte size related alignment: -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -size_t\SpecialChar ~ -BALIGN_CEIL(size_t\SpecialChar ~ -v,\SpecialChar ~ -size_t\SpecialChar ~ -s) -\emph default - This macro aligns -\emph on -v -\emph default - to the nearest larger unit relative to -\emph on -s -\emph default - size as required. - -\emph on -v -\emph default - is not modified, the new value is returned. -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -size_t\SpecialChar ~ -BALIGN_FLOOR(size_t\SpecialChar ~ -v,\SpecialChar ~ -size_t\SpecialChar ~ -s) -\emph default - Very similar to -\emph on -BALIGN_CEIL() -\emph default -, but rounds -\emph on -v -\emph default - to the nearest smaller unit relative to -\emph on -s -\emph default - size as required. -\layout Subsubsection - -Byte order manipulation macros -\layout Standard - -In -\emph on - -\emph default - the following macros are provided for byte order/endian conversions. - These are most useful for network Remote Procedure Call implementations, - as well as for binary file formats which can be in network order so that - multiple architectures may easily use the same file format. - Depending on the native host byte order (the -\emph on -_ARCH_BIG_ENDIAN -\emph default - and -\emph on -_ARCH_LITTLE_ENDIAN -\emph default - definitions defined by processor-specific code), these will perform no - action where no conversion is necessary. - The network order should be used for transferring over networks or writing - to binary files, and actually corresponds to the native host byte order - on big endian architectures. -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -u_int16_t\SpecialChar ~ -BYTEORDER_NETWORK16(u_int16_t) -\emph default - Converts the specified 16-bit word to network order for sending through - the network or writing to a binary file. -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -u_int16_t\SpecialChar ~ -BYTEORDER_HOST16(u_int16_t) -\emph default - Converts the specified network order 16-bit word to native host order after - reading from a binary file or receiving through the network. -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -u_int32_t\SpecialChar ~ -BYTEORDER_NETWORK32(u_int32_t) -\emph default - Converts the specified 32-bit word to network order for sending through - the network or writing to a binary file. -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -u_int32_t\SpecialChar ~ -BYTEORDER_HOST32(u_int32_t) -\emph default - Converts the specified network order 32-bit word to native host order after - reading from a binary file or receiving through the network. -\layout Subsubsection - -Doubly linked lists -\layout Standard - -These macros, as well as the -\emph on -list_t -\emph default - and -\emph on -node_t -\emph default - types are defined in -\emph on - -\emph default - and -\emph on - -\emph default -. -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -void\SpecialChar ~ -DLIST_INIT(list_t\SpecialChar ~ -*list) -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -void\SpecialChar ~ -DLIST_APPEND(list_t\SpecialChar ~ -*list,\SpecialChar ~ -node_t\SpecialChar ~ -*node) -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -void\SpecialChar ~ -DLIST_INSERT(list_t\SpecialChar ~ -*list,\SpecialChar ~ -node_t\SpecialChar ~ -*node) -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -void\SpecialChar ~ -DLIST_INSERTAT(list_t\SpecialChar ~ -*list,\SpecialChar ~ -node_t\SpecialChar ~ -*atnode,\SpecialChar ~ -node_t\SpecialChar ~ -*node) -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -void\SpecialChar ~ -DLIST_SWAP(list\SpecialChar ~ -t\SpecialChar ~ -*dstlist,\SpecialChar ~ -list_t\SpecialChar ~ -*srclist,\SpecialChar ~ -node_t\SpecialChar ~ -*srcnode,\SpecialChar ~ -bool\SpecialChar ~ -insert) -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -void\SpecialChar ~ -DLIST_UNLINK(list_t\SpecialChar ~ -*list,\SpecialChar ~ -node_t\SpecialChar ~ -*node) -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -u_int32_t\SpecialChar ~ -DLIST_NODES(list_t\SpecialChar ~ -*list) -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -node_t\SpecialChar ~ -*DLIST_TOP(list_t\SpecialChar ~ -*list) -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -node_t\SpecialChar ~ -*DLIST_BOTTOM(list_t\SpecialChar ~ -*list) -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -node_t\SpecialChar ~ -*DLIST_NEXT(node_t\SpecialChar ~ -*node) -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -node_t\SpecialChar ~ -*DLIST_PREV(node_t\SpecialChar ~ -*node) -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -void\SpecialChar ~ -DLIST_FOREACH(list_t\SpecialChar ~ -*list,\SpecialChar ~ -node_t\SpecialChar ~ -*iterator) -\layout Standard - - -\emph on -XXX -\layout Subsubsection - -Hash based fast lookup tables -\layout Standard - -The prototypes and types for these are defined in -\emph on - -\emph default - and -\emph on - -\emph default -. -\layout Standard - - -\emph on -XXX -\layout Subsubsection - -FIFO (First In, First Out) buffers -\layout Standard - -These macros as well as the default FIFO types are defined in -\emph on - -\emph default - and -\emph on - -\emph default -. -\layout Standard - - -\emph on -XXX -\layout Subsubsection - -LIFO (Last In, First Out / Stack) buffers -\layout Standard - -These macros as well as the default LIFO types are defined in -\emph on - -\emph default - and -\emph on - -\emph default -. -\layout Standard - - -\emph on -XXX -\layout Subsection - -Xisop source tree organization -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -doc/ -\emph default - This directory holds this file, as well as various notes of interest -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -src/ -\emph default - Where all source resides -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -src/common/ -\emph default - All machine-independent Xisop source -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -src/common/kernel/ -\emph default - Xisop kernel main code -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -src/common/kernlib/ -\emph default - Xisop kernel main machine-independent libraries -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -src/common/library/ -\emph default - Xisop machine-independent shared libraries -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -src/common/device/ -\emph default - Xisop machine-independent devices -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -src/common/handler/ -\emph default - Xisop machine-independent handlers -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -src/common/task/ -\emph default - Xisop machine-independent resident tasks -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -src/processors/ -\emph default - Holds all processor-specific code -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -src/processors/m68k/ -\emph default - The Motorola m68k support (MC68000L8/L10) -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -src/processors/m68k/kernlib/ -\emph default - m68k specific replacement functions for wanted standard machine-independent - kernlib ones (optional) -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -src/ports/ -\emph default - Holds all port-specific code, including boot loaders -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -src/ports/amiga/ -\emph default - The Amiga port of Xisop code resides here -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -src/ports/amiga/kernlib/ -\emph default - Amiga-specific replacement functions for wanted standard machine-independent - kernlib ones (optional) -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -src/ports/amiga/boot/ -\emph default - The Amiga-specific code to generate a bootable kernel -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -src/ports/amiga/library/ -\emph default - Amiga-specific shared libraries (optional) -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -src/ports/amiga/device/ -\emph default - Amiga-specific devices (optional) -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -src/ports/amiga/handler/ -\emph default - Amiga-specific handlers (optional) -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -src/ports/amiga/task/ -\emph default - Amiga-specific resident tasks (optional) -\layout Subsection - -The build process -\layout Standard - -Here is described the way the Xisop source is built to create a binary kernel - image. - -\emph on -/bin/sh -\emph default - is also assumed to exist for the building process. - The convention for the script names are as follows: -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -src/generic_makedefs.sh -\emph default - contains various sh functions and variables assigned to local utilities - which are required by all -\emph on -clean.sh -\emph default - scripts, and the main -\emph on -src/make.sh -\emph default - script. - Those scripts -\emph on -source -\emph default - this file to obtain the common information. -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -src/makedefs.sh -\emph default - consists of a symbolic link to -\emph on -port/makedefs.sh -\emph default -, which contains the configuration information required to build the for - the target port system. - The paths to the various useful utilities are assigned to shell variables. - If functions need to be supplied for other build scripts, they also should - be defined here. - This file is sourced (included) by all other build scripts. - They all should use the variables supplied by this file when accessing - the cross-compile or local utilities. -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -src/make.sh -\emph default - allows to fully compile the kernel to result in a kernel image. - Requires the target port name to be specified, as it also sets up required - symbolic links. -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -src/clean.sh -\emph default - cleans the source tree, that is, deletes all files which may have been - created by the build process. -\layout List -\labelwidthstring 00.00.0000 - -* -\emph on -/make.sh -\emph default - The various sections described below will need such a script which should - perform the necessary steps to compile the section. - These -\emph on -source -\emph default - src/ -\emph on -makedefs.sh -\emph default - to know which commands to invoke and access useful -\emph on -/bin/sh -\emph default - macros.. -\layout List -\labelwidthstring 00.00.0000 - -* -\emph on -/clean.sh -\emph default - The sections also need such a script which is expected to delete all files - which the -\emph on -make.sh -\emph default - script counterpart creates. - These -\emph on -source -\emph default - -\emph on -src/generic_makedefs.sh -\emph default -. -\layout Paragraph - -Cleaning the whole source tree -\layout Standard - -The -\emph on -src/clean.sh -\emph default - script ensures to clean the whole source tree by calling the -\emph on -clean.sh -\emph default - script for each section, including all ports and processors. - The invoker is expected to be in the -\emph on -src/ -\emph default - directory. -\layout Paragraph - -Building the system -\layout Standard - -The -\emph on -src/make.sh -\emph default - script builds the entire system. - The main steps performed to compile the system are described as follows, - in order. -\layout Subsubsection - -Preliminary building process setup -\layout Standard - -The -\emph on -src/processor, -\emph default - -\emph on -src/port -\emph default - and -\emph on -src/makedefs.sh -\emph default - symbolic links should point to their corresponding -\emph on -src/processors/ -\emph default - and -\emph on -src/ports/ -\emph default - and -\emph on -src/ports//makedefs.sh -\emph default -. - These are setup depending on the target system for which Xisop is being - built. - Each of these (port and processor sections) supplies a -\emph on -support.h -\emph default - headerfile which should be sufficient for the rest of the kernel code to - access the functionality of the hardware specific code they provide. - Moreover, each of them will after building provide -\emph on -ar -\emph default - archives ( -\emph on -.a -\emph default - files) into their respective -\emph on -ar/ -\emph default - directory, resulting from their various object files, which will be sufficient - to link with the rest of the kernel code to achieve the end result. - The -\emph on -src/make.sh -\emph default - script creates these symbolic links when supplied with a valid -\emph on -target -\emph default - (using the -\emph on --t -\emph default - parameter). -\layout Standard - - -\emph on -src/make.sh -\emph default - also ensures to set the -\emph on -$SRCDIR -\emph default - environment variable to the absolute path to the current directory ( -\emph on -src/ -\emph default -), which should be used by other build scripts when compiling modules so - that -\emph on -#include -\emph default - directives in the source can locate the file using -\emph on --I -\emph default - parameter, etc. -\layout Subsubsection - -Building the processor-specific support code -\layout Standard - -Control is delegated to -\emph on -src/processor -\emph default -/ -\emph on -make.sh -\emph default -. - This is expected to assemble and compile the various sections it comports - to binary objects independently ( -\emph on -.o -\emph default -) using -\emph on --c -\emph default - parameter to -\emph on -gcc -\emph default - command, and to then archive them as -\emph on -ar -\emph default - archives into the -\emph on -src/processor/ar -\emph default - directory using the -\emph on -ar -\emph default - and -\emph on -ranlib -\emph default - commands. - This final object will be linked with the kernel code by another section - of the build process. - It is to be noted that it will be linked before the general common machine-inde -pendent kernel library, so that it is possible to provide processor-specific - replacements to standard low-level functions, such as -\emph on -memcpy() -\emph default -, -\emph on -memset() -\emph default -, etc. - These could however be overriden by the port-specific support code. - When control is given to -\emph on -make.sh -\emph default - of this section, the current directory will have been changed as well so - that it is safe to access the section files relatively to the current (section) - directory. -\layout Subsubsection - -Building the port-specific support code -\layout Standard - -Control is given to -\emph on -src/port/make.sh -\emph default - to compile this section, and very similarly to the processor-specific section, - the goal is to generate one or more -\emph on -ar -\emph default - archive in the -\emph on -src/port/ar -\emph default - directory. - -\emph on -src/port/support.h -\emph default - will also contain all necessary information for the rest of the kernel - code to use the port-specific support. - This code will be linked with the global kernel before s -\emph on -rc/processor/ar/*.a -\emph default -, which means that it is possible to provide port-specific standard functions - overriding the processor-specific ones, as well as kernel machine-independent - common ones. - This could allow for instance to provide -\emph on -memcpy() -\emph default - and -\emph on -memset() -\emph default - functions using specialized data moving hardware such as blitters, etc. - It is however recommended that libraries be built in a way to not force - the whole library to be included into the final kernel if only a few of - the functions were necessary. - To do this, a library could consists of a directory, with all functions - isolated in their own -\emph on -.c -\emph default - file. - The -\emph on -ar -\emph default - archive then results in a bunch of very small -\emph on -.o -\emph default - files which will be ignored by the linker when unrequired. - It is important to also run -\emph on -ranlib -\emph default - on the -\emph on -ar -\emph default - archive. -\layout Standard - - -\emph on -XXX -\emph default - -\emph on -Hmm I should find a nice way to define the resident libraries, devices and - handlers, both common and port-specific ones. - Also, which tasks should be initially started, etc. - Actually, resident devices and handlers, being tasks, would just need to - be included in the tasks to start, I guess... -\layout Standard - - -\emph on -XXX This next paragraph is currently invalid. -\layout Standard - -The -\emph on -ar/*.a -\emph default - result should also include -\emph on -_init_libraries() -\emph default -, -\emph on -_init_devices() -\emph default - and -\emph on -_init_handlers() -\emph default - functions, which should as required attach the wanted shared libraries, - devices and handlers in the kernel by calling their init function. - These also should initialize machine-independent ones. - If there are port-dependent ones, their code should be located into the - -\emph on -library/ -\emph default -, -\emph on -device/ -\emph default - and -\emph on -handler/ -\emph default - directories in -\emph on -src/port/ -\emph default -. -\layout Subsubsection - -Building the machine-independent main Xisop code -\layout Standard - -The -\emph on -src/common/kernlib/ -\emph default - directory comports a directory for each internal kernel library, which - each contain functions isolated into a single file each. - These are built separately as object modules, and are archived using -\emph on -ar -\emph default - and -\emph on -ranlib -\emph default - into -\emph on -src/common/kernlib/ar -\emph default -directory. - The reason for this is that it allows the resulting kernel image to be - smaller when not all of the functions of a particular library are used. - If all string functions were located into the same -\emph on -string.c -\emph default - file for instance, all of the string functions would automatically be linked - within the result even if only two string functions were actually used, - for instance. -\layout Standard - -The -\emph on -src/common/kernel/ -\emph default - and -\emph on -src/common/kernlib/ -\emph default - sections containing only portable C code are compiled, and their modules - archived with -\emph on -ar -\emph default - and -\emph on -ranlib -\emph default -, to -\emph on -src/common/kernel/ar/*.a -\emph default - and -\emph on -src/common/kernlib/ar/*.a -\emph default - files. - At current time, -\emph on -src/common/library/ -\emph default -, -\emph on -src/common/device/ -\emph default - and -\emph on -src/common/handler/ -\emph default - sections are all compiled and archived together as -\emph on -src/common/ar/*.a -\emph default -. - -\emph on -XXX This last statement is false as nothing is done for libraries, devices - and handlers at current time.. -\layout Subsubsection - -Linking the final kernel -\layout Standard - - -\emph on -src/common/kernel/kernel -\emph default -. -\emph on -a -\emph default -, -\emph on -src/port/support. -\emph default -a, -\emph on -src/processor/support.a -\emph default -, -\emph on -src/common/kernlib/kernlib.a -\emph default - and -\emph on -src/common/shared.a -\emph default - are linked together, in that order, into the ELF relocatable -\emph on -src/xisop.o -\emph default - file (using -\emph on --r -\emph default - option to ld). - This allows processor-specific libraries to replace -\emph on -common/kernlib -\emph default - functions, and port-specific ones to override both processor-specific and - common ones. -\layout Subsubsection - -Linking the final kernel and building the bootable Xisop result -\layout Standard - -After building both the machine dependent and independent sections described - above, the control is then left to -\emph on -src/port/boot/make.sh -\emph default -, after changing to the -\emph on -src/port/boot directory -\emph default -. - The role of this final script is to complete the linking and building process. - Here is what currently happens: -\layout Standard - -As a general rule, image_script.ld file in that directory comports the necessary - information to statically link monolithically the whole kernel as a binary - image. - Here is an excerpt of what -\emph on -src/ports/amiga/boot/make.sh -\emph default - does: -\layout Itemize - -show $C_COMPC -I$SRCDIR init.c -\layout Itemize - -A=`$L_LS ../../../common/kernel/ar/*.a ../ar/*.a ../../../processor/ar/*.a ../.. - /../common/kernlib/ar/*.a` -\layout Itemize - -show $C_LD -nostdlib -e _start -Ttext 0x005f8000 -o image.o init.o $A -\layout Itemize - -show $C_LD -T image_script.ld -nostdlib -o image.bin init.o $A -\layout Itemize - -... -\layout Standard - -The first operation compiles it's initialization code, which provides the - -\emph on -_start -\emph default - entrypoint, which eventually calls Xisop -\emph on -main() -\emph default -. - Then is compiled a list of the various -\emph on -ar -\emph default - archives which should be linked. - The order of these archives is important, as it permits the processor-specific - code to override the common code, and the port-specific code to override - the processor specific and common code. -\layout Standard - -Then follows the linking process, which is done two time. - The first instance creates -\emph on -image.o -\emph default - which can then be viewed and disassembled using the -\emph on -objdump -\emph default - utility. - This is mostly used for debugging so that at runtime in the emulator it - is possible to stop the emulation process and fall into the debugger, which - then discloses the current executing address. - That same address in image.o disassembly should match, and this is where - it can be handy. -\layout Standard - -The second linking command creates the binary Xisop kernel image using the - -\emph on -image_script.ld -\emph default - linker script, to result in -\emph on -image.bin -\emph default -. - This is the actual image, which expects to be loaded into memory at the - address 0x005f8000, and jumped to. - (See the -\emph on -image_script.ld -\emph default - and -\emph on -make.sh -\emph default - scripts for more information). -\layout Standard - -The script then proceeds to compile the disk boot loader (floppy in this - case), which is located into the -\emph on -bootf/ -\emph default - directory. - It then compiles the tools which are needed to assemble the result and - fix the bootblock checksum using the the local compiler (not the cross - compiler, although the same compiler could be used for both local and cross, - if the target and the build system are the same). - It finally uses those tools to create the final xisop.adf floppy image, - and advertizes the location of this file to the user. -\layout Standard - -Those last steps are very port-specific and are best done by someone with - a good amount of experience for the particular port to ensure that it works - right. - It is important to advertize the location of the final result to the user - at the very end. -\layout Section -\pagebreak_top -Hardware specific development notes -\layout Standard - -This chapter describes which hardware specific sections are required to - support Xisop. - They in fact provide the low-level glue which all the machine-independent - common code replies on. - As such, they should be small, effective, and as efficient and stable as - possible. - They should be well tested before releasing an official new Xisop port. -\layout Standard - -To aid in having a well organized source tree, and to prevent code duplication, - hence enhancing stability with time, the processor-specific and port-specific - sections have been separated into two. - Several ports may then take advantage of the same processor-specific code, - such as atomic locking primitives, which are known to work well, which - helps alot to speed up development. -\layout Subsection - -Microprocessor specific notes -\layout Subsubsection - -Required backend functions and support -\layout Standard - -Each processor is different but it is great to abstract most CPU-specific - functions into a standard set. - The headerfile which will be invoked by the common machine-independent - parts of the kernel to acquire support for the hardware specific functions - is -\emph on -processor/support.h -\emph default -, where -\emph on -processor -\emph default - will consist of a symbolic link to the actual -\emph on -processor -\emph default - directory in use in the -\emph on -processors -\emph default - directory. - Although the general organization of the processor specific code is implementat -ion dependent, it is important that -\emph on -support.h -\emph default - loads support for all necessary functions and data types which are processor-sp -ecific, and that -\emph on -ar/*.a -\emph default - be the only necessary modules to link with the rest of the kernel. -\layout Standard - -It is allowed if desired to also supply processor specific functions to - replace some of the common machine-independent ones, which may be desired - for speed at occasions. - When this is done, the functions should behave identically as expected, - should bear the same names, and no prototypes should be provided for them - in -\emph on -support.h -\emph default -. - The processor-specific code will be linked to the final kernel before the - common libraries and those functions will replace the machine-independent - ones then. - These functions could be for instance: -\emph on -memcmp() -\emph default -, -\emph on - memcpy() -\emph default -, -\emph on -memset() -\emph default -, etc. -\layout Standard - -Here are the various recommended functions which each CPU should support, - and make public to the rest of the kernel, at a minimum: -\layout Paragraph - -Context manipulation -\layout Standard - -The -\emph on -_ctx_t -\emph default - structure should be defined by the CPU-specific support headerfile and - should be used as an abstract type representing all required information - to save or restore a context, thus all registers, status register (SR), - stack pointer (SP), and program counter (PC). - The load and save context code sections are generally port-specific are - called from an interrupt, and as needed some care will be taken to save - the user stack pointer (USP) rather than the supervisor stack pointer (SSP), - as their purpose consist of task switching. -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -void\SpecialChar ~ -_ctx_init(_ctx_t\SpecialChar ~ -*context,\SpecialChar ~ -u_int32_t\SpecialChar ~ -*stack,\SpecialChar ~ -size_t\SpecialChar ~ -stacksize,\SpecialChar ~ -void\SpecialChar ~ -*start) -\emph default - Allows to create a new CPU context. - On some systems the stack grows downwards while upwards on others. - For this reason, the -\emph on -stacksize -\emph default - argument is used which permits to set the stack pointer as required, because - -\emph on -stack -\emph default - should be a pointer to the top of the stack. - -\emph on -start -\emph default - is a function pointer to the code to execute (stack startup address). - Usually, SP and PC are set accordingly in -\emph on -context -\emph default -, SR set to the current one, and other registers zeroed. - In some cases it may be good to also ensure to turn off the supervisor - bit from SR in the context, because user tasks are expected to run in userstate - processor mode. -\layout Paragraph - -Simple lock support -\layout Standard - - -\emph on -_lock_t -\emph default - should also be defined for abstraction, and help to perform various synchroniza -tion tasks. - These need not be symetric multiprocessor (SMP) safe, but they should at - least be atomic for the current processor. - Atomic in the sense that test-and-set must be performed at once to acquire - a lock. - If a processor does not allow to make this atomic, it is possible to provide - these by the port-specific code, in which case it could disable interrupts - (at least the scheduler interrupt) before performing it's tasks, so that - operations seem atomic in a multitasking environment. - These lock primitives should not be nestled or recursive, they are intended - for exclusive access. -\layout Standard - -In any case, all following lock primitives should be callable by normal - user tasks, which means that when required a trap can be used internally - to swich to supervisor mode if the processor requires to perform privileged - instructions to achieve the expected atomic behavior, both for -\emph on -_lock_t -\emph default - and -\emph on -_rlock_t -\emph default - primitives. - Fortunately, they can usually be implemented properly using normal instructions. -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -void\SpecialChar ~ -_lock_init(_lock_t\SpecialChar ~ -*lock) -\emph default - Permits to initialize a -\emph on -_lock_t -\emph default - entity as required for future operations on this -\emph on -lock -\emph default -. -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -void\SpecialChar ~ -_lock_acquire(_lock_t\SpecialChar ~ -*lock) -\emph default - Allows to obtain exclusive access on the supplied -\emph on -lock -\emph default -, or wait looping indefinitely until the lock could be obtained, in order - to obtain it as fast as possible. - It is important that this operation be atomic so that in the event of scheduled - context switching race conditions do not occur. -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -void\SpecialChar ~ -_lock_release(_lock_t\SpecialChar ~ -*lock) -\emph default - Should free the specified -\emph on -lock -\emph default -, which will enable any other requester to acquire the lock to obtain it. - This operation also should be atomic. -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -bool\SpecialChar ~ -_lock_try(_lock_t\SpecialChar ~ -*lock) -\emph default - Attempts to obtain exclusive access to -\emph on -lock -\emph default -, returning TRUE/1 if it could obtain it immediately, or FALSE/0 if the - lock is already being held, in which case it also returns immediately. - It is important that this be implemented atomically, in a single test-and-set - instruction. -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -bool\SpecialChar ~ -_lock_available(_lock_t\SpecialChar ~ -*lock) -\emph default - Verifies if -\emph on -lock -\emph default - is available. - This is not to be used to implement -\emph on -_lock_try() -\emph default -, it is used in situations where we only want to make sure that no lockers - currently own the lock, but that we still do not want to obtain it ourselves. - An example of this is where a lock is used as an ON/OFF switch, which can - be implemented using this mechanism, without disabling the event which - needs to access a resource, which may serve other functions but will skip - executing the critical code if the scheduler lock is held. - This is usually best implemented using recursive -\emph on -_rlock_t -\emph default - however, which will be described below. -\layout Standard - -The following locking primitives are different in that a lock may be locked - by any number of lockers, but has to be unlocked the same number of times - for the lock to become free again. - These are called recursive locks. - A useful example consists of the scheduler which wants to ensure that no - task disabled the scheduler temporarily before performing a context switch. - It then evaluates the lock using -\emph on -_rlock_available() -\emph default -, or -\emph on -_rlock_try() -\emph default - if it needs to prevent recursion, and only performs the switch if it is - (and hence the lock counter equals to 0, or 1). - It is recommended that the -\emph on -_rlock_t -\emph default - type consist of an -\emph on -int32_t -\emph default - (signed), but the processor-specific code is left to define it differently - if need be. -\layout Standard - -Although for several architectures C code can also be used to implement - these, it is a good idea to implement them in assembly because there is - no guarantee that the compiler will always use atomic increase and decrease - operations (GCC 2.95.3 at least seems to not always do so for m68k with C - macros, it often loads the value from the lock counter, increase it during - other processing and posts back the new value over the counter, instead - of always generating an atomic increment instruction which m68k is well - capable of). - Moreover, for some other architectures the use of an internal -\emph on -_lock_t -\emph default - or -\emph on -_splhigh() -\emph default - may be required to implement these properly, and if there are privileged - instructions required to implement these, a trap may be needed. -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -void\SpecialChar ~ -_rlock_init(_rlock_t\SpecialChar ~ -*rlock) -\emph default - Initializes -\emph on -rlock -\emph default - to 0. - This normally is rarely done except at system initialization, or lock creation. -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -void\SpecialChar ~ -_rlock_acquire(_rlock_t\SpecialChar ~ -*rlock) -\emph default - Atomically increases the -\emph on -rlock -\emph default - counter by one. -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -void\SpecialChar ~ -_rlock_release(_rlock_t\SpecialChar ~ -*rlock) -\emph default - Atomically decreases the -\emph on -rlock -\emph default - counter by one. - There is no need to perform any check against 0 in this function. -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -void\SpecialChar ~ -_rlock_try(_rlock_t\SpecialChar ~ -*rlock) -\emph default - Permits to atomically increase the -\emph on -rlock -\emph default - counter by one, and returns TRUE if the caller consists of the only locker - (in which case the lock counter should now be 1). - If the counter is higher, it means that more than one locker exists and - the function is then expected to decrease the counter atomically again, - and return FALSE. - This allows exclusive access to a recursive lock. - This function is both used by the scheduler and public interrupt facility - systems. - Because they want to make sure that noone holds the lock when they execute - their critical tasks, and that they also need to lock it to prevent potential - self-recursion, this call is a great facility to use. -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -void\SpecialChar ~ -_rlock_available(_rlock_t\SpecialChar ~ -*rlock) -\emph default - Returns TRUE if the -\emph on -rlock -\emph default - counter equals to 0, or FALSE otherwise (in which case there at least remains - one current locker). - This function is provided for callers which do not need to prevent possible - recursion, but only need to make sure that the lock is currently free. - Following -\emph on -_rlock_available() -\emph default - with -\emph on -_rlock_acquire() -\emph default - is not safe, and -\emph on -_rlock_try() -\emph default - should be used instead when this is desired. -\layout Paragraph - -Byte order conversion support -\layout Standard - -The processor-specific code needs to #define -\emph on -_ARCH_BIG_ENDIAN -\emph default - or -\emph on -_ARCH_LITTLE_ENDIAN -\emph default - in their -\emph on -support.h -\emph default - depending on their native byte order. - These will be used at a higher level in the libraries for the endian-conversion - functions between network (big endian) and host order (big or little endian). - These are most useful when saving a binary file format which needs to be - loadable by another processor of another endian order as well as in the - implementation of networking based Remote Procedure Calls, etc. - Moreover, the two following functions should be provided: -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -u_int16_t\SpecialChar ~ -_bswap16(u_int16_t) -\emph default - Swaps the order of the two bytes held in the supplied 16-bit word and returns - the result. - For instance, 0x1234 becomes 0x3412. -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -u_int32_t\SpecialChar ~ -_bswap32(u_int32_t) -\emph default - Reverses the order of the four bytes held in the supplied 32-bit word and - returns the result. - For instance, 0x12345678 becomes 0x78563412. -\layout Paragraph - -String library optimizations support -\layout Standard - -Note that the following are not necessary to consider if a specially optimized - library functions for string and memory are implemented in assembly for - the architecture. -\layout Standard - -Every other architecture should -\emph on -#define -\emph default - the architecture-specific -\emph on -__ARCH_INT_BITS -\emph default - macro, which should be set to the native word size used by the particular - processor for int, using the compiler. - This should be expressed in bits, not in bytes. - The most common value is 32, but it can vary. -\layout Standard - -It is also important to -\emph on -#define -\emph default - the -\emph on -_ARCH_LOWCACHE -\emph default - macro (with no value), if it is beleived that loop unrolling is of no benefit. - This can be the case on architectures with very low instruction caches, - or ones which are using none. - If loop unrolling is wanted, this should not be defined. -\layout Standard - -The -\emph on -_ARCH_USEINDEXING -\emph default - macro also should be -\emph on -#defined -\emph default - with no value if it is beleived that the compiler generates better code - for this particular processor when using indexed instructions rather than - many post-increment/pre-decrement pointer based instructions. - For instance, the i386 processor has no such special support, and using - indexing can generate better code using GCC2. - For m68k, it is usually better not to use indexing. - Note that this is only taken in consideration if -\emph on -_ARCH_LOWCACHE -\emph default - is not defined, as it only affects loop unrolling of the C string and memory - library. -\layout Standard - -These definitions are expected to be found in the -\emph on -support.h -\emph default - file for every particular processor. -\layout Paragraph - -CPU-saving -\layout Standard - -On CPUs which support this, it is very useful to not hug the processor constantl -y in a loop where the only event that is awaited for consists of an interrupt. - On microprocessors which do not provide such a feature, it is safe to just - make this function do nothing. -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -void\SpecialChar ~ -_idle(void) -\emph default - Suspends execution of instructions by the current processor until the next - interrupt, trap or exception occurs. -\layout Paragraph - -Interrupt level control -\layout Standard - -Most microprocessors support several interrupt levels, where the higher - the level the better precedence of execution over others. - Manipulating the interrupt priority level (IPL) using Set Priority Level - functions becomes useful for critical code sections which need to disable - all interrupts at the specified level and under. - Although port-specific code attempts to provide it's own finer grained - interrupt control code when considered required, these should be available. - The -\emph on -_ipl_t -\emph default - type itself is left to be defined with -\emph on -typedef -\emph default - by the processor-specific code to the best variable type to hold the processor - IPL state. -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -_ipl_t\SpecialChar ~ -_spl -\emph default -n -\emph on -(void) -\emph default - Immediately sets the current priority level to the one specified by -\emph on -n -\emph default -. - Thus, functions such as -\emph on -_spl0() -\emph default -, -\emph on -_spl1() -\emph default -, -\emph on -_spl2() -\emph default -, etc should be provided, for each interrupt level. - -\emph on -_spl0() -\emph default - should enable all interrupt levels to occur. - The returned value serves to eventually restore the previous interrupt - level using -\emph on -_splx() -\emph default -, and is an abstract type defined by the processor-specific code. -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -_ipl_t\SpecialChar ~ -_splhigh(void) -\emph default - Usually a macro to the highest -\emph on -_spl -\emph default -n -\emph on -() -\emph default - function, it disables all interrupts by setting the highest priority level, - thus masking all interrupts. -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -void\SpecialChar ~ -_splx(_ipl_t\SpecialChar ~ -state) -\emph default - Permits to restore the previous interrupt priority level which was active - before a call to an -\emph on -_spl -\emph default -* -\emph on -() -\emph default - function was made, using the state value which was supplied by them. -\layout Subsubsection - -m68k -\layout Standard - -The Motorola MC68000L8 and MC68000L10 processors were the first to run Xisop. - No support for MMU is present, such memory management units usually come - on other external circuits for this processor, and there are various types. - As Xisop does not need MMU, those processors were an ideal target. - Although internally built as 16-bit processors, these processors behave - as 32-bit ones from the programming point of view. - It is also expected to run the same (or with minor modifications to disable - their MMU) on the other processors of the 680x0 family, which are fully - 32-bit. - The assembly code observes the following rules to properly work with GCC - compiled C modules: -\layout Itemize - -Stack pointer consists of A7/SP and frame pointer of A6/FP. - In supervisor mode, the user stack pointer consists of USP and specialized - instructions need to be used to access and manipulate it. -\layout Itemize - -Registers A0 and D0 serve the special purpose of return code for functions. - Where the C code expects A0 or D0 to be used depends on the function prototype - as seen from C. - Functions expected to return a pointer should do so in A0, and D0 is used - for other integer values. - Assembly functions expected to be called from C must have a C function - prototype defined in a headerfile which the C code must include. -\layout Itemize - -Assembly functions save all the registers they modify, and restore them - before returning. - As such, functions returning nothing (void) are expected to never return - with a different register state. - However, this is not always true of GCC generated code. - Of course, the D0 register in the case of integer returning functions, - and A0 for pointer returning ones should as expected modify the corresponding - register as well. - Registers to be saved are pushed on the stack, which grows upwards. -\layout Itemize - -Because there are various registers which GCC generated code modifies without - always saving, exception handlers save all general purpose registers A0-A6 - and D0-D7 registers, and restore them before returning with RTE. - It was a false assumption to previously only backup A0 and D0 which are - used as function return codes, and many problems occurred back then with - this attempt. -\layout Itemize - -When an assembly function calls a C function, it needs to push the arguments - in the stack, growing upwards, in reverse order, before calling it. - After the C function returned, the stack pointer should be updated by additioni -ng the number of bytes that were pushed. - In the case where 16-bit or 8-bit values are passed as arguments, GCC still - expects a stack entry of 32-bit size. -\layout Itemize - -An assembly function expected to be called from C must obtain the arguments - (if any) from the stack. - These are ordered growing downwards, as they were inserted in reverse order - in the stack, growing upwards. - Obviously, when registers are temporarily saved on the stack to preserve - their state and restore them before returning, the offset at which these - parameters are found on the stack changes, and the code has to account - for this. -\layout Itemize - -The -\emph on -_ctx_t -\emph default - manipulation functions had to be implemented as follows: -\emph on -_ctx_init() -\emph default - can be called from usermode and only creates a context with zero registers, - etc. - However, -\emph on -_yield() -\emph default - had to be implemented using a trap to execute in supervisor mode, and the - scheduler preemptive interrupt also executes in supervisor mode. - Both save the current context to -\emph on -root->curctx -\emph default -, call -\emph on -schedule() -\emph default - and load back the context from -\emph on - root->curctx -\emph default -, as expected. - They need to use privileged instructions to change SR (status register) - and USP (user stack pointer), because SR/A7 becomes the SSP (supervisor - stack pointer) when in supervisor mode. - To make sure to respect the PC (program counter) address of the contexts, - they are manipulated on the supervisor stack (SSP), where the m68k saves - them when jumping to the exception handler. - As such, RTE (return from exception) automatically jumps where it should - for the current context. - The offset to the SR 16-bit register is usually %sp@ and the one for PC - 32-bit one in %sp@(2) when initially intering the trap or interrupt exception. - This offset has to be recalculated as registers are being saved on the - stack, of course. -\layout Standard - -Other m68k specific notes about aspects which had to be taken in consideration: -\layout Itemize - -At kernel initialization, room for the supervisor stack pointer needs to - be setup, and the Supervisor Stack Pointer (SSP/A7) should be set properly. - To do this it is necessary to go in supervisor mode, and then set the A7 - register to the right address. - The way this must be done depends on the architecture. - Because at bootup ROM code may have taken control already and one must - use it's own facilities to obtain supervisor privileges. -\layout Itemize - -Dropping to user state from supervisor state to call Xisop -\emph on -main() -\emph default - is rather simple. - 1024 bytes are taken from the current SSP (A7), and assigned to the USP. - The supervisor bit in SR is then unset, and a jump to -\emph on -main() -\emph default - is made. - The function is very tiny and only ensures to launch the various initial - tasks, then waits forever in a loop using -\emph on -_idle() -\emph default - calls via -\emph on -sys_idle() -\emph default -. - It corresponds to the -\emph on -_scontext -\emph default - -\emph on -_ctx_t -\emph default - in -\emph on -root->curctx -\emph default - when no tasks are on the ready queue. - Such a small stack buffer is then safe. -\layout Itemize - -Using GCC 2.95.3, -O2 and -fomit-frame-pointer compilation directives seem - to generate both well optimized and small m68k code. - I however noted that using -fno-function-cse was also required with -O2, - without which the resulting code crashed. - -m68000 was used to generate true MC68000 code (no support for 020+ specific - instructions which wouldn't run on a plain 68000). -\layout Itemize - -Although m68k is very good to produce position-independent code, the default - output GCC produces still comports instructions using direct addressing, - except when -fpic is used, in which case additional symbols, with a .got - table need to also be in the code, even if m68k generally doesn't require - these for position-independent code (it has all the relative addressing - instructions required for large memory model). - As the only solution I found to properly relocate the code upon loading - would be to write an ELF or a.out loader, which isn't done yet, the kernel - code is loaded at a specific location, defined before compilation. - A GCC ld BFD backend will need to be written, or an ELF loader, to allow - to relocate the kernel, as well as file executable binaries. -\layout Itemize - -The -\emph on -_spl -\emph default -n -\emph on -() -\emph default - and -\emph on -_splhigh() -\emph default - functions were implemented as macros, calling the assembly -\emph on -_spl() -\emph default - function which takes a 16-bit argument (the new SR to apply). - -\emph on -_splx() -\emph default - assembly functions restores the previous SR. - As SR is 16-bit, the -\emph on -_ipl_t -\emph default - type was defined as an -\emph on -u_int16_t -\emph default - internally. - This allowed to generate very compact code for the eight interrupt priority - level control functions which m68k required. -\layout Itemize - -The -\emph on -_lock_ -\emph default -* -\emph on -() -\emph default - functions were implemented using the TAS instruction for atomicity, and - the -\emph on -_lock_t -\emph default - data type was internally defined as an -\emph on -u_int8_t -\emph default -. -\layout Itemize - -The -\emph on -_rlock -\emph default -* -\emph on -() -\emph default - functions could be implemented without the use of an internal -\emph on -_lock_t -\emph default - to guarrantee atomicity, because the m68k processor is capable of addition - and substraction on a 32-bit value in a single instruction. - An -\emph on -_rlock_t -\emph default - consists of a -\emph on -int32_t -\emph default - for this architecture. -\layout Itemize - -Before the port-specific code calls Xisop -\emph on -main() -\emph default -, it is necessary to switch back to user processor mode. - The -\emph on -_usermode() -\emph default - function was implemented for this and added to the m68k set of processor-specif -ic functions, which allows to create a 1024 bytes user stack from the current - supervisor stack, switches to usermode, and jumps to the specified function. -\layout Subsection - -Port specific notes -\layout Subsubsection - -Required backend and support functions -\layout Standard - -Each architecture needs a specific initialization section, such as setting - up exceptions, interrupts and memory. - Although this can also be CPU-specific, the various architectures using - the same processor would most likely still need these to be different. - They are thus considered as port-specific low-level backend support. - Similarly to the processor-specific support code, -\emph on -support.h -\emph default - and -\emph on -ar/*.a -\emph default - are the main targets that should be provided to allow the rest of the kernel - to use it, as it will include -\emph on -port/support.h -\emph default - and will link in -\emph on -port/ar/ -\emph default -*. -\emph on -a -\emph default -, where -\emph on -port -\emph default - consists of a symbolic link to the actual -\emph on -ports/ -\emph default - directory. -\layout Standard - -It is allowed if desired to also supply port specific functions to replace - some of the common machine-independent ones, which may be desired for speed - at occasions. - When this is done, the functions should behave identically as expected, - should bear the same names, and no prototypes should be provided for them - in -\emph on -support.h -\emph default -. - The port-specific -\emph on -ar/*.a -\emph default -modules will be linked before the processor-specific and Xisop common libraries - and those functions will replace them if they are supplied. - These functions could be for instance: -\emph on -memcmp() -\emph default -, -\emph on - memcpy() -\emph default -, -\emph on -memset() -\emph default -, etc. -\layout Standard - -After the kernel was built, using both common, processor and port code, - control is left again to the port specific code which should handle boot - support; This can be creating a floppy image, etc. - See the section on the building process for more information. -\layout Standard - -It is important that the port-specific code provides the -\emph on -_start -\emph default - entry point. - This code should then set the processor in supervisor mode where required, - setup the supervisor mode stack pointer, disable interrupts and perform - initialization of the various systems described below. - Afterwards, it should switch back to userstate processor mode, and leave - control to the Xisop -\emph on -main() -\emph default - function, which will not return. - That -\emph on -_start -\emph default - entry point is where the boot kernel loader needs to jump to. -\layout Paragraph - -Memory initialization and requirements -\layout Standard - -The port-specific -\emph on -support.h -\emph default - should define C enumerators (enum) and definitions (#define) for the machine-in -dependent Xisop memory code. - It is recommended to also read the section on Xisop memory management for - more information. - The port-specific requirements are explained as follows: -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -#define\SpecialChar ~ -_PAGE_SIZE\SpecialChar ~ - -\emph default - The memory management system needs to know the amount of bytes in which - to split pages. - On operating systems which support Memory Management Units (MMU), this - is required to match the page size which the hardware requires. - In the case of Xisop, it is safe to use any reasonable multiple of 16 bytes - here. - A common -\emph on -_PAGE_SIZE -\emph default - is 4096 bytes. - On systems with very low memory it may be useful to use 1024, or even 256. - This value is used by the page memory allocation primitives. -\layout Standard - -The -\emph on -mpool_t -\emph default -, a multi-purpose memory pool which allows management primitives such as - -\emph on -_malloc() -\emph default - and -\emph on -_free() -\emph default -, requires specific definitions: -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -#define\SpecialChar ~ -_MPOOLS\SpecialChar ~ - -\emph default - This is the number of internal -\emph on -pool_t -\emph default - which are necessary to initialize for an -\emph on -mpool_t -\emph default -, to be able to use these efficient pools when dealing with byte requirements - which are too small to be rounded at page boundaries. -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -#define\SpecialChar ~ -_MPOOLSTART\SpecialChar ~ - -\emph default - The smallest amount of bytes which an -\emph on -mpool_t -\emph default - can allocate, which is multiplied by two for each consecutive -\emph on -pool_t -\emph default - initialized for the -\emph on -mpool_t -\emph default - at -\emph on -mpool_init() -\emph default -. -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -#define\SpecialChar ~ -_MPOOLSTEP\SpecialChar ~ - -\emph default - The number of physical -\emph on -_PAGE_SIZE -\emph default - bytes pages into a -\emph on -page_t -\emph default - for every -\emph on -pool_t -\emph default - of an -\emph on -mpool_t -\emph default -. -\layout Standard - -To explain better the previous definitions, what the -\emph on -mpool_init() -\emph default - function actually does is iterate -\emph on -_MPOOLS -\emph default - times to initialize the -\emph on -pool_t -\emph default - objects, setting the first -\emph on -pool_t -\emph default - to allocate units of -\emph on -_MPOOLSTART -\emph default - bytes, the second -\emph on -_MPOOLSTART -\emph default - * 2, and continueing to iterate multiplying the unit size by two until - -\emph on -_MPOOLS -\emph default - number of -\emph on -pool_t -\emph default - were initialized. - Larger unit sizes which cannot be handled by the -\emph on -pool_t -\emph default - will be rounded at page boundaries. - -\emph on -_MPOOLSTEP -\emph default - simply consists of the -\emph on -stepp -\emph default - argument to -\emph on -pool_init() -\emph default -. - As such, all the definitions above intimately correlate to eachother, and - are quite versatile to match various requirements an architecture may need. -\layout Standard - -For a system with a -\emph on -_PAGE_SIZE -\emph default - of 4096 bytes, an -\emph on -_MPOOLSTART -\emph default - of 16 and -\emph on -_MPOOLSTEP -\emph default - of 1, 7 consists of an ideal value for -\emph on -_MPOOLS -\emph default -. - On a system with a fair amount of memory, if it is wanted to minimize calls - to the page management primitives even more, it is possible to set a larger - -\emph on -_MPOOLSTEP -\emph default - and raise -\emph on -_MPOOLS -\emph default - accordingly, while keeping the same underlaying -\emph on -_PAGE_SIZE -\emph default -. - Basically -\emph on -_MPOOLS -\emph default - should be set just below the -\emph on -pool_init() -\emph default - breaking point, where it returns FALSE because -\emph on -sizeof(mnode_t) -\emph default - plus the current -\emph on -_MPOOLSTART -\emph default - multiple consist of too large objects to fit into a -\emph on -pool_t -\emph default - page (which in turn depends on -\emph on -_MPOOLSTEP -\emph default - which changes the -\emph on -page_t -\emph default - size for a -\emph on -pool_t -\emph default -). -\layout Standard - -It is recommended to read the source for -\emph on -mpool_init() -\emph default - which is located in -\emph on -src/common/kernel/memory.c -\emph default - for a better understanding, as well as the documentation on Xisop memory - management primitives. -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -enum\SpecialChar ~ -_memtypes -\emph default - This enumeration should define the various memory types the architecture - provides, in preference order when -\emph on -_MEM_ANY -\emph default - is used (-1) when calling the allocation primitives. - The enumeration should set -\emph on -_MEM_ -\emph default -* names, the first one corresponding to 0, and the last element should consist - of -\emph on -_MEM_MAX -\emph default -, corresponding to the number of memory types the system provides. - Not all architectures provide more than a single memory type, under which - case -\emph on -_MEM_ALL -\emph default - will correspond to 0 and -\emph on -_MEM_MAX -\emph default - to 1, respectively. -\layout Standard - -Other than defining these requirements in it's -\emph on -support.h -\emph default - headerfile, the port-specific initialization code is responsible for attaching - the available physical memory pages to the system pools, before initializing - the public interrupt facilities. - This is done by first calling the -\emph on -memory_init() -\emph default - machine-independent function. - Then, the -\emph on - mchunk_init() -\emph default - and -\emph on -mchunk_attach() -\emph default - functions which are documented in the Xisop memory management section are - normally called once for each contiguous memory area which is to be used - as general purpose memory. - The video memory, or other special memory sections should not be included - in the system memory pools. - It is important to perform this step before continuing on with the next - initialization sections. - The -\emph on -mchunk_init() -\emph default - and -\emph on -mchunk_attach() -\emph default - functions are described in detail in the Xisop memory management section. -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -void\SpecialChar ~ -memory_init(void -\emph default -) This function is only called once by the port-specific initialization - code before starting to attach memory chunks to the system pools. - After this function executes, it becomes possible to use the -\emph on -mchunk_init() -\emph default - and -\emph on -mchunk_attach() -\emph default - functions to attach contiguous memory regions to the system page pools. -\layout Paragraph - -Exceptions initialization and public interrupt facilities -\layout Standard - -It is recommended to maintain an -\emph on -_interrupt_depth -\emph default - variable or recursive lock, which each trap or interrupt handler should - increase at startup and decrease before returning, which can be used for - the scheduler interrupt to determine if it should call -\emph on -schedule() -\emph default - or not. - As the scheduler timer interrupt would also raise it at startup, it can - then mask interrupts and evaluate if it equals 1 afterwards, in which case - it is sure to have the right to perform a context switch. - This permits to make system calls uninterruptible for the time of their - execution and to protect the scheduler from performing context switches - while an interrupt handler is executing, which at occasions could result - in recursivity and context corruption. - As an effort is made to minimize the number of system call traps required - during normal Xisop function, the preemptive nature of the scheduler is - not bothered by having uninterruptible system calls, unless a task voluntarily - abuses -\emph on -sys_custom() -\emph default -, which is by all meals legal if one wants to. -\layout Standard - -Obviously, most exception handlers are responsible to return with the registers - unchanged, and as such should normally save all general-purpose registers - on the stack at startup, and return them before returning. - This is especially true if C functions are to be called from the interrupt - handler, where unexpected registers may be tempered with. -\layout Standard - -After setting up the memory, the public interrupt facilities can be defined - to the system and their internal handler vectors setup. - Usually, -\emph on -facilities_init() -\emph default - will be called before vectors are initialized, or if not possible, dummy - do-nothing vectors can be installed, and can then be setup definitely after - calling f -\emph on -acilities_init() -\emph default -, when it becomes safe. -\layout Standard - -This function call depends on the -\emph on -enum _facilities -\emph default - C enumerator which should be defined in the port specific -\emph on -support.h -\emph default - headerfile. - This enumerator defines each facility in the form of -\emph on -_FACILITY_ -\emph default -*, where * consists of the name of the facility. - The first entry should evaluate to 0, and the last one to the total number - of facilities ( -\emph on -_FACILITY_MAX -\emph default -). - The various interrupt handlers need to internally call -\emph on -facility_exechooks() -\emph default - on the facility they are serving for the public facilities to become alive. - At it's discretion, the handler may temporarily disable the interrupt source - when calling the function, but -\emph on -facility_exechooks() -\emph default - internally performs recursion prevention and makes sure to not execute - the hooks if a hook is currently being inserted or removed, using an -\emph on -_rlock_t -\emph default - for each facility internally. -\layout Standard - -There only is at least one facility which is required for all ports to provide. - This facility should be named -\emph on -_FACILITY_SCHEDTIMER -\emph default -, and should call the hooks at -\emph on -_SCHEDTIMER_HZ -\emph default - frequency, which should also be defined by the port-specific -\emph on -support.h -\emph default -. - This way, simple time-based Xisop applications can work portably on all - ports. - This facility should correspond to the timer interrupt which the port chose - to use for the preemptive scheduler timer. - This facility does not interfere with the scheduler activities; it is called - when the interrupt occurs even if the scheduler -\emph on -rlock_t -\emph default - is set ( -\emph on -schedule() -\emph default - handles the scheduler locked/disabled case already). - Generally, the scheduler interrupt handler works as follows: -\layout Itemize - -Increase the global -\emph on -_interrupt_depth -\emph default - variable like for all handlers -\layout Itemize - -Temporarily disable the interrupt source (by raising the IPL using -\emph on -_spl -\emph default -* -\emph on -() -\emph default - or otherwise) to prevent any possible recursion or other interruption. -\layout Itemize - -Save the current user CPU context to the -\emph on -root->curctx _ctx_t -\layout Itemize - -Execute the facility hooks using -\emph on -facility_exechooks(_FACILITY_SCHEDTIMER) -\layout Itemize - -Verify if the -\emph on -_interrupt_depth -\emph default - variable equals to 1. - If so, call -\emph on -schedule(NULL) -\emph default -, which may or may not change the -\emph on -root->curctx -\emph default - backed up context pointer and -\emph on -root->curtask -\layout Itemize - -Load back the CPU context from the new -\emph on -root->curctx -\emph default - (which possibly can be the same, but this must not be assumed) -\layout Itemize - -Re-enable the scheduler interrupt source -\layout Itemize - -Decrease the global -\emph on -_interrupt_depth -\emph default - variable like for other handlers -\layout Itemize - -Return from interrupt handler while ensuring to jump to the PC of the new - context. - Generally, the address to return to is backed up into the supervisor stack, - which needs to be modified for this. - That address within the supervisor stack pointer is where context save - and load operations obtain and set the Program Counter address. -\layout Standard - -Because of the context load/save operations, and return address hack, the - scheduler interrupt handler is usually implemented entirely in assembly - (although it calls the -\emph on -schedule() -\emph default - and -\emph on -facility_exechooks() -\emph default - C functions). -\layout Standard - -The other facilities, which are optional and can be provided by the port-specifi -c code will often be implemented as a mix of assembly and C code and will - similarily at least: -\layout Itemize - -increase global -\emph on -_interrupt_depth -\emph default - and optionally disable interrupt source -\layout Itemize - -save registers -\layout Itemize - -perform any additional wanted operation -\layout Itemize - -call -\emph on -facility_exechooks() -\emph default - on their facility -\layout Itemize - -restore registers -\layout Itemize - -re-enable the interrupt source if it was temporarily disabled, and decrease - global -\emph on -_interrupt_depth -\layout Itemize - -return -\layout Standard - -The facility public interface and -\emph on -facility_exechooks() -\emph default - are described in more details in the Xisop public facilities section. -\layout Paragraph - -System trap triggers and handlers initialization -\layout Standard - -It is important for the port-specific code to define the -\emph on -_syscall() -\emph default - and -\emph on -_yield() -\emph default - functions. - The role of the system call trap handler is to serve system call functions - uninterruptibly, internally calling -\emph on -_scatch() -\emph default - Xisop common function with the requested arguments. - Here is described the trigger, which function should be supplied by the - port-dependent code: -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -void\SpecialChar ~ -_syscall(u_int32_t\SpecialChar ~ -function,\SpecialChar ~ -void\SpecialChar ~ -*res,\SpecialChar ~ -void\SpecialChar ~ -*args) -\emph default - Internally places the supplied parameters into a static buffer or in registers, - and generate a processor trap interrupt. - It is safe to save the registers we modify on the stack, and restore them - from the stack after the trap returns, and then return ourselves, because - nor -\emph on -_yield() -\emph default - nor context switching are implemented via syscalls. - Although the understanding of the arguments is not necessary at this point, - -\emph on -function -\emph default - specifies the syscall number which is to be performed, -\emph on -res -\emph default - a pointer to eventual results expected from that system call (or NULL), - and -\emph on -args -\emph default - optional arguments which need to be passed to the system call (or NULL). - Although this function is also highly processor-specific, the choice of - the trap vector to implement system calls is left to the port writer, and - as such this function as well. -\layout Standard - -The other end, consisting of the system call trap handler, is responsible - for the following: -\layout Itemize - -Increment -\emph on -_interrupt_depth -\emph default - global variable -\layout Itemize - -Save all general purpose registers -\layout Itemize - -Read arguments supplied by -\emph on -_syscall() -\emph default - from the static buffer or registers, and insert them on the stack as C - arguments, then call -\emph on -_scatch() -\emph default - C function. - Fix the stack pointer to forget the pushed stack arguments. -\layout Itemize - -Restore general purpose registers we saved -\layout Itemize - -Decrement -\emph on -_interrupt_depth -\emph default - global variable -\layout Itemize - -Return -\layout Standard - -The -\emph on -_scatch() -\emph default - function (which is defined in -\emph on -src/common/kernel/syscall.c -\emph default -) is responsible for performing the necessary sanity checking on the arguments, - and does not need to be provided by the machine-specific code: -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -void\SpecialChar ~ -_scatch(u_int32_t\SpecialChar ~ -function,\SpecialChar ~ -void\SpecialChar ~ -*results,\SpecialChar ~ -void\SpecialChar ~ -*arguments) -\emph default - Consists of the heart of the syscall trap. - -\emph on -function -\emph default - specifies the requested syscall function number which was called. - These are standard and are described in the -\begin_inset Quotes eld -\end_inset - -System Calls -\begin_inset Quotes erd -\end_inset - - section. - -\emph on -results -\emph default - consists of a pointer to the block of memory which will be modified to - store the syscall results by this -\emph on -function -\emph default -. - It can be NULL. - -\emph on -arguments -\emph default - similarly specifies the location of the arguments expected for this -\emph on -function -\emph default -, or NULL. - This function refuses to perform any call if the supplied -\emph on -function -\emph default - is invalid (out of bounds). -\layout Standard - -After setting up the -\emph on -_syscall() -\emph default - trap vector, the interrupts can remain disabled/masked still, like since - the beginning. - More information on the generic user system calls interface is provided - in the Xisop system calls section. -\layout Standard - -Another requirement that the port-specific code must satisfy consists of - the -\emph on -_yield() -\emph default - internal function: -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -_yield(task_t\SpecialChar ~ -*task) -\emph default - permits the current task to immediately perform a context switch to another - task, in fact preempting itself. - -\emph on -task -\emph default - is an optional preference of which task to switch to, or NULL, which parameter - should be passed when the trap handler internally calls -\emph on -schedule() -\emph default -. - This is usually implemented in the form of a trap like for -\emph on -_syscall() -\emph default -, which sets the supplied argument in a static buffer to prevent modifying - a register (it is unsafe to save registers on the stack as we most likely - won't be the next task to return from the trap). - The backend trap handler acts as follows: -\layout Itemize - -Increase the global -\emph on -_interrupt_depth -\emph default - variable like for all handlers -\layout Itemize - -Temporarily disable all interrupt sources (by raising the IPL using -\emph on -_splhigh() -\emph default - or equivalent to prevent any possible interruption, but without modifying - registers, which can be saved and restored safely before performing the - next steps -\layout Itemize - -Save the current user CPU context to the -\emph on -root->curctx _ctx_t -\layout Itemize - -Insert the supplied argument from the static buffer in the stack as a C - argument -\layout Itemize - -Call -\emph on -schedule() -\emph default -, which may or may not change the -\emph on -root->curctx -\emph default - backed up context pointer and -\emph on -root->curtask -\layout Itemize - -Adjust stack pointer to forget the passed argument -\layout Itemize - -Load back the CPU context from the new -\emph on -root->curctx -\emph default - (which possibly can be the same, but this must not be assumed) -\layout Itemize - -Re-enable interrupts calling -\emph on -_spl0() -\emph default - or performing equivalent taking care not to modify registers. - Using the stack is now safe. - (remember that the old level obtained from -\emph on -_splhigh() -\emph default - cannot be obtained back unless saved to a static buffer, in which case - it can be restored properly. - Saving it on the stack is also safe if the context switching function only - modified the user stack pointer and the supervisor stack pointer consists - of the active stack during the trap). -\layout Itemize - -Decrease the global -\emph on -_interrupt_depth -\emph default - variable like for other handlers -\layout Itemize - -Return from interrupt handler while ensuring to jump to the PC of the new - context. - Generally, the address to return to is backed up into the supervisor stack, - which needs to be modified for this. - That address within the supervisor stack pointer is where context save - and load operations obtain and set the Program Counter address. -\layout Paragraph - -Suggestions -\layout Standard - -The rest of the port-specific code internals which it needs to perform are - left to the implementor, as long as they suit well the purpose. - However, a few suggestions are made which can help to keep some consistency - among the various ports, in their choice of function names for instance. - This example attempts to restrict the assembly code to the minimum, while - calling C functions as much as possible to handle most exception code. -\layout Standard - -It is generally a good idea for hardware interrupts to provide one separate - assembly handler per interrupt level, to prevent the C code from having - to perform unnecessary additional conditional instructions to evaluate - the level, as it already usually needs to detect the source. - For other general-purpose trap vectors, it is allowed to provide support - to execute a single C function for all of them, passing in an argument - the required information on the trap vector number. - This however would be less desireable than having a different facility - for each, if their frequency was high and a large number of hooks were - attached, because they then would obviously all run often, evaluating one - by one if they are interested in the trap. - Suggestion names for various common facility types are shown in the Xisop - public interrupt facilities section. -\layout Standard - -It is to be noted that the timer interrupt chosen to be used as the preemptive - scheduler one is special as it needs to perform context switching, often - also implying stack pointer access and modifications. - This handler is therefore usually fully written in assembly, as previously - demonstrated. - As a general rule, the role of the exception vectors is to call a C function - which can then handle the event. -\layout Standard - -Here are various C functions which can be called by the machine language - backend to exceptions, traps and interrupts: -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -void\SpecialChar ~ -_icatch -\emph default -n -\emph on -(void) -\emph default - For every hardware interrupt level, une such C function can be called. - For interrupt level 3, -\emph on -icatch3() -\emph default - would be called, for instance. - It is possible to pass parameters if required to detect the interrupt source - in the C functions. -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -void\SpecialChar ~ -_tcatch(int\SpecialChar ~ -vector) -\emph default - This C function can be called for all software traps which occur that do - not correspond to the syscall or yield trap vectors. - -\emph on -vector -\emph default - argument specifies the number of the trap vector. -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -void\SpecialChar ~ -_ecatch(int\SpecialChar ~ -vector) -\emph default - A C function called when an hardware exception is generated, such as bus - error, division by zero, etc. - It is recommended that the code processing these do not hang or crash the - system permanently if high-reliability is required. - -\emph on -vector -\emph default - consists of the exception vector, or reason which caused it, and -\emph on -stack -\emph default - points as usual to the retrurn from exception address on the stack. - Although this is CPU-specific, port-specific exception handling code may - be provided. -\layout Paragraph - -Port-specific system shared libraries to attach and system tasks to launch -\layout Standard - -Although Xisop has a few common portable system libraries and tasks which - it initializes at startup, it is ideal for the port-specific section to - be able to describe which other tasks should be launched, and their parameters - such as priority level, stack size, etc. - To do this the port-specific code needs to provide the -\emph on -_port_init() -\emph default - function which the Xisop init task will invoke and which then can use the - required flexibility. - It should be noted that as the init task only has a 4096 bytes stack, this - function is expected to at least create a task with a larger stack if it - needs to before performing it's own initialization if it overuses the stack. - The kernel expects the current state to remain the same when the function - returns (apart of course from the new libraries and tasks which may now - exist and be resident). -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -void\SpecialChar ~ -_port_init(void) -\emph default - Port-specific function who's purpose is to attach the wanted port-specific - libraries, and to launch the wanted port-specific tasks. - This function is called by the Xisop -\emph on -init -\emph default - task which runs with a stack of 4096 bytes, and runs in userstate mode - like normal tasks. -\layout Paragraph - -Last steps of the port-specific initialization code -\layout Standard - -After switching to supervisor mode, setting up memory, system call and yield - traps, as well as scheduler timer interrupt, and initializing the public - interrupt facilities, port-specific initialization code is then complete, - and it now should execute the following steps: -\layout Itemize - -Call the machine-independent -\emph on -xisop_init() -\emph default - function, which sets up various internal structures and disables the scheduler, - and then internally calls -\emph on - _spl0() -\emph default - as interrupts finally become safe to enable. -\layout Itemize - -Call the famous Xisop machine-independent -\emph on -main() -\emph default - function which is expected to never return. - If the processor currently runs into supervisor mode, it is necessary to - drop to usermode before calling -\emph on -main() -\emph default -. - The role of this function is to enable the scheduler, launch the Xisop - -\emph on -init -\emph default - task, which in turn will make sure to launch the -\emph on -task reaper -\emph default - task, and call the port-dependent -\emph on -_port_init() -\emph default - function which then can also attach and launch the wanted resources. -\newline -Note that the -\emph on -main() -\emph default - function with it's current stack becomes the initial context that the scheduler - always switches to when there remains no tasks in the ready queue to run. - As such, after launching the init task, it loops forever calling -\emph on -sys_idle() -\emph default - system call which internall calls processor-specific -\emph on -_idle() -\emph default -, which permits the processor to stop spinning until the next interrupt - or trap event occurs. -\layout Subsubsection - -Amiga -\layout Itemize - -The Amiga port uses the -\emph on -processors/m68k -\emph default - processor-specific code. -\layout Itemize - -In addition to the m68k -\emph on -_spl -\emph default -* -\emph on -() -\emph default - functions, the amiga low level library also supplies -\emph on -aspl -\emph default -* -\emph on -() -\emph default - functions using INTENA control register for finer grained control to disable - certain interrupts for a period of time. - For instance, -\emph on -asplvblank() -\emph default - disables the vertical blank interrupt, -\emph on -asplsched() -\emph default - disables the scheduler, etc. - -\emph on -asplx() -\emph default - is used to restore the previous state, as usual. - For -\emph on -asplsched() -\emph default -, a -\emph on -_lock_t -\emph default - is used to turn the scheduler ON/OFF, so that the timer interrupt it ties - to still can execute other code if required. - -\emph on -XXX -\layout Itemize - -The chosen -\emph on -_syscall() -\emph default - trap vector was 0. -\layout Itemize - -The chosen -\emph on -_yield() -\emph default - trap vector was 1. -\layout Itemize - -The Amiga has four multi-purpose timers in it's two CIA chips. - The use Xisop currently makes of them is as follows: CIA-A TimerA is reserved - for keyboard timing, which is a hardware requirement. - CIA-B Timer A is used by the Xisop scheduler, which generates high-level - hardware interrupts of high priority (IPL 6). - The B timers and TOD counters are unused and remain available for devices - and user code for each CIA. -\layout Itemize - -The two memory page pools ( -\emph on -ppool_t -\emph default -) initialized at startup by -\emph on -_init_memory() -\emph default - consist of one for CHIP RAM, and another one for FAST RAM. - The -\emph on -enum _memtypes -\emph default - as such set -\emph on -_MEM_FAST -\emph default - to 0 and -\emph on -_MEM_CHIP -\emph default - to 1, FAST memory being the prefered if -\emph on -_MEM_ANY -\emph default - is used. - Currently, the addresses mapped in the standard distribution are 0x - 0x - for CHIP (enhanced 2 megabytes agnus chip (fatter)), and 0x00200000-0x00600000 - for FAST (usual 4 first megabytes of ZorroII memory found on A2000). - This currently needs to be modified in the code itself as the booting process - currently does not detect the available RAM amounts. - The provided UAE Amiga Emulator configuration file which is configured - as such can be located in the -\emph on -src/ports/amiga/boot -\emph default - directory. - -\emph on -XXX -\layout Itemize - -To setup the initial supervisor stack, the AmigaOS SuperState() exec.library - call must be used to gain supervisor privileges. - The SSP/A7 register can then be set to the proper location. - To do this a special function is provided in assembly by the Amiga support - library, -\emph on -void\SpecialChar ~ -_supervisor(u_int32_t\SpecialChar ~ -*sp,\SpecialChar ~ -size_t\SpecialChar ~ -ssize\SpecialChar ~ -void (*func)(void)) -\emph default - which allows to set the new entry point function and stack. - To the provided -\emph on -stack -\emph default - pointer will be additionned the supplied stack size -\emph on -ssize -\emph default - automatically because of the stack which grows upwards. - The supplied function -\emph on -func -\emph default - is then given control to. - This function is expected to never return. -\the_end diff --git a/Xisop/src/clean.sh b/Xisop/src/clean.sh deleted file mode 100755 index 2576fa9..0000000 --- a/Xisop/src/clean.sh +++ /dev/null @@ -1,16 +0,0 @@ -#!/bin/sh - -# $Id: clean.sh,v 1.1 2003/10/26 21:19:57 mmondor Exp $ - -. ./generic_makedefs.sh - -show cd processors/m68k -./clean.sh -show cd ../../ports/amiga -./clean.sh -show cd boot -./clean.sh -show cd ../../../common -./clean.sh -show cd .. -show $L_RM processor port makedefs.sh diff --git a/Xisop/src/common/clean.sh b/Xisop/src/common/clean.sh deleted file mode 100755 index 76a9ead..0000000 --- a/Xisop/src/common/clean.sh +++ /dev/null @@ -1,11 +0,0 @@ -#!/bin/sh - -# $Id: clean.sh,v 1.1 2003/10/26 21:19:57 mmondor Exp $ - -. ../generic_makedefs.sh - -show cd kernlib -./clean.sh -show cd ../kernel -./clean.sh -show cd .. diff --git a/Xisop/src/common/kernel/clean.sh b/Xisop/src/common/kernel/clean.sh deleted file mode 100755 index d386a45..0000000 --- a/Xisop/src/common/kernel/clean.sh +++ /dev/null @@ -1,8 +0,0 @@ -#!/bin/sh - -# $Id: clean.sh,v 1.1 2003/10/26 21:19:57 mmondor Exp $ - -. ../../generic_makedefs.sh - -cleanlib . -show $L_RM ar/*.a diff --git a/Xisop/src/common/kernel/debug.c b/Xisop/src/common/kernel/debug.c deleted file mode 100644 index 976e387..0000000 --- a/Xisop/src/common/kernel/debug.c +++ /dev/null @@ -1,408 +0,0 @@ -/* $Id: debug.c,v 1.3 2004/06/04 02:15:47 mmondor Exp $ */ - -/* - * Copyright (C) 2003, Matthew Mondor - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Matthew Mondor. - * 4. The name of Matthew Mondor may not be used to endorse or promote - * products derived from this software without specific prior written - * permission. - * 5. Redistribution of source code may not be released under the terms of - * any GNU Public License derivate. - * - * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - - - -#include -#include -#include -#include -#include -#include - - - -#ifdef DEBUG - - - -static fifo8_t fifo; -static u_int8_t *fifobuf; - - - -static size_t btoa(char *, u_int32_t); -static size_t dtoa(char *, int32_t); -static size_t utoa(char *, u_int32_t); -static size_t xtoa(char *, u_int32_t); -static void fifowrite(const char *, size_t); - - - -static const char ddigits[] = "0123456789"; -static const char xdigits[] = "0123456789ABCDEF"; - - - -void debug_init(void) -{ - fifobuf = MALLOC(DEBUG); - FIFO_INIT(&fifo, fifobuf, DEBUG); - root->debugfifo = &fifo; - _lock_init(&root->debuglock); - /* Not ideal but considerably reduces kernel size compared to a static - * array buffer. Has the disadventage that debugging is only available - * after memory initialization. - */ - debug_printf("Xisop debugging enabled\n"); -} - - -void debug_printf(const char *fmt, ...) -{ - debug_va_list ap; - register char c; - register const char *ptr = fmt, *optr; - - if (ptr == NULL) - return; - - lock_acquire(&root->debuglock); - - debug_va_start(ap, fmt); - optr = ptr; - while ((c = *ptr++) != '\0') { - if (c == '%') { - ptr--; - /* Output pending chars */ - if (optr < ptr) - fifowrite(optr, ptr - optr); - ptr++; - /* "%\0" ! */ - if ((c = *ptr++) == '\0') { - ptr--; - break; - } - - /* Process debug_va_arg */ - switch (c) { - case '%': - ptr--; - break; - case 'B': /* Boolean */ - { - register bool t = debug_va_arg(ap, bool); - - if (t) - fifowrite("TRUE", 4); - else - fifowrite("FALSE", 5); - } - break; - case 'b': /* Binary 32-bit */ - { - char buf[34]; - - btoa(buf, debug_va_arg(ap, u_int32_t)); - fifowrite(buf, 33); - } - break; - case 'c': /* Character */ - { - char ch = debug_va_arg(ap, char); - - fifowrite(&ch, 1); - } - break; - case 'd': /* 32-bit decimal */ - { - char buf[12]; - size_t len; - - len = dtoa(buf, debug_va_arg(ap, int32_t)); - fifowrite(buf, len); - } - break; - case 'p': /* 32-bit pointer */ - { - char buf[11]; - register void *p = debug_va_arg(ap, void *); - - if (p != NULL) { - xtoa(buf, (u_int32_t)p); - fifowrite(buf, 10); - } else - fifowrite("NULL", 4); - } - break; - case 's': /* String */ - { - register const char *s = debug_va_arg(ap, char *); - - if (s != NULL) - fifowrite(s, strlen(s)); - else - fifowrite("", 6); - } - break; - case 'T': /* Current task and PC address, no va_arg() */ - { - register task_t *t = CURTASK(); - register void *pc = root->curctx->pc; - char str[24]; - register char *ptr = str; - - *ptr++ = '['; - xtoa(ptr, (u_int32_t)t); - ptr += 10; - *ptr++ = '.'; - xtoa(ptr, (u_int32_t)pc); - ptr += 10; - *ptr++ = ']'; - *ptr = '\0'; - fifowrite(str, 23); - } - case 'u': /* 32-bit unsigned decimal */ - { - char buf[11]; - size_t len; - - len = utoa(buf, debug_va_arg(ap, u_int32_t)); - fifowrite(buf, len); - } - break; - case 'x': /* 32-bit hexadecimal */ - { - char buf[11]; - - xtoa(buf, debug_va_arg(ap, u_int32_t)); - fifowrite(buf, 10); - } - break; - default: /* Display %, it's a bug, and we debug! */ - { - char s[2]; - - s[0] = '%'; - s[1] = c; - fifowrite(s, 2); - } - break; - } - /* Adjust optr for our pending chars record */ - optr = ptr; - } - } - debug_va_end(ap); - - /* Any pending chars remaining? */ - if (optr < ptr) - fifowrite(optr, ptr - optr); - - _lock_release(&root->debuglock); -} - - -/* XXX Need to work on stdarg-like system and vsnprintf() instead of everything - * in debug_printf(). -void dprintf2(const char *file, const char *function, u_int32_t line, - const char *fmt, ...) -{ - debug_printf("%s:%s():%d - %s", file, func, line); -} -*/ - - - -/* This function writes into the debug FIFO buffer in a way to cause automatic - * recycling so that the last DEBUG bytes will always be available in the - * history until read. We do not use the lock here, because we only want - * full lines to be recorded, we let debug_printf() do it. - */ -static void fifowrite(const char *buf, size_t len) -{ - register fifo8_t *f = &fifo; - - while (len > 0) { - FIFO_PUT(f, buf); - buf++; - len--; - } - - /* XXX This would be more efficient if it worked :) I need to debug this. - if (len == 1) - FIFO_PUT(f, buf); - else { - u_int8_t *ptr; - size_t size; - register size_t l = len; - - if ((size = FIFO_AVAIL(f)) < l) { - l -= size; - - while (size ) XXX; - size = l; - FIFO_FREE(f, &ptr, &size, l); - } - - while (l > 0) { - FIFO_ALLOC(f, &ptr, &size, l); - memcpy(ptr, buf, size); - buf += size; - l -= size; - } - } - */ -} - - -/* Allows to read data from the FIFO buffer. The bytes are unlinked from the - * buffer dynamically when they are read. - */ -size_t dread(char *buf, size_t len) -{ - register fifo8_t *f = &fifo; - register size_t l = len; - - if (buf == NULL || len == 0) - return 0; - - lock_acquire(&root->debuglock); - - while (l > 0) { - if (FIFO_EMPTY(f)) - break; - FIFO_GET(f, buf); - buf++; - l--; - } - len = len - l; - - /* XXX Debug this more efficient alternative - if (len == 1) { - if (!FIFO_EMPTY(f)) - FIFO_GET(f, buf); - else - len = 0; - } else { - u_int8_t *ptr; - int size; - register int l = len, i; - - len = 0; - for (i = 0; i < 2 && l > 0; i++) { - FIFO_FREE(f, &ptr, &size, l); - if (size == 0) - break; - memcpy(buf, ptr, size); - buf += size; - len += size; - l -= size; - } - } - */ - - _lock_release(&root->debuglock); - - return len; -} - - -/* Buffer should at least be 34 bytes. Performs 32-bit value to ASCII binary - * convertion. Returns length of string. - */ -static size_t btoa(char *buf, u_int32_t val) -{ - register int i; - register char *ptr = buf; - - *ptr++ = '%'; - for (i = 31; i > -1; i--) - *ptr++ = (val & (1L << i)) ? '1' : '0'; - *ptr = '\0'; - - return (ptr - buf); -} - - -/* Buffer should be at least 12 bytes. Converts signed 32-bit value to decimal - * ASCII representation. Returns length of string. - */ -static size_t dtoa(char *buf, int32_t val) -{ - register int32_t v = val; - register char *ptr = buf; - register const char *d = ddigits; - - if (v < 0) { - *ptr++ = '-'; - v = -v; - } - for (; v > 9; v /= 10) - *ptr++ = d[v % 10]; - *ptr++ = d[v]; - *ptr = '\0'; - - return (ptr - buf); -} - - -/* Buffer should be at least 11 bytes. Unsigned 32-bit value to decimal ASCII - * convertion. Returns length of string. - */ -static size_t utoa(char *buf, u_int32_t val) -{ - register u_int32_t v = val; - register char *ptr = buf; - register const char *d = ddigits; - - for (; v > 9; v /= 10) - *ptr++ = d[v % 10]; - *ptr++ = d[v]; - *ptr = '\0'; - - return (ptr - buf); -} - - -/* Buffer should be at least 11 bytes. Converts 32-bit value to hexadecimal - * ASCII representation. Returns length of string. - */ -static size_t xtoa(char *buf, u_int32_t val) -{ - register u_int32_t v = val; - register char *ptr = buf; - register const char *d = xdigits; - register int i; - - *ptr++ = '0'; - *ptr++ = 'x'; - for (i = 32 - 4; i > -1; i -= 4) - *ptr++ = d[v >> i & 15]; - *ptr = '\0'; - - return (ptr - buf); -} -#endif diff --git a/Xisop/src/common/kernel/debug.h b/Xisop/src/common/kernel/debug.h deleted file mode 100644 index 5f3f113..0000000 --- a/Xisop/src/common/kernel/debug.h +++ /dev/null @@ -1,104 +0,0 @@ -/* $Id: debug.h,v 1.3 2004/06/04 02:15:47 mmondor Exp $ */ - -/* - * Copyright (C) 2003, Matthew Mondor - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Matthew Mondor. - * 4. The name of Matthew Mondor may not be used to endorse or promote - * products derived from this software without specific prior written - * permission. - * 5. Redistribution of source code may not be released under the terms of - * any GNU Public License derivate. - * - * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - - - -#ifndef KERNEL_DEBUG_H -#define KERNEL_DEBUG_H - - - -#include -#include - - - -/* This implements various debugging primitives. Macros are defined - * so that no debugging code be compiled in the kernel if DEBUG - * is not defined. - */ - - - -#ifndef DEBUG - -/* Debugging disabled, ensure that unnecessary code doesn't get compiled in. - * if (DEBUG_TRUE(condition)) will always cause the condition to be TRUE, while - * if (DEBUG_FALSE(condition)) will always cause the condition to be FALSE. - * DEBUG_PRINTF() will do nothing. - */ - -#define DEBUG_PRINTF(s, ...) -#define DEBUG_READ(b, s) 0 -#define DEBUG_TRUE(c) /* CONSTCOND */1 -#define DEBUG_FALSE(c) /* CONSTCOND */0 - -#else - -/* Debugging enabled, macros must now do something */ - -#define DEBUG_PRINTF debug_printf -#define DEBUG_READ(b, s) debug_read(b, s) -#define DEBUG_TRUE(c) (c) -#define DEBUG_FALSE(c) (c) - - - -/* These should eventually be available even if no debugging is wanted. - * However, these are really simple and are mostly made to serve 32-bit - * values (although characters also work). - */ - -typedef u_int32_t * debug_va_list; - -#define debug_va_start(a, l) (a) = (u_int32_t *)(&(l) + sizeof(*(l))) -#define debug_va_arg(a, t) (t)(*a++) -#define debug_va_copy(d, s) (d) = (s) -#define debug_va_end(a) - - - -void debug_init(void); -void debug_printf(const char *, ...); -/*void dprintf2(const char *, const char *, int, const char *, ...);*/ -size_t debug_read(char *, size_t); - - - -#endif - - - -#endif diff --git a/Xisop/src/common/kernel/device.c b/Xisop/src/common/kernel/device.c deleted file mode 100644 index c8105e8..0000000 --- a/Xisop/src/common/kernel/device.c +++ /dev/null @@ -1,525 +0,0 @@ -/* $Id: device.c,v 1.8 2004/06/04 02:15:47 mmondor Exp $ */ - -/* - * Copyright (C) 2003, Matthew Mondor - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Matthew Mondor. - * 4. The name of Matthew Mondor may not be used to endorse or promote - * products derived from this software without specific prior written - * permission. - * 5. Redistribution of source code may not be released under the terms of - * any GNU Public License derivate. - * - * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - - - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - - - -/* Allows current task to open a devicenode_t, obtaining a device_t handle */ -device_t *device_open(const char *name, u_int32_t ver, u_int32_t unit) -{ - device_t *dh = NULL; - - if (name != NULL) { - register devicenode_t *dn; - - SYSTABLE_RLOCK(SYSTABLE_DEVICES); - dn = (devicenode_t *)hashtable_lookup(SYSTABLE(SYSTABLE_DEVICES), - name, strnlen(name, 32)); - SYSTABLE_UNLOCK(SYSTABLE_DEVICES); - if (dn != NULL && (dn->version == ver || ver == 0)) { - if ((dh = (device_t *)spool_alloc(POOL_DEVICEHANDLE)) != NULL) { - if (dn->open(&dh->udata, unit)) { - /* Validate object and set dependancy. We also register - * it since iorequest_t depend on device_t. - */ - OBJECT_VALIDATE(dh, OBJECT_DEVICEHANDLE); - OBJECT_REGISTER(dh); - OBJECT_SETDEP(dh, dn); - dh->devnode = dn; - dn->usecount++; - /* Initialize other device_t fields */ - dh->devport = dn->port; - dh->owner = CURTASK(); - dh->unit = unit; - /* Attach to task resources for automatic freeing */ - SCHED_DISABLE(); - DLIST_APPEND(&CURTASK()->resources.devices, &dh->tasknode); - SCHED_ENABLE(); - } else { - dh = (device_t *)spool_free(POOL_DEVICEHANDLE, - (pnode_t *)dh); - DEBUG_PRINTF("- %T device_open(%s, %u, %u) - Refused\n", - name, ver, unit); - } - } else - DEBUG_PRINTF("* %T device_open(%s, %u, %u) - Out of memory\n", - name, ver, unit); - } else - DEBUG_PRINTF("- %T device_open(%s, %u, %u) - Unknown device\n", - name, ver, unit); - } else - DEBUG_PRINTF("* %T device_open(%s, %u, %u) - Illegal parameters\n", - name, ver, unit); - - return dh; -} - - -/* Closes and frees a device_t from the task it belongs to */ -device_t *device_close(device_t *dh) -{ - if (OBJECT_VALID(dh, OBJECT_DEVICEHANDLE)) { - if (OBJECT_DEPENDS(dh, dh->devnode)) { - register devicenode_t *dn = dh->devnode; - - SYSTABLE_RLOCK(SYSTABLE_DEVICES); - /* devicenode_t for this device_t still exists */ - dn->close(dh->udata, dh->unit); - if ((--(dn->usecount)) == 0 && (dn->flags & DNF_RESIDENT) == 0) { - if (OBJECT_VALID(dn->task, OBJECT_TASK)) - signal_send(dn->task, SIGMASK(SIGTERM)); - } - SYSTABLE_UNLOCK(SYSTABLE_DEVICES); - } else - DEBUG_PRINTF("* %T device_close(%p) - Device has died\n", dh); - /* Unlink task resource and free handle */ - SCHED_DISABLE(); - DLIST_UNLINK(&(dh->owner->resources.devices), &dh->tasknode); - SCHED_ENABLE(); - OBJECT_INVALIDATE(dh); - spool_free(POOL_DEVICEHANDLE, (pnode_t *)dh); - } else - DEBUG_PRINTF("* %T device_close(%p) - Invalid device_t pointer\n", - dh); - - return NULL; -} - - -/* Initializes an iorequest_t for use with specified device_t, necessary - * before using it to send device requests. - */ -bool iorequest_init(iorequest_t *req, device_t *dh, port_t *rport) -{ - bool ok = FALSE; - - if (OBJECT_VALID(dh, OBJECT_DEVICEHANDLE) && - OBJECT_DEPENDS(dh, dh->devnode) && - OBJECT_VALID(rport, OBJECT_PORT)) { - req->udata = NULL; - if (dh->devnode->iorinit == NULL || - (req->udata = dh->devnode->iorinit()) != NULL) { - /* Validate object and register dependancy on device_t */ - OBJECT_VALIDATE(req, OBJECT_IOREQUEST); - OBJECT_SETDEP(req, dh); - req->devhandle = dh; - /* Initialize other iorequest_t fields */ - req->devport = dh->devport; - req->rport = rport; - req->flags = 0; - req->success = FALSE; - req->result = 0; - req->actual = 0; - ok = TRUE; - } else - DEBUG_PRINTF("* %T iorequest_init(%p, %p, %p) - Out of memory\n", - req, dh, rport); - } else - DEBUG_PRINTF( - "* %T iorequest_init(%p, %p, %p) - Invalid device_t pointer\n", - req, dh, rport); - - return ok; -} - - -bool iorequest_destroy(iorequest_t *req) -{ - bool ok = FALSE; - - if (OBJECT_VALID(req, OBJECT_IOREQUEST)) { - if (OBJECT_DEPENDS(req, req->devhandle) && - OBJECT_DEPENDS(req->devhandle, req->devhandle->devnode)) { - register devicenode_t *dn = req->devhandle->devnode; - - if (dn->iordestroy != NULL && req->udata != NULL) - dn->iordestroy(req->udata); - } - OBJECT_INVALIDATE(req); - } else - DEBUG_PRINTF( - "* %T iorequest_destroy(%p) - Invalid iorequest_t poinder\n", - req); - - return ok; -} - - -/* Sends the iorequest_t message to it's corresponding device, and waits for - * results to be obtained, then returns. This consists of a synchroneous - * device request. - */ -bool iorequest_sync(iorequest_t *req) -{ - bool ok = FALSE; - - if (OBJECT_VALID(req, OBJECT_IOREQUEST)) { - if ((req->flags & IOF_PENDING) == 0) { - if (port_send(req->devport, req->rport, (message_t *)req)) { - register sigmask_t sigmport = PORT_SIGMASK(req->devport); - - req->flags = IOF_SYNC | IOF_PENDING; - while (((signal_wait(sigmport, NULL)) & sigmport) == 0) ; - port_get(req->devport); - /* Request satisfied */ - req->flags &= ~(IOF_SYNC & IOF_PENDING); - ok = TRUE; - } else - DEBUG_PRINTF("* %T iorequest_sync(%p) - port_send()\n", req); - } else - DEBUG_PRINTF( - "* %T iorequest_sync(%p) - iorequest_t already pending\n", - req); - } else - DEBUG_PRINTF( - "* %T iorequest_sync(%p) - Invalid iorequest_t pointer\n", - req); - - return ok; -} - - -/* Sends the iorequest_t message to it's corresponding device, but returns - * immediately, without waiting for results. This consists of an asynchroneous - * request. The application is responsible to monitor the reply port status - * for the request completion, and to unqueue the reply from the reply port - * before performing another request using this iorequest_t. - */ -bool iorequest_async(iorequest_t *req) -{ - bool ok = FALSE; - - if (OBJECT_VALID(req, OBJECT_IOREQUEST)) { - if ((req->flags & IOF_PENDING) == 0) { - if (port_send(req->devport, req->rport, (message_t *)req)) { - req->flags = IOF_ASYNC | IOF_PENDING; - ok = TRUE; - } else - DEBUG_PRINTF("* %T iorequest_async(%p) - port_send()\n", - req); - } else - DEBUG_PRINTF( - "* %T iorequest_async(%p) - iorequest_t already pending\n", - req); - } else - DEBUG_PRINTF( - "* %T iorequest_async(%p) - Invalid iorequest_t pointer\n", - req); - - return ok; -} - - -/* Aborts a pending asynchroneous request which has not yet completed. - * This in fact sends back a new request using the same iorequest_t, but - * does not expect a reply back from the device for the abort request. However, - * the reply port will be sent the reply as usual when the aborted iorequest_t - * ends (which always happens even when a request is aborted). - */ -bool iorequest_abort(iorequest_t *req) -{ - bool ok = FALSE; - - if (OBJECT_VALID(req, OBJECT_IOREQUEST)) { - if ((req->flags & IOF_PENDING) != 0 && (req->flags & IOF_ASYNC) != 0) { - req->function = IO_ABORT; - if (port_send(req->devport, req->rport, (message_t *)req)) { - req->flags |= IOF_ABORTING; - ok = TRUE; - } else - DEBUG_PRINTF("* %T iorequest_abort(%p) - port_send()\n", - req); - } else - DEBUG_PRINTF( - "* %T iorequest_abort(%p) - iorequest_t not pending\n", - req); - } else - DEBUG_PRINTF( - "* %T iorequest_abort(%p) - Invalid iorequest_t pointer\n", - req); - - if (!ok) - DEBUG_PRINTF("* %T iorequest_abort(%p)\n", req); - - return ok; -} - - -/* Waits until the currently pending asynchroneous request completes. The reply - * message is automatically unqueued from the reply port in this case. - */ -bool iorequest_wait(iorequest_t *req) -{ - bool ok = FALSE; - - if (OBJECT_VALID(req, OBJECT_IOREQUEST)) { - if ((req->flags & IOF_PENDING) != 0 && (req->flags & IOF_ASYNC) != 0) { - register sigmask_t sigmport = PORT_SIGMASK(req->devport); - - while (((signal_wait(sigmport, NULL)) & sigmport) == 0) ; - port_get(req->devport); - /* Request satisfied */ - req->flags &= ~(IOF_ASYNC & IOF_PENDING); - ok = TRUE; - } else - DEBUG_PRINTF( - "* %T iorequest_wait(%p) - iorequest_t not pending\n", - req); - } else - DEBUG_PRINTF( - "* %T iorequest_wait(%p) - Invalid iorequest_t pointer\n", - req); - - return ok; -} - - -/* Returns TRUE if the request is asynchroneous and still pending. */ -bool iorequest_pending(iorequest_t *req) -{ - bool ok = FALSE; - - if (OBJECT_VALID(req, OBJECT_IOREQUEST)) { - if ((req->flags & IOF_PENDING) != 0 && (req->flags & IOF_ASYNC) != 0) - ok = TRUE; - } else - DEBUG_PRINTF( - "* %T iorequest_pending(%p) - Invalid iorequest_t pointer\n", - req); - - return ok; -} - - -/* Returns TRUE if the request last terminated by iorequest_abort(). */ -bool iorequest_aborted(iorequest_t *req) -{ - bool ok = FALSE; - - if (OBJECT_VALID(req, OBJECT_IOREQUEST)) { - if ((req->flags & IOF_ABORTED) != 0) - ok = TRUE; - } else - DEBUG_PRINTF( - "* %T iorequest_aborted(%p) - Invalid iorequest_t pointer\n", - req); - - return ok; -} - - -/* Made for devices to satisfy a user iorequest_t, at the same time setting - * the boolean result code for it. - */ -bool iorequest_satisfy(iorequest_t *req, bool result) -{ - bool ok = FALSE; - - if (OBJECT_VALID(req, OBJECT_IOREQUEST)) { - if ((req->flags & IOF_PENDING) != 0) { - req->flags &= ~(IOF_PENDING | IOF_SYNC | IOF_ASYNC); - if ((req->flags & IOF_ABORTING) != 0) { - req->flags &= ~IOF_ABORTING; - req->flags |= IOF_ABORTED; - req->success = result; - } - if (!(ok = port_reply((message_t *)req))) - DEBUG_PRINTF( - "* %T iorequest_satisfy(%p, %B) - port_reply(%p)\n", - req, result, req); - } - } else - DEBUG_PRINTF("* %T iorequest_satisfy(%p, %B) - Invalid iorequest_t\n", - req, result); - - return ok; -} - - -/* Allows a task to attach a new device to the system lists. It then of course - * should serve requests through it's port. The task may only become one - * device, that is, it may not attach more than a single device. - * It however can serve multiple units on that device, of course. - */ -bool device_attach(const char *name, u_int32_t version, port_t *port, - void (*clean)(void), bool (*open)(void **, u_int32_t), - void (*close)(void *, u_int32_t), void *(*iorinit)(void), - void (*iordestroy)(void *), u_int8_t flags) -{ - if (CURTASK()->resources.device == NULL && name != NULL && - OBJECT_VALID(port, OBJECT_PORT) && open != NULL && close != NULL) { - register bstr_t *bstr; - - if ((bstr = bstr_new(name, 32, FALSE)) != NULL) { - register devicenode_t *dn; - - SYSTABLE_RLOCK(SYSTABLE_DEVICES); - if ((dn = (devicenode_t *)hashtable_lookup( - SYSTABLE(SYSTABLE_DEVICES), - bstr->data, bstr->len)) == NULL || - version != dn->version) { - if ((dn = (devicenode_t *)spool_alloc(POOL_DEVICENODE)) - != NULL) { - /* Validate and register for dependancies */ - OBJECT_VALIDATE(dn, OBJECT_DEVICENODE); - OBJECT_REGISTER(dn); - /* Initialize other devicenode_t fields */ - dn->version = version; - dn->name = bstr; - dn->usecount = 0; - dn->flags = flags; - dn->port = port; - dn->task = CURTASK(); - dn->clean = clean; - dn->open = open; - dn->close = close; - dn->iorinit = iorinit; - dn->iordestroy = iordestroy; - /* Attach */ - SYSTABLE_UPGRADE(SYSTABLE_DEVICES); - (void) hashtable_link(SYSTABLE(SYSTABLE_DEVICES), - (hashnode_t *)dn, bstr->data, - bstr->len, FALSE); - SYSTABLE_UNLOCK(SYSTABLE_DEVICES); - - return TRUE; - } else - DEBUG_PRINTF("* %T device_attach() - Out of memory\n"); - } else - DEBUG_PRINTF( - "* %T device_attach() - Device exists already\n"); - - SYSTABLE_UNLOCK(SYSTABLE_DEVICES); - bstr_free(bstr); - } else - DEBUG_PRINTF("* %T device_attach() - Out of memory\n"); - } else - DEBUG_PRINTF("* %T device_attach() - Invalid parameters\n"); - - DEBUG_PRINTF("* %T device_attach(%s, %u, %p, %p, %p, %p, %p, %p, %x)\n", - name, version, port, clean, open, close, iorinit, iordestroy, - (u_int32_t)flags); - - return FALSE; -} - - -bool device_detach(devicenode_t *dn) -{ - bool ok = FALSE; - - if (OBJECT_VALID(dn, OBJECT_DEVICENODE)) { - SYSTABLE_WLOCK(SYSTABLE_DEVICES); - hashtable_unlink(SYSTABLE(SYSTABLE_DEVICES), (hashnode_t *)dn); - SYSTABLE_UNLOCK(SYSTABLE_DEVICES); - if (dn->clean != NULL) - dn->clean(); - OBJECT_INVALIDATE(dn); - if (dn->name != NULL) - bstr_free(dn->name); - spool_free(POOL_DEVICENODE, (pnode_t *)dn); - } else - DEBUG_PRINTF( - "* %T device_detach(%p) - Invalid devicenode_t pointer\n", - dn); - - return ok; -} - - - -/* Utility functions for very simple synchroneous I/O */ - -/* Like read(), but on a Xisop device */ -ssize_t device_read(iorequest_t *req, void *buf, size_t size) -{ - ssize_t len = -1; - - if (OBJECT_VALID(req, OBJECT_IOREQUEST)) { - req->function = IO_READ; - req->len = size; - req->data = buf; - if (iorequest_sync(req)) { - if (req->success) - len = req->actual; - } else - DEBUG_PRINTF( - "* %T device_read(%p, %p, %u) - iorequest_sync(%p)\n", - req, buf, size, req); - } else - DEBUG_PRINTF( - "* %T device_read(%p, %p, %u) - Invalid iorequest_t pointer\n", - req, buf, size); - - return len; -} - - -/* Like write(), but on a Xisop device */ -ssize_t device_write(iorequest_t *req, void *buf, size_t size) -{ - ssize_t len = -1; - - if (OBJECT_VALID(req, OBJECT_IOREQUEST)) { - req->function = IO_WRITE; - req->len = size; - req->data = buf; - if (iorequest_sync(req)) { - if (req->success) - len = req->actual; - } else - DEBUG_PRINTF( - "* %T device_read(%p, %p, %u) - iorequest_sync(%p)\n", - req, buf, size, req); - } else - DEBUG_PRINTF( - "* %T device_write(%p, %p, %u) - Invalid iorequest_t ptr\n", - req, buf, size); - - return len; -} diff --git a/Xisop/src/common/kernel/device.h b/Xisop/src/common/kernel/device.h deleted file mode 100644 index 7276d3a..0000000 --- a/Xisop/src/common/kernel/device.h +++ /dev/null @@ -1,176 +0,0 @@ -/* $Id: device.h,v 1.2 2004/01/18 17:42:59 mmondor Exp $ */ - -/* - * Copyright (C) 2003, Matthew Mondor - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Matthew Mondor. - * 4. The name of Matthew Mondor may not be used to endorse or promote - * products derived from this software without specific prior written - * permission. - * 5. Redistribution of source code may not be released under the terms of - * any GNU Public License derivate. - * - * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - - - -#ifndef KERNEL_DEVICE_H -#define KERNEL_DEVICE_H - - - -#include -#include -#include -#include -#include -#include -#include - - - -/* devicenode/device_t flags */ -#define DNF_RESIDENT (1 << 0) - -/* iorequest_t flags */ -#define IOF_SYNC (1 << 0) -#define IOF_ASYNC (1 << 1) -#define IOF_PENDING (1 << 2) -#define IOF_ABORTING (1 << 3) -#define IOF_ABORTED (1 << 4) - -/* Standard device commands */ -enum _devicecommands { - IO_ABORT = 0, - IO_READ, - IO_WRITE, - IO_CONTROL /* General purpose like unix ioctl() */ -}; - - - -/* For device task functions to access user data they associated with - * device_t and iorequest_t handles (if any, or NULL). - */ -#define DEVICEHANDLE_UDATA(d) (d)->udata -#define IOREQUEST_UDATA(r) (r)->udata - - - -/* This structure holds the only necessary information which Xisop needs to - * know. A device has to internally handle other information but which is of - * no use to Xisop itself. - */ -struct devicenode { - /* System link and information, for devices system list */ - hashnode_t node; - u_int32_t version; - bstr_t *name; - u_int32_t usecount; - u_int8_t flags; - /* Validity sceal. device_t objects depend on us */ - u_int32_t object_magic, object_id; - - /* Port used to send device requests to */ - port_t *port; - /* Task associated with device */ - task_t *task; - - /* Functions provided by device, described in Xisop documentation. */ - void (*clean)(void); - bool (*open)(void **, u_int32_t); - void (*close)(void *, u_int32_t); - void *(*iorinit)(void); - void (*iordestroy)(void *); -}; - -/* Consists of a device handle, returned to tasks when opening a device */ -struct devicehandle { - pnode_t usernode; /* Used by device for optional queuing */ - node_t tasknode; /* Used to remember task resource */ - /* Validity and dependancy sceal, we depend on devicenode_t */ - u_int32_t object_magic, object_id, objdep_magic, objdep_id; - devicenode_t *devnode; - /* Other fields */ - task_t *owner; /* Owner task of this handle */ - port_t *devport; /* Device port to use */ - u_int32_t devnodeid; /* Id of devnode */ - u_int32_t unit; /* Unit opened on device */ - void *udata; /* Device may link custom data here */ -}; - -/* An iorequest_t consists of a message. This also means that the device task - * may queue the message into custom list_t as required after they obtain it, - * as long as they unlink it before they return it of course. - */ -struct iorequest { - message_t msg; /* An iorequest_t is a message */ - /* Validity sceal and dependancy link */ - u_int32_t object_magic, objdep_magic, objdep_id; - device_t *devhandle; - /* Other */ - port_t *devport, *rport; - void *udata; - u_int8_t flags; - - /* The following are the operation request control fields */ - u_int32_t function, subfunction; - size_t len; - size_t offset; /* Useful for block devices */ - void *data; - - /* And operation results fields */ - bool success; - int result; - size_t actual; -}; - - - -/* User API functions */ -device_t *device_open(const char *, u_int32_t, u_int32_t); -device_t *device_close(device_t *); -bool iorequest_init(iorequest_t *, device_t *, port_t *); -bool iorequest_destroy(iorequest_t *); -bool iorequest_sync(iorequest_t *); -bool iorequest_async(iorequest_t *); -bool iorequest_abort(iorequest_t *); -bool iorequest_wait(iorequest_t *); -bool iorequest_pending(iorequest_t *); -bool iorequest_aborted(iorequest_t *); - -/* Device task functions */ -bool iorequest_satisfy(iorequest_t *, bool); -bool device_attach(const char *, u_int32_t, port_t *, void (*)(void), - bool (*)(void **, u_int32_t), void (*)(void *, u_int32_t), - void *(*)(void), void (*)(void *), u_int8_t); -bool device_detach(devicenode_t *); - -/* Useful but very simple synchroneous functions */ -ssize_t device_read(iorequest_t *, void *, size_t); -ssize_t device_write(iorequest_t *, void *, size_t); - - - -#endif diff --git a/Xisop/src/common/kernel/exception.c b/Xisop/src/common/kernel/exception.c deleted file mode 100644 index 5fe0f7f..0000000 --- a/Xisop/src/common/kernel/exception.c +++ /dev/null @@ -1,222 +0,0 @@ -/* $Id: exception.c,v 1.4 2004/06/04 02:15:47 mmondor Exp $ */ - -/* - * Copyright (C) 2003, Matthew Mondor - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Matthew Mondor. - * 4. The name of Matthew Mondor may not be used to endorse or promote - * products derived from this software without specific prior written - * permission. - * 5. Redistribution of source code may not be released under the terms of - * any GNU Public License derivate. - * - * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - - - -#include -#include -#include -#include -#include -#include -#include -#include - - - -/* These are the machine-independant kernel frontend to interrupt hooks - * facilities. They internally use a recursive _rlock_t which allows to - * ensure reliability when adding and removing hooks to a facility, - * while removing the need for system call traps to access the functionality. - */ - -hookid_t hook_attach(u_int32_t facility, u_int32_t skipcount, - u_int32_t runcount, void (*code)(hookid_t, int, void *), void *data) -{ - register hookid_t id = 0; - - if (facility < (enum _facilities)_FACILITY_MAX && code != NULL) { - register facility_t *f; - register hook_t *hook; - - f = (facility_t *)&root->int_facilities[facility]; - _rlock_acquire(&f->rlock); - if ((hook = (hook_t *)pool_alloc(&f->pool, FALSE)) != NULL) { - if (++f->idcnt == 0) - f->idcnt++; - id = hook->id = f->idcnt; - hook->skipcount = skipcount; - hook->runcount = runcount; - hook->code = code; - hook->data = data; - DLIST_APPEND(&f->hooks, (node_t *)hook); - STAT(STAT_HOOKS_ATTACHED, 1); - } else { - STAT(STAT_HOOKS_ATTACHED_NOMEM, 1); - DEBUG_PRINTF("* %T hook_attach() - Out of memory\n"); - } - _rlock_release(&f->rlock); - } else { - STAT(STAT_HOOKS_ATTACHED_FAILED, 1); - DEBUG_PRINTF("* %T hook_attach(%u, %u, %u, %p, %p)\n", - facility, skipcount, runcount, code, data); - } - - return id; -} - - -bool hook_detach(u_int32_t facility, hookid_t id) -{ - register bool ok = FALSE; - - if (facility < (enum _facilities)_FACILITY_MAX && id != 0) { - register facility_t *f; - register hook_t *node, *next; - - f = (facility_t *)&root->int_facilities[facility]; - _rlock_acquire(&f->rlock); - - for (node = DLIST_TOP(&f->hooks); node != NULL; node = next) { - next = DLIST_NEXT(node); - if (node->id == id) { - DLIST_UNLINK(&f->hooks, (node_t *)node); - pool_free((pnode_t *)node); - ok = TRUE; - STAT(STAT_HOOKS_DETACHED, 1); - break; - } - } - if (node == NULL) - STAT(STAT_HOOKS_DETACHED_NOEXIST, 1); - - _rlock_release(&f->rlock); - } else { - STAT(STAT_HOOKS_DETACHED_FAILED, 1); - DEBUG_PRINTF("* %T hook_detach(%u, %u)\n", facility, id); - } - - return ok; -} - - -void facility_disable(u_int32_t facility) -{ - if (facility < (enum _facilities)_FACILITY_MAX) { - _rlock_acquire(&(root->int_facilities[facility].rlock)); - STAT(STAT_FACILITY_DISABLED, 1); - } else { - STAT(STAT_FACILITY_DISABLED_FAILED, 1); - DEBUG_PRINTF("* %T facility_disable(%u)\n", facility); - } -} - - -void facility_enable(u_int32_t facility) -{ - if (facility < (enum _facilities)_FACILITY_MAX) { - _rlock_release(&(root->int_facilities[facility].rlock)); - STAT(STAT_FACILITY_ENABLED, 1); - } else { - STAT(STAT_FACILITY_ENABLED_FAILED, 1); - DEBUG_PRINTF("* %T facility_enable(%u)\n", facility); - } -} - - -/* This is called by the port-specific code to execute the hooks associated - * with a facility. Note that a recursive lock is internally maintained which - * ensures to prevent recursion, or to execute the hooks while new ones are - * being added, or when a hook is being deleted. - */ - - -/* Execute the hooks and transparently delete expired ones */ -void facility_exechooks(u_int32_t facility, int origin) -{ - if (facility < (enum _facilities)_FACILITY_MAX) { - register facility_t *f = &root->int_facilities[facility]; - - if (_rlock_try(&f->rlock)) { - register list_t *hooks = &f->hooks; - register hook_t *node, *tmp; - - STAT(STAT_FACILITY_EXECUTED, 1); - node = DLIST_TOP(hooks); - while (node != NULL) { - if (node->skipcount > 0) { - node->skipcount--; - STAT(STAT_HOOKS_SKIPPED, 1); - node = DLIST_NEXT(node); - } else { - node->code(node->id, origin, node->data); - STAT(STAT_HOOKS_EXECUTED, 1); - if (node->runcount != 0) { - tmp = DLIST_NEXT(node); - if ((--node->runcount) == 0) { - /* This hook is temporary and expired, - * extract it. - */ - DLIST_UNLINK(hooks, (node_t *)node); - pool_free((pnode_t *)node); - STAT(STAT_HOOKS_EXPIRED, 1); - } - node = tmp; - } else - node = DLIST_NEXT(node); - } - } - _rlock_release(&f->rlock); - } else - STAT(STAT_FACILITY_EXECUTED_LOCKED, 1); - } else { - STAT(STAT_FACILITY_EXECUTED_FAILED, 1); - DEBUG_PRINTF("* %T facility_exechooks(%u, %d)\n", facility, origin); - } -} - - -/* Initialize all facility_t */ -void facilities_init(void) -{ - register u_int32_t i; - - for (i = 0; i < (enum _facilities)_FACILITY_MAX; i++) { - register facility_t *f = &root->int_facilities[i]; - - f->idcnt = 0; - _spl0(); - pool_init(&f->pool, 1, 1, 1, sizeof(hook_t), 0); - _splhigh(); - DLIST_INIT(&f->hooks); - _rlock_init(&f->rlock); - _rlock_acquire(&f->rlock); - } - for (i = 0; i < (enum _facilities)_FACILITY_MAX; i++) { - register facility_t *f = &root->int_facilities[i]; - - _rlock_release(&f->rlock); - } -} diff --git a/Xisop/src/common/kernel/exception.h b/Xisop/src/common/kernel/exception.h deleted file mode 100644 index 760bdb6..0000000 --- a/Xisop/src/common/kernel/exception.h +++ /dev/null @@ -1,84 +0,0 @@ -/* $Id: exception.h,v 1.1 2003/10/26 21:19:57 mmondor Exp $ */ - -/* - * Copyright (C) 2003, Matthew Mondor - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Matthew Mondor. - * 4. The name of Matthew Mondor may not be used to endorse or promote - * products derived from this software without specific prior written - * permission. - * 5. Redistribution of source code may not be released under the terms of - * any GNU Public License derivate. - * - * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - - - -#ifndef KERNEL_EXCEPTION_H -#define KERNEL_EXCEPTION_H - - - -#include -#include -#include -#include -#include - - - -/* Used to hold user code hooks which should be executed at exceptions - * depending on the facility they are attached to. - */ -struct _int_hook { - pnode_t node; - hookid_t id; - u_int32_t skipcount, runcount; - void (*code)(hookid_t, int, void *); - void *data; -}; - -/* There are _FACILITIES_MAX of these in the root->facilities array. - * The facilities are defined by the port-specific code. - */ -struct _int_facility { - hookid_t idcnt; - list_t hooks; - pool_t pool; - _rlock_t rlock; -}; - - -hookid_t hook_attach(u_int32_t, u_int32_t, u_int32_t, - void (*)(hookid_t, int, void *), void *); -bool hook_detach(u_int32_t, hookid_t); -void facility_disable(u_int32_t); -void facility_enable(u_int32_t); - -void facilities_init(void); -void facility_exechooks(u_int32_t, int); - - - -#endif diff --git a/Xisop/src/common/kernel/main.c b/Xisop/src/common/kernel/main.c deleted file mode 100644 index 8232cc7..0000000 --- a/Xisop/src/common/kernel/main.c +++ /dev/null @@ -1,234 +0,0 @@ -/* $Id: main.c,v 1.8 2004/06/04 02:25:15 mmondor Exp $ */ - -/* - * Copyright (C) 2003, Matthew Mondor - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Matthew Mondor. - * 4. The name of Matthew Mondor may not be used to endorse or promote - * products derived from this software without specific prior written - * permission. - * 5. Redistribution of source code may not be released under the terms of - * any GNU Public License derivate. - * - * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - - - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - - - -COPYRIGHT("\0\nXisop Copyright 2001-2003, Matthew Mondor, \ -All rights reserved.\n"); - - - -/* The famous global structure where all Xisop control information is stored */ -struct xisop_root root[1]; - -static const char *systables_names[SYSTABLE_MAX] = { - "systable_publicports", - "systable_libraries", - "systable_devices", - "systable_handlers", - "systable_volumes" -}; - - - -/* Xisop main code. The port-specific code should switch to supervisor mode, - * setup kernel stack, disable interrupts and setup it's interrupt handlers, - * setup the memory ppools, call xisop_init(), switch back to usermode, and - * jump definitively to this function. - */ -int main(void) -{ - /* And we're Xisop-hosted! */ - - /* Setup and launch our main Xisop init task. It's source is in - * src/common/kernel/task.c. - */ - { - register task_t *task; - - if ((task = task_alloc(task_init, NULL, NULL, 0, 4096, TF_KERNEL)) - != NULL) - task_start(task); - } - - /* Here we enable the scheduler, which means that this current context - * will soon be the first to be saved in root->curctx, which currently - * consists of the _scontext _ctx_t buffer. When no more tasks are - * in the ready queue, _scontext will also be the restored context, - * in which case we want to avoid wasting power and overheating the - * CPU unnecessarily, and are using _idle() in an endless loop to do that. - * Because we are in usermode, we use sys_idle(). - */ - SCHED_ENABLE(); - - for (;;) { - /* Just idle processor */ - /* XXX Amiga-specific, can be taken out, purple color on blitter to - * show that the system is all idle - */ - CUSTOM->COLOR[0] = 0x0F0F; - sys_idle(); - } - - /* NOTREACHED */ - return 0; -} - - -void xisop_init(void) -{ - /* Enable interrupts and therefore syscalls, facilities and scheduler - * timer as well. Note that until we SCHED_ENABLE(), the scheduler will - * not attempt to perform contex switches, even though the scheduler - * timer interrupt is enabled. - */ - -#ifdef STATISTICS - statistic_init(); -#endif - - /* Unique number generator */ - _lock_init(&root->unique_lock); - root->unique = 0; - - /* Kernel memory allocators */ - _lock_init(&root->kernpool_lock); - spools_init(); /* memory.h */ - if ((root->kernpool = (mpool_t *)spool_alloc(POOL_MPOOL)) == NULL) { - /* XXX Panic */ - CUSTOM->COLOR[0] = 0x0F00; - } - if (!mpool_init(root->kernpool)) { - /* XXX Panic */ - CUSTOM->COLOR[0] = 0x0F00; - } - - /* And task multitasking scheduler */ - scheduler_init(); /* scheduler.c */ - - /* Syscalls service */ - syscall_init(); - -#ifdef DEBUG - /* Debugging messages FIFO */ - debug_init(); -#endif - - /* System hash tables */ - /* XXX Hmm the following calls kmalloc() which calls _kmalloc() which - * in turn calls lock_acquire(&root->kernpool_lock) which finally calls - * _yield(NULL) if it cannot immediately obtain the lock. However, _yield() - * function requires the scheduler interrupt to be running, and the - * scheduler lock to be released, of course. This appears to be the reason - * why the system now locks in trap_catch1() which corresponds to the - * _yield() handler... But, why can't the lock be obtained immediately? - * Since it is properly initialized first... - * I tried this in xisop_init(), in start of main() and in main() after - * SCHED_ENABLE(), always with the same results. Would it be possible that - * something we lock a lock, and are calling something which also attempts - * to lock it (and we already hold it)? If so, it would be either - * _kmalloc() or pages_alloc(). But they are both using a different lock.. - * Or, would it be possible that I messed up port.c locking? - * XXX Oh! It works fine when I strip out DEBUG and STATISTICS. This - * probably means that the problem is the the xisop kernel size and boot - * loader which need adjusting. - */ - { - register int i; - - for (i = 0; i < (enum systables)SYSTABLE_MAX; i++) { - rwlock_init(&root->systables[i].lock); - if (!hashtable_init(SYSTABLE(i), systables_names[i], - HT_DEFAULT_CAPACITY, kmalloc, kfree, - memcmp, memhash32, TRUE)) { - /* XXX PANIC! */ - CUSTOM->COLOR[0] = 0x0F00; - } - } - } - - /* Enable interrupts */ - _spl0(); -} - - -u_int32_t unique_id(void) -{ - register u_int32_t id; - - lock_acquire(&root->unique_lock); - id = (++root->unique); - _lock_release(&root->unique_lock); - - return id; -} - - -hashtable_t *systable_lock(u_int32_t systable, bool exclusive) -{ - hashtable_t *list = NULL; - - if (systable < (enum systables)SYSTABLE_MAX) { - if (exclusive) - SYSTABLE_WLOCK(systable); - else - SYSTABLE_RLOCK(systable); - list = SYSTABLE(systable); - } - - return list; -} - - -void systable_unlock(u_int32_t systable) -{ - if (systable < (enum systables)SYSTABLE_MAX) - SYSTABLE_UNLOCK(systable); -} - - -void systable_upgrade(u_int32_t systable) -{ - if (systable < (enum systables)SYSTABLE_MAX) - SYSTABLE_UPGRADE(systable); -} diff --git a/Xisop/src/common/kernel/main.h b/Xisop/src/common/kernel/main.h deleted file mode 100644 index 90dc56a..0000000 --- a/Xisop/src/common/kernel/main.h +++ /dev/null @@ -1,155 +0,0 @@ -/* $Id: main.h,v 1.5 2004/01/19 18:07:11 mmondor Exp $ */ - -/* - * Copyright (C) 2003, Matthew Mondor - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Matthew Mondor. - * 4. The name of Matthew Mondor may not be used to endorse or promote - * products derived from this software without specific prior written - * permission. - * 5. Redistribution of source code may not be released under the terms of - * any GNU Public License derivate. - * - * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - - - -#ifndef KERNEL_MAIN_H -#define KERNEL_MAIN_H - - - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - - - -/* To access and manipulate system lists */ -#define SYSTABLE(l) (&(root->systables[(enum systables)(l)].table)) -#define SYSLOCK(l) (&(root->systables[(enum systables)(l)].lock)) -#define SYSTABLE_RLOCK(l) rwlock_acquire(SYSLOCK(l), FALSE) -#define SYSTABLE_WLOCK(l) rwlock_acquire(SYSLOCK(l), TRUE) -#define SYSTABLE_UNLOCK(l) rwlock_release(SYSLOCK(l)) -#define SYSTABLE_UPGRADE(l) rwlock_upgrade(SYSLOCK(l)) - - -/* A system list. The lock allows simultaneous read-only access, or exclusive - * access when write operations are required. - */ -struct systable { - rwlock_t lock; - hashtable_t table; -}; - -enum systables { - SYSTABLE_PUBLICPORTS = 0, - SYSTABLE_LIBRARIES, - SYSTABLE_DEVICES, - SYSTABLE_HANDLERS, - SYSTABLE_VOLUMES, - SYSTABLE_MAX -}; - - -/* The Xisop main root structure */ -struct xisop_root { - - /* Do not change the order of the following block fields, as port-specific - * assembly code may assume their offsets. - */ - - /* Scheduling */ - _rlock_t sched_lock; - list_t tasks_ready, tasks_wait, tasks_dead; - task_t *curtask; - _ctx_t *curctx; - - /* The remaining fields are only accessed by common C code. */ - task_t *task_init, *task_reaper; - - /* Memory management. First are the system page pools */ - ppool_t ppools[(enum _memtypes)_MEM_MAX]; - /* Then the system object pools */ - _lock_t spools_locks[(enum _syspools)POOL_MAX]; - pool_t spools[(enum _syspools)POOL_MAX]; - /* And the kernel general purpose pool */ - _lock_t kernpool_lock; - mpool_t *kernpool; - - /* Interrupts abstraction system */ - facility_t int_facilities[(enum _facilities)_FACILITY_MAX]; - - /* Various important system lists */ - struct systable systables[(enum systables)SYSTABLE_MAX]; - - /* System calls */ - void (**syscalls)(void *, void *); - - /* Useful counter to create unique IDs for arbitrary objects, like for - * ports, using the UNIQUE() macro. Should normally be used when the - * scheduler is disabled. - */ - _lock_t unique_lock; - u_int32_t unique; - -#ifdef STATISTICS - /* Statistics support */ - u_int32_t stats[(enum stat_keys)STAT_MAX]; -#endif -#ifdef DEBUG - /* dprintf() support */ - _lock_t debuglock; - fifo8_t *debugfifo; -#endif -}; - - - -/* xisop global data */ -extern struct xisop_root root[1]; - - - -int main(void); -void xisop_init(void); -u_int32_t unique_id(void); - -hashtable_t *systable_lock(u_int32_t, bool); -void systable_upgrade(u_int32_t); -void systable_unlock(u_int32_t); - - - -#endif diff --git a/Xisop/src/common/kernel/make.sh b/Xisop/src/common/kernel/make.sh deleted file mode 100755 index fbd47d8..0000000 --- a/Xisop/src/common/kernel/make.sh +++ /dev/null @@ -1,9 +0,0 @@ -#!/bin/sh - -# $Id: make.sh,v 1.1 2003/10/26 21:19:57 mmondor Exp $ - -. ../../makedefs.sh - -buildlib . -show $C_AR ar/kernel.a *.o -show $C_RANLIB ar/kernel.a diff --git a/Xisop/src/common/kernel/memory.c b/Xisop/src/common/kernel/memory.c deleted file mode 100644 index 3811e14..0000000 --- a/Xisop/src/common/kernel/memory.c +++ /dev/null @@ -1,1425 +0,0 @@ -/* $Id: memory.c,v 1.9 2004/06/04 03:09:48 mmondor Exp $ */ - -/* - * Copyright (C) 2003, Matthew Mondor - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Matthew Mondor. - * 4. The name of Matthew Mondor may not be used to endorse or promote - * products derived from this software without specific prior written - * permission. - * 5. Redistribution of source code may not be released under the terms of - * any GNU Public License derivate. - * - * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -/* - * Support for multiple memory types and dynamically attaching pages at - * runtime was implemented the 25 Febuary 2003, when this code was rewritten - * from scratch. - * - * This memory management system works with physical pages. This means that - * we must provide facilities to work with actual physical contiguous pages - * when large memory areas are required. Pages only consist of useful units - * for management; They thus can be of any size (although multiples of 16 - * bytes), and do not need to correspond to the page size required by the - * MMU system (if any). - * - * It may not be the best or most efficient way to deal with this, as I - * wrote this code from scratch using my own ideas, without reference. - * But it works well and seems quite fast. Moreover, _PAGE_SIZE, _MEM_MAX - * and _MPOOLS are provided by the port-specific code, and allows to adapt - * the system to a variety of situations. It thus well serves it's intended - * purpose. We provide operations on pages, on basic fixed-sized pools and - * multiple block size pools for general purpose memory management functions. - * - * A previous pool_t implementation attempted to not have to delete all the - * nodes from a page when moving an unused page to the page cache, and - * statistics would be kept to know when to free them, at which time their - * pnode_t nodes were unlinked as well. Some care was taken to append freed - * nodes of less used pages at the end of the list and insert freed nodes of - * very used ones at the top of the list, so that over time hopefully - * the system would stabilize well. It used to result in more fragmentation - * than the current method which still caches unused pages and uses statistics - * to free them less often, but immediately removes all nodes of a page from - * the free nodes list when a page is unused, and has to re-initialize all - * those nodes for a page when retreiving a page back from the cache. - * Because Xisop works with actual physical pages and that it requires actual - * contiguous pages for large data blocks, it is very important to do what is - * necessary to avoid fragmentation as much as possible, in favor of stability - * and performance over long uptimes periods, and so I reverted to this method. - * We favor reuse of recently used nodes and pages as much as possible. - * - * It would be nice if the free list consisted of nodes linking to the next - * contiguous pages block rather than only individual pages. This would speed - * up multiple page allocations, which currently requires running among free - * pages to locate contiguous ones. Possibly that freeing could also adapt - * the pages index dynamically so that nodes could be restored without running - * among individual pages to know where to insert the node. - * - * The system was first tested on NetBSD in userspace, to ensure that - * everything works as expected before the code was imported in Xisop. - * - * Matt - */ - - - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - - - -/* Used by spools_init() */ -static const struct syspools_params syspools_params - [(enum _syspools)POOL_MAX] = { - {"tasks_pool", 1, 0, 0, sizeof(task_t)}, - {"ports_pool", 1, 0, 0, sizeof(port_t)}, - {"devicenodes_pool", 1, 0, 0, sizeof(devicenode_t)}, - {"devices_pool", 1, 0, 0, sizeof(device_t)}, - {"mpools_pool", 1, 0, 0, sizeof(mpool_t)} -}; - - - -/* This function is required to call once before initializing the memory - * pages, by the port-specific code. - */ -void memory_init(void) -{ - register u_int32_t i; - - for (i = 0; i < (enum _memtypes)_MEM_MAX; i++) { - _lock_init(&root->ppools[i].lock); - DLIST_INIT(&root->ppools[i].mchunks); - } -} - - -/* This function is extremely useful to prepare a given contiguous memory area - * for it to be attached to the system pages using mchunk_attach(). - * Sanity checking is performed to fix page alignment if needed, and the - * internal control structures are automatically prepared. - * Returns a pointer to an mchunk_t, or NULL if the supplied memory area is - * too small (under two valid pages). - */ -mchunk_t *mchunk_init(void *mem, size_t size) -{ - mchunk_t *mchunk = NULL; - void *begin, *end; - u_int32_t pages; - - /* Page-align pointers, and calculate number of supplied memory pages */ - end = mem + size; - begin = (void *)BALIGN_CEIL(mem, _PAGE_SIZE); - end = (void *)BALIGN_FLOOR(end, _PAGE_SIZE); - pages = (end - begin) / _PAGE_SIZE; - - if (pages > 1) { - void *reserved; - page_t **index, *page; - u_int32_t extrapages; - size_t extrabytes; - - /* Evaluate how many pages are required to be reserved to setup the - * mchunk_t and it's components. As we evaluate this on the number of - * available pages, which will shrink a bit after we reserve the area, - * we will re-evaluate it later, but will still be using the same - * reserved area, thus loosing a few bytes. The loss is however quite - * negligable. - */ - extrabytes = (size_t)OALIGN_CEIL(sizeof(mchunk_t), u_int32_t); - extrabytes += (sizeof(page_t *) + sizeof(page_t)) * pages; - extrapages = extrabytes / _PAGE_SIZE; - if (extrabytes % _PAGE_SIZE) - extrapages++; - reserved = begin; - begin += extrapages * _PAGE_SIZE; - pages -= extrapages; - - /* Now our necessary reserved area is at , and has little - * more room than required to store all the control data. We have to - * setup pages, starting at and ending at . - * First setup our control structures pointers. - */ - mchunk = (mchunk_t *)reserved; - reserved += sizeof(mchunk_t); - reserved = (void *)OALIGN_CEIL(reserved, u_int32_t); - page = reserved; - reserved += sizeof(page_t) * pages; - reserved = (void *)OALIGN_CEIL(reserved, u_int32_t); - index = reserved; - - /* Run through pages filling the control index and headers, while - * linking the page_t nodes into the mchunk_t. - */ - DLIST_INIT(&mchunk->free); - mchunk->index = index; - mchunk->pages = pages; - { - register u_int8_t *run = begin; - register list_t *l = &mchunk->free; - register u_int32_t i; - - for (i = 0; i < pages; i++, run += _PAGE_SIZE) { - register page_t *p = &page[i]; - - p->mchunk = mchunk; - p->address = run; - p->last = NULL; - p->id = i; - p->state = PS_FREE; - p->pool = NULL; - index[i] = p; - DLIST_APPEND(l, (node_t *)p); - } - } - } - - if (mchunk != NULL) - OBJECT_VALIDATE(mchunk, OBJECT_MCHUNK); - - return mchunk; -} - - -/* Allows to dynamically attach new memory at runtime. This can be useful - * for instance to assign memory for a hotplug device such as PCMCIA RAM. - * The mchunk_t should be setup using mchunk_init(). It is recommended to - * read the Xisop documentation for more information. One of the existing - * memory types supplied by the port-specific code should be chosen to which - * attach the chunk. mchunk_attach() is also used by the port-specific code - * to attach the initial memory pages. - */ -bool mchunk_attach(int memtype, mchunk_t *mchunk) -{ - bool ok = FALSE; - - if (memtype < (enum _memtypes)_MEM_MAX && memtype != _MEM_ANY && - OBJECT_VALID(mchunk, OBJECT_MCHUNK)) { - register ppool_t *p; - register mchunk_t *m; - - p = &root->ppools[memtype]; - mchunk->ppool = p; - - /* Obtain the system pages protection lock */ - lock_acquire(&p->lock); - - /* Make sure that this chunk is not already in the pool. We can - * afford this as this is a rare call, although dangerous. - */ - DLIST_FOREACH(&p->mchunks, m) { - if (m == mchunk) - break; - } - if (m == NULL) { - DLIST_APPEND(&p->mchunks, (node_t *)mchunk); - ok = TRUE; - STAT(STAT_MCHUNKS_ATTACH, 1); - } - - _lock_release(&p->lock); - } - - if (!ok) { - STAT(STAT_MCHUNKS_ATTACH_FAILED, 1); - DEBUG_PRINTF("* %T mchunk_attach(%d, %p)\n", memtype, mchunk); - } - - return ok; -} - - -/* Permits to dynamically detach memory at runtime, which was previously - * attached using mchunk_attach(). Note that this function fails with FALSE - * if any page of memory currently remains allocated, or if the mchunk_t is - * not currently attached. - */ -bool mchunk_detach(int memtype, mchunk_t *mchunk) -{ - bool ok = FALSE; - - if (memtype < (enum _memtypes)_MEM_MAX && memtype != _MEM_ANY && - OBJECT_VALID(mchunk, OBJECT_MCHUNK)) { - register ppool_t *p; - register mchunk_t *m; - - p = &root->ppools[memtype]; - - /* Lock system pages safely lock */ - lock_acquire(&p->lock); - - /* Make sure that this chunk is attached in the expected memory type, - * and also make sure that all pages are free (unallocated). - */ - DLIST_FOREACH(&p->mchunks, m) { - if (m == mchunk) - break; - } - if (m == mchunk) { - if (m->pages == DLIST_NODES(&m->free)) { - DLIST_UNLINK(&p->mchunks, (node_t *)m); - ok = TRUE; - STAT(STAT_MCHUNKS_DETACH, 1); - } - } - - _lock_release(&p->lock); - } - - if (!ok) { - STAT(STAT_MCHUNKS_DETACH_FAILED, 1); - DEBUG_PRINTF("* %T mchunk_detach(%d, %p)\n", memtype, mchunk); - } - - return ok; -} - - -/* Requests obtention of one or more contiguous physical pages from the system. - * Returns a pointer to the first page_t on success, or NULL on failure - * (out of memory). If is true, the returned memory area will be - * cleared to 0x00 bytes. To properly be freed, pages_free() is expected to - * be called on the same supplied pointer, which will free back all pages - * which were allocated at once with this function. On success, - * pages_t->address can be used to access our requested memory. - */ -page_t *pages_alloc(int memtype, u_int32_t many, bool zero) -{ - register ppool_t *fp = NULL, *tp = NULL; - page_t *pages = NULL; - bool ok; - - /* If a memory type was specified, only run through that ppool_t, but - * run through each ppool_t in order otherwise until we satisfy the - * request. It is safe to do this interruptible as the memory types - * pools always remain static. - */ - ok = TRUE; - if (many < 1) - ok = FALSE; - else { - if (memtype > -1) { - if (memtype < (enum _memtypes)_MEM_MAX) { - /* Will only try requested memory type */ - fp = tp = &root->ppools[memtype]; - tp++; - } else - ok = FALSE; - } else if (memtype == _MEM_ANY) { - /* Will try all memory types sequencially in order */ - fp = &root->ppools[0]; - tp = &root->ppools[(enum _memtypes)_MEM_MAX]; - tp++; - } else - ok = FALSE; - } - - if (ok) { - /* Loop through ppool_t types */ - for (; fp < tp; fp++) { - register mchunk_t *m; - - lock_acquire(&fp->lock); - - /* Loop through mchunk_t nodes of the ppool_t */ - DLIST_FOREACH(&fp->mchunks, m) { - /* Skip any mchunk_t which doesn't have enough pages */ - if (DLIST_NODES(&m->free) >= many) { - if (many == 1) { - register page_t *p; - - /* No need to look for contiguous pages as only a - * single one was requested. Detach the first page. - */ - p = DLIST_TOP(&m->free); - DLIST_UNLINK(&m->free, &p->node); - p->node.next = p->node.prev = NULL; - p->last = p; - p->state = PS_ALLOCATED; - _lock_release(&fp->lock); - if (zero) - pageclr(p->address, 1); - pages = p; - STAT(STAT_PAGES_ALLOC, 1); - goto end; - } else { - register page_t *n, *o; - register u_int32_t c, oid; - - /* Scan for contiguous pages in this mchunk_t */ - c = 1; - o = DLIST_TOP(&m->free); - oid = o->id; - for (n = DLIST_NEXT(o); n != NULL; n = DLIST_NEXT(n)) { - if (++oid == n->id) { - /* Contiguous, count and continue */ - c++; - if (c == many) - break; - } else { - /* Not contiguous, reset and continue */ - c = 1; - o = n; - } - } - if (c == many) { - register node_t *next, *prev; - - /* contiguous pages were found, starting at - * o and ending at n, unlink them all at once - * efficiently and set last pointer. - */ - prev = o->node.prev; - next = n->node.next; - if (prev) - prev->next = next; - else - m->free.top = next; - if (next) - next->prev = prev; - else - m->free.bottom = prev; - o->node.prev = n->node.next = NULL; - o->last = n; - m->free.nodes -= c; - - /* Then initialize the pages. Unfortunately - * we need to perform this with lock held - * because freeing pages runs among pages - * looking for PS_FREE nodes. - */ - for (n = o; c > 0; - n = (page_t *)n->node.next, c--) - n->state = PS_ALLOCATED; - - /* Finished with system pools, restore level */ - _lock_release(&fp->lock); - - /* It's safe to do the following interruptible */ - if (zero) - pageclr(o->address, many); - pages = o; - STAT(STAT_PAGES_ALLOC, many); - goto end; - } - } - } - } - - _lock_release(&fp->lock); - } - /* If we reach this point the allocation process desperatly failed */ - } - -end: - if (pages == NULL) { - STAT(STAT_PAGES_ALLOC_NOMEM, 1); - DEBUG_PRINTF("- %T pages_alloc(%d, %u, %B) - Out of memory\n", - memtype, many, zero); - } - - return pages; -} - - -/* Frees one or more contiguous pages of physical memory which were obtained - * using pages_alloc(). It is important to call this function on the same - * page_t pointer which was obtained from pages_alloc(). - */ -bool pages_free(page_t *pages) -{ - bool ok = FALSE; - - if (pages != NULL && pages->last != NULL) { - register mchunk_t *m; - register page_t **idx, *fp, *lp, *lfp, *llp; - register u_int32_t id; - - /* Find mchunk_t we belong to, setup variables */ - fp = pages; - lp = pages->last; - m = pages->mchunk; - idx = m->index; - - lock_acquire(&m->ppool->lock); - - /* Reset pages fields. Because the freeing process requires running - * among pages looking for PS_FREE ones, we need to perform this - * while we own the lock. - */ - for (lfp = fp; lfp != NULL; lfp = (page_t *)lfp->node.next) - lfp->state = PS_FREE; - - /* Determine where in the free list_t of our mchunk_t should our page_t - * nodes be inserted. It is important that the free list always remain - * sorted. Using the page index ID of our first and last allocated - * pages, we can run up and down among pages to obtain this - * information. Another possible method would be to use a minheap - * implementation. - */ - for (id = fp->id - 1; id > -1 && idx[id]->state != PS_FREE; id--) ; - if (id == -1 || idx[id]->state == PS_ALLOCATED) - lfp = NULL; - else - lfp = idx[id]; - for (id = lp->id + 1; id < m->pages && idx[id]->state != PS_FREE; - id++) ; - if (id == m->pages || idx[id]->state == PS_ALLOCATED) - llp = NULL; - else - llp = idx[id]; - - /* Now lfp == free page_t to attach first page_t to (or NULL) - * and llp == free page_t to attach last page_t to (or NULL). - * We also know that within this mchunk_t no pages should exist - * between our first and last allocated pages because they are - * contiguous; We thus can safely insert our allocated pages in one - * efficient step between lfp and llp. We could have done the following - * immediately after checking for ID boundaries in the previous loops, - * but decide to perform NULL checking instead for code clarity. - * Link lfp (or top of list) to fp in both directions, and make sure - * to set the list top and bottom pointers when necessary as well. - */ - if (lfp == NULL) { - m->free.top = (node_t *)fp; - fp->node.prev = NULL; - } else { - lfp->node.next = (node_t *)fp; - fp->node.prev = (node_t *)lfp; - } - /* Link llp (or bottom of list) to lp in both directions */ - if (llp == NULL) { - m->free.bottom = (node_t *)lp; - lp->node.next = NULL; - } else { - llp->node.prev = (node_t *)lp; - lp->node.next = (node_t *)llp; - } - /* Fix number of free nodes counter */ - m->free.nodes += (lp->id - fp->id) + 1; - - _lock_release(&m->ppool->lock); - - STAT(STAT_PAGES_FREE, (lp->id - fp->id) + 1); - - /* Make sure to not agree to free these anymore until this page_t * - * is obtained again from pages_alloc(). As the page may have been - * part of a pool_t, we also must zero the pool pointer. - */ - pages->last = NULL; - pages->pool = NULL; - - ok = TRUE; - } - - if (!ok) { - STAT(STAT_PAGES_FREE_FAILED, 1); - DEBUG_PRINTF("* %T pages_free(%p)\n", pages); - } - - return ok; -} - - -/* Initializes a pool_t and allocates the required minimum pages if required. - * The pages of a pool_t can be virtually sized as large as required by - * specifying a steppages larger than 1. steppages * _PAGE_SIZE is the - * actual size of a pool_t page. A pool can then be used to allocate objects - * exceeding _PAGE_SIZE. No locking or synchronization is used by - * pool_t functions, those must be provided externally by the caller if - * needed. Of course internal page allocation/free will be done protected by - * the system pages pool lock. A pool_t links the pages to it's lists via - * the pages_t->poolnode node_t. If minpages == 0, no pages are allocated - * until the first pool_alloc() is called on the pool_t. If maxpages == 0 - * the pool will always attempt to grow dynamically if required (and pnode_t - * allocations will fail if out of memory). If minpages and maxpages are - * non-zero and the same, pages are pre-allocated immediately and the pool - * will never shrink or grow. This can be very useful in the case of critical - * code sections which need to allocate and free objects in an interrupt - * context. A pool_t, unlike an mpool_t, will not allow to specify which - * memory type to allocate at each pnode_t allocation. It is however safe to - * use _MEM_ANY for memtype here in which case any free memory will be used, - * requested and freed as necessary. - */ -bool pool_init(pool_t *pool, u_int32_t steppages, u_int32_t minpages, - u_int32_t maxpages, size_t nodesize, int memtype) -{ - bool ok = FALSE; - - if (pool != NULL && steppages != 0 && nodesize >= sizeof(pnode_t) && - memtype < (enum _memtypes)_MEM_MAX && memtype > -2) { - register size_t psize; - register u_int32_t nodesperpage, step = steppages; - - /* Evaluate how many nodes can fit a page, and that it's realistic. - * There is no point in using a pool_t if we cannot fit at least two - * pnode_t objects per page_t. So we'll automatically increase - * steppages if needed here, upto 8 maximum, and return FALSE if - * we couln't reach a decent size relatively to the object size. - * The reason we're doing this is that Xisop system objects pools - * should grow steppages automatically if needed, and are always sure - * to be small enough to at least fit into 8 pages (usually 1-2). - * We want main.c's spools_init() to always succeed even if - * an object was slightly too large for a single page. - */ - OBJECT_INVALIDATE(pool); - nodesize = (size_t)OALIGN_CEIL(nodesize, u_int32_t); - /* All your optimization are belong to us. */ - for (psize = _PAGE_SIZE * step; - (nodesperpage = psize / nodesize) < 2 && step < 9; - psize = _PAGE_SIZE * (++step)) - STAT(STAT_POOLS_ENLARGED, 1); - if (nodesperpage > 1) { - pool->nodesperpage = nodesperpage; - pool->steppages = step; - pool->minpages = minpages; - pool->maxpages = maxpages; - pool->avgtotal = pool->avgcnt = minpages; - pool->nodesize = nodesize; - pool->memtype = memtype; - DLIST_INIT(&pool->pages); - DLIST_INIT(&pool->fpages); - DLIST_INIT(&pool->nodes); - pool->mpool = NULL; - /* Allocate and initialize pages and nodes if needed */ - for (; minpages > 0; minpages--) { - register page_t *p; - - if ((p = pages_alloc(memtype, step, FALSE)) != NULL) { - register u_int8_t *ptr, *toptr; - - /* pool_t pages are linked via page_t->poolnode */ - p->pnodes = nodesperpage; - p->pool = pool; - DLIST_APPEND(&pool->pages, &p->poolnode); - for (ptr = (u_int8_t *)p->address, toptr = ptr + psize; - ptr + nodesize < toptr; - ptr += nodesize) { - ((pnode_t *)ptr)->page = p; - DLIST_APPEND(&pool->nodes, (node_t *)ptr); - } - } else - break; - } - OBJECT_VALIDATE(pool, OBJECT_POOL); - if (minpages == 0) - ok = TRUE; - else if (minpages < pool->minpages) { - /* Some of minpages were allocated, we need to free them */ - pool_destroy(pool); - DEBUG_PRINTF( - "* %T pool_init(%p, %u, %u, %u, %u, %d) - Out of memory\n", - pool, steppages, minpages, maxpages, nodesize, memtype); - } - } - } - - if (ok) - STAT(STAT_POOLS_CREATED, 1); - else { - STAT(STAT_POOLS_CREATED_FAILED, 1); - DEBUG_PRINTF("* %T pool_init(%p, %u, %u, %u, %u, %d)\n", - pool, steppages, minpages, maxpages, nodesize, memtype); - } - - return ok; -} - - -/* Frees all memory allocated by a pool_t, thus rendering any allocated - * pnode_t objects invalid as well. The pool_t is then marked as invalid, - * it can only be valid again if initialized using pool_init(). - * FALSE is returned if the pool_t is invalidated already. - */ -bool pool_destroy(pool_t *pool) -{ - bool ok = FALSE; - - if (OBJECT_VALID(pool, OBJECT_POOL)) { - register node_t *p, *t; - - /* Just free all pages allocated by the pool, and mark the pool - * as freed/nonfunctional. All nodes on the list will be reset if - * a new pool is initialized using this pool_t *. They become - * invalid as soon as pages are freed, since they only consist of - * pointers into those pages. Remember that pool pages are tied - * up in the list_t via the page_t->poolnode node_t. - */ - for (p = DLIST_TOP(&pool->pages); p != NULL; p = t) { - t = DLIST_NEXT(p); - pages_free((page_t *)(--p)); - } - for (p = DLIST_TOP(&pool->fpages); p != NULL; p = t) { - t = DLIST_NEXT(p); - pages_free((page_t *)(--p)); - } - OBJECT_INVALIDATE(pool); - ok = TRUE; - } - - if (ok) - STAT(STAT_POOLS_DESTROYED, 1); - else { - STAT(STAT_POOLS_DESTROYED_FAILED, 1); - DEBUG_PRINTF("* %T pool_destroy(%p)\n", pool); - } - - return ok; -} - - -/* Attempts to allocate a pnode_t from the specified pool_t, and optionally - * clear the object to 0x00 bytes if zero is TRUE. NULL is returned if - * no memory was available or if the pool was setup to not be able to hold - * more objects. The size of the pnode_t prefixed object depends on the - * attributes which were set forthe pool_t at pool_init(). As each node - * object should start with a pnode_t structure, we return a pointer to - * the object structure itself at the same time. The type of memory used - * can only be the one the pool_t was initialized for. The pnode_t allocation - * process is efficient, compared to managing page_t. Special care is taken - * to avoid calling page primitives as much as possible using buffering, - * while still allowing a pool_t to be dynamically resizing if wanted. - */ -pnode_t *pool_alloc(pool_t *pool, bool zero) -{ - pnode_t *pnode = NULL; - - if (OBJECT_VALID(pool, OBJECT_POOL)) { - register pnode_t *pn; - - /* If there are pre-buffered nodes, simply return the first one. */ - if ((pn = DLIST_TOP(&pool->nodes)) != NULL) { - DLIST_UNLINK(&pool->nodes, (node_t *)pn); - pn->page->pnodes--; - if (zero) { - register page_t *p; - - p = pn->page; - memclr(pn, pool->nodesize); - pn->page = p; - } - pnode = pn; - } else { - register page_t *p = NULL; - register node_t *n; - - /* No pnode_t left, we need to allocate a new page_t to grow and - * initialize the new bnode_t objects. First verify if there is - * any available page already in our cache, which pool_free() - * maintains using statistics, to minimize calls to page - * primitives functions. If there are none, allocate a new page_t. - * Remember that we link the pages via the page_t->poolnode node_t. - */ - if (pool->maxpages == 0 || - DLIST_NODES(&pool->pages) < pool->maxpages) { - if ((n = DLIST_TOP(&pool->fpages)) != NULL) { - DLIST_UNLINK(&pool->fpages, n); - p = (page_t *)(--n); - STAT(STAT_PAGES_REUSED, pool->steppages); - } else - p = pages_alloc(pool->memtype, pool->steppages, FALSE); - if (p != NULL) { - register u_int8_t *ptr, *toptr; - register size_t nodesize = pool->nodesize; - - p->pnodes = pool->nodesperpage; - p->pool = pool; - DLIST_APPEND(&pool->pages, &p->poolnode); - for (ptr = (u_int8_t *)p->address, - toptr = ptr + _PAGE_SIZE * pool->steppages; - ptr + nodesize < toptr; - ptr += nodesize) { - ((pnode_t *)ptr)->page = p; - DLIST_APPEND(&pool->nodes, (node_t *)ptr); - } - /* Now grab first pnode_t */ - pn = DLIST_TOP(&pool->nodes); - DLIST_UNLINK(&pool->nodes, (node_t *)pn); - p->pnodes--; - if (zero) - memclr(pn, nodesize); - pn->page = p; - pnode = pn; - } - } else { - STAT(STAT_POOLS_ALLOC_NOMEM, 1); - DEBUG_PRINTF("- %T pool_alloc(%p, %B) - Out of memory\n", - pool, zero); - } - } - } else { - STAT(STAT_POOLS_ALLOC_FAILED, 1); - DEBUG_PRINTF("* %T pool_alloc(%p, %B)\n", pool, zero); - } - - if (pnode != NULL) - STAT(STAT_POOLS_ALLOC, 1); - - return pnode; -} - - -/* Used to free a node previously allocated using pool_alloc(). - * Keeps statistics and a page cache to reduce the frequency at which - * pages_*() functions need to be called. - */ -pnode_t *pool_free(pnode_t *pnode) -{ - if (pnode != NULL && OBJECT_VALID(pnode->page->pool, OBJECT_POOL)) { - register page_t *p = pnode->page; - register pool_t *pool = p->pool; - register u_int32_t exceeding; - - /* Efficiently return this node in the free list */ - DLIST_INSERT(&pool->nodes, (node_t *)pnode); - p->pnodes++; - STAT(STAT_POOLS_FREE, 1); - if ((pool->minpages < pool->maxpages) || - (pool->minpages == 0 && pool->maxpages == 0)) { - register u_int32_t pages = DLIST_NODES(&pool->pages); - - /* This is a pool_t which can shrink, book-keep statistics on - * average pages usage. - */ - pool->avgtotal += pages; - pool->avgcnt++; - if (pool->avgcnt > - ((pool->steppages * _PAGE_SIZE / pool->nodesize) * 3)) { - pool->avgcnt = 1; - pool->avgtotal = pages; - } - - if (p->pnodes == pool->nodesperpage && pool->minpages < pages) { - register u_int8_t *ptr, *toptr; - register size_t nodesize; - - /* All pnode_t objects belonging to this page_t were freed. - * Swap the page to the cache to be freed. We also need - * to sequencially unlink all the pnode_t objects this page - * supplied in the free nodes list_t. Remember that pages - * are linked via the page_t->poolnode node_t. - */ - for (ptr = (u_int8_t *)p->address, - toptr = ptr + _PAGE_SIZE * pool->steppages, - nodesize = pool->nodesize; - ptr + nodesize < toptr; - ptr += nodesize) - DLIST_UNLINK(&pool->nodes, (node_t *)ptr); - /* Insert to preferably reuse recently used pages */ - DLIST_SWAP(&pool->fpages, &pool->pages, &(p->poolnode), TRUE); - STAT(STAT_PAGES_BUFFERED, pool->steppages); - } - - /* Do statistics suggest that we should shrink the pool? If so, - * free pages from our cache back to the system. - */ - if ((exceeding = (DLIST_NODES(&pool->pages) + - DLIST_NODES(&pool->fpages)) - - (pool->avgtotal / pool->avgcnt)) > 0) { - register list_t *fpages = &pool->fpages; - register node_t *n; - - /* Preferably free pages which haven't been used recently */ - for (; exceeding > 0 && (n = fpages->bottom) != NULL; - exceeding--) { - DLIST_UNLINK(fpages, n); - p = (page_t *)(--n); - pages_free(p); - STAT(STAT_PAGES_UNBUFFERED, pool->steppages); - } - } - } - } else { - STAT(STAT_POOLS_FREE_FAILED, 1); - DEBUG_PRINTF("* %T pool_free(%p)\n", pnode); - } - - return NULL; -} - - -/* Finally, the higher-level general purpose allocation system consists of - * a bunch of pool_t, as well as a page_t list_t for requests which exceed - * in size and need to be rounded at page boundaries. Memory of all available - * types may be allocated and freed at will, and of arbitrary sized blocks. - * The current implementation requires more memory for mpool setup than - * previously, there however are advantages in versatility, performance - * and code quality over the latter. - * We depend on _PAGE_SIZE and _MPOOLS which should be defined by the - * port-specific code (port/support.h). - * No special synchronization is performed in these functions, the caller - * is required to provide it where necessary (i.e. shared mpool_t). However, - * the underlaying page_t primitives use the system pages pool lock. - */ -bool mpool_init(mpool_t *mpool) -{ - bool ok = FALSE; - - if (mpool != NULL) { - register int t, p; - register size_t c = _MPOOLSTART; - - DLIST_INIT(&mpool->pages); - ok = TRUE; - OBJECT_VALIDATE(mpool, OBJECT_MPOOL); - for (p = 0; p < _MPOOLS; p++) { - for (t = 0; t < (enum _memtypes)_MEM_MAX; t++) { - if (!pool_init(&mpool->pools[p][t], _MPOOLSTEP, 0, 0, - sizeof(mnode_t) + c, t)) - ok = FALSE; - else - mpool->pools[p][t].mpool = mpool; - } - c *= 2; - } - if (!ok) { - /* Make sure to free everything if part of the system only could be - * setup - */ - mpool_destroy(mpool); - } else { - mpool->shared = FALSE; - _lock_init(&mpool->lock); - mpool->usecount = 0; - } - } - - if (ok) - STAT(STAT_MPOOLS_CREATED, 1); - else { - STAT(STAT_MPOOLS_CREATED_FAILED, 1); - DEBUG_PRINTF("* %T mpool_init(%p)\n", mpool); - } - - return ok; -} - - -bool mpool_destroy(mpool_t *mpool) -{ - bool ok = FALSE; - - if (OBJECT_VALID(mpool, OBJECT_MPOOL)) { - register int t, p; - register node_t *pg, *tmp; - register u_int32_t usecount = 0; - - if (mpool->shared) { - lock_acquire(&mpool->lock); - usecount = --mpool->usecount; - _lock_release(&mpool->lock); - } - if (usecount < 1) { - /* All your pool_t are belong to us. */ - for (p = 0; p < _MPOOLS; p++) - for (t = 0; t < (enum _memtypes)_MEM_MAX; t++) - pool_destroy(&mpool->pools[p][t]); - /* And rounded page requests too. Remember that they are tied via - * the page_t->poolnode node_t in our list_t. - */ - for (pg = DLIST_TOP(&mpool->pages); pg != NULL; pg = tmp) { - tmp = DLIST_NEXT(pg); - pages_free((page_t *)(--pg)); - } - OBJECT_INVALIDATE(mpool); - ok = TRUE; - STAT(STAT_MPOOLS_DESTROYED, 1); - } - } else { - STAT(STAT_MPOOLS_DESTROYED_FAILED, 1); - DEBUG_PRINTF("* %T mpool_destroy(%p)\n", mpool); - } - - return ok; -} - - -/* malloc() clone which can be specified mpool_t to use, memory type - * and optional memory clear flag. - */ -void *_malloc(mpool_t *mpool, int memtype, size_t size, bool zero) -{ - void *mem = NULL; - - if (OBJECT_VALID(mpool, OBJECT_MPOOL) && - (memtype == _MEM_ANY || memtype < (enum _memtypes)_MEM_MAX)) { - register int p; - register size_t rsize; - - /* Synchronization for cases where multiple tasks share an mpool_t */ - if (mpool->shared) - lock_acquire(&mpool->lock); - - /* Verify if any of our pool_t can serve the requested size */ - rsize = (size_t)OALIGN_CEIL(sizeof(mnode_t), u_int32_t); - rsize += size; - for (p = 0; p < _MPOOLS; p++) { - if (mpool->pools[p][0].nodesize >= rsize) { - register mnode_t *n = NULL; - - /* This pool can serve this request. */ - if (memtype == _MEM_ANY) { - register int t; - - /* Try available memory types in order */ - for (t = 0; t < (enum _memtypes)_MEM_MAX; t++) { - n = (mnode_t *)pool_alloc(&mpool->pools[p][t], zero); - if (n != NULL) - break; - } - } else - n = (mnode_t *)pool_alloc(&mpool->pools[p][memtype], zero); - if (n != NULL) { - /* Best case, everything went fine */ - n->type = MNT_PNODE; - OBJECT_VALIDATE(n, OBJECT_MNODE); - mem = (++n); - break; - } else { - STAT(STAT_MPOOLS_ALLOC_NOMEM, 1); - DEBUG_PRINTF( - "- %T _malloc(%p, %d, %u, %B) - Out of memory\n", - mpool, memtype, size, zero); - } - } - } - if (p == _MPOOLS) { - register u_int32_t many; - register page_t *p; - - /* We need to round size on page boundaries and allocate pages. - * If we were requested zeroed memory, the pageclr() will be used - * internally which is more efficient than memclr(). - */ - many = rsize / _PAGE_SIZE; - if (rsize % _PAGE_SIZE) - many++; - if ((p = pages_alloc(memtype, many, zero)) != NULL) { - register mnode_t *n = p->address; - - /* Link in our pages list via the page_t->poolnode node_t */ - DLIST_APPEND(&mpool->pages, &p->poolnode); - n->u.pgnode.pages = p; - n->u.pgnode.mpool = mpool; - n->type = MNT_PAGES; - OBJECT_VALIDATE(n, OBJECT_MNODE); - mem = (++n); - } else { - STAT(STAT_MPOOLS_ALLOC_NOMEM, 1); - DEBUG_PRINTF("- %T _malloc(%p, %d, %u, %B) - Out of memory\n", - mpool, memtype, size, zero); - } - } - - if (mpool->shared) - _lock_release(&mpool->lock); - } else { - STAT(STAT_MPOOLS_ALLOC_FAILED, 1); - DEBUG_PRINTF("* %T _malloc(%p, %d, %u, %B)\n", - mpool, memtype, size, zero); - } - - if (mem != NULL) - STAT(STAT_MPOOLS_ALLOC, 1); - - return mem; -} - - -/* free() clone for _malloc() */ -void *_free(void *block) -{ - register mpool_t *mpool = NULL; - - if (block != NULL) { - register mnode_t *mn = block; - - mn--; - if (OBJECT_VALID(mn, OBJECT_MNODE)) { - - OBJECT_INVALIDATE(mn); - switch (mn->type) { - case MNT_PNODE: - /* A pool_t pnode_t which needs to be freed back. Track our - * mpool_t for synchronization if required. - */ - if ((mpool = mn->u.pnode.page->pool->mpool) != NULL) { - if (mpool->shared) - lock_acquire(&mpool->lock); - else - mpool = NULL; - } - pool_free((pnode_t *)mn); - break; - case MNT_PAGES: - { - register page_t *p = mn->u.pgnode.pages; - - /* A page_t which needs to be unlinked and freed. - * Remember that pages are linked via page_t->poolnode - * node_t. Track our mpool_t for synchronization if needed. - */ - mpool = mn->u.pgnode.mpool; - if (mpool->shared) - lock_acquire(&mpool->lock); - else - mpool = NULL; - DLIST_UNLINK(&mn->u.pgnode.mpool->pages, &p->poolnode); - pages_free(p); - break; - } - } - STAT(STAT_MPOOLS_FREE, 1); - } else { - STAT(STAT_MPOOLS_FREE_FAILED, 1); - DEBUG_PRINTF("* %T _free(%p)\n", block); - } - } else { - STAT(STAT_MPOOLS_FREE_FAILED, 1); - DEBUG_PRINTF("* %T _free(%p)\n", block); - } - if (mpool != NULL) - _lock_release(&mpool->lock); - - return NULL; -} - - - -/* This function initializes the various pool_t and _lock_t which are reserved - * to allocate specific types of Xisop system objects for efficiency. - */ -void spools_init(void) -{ - register int i; - - /* XXX Should panic on failure */ - for (i = 0; i < (enum _syspools)POOL_MAX; i++) { - _lock_init(&root->spools_locks[i]); - (void) pool_init(&root->spools[i], syspools_params[i].steppages, - syspools_params[i].minpages, - syspools_params[i].maxpages, - syspools_params[i].nodesize, 0); - } -} - - -pnode_t *spool_alloc(u_int32_t pool) -{ - pnode_t *node = NULL; - - if (pool < (enum _syspools)POOL_MAX) { - lock_acquire(&root->spools_locks[pool]); - node = pool_alloc(&root->spools[pool], FALSE); - _lock_release(&root->spools_locks[pool]); - } - - if (node == NULL) - DEBUG_PRINTF("* %T spool_alloc(%u)\n", pool); - - return node; -} - - -pnode_t *spool_free(u_int32_t pool, pnode_t *node) -{ - if (pool < (enum _syspools)POOL_MAX) { - lock_acquire(&root->spools_locks[pool]); - if (!pool_free(node)) - DEBUG_PRINTF("* %T spool_free(%u, %p)\n", pool, node); - _lock_release(&root->spools_locks[pool]); - } - - return NULL; -} - - -/* Specifically made to allocate general purpose memory for the kernel. - * Although mpool_t now can support synchronization itself when shared, - * it is faster to perform it unconditionally. - */ -void *_kmalloc(int memtype, size_t size, bool zero) -{ - void *mem = NULL; - - lock_acquire(&root->kernpool_lock); - mem = _malloc(root->kernpool, memtype, size, zero); - _lock_release(&root->kernpool_lock); - - return mem; -} - -void *_kfree(void *mem) -{ - lock_acquire(&root->kernpool_lock); - _free(mem); - _lock_release(&root->kernpool_lock); - - return NULL; -} - - -/* Closer to ANSI-C but still for general purpose kernel memory */ -void *kmalloc(size_t size) -{ - return MALLOC(size); -} - -void kfree(void *mem) -{ - FREE(mem); -} - - -/* These functions are ANSI-C standard, and only use the current task pool. */ - -void *malloc(size_t size) -{ - return _malloc(CURTASK()->mpool, _MEM_ANY, size, FALSE); -} - -void *calloc(int number, size_t size) -{ - return _malloc(CURTASK()->mpool, _MEM_ANY, number * size, TRUE); -} - -void *realloc(void *ptr, size_t size) -{ - register void *nptr = ptr; - - /* Not very efficient, but mostly provided for compatibility. */ - if (nptr != NULL) { - if (size == 0) - /* Basically a request to free the buffer */ - nptr = _free(nptr); - else { - register mnode_t *mnode; - - /* Climb up to obtain actual buffer size */ - mnode = (mnode_t *)nptr; - mnode--; - if (OBJECT_VALID(mnode, OBJECT_MNODE)) { - register size_t osize; - - switch (mnode->type) { - case MNT_PNODE: - { - osize = mnode->u.pnode.page->pool->nodesize - - sizeof(mnode_t); - break; - } - case MNT_PAGES: - { - register page_t *pages = mnode->u.pgnode.pages; - - osize = ((pages->last->id - pages->id) +1) * - _PAGE_SIZE; - break; - } - default: - nptr = NULL; - osize = 0; - break; - } - - /* Is the requested size larger than the currently available - * number of bytes? If not, don't do anything, return the - * current buffer pointer. - */ - if (nptr != NULL && osize < size) { - /* Allocate a buffer which can at least hold the requested - * number of bytes. Well at least, attempt to. If we can't - * don't do anything and return NULL. - */ - if ((nptr = malloc(size)) != NULL) { - /* Allocation successful, copy all previous buffer - * contents to the new one, then free the old buffer, - * and return the new buffer pointer. - */ - memcpy(nptr, ptr, osize); - free(ptr); - } - } - } else - nptr = NULL; - } - } else - /* A request to simply allocate */ - nptr = malloc(size); - - return nptr; -} - -void free(void *ptr) -{ - _free(ptr); -} - - -/* Xisop extentions as we support multiple memory types */ -void *tmalloc(int memtype, size_t size) -{ - return _malloc(CURTASK()->mpool, memtype, size, FALSE); -} - -void *tcmalloc(int memtype, int number, size_t size) -{ - return _malloc(CURTASK()->mpool, memtype, number * size, TRUE); -} - - - -/* The following code is only compiled in if DEBUG was #defined in config.h */ -#ifdef DEBUG - - -/* Dump the current state of all memory pages in the system. */ -void pages_dump(void) -{ - register u_int32_t ppool; - _ipl_t x; - - /* Become absolutely uninterruptible */ - x = _splhigh(); - - debug_printf("\nCURRENT STATE OF SYSTEM MEMORY PAGES\n"); - for (ppool = 0; ppool < (enum _memtypes)_MEM_MAX; ppool++) { - register mchunk_t *mchunk; - - debug_printf("\nDumping mchunks for ppool_t of _MEM_TYPE %u\n", - ppool); - DLIST_FOREACH(&(root->ppools[ppool].mchunks), mchunk) { - register page_t **index = mchunk->index; - register u_int32_t id, pages = mchunk->pages; - - debug_printf( - " Dumping pages for mchunk_t *%p (%u pages, page_t **%p)\n", - mchunk, pages, index); - debug_printf(" mchunk->free.top = "); - if (mchunk->free.top != NULL) - debug_printf( - "%u (page_t *%p)\n", ((page_t *)mchunk->free.top)->id, - mchunk->free.top); - else - debug_printf("NULL\n"); - debug_printf(" mchunk->free.bottom = "); - if (mchunk->free.bottom != NULL) - debug_printf("%u (page_t *%p)\n", - ((page_t *)mchunk->free.bottom)->id, - mchunk->free.bottom); - else - debug_printf("NULL\n"); - for (id = 0; id < pages; id++) { - register page_t *page = index[id]; - - /* Print summary information */ - debug_printf(" - %u (page_t *%p, void *%p)\n next = ", - id, page, page->address); - if (page->node.next == NULL) - debug_printf("NULL, prev = "); - else - debug_printf("%u (page_t *%p), prev = ", - ((page_t *)page->node.next)->id, - page->node.next); - if (page->node.prev == NULL) - debug_printf("NULL\n"); - else - debug_printf("%u (page_t *%p)\n", - ((page_t *)page->node.prev)->id, - page->node.prev); - - /* Rest will vary depending if page is allocated */ - if(page->state == PS_ALLOCATED) { - debug_printf(" Allocated, last = "); - if (page->last != NULL) - debug_printf("%u (page_t *%p)\n", page->last->id, - page->last); - else - debug_printf("NULL\n"); - if (page->pool != NULL) - debug_printf(" pool_t = *%p, mem %d, nodesize %u\n", - page->pool, page->pool->memtype, - page->pool->nodesize); - } else - debug_printf(" Free\n"); - - /* Perform some basic sanity checking and report problems - * which are known to disrupt the system. If those occur, - * the pools were corrupted and the system may need a reset - * for proper operation. - */ - if (page->node.next != NULL) { - register node_t *n = page->node.next; - - if (n->prev == NULL) - debug_printf( - " * page->node.next->node.prev = NULL\n"); - else if (n->prev != &page->node) - debug_printf( - " * page->node.next->node.prev != page\n"); - } - if (page->node.prev != NULL) { - register node_t *n = page->node.prev; - - if (n->next == NULL) - debug_printf( - " * page->node.prev->node.next = NULL\n"); - else if (n->next != &page->node) - debug_printf( - " * page->node.prev->node.next != page\n"); - } - if (page->id != id) - debug_printf(" * page->id != index[id]\n"); - if (page->state != PS_FREE && page->state != PS_ALLOCATED) - debug_printf(" * Unknown page->state\n"); - if (page->state == PS_FREE && page->last != NULL) - debug_printf(" * PS_FREE && last != NULL\n"); - if (page->state == PS_FREE && page->pool != NULL) - debug_printf(" * PS_FREE && page->pool != NULL"); - if (page->mchunk != mchunk) - debug_printf(" * page->mchunk != mchunk\n"); - } - } - } - - _splx(x); -} - - -#endif diff --git a/Xisop/src/common/kernel/memory.h b/Xisop/src/common/kernel/memory.h deleted file mode 100644 index 146d7b8..0000000 --- a/Xisop/src/common/kernel/memory.h +++ /dev/null @@ -1,260 +0,0 @@ -/* $Id: memory.h,v 1.4 2004/06/04 03:09:48 mmondor Exp $ */ - -/* - * Copyright (C) 2003, Matthew Mondor - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Matthew Mondor. - * 4. The name of Matthew Mondor may not be used to endorse or promote - * products derived from this software without specific prior written - * permission. - * 5. Redistribution of source code may not be released under the terms of - * any GNU Public License derivate. - * - * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - - - -#ifndef KERNEL_MEMORY_H -#define KERNEL_MEMORY_H - - - -#include -#include -#include -#include - - - -/* Only common memory type which is always available */ -#define _MEM_ANY -1 - -/* Page state */ -#define PS_FREE 0 -#define PS_ALLOCATED 1 - -/* mnode_t types */ -#define MNT_PNODE 0 -#define MNT_PAGES 1 - - - -/* This is a memory page node. One is required for each physical page which - * is to be included in the memory management system. The pool_t related - * fields seem to pollute the page space, but there is no better place to put - * them; Using an additional page_t based structure for pool_t page_t nodes - * would require allocating more memory. The pool_t primitives are such a - * vital element to Xisop that we can add support for it inherently. - */ -struct page { - node_t node; /* Link to pages_free of mchunk or pages_t */ - node_t poolnode; /* Used for pool_t page linking */ - mchunk_t *mchunk; /* mchunk_t we belong to for freeing */ - void *address; /* Page address in memory (should be 32-bit aligned) */ - page_t *last; /* Allocated? Link to last contiguous node, or NULL */ - pool_t *pool; /* If part of a pool_t, pointer to the pool_t */ - u_int32_t id; /* Page index offset in mchunk_t */ - u_int32_t pnodes; /* Number of free blocks in this page_t for pool_t */ - u_int32_t state; /* PS_FREE | PS_ALLOCATED */ -}; - -/* A chunk of memory consists of an arbitrary amount of contiguous pages - * of physical memory. This construct allows to dynamically append memory - * which can be mapped anytime, i.e. PCMCIA memory which just was inserted. - * Obviously, to add new memory some memory is required to be setup for the - * lists, index and page nodes. This memory should never be freed back unless - * it is certain that the device was detached, and all allocated memory on it - * is to be discarded. Each such chunk will be scanned in order for available - * memory when memory is to be allocated from a ppool_t. - */ -struct mchunk { - node_t node; /* Link to mpool_t */ - list_t free; /* List of free pages */ - ppool_t *ppool; /* Link to ppool_t we belong to */ - page_t **index; /* Index of all pages in this mchunk_t */ - u_int32_t pages; /* Number of pages in this mchunk_t */ - u_int32_t object_magic; /* Validity sceal */ -}; - -/* A pool of pages, there is one such pool per memory type. These are - * initialized by the port-specific _init_memory() function. - */ -struct ppool { - _lock_t lock; /* Secure exclusive access lock */ - list_t mchunks; /* List of mchunk_t objects */ -}; - - - -/* A pool used to allocate objects of a fixed size, which are smaller than - * half a page. maxpages permits to restrict the pool from growing more than - * wanted, minpages specifies the minimum number of pages which should always - * remain allocated (and setup as objects), avgtotal and avgcnt are used for - * statistics when freeing pages (and destroying their objects). An unused - * page is moved to fpages list, and moved back in pages list when an object - * on the page is used. If statistics show that the pool should shrink, pages - * from fpages are freed back to the system (unless minpages is reached). - * steppages specify how many pages to allocate and free at once. A page size - * for the pool consists of steppages * _PAGE_SIZE. All pool objects always - * start by a node_t, used for internal linking when freed, and can be used - * for custom linking after allocation until freed back. - */ -struct pool { - u_int32_t steppages, minpages, /* Size requirements */ - maxpages; - u_int32_t avgtotal, avgcnt; /* Statistics for page_t cache */ - u_int32_t nodesperpage; /* Number of pnode_t per page_t */ - size_t nodesize; /* 32-bit aligned size of pnode_t */ - list_t pages, fpages, /* Allocated and cached page_t's */ - nodes; /* Ready free pnode_t's */ - int memtype; /* Memory type to allocate */ - u_int32_t object_magic; /* Validity sceal */ - mpool_t *mpool; /* mpool_t we belong to (or NULL) */ -}; - -/* A pool_t node */ -struct pnode { - node_t node; /* pool_t->nodes or user list_t linking */ - page_t *page; /* page_t this pnode_t belongs to */ -}; - - - -/* And finally, the general purpose memory management functions, allowing - * blocks of memory of arbitrary size and type to be allocated and freed back, - * internally based upon the pool_t system. - */ -struct mpool { - /* All required pool_t for the various memory types and request sizes */ - pool_t pools[_MPOOLS][(enum _memtypes)_MEM_MAX]; - list_t pages; /* Large requests rounded on page boundaries */ - u_int32_t object_magic; /* Validity sceal */ - /* These are used in the case where several tasks share a common mpool_t, - * In which case the mpool_t is synchronized and only destroyed when no - * more tasks are using it. Because Xisop does not support MMU the - * requirement for this is questionable. Especially that through the - * message passing system tasks can easily synchronize on wanted shared - * memory regions. It can however save some memory on very small systems. - * All tasks could potentially share the same mpool_t if the init task - * was setup to do so, for instance. - */ - bool shared; - _lock_t lock; - u_int32_t usecount; -}; - -/* An mpool_t node, which prefixes all allocated blocks, the ones from pools - * as well as the ones made of rounded pages (which were too large to satisfy - * using the pool_t objects). Contrary to pool_*() and pages_*() functions, the - * mnode_t is abstracted and hidden in the supplied data block. The pointer - * supplied to the caller points immediately after this structure - * (32-bit aligned). We use a union to reduce as much as possible the size - * of the structure, and be able to reference back to both pnode_t or - * page_t + mpool_t objects. - */ -struct mnode { - union { - pnode_t pnode; /* We're a pnode_t */ - struct { - page_t *pages; /* We're a page_t from mpool_t */ - mpool_t *mpool; - } pgnode; - } u; - int type; /* MNT_PAGES || MNT_PNODE */ - u_int32_t object_magic; /* Validity sceal */ -}; - - - -/* The pool_t types Xisop initializes for efficient management of common - * system objects. These should correspond to the osize structure defined in - * memory.c's spools_init() function. - */ -enum _syspools { - POOL_TASK = 0, - POOL_PORT, - POOL_DEVICENODE, - POOL_DEVICEHANDLE, - POOL_MPOOL, - POOL_MAX -}; - -/* An array of these is filled in memory.c for pools initialization */ -struct syspools_params { - const char *label; - u_int32_t steppages, minpages, maxpages; - size_t nodesize; -}; - - - -/* Useful macros intended to be used by kernel code */ -#define TMALLOC(t, s) _kmalloc((t), (s), FALSE) -#define TCMALLOC(t, n, s) _kmalloc((t), (n) * (s), TRUE) -#define MALLOC(s) _kmalloc(_MEM_ANY, (s), FALSE) -#define CMALLOC(n, s) _kmalloc(_MEM_ANY, (n) * (s), TRUE) -#define FREE(p) _kfree((p)) - - - -void memory_init(void); - -void spools_init(void); -pnode_t *spool_alloc(u_int32_t); -pnode_t *spool_free(u_int32_t, pnode_t *); - -mchunk_t *mchunk_init(void *, size_t); -bool mchunk_attach(int, mchunk_t *); -bool mchunk_detach(int, mchunk_t *); - -page_t *pages_alloc(int, u_int32_t, bool); -bool pages_free(page_t *); - -bool pool_init(pool_t *, u_int32_t, u_int32_t, u_int32_t, size_t, int); -bool pool_destroy(pool_t *); -pnode_t *pool_alloc(pool_t *, bool); -pnode_t *pool_free(pnode_t *); - -bool mpool_init(mpool_t *); -bool mpool_destroy(mpool_t *); -void *_malloc(mpool_t *, int, size_t, bool); -void *_free(void *); -#define mpool_alloc _malloc -#define mpool_free _free - -void *_kmalloc(int, size_t, bool); -void *_kfree(void *); -void *kmalloc(size_t); -void kfree(void *); - -void *malloc(size_t); -void *calloc(int, size_t); -void *realloc(void *, size_t); -void free(void *); -void *tmalloc(int, size_t); -void *tcmalloc(int, int, size_t); - - - -#endif diff --git a/Xisop/src/common/kernel/object.h b/Xisop/src/common/kernel/object.h deleted file mode 100644 index f9a3e23..0000000 --- a/Xisop/src/common/kernel/object.h +++ /dev/null @@ -1,87 +0,0 @@ -/* $Id: object.h,v 1.4 2004/06/04 19:03:36 mmondor Exp $ */ - -/* - * Copyright (C) 2003, Matthew Mondor - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Matthew Mondor. - * 4. The name of Matthew Mondor may not be used to endorse or promote - * products derived from this software without specific prior written - * permission. - * 5. Redistribution of source code may not be released under the terms of - * any GNU Public License derivate. - * - * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -/* For a good example of code which uses these, look at port.c's port_create(), - * port_reply() and port_destroy() functions. - */ - - - -#ifndef COMMON_KERNEL_OBJECT_H -#define COMMON_KERNEL_OBJECT_H - - - -#include - - - -/* These should be unique and quite uncommon to happen randomly */ -#define OBJECT_PORT 0x504f5254 /* PORT */ -#define OBJECT_TASK 0x5441534b /* TASK */ -#define OBJECT_POOL 0x504f4f4c /* POOL */ -#define OBJECT_MPOOL 0x4d504f4c /* MPOL */ -#define OBJECT_MNODE 0x4d4e4f44 /* MNOD */ -#define OBJECT_MCHUNK 0x4d43484b /* MCHK */ -#define OBJECT_DEVICEHANDLE 0x44455648 /* DEVH */ -#define OBJECT_DEVICENODE 0x4445564e /* DEVN */ -#define OBJECT_IOREQUEST 0x494f5251 /* IORQ */ -#define OBJECT_HASHTABLE 0x4854424c /* HTBL */ -#define OBJECT_HASHNODE 0x484e4f44 /* HNOD */ - - - -/* Validate an object */ -#define OBJECT_VALIDATE(o, m) (o)->object_magic = (m) -/* Invalidate an object */ -#define OBJECT_INVALIDATE(o) (o)->object_magic = 0 -/* Register an object which others could depend on */ -#define OBJECT_REGISTER(o) (o)->object_id = unique_id() -/* Register a dependancy for the object */ -#define OBJECT_SETDEP(o, r) do { \ - (o)->objdep_id = (r)->object_id; \ - (o)->objdep_magic = (r)->object_magic; \ -} while (/* CONSTCOND */0) - -/* Returns TRUE if the object is valid and of expected type, FALSE if not */ -#define OBJECT_VALID(o, m) ((o) != NULL && (o)->object_magic == (m)) -/* Returns TRUE if the object's dependancy matches with (d) */ -#define OBJECT_DEPENDS(o, d) ((d) != NULL && \ - (d)->object_magic == (o)->objdep_magic && \ - (d)->object_id == (o)->objdep_id) - - - -#endif diff --git a/Xisop/src/common/kernel/port.c b/Xisop/src/common/kernel/port.c deleted file mode 100644 index 8e17e36..0000000 --- a/Xisop/src/common/kernel/port.c +++ /dev/null @@ -1,337 +0,0 @@ -/* $Id: port.c,v 1.7 2004/10/14 15:05:23 mmondor Exp $ */ - -/* - * Copyright (C) 2003, Matthew Mondor - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Matthew Mondor. - * 4. The name of Matthew Mondor may not be used to endorse or promote - * products derived from this software without specific prior written - * permission. - * 5. Redistribution of source code may not be released under the terms of - * any GNU Public License derivate. - * - * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - - - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - - - -port_t *port_create(const char *name) -{ - bstr_t *bstr = NULL; - signum_t signum; - - if (name != NULL) { - if ((bstr = bstr_new(name, 32, FALSE)) == NULL) { - STAT(STAT_PORTS_CREATED_NOMEM, 1); - DEBUG_PRINTF("* %T port_create(%p) - Out of memory\n", name); - } else { - SYSTABLE_RLOCK(SYSTABLE_PUBLICPORTS); - if ((hashtable_lookup(SYSTABLE(SYSTABLE_PUBLICPORTS), bstr->data, - bstr->len)) != NULL) { - SYSTABLE_UNLOCK(SYSTABLE_PUBLICPORTS); - STAT(STAT_PORTS_CREATED_EXISTS, 1); - DEBUG_PRINTF("* %T port_create(%p) - Port exists (%s)\n", - bstr->data); - bstr = bstr_free(bstr); - } - } - } - if (name == NULL || bstr != NULL) { - if ((signum = signal_alloc()) != -1) { - register task_t *task = CURTASK(); - register port_t *port = NULL; - - if ((port = (port_t *)spool_alloc(POOL_PORT)) != NULL) { - /* Validate object, and register it since message_t depends on - * us for reply-port validation - */ - OBJECT_VALIDATE(port, OBJECT_PORT); - OBJECT_REGISTER(port); - /* Initialize other port_t fields */ - port->sigtask = task; - port->signum = signum; - DLIST_INIT(&port->messages); - port->name = bstr; - if (bstr != NULL) { - /* Public port, link via sysnode */ - SYSTABLE_UPGRADE(SYSTABLE_PUBLICPORTS); - (void) hashtable_link(SYSTABLE(SYSTABLE_PUBLICPORTS), - (hashnode_t *)port, bstr->data, - bstr->len, FALSE); - SYSTABLE_UNLOCK(SYSTABLE_PUBLICPORTS); - } - /* Register task resource */ - SCHED_DISABLE(); - DLIST_APPEND(&task->resources.ports, &port->tasknode); - SCHED_ENABLE(); - STAT(STAT_PORTS_CREATED, 1); - - return port; - } else { - if (bstr != NULL) - SYSTABLE_UNLOCK(SYSTABLE_PUBLICPORTS); - STAT(STAT_PORTS_CREATED_NOMEM, 1); - DEBUG_PRINTF("* %T port_create(%p) - Out of memory\n", name); - } - signal_free(SIGMASK(signum)); - } else { - if (bstr != NULL) - SYSTABLE_UNLOCK(SYSTABLE_PUBLICPORTS); - STAT(STAT_PORTS_CREATED_NOSIG, 1); - DEBUG_PRINTF("* %T port_create(%p) - Out of signals\n", name); - } - } - - if (bstr != NULL) - bstr_free(bstr); - - return NULL; -} - - -/* Can destroy a port of any task. */ -port_t *port_destroy(port_t *port) -{ - if (OBJECT_VALID(port, OBJECT_PORT)) { - register task_t *task = port->sigtask; - - SCHED_DISABLE(); - OBJECT_INVALIDATE(port); - _signal_free(task, PORT_SIGMASK(port)); - /* Unlink task resource, linked via tasknode */ - DLIST_UNLINK(&task->resources.ports, &port->tasknode); - SCHED_ENABLE(); - if (port->name != NULL) { - bstr_free(port->name); - /* Unlink system resource, linked via sysnode */ - SYSTABLE_WLOCK(SYSTABLE_PUBLICPORTS); - hashtable_unlink(SYSTABLE(SYSTABLE_PUBLICPORTS), - (hashnode_t *)port); - SYSTABLE_UNLOCK(SYSTABLE_PUBLICPORTS); - } - spool_free(POOL_PORT, (pnode_t *)port); - STAT(STAT_PORTS_DESTROYED, 1); - } else { - STAT(STAT_PORTS_DESTROYED_FAILED, 1); - DEBUG_PRINTF("* %T port_destroy(%p)\n", port); - } - - return NULL; -} - - -port_t *port_find(const char *name) -{ - register port_t *port = NULL; - - if (name != NULL) { - SYSTABLE_RLOCK(SYSTABLE_PUBLICPORTS); - port = (port_t *)hashtable_lookup(SYSTABLE(SYSTABLE_PUBLICPORTS), - name, strnlen(name, 32)); - SYSTABLE_UNLOCK(SYSTABLE_PUBLICPORTS); - } - - if (port != NULL) - STAT(STAT_PORTS_FIND_FOUND, 1); - else - STAT(STAT_PORTS_FIND_NOTFOUND, 1); - - return port; -} - - -bool port_send(port_t *port, port_t *replyport, message_t *msg) -{ - bool ok = FALSE; - message_t *tmsg; - - if (OBJECT_VALID(port, OBJECT_PORT) && msg != NULL) { - ok = TRUE; - if (replyport != NULL) { - if (OBJECT_VALID(replyport, OBJECT_PORT)) { - msg->replyport = replyport; - OBJECT_SETDEP(msg, replyport); - } else - ok = FALSE; - } else - msg->replyport = NULL; - /* Queue message */ - SCHED_DISABLE(); - tmsg = DLIST_TOP(&port->messages); - DLIST_APPEND(&port->messages, (node_t *)msg); - SCHED_ENABLE(); - /* Notify that a message arrived, but only if the port was previously - * empty. - */ - if (tmsg == NULL) - signal_send(port->sigtask, PORT_SIGMASK(port)); - } - - if (ok) - STAT(STAT_PORTS_SEND, 1); - else { - STAT(STAT_PORTS_SEND_FAILED, 1); - DEBUG_PRINTF("* %T port_send(%p, %p, %p)\n", port, replyport, msg); - } - - return ok; -} - - -message_t *port_get(port_t *port) -{ - register message_t *msg = NULL; - - if (OBJECT_VALID(port, OBJECT_PORT)) { - SCHED_DISABLE(); - if ((msg = DLIST_TOP(&port->messages)) != NULL) - DLIST_UNLINK(&port->messages, (node_t *)msg); - SCHED_ENABLE(); - } else { - STAT(STAT_PORTS_GET_FAILED, 1); - DEBUG_PRINTF("* %T port_get(%p)\n", port); - } - - if (msg != NULL) - STAT(STAT_PORTS_GET, 1); - - return msg; -} - - -bool port_reply(message_t *msg) -{ - bool ok = FALSE; - - if (msg != NULL && OBJECT_DEPENDS(msg, msg->replyport)) - ok = port_send(msg->replyport, NULL, msg); - - if (ok) - STAT(STAT_PORTS_REPLY, 1); - else { - STAT(STAT_PORTS_REPLY_FAILED, 1); - DEBUG_PRINTF("* %T port_reply(%p)\n", msg); - } - - return ok; -} - - -/* Somewhat like poll(), but returns the port which caused the awakening. - * The task argument is optional, and specifies a prefered task to run next, - * or NULL. Note that a task can only wait for messages on it's own ports. - */ -port_t *port_wait(port_t **ports, u_int32_t num, task_t *yield) -{ - register port_t *port = NULL; - - if (ports != NULL && num > 0) { - register sigmask_t sigmask = 0; - register u_int32_t i; - - SCHED_DISABLE(); - /* By definition, a port has a tied signal. Form the sigmask. */ - for (i = 0; i < num; i++) { - register port_t *p = ports[i]; - - if (OBJECT_VALID(p, OBJECT_PORT)) { - sigmask |= PORT_SIGMASK(p); - if (DLIST_TOP(&p->messages) != NULL) { - port = p; - break; - } - } - } - SCHED_ENABLE(); - /* Only wait if all ports were empty */ - if (port == NULL) { - STAT(STAT_PORTS_WAIT, 1); - sigmask = signal_wait(sigmask, yield); - STAT(STAT_PORTS_WAIT_RETURNED_SLEEP, 1); - SCHED_DISABLE(); - for (i = 0; i < num; i++) { - register port_t *p = ports[i]; - - if (OBJECT_VALID(p, OBJECT_PORT) && - (sigmask & PORT_SIGMASK(p))) { - port = p; - break; - } - } - /* A big problem! We awaken, but not as the result of one of the - * port signals. When such a thing can occur, the task should - * use signal_wait() and manually include in the mask the wanted - * port signals, not port_wait(). We'll return NULL of course. - */ - if (i == num) { - STAT(STAT_PORTS_WAIT_RETURNED_NOSIG, 1); - DEBUG_PRINTF( - "- %T port_wait(%p, %u, %p) - Unexpected signal\n", - ports, num, yield); - } - SCHED_ENABLE(); - } else - STAT(STAT_PORTS_WAIT_RETURNED_IMMEDIATE, 1); - } else { - STAT(STAT_PORTS_WAIT_FAILED, 1); - DEBUG_PRINTF("* %T port_wait(%p, %u, %p)\n", ports, num, yield); - } - - return port; -} - - -/* Unlinks all currently queued messages (if any) from the supplied port_t. - * Should normally be performed on the tasks of the caller process only. - */ -bool port_flush(port_t *port) -{ - bool ok = FALSE; - - if (OBJECT_VALID(port, OBJECT_PORT)) { - ok = TRUE; - SCHED_DISABLE(); - if (DLIST_TOP(&port->messages) != NULL) - DLIST_INIT(&port->messages); - SCHED_ENABLE(); - STAT(STAT_PORTS_FLUSHED, 1); - } else { - STAT(STAT_PORTS_FLUSHED_FAILED, 1); - DEBUG_PRINTF("* %T port_flush(%p)\n", port); - } - - return ok; -} diff --git a/Xisop/src/common/kernel/port.h b/Xisop/src/common/kernel/port.h deleted file mode 100644 index 0a2ee44..0000000 --- a/Xisop/src/common/kernel/port.h +++ /dev/null @@ -1,103 +0,0 @@ -/* $Id: port.h,v 1.2 2004/01/18 17:43:00 mmondor Exp $ */ - -/* - * Copyright (C) 2003, Matthew Mondor - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Matthew Mondor. - * 4. The name of Matthew Mondor may not be used to endorse or promote - * products derived from this software without specific prior written - * permission. - * 5. Redistribution of source code may not be released under the terms of - * any GNU Public License derivate. - * - * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - - - -#ifndef KERNEL_PORT_H -#define KERNEL_PORT_H - - - -#include -#include -#include -#include -#include -#include -#include -#include - - - -struct port { - hashnode_t sysnode; - node_t tasknode; /* Link to task_t ressources list_t */ - /* Validity sceal, message_t can depend on us */ - u_int32_t object_magic, object_id; - /* Other */ - task_t *sigtask; - signum_t signum; - list_t messages; - /* The following are used for public ports */ - bstr_t *name; -}; - -struct message { - pnode_t node; /* Allows for linking and using pool_t */ - /* The following two are used to allow replying back, but only to the - * actually expected reply port. - */ - port_t *replyport; - /* Object dependancy */ - u_int32_t objdep_magic, objdep_id; -}; - -/* This message type is made to pass through the special multiplexed port */ -/* XXX */ -struct mmessage { - message_t node; - int type; - union { - } u; -}; - - - -#define PORT_SIGMASK(p) SIGMASK((p)->signum) - - - -port_t *port_create(const char *); -port_t *port_destroy(port_t *); -port_t *port_find(const char *); -bool port_send(port_t *, port_t *, message_t *); -message_t *port_get(port_t *); -bool port_reply(message_t *); -port_t *port_wait(port_t **, u_int32_t, task_t *); -bool port_flush(port_t *); - - - -#endif diff --git a/Xisop/src/common/kernel/scheduler.c b/Xisop/src/common/kernel/scheduler.c deleted file mode 100644 index f9329a4..0000000 --- a/Xisop/src/common/kernel/scheduler.c +++ /dev/null @@ -1,382 +0,0 @@ -/* $Id: scheduler.c,v 1.5 2004/01/29 21:52:12 mmondor Exp $ */ - -/* - * Copyright (C) 2003, Matthew Mondor - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Matthew Mondor. - * 4. The name of Matthew Mondor may not be used to endorse or promote - * products derived from this software without specific prior written - * permission. - * 5. Redistribution of source code may not be released under the terms of - * any GNU Public License derivate. - * - * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - - - -#include -#include -#include -#include -#include -#include -#include - - - -/* The famous initial context (the first scheduler interrupt context save - * operation saves main()'s context there. This context is resumed when there - * are no more tasks in the ready queue to run, in which case main()'s CPU - * ideling loop resumes. - */ -static _ctx_t _scontext; - - - -/* Used to initialize the xisop_root necessary fields */ -void scheduler_init(void) -{ - /* Initialize scheduler recursive lock, and obtain it. The system will - * need to SCHED_ENABLE() to start the scheduler. - */ - _rlock_init(&root->sched_lock); - _rlock_acquire(&root->sched_lock); - DLIST_INIT(&root->tasks_ready); - DLIST_INIT(&root->tasks_wait); - DLIST_INIT(&root->tasks_dead); - root->curtask = NULL; - root->curctx = &_scontext; -} - - -/* This function only has effect if the scheduler recursive lock is free, - * in which case scheduling is enabled. When so, it evaluates which task - * should run next and internally performs task and context switching if - * needed, via root->curtask and _scontext, the static context buffer used - * by the scheduler interrupt handler to store and load CPU context. We are - * only dealing with the tasks which are currently on the root->tasks_ready - * queue, and are working appropriately around the events of no tasks in the - * queue and the first context switch, and against recursion. - * - * What the scheduler timer interrupt does is disable the interrupt source, - * save the current user CPU context in root->curctx, which may be _scontext - * or old task context, execute the _FACILITY_SCHEDTIMER hooks calling - * facility_exechooks(), call us, load back the saved user CPU context - * from root->curctx, re-enable the interrupt source and return from exception - * to the new context's PC address. So basically we are between the context - * save and load operations, and can access and alter root->curtask and - * root->curctx, thus the buffer it uses to load the context back. - * - * Our task priority scheduling algorithm uses a credits attribution method - * where when starting a round, each task in the queue receives credits - * according to it's specific priority. The round expires when none of the - * tasks have any credits left, in which case a new round restarts over. - * During a round, an effort is made to both allow tasks with the most credits - * to run more often and faster than others, while still distributing as much - * as possible their turns evenly to make the signal and message port systems - * work as efficiently as possible in all cases. - * - * This does not account for how long each task has run, a task may - * take any time from 0 to the scheduler rate before it is interrupted and - * a credit is substracted from it. Another type of scheduler could be - * more suitable for a realtime system. - */ -void old_schedule(void) -{ - /* Do nothing unless the scheduler is enabled */ - if (_rlock_try(&root->sched_lock)) { - register task_t *ntsk, *old; - - if ((old = root->curtask) == NULL) - ntsk = DLIST_TOP(&root->tasks_ready); - else { - if (old->state == STATE_RUN) - old->state = STATE_READY; - for (;;) { - register task_t *tsk; - register priority_t credit; - - /* Find which task has the most credits and deserves next run, - * but continue the loop at previous task, otherwise the - * highest priority task will get all turns at once until it - * reaches equality with other tasks. - */ - credit = -128; - tsk = old; - ntsk = NULL; - for (;;) { - if ((tsk = (task_t *)tsk->node.node.next) == NULL) - tsk = DLIST_TOP(&root->tasks_ready); - if (tsk != old) { - if (credit < tsk->credits) { - credit = tsk->credits; - ntsk = tsk; - } - } else - break; - } - if (ntsk == NULL) { - /* If this happens there is only one task in the queue, - * or none. Revert back to previous task, if any. - */ - if (DLIST_NODES(&root->tasks_ready) > 0) - ntsk = old; - else break; - } - - if (credit == -128 /* XXX && ntsk != old */) { - /* All out of credits, redistribute them. This is the - * end and beginning of a new round. - */ - DLIST_FOREACH(&root->tasks_ready, tsk) { - if ((tsk->credits = tsk->priority) == -128) - tsk->credits++; - } - continue; - } - - break; - } - } - - if (ntsk != NULL) { - /* This is the next task to run */ - ntsk->credits--; - if (ntsk != old) { - /* Perform actual context switch */ - STAT(STAT_SCHED_PREEMPTED, 1); - ntsk->state = STATE_RUN; - root->curtask = ntsk; - root->curctx = &ntsk->ctx; - } - } else { - root->curtask = NULL; - root->curctx = &_scontext; - } - - _rlock_release(&root->sched_lock); - } -} - -/* Because there seems to be a bug with the previous function and that - * all we need for testing and building the rest of Xisop is at least - * round robin, here is a function which at least works for now. - */ -void schedule(task_t *pref) -{ - if (_rlock_try(&root->sched_lock)) { - register task_t *tsk; - - /* First evaluate if requested task is ok to switch to */ - if (OBJECT_VALID(pref, OBJECT_TASK) && pref != root->curtask && - pref->state == STATE_READY) - tsk = pref; - else { - if ((tsk = root->curtask) == NULL) - tsk = DLIST_TOP(&root->tasks_ready); - else { - /* The current task may not be in the ready list anymore */ - if (tsk->state == STATE_READY || tsk->state == STATE_RUN) { - tsk->state = STATE_READY; - if ((tsk = DLIST_NEXT(tsk)) == NULL) - tsk = DLIST_TOP(&root->tasks_ready); - } else - tsk = DLIST_TOP(&root->tasks_ready); - } - } - if (tsk != NULL) { - tsk->state = STATE_RUN; - root->curtask = tsk; - root->curctx = &tsk->ctx; - } else { - /* No tasks in the ready queue to run, return to the original - * main() context, which idles the CPU as much as possible - * using sys_idle(). - */ - root->curtask = NULL; - root->curctx = &_scontext; - } - _rlock_release(&root->sched_lock); - } -} - - -/* Allows to make a task sleep forcibly. The only way it can then wake up - * is by task_wakeup() using at least one of the bits in the flags. - */ -void task_sleep(task_t *task, u_int32_t flags, task_t *yield) -{ - bool current = FALSE; - - SCHED_DISABLE(); - if (OBJECT_VALID(task, OBJECT_TASK) && - (task->state == STATE_READY || task->state == STATE_RUN)) { - task->sleepflags = flags; - task->state = STATE_WAIT; - DLIST_SWAP(&root->tasks_wait, &root->tasks_ready, (node_t *)task, - FALSE); - if (task == root->curtask) - current = TRUE; - } - SCHED_ENABLE(); - if (current) - _yield(yield); -} - -/* Awakes a task which should have been put asleep using task_sleep(). - * The task is only awaken if at least one of the reasons in flags matches. - */ -bool task_wakeup(task_t *task, u_int32_t flags) -{ - bool ok = FALSE; - - SCHED_DISABLE(); - if (OBJECT_VALID(task, OBJECT_TASK) && - task->state == STATE_WAIT && (task->sleepflags & flags)) { - task->state = STATE_READY; - task->sleepflags = 0; - task->credits = task->priority; - if (task->credits == -128) - task->credits++; - DLIST_SWAP(&root->tasks_ready, &root->tasks_wait, (node_t *)task, - FALSE); - ok = TRUE; - } - SCHED_ENABLE(); - - return ok; -} - - -void sched_disable(void) -{ - SCHED_DISABLE(); -} - -void sched_enable(void) -{ - SCHED_ENABLE(); -} - - -/* Nestled locking is not permitted with these locks */ -void lock_acquire(_lock_t *lock) -{ - /* Always immediately _yield() unless we can obtain the lock. - * This way we use the less CPU time possible while at the same time - * allowing the locker to eventually release the lock. - */ - for (;;) { - if (_lock_try(lock)) - break; - _yield(NULL); - } -} - -void lock_release(_lock_t *lock) -{ - _lock_release(lock); -} - - -/* A lock type permitting multiple readers but exclusive access for write */ -void rwlock_init(rwlock_t *lock) -{ - _lock_init(&lock->exclusive_lock); - _rlock_init(&lock->recursive_lock); - lock->state = RWLOCK_SLOCKED; -} - -void rwlock_acquire(rwlock_t *lock, bool exclusive) -{ - lock_acquire(&lock->exclusive_lock); - if (exclusive) { - while (!_rlock_try(&lock->recursive_lock)) - _yield(NULL); - lock->state = RWLOCK_XLOCKED; - _rlock_release(&lock->recursive_lock); - } else { - _rlock_acquire(&lock->recursive_lock); - lock->state = RWLOCK_SLOCKED; - _lock_release(&lock->exclusive_lock); - } -} - -void rwlock_release(rwlock_t *lock) -{ - switch (lock->state) { - case RWLOCK_SLOCKED: - _rlock_release(&lock->recursive_lock); - break; - case RWLOCK_XLOCKED: - lock->state = RWLOCK_SLOCKED; - _lock_release(&lock->exclusive_lock); - break; - } -} - -bool rwlock_try(rwlock_t *lock, bool exclusive) -{ - bool ok = FALSE; - - if (_lock_try(&lock->exclusive_lock)) { - if (exclusive) { - if (_rlock_try(&lock->recursive_lock)) { - _rlock_release(&lock->recursive_lock); - _lock_release(&lock->exclusive_lock); - } else { - lock->state = RWLOCK_XLOCKED; - ok = TRUE; - } - } else { - _rlock_acquire(&lock->recursive_lock); - lock->state = RWLOCK_SLOCKED; - _lock_release(&lock->exclusive_lock); - ok = TRUE; - } - } - - return ok; -} - -void rwlock_upgrade(rwlock_t *lock) -{ - if (lock->state == RWLOCK_SLOCKED) { - lock_acquire(&lock->exclusive_lock); - _rlock_release(&lock->recursive_lock); - while (!_rlock_try(&lock->recursive_lock)) - _yield(NULL); - lock->state = RWLOCK_XLOCKED; - _rlock_release(&lock->recursive_lock); - } -} - -void rwlock_downgrade(rwlock_t *lock) -{ - if (lock->state == RWLOCK_XLOCKED) { - _rlock_acquire(&lock->recursive_lock); - lock->state = RWLOCK_SLOCKED; - _lock_release(&lock->exclusive_lock); - } -} diff --git a/Xisop/src/common/kernel/scheduler.h b/Xisop/src/common/kernel/scheduler.h deleted file mode 100644 index 02b95ce..0000000 --- a/Xisop/src/common/kernel/scheduler.h +++ /dev/null @@ -1,90 +0,0 @@ -/* $Id: scheduler.h,v 1.2 2004/01/19 00:03:23 mmondor Exp $ */ - -/* - * Copyright (C) 2003, Matthew Mondor - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Matthew Mondor. - * 4. The name of Matthew Mondor may not be used to endorse or promote - * products derived from this software without specific prior written - * permission. - * 5. Redistribution of source code may not be released under the terms of - * any GNU Public License derivate. - * - * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - - - -#ifndef KERNEL_SCHEDULER_H -#define KERNEL_SCHEDULER_H - - - -#include -#include -#include - - - -/* Scheduler control without affecting the scheduler interrupt */ -#define SCHED_DISABLE() _rlock_acquire(&root->sched_lock) -#define SCHED_ENABLE() _rlock_release(&root->sched_lock) - -/* Sleep reason flags for task_sleep() and task_wakeup() */ -#define TSF_SIGNAL (1L << 0) -#define TSF_KERNEL (1L << 1) -#define TSF_CUSTOM (1L << 2) - -/* State of an rwlock_t */ -enum _rwlock_states { - RWLOCK_SLOCKED, - RWLOCK_XLOCKED -}; - -struct rwlock { - _lock_t exclusive_lock; - _rlock_t recursive_lock; - enum _rwlock_states state; -}; - - - -void scheduler_init(void); -void schedule(task_t *); -void task_sleep(task_t *, u_int32_t, task_t *); -bool task_wakeup(task_t *, u_int32_t); -void sched_disable(void); -void sched_enable(void); - -void lock_acquire(_lock_t *); -void lock_release(_lock_t *); -void rwlock_init(rwlock_t *); -void rwlock_acquire(rwlock_t *, bool); -void rwlock_release(rwlock_t *); -bool rwlock_try(rwlock_t *, bool); -void rwlock_upgrade(rwlock_t *); -void rwlock_downgrade(rwlock_t *); - - - -#endif diff --git a/Xisop/src/common/kernel/signal.c b/Xisop/src/common/kernel/signal.c deleted file mode 100644 index a882416..0000000 --- a/Xisop/src/common/kernel/signal.c +++ /dev/null @@ -1,195 +0,0 @@ -/* $Id: signal.c,v 1.3 2004/06/04 02:15:47 mmondor Exp $ */ - -/* - * Copyright (C) 2003, Matthew Mondor - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Matthew Mondor. - * 4. The name of Matthew Mondor may not be used to endorse or promote - * products derived from this software without specific prior written - * permission. - * 5. Redistribution of source code may not be released under the terms of - * any GNU Public License derivate. - * - * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - - - -#include -#include -#include -#include -#include -#include -#include -#include -#include - - - -/* Attempts to allocate a general purpose user signal from the current task */ -signum_t signal_alloc(void) -{ - register signum_t signum = -1; - register task_t *task = CURTASK(); - - if (task->sigalloc != 0xFFFFFFFF) { - register sigmask_t sigmask = task->sigalloc; - - for (signum = 0; signum < 32; signum++) { - if ((sigmask & SIGMASK(signum)) == 0) { - sigmask |= SIGMASK(signum); - task->sigalloc = sigmask; - STAT(STAT_SIGNALS_ALLOC, 1); - break; - } - } - if (signum == 32) - signum = -1; - } else { - STAT(STAT_SIGNALS_ALLOC_NOSIG, 1); - DEBUG_PRINTF("- %T signal_alloc() - Out of signals\n"); - } - - return signum; -} - - -void _signal_free(task_t *task, sigmask_t sigmask) -{ - if (OBJECT_VALID(task, OBJECT_TASK)) { - /* Refuse to free reserved signals */ - sigmask &= ~SIGRESMASK; - /* But free all other requested ones */ - task->sigalloc &= ~sigmask; - STAT(STAT_SIGNALS_FREE, 1); - } -} - - -/* Frees back specified allocated signals of the current process, for future - * obtention with signal_alloc() again. - */ -void signal_free(sigmask_t sigmask) -{ - _signal_free(CURTASK(), sigmask); -} - - -/* Suspends the current task until at least one signal in sigmask is received - * by it. The task is moved to the wait queue and sigwait is set, as - * opposition to yield() which does not change the task state and just causes - * a reschedule. - * Obviously, using a sigmask of 0 here woild suspend the task indefinitely, - * and is invalid. If the task needs to be awakened on a timeout event, it - * should allocate a signal for that event and ensure to receive that signal - * when the delay expires. This can be done using a timer.device or another - * task. - * The optional task argument, which may be NULL, specifies a preference to - * which task to run next (as we yield()). - */ -sigmask_t signal_wait(sigmask_t sigmask, task_t *yield) -{ - register sigmask_t recvmask = 0; - - if (sigmask != 0) { - register task_t *t = CURTASK(); - - sigmask |= SIGRESMASK; /* Always awake on those */ - - SCHED_DISABLE(); - /* Set new sigwait mask */ - t->sigwait = sigmask; - SCHED_ENABLE(); - - /* Swap task to waiting queue (we know that we are currently in the - * ready one, in STATE_RUN, otherwise we would not be running), and - * relay control back to scheduler immediately. - */ - task_sleep(t, TSF_SIGNAL, yield); - - /* If we get here, it's because we were swapped back to the ready queue - * as the result of receiving a signal we were waiting for, and - * were awaken by the scheduler, and we are back in STATE_RUN. Clear - * the sigwait mask, but also return it so that the caller knows which - * signal(s) caused our task to wake up again. - */ - SCHED_DISABLE(); - recvmask = t->sigrecv; - t->sigrecv = 0; - SCHED_ENABLE(); - - STAT(STAT_SIGNALS_WAIT_RETURNED_SLEEP, 1); - } else { - STAT(STAT_SIGNALS_WAIT_FAILED, 1); - DEBUG_PRINTF("* %T signal_wait(%b, %p)\n", sigmask, yield); - } - - return recvmask; -} - - -/* Sends a signal to a task. The task is immediately moved to the ready - * queue if it currently was waiting for it. The current task is however - * left executing, but may _yield() if it wants the other end to be able to - * react to the signal as soon as possible. Otherwise the scheduler frequency - * and tasks priorities will decide when the other end can run. - */ -void signal_send(task_t *task, sigmask_t sigmask) -{ - if (OBJECT_VALID(task, OBJECT_TASK) && sigmask != 0) { - bool awake = FALSE; - - SCHED_DISABLE(); - /* Apply signals */ - task->sigrecv |= sigmask; - SCHED_ENABLE(); - - STAT(STAT_SIGNALS_SEND, 1); - - /* If needed, swap task to ready queue */ - if ((task->sigwait & task->sigrecv) != 0) - awake = task_wakeup(task, TSF_SIGNAL); - - /* XXX UGH! Unless we _yield(), synchronization problems quickly occur - * among communicating tasks, they eventually all reside in the wait - * queue, in which case they obviously deadlock. And strangely enough, - * we have to _yield() twice to solve this issue! Why is currently - * still a mystery, but possibly that it has to do with the fact that - * when communicating through message ports, we expect a reply? - * Maybe that the scheduler should be left to evaluate when a task - * should be moved to the waiting queue, and when it needs to be - * brought back on the ready queue because it received an expected - * signal... rather than having tasks do so themselves, which is what - * we currently are doing. - */ - if (awake) { - STAT(STAT_SIGNALS_SEND_WOKE, 1); - _yield(task); - _yield(NULL); - } - } else { - STAT(STAT_SIGNALS_SEND_FAILED, 1); - DEBUG_PRINTF("* %T signal_send(%p, %b)\n", task, sigmask); - } -} diff --git a/Xisop/src/common/kernel/signal.h b/Xisop/src/common/kernel/signal.h deleted file mode 100644 index 50eca01..0000000 --- a/Xisop/src/common/kernel/signal.h +++ /dev/null @@ -1,67 +0,0 @@ -/* $Id: signal.h,v 1.1 2003/10/26 21:19:57 mmondor Exp $ */ - -/* - * Copyright (C) 2003, Matthew Mondor - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Matthew Mondor. - * 4. The name of Matthew Mondor may not be used to endorse or promote - * products derived from this software without specific prior written - * permission. - * 5. Redistribution of source code may not be released under the terms of - * any GNU Public License derivate. - * - * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - - - -#ifndef KERNEL_SIGNAL_H -#define KERNEL_SIGNAL_H - - - -#include -#include - - - -/* Reserved signals */ -#define SIGRESERVED 2 -#define SIGRESMASK 0x00000003L -#define SIGTERM 0 -#define SIGPOLL 1 - - -/* Useful to form a sigmask_t from signum_t */ -#define SIGMASK(n) (sigmask_t )(1L << (n)) - - -signum_t signal_alloc(void); -void signal_free(sigmask_t); -void _signal_free(task_t *, sigmask_t); -sigmask_t signal_wait(sigmask_t, task_t *); -void signal_send(task_t *, sigmask_t); - - - -#endif diff --git a/Xisop/src/common/kernel/statistic.c b/Xisop/src/common/kernel/statistic.c deleted file mode 100644 index 154823b..0000000 --- a/Xisop/src/common/kernel/statistic.c +++ /dev/null @@ -1,168 +0,0 @@ -/* $Id: statistic.c,v 1.3 2004/06/04 02:15:47 mmondor Exp $ */ - -/* - * Copyright (C) 2003, Matthew Mondor - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Matthew Mondor. - * 4. The name of Matthew Mondor may not be used to endorse or promote - * products derived from this software without specific prior written - * permission. - * 5. Redistribution of source code may not be released under the terms of - * any GNU Public License derivate. - * - * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - - - -#include -#include -#include -#include -#include -#include - - - -#ifdef STATISTICS - - - -/* Should match with stat_keys enum of statistic.h */ -const static char *stat_strings[] = { - "mem.mchunks.attach", - "mem.mchunks.attach.failed", - "mem.mchunks.detach", - "mem.mchunks.detach.failed", - "mem.pages.alloc", - "mem.pages.alloc.nomem", - "mem.pages.free", - "mem.pages.free.failed", - "mem.pages.buffered", - "mem.pages.unbuffered", - "mem.pages.reused", - "mem.pools.created", - "mem.pools.created.failed", - "mem.pools.enlarged", - "mem.pools.destroyed", - "mem.pools.destroyed.failed", - "mem.pools.alloc", - "mem.pools,alloc.failed", - "mem.pools.alloc.nomem", - "mem.pools.free", - "mem.pools.free.failed", - "mem.mpools.created", - "mem.mpools.created.failed", - "mem.mpools.destroyed", - "mem.mpools.destroyed.failed", - "mem.mpools.alloc", - "mem.mpools.alloc.failed", - "mem.mpools.alloc.nomem", - "mem.mpools.free", - "mem.mpools.free.failed", - "syscalls.invalid", - "syscalls.invoked", - "hooks.attached", - "hooks.attached.failed", - "hooks.attached.nomem", - "hooks.detached", - "hooks.detached,failed", - "hooks.detached.noexist", - "hooks.expired", - "hooks.executed", - "hooks.skipped", - "facility.disabled", - "facility.disabled.failed", - "facility.enabled", - "facility.enabled.failed", - "facility.executed", - "facility.executed.failed", - "facility.executed.locked", - "sched.preempted", - "tasks.alloc", - "tasks.alloc.failed", - "tasks.alloc.nomem", - "tasks.free", - "tasks.free.failed", - "tasks.started", - "tasks.started.failed", - "tasks.ended", - "tasks.ended.failed", - "ports.created", - "ports.created.exists", - "ports.created.nomem", - "ports.created.nosig", - "ports.destroyed", - "ports.destroyed.failed", - "ports.find.found", - "ports.find.notfound", - "ports.send", - "ports.send.failed", - "ports.send", - "ports.send.failed", - "ports.get", - "ports.get.failed", - "ports.reply", - "ports.reply.failed", - "ports.wait", - "ports.wait.failed", - "ports.wait.sleep", - "ports.wait.returned.immediate", - "ports.wait.returned.sleep", - "ports.wait.returned.nosig", - "ports.flushed", - "ports.flushed.failed", - "signals.alloc", - "signals.alloc.nosig", - "signals.free", - "signals.wait", - "signals.wait.failed", - "signals.wait.returned.sleep", - "signals.send", - "signals.send.failed", - "signals.send.woke", - NULL -}; - - - -void statistic_init(void) -{ - register u_int32_t i; - - for (i = 0; i < (enum stat_keys)STAT_MAX; i++) - root->stats[i] = 0; -} - - -void stat_dump(void) -{ - register u_int32_t i; - - for (i = 0; i < (enum stat_keys)STAT_MAX; i++) - DEBUG_PRINTF("%u\t%s\n", root->stats[i], stat_strings[i]); -} - - - -#endif diff --git a/Xisop/src/common/kernel/statistic.h b/Xisop/src/common/kernel/statistic.h deleted file mode 100644 index 8024431..0000000 --- a/Xisop/src/common/kernel/statistic.h +++ /dev/null @@ -1,163 +0,0 @@ -/* $Id: statistic.h,v 1.2 2004/01/18 17:43:00 mmondor Exp $ */ - -/* - * Copyright (C) 2003, Matthew Mondor - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Matthew Mondor. - * 4. The name of Matthew Mondor may not be used to endorse or promote - * products derived from this software without specific prior written - * permission. - * 5. Redistribution of source code may not be released under the terms of - * any GNU Public License derivate. - * - * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - - - -#ifndef KERNEL_STATISTIC_H -#define KERNEL_STATISTIC_H - - - -#include - - - -#ifndef STATISTICS - -#define STAT(k, m) - -#else - - - -enum stat_keys { - STAT_MCHUNKS_ATTACH = 0, - STAT_MCHUNKS_ATTACH_FAILED, - STAT_MCHUNKS_DETACH, - STAT_MCHUNKS_DETACH_FAILED, - STAT_PAGES_ALLOC, - STAT_PAGES_ALLOC_NOMEM, - STAT_PAGES_FREE, - STAT_PAGES_FREE_FAILED, - STAT_PAGES_BUFFERED, - STAT_PAGES_UNBUFFERED, - STAT_PAGES_REUSED, - STAT_POOLS_CREATED, - STAT_POOLS_CREATED_FAILED, - STAT_POOLS_ENLARGED, - STAT_POOLS_DESTROYED, - STAT_POOLS_DESTROYED_FAILED, - STAT_POOLS_ALLOC, - STAT_POOLS_ALLOC_FAILED, - STAT_POOLS_ALLOC_NOMEM, - STAT_POOLS_FREE, - STAT_POOLS_FREE_FAILED, - STAT_MPOOLS_CREATED, - STAT_MPOOLS_CREATED_FAILED, - STAT_MPOOLS_DESTROYED, - STAT_MPOOLS_DESTROYED_FAILED, - STAT_MPOOLS_ALLOC, - STAT_MPOOLS_ALLOC_FAILED, - STAT_MPOOLS_ALLOC_NOMEM, - STAT_MPOOLS_FREE, - STAT_MPOOLS_FREE_FAILED, - STAT_SYSCALLS_INVALID, - STAT_SYSCALLS_INVOKED, - STAT_HOOKS_ATTACHED, - STAT_HOOKS_ATTACHED_FAILED, - STAT_HOOKS_ATTACHED_NOMEM, - STAT_HOOKS_DETACHED, - STAT_HOOKS_DETACHED_FAILED, - STAT_HOOKS_DETACHED_NOEXIST, - STAT_HOOKS_EXPIRED, - STAT_HOOKS_EXECUTED, - STAT_HOOKS_SKIPPED, - STAT_FACILITY_DISABLED, - STAT_FACILITY_DISABLED_FAILED, - STAT_FACILITY_ENABLED, - STAT_FACILITY_ENABLED_FAILED, - STAT_FACILITY_EXECUTED, - STAT_FACILITY_EXECUTED_FAILED, - STAT_FACILITY_EXECUTED_LOCKED, - STAT_SCHED_PREEMPTED, - STAT_TASKS_ALLOC, - STAT_TASKS_ALLOC_FAILED, - STAT_TASKS_ALLOC_NOMEM, - STAT_TASKS_FREE, - STAT_TASKS_FREE_FAILED, - STAT_TASKS_STARTED, - STAT_TASKS_STARTED_FAILED, - STAT_TASKS_ENDED, - STAT_TASKS_ENDED_FAILED, - STAT_PORTS_CREATED, - STAT_PORTS_CREATED_EXISTS, - STAT_PORTS_CREATED_NOMEM, - STAT_PORTS_CREATED_NOSIG, - STAT_PORTS_DESTROYED, - STAT_PORTS_DESTROYED_FAILED, - STAT_PORTS_FIND_FOUND, - STAT_PORTS_FIND_NOTFOUND, - STAT_PORTS_SEND, - STAT_PORTS_SEND_FAILED, - STAT_PORTS_GET, - STAT_PORTS_GET_FAILED, - STAT_PORTS_REPLY, - STAT_PORTS_REPLY_FAILED, - STAT_PORTS_WAIT, - STAT_PORTS_WAIT_FAILED, - STAT_PORTS_WAIT_SLEEP, - STAT_PORTS_WAIT_RETURNED_IMMEDIATE, - STAT_PORTS_WAIT_RETURNED_SLEEP, - STAT_PORTS_WAIT_RETURNED_NOSIG, - STAT_PORTS_FLUSHED, - STAT_PORTS_FLUSHED_FAILED, - STAT_SIGNALS_ALLOC, - STAT_SIGNALS_ALLOC_NOSIG, - STAT_SIGNALS_FREE, - STAT_SIGNALS_WAIT, - STAT_SIGNALS_WAIT_FAILED, - STAT_SIGNALS_WAIT_RETURNED_SLEEP, - STAT_SIGNALS_SEND, - STAT_SIGNALS_SEND_FAILED, - STAT_SIGNALS_SEND_WOKE, - STAT_MAX -}; - - - -#define STAT(k, n) root->stats[(enum stat_keys)(k)] += (n) - - - -void statistic_init(void); -void statistic_dump(void); - - - -#endif - - - -#endif diff --git a/Xisop/src/common/kernel/syscall.c b/Xisop/src/common/kernel/syscall.c deleted file mode 100644 index 9df06e8..0000000 --- a/Xisop/src/common/kernel/syscall.c +++ /dev/null @@ -1,204 +0,0 @@ -/* $Id: syscall.c,v 1.2 2004/06/04 02:15:47 mmondor Exp $ */ - -/* - * Copyright (C) 2003, Matthew Mondor - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Matthew Mondor. - * 4. The name of Matthew Mondor may not be used to endorse or promote - * products derived from this software without specific prior written - * permission. - * 5. Redistribution of source code may not be released under the terms of - * any GNU Public License derivate. - * - * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -/* It also would be possible to simply use a structure with all the - * function pointers of the various types... And have the user functions - * refer those with the structure as well, just like for Xisop libraries... - * However, this system is a little more secure for the kernel stack, - * which state is always controled. It also allows to export an ID which - * we can validate. - */ - - - -#include -#include -#include -#include -#include -#include -#include -#include -#include /* XXX */ - - - -static void _sys_getroot(void *, void *); -static void _sys_int_disable(void *, void *); -static void _sys_int_enable(void *, void *); -static void _sys_idle(void *, void *); -static void _sys_custom(void *, void *); - - - -/* Should match with enum in syscall.h */ -static void (*_syscalls[])(void *, void *) = { - _sys_getroot, - _sys_int_disable, - _sys_int_enable, - _sys_idle, - _sys_custom -}; - - - -/* Called by port-specific code before initializing interrupt facilities */ -void syscall_init(void) -{ - root->syscalls = _syscalls; -} - - -/* The following consist of the kernel-side functions which execute in - * supervisor mode. - */ - -/* Syscall trap handler */ -void _scatch(u_int32_t func, void *res, void *args) -{ - if (func < (enum syscalls)SYS_MAX && root->syscalls[func]) - root->syscalls[func](res, args); - else { - STAT(STAT_SYSCALLS_INVALID, 1); - DEBUG_PRINTF("* %T _scatch(%u, %p, %p) - Invalid syscall number\n", - func, res, args); - } - STAT(STAT_SYSCALLS_INVOKED, 1); -} - - -static void _sys_getroot(void *res, void *args) -{ - struct _sres { - struct xisop_root *root; - } *sres = res; - - sres->root = root; -} - - -/* ARGSUSED */ -static void _sys_int_disable(void *res, void *args) -{ - _splhigh(); -} - -/* ARGSUSED */ -static void _sys_int_enable(void *res, void *args) -{ - _spl0(); -} - -/* ARGSUSED */ -static void _sys_idle(void *res, void *args) -{ - _idle(); -} - - -static void _sys_custom(void *res, void *args) -{ - struct _sargs { - void (*func)(void *, void *); - void *args; - } *sargs = args; - - sargs->func(res, sargs->args); -} - - - -/* And here are the functions which are called from userspace to trigger - * system calls. - */ - -/* Returns the Xisop system pointer. This obviously should be used with care */ -struct xisop_root *sys_getroot(void) -{ - struct _sres { - struct xisop_root *root; - } sres; - - _syscall(SYS_GETROOT, &sres, NULL); - - return sres.root; -} - - -/* And these allow to disable all interrupts, which of course also causes - * the scheduler to be disabled. Should however not generally be used as a - * substitute to sched_disable(). - */ -void sys_int_disable(void) -{ - _syscall(SYS_INT_DISABLE, NULL, NULL); -} - -void sys_int_enable(void) -{ - _syscall(SYS_INT_ENABLE, NULL, NULL); -} - -/* Causes the CPU to sleep until the next interrupt occurs */ -void sys_idle(void) -{ - _syscall(SYS_IDLE, NULL, NULL); -} - - -/* Since Xisop does not use MMU, and in now way claims to be secure against - * it's own tasks, other than providing clean facilities, a very useful - * syscall to execute arbitrary code in supervisor mode. If one needs - * unix security, they should be running NetBSD :) - * This allows userspace tasks to extend system calls. The user provided - * function uses the same semantics as Xisop syscall ones. An int value - * can be returned, which will then be returned by sys_custom(), and - * arbitrary data may be written into the supplied results pointer if wanted - * (the first void * argument, which is set to the supplied res pointer). - * The user function can obtain it's arguments from the second void *, - * which is supplied using args. This way possibilities are endless. - */ -void sys_custom(void *res, void (*func)(void *, void *), void *args) -{ - struct _sargs { - void (*func)(void *, void *); - void *args; - } sargs = { - func, - args - }; - - _syscall(SYS_CUSTOM, res, &sargs); -} diff --git a/Xisop/src/common/kernel/syscall.h b/Xisop/src/common/kernel/syscall.h deleted file mode 100644 index 639a0c5..0000000 --- a/Xisop/src/common/kernel/syscall.h +++ /dev/null @@ -1,76 +0,0 @@ -/* $Id: syscall.h,v 1.1 2003/10/26 21:19:57 mmondor Exp $ */ - -/* - * Copyright (C) 2003, Matthew Mondor - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Matthew Mondor. - * 4. The name of Matthew Mondor may not be used to endorse or promote - * products derived from this software without specific prior written - * permission. - * 5. Redistribution of source code may not be released under the terms of - * any GNU Public License derivate. - * - * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - - - -#ifndef KERN_SYSCALL_H -#define KERN_SYSCALL_H - - - -#include -#include -#include - - - -/* Currently available syscalls */ -enum syscalls { - SYS_GETROOT = 0, - SYS_INT_DISABLE, - SYS_INT_ENABLE, - SYS_IDLE, - SYS_CUSTOM, - SYS_MAX -}; - - - -void syscall_init(void); -void _scatch(u_int32_t, void *, void *); - -struct xisop_root *sys_getroot(void); -void sys_int_disable(void); -void sys_int_enable(void); -void sys_idle(void); -void sys_custom(void *, void (*)(void *, void *), void *); - - - -extern void (*_syscalls[])(void *, void *); - - - -#endif diff --git a/Xisop/src/common/kernel/task.c b/Xisop/src/common/kernel/task.c deleted file mode 100644 index 0234078..0000000 --- a/Xisop/src/common/kernel/task.c +++ /dev/null @@ -1,432 +0,0 @@ -/* $Id: task.c,v 1.8 2004/06/04 02:15:47 mmondor Exp $ */ - -/* - * Copyright (C) 2003, Matthew Mondor - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Matthew Mondor. - * 4. The name of Matthew Mondor may not be used to endorse or promote - * products derived from this software without specific prior written - * permission. - * 5. Redistribution of source code may not be released under the terms of - * any GNU Public License derivate. - * - * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - - - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - - - -static void task_startend_code(void); - -static int task_reaper(void *, void *); - - - -task_t *task_alloc(int (*start)(void *, void *), void *res, void *args, - priority_t priority, size_t stacksize, u_int8_t flags) -{ - if (start != NULL && stacksize != 0) { - register page_t *stack = NULL; - register u_int32_t pages; - - pages = stacksize / _PAGE_SIZE; - if (stacksize % _PAGE_SIZE) - pages++; - if ((stack = pages_alloc(0, pages, FALSE)) != NULL) { - register task_t *task = NULL; - - if ((task = (task_t *)spool_alloc(POOL_TASK)) != NULL) { - if ((flags & TF_SHARED) != 0) { - register mpool_t *mpool = CURTASK()->mpool; - - if (OBJECT_VALID(mpool, OBJECT_MPOOL)) { - /* This task will share mpool_t with parent */ - lock_acquire(&mpool->lock); - mpool->shared = TRUE; - mpool->usecount++; - task->mpool = mpool; - _lock_release(&mpool->lock); - } - } else { - /* This task needs it's own unique mpool_t */ - if ((task->mpool = (mpool_t *)spool_alloc(POOL_MPOOL)) - != NULL) { - if (!mpool_init(task->mpool)) - task->mpool = (mpool_t *)spool_free(POOL_MPOOL, - (pnode_t *)task->mpool); - } - } - if (task->mpool != NULL) { - /* Validate task_t object */ - OBJECT_VALIDATE(task, OBJECT_TASK); - /* Initialize other task_t fields */ - task->sleepflags = 0; - task->flags = flags; - task->state = STATE_START; - task->priority = priority; - task->sigalloc = task->sigwait = task->sigrecv = 0; - task->start = start; - task->res = res; - task->args = args; - task->rescode = 0; - task->stack = stack; - task->stacksize = pages * _PAGE_SIZE; - _ctx_init(&task->ctx, (u_int32_t *)stack->address, - task->stacksize, (void *)task_startend_code); - /* Resources */ - DLIST_INIT(&task->resources.ports); - DLIST_INIT(&task->resources.devices); - task->resources.device = NULL; - STAT(STAT_TASKS_ALLOC, 1); - - return task; - } else { - STAT(STAT_TASKS_ALLOC_NOMEM, 1); - DEBUG_PRINTF( - "* %T task_alloc(%p. %p, %p, %d, %u, %x) - mpool_init()\n", - start, res, args, (int32_t)priority, stacksize, - (u_int32_t)flags); - } - spool_free(POOL_TASK, (pnode_t *)task); - } else { - STAT(STAT_TASKS_ALLOC_NOMEM, 1); - DEBUG_PRINTF( - "* %T task_alloc(%p, %p, %p, %d, %u, %x) - Out of memory\n", - start, res, args, (int32_t)priority, stacksize, - (u_int32_t)flags); - } - pages_free(stack); - } else { - STAT(STAT_TASKS_ALLOC_NOMEM, 1); - DEBUG_PRINTF( - "* %T task_alloc(%p, %p, %p, %d, %u, %x) - Out of memory\n", - start, res, args, (int32_t)priority, stacksize, - (u_int32_t)flags); - } - } else { - STAT(STAT_TASKS_ALLOC_FAILED, 1); - DEBUG_PRINTF("* %T task_alloc(%p, %p, %p, %d, %u, %x)\n", - start, res, args, (int32_t)priority, stacksize, - (u_int32_t)flags); - } - - return NULL; -} - - -/* Free a task_t. Also unlinks the task from the system lists. The task - * must first have been processed by task_end() and thus moved into the - * dead queue. - */ -task_t *task_free(task_t *task) -{ - if (OBJECT_VALID(task, OBJECT_TASK) && - (task->state == STATE_DEAD || task->state == STATE_START)) { - SCHED_DISABLE(); - if (task->state == STATE_DEAD) - DLIST_UNLINK(&root->tasks_dead, (node_t *)task); - SCHED_ENABLE(); - if (task->stack != NULL) - pages_free(task->stack); - /* Free task resources */ - /* All ports the task created. Linked via port_t->tasknode. */ - { - register port_t *port, *next; - - for (port = DLIST_TOP(&(task->resources.ports)); port != NULL; - port = next) { - next = DLIST_NEXT(&port->tasknode); - port_destroy((port_t *)&(((hashnode_t *)port)[-1])); - } - } - /* Devices we opened. Linked via device_t->tasknode. */ - { - register node_t *node, *next; - - for (node = DLIST_TOP(&(task->resources.devices)); node != NULL; - node = next) { - next = DLIST_NEXT(node); - device_close((device_t *)&(((pnode_t *)node)[-1])); - } - } - /* If we are a device */ - if (task->resources.device) - device_detach(task->resources.device); - /* XXX other future resources handling, like opened libraries, etc */ - /* Invalidate task */ - OBJECT_INVALIDATE(task); - /* All memory the task allocated using malloc(). If mpool_destroy() - * fails with FALSE, either a problem occured or the mpool_t is still - * in use by another task sharing it, in which case we do not free it. - */ - if (mpool_destroy(task->mpool)) - spool_free(POOL_MPOOL, (pnode_t *)task->mpool); - /* And finally the task node itself. */ - spool_free(POOL_TASK, (pnode_t *)task); - STAT(STAT_TASKS_FREE, 1); - } else { - STAT(STAT_TASKS_FREE_FAILED, 1); - DEBUG_PRINTF("* %T task_free(%p)\n", task); - } - - return NULL; -} - - -/* Allows to start a new task */ -bool task_start(task_t *task) -{ - bool ok = FALSE; - - if (OBJECT_VALID(task, OBJECT_TASK) && task->state == STATE_START) { - /* This task was just allocated, start it */ - task->state = STATE_READY; - if ((task->credits = task->priority) == -128) - task->credits++; - SCHED_DISABLE(); - DLIST_APPEND(&root->tasks_ready, (node_t *)task); - SCHED_ENABLE(); - ok = TRUE; - STAT(STAT_TASKS_STARTED, 1); - } else { - STAT(STAT_TASKS_STARTED_FAILED, 1); - DEBUG_PRINTF("* %T task_start(%p)\n", task); - } - - return ok; -} - - -bool task_end(task_t *task) -{ - bool ok = FALSE, current = FALSE; - - if (OBJECT_VALID(task, OBJECT_TASK) && task->state != STATE_DEAD) { - ok = TRUE; - SCHED_DISABLE(); - switch (task->state) { - case STATE_RUN: - /* FALLTHROUGH */ - case STATE_READY: - DLIST_SWAP(&root->tasks_dead, &root->tasks_ready, (node_t *)task, - FALSE); - break; - case STATE_WAIT: - DLIST_SWAP(&root->tasks_dead, &root->tasks_wait, (node_t *)task, - FALSE); - break; - default: - ok = FALSE; - break; - } - if (ok) { - STAT(STAT_TASKS_ENDED, 1); - task->state = STATE_DEAD; - /* Wakeup reaper as there's at least one task on the dead queue */ - task_wakeup(root->task_reaper, TSF_KERNEL); - /* If it's the current task, ensure to yield now. */ - if (task == root->curtask) - current = TRUE; - } - SCHED_ENABLE(); - } - - if (current) { - /* We also probably could go to sleep indefinitely instead, - * but as we are in the dead queue, we are certain that we will not - * be given another chance to run until we get freed. - */ - for (;;) - _yield(NULL); - } - if (!ok) { - STAT(STAT_TASKS_ENDED_FAILED, 1); - DEBUG_PRINTF("* %T task_end(%p)\n", task); - } - - return ok; -} - - -priority_t task_getpriority(task_t *task) -{ - register priority_t p = 0; - - if (OBJECT_VALID(task, OBJECT_TASK)) - p = task->priority; - - return p; -} - - -priority_t task_setpriority(task_t *task, priority_t new) -{ - register priority_t p = 0; - - if (OBJECT_VALID(task, OBJECT_TASK)) { - SCHED_DISABLE(); - p = task->priority; - task->priority = new; - if ((task->credits = new) == -128) - task->credits++; - SCHED_ENABLE(); - } - - return p; -} - - -/* This is the code which each new task automatically starts executing, which - * performs some initializations and then executes the task-specific code. - * It also takes control again when the task ends and returns. - */ -static void task_startend_code(void) -{ - register task_t *task = CURTASK(); - - /* XXX Setup our reserved signals and port */ - - /* Call the supplied entry point function */ - task->rescode = task->start(task->res, task->args); - - /* Adios amigo! Translation: KTHXBYE!111 */ - task_end(task); - /* NOTREACHED */ -} - - - -/* System tasks - * ============ - */ - -/* This consists of Xisop init task. It's purpose is to launch the system - * tasks as well as all tasks which the port-specific code defined which should - * be launched. We also link in system and port-defined shared libraries. - * Resident devices and handlers actually consist of tasks and are given - * no special treatment. - */ -/* ARGSUSED */ -int task_init(void *res, void *args) -{ - /* Register ourself */ - root->task_init = CURTASK(); - - /* Attach system shared libraries */ - { - /* XXX */ - } - - /* Launch system tasks, which auto-register themselves by storeing their - * address in the Xisop root structure. - */ - { - register task_t *task; - - /* Task reaper */ - if ((task = task_alloc(task_reaper, NULL, NULL, 0, 4096, - TF_KERNEL | TF_SHARED)) != NULL) - task_start(task); - } - - /* Attach port-specified shared libraries and launch port-specific tasks */ - _port_init(); - - /* And perform our init task shores, which are neverending. */ - { - port_t *pubport; - - if ((pubport = port_create("INIT")) != NULL) { - port_t *ports[] = { - pubport - }; - register message_t *msg; - - /* Currently just reply to any message we get and do nothing */ - for (;;) { - if ((port_wait(ports, 1, NULL)) == pubport) { - while ((msg = port_get(pubport)) != NULL) - port_reply(msg); - } - } - } - } - - /* XXX We actually could safely die here for now, but don't want to. - * Eventually we will make sure that the tasks remain running, and - * restart tasks which are marked to be persistant if they ever die. - */ - /* NOTREACHED */ - return 0; -} - - -/* This consists of the reaper task, which is started by Xisop init task. - * Our duty consists of sleeping, but to free the tasks which are on the - * dead queue, if any, when wakeing up. The only event which awakes us up - * consists of the task_end() function. As tasks may have alot of resources - * to free back to the system, it is a good idea to have this task dedicating - * it's own CPU time to do it, it releives all other tasks, as well as the - * kernel from having to. - */ -/* ARGSUSED */ -static int task_reaper(void *res, void *args) -{ - /* Register ourselves for task_end() */ - root->task_reaper = CURTASK(); - - for (;;) { - task_sleep(CURTASK(), TSF_KERNEL, NULL); - /* We were awaken by task_end() */ - for (;;) { - register task_t *task; - - SCHED_DISABLE(); - task = DLIST_TOP(&root->tasks_dead); - SCHED_ENABLE(); - if (task != NULL) { - /* Free this task and let some CPU time to others */ - task_free(task); - _yield(NULL); - } else - break; - } - } - - /* NOTREACHED */ - return 0; -} diff --git a/Xisop/src/common/kernel/task.h b/Xisop/src/common/kernel/task.h deleted file mode 100644 index ea9f069..0000000 --- a/Xisop/src/common/kernel/task.h +++ /dev/null @@ -1,164 +0,0 @@ -/* $Id: task.h,v 1.2 2004/01/19 18:07:11 mmondor Exp $ */ - -/* - * Copyright (C) 2003, Matthew Mondor - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Matthew Mondor. - * 4. The name of Matthew Mondor may not be used to endorse or promote - * products derived from this software without specific prior written - * permission. - * 5. Redistribution of source code may not be released under the terms of - * any GNU Public License derivate. - * - * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - - - -#ifndef KERNEL_TASK_H -#define KERNEL_TASK_H - - - -#include -#include -#include -#include -#include -#include -#include -#include -/*#include XXX I must be able to include this!*/ - - - -/* Useful macro to be used in kernel code */ -#define CURTASK() (root->curtask) - - - -/* task.state */ -#define STATE_START 0 -#define STATE_READY 1 -#define STATE_WAIT 2 -#define STATE_DEAD 3 /* To be removed */ -#define STATE_RUN 4 - -/* task.flags and tn_message.event */ -#define TF_KERNEL (1 << 0) /* Kernelspace task */ -#define TF_SYSTEM (1 << 1) /* A system task */ -#define TF_DEVICE (1 << 2) /* Task is a device */ -#define TF_HANDLER (1 << 3) /* Task is a handler */ -#define TF_OS (1 << 4) /* OS resident task */ -#define TF_SHARED (1 << 5) /* Shares mpool_t w/ parent */ - -/* some defined values for task.priority */ -#define PRI_MAX 127 /* highest priority */ -#define PRI_DEFAULT 0 /* default priority for normal tasks */ -#define PRI_MIN -127 /* lowest priority */ - - - -/* Task's allocated resources */ -struct resources { - list_t ports; /* Linked via port_t->tasknode */ - list_t devices; /* Linked via device_t->tasknode */ - devicenode_t *device; /* If we are a device */ - /*handlernode_t *handler;*/ /* If we are a handler */ -}; - -/* XXX Don't know if this will be useful/required yet */ -/* These, similarly to the task's memory pool, will only be freed once the - * parent task is freed, in case it has threads. - * XXX hmm I think that the message ports cannot be shared among threads, since - * they require a signal bit, allocated on the specific task! - * now would signal shareing be wanted? several tasks would then be awaken - * by the same signal. I doubt we want this on xisop. - */ -/* -struct procstate { - lock *currentdir; - lock *in; - lock *out; - lock *err; - struct handlerpacket pkt; - struct msgport *pktrp; -}; -*/ - -/* This is a task node, as the kernel sees it. */ -struct task { - /* Tasks use the primary node for swapping */ - pnode_t node; - /* User multipurpose node */ - node_t usernode; - - /* Validity sceal */ - u_int32_t object_magic; - - /* Info */ - u_int32_t sleepflags; - u_int8_t flags; - u_int8_t state; - - /* Credits are given according to priority by the scheduler */ - priority_t priority, credits; - - /* Signal */ - sigmask_t sigalloc, sigwait, sigrecv; - /* XXX sigfunc sighandlers[32]; */ - - /* Entry point and parameters/results */ - int (*start)(void *, void *); - void *res, *args; - int rescode; - - /* Context */ - page_t *stack; - size_t stacksize; - _ctx_t ctx; - - /* Memory pool, which can be shared or unique */ - mpool_t *mpool; - - /* Resources we have opened which need special handling other than - * freeing the memory they allocated. - */ - struct resources resources; -}; - - - -task_t *task_alloc(int (*)(void *, void *), void *, void *, priority_t, - size_t, u_int8_t); -task_t *task_free(task_t *); -bool task_start(task_t *); -bool task_end(task_t *); -priority_t task_getpriority(task_t *); -priority_t task_setpriority(task_t *, priority_t); - -int task_init(void *, void *); - - - -#endif diff --git a/Xisop/src/common/kernlib/clean.sh b/Xisop/src/common/kernlib/clean.sh deleted file mode 100755 index c8bd473..0000000 --- a/Xisop/src/common/kernlib/clean.sh +++ /dev/null @@ -1,9 +0,0 @@ -#!/bin/sh - -# $Id: clean.sh,v 1.1 2003/10/26 21:19:57 mmondor Exp $ - -. ../../generic_makedefs.sh - -cleanlib . -cleanlib string -show $L_RM ar/*.a diff --git a/Xisop/src/common/kernlib/fifo.h b/Xisop/src/common/kernlib/fifo.h deleted file mode 100644 index 3e4cc71..0000000 --- a/Xisop/src/common/kernlib/fifo.h +++ /dev/null @@ -1,165 +0,0 @@ -/* $Id: fifo.h,v 1.2 2004/06/04 19:03:36 mmondor Exp $ */ - -/* - * Copyright (C) 2003, Matthew Mondor - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Matthew Mondor. - * 4. The name of Matthew Mondor may not be used to endorse or promote - * products derived from this software without specific prior written - * permission. - * 5. Redistribution of source code may not be released under the terms of - * any GNU Public License derivate. - * - * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - - - -#ifndef KERNLIB_FIFO_H -#define KERNLIB_FIFO_H - - - -#include - - - -/* Allows to create a new fifo_t type structure, to fit any data type */ -#define FIFO_DEFINE(n, o) typedef struct n { \ - o *top, *bottom, *head, *tail; \ - u_int32_t size; \ -} n - -FIFO_DEFINE(fifo8_t, u_int8_t); -FIFO_DEFINE(fifo16_t, u_int16_t); -FIFO_DEFINE(fifo32_t, u_int32_t); -FIFO_DEFINE(fifo64_t, u_int64_t); - - - -/* Initializes a FIFO */ -#define FIFO_INIT(f, b, s) do { \ - (f)->top = (f)->head = (f)->tail = (b); \ - (f)->bottom = &((b)[(s)]); \ - (f)->size = (s) - 1; \ -} while (/* CONSTCOND */0) - -/* Used to compute the next location of a tail or head pointer, accounting - * for the necessary occasional rotation. - */ -#define FIFO_NEXT(f, p) (&((p)[1]) == (f)->bottom ? (f)->top : &((p)[1])) - -/* Returns TRUE if the FIFO is full, that is, cannot hold more elements */ -#define FIFO_FULL(f) (FIFO_NEXT((f), (f)->head) == (f)->tail) - -/* Returns TRUE if the FIFO is empty */ -#define FIFO_EMPTY(f) ((f)->head == (f)->tail) - -/* Returns the number of currently held elements into a FIFO */ -#define FIFO_STAT(f) (FIFO_EMPTY(f) ? 0 : \ - (f)->head - (f)->tail > 0 ? \ - ((f)->head - (f)->tail) / sizeof(*(f)->head): \ - ((f)->size - ((f)->tail - (f)->head)) / sizeof(*(f)->head)) - -#define FIFO_AVAIL(f) ((f)->size - FIFO_STAT(f)) - -#define FIFO_FLUSH(f) ((f)->tail = (f)->head) - -/* If no available room the oldest element is lost. The caller may verify - * with FIFO_FULL() first if needed. - */ -#define FIFO_PUT(f, e) do { \ - *(f)->head = *(e); \ - (f)->head = FIFO_NEXT((f), (f)->head); \ - if (FIFO_EMPTY(f)) \ - (f)->tail = FIFO_NEXT((f), (f)->tail); \ -} while (/* CONSTCOND */0) - -/* Has no action if the buffer has no more elements, but does not return any - * result to say so. The caller may use FIFO_EMPTY() to check if needed. - */ -#define FIFO_GET(f, e) do { \ - if (!FIFO_EMPTY(f)) { \ - *(e) = *(f)->tail; \ - (f)->tail = FIFO_NEXT((f), (f)->tail); \ - } \ -} while (/* CONSTCOND */0) - -#define FIFO_FIND(f, p, e) do { \ - register typeof(*(e)) *r; \ - \ - *(p) = NULL; \ - for (r = (f)->tail; r != (f)->head; r = FIFO_NEXT(f, r)) \ - if (*r == *(e)) { \ - *(p) = r; \ - break; \ - } \ -} while (/* CONSTCOND */0) - -/* XXX Those are bugged for now */ -#define FIFO_ALLOC(f, p, a, s) do { \ - register int r; \ - \ - if ((r = (f)->tail - (f)->head) != 0) { \ - if (r < 1) \ - r = (f)->bottom - (f)->head; \ - if ((r /= sizeof(*(f)->head)) > (int)(s)) \ - r = (int)(s); \ - *(p) = (f)->head; \ - (f)->head = (&((f)->head[r]) == (f)->bottom ? (f)->top : \ - &((f)->head[r])); \ - } \ - *(a) = (size_t)r; \ -} while (/* CONSTCOND */0) - -#define FIFO_FREE(f, p, a, s) do { \ - register int r; \ - \ - if ((r = (f)->head - (f)->tail) != 0) { \ - if (r < 1) \ - r = (f)->bottom - (f)->tail; \ - if ((r /= sizeof(*(f)->tail)) > (int)(s)) \ - r = (int)(s); \ - *(p) = (f)->tail; \ - (f)->tail = (&((f)->tail[r]) == (f)->bottom ? (f)->top : \ - &((f)->tail[r])); \ - } \ - *(a) = (size_t)r; \ -} while (/* CONSTCOND */0) - -#define FIFO_WRITE(f, p, a, s) do { \ - /* XXX */ \ -} while (/* CONSTCOND */0) - -#define FIFO_READ(f, p, a, s) do { \ - /* XXX */ \ -} while (/* CONSTCOND */0) - - - -size_t getnfifo8(u_int8_t *, fifo8_t *, size_t); -size_t putnfifo8(fifo8_t *, u_int8_t *, size_t); - - - -#endif diff --git a/Xisop/src/common/kernlib/hash.c b/Xisop/src/common/kernlib/hash.c deleted file mode 100644 index c652405..0000000 --- a/Xisop/src/common/kernlib/hash.c +++ /dev/null @@ -1,381 +0,0 @@ -/* $Id: hash.c,v 1.6 2004/06/04 02:15:47 mmondor Exp $ */ - -/* - * Copyright (C) 2003, Matthew Mondor - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software written by Matthew Mondor. - * 4. The name of Matthew Mondor may not be used to endorse or promote - * products derived from this software without specific prior written - * permission. - * 5. Redistribution of source code may not be released under the terms of - * any GNU Public License derivate. - * - * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - - - -#include -#include -#include -#include -#include -#include - - - -/* This system is safe to use 32-bit hashes internally, despite the possibility - * for collisions. We maintain an array or buckets, within which the entries - * are distributed. A 32-bit hash collision entry will end up on the same - * bucket. We however also make sure to not allow to store exact duplicate - * keys. The number if buckets will increase and decrease whenever the system - * detects that it becomes necessary for efficiency. The larger the number of - * buckets, the less nodes are likely to coexist in each bucket. The bucket - * index to use is evaluated using a modulo to convert the 32-bit hash to - * fit into the current number of buckets in the table. Of course, when - * the number of buckets is to be updated (which ideally happens rarely), - * the entries are rehashed to be properly indexed within the new capacity. - * - * The number of buckets is automatically doubled when the table fills up - * at a factor of 1. This way, we avoid having to calculate a fill factor - * using floating point arithmetic. Commonly used value for the initial hash - * table bucket capacity is 16, which are set HT_DEFAULT_CAPACITY represents. - * - * Searching for a key by pattern, which requires iterating through the - * nodes, rather than through it's absolute key can actually be a little - * slower than running through a simple linked list of absolute hash values, - * since all buckets must be scanned. However, we ensure to stop running - * through buckets when the total number of mappings have been scanned already. - * Lookups using the absolute key of the node will be much faster in the case - * where the hash table grows considerably, however. - */ - - - -#define HASH_INDEX(h, s) ((h) % (s)) - - - -static void hashtable_rehash(hashtable_t *, unsigned int); - - - -bool hashtable_init(hashtable_t *t, const char *label, - unsigned int initialcapacity, void *(*allocfunc)(size_t), - void (*freefunc)(void *), - int (*keycomp)(const void *, const void *, size_t), - u_int32_t (*keyhash)(const void *, size_t), bool dynamic) -{ - if (!OBJECT_VALID(t, OBJECT_HASHTABLE)) { - if ((t->array = allocfunc(sizeof(list_t) * initialcapacity)) != NULL) { - unsigned int i; - - OBJECT_VALIDATE(t, OBJECT_HASHTABLE); - t->label = label; - t->malloc = allocfunc; - t->free = freefunc; - t->keycomp = keycomp; - t->keyhash = keyhash; - t->nodes = 0; - t->initial = t->capacity = initialcapacity; - t->avgtotal = t->avgcnt = initialcapacity; - t->dynamic = dynamic; - t->iterating = FALSE; - for (i = 0; i < initialcapacity; i++) - DLIST_INIT(&(t->array[i])); - - return TRUE; - } else - DEBUG_PRINTF("* %T hashtable_init(%p = %s) - malloc(%d)\n", - label, t, (int)sizeof(list_t) * initialcapacity); - } else - DEBUG_PRINTF( - "* %T hashtable_init(%p = %s) - Table already initialized", - label, t); - - return FALSE; -} - - -void hashtable_destroy(hashtable_t *t, bool freeall) -{ - if (OBJECT_VALID(t, OBJECT_HASHTABLE)) { - if (t->array != NULL) { - if (freeall) { - register unsigned int i, done; - register list_t *l; - register hashnode_t *k, *kt; - - for (i = done = 0; done < t->nodes && i < t->capacity; i++) { - l = &(t->array[i]); - if (DLIST_NODES(l) > 0) { - for (k = DLIST_TOP(l); k != NULL; k = kt) { - kt = DLIST_NEXT(k); - pool_free((pnode_t *)k); - done++; - } - } - } - } - t->free(t->array); - } - OBJECT_INVALIDATE(t); - } else - DEBUG_PRINTF( - "* %T hashtable_destroy(%p) - Invalid hashtable_t pointer", - t); -} - - -hashnode_t *hashtable_lookup(hashtable_t *t, const void *key, size_t keysize) -{ - register u_int32_t hash; - register unsigned int i; - register list_t *l; - register hashnode_t *k = NULL; - - if (OBJECT_VALID(t, OBJECT_HASHTABLE)) { - hash = t->keyhash(key, keysize); - i = HASH_INDEX(hash, t->capacity); - l = &(t->array[i]); - if (DLIST_NODES(l) > 0) { - DLIST_FOREACH(l, k) { - if (k->hash == hash && k->keysize == keysize && - t->keycomp(k->key, key, keysize) == 0) - break; - } - } - } else - DEBUG_PRINTF( - "* %T hashtable_lookup(%p) - Invalid hashtable_t pointer", - t); - - return k; -} - - -bool hashtable_link(hashtable_t *t, hashnode_t *k, const void *key, - size_t keysize, bool check) -{ - register u_int32_t hash; - register unsigned int i; - register list_t *l; - bool ok = TRUE; - - if (!OBJECT_VALID(t, OBJECT_HASHTABLE)) { - DEBUG_PRINTF("* %T hashtable_link(%p) - Invalid hashtable_t pointer", - t); - return FALSE; - } - if (k == NULL) { - DEBUG_PRINTF( - "* %T hashtable_link(NULL) - Table (%p = %s) Invalid " - "hashnode_t pointer", - t, t->label); - return FALSE; - } - - hash = t->keyhash(key, keysize); - i = HASH_INDEX(hash, t->capacity); - l = &(t->array[i]); - if (check) { - /* We do not allow exact duplicates, so verify first. Duplicate - * hashes are fine, however, as long as the key data is not identical. - */ - if (DLIST_NODES(l) > 0) { - register hashnode_t *tk; - - DLIST_FOREACH(l, tk) { - if (tk == k || (tk->hash == hash && tk->keysize == keysize && - t->keycomp(tk->key, key, keysize) == 0)) { - DEBUG_PRINTF( - "* %T hashtable_link(%p = %s, %p) - Duplicate key " - "insert attempt", t, t->label, k); - ok = FALSE; - break; - } - } - } - } - if (ok) { - OBJECT_VALIDATE(k, OBJECT_HASHNODE); - k->hash = hash; - k->list = l; - k->key = key; - k->keysize = keysize; - DLIST_INSERT(l, (node_t *)k); - /* Grow capacity if necessary */ - t->nodes++; - if (t->dynamic && !t->iterating) { - if (t->dynamic && !t->iterating) - hashtable_rehash(t, t->capacity * 2); - } - } - - return ok; -} - - -void hashtable_unlink(hashtable_t *t, hashnode_t *k) -{ - if (OBJECT_VALID(t, OBJECT_HASHTABLE)) { - if (OBJECT_VALID(k, OBJECT_HASHNODE)) { - unsigned int exceeding; - - OBJECT_INVALIDATE(k); - DLIST_UNLINK(k->list, (node_t *)k); - k->list = NULL; - t->nodes--; - - /* Verify if the capacity should be reduced, using statistics */ - t->avgtotal += t->capacity; - t->avgcnt++; - if (t->avgcnt > t->capacity / (t->initial * 3)) { - t->avgcnt = 1; - t->avgtotal = t->capacity; - } - /* Rehash with a smaller capacity if necessary */ - if (t->dynamic && !t->iterating) { - if ((exceeding = t->capacity - (t->avgtotal / t->avgcnt)) > 0) - hashtable_rehash(t, t->capacity - exceeding); - } - } else - DEBUG_PRINTF( - "* %T hashtable_unlink(%p) - Table (%p = %s) Invalid " - "hashnode_t pointer", - k, t, t->label); - } else - DEBUG_PRINTF( - "* %T hashtable_unlink(%p) - Invalid hashtable_t pointer", - t); -} - - -/* Note that as the user generally has a pool_t dedicated to the hashnode_t - * elements for a particular table, it may be more efficient to not use - * the option and to pool_free() which does not need to iterate - * through nodes. This function has to however, because it obviously cannot - * assume that the caller wishes to free all nodes of the origin pool_t, or - * that all entries origin from the same pool_t. - */ -void hashtable_empty(hashtable_t *t, bool freeall) -{ - register unsigned int i; - register list_t *l; - register hashnode_t *k, *kt; - - if (OBJECT_VALID(t, OBJECT_HASHTABLE)) { - if (freeall) { - for (i = 0; i < t->capacity; i++) { - l = &(t->array[i]); - if (DLIST_NODES(l) > 0) { - for (k = DLIST_TOP(l); k != NULL; k = kt) { - kt = DLIST_NEXT(k); - pool_free((pnode_t *)k); - } - } - } - } else { - for (i = 0; i < t->capacity; i++) - DLIST_INIT(&(t->array[i])); - } - if (t->dynamic && !t->iterating) - hashtable_rehash(t, t->initial); - } else - DEBUG_PRINTF( - "* %T hashtable_empty(%p) - Invalid hashtable_t pointer", - t); -} - - -void hashtable_iterate(hashtable_t *t, - bool (*func)(hashnode_t *, void *), void *udata) -{ - register unsigned int i; - register list_t *l; - register hashnode_t *k, *kt; - - if (OBJECT_VALID(t, OBJECT_HASHTABLE)) { - if (t->nodes > 0) { - t->iterating = TRUE; - for (i = 0; i < t->capacity; i++) { - l = &(t->array[i]); - if (DLIST_NODES(l) > 0) { - /* Note that we use a temporary variable to hold the next - * key, in case the user function alters the key node - * (i.e. unlinks it) - */ - for (k = DLIST_TOP(l); k != NULL; k = kt) { - kt = DLIST_NEXT(k); - if (!func(k, udata)) { - t->iterating = FALSE; - return; - } - } - } - } - t->iterating = FALSE; - } - } else - DEBUG_PRINTF( - "* %T hashtable_iterate(%p) - Invalid hashtable_t pointer", - t); -} - - -/* Rehashes the whole hashtable so that the capacity may be changed to the - * specified one. The memory area is also automatically changed. Ideally, - * this only occurs rarely. If it fails because of a lack of memory, the - * hash table will simply not be affected, but lookups will become slower. - */ -static void hashtable_rehash(hashtable_t *t, unsigned int newcapacity) -{ - list_t *newarray; - - if ((newarray = t->malloc(sizeof(list_t) * newcapacity)) != NULL) { - register unsigned int i, done; - - for (i = 0; i < newcapacity; i++) - DLIST_INIT(&newarray[i]); - - for (i = done = 0; done < t->nodes && i < t->capacity; i++) { - register hashnode_t *k, *kt; - register list_t *l, *newl; - - l = &(t->array[i]); - if (DLIST_NODES(l) > 0) { - for (k = DLIST_TOP(l); k != NULL; k = kt) { - kt = DLIST_NEXT(k); - newl = &newarray[HASH_INDEX(k->hash, newcapacity)]; - DLIST_SWAP(newl, l, (node_t *)k, TRUE); - k->list = newl; - done++; - } - } - } - - t->capacity = newcapacity; - t->free(t->array); - t->array = newarray; - } -} diff --git a/Xisop/src/common/kernlib/hash.h b/Xisop/src/common/kernlib/hash.h deleted file mode 100644 index ed04553..0000000 --- a/Xisop/src/common/kernlib/hash.h +++ /dev/null @@ -1,96 +0,0 @@ -/* $Id: hash.h,v 1.5 2004/06/04 02:15:47 mmondor Exp $ */ - -/* - * Copyright (C) 2003, Matthew Mondor - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Matthew Mondor. - * 4. The name of Matthew Mondor may not be used to endorse or promote - * products derived from this software without specific prior written - * permission. - * 5. Redistribution of source code may not be released under the terms of - * any GNU Public License derivate. - * - * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - - - -#ifndef KERNLIB_HASH_H -#define KERNLIB_HASH_H - - - -#include -#include -#include - - - -struct hashnode { - pnode_t node; - u_int32_t object_magic, hash; - list_t *list; - const void *key; - size_t keysize; - /* Custom user data will follow, uncluding the key element to which the - * previous key pointer is expected to point. - */ -}; - -struct hashtable { - pnode_t node; /* In case we want a pool_t of hashtable_t */ - u_int32_t object_magic; - unsigned int initial, capacity, nodes; - const char *label; - list_t *array; - void *(*malloc)(size_t); - void (*free)(void *); - int (*keycomp)(const void *, const void *, size_t); - u_int32_t (*keyhash)(const void *, size_t); - unsigned int avgtotal, avgcnt; - bool dynamic, iterating; -}; - - - -#define HT_DEFAULT_CAPACITY 16 - -#define HASHTABLE_NODES(t) ((t)->nodes) - - - -bool hashtable_init(hashtable_t *, const char *, unsigned int, - void *(*)(size_t), void (*)(void *), - int (*)(const void *, const void *, size_t), - u_int32_t (*)(const void *, size_t), bool); -void hashtable_destroy(hashtable_t *, bool); -hashnode_t *hashtable_lookup(hashtable_t *, const void *, size_t); -bool hashtable_link(hashtable_t *, hashnode_t *, const void *, size_t, bool); -void hashtable_unlink(hashtable_t *, hashnode_t *); -void hashtable_empty(hashtable_t *, bool); -void hashtable_iterate(hashtable_t *, bool (*)(hashnode_t *, void *), - void *); - - - -#endif diff --git a/Xisop/src/common/kernlib/lifo.h b/Xisop/src/common/kernlib/lifo.h deleted file mode 100644 index ae9e260..0000000 --- a/Xisop/src/common/kernlib/lifo.h +++ /dev/null @@ -1,107 +0,0 @@ -/* $Id: lifo.h,v 1.3 2004/06/04 19:14:07 mmondor Exp $ */ - -/* - * Copyright (C) 2003, Matthew Mondor - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Matthew Mondor. - * 4. The name of Matthew Mondor may not be used to endorse or promote - * products derived from this software without specific prior written - * permission. - * 5. Redistribution of source code may not be released under the terms of - * any GNU Public License derivate. - * - * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - - - -#ifndef KERNLIB_LIFO_H -#define KERNLIB_LIFO_H - - - -#include - - - -/* LIFO_DEFINE(lifotypename, objecttype); */ -#define LIFO_DEFINE(n, o) typedef struct n { \ - u_int32_t size, elements; \ - o *buffer, *endbuffer, *head; \ -} n - -/* Because of the way this is implemented using macros, it would also be - * possible to provide lifo types to hold structures. - */ -LIFO_DEFINE(lifo8_t, u_int8_t); -LIFO_DEFINE(lifo16_t, u_int16_t); -LIFO_DEFINE(lifo32_t, u_int32_t); -LIFO_DEFINE(lifo64_t, u_int64_t); - - - -/* XXX Although it's great to use macros for these operations, it also - * prevents assembly functions to be provided to replace them. - */ -/* void LIFO_INIT(lifo*_t *, u_int*_t *, u_int32_t); */ -#define LIFO_INIT(f, b, s) do { \ - (f)->size = (s); \ - (f)->elements = 0; \ - (f)->buffer = (f)->endbuffer = (f)->head = (b); \ -} while (/* CONSTCOND */0) - -/* bool LIFO_FULL(lifo*_t *); */ -#define LIFO_FULL(f) ((f)->elements == (f)->size) - -/* u_int32_t LIFO_STAT(lifo*_t *); */ -#define LIFO_STAT(f) ((f)->elements) - -/* void LIFO_FLUSH(lifo*_t *); */ -#define LIFO_FLUSH(f) do { \ - (f)->head = (f)->buffer; \ - (f)->elements = 0; \ -} while (/* CONSTCOND */0) - -/* void LIFO_PUT(lifo*_t *, u_int*_t *); */ -#define LIFO_PUT(s, e) do { \ - if ((s)->elements < (s)->size) { \ - *((s)->head++) = *(e); \ - (s)->elements++; \ - } \ -} while (/* CONSTCOND */0) - -/* void LIFO_GET(lifo*_t *, u_int*_t *); */ -#define LIFO_GET(s, e) do { \ - if ((s)->elements > 0) { \ - *(e) = *(--(s)->head); \ - (s)->elements--; \ - } \ -} while (/* CONSTCOND */0) - -/* LIFO_ALLOC(lifo*_t *, u_int*_t **, size_t *); */ - -/* LIFO_FREE(lifo*_t, u_int*_t **, size_t *); */ - - - -#endif diff --git a/Xisop/src/common/kernlib/list.h b/Xisop/src/common/kernlib/list.h deleted file mode 100644 index ebb8bd1..0000000 --- a/Xisop/src/common/kernlib/list.h +++ /dev/null @@ -1,178 +0,0 @@ -/* $Id: list.h,v 1.5 2004/06/04 19:14:07 mmondor Exp $ */ - -/* - * Copyright (C) 2003, Matthew Mondor - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Matthew Mondor. - * 4. The name of Matthew Mondor may not be used to endorse or promote - * products derived from this software without specific prior written - * permission. - * 5. Redistribution of source code may not be released under the terms of - * any GNU Public License derivate. - * - * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - - - -#ifndef KERNEL_LIST_H -#define KERNEL_LIST_H - - - -#include - - - -typedef struct list list_t; -typedef struct node node_t; - - - -struct node { - node_t *prev, *next; -}; - -struct list { - node_t *top, *bottom; - u_int32_t nodes; -}; - - - -/* Some macros to optimize operations on doubly linked lists */ -#define DLIST_INITIALIZER {NULL, NULL, 0} - -#define DLIST_INIT(lst) do { \ - (lst)->top = (lst)->bottom = NULL; \ - (lst)->nodes = 0; \ -} while (/* CONSTCOND */0) - -#define DLIST_UNLINK(lst, nod) do { \ - register node_t *prev = (nod)->prev, *next = (nod)->next; \ - \ - if (prev != NULL) \ - prev->next = next; \ - else \ - (lst)->top = next; \ - if (next != NULL) \ - next->prev = prev; \ - else \ - (lst)->bottom = prev; \ - (lst)->nodes--; \ -} while (/* CONSTCOND */0) - -#define DLIST_APPEND(lst, nod) do { \ - register node_t *tmp = (lst)->bottom; \ - \ - if (tmp != NULL) { \ - tmp->next = (nod); \ - (nod)->prev = tmp; \ - (nod)->next = NULL; \ - (lst)->bottom = (nod); \ - } else { \ - (lst)->bottom = (lst)->top = (nod); \ - (nod)->next = (nod)->prev = NULL; \ - } \ - (lst)->nodes++; \ -} while (/* CONSTCOND */0) - -#define DLIST_INSERT(lst, nod) do { \ - register node_t *tmp = (lst)->top; \ - \ - if (tmp != NULL) { \ - tmp->prev = (nod); \ - (nod)->prev = NULL; \ - (nod)->next = tmp; \ - (lst)->top = (nod); \ - } else { \ - (lst)->top = (lst)->bottom = (nod); \ - (nod)->next = (nod)->prev = NULL; \ - } \ - (lst)->nodes++; \ -} while (/* CONSTCOND */0) - -#define DLIST_INSERTAT(lst, atnode, nod) do { \ - register node_t *prev = (atnode)->prev, *next = (atnode); \ - \ - (nod)->next = next; \ - next->prev = (nod); \ - if (prev != NULL) { \ - prev->next = (nod); \ - (nod)->prev = prev; \ - } else { \ - (lst)->top = (nod); \ - (nod)->prev = NULL; \ - } \ - (lst)->nodes++; \ -} while (/* CONSTCOND */0) - -#define DLIST_SWAP(dst, src, nod, ins) do { \ - register node_t *prev = (nod)->prev, *next = (nod)->next; \ - \ - if (prev != NULL) \ - prev->next = next; \ - else \ - (src)->top = next; \ - if (next != NULL) \ - next->prev = prev; \ - else \ - (src)->bottom = prev; \ - (src)->nodes--; \ - if ((ins)) { \ - if ((prev = (dst)->top) != NULL) { \ - prev->prev = (nod); \ - (nod)->prev = NULL; \ - (nod)->next = prev; \ - (dst)->top = (nod); \ - } else { \ - (dst)->top = (dst)->bottom = (nod); \ - (nod)->next = (nod)->prev = NULL; \ - } \ - } else { \ - if ((prev = (dst)->bottom) != NULL) { \ - prev->next = (nod); \ - (nod)->prev = prev; \ - (nod)->next = NULL; \ - (dst)->bottom = (nod); \ - } else { \ - (dst)->bottom = (dst)->top = (nod); \ - (nod)->next = (nod)->prev = NULL; \ - } \ - } \ - (dst)->nodes++; \ -} while (/* CONSTCOND */0) - -#define DLIST_TOP(lst) ((void *)((list_t *)(lst))->top) -#define DLIST_BOTTOM(lst) ((void *)((list_t *)(lst))->bottom) -#define DLIST_NEXT(var) ((void *)((node_t *)(var))->next) -#define DLIST_PREV(var) ((void *)((node_t *)(var))->prev) - -#define DLIST_FOREACH(lst, var) \ - for ((var) = DLIST_TOP((lst)); (var) != NULL; (var) = DLIST_NEXT((var))) - -#define DLIST_NODES(lst) (((list_t *)(lst))->nodes) - - - -#endif diff --git a/Xisop/src/common/kernlib/make.sh b/Xisop/src/common/kernlib/make.sh deleted file mode 100755 index 0bf7318..0000000 --- a/Xisop/src/common/kernlib/make.sh +++ /dev/null @@ -1,13 +0,0 @@ -#!/bin/sh - -# $Id: make.sh,v 1.1 2003/10/26 21:19:57 mmondor Exp $ - -. ../../makedefs.sh - -buildlib string -show $C_AR ar/string.a string/*.o -show $C_RANLIB ar/string.a - -buildlib . -show $C_AR ar/kernlib.a *.o -show $C_RANLIB ar/kernlib.a diff --git a/Xisop/src/common/kernlib/rand.c b/Xisop/src/common/kernlib/rand.c deleted file mode 100644 index f2071fe..0000000 --- a/Xisop/src/common/kernlib/rand.c +++ /dev/null @@ -1,108 +0,0 @@ -/* $Id: rand.c,v 1.2 2004/01/29 04:56:50 mmondor Exp $ */ - -/* - * Copyright (C) 2001-2003, Matthew Mondor - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software written by Matthew Mondor. - * 4. The name of Matthew Mondor may not be used to endorse or promote - * products derived from this software without specific prior written - * permission. - * 5. Redistribution of source code may not be released under the terms of - * any GNU Public License derivate. - * - * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -/* Algorithm was borrowed from: - * Compute x[n + 1] = (7^5 * x[n]) mod (2^31 - 1). - * "Random number generators: good ones are hard to find", - * Park and Miller, Communications of the ACM, vol. 31, no. 10, - * October 1988, p. 1195. - * - * The 10,000nth invokation with default initial seed of 1 should result - * in 1043618065. Of course, this is a highly predictable algorithm, but - * it is rather well distributed, while also being quite fast, and is thus - * suitable in the implementation of ANSI/C89 rand(3)/srand(3) functions. - * Do NOT use for cryptography related work. - * Matt - */ - - - -#include -#include - - - -static unsigned int global_seed = 1; - - - -int rand(void) -{ - /* - register int a, b; - - a = b = (signed int)global_seed; - a /= 127773; - b %= 127773; - - b *= 16807; - a *= 2836; - b -= a; - if (b <= 0) - b += 0x7fffffff; - - global_seed = b; - - return b; - */ - - int v; - - if ((v = (global_seed % 127773 * 16807) - - (global_seed / 127773 * 2836)) < 1) - v += 0x7fffffff; - global_seed = v; - - return v; -} - - -void srand(unsigned int seed) -{ - global_seed = seed; -} - - -/* This is the POSIX reentrant variant where caller supplies seed */ -int rand_r(unsigned int *seed) -{ - int v; - - if ((v = (*seed % 127773 * 16807) - (*seed / 127773 * 2836)) < 1) - v += 0x7fffffff; - *seed = v; - - return v; -} diff --git a/Xisop/src/common/kernlib/rand.h b/Xisop/src/common/kernlib/rand.h deleted file mode 100644 index 29ed498..0000000 --- a/Xisop/src/common/kernlib/rand.h +++ /dev/null @@ -1,53 +0,0 @@ -/* $Id: rand.h,v 1.1 2004/01/29 04:55:06 mmondor Exp $ */ - -/* - * Copyright (C) 2001-2003, Matthew Mondor - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software written by Matthew Mondor. - * 4. The name of Matthew Mondor may not be used to endorse or promote - * products derived from this software without specific prior written - * permission. - * 5. Redistribution of source code may not be released under the terms of - * any GNU Public License derivate. - * - * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - - - -#ifndef KERNLIB_RAND_H -#define KERNLIB_RAND_H - - - -#include - - - -int rand(void); -void srand(unsigned int); -int rand_r(unsigned int *); - - - -#endif diff --git a/Xisop/src/common/kernlib/setjmp.h b/Xisop/src/common/kernlib/setjmp.h deleted file mode 100644 index e19e520..0000000 --- a/Xisop/src/common/kernlib/setjmp.h +++ /dev/null @@ -1,64 +0,0 @@ -/* $Id: setjmp.h,v 1.1 2004/01/30 07:01:24 mmondor Exp $ */ - -/* - * Copyright (C) 2003, Matthew Mondor - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Matthew Mondor. - * 4. The name of Matthew Mondor may not be used to endorse or promote - * products derived from this software without specific prior written - * permission. - * 5. Redistribution of source code may not be released under the terms of - * any GNU Public License derivate. - * - * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - - - -#ifndef KERNLIB_SETJMP_H -#define KERNLIB_SETJMP_H - - - -/* These functions, as well as _ctx_t are defined by the processor-specific - * support headerfile, . These are the C89/ANSI - * setjmp()/longjmp(). - */ - - - -#include -#include - - - -typedef _ctx_t jmp_buf[1]; - - - -int setjmp(jmp_buf); -void longjmp(jmp_buf, int); - - - -#endif diff --git a/Xisop/src/common/kernlib/string.h b/Xisop/src/common/kernlib/string.h deleted file mode 100644 index cba5365..0000000 --- a/Xisop/src/common/kernlib/string.h +++ /dev/null @@ -1,106 +0,0 @@ -/* $Id: string.h,v 1.3 2004/06/03 05:40:46 mmondor Exp $ */ - -/* - * Copyright (C) 1989-2003, Matthew Mondor - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Matthew Mondor. - * 4. The name of Matthew Mondor may not be used to endorse or promote - * products derived from this software without specific prior written - * permission. - * 5. Redistribution of source code may not be released under the terms of - * any GNU Public License derivate. - * - * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - - - -#ifndef KERNLIB_STRING_H -#define KERNLIB_STRING_H - - - -#include -#include - - - -/* This is used for some kernel strings, such as public message port names */ -typedef struct bstr { - size_t size, len; /* Maximum and current lengths */ - u_int8_t data[1]; /* Actual buffer follows */ -} bstr_t; - - -bstr_t *bstr_alloc(size_t); -bstr_t *bstr_new(const char *, size_t, bool); -bstr_t *bstr_free(bstr_t *); - - - -/* More conventional string functions */ -size_t strlen(const char *); -size_t strnlen(const char *, size_t); - -char *_strcpy(char *, const char *); -size_t _strncpy(char *, const char *, size_t); - -char *_strcat(char *, const char *); -char *_strncat(char *, const char *, size_t); - -int strcmp(const char *, const char *); -int strncmp(const char *, const char *, size_t); - -char *strchr(const char *, int); -char *strnchr(const char *, int, size_t); -char *strrchr(const char *, int); -char *strnrchr(const char *, int, size_t); - -char *_strdup(const char *); -char *_strndup(const char *, size_t); - -int straspl(char **, char *, int); -int strspl(char **, char *, int, char); - -int strcasecmp(const char *, const char *); -int strncasecmp(const char *, const char *, size_t); -void _strlower(char *); -void _strupper(char *); -u_int32_t _strpack32(const char *, size_t); -u_int32_t _memcasehash32(const void *, size_t); -int _memcasecmp(const void *, const void *, size_t); - -u_int32_t htol(const char *); -void _strrev(char *); -u_int32_t memhash32(const void *, size_t); - -#define memclr(a, l) memset((a), 0, (l)) -int memcmp(const void *, const void *, size_t); -void *memcpy(void *, const void *, size_t); -void *memmove(void *, const void *, size_t); -void *memset(void *, int, size_t); -void pageclr(void *, u_int32_t); - - - -#endif diff --git a/Xisop/src/common/kernlib/string/_strcat.c b/Xisop/src/common/kernlib/string/_strcat.c deleted file mode 100644 index f431a05..0000000 --- a/Xisop/src/common/kernlib/string/_strcat.c +++ /dev/null @@ -1,52 +0,0 @@ -/* $Id: _strcat.c,v 1.1 2004/06/03 05:40:02 mmondor Exp $ */ - -/* - * Copyright (C) 2003, Matthew Mondor - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Matthew Mondor. - * 4. The name of Matthew Mondor may not be used to endorse or promote - * products derived from this software without specific prior written - * permission. - * 5. Redistribution of source code may not be released under the terms of - * any GNU Public License derivate. - * - * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - - - -#include -#include - - - -/* XXX Unlike ANSI, returns pointer at end of destination rather to beginning - * to allow special optimizations in loops. - */ -char *_strcat(char *dest, const char *src) -{ - for (; *dest != '\0'; dest++) ; - for (; (*dest = *src++) != '\0'; dest++) ; - - return (dest); -} diff --git a/Xisop/src/common/kernlib/string/_strcpy.c b/Xisop/src/common/kernlib/string/_strcpy.c deleted file mode 100644 index affc726..0000000 --- a/Xisop/src/common/kernlib/string/_strcpy.c +++ /dev/null @@ -1,53 +0,0 @@ -/* $Id: _strcpy.c,v 1.1 2004/06/03 05:40:03 mmondor Exp $ */ - -/* - * Copyright (C) 2003, Matthew Mondor - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Matthew Mondor. - * 4. The name of Matthew Mondor may not be used to endorse or promote - * products derived from this software without specific prior written - * permission. - * 5. Redistribution of source code may not be released under the terms of - * any GNU Public License derivate. - * - * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - - - -#include -#include - - - -/* XXX Unlike standard strcpy(), returns pointer to end of copied string in - * destination, rather than to beginning, more useful to optimize some loops. - * This variant should never be called strcpy() (i.e., could be called - * _strcpy() however). - */ -char *_strcpy(char *dest, const char *src) -{ - for (; (*dest = *src++) != '\0'; dest++) ; - - return (dest); -} diff --git a/Xisop/src/common/kernlib/string/_strdup.c b/Xisop/src/common/kernlib/string/_strdup.c deleted file mode 100644 index 76a78c9..0000000 --- a/Xisop/src/common/kernlib/string/_strdup.c +++ /dev/null @@ -1,59 +0,0 @@ -/* $Id: _strdup.c,v 1.3 2004/06/03 05:54:44 mmondor Exp $ */ - -/* - * Copyright (C) 2003, Matthew Mondor - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Matthew Mondor. - * 4. The name of Matthew Mondor may not be used to endorse or promote - * products derived from this software without specific prior written - * permission. - * 5. Redistribution of source code may not be released under the terms of - * any GNU Public License derivate. - * - * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - - - -#include -#include -#include -#include - - - -/* Uses kernel memory pool, should only be used by kernel code */ -char *_strdup(const char *str) -{ - char *new; - register const char *ptr; - register size_t len; - - for (new = NULL, ptr = str; *ptr != '\0'; ptr++) ; - - len = (size_t)(ptr - str) + 1; - if ((new = MALLOC(len)) != NULL) - (void) memcpy(new, str, len); - - return new; -} diff --git a/Xisop/src/common/kernlib/string/_strncat.c b/Xisop/src/common/kernlib/string/_strncat.c deleted file mode 100644 index 61115b2..0000000 --- a/Xisop/src/common/kernlib/string/_strncat.c +++ /dev/null @@ -1,59 +0,0 @@ -/* $Id: _strncat.c,v 1.1 2004/06/03 05:40:03 mmondor Exp $ */ - -/* - * Copyright (C) 2003, Matthew Mondor - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Matthew Mondor. - * 4. The name of Matthew Mondor may not be used to endorse or promote - * products derived from this software without specific prior written - * permission. - * 5. Redistribution of source code may not be released under the terms of - * any GNU Public License derivate. - * - * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - - - -#include -#include - - - -/* XXX Unlike ANSI, returns pointer at end of destination rather to beginning - * to allow special optimizations in loops. - */ -char *_strncat(char *dest, const char *src, size_t max) -{ - if (max != 0) { - register const char *toptr; - - for (toptr = dest, toptr += max; dest < toptr && *dest != '\0'; - dest++) ; - for (; dest < toptr && (*dest = *src++) != '\0'; dest++) ; - if (dest < toptr) - *dest = '\0'; - } - - return dest; -} diff --git a/Xisop/src/common/kernlib/string/_strncpy.c b/Xisop/src/common/kernlib/string/_strncpy.c deleted file mode 100644 index d9132eb..0000000 --- a/Xisop/src/common/kernlib/string/_strncpy.c +++ /dev/null @@ -1,62 +0,0 @@ -/* $Id: _strncpy.c,v 1.2 2004/06/03 05:40:03 mmondor Exp $ */ - -/* - * Copyright (C) 2003, Matthew Mondor - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Matthew Mondor. - * 4. The name of Matthew Mondor may not be used to endorse or promote - * products derived from this software without specific prior written - * permission. - * 5. Redistribution of source code may not be released under the terms of - * any GNU Public License derivate. - * - * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - - - -#include -#include - - - -/* Unlike the useless return code of the standard ANSI one, - * this function returns the number of bytes successfully copied. - */ -size_t _strncpy(char *dest, const char *src, size_t max) -{ - if (max > 0) { - register const char *sptr; - register char *toptr; - - for (sptr = src, toptr = dest, toptr += max; - dest < toptr && (*dest = *sptr) != '\0'; sptr++, dest++) ; - if (dest == toptr) - *dest = '\0'; - - return ((size_t)(sptr - src)); - } - - *dest = '\0'; - return 0; -} diff --git a/Xisop/src/common/kernlib/string/_strndup.c b/Xisop/src/common/kernlib/string/_strndup.c deleted file mode 100644 index 007d15e..0000000 --- a/Xisop/src/common/kernlib/string/_strndup.c +++ /dev/null @@ -1,61 +0,0 @@ -/* $Id: _strndup.c,v 1.3 2004/06/03 05:54:44 mmondor Exp $ */ - -/* - * Copyright (C) 2003, Matthew Mondor - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Matthew Mondor. - * 4. The name of Matthew Mondor may not be used to endorse or promote - * products derived from this software without specific prior written - * permission. - * 5. Redistribution of source code may not be released under the terms of - * any GNU Public License derivate. - * - * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - - - -#include -#include -#include -#include - - - -/* Uses kernel memory. */ -char *_strndup(const char *str, size_t max) -{ - char *new; - register const char *ptr, *toptr; - size_t len; - - for (toptr = ptr = str, toptr += max, new = NULL; - ptr < toptr && *ptr != '\0'; ptr++) ; - len = (size_t)(ptr - str); - if ((new = MALLOC(len + 1)) != NULL) { - (void) memcpy(new, str, len); - new[len] = '\0'; - } - - return new; -} diff --git a/Xisop/src/common/kernlib/string/_strrev.c b/Xisop/src/common/kernlib/string/_strrev.c deleted file mode 100644 index 3019ad2..0000000 --- a/Xisop/src/common/kernlib/string/_strrev.c +++ /dev/null @@ -1,57 +0,0 @@ -/* $Id: _strrev.c,v 1.2 2004/06/03 05:40:03 mmondor Exp $ */ - -/* - * Copyright (C) 2003, Matthew Mondor - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Matthew Mondor. - * 4. The name of Matthew Mondor may not be used to endorse or promote - * products derived from this software without specific prior written - * permission. - * 5. Redistribution of source code may not be released under the terms of - * any GNU Public License derivate. - * - * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - - - -#include -#include - - - -/* Iteratively reverses the supplied string */ -void _strrev(char *str) -{ - register char *p1, *p2, t; - - for (p1 = p2 = str; *p2; p2++) ; - if (p2 > p1) - p2--; - - for (;p1 < p2; p1++, p2--) { - t = *p1; - *p1 = *p2; - *p2 = t; - } -} diff --git a/Xisop/src/common/kernlib/string/bstr_alloc.c b/Xisop/src/common/kernlib/string/bstr_alloc.c deleted file mode 100644 index 34b63c6..0000000 --- a/Xisop/src/common/kernlib/string/bstr_alloc.c +++ /dev/null @@ -1,59 +0,0 @@ -/* $Id: bstr_alloc.c,v 1.1 2003/10/26 21:19:57 mmondor Exp $ */ - -/* - * Copyright (C) 2003, Matthew Mondor - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Matthew Mondor. - * 4. The name of Matthew Mondor may not be used to endorse or promote - * products derived from this software without specific prior written - * permission. - * 5. Redistribution of source code may not be released under the terms of - * any GNU Public License derivate. - * - * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - - - -#include -#include -#include -#include - - - -/* This is always allocated using kernel memory, and is for use by kernel - * functions. - */ -bstr_t *bstr_alloc(size_t size) -{ - bstr_t *bstr = NULL; - - if ((bstr = MALLOC(sizeof(bstr_t) + size + 1)) != NULL) { - bstr->size = size; - bstr->len = 0; - *bstr->data = 0; - } - - return bstr; -} diff --git a/Xisop/src/common/kernlib/string/bstr_free.c b/Xisop/src/common/kernlib/string/bstr_free.c deleted file mode 100644 index ba7b837..0000000 --- a/Xisop/src/common/kernlib/string/bstr_free.c +++ /dev/null @@ -1,50 +0,0 @@ -/* $Id: bstr_free.c,v 1.1 2003/10/26 21:19:57 mmondor Exp $ */ - -/* - * Copyright (C) 2003, Matthew Mondor - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Matthew Mondor. - * 4. The name of Matthew Mondor may not be used to endorse or promote - * products derived from this software without specific prior written - * permission. - * 5. Redistribution of source code may not be released under the terms of - * any GNU Public License derivate. - * - * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - - - -#include -#include -#include - - - -bstr_t *bstr_free(bstr_t *bstr) -{ - if (bstr != NULL) - FREE(bstr); - - return NULL; -} diff --git a/Xisop/src/common/kernlib/string/bstr_new.c b/Xisop/src/common/kernlib/string/bstr_new.c deleted file mode 100644 index 04eca59..0000000 --- a/Xisop/src/common/kernlib/string/bstr_new.c +++ /dev/null @@ -1,67 +0,0 @@ -/* $Id: bstr_new.c,v 1.1 2003/10/26 21:19:57 mmondor Exp $ */ - -/* - * Copyright (C) 2003, Matthew Mondor - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Matthew Mondor. - * 4. The name of Matthew Mondor may not be used to endorse or promote - * products derived from this software without specific prior written - * permission. - * 5. Redistribution of source code may not be released under the terms of - * any GNU Public License derivate. - * - * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - - - -#include -#include -#include -#include - - - -/* This is always allocated using kernel memory, and is for use by kernel - * functions. - */ -bstr_t *bstr_new(const char *string, size_t max, bool fixed) -{ - register bstr_t *bstr = NULL; - register size_t len = strnlen(string, max); - register size_t size; - - if (fixed) - size = max; - else - size = len; - - if ((bstr = MALLOC(sizeof(bstr_t) + size + 1)) != NULL) { - bstr->size = size; - bstr->len = len; - memcpy(bstr->data, string, len); - bstr->data[len] = 0; - } - - return bstr; -} diff --git a/Xisop/src/common/kernlib/string/case.c b/Xisop/src/common/kernlib/string/case.c deleted file mode 100644 index 4835110..0000000 --- a/Xisop/src/common/kernlib/string/case.c +++ /dev/null @@ -1,241 +0,0 @@ -/* $Id: case.c,v 1.2 2004/06/03 05:40:03 mmondor Exp $ */ - -/* - * Copyright (C) 2003, Matthew Mondor - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Matthew Mondor. - * 4. The name of Matthew Mondor may not be used to endorse or promote - * products derived from this software without specific prior written - * permission. - * 5. Redistribution of source code may not be released under the terms of - * any GNU Public License derivate. - * - * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - - - -#include -#include - - - -static const unsigned char toupper_table[] = { - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, - 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, - 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, - 0x1E, 0x1F, ' ', '!', '"', '#', '$', '%', '&', 0x27, - '(', ')', '*', '+', ',', '-', '.', '/', '0', '1', - '2', '3', '4', '5', '6', '7', '8', '9', ':', ';', - '<', '=', '>', '?', '@', 'A', 'B', 'C', 'D', 'E', - 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', - 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', - 'Z', '[', 0x5C, ']', '^', '_', '`', 'A', 'B', 'C', - 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', - 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', - 'X', 'Y', 'Z', '{', '|', '}', '~', 0x7F, 0x80, 0x81, - 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, - 0x8C, 0x8D, 0x8E, 0x8F, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, - 0x96, 0x97, 0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9D, 0x9E, 0x9F, - 0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, - 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF, 0xB0, 0xB1, 0xB2, 0xB3, - 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD, - 0xBE, 0xBF, 0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, - 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF, 0xD0, 0xD1, - 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xDB, - 0xDC, 0xDD, 0xDE, 0xDF, 0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, - 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF, - 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, - 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF -}; - -static const unsigned char tolower_table[] = { - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, - 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, - 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, - 0x1E, 0x1F, ' ', '!', '"', '#', '$', '%', '&', 0x27, - '(', ')', '*', '+', ',', '-', '.', '/', '0', '1', - '2', '3', '4', '5', '6', '7', '8', '9', ':', ';', - '<', '=', '>', '?', '@', 'a', 'b', 'c', 'd', 'e', - 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', - 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', - 'z', '[', 0x5C, ']', '^', '_', '`', 'a', 'b', 'c', - 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', - 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', - 'x', 'y', 'z', '{', '|', '}', '~', 0x7F, 0x80, 0x81, - 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, - 0x8C, 0x8D, 0x8E, 0x8F, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, - 0x96, 0x97, 0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9D, 0x9E, 0x9F, - 0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, - 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF, 0xB0, 0xB1, 0xB2, 0xB3, - 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD, - 0xBE, 0xBF, 0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, - 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF, 0xD0, 0xD1, - 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xDB, - 0xDC, 0xDD, 0xDE, 0xDF, 0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, - 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF, - 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, - 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF -}; - - - -int strcasecmp(const char *s1, const char *s2) -{ - register const unsigned char *us1, *us2; - register unsigned char cs1, cs2; - - for (us1 = (const unsigned char *)s1, us2 = (const unsigned char *)s2, - cs1 = tolower_table[(int)*us1], cs2 = tolower_table[(int)*us2]; - cs1 != '\0' && cs2 != '\0' && - (cs1 = tolower_table[(int)*us1]) == - (cs2 = tolower_table[(int)*us2]); - us1++, us2++) ; - - return ((int)(cs1 - cs2)); -} - - -int strncasecmp(const char *s1, const char *s2, size_t max) -{ - register const unsigned char *us1, *us2, *toptr; - register unsigned char cs1, cs2; - - if (max == 0) - return 0; - - for (us1 = (const unsigned char *)s1, us2 = (const unsigned char *)s2, - cs1 = tolower_table[(int)*us1], cs2 = tolower_table[(int)*us2], - toptr = us1, toptr += max; - us1 < toptr && cs1 != '\0' && cs2 != '\0' && - (cs1 = tolower_table[(int)*us1]) == - (cs2 = tolower_table[(int)*us2]); - us1++, us2++) ; - - return (us1 < toptr ? ((int)(cs1 - cs2)) : 0); -} - - -void _strlower(char *str) -{ - register unsigned char *ustr; - - for (ustr = (unsigned char *)str; *ustr != '\0'; ustr++) - *ustr = tolower_table[(int)*ustr]; -} - - -void _strupper(char *str) -{ - register unsigned char *ustr; - - for (ustr = (unsigned char *)str; *ustr != '\0'; ustr++) - *ustr = toupper_table[(int)*ustr]; -} - - -/* This function generates a 32-bit hash using the supplied string which - * is suitable for fast lookup for command comparision. It simply converts - * characters to uppercase and stores them in the value. It of course can - * only perform this for 4 bytes. It will stop at either end of string '\0' - * or space ' '. If the string has more than 4 characters -1 is returned. - */ -u_int32_t _strpack32(const char *str, size_t min) -{ - register unsigned const char *ustr; - register u_int32_t hash = 0; - size_t i; - - for (ustr = (unsigned const char *)str, i = 0; *ustr > 32 && i < 5; i++) { - hash <<= 8; - hash |= toupper_table[(int)*ustr++]; - } - if (i < min || i > 4) - hash = 0; - - return hash; -} - - -/* These functions are useful to use in conjunction with hash tables if - * case-insensitive processing of data is required while case-sensitivity of - * records storage must be preserved. - */ - -u_int32_t _memcasehash32(const void *mem, size_t size) -{ - register u_int32_t hash; - register const unsigned char *curmem, *tomem; - - hash = 0; - curmem = tomem = mem; - tomem += size; - -#if !defined(_ARCH_LOWCACHE) - while (curmem < tomem - 4) { -#if !defined(_ARCH_USEINDEXING) - hash = toupper_table[(int)*curmem++] + (31 * hash); - hash = toupper_table[(int)*curmem++] + (31 * hash); - hash = toupper_table[(int)*curmem++] + (31 * hash); - hash = toupper_table[(int)*curmem++] + (31 * hash); -#else /* !defined(_ARCH_USEINDEXING) */ - hash = toupper_table[(int)curmem[0]] + (31 * hash); - hash = toupper_table[(int)curmem[1]] + (31 * hash); - hash = toupper_table[(int)curmem[2]] + (31 * hash); - hash = toupper_table[(int)curmem[3]] + (31 * hash); - curmem += 4; -#endif /* !defined(_ARCH_USEINDEXING) */ - } -#endif /* !defined(_ARCH_LOWCACHE) */ - while (curmem < tomem) - hash = toupper_table[(int)*curmem++] + (31 * hash); - - return hash; -} - -int _memcasecmp(const void *s1, const void *s2, size_t size) -{ - register const unsigned char *ptr1, *ptr2, *toptr; - -#define CMP() toupper_table[(int)*ptr1++] != toupper_table[(int)*ptr2++] -#define RET() return (int)(toupper_table[(int)*(--ptr1)] - \ - toupper_table[(int)*(--ptr2)]) - - ptr1 = toptr = s1; - toptr += size; - ptr2 = s2; - -#if !defined(_ARCH_LOWCACHE) - while (ptr1 < toptr - 4) - if (CMP() || CMP() || CMP() || CMP()) - RET(); -#endif - while (ptr1 < toptr) - if (CMP()) - RET(); - -#undef CMP -#undef RET - - return 0; -} diff --git a/Xisop/src/common/kernlib/string/htol.c b/Xisop/src/common/kernlib/string/htol.c deleted file mode 100644 index 79ef88d..0000000 --- a/Xisop/src/common/kernlib/string/htol.c +++ /dev/null @@ -1,71 +0,0 @@ -/* $Id: htol.c,v 1.2 2004/06/03 05:40:03 mmondor Exp $ */ - -/* - * Copyright (C) 2003, Matthew Mondor - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Matthew Mondor. - * 4. The name of Matthew Mondor may not be used to endorse or promote - * products derived from this software without specific prior written - * permission. - * 5. Redistribution of source code may not be released under the terms of - * any GNU Public License derivate. - * - * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - - - -#include -#include - - - -/* Converts an hexadecimal string to u_int32_t */ -u_int32_t htol(const char *s) -{ - register u_int32_t v = 0; - - while (*s) { - if (*s >= '0' && *s <= '9') { - if (v <= (u_int16_t)-1) - v = (u_int16_t)v * 16; - else - v = v * 16; - v += *s++ - '0'; - } else if (*s >= 'a' && *s <= 'f') { - if (v <= (u_int16_t)-1) - v = (u_int16_t)v * 16; - else - v = v * 16; - v += *s++ - 87; - } else if (*s >= 'A' && *s <= 'F') { - if (v <= (u_int16_t)-1) - v = (u_int16_t)v * 16; - else - v = v * 16; - v += *s++ - 55; - } else break; - } - - return v; -} diff --git a/Xisop/src/common/kernlib/string/memcmp.c b/Xisop/src/common/kernlib/string/memcmp.c deleted file mode 100644 index 6b3b0f9..0000000 --- a/Xisop/src/common/kernlib/string/memcmp.c +++ /dev/null @@ -1,120 +0,0 @@ -/* $Id: memcmp.c,v 1.2 2004/06/03 05:40:03 mmondor Exp $ */ - -/* - * Copyright (C) 2003, Matthew Mondor - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Matthew Mondor. - * 4. The name of Matthew Mondor may not be used to endorse or promote - * products derived from this software without specific prior written - * permission. - * 5. Redistribution of source code may not be released under the terms of - * any GNU Public License derivate. - * - * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - - - -#include -#include - - - -int memcmp(const void *dest, const void *src, size_t len) -{ - register const unsigned char *ptr, *toptr, *dptr; - - ptr = toptr = src; - toptr += len; - dptr = dest; - -#define RET(a, b) return (int)(*(--(a)) - *(--(b))) - -#if _ARCH_INT_BITS == 8 - -#if !defined(_ARCH_LOWCACHE) - while (ptr < toptr - 8) - if (*ptr++ != *dptr++ || *ptr++ != *dptr++ || *ptr++ != *dptr++ || - *ptr++ != *dptr++ || *ptr++ != *dptr++ || *ptr++ != *dptr++ || - *ptr++ != *dptr++ || *ptr++ != *dptr++) - RET(dptr, ptr); -#endif /* !defined(_ARCH_LOWCACHE) */ - while (ptr < toptr) - if (*ptr++ != *dptr++) - RET(dptr, ptr); - -#else /* _ARCH_INT_BITS != 8 */ - - if (len < 32 || ((int)ptr % sizeof(int)) != ((int)dptr % sizeof(int))) { -#if !defined(_ARCH_LOWCACHE) - while (ptr < toptr - 8) - if (*ptr++ != *dptr++ || *ptr++ != *dptr++ || *ptr++ != *dptr++ || - *ptr++ != *dptr++ || *ptr++ != *dptr++ || - *ptr++ != *dptr++ || *ptr++ != *dptr++ || - *ptr++ != *dptr++) - RET(dptr, ptr); -#endif /* !defined(_ARCH_LOWCACHE) */ - while (ptr < toptr) - if (*ptr++ != *dptr++) - RET(dptr, ptr); - } else { - register const unsigned int *lptr, *ltoptr, *ldptr, *ldtoptr; - register const unsigned char *dtoptr; - - dtoptr = dptr; - dtoptr += len; - lptr = (const unsigned int *)OALIGN_CEIL(ptr, int); - ltoptr = (const unsigned int *)OALIGN_FLOOR(toptr, int); - ldptr = (const unsigned int *)OALIGN_CEIL(dptr, int); - ldtoptr = (const unsigned int *)OALIGN_FLOOR(dtoptr, int); - if (ldtoptr - ldptr < ltoptr - lptr) - ltoptr--; - - while (ptr < (const unsigned char *)lptr) - if (*ptr++ != *dptr++) - RET(dptr, ptr); -#if !defined(_ARCH_LOWCACHE) - while (lptr < ltoptr - (sizeof(int) * 8)) { - if (*lptr++ != *ldptr++ || *lptr++ != *ldptr++ || - *lptr++ != *ldptr++ || *lptr++ != *ldptr++ || - *lptr++ != *ldptr++ || *lptr++ != *ldptr++ || - *lptr++ != *ldptr++ || *lptr++ != *ldptr++) - RET(ldptr, lptr); - } -#endif /* !defined(_ARCH_LOWCACHE) */ - while (lptr < ltoptr) - if (*lptr++ != *ldptr++) - RET(ldptr, lptr); - ptr = (const unsigned char *)lptr; - dptr = (const unsigned char *)ldptr; - while (ptr < toptr) - if (*ptr++ != *dptr++) - RET(dptr, ptr); - } - -#endif /* _ARCH_INT_BITS == 8 */ - -#undef RET - - return 0; -} diff --git a/Xisop/src/common/kernlib/string/memcpy.c b/Xisop/src/common/kernlib/string/memcpy.c deleted file mode 100644 index 398d91e..0000000 --- a/Xisop/src/common/kernlib/string/memcpy.c +++ /dev/null @@ -1,164 +0,0 @@ -/* $Id: memcpy.c,v 1.2 2004/06/03 05:40:03 mmondor Exp $ */ - -/* - * Copyright (C) 2003, Matthew Mondor - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Matthew Mondor. - * 4. The name of Matthew Mondor may not be used to endorse or promote - * products derived from this software without specific prior written - * permission. - * 5. Redistribution of source code may not be released under the terms of - * any GNU Public License derivate. - * - * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - - - -#include -#include - - - -void *memcpy(void *dest, const void *src, size_t len) -{ - register const unsigned char *ptr, *toptr; - register unsigned char *dptr; - - ptr = toptr = src; - toptr += len; - dptr = dest; - -#if _ARCH_INT_BITS == 8 - -#if !defined(_ARCH_LOWCACHE) - while (ptr < toptr - 8) { -#if !defined(_ARCH_USEINDEXING) - *dptr++ = *ptr++; - *dptr++ = *ptr++; - *dptr++ = *ptr++; - *dptr++ = *ptr++; - *dptr++ = *ptr++; - *dptr++ = *ptr++; - *dptr++ = *ptr++; - *dptr++ = *ptr++; -#else /* !defined(_ARCH_USEINDEXING) */ - dptr[0] = ptr[0]; - dptr[1] = ptr[1]; - dptr[2] = ptr[2]; - dptr[3] = ptr[3]; - dptr[4] = ptr[4]; - dptr[5] = ptr[5]; - dptr[6] = ptr[6]; - dptr[7] = ptr[7]; - dptr += 8; - ptr += 8; -#endif /* !defined(_ARCH_USEINDEXING) */ - } -#endif /* !defined(_ARCH_LOWCACHE) */ - while (ptr < toptr) - *dptr++ = *ptr++; - -#else /* _ARCH_INT_BITS != 8 */ - - if (len < 32 || ((int)ptr % sizeof(int)) != ((int)dptr % sizeof(int))) { -#if !defined(_ARCH_LOWCACHE) - while (ptr < toptr - 8) { -#if !defined(_ARCH_USEINDEXING) - *dptr++ = *ptr++; - *dptr++ = *ptr++; - *dptr++ = *ptr++; - *dptr++ = *ptr++; - *dptr++ = *ptr++; - *dptr++ = *ptr++; - *dptr++ = *ptr++; - *dptr++ = *ptr++; -#else /* !defined(_ARCH_USEINDEXING) */ - dptr[0] = ptr[0]; - dptr[1] = ptr[1]; - dptr[2] = ptr[2]; - dptr[3] = ptr[3]; - dptr[4] = ptr[4]; - dptr[5] = ptr[5]; - dptr[6] = ptr[6]; - dptr[7] = ptr[7]; - dptr += 8; - ptr += 8; -#endif /* !defined(_ARCH_USEINDEXING) */ - } -#endif /* !defined(_ARCH_LOWCACHE) */ - while (ptr < toptr) - *dptr++ = *ptr++; - } else { - register const unsigned int *lptr, *ltoptr; - register unsigned int *ldptr, *ldtoptr; - register unsigned char *dtoptr; - - dtoptr = dptr; - dtoptr += len; - lptr = (const unsigned int *)OALIGN_CEIL(ptr, int); - ltoptr = (const unsigned int *)OALIGN_FLOOR(toptr, int); - ldptr = (unsigned int *)OALIGN_CEIL(dptr, int); - ldtoptr = (unsigned int *)OALIGN_FLOOR(dtoptr, int); - if (ldtoptr - ldptr < ltoptr - lptr) - ltoptr--; - - while (ptr < (const unsigned char *)lptr) - *dptr++ = *ptr++; -#if !defined(_ARCH_LOWCACHE) - while (lptr < ltoptr - (sizeof(int) * 8)) { -#if !defined(_ARCH_USEINDEXING) - *ldptr++ = *lptr++; - *ldptr++ = *lptr++; - *ldptr++ = *lptr++; - *ldptr++ = *lptr++; - *ldptr++ = *lptr++; - *ldptr++ = *lptr++; - *ldptr++ = *lptr++; - *ldptr++ = *lptr++; -#else /* !defined(_ARCH_USEINDEXING) */ - ldptr[0] = lptr[0]; - ldptr[1] = lptr[1]; - ldptr[2] = lptr[2]; - ldptr[3] = lptr[3]; - ldptr[4] = lptr[4]; - ldptr[5] = lptr[5]; - ldptr[6] = lptr[6]; - ldptr[7] = lptr[7]; - ldptr = &ldptr[8]; - lptr = &lptr[8]; -#endif /* !defined(_ARCH_USEINDEXING) */ - } -#endif /* !defined(_ARCH_LOWCACHE) */ - while (lptr < ltoptr) - *ldptr++ = *lptr++; - ptr = (unsigned const char *)lptr; - dptr = (unsigned char *)ldptr; - while (ptr < toptr) - *dptr++ = *ptr++; - } - -#endif /* _ARCH_INT_BITS == 8 */ - - return dest; -} diff --git a/Xisop/src/common/kernlib/string/memhash32.c b/Xisop/src/common/kernlib/string/memhash32.c deleted file mode 100644 index 9184827..0000000 --- a/Xisop/src/common/kernlib/string/memhash32.c +++ /dev/null @@ -1,72 +0,0 @@ -/* $Id: memhash32.c,v 1.2 2004/06/03 05:40:03 mmondor Exp $ */ - -/* - * Copyright (C) 2003, Matthew Mondor - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Matthew Mondor. - * 4. The name of Matthew Mondor may not be used to endorse or promote - * products derived from this software without specific prior written - * permission. - * 5. Redistribution of source code may not be released under the terms of - * any GNU Public License derivate. - * - * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - - - -#include -#include - - - -u_int32_t memhash32(const void *mem, size_t size) -{ - register u_int32_t hash; - register const unsigned char *curmem, *tomem; - - hash = 0; - curmem = tomem = mem; - tomem += size; - -#if !defined(_ARCH_LOWCACHE) - while (curmem < tomem - 4) { -#if !defined(_ARCH_USEINDEXING) - hash = *curmem++ + (31 * hash); - hash = *curmem++ + (31 * hash); - hash = *curmem++ + (31 * hash); - hash = *curmem++ + (31 * hash); -#else /* !defined(_ARCH_USEINDEXING) */ - hash = curmem[0] + (31 * hash); - hash = curmem[1] + (31 * hash); - hash = curmem[2] + (31 * hash); - hash = curmem[3] + (31 * hash); - curmem += 4; -#endif /* !defined(_ARCH_USEINDEXING) */ - } -#endif /* !defined(_ARCH_LOWCACHE) */ - while (curmem < tomem) - hash = *curmem++ + (31 * hash); - - return hash; -} diff --git a/Xisop/src/common/kernlib/string/memmove.c b/Xisop/src/common/kernlib/string/memmove.c deleted file mode 100644 index cd0db28..0000000 --- a/Xisop/src/common/kernlib/string/memmove.c +++ /dev/null @@ -1,305 +0,0 @@ -/* $Id: memmove.c,v 1.1 2004/06/03 05:40:03 mmondor Exp $ */ - -/* - * Copyright (C) 2003, Matthew Mondor - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Matthew Mondor. - * 4. The name of Matthew Mondor may not be used to endorse or promote - * products derived from this software without specific prior written - * permission. - * 5. Redistribution of source code may not be released under the terms of - * any GNU Public License derivate. - * - * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - - - -#include -#include - - - -/* Can work with overlapping areas */ -void *memmove(void *dest, const void *src, size_t len) -{ - register const unsigned char *ptr = NULL, *toptr = NULL; - register unsigned char *dptr = NULL; - size_t d; - - if (dest < src) - d = (const unsigned char *)src - (unsigned char *)dest; - else - d = (unsigned char *)dest - (const unsigned char *)src; - -#if _ARCH_INT_BITS == 8 - - if (dest < src) { - /* Copy in increasing order */ - ptr = toptr = (const unsigned char *)src; - toptr += len; - dptr = dest; -#if !defined(_ARCH_LOWCACHE) - while (ptr < toptr - 8) { -#if !defined(_ARCH_USEINDEXING) - *dptr++ = *ptr++; - *dptr++ = *ptr++; - *dptr++ = *ptr++; - *dptr++ = *ptr++; - *dptr++ = *ptr++; - *dptr++ = *ptr++; - *dptr++ = *ptr++; - *dptr++ = *ptr++; -#else /* !defined(_ARCH_USEINDEXING) */ - dptr[0] = ptr[0]; - dptr[1] = ptr[1]; - dptr[2] = ptr[2]; - dptr[3] = ptr[3]; - dptr[4] = ptr[4]; - dptr[5] = ptr[5]; - dptr[6] = ptr[6]; - dptr[7] = ptr[7]; - dptr += 8; - ptr += 8; -#endif /* !defined(_ARCH_USEINDEXING) */ - } -#endif /* !defined(_ARCH_LOWCACHE) */ - while (ptr < toptr) - *dptr++ = *ptr++; - } else if (dest > src) { - /* Copy in reverse order */ - ptr = toptr = (const unsigned char *)src; - ptr += len; - dptr = dest; - dptr += len; -#if !defined(_ARCH_LOWCACHE) - while (ptr >= toptr + 8) { -#if !defined(_ARCH_USEINDEXING) - *dptr-- = *ptr--; - *dptr-- = *ptr--; - *dptr-- = *ptr--; - *dptr-- = *ptr--; - *dptr-- = *ptr--; - *dptr-- = *ptr--; - *dptr-- = *ptr--; - *dptr-- = *ptr--; -#else /* !defined(_ARCH_USEINDEXING) */ - dptr[0] = ptr[0]; - dptr[-1] = ptr[-1]; - dptr[-2] = ptr[-2]; - dptr[-3] = ptr[-3]; - dptr[-4] = ptr[-4]; - dptr[-5] = ptr[-5]; - dptr[-6] = ptr[-6]; - dptr[-7] = ptr[-7]; - dptr -= 8; - ptr -= 8; -#endif /* !defined(_ARCH_USEINDEXING) */ - } -#endif /* !defined(_ARCH_LOWCACHE) */ - while (ptr >= toptr) - *dptr-- = *ptr--; - } - -#else /* _ARCH_INT_BITS != 8 */ - - if (len < 32 || d < sizeof(int) || - ((int)ptr % sizeof(int)) != ((int)dptr % sizeof(int))) { - if (dest < src) { - /* Copy in increasing order */ - ptr = toptr = (const unsigned char *)src; - toptr += len; - dptr = dest; -#if !defined(_ARCH_LOWCACHE) - while (ptr < toptr - 8) { -#if !defined(_ARCH_USEINDEXING) - *dptr++ = *ptr++; - *dptr++ = *ptr++; - *dptr++ = *ptr++; - *dptr++ = *ptr++; - *dptr++ = *ptr++; - *dptr++ = *ptr++; - *dptr++ = *ptr++; - *dptr++ = *ptr++; -#else /* !defined(_ARCH_USEINDEXING) */ - dptr[0] = ptr[0]; - dptr[1] = ptr[1]; - dptr[2] = ptr[2]; - dptr[3] = ptr[3]; - dptr[4] = ptr[4]; - dptr[5] = ptr[5]; - dptr[6] = ptr[6]; - dptr[7] = ptr[7]; - dptr += 8; - ptr += 8; -#endif /* !defined(_ARCH_USEINDEXING) */ - } -#endif /* !defined(_ARCH_LOWCACHE) */ - while (ptr < toptr) - *dptr++ = *ptr++; - } else if (dest > src) { - /* Copy in reverse order */ - ptr = toptr = (const unsigned char *)src; - ptr += len; - dptr = dest; - dptr += len; -#if !defined(_ARCH_LOWCACHE) - while (ptr >= toptr + 8) { -#if !defined(_ARCH_USEINDEXING) - *dptr-- = *ptr--; - *dptr-- = *ptr--; - *dptr-- = *ptr--; - *dptr-- = *ptr--; - *dptr-- = *ptr--; - *dptr-- = *ptr--; - *dptr-- = *ptr--; - *dptr-- = *ptr--; -#else /* !defined(_ARCH_USEINDEXING) */ - dptr[0] = ptr[0]; - dptr[-1] = ptr[-1]; - dptr[-2] = ptr[-2]; - dptr[-3] = ptr[-3]; - dptr[-4] = ptr[-4]; - dptr[-5] = ptr[-5]; - dptr[-6] = ptr[-6]; - dptr[-7] = ptr[-7]; - dptr -= 8; - ptr -= 8; -#endif /* !defined(_ARCH_USEINDEXING) */ - } -#endif /* !defined(_ARCH_LOWCACHE) */ - while (ptr >= toptr) - *dptr-- = *ptr--; - } - } else { - if (dest < src) { - /* Increasing order */ - register const unsigned int *lptr, *ltoptr; - register unsigned int *ldptr, *ldtoptr; - register unsigned char *dtoptr; - - ptr = toptr = (const unsigned char *)src; - toptr += len; - dptr = dest; - - dtoptr = dptr; - dtoptr += len; - lptr = (const unsigned int *)OALIGN_CEIL(ptr, int); - ltoptr = (const unsigned int *)OALIGN_FLOOR(toptr, int); - ldptr = (unsigned int *)OALIGN_CEIL(dptr, int); - ldtoptr = (unsigned int *)OALIGN_FLOOR(dtoptr, int); - if (ldtoptr - ldptr < ltoptr - lptr) - ltoptr--; - - while (ptr < (const unsigned char *)lptr) - *dptr++ = *ptr++; -#if !defined(_ARCH_LOWCACHE) - while (lptr < ltoptr - (sizeof(int) * 8)) { -#if !defined(_ARCH_USEINDEXING) - *ldptr++ = *lptr++; - *ldptr++ = *lptr++; - *ldptr++ = *lptr++; - *ldptr++ = *lptr++; - *ldptr++ = *lptr++; - *ldptr++ = *lptr++; - *ldptr++ = *lptr++; - *ldptr++ = *lptr++; -#else /* !defined(_ARCH_USEINDEXING) */ - ldptr[0] = lptr[0]; - ldptr[1] = lptr[1]; - ldptr[2] = lptr[2]; - ldptr[3] = lptr[3]; - ldptr[4] = lptr[4]; - ldptr[5] = lptr[5]; - ldptr[6] = lptr[6]; - ldptr[7] = lptr[7]; - ldptr = &ldptr[8]; - lptr = &lptr[8]; -#endif /* !defined(_ARCH_USEINDEXING) */ - } -#endif /* !defined(_ARCH_LOWCACHE) */ - while (lptr < ltoptr) - *ldptr++ = *lptr++; - ptr = (const unsigned char *)lptr; - dptr = (unsigned char *)ldptr; - while (ptr < toptr) - *dptr++ = *ptr++; - } else if (dest > src) { - /* Reverse order */ - register const int *lptr, *ltoptr; - register int *ldptr, *ldtoptr; - register char *dtoptr; - - ptr = toptr = (const unsigned char *)src; - ptr += len; - dptr = dest; - dptr += len; - - dtoptr = dest; - lptr = (const unsigned int *)OALIGN_FLOOR(ptr, int); - ldptr = (unsigned int *)OALIGN_FLOOR(dptr, int); - ltoptr = (const unsigned int *)OALIGN_CEIL(toptr, int); - ldtoptr = (unsigned int *)OALIGN_CEIL(dtoptr, int); - if (ldptr - ldtoptr < lptr - ltoptr) - ltoptr++; - - while (ptr >= (const unsigned char *)lptr) - *dptr-- = *ptr--; -#if !defined(_ARCH_LOWCACHE) - while (lptr >= ltoptr + (sizeof(int) * 8)) { -#if !defined(_ARCH_USEINDEXING) - *ldptr-- = *lptr--; - *ldptr-- = *lptr--; - *ldptr-- = *lptr--; - *ldptr-- = *lptr--; - *ldptr-- = *lptr--; - *ldptr-- = *lptr--; - *ldptr-- = *lptr--; - *ldptr-- = *lptr--; -#else /* !defined(_ARCH_USEINDEXING) */ - ldptr[0] = lptr[0]; - ldptr[-1] = lptr[-1]; - ldptr[-2] = lptr[-2]; - ldptr[-3] = lptr[-3]; - ldptr[-4] = lptr[-4]; - ldptr[-5] = lptr[-5]; - ldptr[-6] = lptr[-6]; - ldptr[-7] = lptr[-7]; - ldptr = &ldptr[-8]; - lptr = &lptr[-8]; -#endif /* !defined(_ARCH_USEINDEXING) */ - } -#endif /* !defined(_ARCH_LOWCACHE) */ - while (lptr >= ltoptr) - *ldptr-- = *lptr--; - ptr = (const unsigned char *)lptr; - dptr = (unsigned char *)ldptr; - while (ptr >= toptr) - *dptr-- = *ptr--; - } - } - -#endif /* _ARCH_INT_BITS == 8 */ - - return dest; -} diff --git a/Xisop/src/common/kernlib/string/memset.c b/Xisop/src/common/kernlib/string/memset.c deleted file mode 100644 index 65f0cdd..0000000 --- a/Xisop/src/common/kernlib/string/memset.c +++ /dev/null @@ -1,179 +0,0 @@ -/* $Id: memset.c,v 1.2 2004/06/03 05:40:03 mmondor Exp $ */ - -/* - * Copyright (C) 2003, Matthew Mondor - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Matthew Mondor. - * 4. The name of Matthew Mondor may not be used to endorse or promote - * products derived from this software without specific prior written - * permission. - * 5. Redistribution of source code may not be released under the terms of - * any GNU Public License derivate. - * - * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - - - -#include -#include - - - -void *memset(void *mem, int fill, size_t len) -{ - register unsigned char *ptr, *toptr; - u_int8_t byte = (u_int8_t)fill; - - ptr = toptr = mem; - toptr += len; - -#if _ARCH_INT_BITS == 8 - -#if !defined(_ARCH_LOWCACHE) - while (ptr < toptr - 8) { -#if !defined(_ARCH_USEINDEXING) - *ptr++ = byte; - *ptr++ = byte; - *ptr++ = byte; - *ptr++ = byte; - *ptr++ = byte; - *ptr++ = byte; - *ptr++ = byte; - *ptr++ = byte; -#else /* !defined(_ARCH_USEINDEXING) */ - ptr[0] = byte; - ptr[1] = byte; - ptr[2] = byte; - ptr[3] = byte; - ptr[4] = byte; - ptr[5] = byte; - ptr[6] = byte; - ptr[7] = byte; - ptr += 8; -#endif /* !defined(_ARCH_USEINDEXING) */ - } -#endif /* !defined(_ARCH_LOWCACHE) */ - while (ptr < toptr) - *ptr++ = byte; - -#else /* _ARCH_INT_BITS != 8 */ - - if (len < 32) { -#if !defined(_ARCH_LOWCACHE) - while (ptr < toptr - 8) { -#if !defined(_ARCH_USEINDEXING) - *ptr++ = byte; - *ptr++ = byte; - *ptr++ = byte; - *ptr++ = byte; - *ptr++ = byte; - *ptr++ = byte; - *ptr++ = byte; - *ptr++ = byte; -#else /* !defined(_ARCH_USEDARRAYS) */ - ptr[0] = byte; - ptr[1] = byte; - ptr[2] = byte; - ptr[3] = byte; - ptr[4] = byte; - ptr[5] = byte; - ptr[6] = byte; - ptr[7] = byte; - ptr += 8; -#endif /* !defined(_ARCH_USEINDEXING) */ - } -#endif /* !defined(_ARCH_LOWCACHE) */ - while (ptr < toptr) - *ptr++ = byte; - } else { - register unsigned int *lptr, *ltoptr, lword = 0; - - if (byte != 0) { -#if _ARCH_INT_BITS == 16 - lword = ((byte << 8) & 0xff00) | (byte & 0x00ff); -#elif _ARCH_INT_BITS == 32 - /* Creating a 16-bit word from the 8-bit one, then the 32-bit word - * from the 16-bit one require less instructions. - */ - register u_int16_t tmp; - - tmp = ((byte << 8) & 0xff00) | (byte & 0x00ff); - lword = ((tmp << 16) & 0xffff0000) | (tmp & 0x0000ffff); -#elif _ARCH_INT_BITS == 64 - /* Unlikely, since long is usually 64-bit on 64-bit archs, leaving - * 32-bit ints. - * Creating a 16-bit word from the 8-bit one, a 32-bit word from - * the 16-bit one and a 64-bit word from the 32-bit one require - * less instructions. - */ - register u_int32_t tmp2; - register u_int16_t tmp; - - tmp = ((byte << 8) & 0xff00) | (byte & 0x00ff); - tmp2 = ((tmp << 16) & 0xffff0000) | (tmp & 0x0000ffff); - lword = ((tmp2 << 32) & 0xffffffff00000000ULL) | - (tmp2 & 0x00000000ffffffffULL); -#endif - } - - lptr = (unsigned int *)OALIGN_CEIL(ptr, int); - ltoptr = (unsigned int *)OALIGN_FLOOR(toptr, int); - - while (ptr < (unsigned char *)lptr) - *ptr++ = byte; -#if !defined(_ARCH_LOWCACHE) - while (lptr < ltoptr - (sizeof(int) * 8)) { -#if !defined(_ARCH_USEINDEXING) - *lptr++ = lword; - *lptr++ = lword; - *lptr++ = lword; - *lptr++ = lword; - *lptr++ = lword; - *lptr++ = lword; - *lptr++ = lword; - *lptr++ = lword; -#else /* !defined(_ARCH_USEINDEXING) */ - lptr[0] = lword; - lptr[1] = lword; - lptr[2] = lword; - lptr[3] = lword; - lptr[4] = lword; - lptr[5] = lword; - lptr[6] = lword; - lptr[7] = lword; - lptr = &lptr[8]; -#endif /* !defined(_ARCH_USEINDEXING) */ - } -#endif /* !defined(_ARCH_LOWCACHE) */ - while (lptr < ltoptr) - *lptr++ = lword; - ptr = (unsigned char *)lptr; - while (ptr < toptr) - *ptr++ = byte; - } - -#endif /* _ARCH_INT_BITS == 8 */ - - return mem; -} diff --git a/Xisop/src/common/kernlib/string/pageclr.c b/Xisop/src/common/kernlib/string/pageclr.c deleted file mode 100644 index cb74b59..0000000 --- a/Xisop/src/common/kernlib/string/pageclr.c +++ /dev/null @@ -1,96 +0,0 @@ -/* $Id: pageclr.c,v 1.2 2004/06/03 05:40:03 mmondor Exp $ */ - -/* - * Copyright (C) 2003, Matthew Mondor - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Matthew Mondor. - * 4. The name of Matthew Mondor may not be used to endorse or promote - * products derived from this software without specific prior written - * permission. - * 5. Redistribution of source code may not be released under the terms of - * any GNU Public License derivate. - * - * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - - - -#include -#include -#include - - - -/* Faster than memset(), but assumes that be aligned and that _PAGE_SIZE - * is a multiple of sizeof(int). - */ -void pageclr(void *mem, u_int32_t many) -{ - register int *lptr, *ltoptr, lword = 0; - - lptr = (int *)mem; - ltoptr = (int *)(((u_int8_t *)mem + (_PAGE_SIZE * many))); - -#if !defined(_ARCH_LOWCACHE) - while (lptr < ltoptr - (sizeof(int) * 16)) { -#if !defined(_ARCH_USEINDEXING) - *lptr++ = lword; - *lptr++ = lword; - *lptr++ = lword; - *lptr++ = lword; - *lptr++ = lword; - *lptr++ = lword; - *lptr++ = lword; - *lptr++ = lword; - *lptr++ = lword; - *lptr++ = lword; - *lptr++ = lword; - *lptr++ = lword; - *lptr++ = lword; - *lptr++ = lword; - *lptr++ = lword; - *lptr++ = lword; -#else /* !defined(_ARCH_USEINDEXING) */ - lptr[0] = lword; - lptr[1] = lword; - lptr[2] = lword; - lptr[3] = lword; - lptr[4] = lword; - lptr[5] = lword; - lptr[6] = lword; - lptr[7] = lword; - lptr[8] = lword; - lptr[9] = lword; - lptr[10] = lword; - lptr[11] = lword; - lptr[12] = lword; - lptr[13] = lword; - lptr[14] = lword; - lptr[15] = lword; - lptr = &lptr[16]; -#endif /* !defined(_ARCH_USEINDEXING) */ - } -#endif /* !defined(_ARCH_LOWCACHE) */ - while (lptr < ltoptr) - *lptr++ = lword; -} diff --git a/Xisop/src/common/kernlib/string/straspl.c b/Xisop/src/common/kernlib/string/straspl.c deleted file mode 100644 index 7f15a91..0000000 --- a/Xisop/src/common/kernlib/string/straspl.c +++ /dev/null @@ -1,68 +0,0 @@ -/* $Id: straspl.c,v 1.2 2004/06/03 05:40:03 mmondor Exp $ */ - -/* - * Copyright (C) 2003, Matthew Mondor - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Matthew Mondor. - * 4. The name of Matthew Mondor may not be used to endorse or promote - * products derived from this software without specific prior written - * permission. - * 5. Redistribution of source code may not be released under the terms of - * any GNU Public License derivate. - * - * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - - - -#include -#include - - - -/* Splits columns of a string delimited by spaces and/or tabs, and fills - * char **argv with pointers to each column. Returns the number of columns - * that could be filled in. The supplied string IS modified. - */ -int straspl(char **argv, char *str, int maxcols) -{ - register char *ptr, *ptr2; - int col; - - for (ptr = str, col = 0; *ptr != '\0' && col < maxcols; ) { - for (; *ptr == ' ' || *ptr == '\t'; ptr++) ; - if (*ptr != '\0') { - for (ptr2 = ptr; *ptr != '\0' && *ptr != ' ' && *ptr != '\t'; - ptr++) ; - if (ptr != ptr2) { - if (*ptr != '\0') - *ptr++ = '\0'; - argv[col++] = ptr2; - } else - break; - } else - break; - } - - return col; -} diff --git a/Xisop/src/common/kernlib/string/strchr.c b/Xisop/src/common/kernlib/string/strchr.c deleted file mode 100644 index fd4c51d..0000000 --- a/Xisop/src/common/kernlib/string/strchr.c +++ /dev/null @@ -1,48 +0,0 @@ -/* $Id: strchr.c,v 1.2 2004/06/03 05:40:03 mmondor Exp $ */ - -/* - * Copyright (C) 2003, Matthew Mondor - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Matthew Mondor. - * 4. The name of Matthew Mondor may not be used to endorse or promote - * products derived from this software without specific prior written - * permission. - * 5. Redistribution of source code may not be released under the terms of - * any GNU Public License derivate. - * - * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - - - -#include -#include - - - -char *strchr(const char *str, int c) -{ - for (; *str != '\0' && *str != c; str++) ; - - return (*str != '\0' ? (char *)str : NULL); -} diff --git a/Xisop/src/common/kernlib/string/strcmp.c b/Xisop/src/common/kernlib/string/strcmp.c deleted file mode 100644 index 3d5c931..0000000 --- a/Xisop/src/common/kernlib/string/strcmp.c +++ /dev/null @@ -1,48 +0,0 @@ -/* $Id: strcmp.c,v 1.2 2004/06/03 05:40:03 mmondor Exp $ */ - -/* - * Copyright (C) 2003, Matthew Mondor - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Matthew Mondor. - * 4. The name of Matthew Mondor may not be used to endorse or promote - * products derived from this software without specific prior written - * permission. - * 5. Redistribution of source code may not be released under the terms of - * any GNU Public License derivate. - * - * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - - - -#include -#include - - - -int strcmp(const char *s1, const char *s2) -{ - for (; *s1 != '\0' && *s2 != '\0' && *s1 == *s2; s1++, s2++) ; - - return ((int)((const unsigned char)*s1 - (const unsigned char)*s2)); -} diff --git a/Xisop/src/common/kernlib/string/strlen.c b/Xisop/src/common/kernlib/string/strlen.c deleted file mode 100644 index 3bcca1b..0000000 --- a/Xisop/src/common/kernlib/string/strlen.c +++ /dev/null @@ -1,56 +0,0 @@ -/* $Id: strlen.c,v 1.2 2004/06/03 05:40:03 mmondor Exp $ */ - -/* - * Copyright (C) 2003, Matthew Mondor - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Matthew Mondor. - * 4. The name of Matthew Mondor may not be used to endorse or promote - * products derived from this software without specific prior written - * permission. - * 5. Redistribution of source code may not be released under the terms of - * any GNU Public License derivate. - * - * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - - - -#include -#include - - - -size_t strlen(const char *str) -{ - register const char *ptr = str; - - /* Although this causes ptr to be increased even when 0 is reached, - * compilers generally generate better code. - * i.e. GCC2-m68k: tstb %a0@+ vs tstb %a0@ and addql #1, %a0 - * Matt - */ - while (*ptr++ != '\0') ; - - /* Don't forget to substract 1 */ - return ((size_t)((ptr - 1) - str)); -} diff --git a/Xisop/src/common/kernlib/string/strnchr.c b/Xisop/src/common/kernlib/string/strnchr.c deleted file mode 100644 index a7ba5b8..0000000 --- a/Xisop/src/common/kernlib/string/strnchr.c +++ /dev/null @@ -1,58 +0,0 @@ -/* $Id: strnchr.c,v 1.2 2004/06/03 05:40:04 mmondor Exp $ */ - -/* - * Copyright (C) 2003, Matthew Mondor - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Matthew Mondor. - * 4. The name of Matthew Mondor may not be used to endorse or promote - * products derived from this software without specific prior written - * permission. - * 5. Redistribution of source code may not be released under the terms of - * any GNU Public License derivate. - * - * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - - - -#include -#include - - - -char *strnchr(const char *str, int c, size_t max) -{ - if (max > 0) { - register const char *toptr; - - for (toptr = str, toptr += max; - str < toptr && *str != '\0' && (int)*str != c; str++) ; - - if (str == toptr || *str == '\0') - return NULL; - - return (char *)str; - } - - return NULL; -} diff --git a/Xisop/src/common/kernlib/string/strncmp.c b/Xisop/src/common/kernlib/string/strncmp.c deleted file mode 100644 index de97198..0000000 --- a/Xisop/src/common/kernlib/string/strncmp.c +++ /dev/null @@ -1,57 +0,0 @@ -/* $Id: strncmp.c,v 1.3 2004/06/03 05:40:04 mmondor Exp $ */ - -/* - * Copyright (C) 2003, Matthew Mondor - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Matthew Mondor. - * 4. The name of Matthew Mondor may not be used to endorse or promote - * products derived from this software without specific prior written - * permission. - * 5. Redistribution of source code may not be released under the terms of - * any GNU Public License derivate. - * - * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - - - -#include -#include - - - -int strncmp(const char *s1, const char *s2, size_t max) -{ - if (max > 0) { - register const char *toptr; - - for (toptr = s1, toptr += max; s1 < toptr && *s1 != '\0' && - *s2 != '\0' && *s1 == *s2; s1++, s2++) ; - - return (s1 < toptr ? - ((int)((const unsigned char)*s1 - (const unsigned char)*s2)) : - 0); - } - - return 0; -} diff --git a/Xisop/src/common/kernlib/string/strnlen.c b/Xisop/src/common/kernlib/string/strnlen.c deleted file mode 100644 index 231521e..0000000 --- a/Xisop/src/common/kernlib/string/strnlen.c +++ /dev/null @@ -1,51 +0,0 @@ -/* $Id: strnlen.c,v 1.2 2004/06/03 05:40:04 mmondor Exp $ */ - -/* - * Copyright (C) 2003, Matthew Mondor - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Matthew Mondor. - * 4. The name of Matthew Mondor may not be used to endorse or promote - * products derived from this software without specific prior written - * permission. - * 5. Redistribution of source code may not be released under the terms of - * any GNU Public License derivate. - * - * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - - - -#include -#include - - - -size_t strnlen(const char *str, size_t max) -{ - register const char *fptr, *tptr; - - for (fptr = tptr = str, tptr += max; fptr < tptr && *fptr != '\0'; fptr++) - ; - - return ((size_t)(fptr - str)); -} diff --git a/Xisop/src/common/kernlib/string/strnrchr.c b/Xisop/src/common/kernlib/string/strnrchr.c deleted file mode 100644 index a156119..0000000 --- a/Xisop/src/common/kernlib/string/strnrchr.c +++ /dev/null @@ -1,54 +0,0 @@ -/* $Id: strnrchr.c,v 1.2 2004/06/03 05:40:04 mmondor Exp $ */ - -/* - * Copyright (C) 2003, Matthew Mondor - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Matthew Mondor. - * 4. The name of Matthew Mondor may not be used to endorse or promote - * products derived from this software without specific prior written - * permission. - * 5. Redistribution of source code may not be released under the terms of - * any GNU Public License derivate. - * - * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - - - -#include -#include - - - -char *strnrchr(const char *str, int c, size_t max) -{ - register const char *toptr, *found; - - for (found = NULL, toptr = str, toptr += max; - str < toptr && *str != '\0'; str++) { - if (*str == c) - found = str; - } - - return (char *)found; -} diff --git a/Xisop/src/common/kernlib/string/strrchr.c b/Xisop/src/common/kernlib/string/strrchr.c deleted file mode 100644 index d871c0e..0000000 --- a/Xisop/src/common/kernlib/string/strrchr.c +++ /dev/null @@ -1,53 +0,0 @@ -/* $Id: strrchr.c,v 1.2 2004/06/03 05:40:04 mmondor Exp $ */ - -/* - * Copyright (C) 2003, Matthew Mondor - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Matthew Mondor. - * 4. The name of Matthew Mondor may not be used to endorse or promote - * products derived from this software without specific prior written - * permission. - * 5. Redistribution of source code may not be released under the terms of - * any GNU Public License derivate. - * - * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - - - -#include -#include - - - -char *strrchr(const char *str, int c) -{ - register const char *found; - - for (found = NULL; *str != '\0'; str++) { - if (*str == c) - found = str; - } - - return (char *)found; -} diff --git a/Xisop/src/common/kernlib/string/strspl.c b/Xisop/src/common/kernlib/string/strspl.c deleted file mode 100644 index 0dac4a9..0000000 --- a/Xisop/src/common/kernlib/string/strspl.c +++ /dev/null @@ -1,65 +0,0 @@ -/* $Id: strspl.c,v 1.2 2004/06/03 05:40:04 mmondor Exp $ */ - -/* - * Copyright (C) 2003, Matthew Mondor - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Matthew Mondor. - * 4. The name of Matthew Mondor may not be used to endorse or promote - * products derived from this software without specific prior written - * permission. - * 5. Redistribution of source code may not be released under the terms of - * any GNU Public License derivate. - * - * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - - - -#include -#include - - - -/* Splits columns of a string delimited by sep, and fills - * char **argv with pointers to each column. Returns the number of columns - * that could be filled in. The supplied string IS modified. Note that two - * contiguous separators cause empty entries to be filled in. An exception - * consists of the last separator, which is ignored if nothing is found after - * it. - */ -int strspl(char **argv, char *str, int maxcols, char sep) -{ - register char *ptr, *ptr2; - int col; - - for (col = 0, ptr = str; *ptr != '\0' && col < maxcols; ) { - for (ptr2 = ptr; *ptr != '\0' && *ptr != sep; ptr++) ; - if (*ptr != '\0') - *ptr++ = '\0'; - argv[col++] = ptr2; - } - - return col; -} - - diff --git a/Xisop/src/common/make.sh b/Xisop/src/common/make.sh deleted file mode 100755 index 962d2c7..0000000 --- a/Xisop/src/common/make.sh +++ /dev/null @@ -1,15 +0,0 @@ -#!/bin/sh - -# $Id: make.sh,v 1.1 2003/10/26 21:19:57 mmondor Exp $ - -. ../makedefs.sh - -# Build kernlib -show cd kernlib -./make.sh -show cd .. - -# Build kernel -show cd kernel -./make.sh -show cd .. diff --git a/Xisop/src/common/types.h b/Xisop/src/common/types.h deleted file mode 100644 index c46da28..0000000 --- a/Xisop/src/common/types.h +++ /dev/null @@ -1,133 +0,0 @@ -/* $Id: types.h,v 1.6 2004/06/03 05:54:44 mmondor Exp $ */ - -/* - * Copyright (C) 2003, Matthew Mondor - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Matthew Mondor. - * 4. The name of Matthew Mondor may not be used to endorse or promote - * products derived from this software without specific prior written - * permission. - * 5. Redistribution of source code may not be released under the terms of - * any GNU Public License derivate. - * - * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - - - -#ifndef COMMON_TYPES_H -#define COMMON_TYPES_H - - - -typedef unsigned char u_int8_t; -typedef signed char int8_t; -typedef unsigned short u_int16_t; -typedef signed short int16_t; -typedef unsigned long u_int32_t; -typedef signed long int32_t; -typedef unsigned long long u_int64_t; -typedef signed long long int64_t; -typedef int bool; -typedef u_int32_t size_t; -typedef int32_t ssize_t; -#define NULL ((void *)0) -#define TRUE /* CONSTCOND */(1) -#define FALSE /* CONSTCOND */(0) - -/* For code-embedded copyright and CVS/RCS ID strings */ -#define COPYRIGHT(x) static const char _copyright[] = x -#define RCSID(x) static const char _rcsid[] = x - - -/* Useful to o-align a value relative to v (sizes and pointers) */ -#define OALIGN_CEIL(v, o) \ - ((((size_t)(v)) + (sizeof(o)) - 1) / (sizeof(o)) * (sizeof(o))) -#define OALIGN_FLOOR(v, o) ((size_t)(v) - (((size_t)(v) % sizeof(o)))) - -/* Useful to byte align a value with supplied size */ -#define BALIGN_CEIL(v, s) ((((size_t)(v)) + (s) - 1) / (s) * (s)) -#define BALIGN_FLOOR(v, s) ((size_t)(v) - (((size_t)(v) % (s)))) - -/* Import necessary processor-specific optimization macros */ -#include -/* For network and host byte order of 16-bit and 32-bit types */ -#if defined(_ARCH_LITTLE_ENDIAN) -#define BYTEORDER_NETWORK16 _bswap16 -#define BYTEORDER_HOST16 _bswap16 -#define BYTEORDER_NETWORK32 _bswap32 -#define BYTEORDER_HOST32 _bswap32 -#elif defined(_ARCH_BIG_ENDIAN) -#define BYTEORDER_NETWORK16(w) (w) -#define BYTEORDER_HOST16(w) (w) -#define BYTEORDER_NETWORK32(w) (w) -#define BYTEORDER_HOST32(w) (w) -#else -error "Endian not specified (_ARCH_LITTLE_ENDIAN | _ARCH_BIG_ENDIAN)"; -#endif -/* Ensure that _ARCH_INT_BITS was defined */ -#ifndef _ARCH_INT_BITS -error "Bits in an int (_ARCH_INT_BITS) not specified (8, 16, 32, 64)"; -#endif - -/* common/kernel/device.h */ -typedef struct devicenode devicenode_t; -typedef struct devicehandle device_t; -typedef struct iorequest iorequest_t; - -/* common/kernel/exception.h */ -typedef u_int32_t hookid_t; -typedef struct _int_hook hook_t; -typedef struct _int_facility facility_t; - -/* common/kernel/memory.h */ -typedef struct page page_t; -typedef struct mchunk mchunk_t; -typedef struct pool pool_t; -typedef struct ppool ppool_t; -typedef struct pnode pnode_t; -typedef struct mpool mpool_t; -typedef struct mnode mnode_t; - -/* common/kernel/port.h */ -typedef struct port port_t; -typedef struct message message_t; -typedef struct mmessage mmessage_t; - -/* common/kernel/scheduler.h */ -typedef struct rwlock rwlock_t; - -/* common/kernel/signal.h */ -typedef int signum_t; -typedef u_int32_t sigmask_t; - -/* common/kernel/task.h */ -typedef struct task task_t; -typedef int8_t priority_t; - -/* common/kernlib/hash.h */ -typedef struct hashtable hashtable_t; -typedef struct hashnode hashnode_t; - - -#endif diff --git a/Xisop/src/config.h b/Xisop/src/config.h deleted file mode 100644 index 08cc233..0000000 --- a/Xisop/src/config.h +++ /dev/null @@ -1,50 +0,0 @@ -/* $Id: config.h,v 1.5 2004/01/29 05:02:02 mmondor Exp $ */ - -/* - * Copyright (C) 2003, Matthew Mondor - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Matthew Mondor. - * 4. The name of Matthew Mondor may not be used to endorse or promote - * products derived from this software without specific prior written - * permission. - * 5. Redistribution of source code may not be released under the terms of - * any GNU Public License derivate. - * - * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - - - -#ifndef CONFIG_H -#define CONFIG_H - - - -/* Flags which are possible to uncomment to compile in the kernel */ - -#define DEBUG 4096 -#define STATISTICS - - - -#endif diff --git a/Xisop/src/generic_makedefs.sh b/Xisop/src/generic_makedefs.sh deleted file mode 100644 index a3bf722..0000000 --- a/Xisop/src/generic_makedefs.sh +++ /dev/null @@ -1,40 +0,0 @@ -#!/bin/sh - -# $Id: generic_makedefs.sh,v 1.1 2003/10/26 21:19:57 mmondor Exp $ - -# These are various defaults which are used for the building process. -# Change as required, and don't forget to create the ./port and ./processor -# symbolic links to their respective directory. - -# Local non-cross building tools -L_AR='ar qS' -L_AS='as' -L_CC='gcc' -L_LD='ld' -L_NM='nm' -L_OBJDUMP='objdump' -L_RANLIB='ranlib' -L_STRIP='strip' -L_CAT='cat' -L_DD='dd' - -# Other general purpose tools -L_RM='rm -f' -L_LN='ln -s' -L_SED='sed' -L_ECHO='echo' -L_LS='ls' - -show() -{ - # Only output to stderr, not stdout - $L_ECHO "$@" >&2 - $@ -} - -# Deletes all .o files in a directory -# $1 = directory -cleanlib() -{ - show $L_RM $1/*.o -} diff --git a/Xisop/src/make.sh b/Xisop/src/make.sh deleted file mode 100755 index 3ae7f99..0000000 --- a/Xisop/src/make.sh +++ /dev/null @@ -1,68 +0,0 @@ -#!/bin/sh - -# $Id: make.sh,v 1.1 2003/10/26 21:19:57 mmondor Exp $ - -. ./generic_makedefs.sh - -usage() -{ - $L_ECHO - $L_ECHO 'Usage: ./make.sh -t ' - $L_ECHO - $L_ECHO 'Available targets:' - $L_ECHO '- amiga' - $L_ECHO -} - -while getopts t: f; do - case $f in - t) - case $OPTARG in - amiga) - PROCESSOR='processors/m68k' - PORT='ports/amiga' - ;; - *) - $L_ECHO "Unknown target $OPTARG" - ;; - esac - ;; - *) - usage - exit 0 - ;; - esac -done -if [ -z $PORT ] || [ -z $PROCESSOR ]; then - usage - exit 0 -fi - -./clean.sh -show $L_LN $PROCESSOR processor -show $L_LN $PORT port -show $L_LN $PORT/makedefs.sh makedefs.sh -show export SRCDIR=`pwd` - -# XXX Is this a bug? As port/ symbolic link goes down two levels, I have to -# cd back two levels? - -# Build processor-specific support code to processor/ar/*.a -show cd processor -show ./make.sh - -# Build port-specific support code to port/ar/*.a -show cd ../../port -show ./make.sh - -# Build the portable common code to common/kernel/ar/*.a, -# common/kernlib/ar/*.a and XXX ??? -# and link to global ELF relocatable kernel object xisop.o -show cd ../../common -show ./make.sh - -# Let the port-specific boot code create the kernel image -show cd ../port/boot -show ./make.sh - -show cd .. diff --git a/Xisop/src/ports/amiga/boot/DOTuaerc b/Xisop/src/ports/amiga/boot/DOTuaerc deleted file mode 100644 index b78e08c..0000000 --- a/Xisop/src/ports/amiga/boot/DOTuaerc +++ /dev/null @@ -1,27 +0,0 @@ -config_description=UAE -x11.rom_path=./ -x11.floppy_path=./ -x11.hardfile_path=./ -x11.low_bandwidth=false -x11.use_mitshm=false -x11.hide_cursor=true -32bit_blits=true -use_gui=nowait -use_debugger=false -kickstart_rom_file=/home/mmondor/kick.rom -floppy0=/home/mmondor/src/work/Xisop/src/ports/amiga/boot/xisop.adf -floppy1= -floppy2= -floppy3= -sound_output=none -joyport0=mouse -joyport1=kbd1 -kbd_lang=us -immediate_blits=yes -cpu_speed=max -cpu_type=68000 -chipmem_size=2 -fastmem_size=4 -#filesystem=rw,System3.1:/data2/amiga/System3.1 -#filesystem=rw,Work:/data2/amiga/Work -#filesystem=rw,ASM:/data2/amiga/asm diff --git a/Xisop/src/ports/amiga/boot/README b/Xisop/src/ports/amiga/boot/README deleted file mode 100644 index a088fde..0000000 --- a/Xisop/src/ports/amiga/boot/README +++ /dev/null @@ -1,12 +0,0 @@ -DOTuaerc ~/.uaerc used for testing and developping with UAE - Amiga emulator - -xisop.adf Actual Amiga bootable Xisop floppy image - -image.bin Consists of the actual compiled kernel binary image -image.o Useful for debugging, objdump -drw image.o -bootf/bootf.bin Compiled floppy boot sector image -bootf/kernel.bin Compiled floppy boot sector image + Xisop image - -config.h File to modify depending on your Amiga model and - kernel size, stack size, etc. diff --git a/Xisop/src/ports/amiga/boot/bootf/bootf_c.c b/Xisop/src/ports/amiga/boot/bootf/bootf_c.c deleted file mode 100644 index 7088e55..0000000 --- a/Xisop/src/ports/amiga/boot/bootf/bootf_c.c +++ /dev/null @@ -1,139 +0,0 @@ -/* $Id: bootf_c.c,v 1.2 2004/01/20 20:56:20 mmondor Exp $ */ - -/* - * Copyright (C) 2003, Matthew Mondor - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Matthew Mondor. - * 4. The name of Matthew Mondor may not be used to endorse or promote - * products derived from this software without specific prior written - * permission. - * 5. Redistribution of source code may not be released under the terms of - * any GNU Public License derivate. - * - * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -/* This used to be done by assembly code, it's however best to have a C - * loader which could eventually perform fancy stuff, and relocation as well. - * If the loader eventually gets too large to fit into the default boot block - * size, this boot block can then simply serve to load another loader and - * launch it. - */ - - - -#include - -#include "../config.h" - - -COPYRIGHT("\0\nXisop bootloader Copyright 2001-2003, Matthew Mondor, \ -All rights reserved.\n"); - - - -/* CONFIGURATION */ -#define KERNSTART 1024 /* Start of kernel image on disk */ -#define READSIZE 4096 /* Block size for read/relocation (512 min) */ - - - -#define MEMF_CHIP 0x0001 -#define MEMF_FAST 0x0002 -#define CMD_READ 2 -#define ALRT_NOMEM 0x00010001 -#define ALRT_RERR 0x14000001 - - -struct iostdreq { - u_int8_t pad[28]; - u_int16_t command; - u_int8_t flags; - int8_t error; - u_int32_t actual, length; - void *data; - u_int32_t offset; -}; - - -void *AllocMem(long, long); -void *AllocAbs(long, void *); -void FreeMem(void *, long); -long DoIO(struct iostdreq *); -void Alert(long, void *); - -void load(struct iostdreq *); - - - -void load(struct iostdreq *ioreq) -{ - void *buf = NULL, *readbuf = NULL; - bool ok = FALSE; - - /* Allocate CHIP memory buffer to load disk blocks into */ - if ((readbuf = AllocMem(READSIZE, MEMF_CHIP)) != NULL) { - - if ((buf = AllocAbs(KERNSIZE + STACKSIZE, (void *)KERNADDR)) - == (void *)KERNADDR) { - register u_int32_t offset, *addr, *toaddr; - - /* Read image from disk starting at KERNSTART, upto - * KERNSTART + KERNSIZE, in blocks of READSIZE bytes, which we - * relocate to our kernel buffer on the fly in KERNADDR. - */ - for (offset = KERNSTART, addr = buf, toaddr = buf + KERNSIZE; - addr < toaddr; offset += READSIZE) { - register u_int32_t *raddr, *taddr; - - /* Read a block */ - ioreq->command = CMD_READ; - ioreq->length = READSIZE; - ioreq->data = readbuf; - ioreq->offset = offset; - if ((DoIO(ioreq)) != 0) { - Alert(ALRT_RERR, load); - for (;;) ; - /* NOTREACHED */ - } - - /* Relocate block */ - for (raddr = readbuf, taddr = readbuf + READSIZE; - raddr < taddr; *addr++ = *raddr++) ; - } - ok = TRUE; - } - FreeMem(readbuf, READSIZE); - if (ok) { - void (*code)(u_int32_t *, size_t); - - /* Jump to relocated code, passing it the supervisor stack */ - code = (void *)KERNADDR; - code((u_int32_t *)STACKADDR, STACKSIZE); - } - } - - Alert(ALRT_NOMEM, load); - for (;;) ; - /* NOTREACHED */ -} diff --git a/Xisop/src/ports/amiga/boot/bootf/bootf_s.s b/Xisop/src/ports/amiga/boot/bootf/bootf_s.s deleted file mode 100644 index 2937275..0000000 --- a/Xisop/src/ports/amiga/boot/bootf/bootf_s.s +++ /dev/null @@ -1,118 +0,0 @@ -/* $Id: bootf_s.s,v 1.2 2004/01/20 20:56:20 mmondor Exp $ */ - -/* - * Copyright (C) 2003, Matthew Mondor - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Matthew Mondor. - * 4. The name of Matthew Mondor may not be used to endorse or promote - * products derived from this software without specific prior written - * permission. - * 5. Redistribution of source code may not be released under the terms of - * any GNU Public License derivate. - * - * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - - - -ExecBase = 0x0004 - - -.globl _start, AllocMem, AllocAbs, FreeMem, DoIO, Alert - - -.text - -| AmigaOS boot header (checksum should be calculated after assembling) -| This is actually loaded at 0x0020b660, the ROM jumps at 0x0020b66c. - .long 0x444f5300, 0x00000000, 0x00000000 - -_start: - -| Loader code (startup leaves us with a6 to exec.lib and a1 to IOreq pointer) -| Call our C load() function! -| - movel %a1, %sp@- - bsrl load -| NOTREACHED - addql #4, %sp - rts - - -| Amiga library of required function stubs for load() C function - -| [d0]void * = [-198]AllocMem([d0]long bytes, [d1]long reqs) -| -AllocMem: - moveml %a6/%d1-%d0, %sp@- - moveal ExecBase:w, %a6 - movel %sp@(16), %d0 - movel %sp@(20), %d1 - jsr %a6@(-198) - moveal %d0, %a0 - moveml %sp@+, %d0-%d1/%a6 - rts - -| [d0]void * = [-204]AllocAbs([d0]long bytes, [a1]void *addr) -| -AllocAbs: - moveml %a1/%a6/%d0, %sp@- - moveal ExecBase:w, %a6 - movel %sp@(16), %d0 - moveal %sp@(20), %a1 - jsr %a6@(-204) - moveal %d0, %a0 - moveml %sp@+, %d0/%a6/%a1 - rts - -| void [-210]FreeMem([a1]memptr, [d0]bytesize) -| -FreeMem: - moveml %a1/%a6/%d0, %sp@- - moveal ExecBase:w, %a6 - moveal %sp@(16), %a1 - movel %sp@(20), %d0 - jsr %a6@(-210) - moveml %sp@+, %d0/%a6/%a1 - rts - -| [d0] = [-456]DoIO([a1]struct IORequest *) -| -DoIO: - moveml %a1/%a6, %sp@- - moveal ExecBase:w, %a6 - moveal %sp@(12), %a1 - jsr %a6@(-456) - moveml %sp@+, %a6/%a1 - rts - -| void [-108]Alert([d7]long alertNum,[a5]char *flags) -| -Alert: - moveml %d7/%a5-%a6, %sp@- - moveal ExecBase:w, %a6 - movel %sp@(16), %d7 - moveal %sp@(20), %a5 - jsr %a6@(-108) - moveml %sp@+, %a6-%a5/%d7 - rts diff --git a/Xisop/src/ports/amiga/boot/bootf/bootf_script.ld b/Xisop/src/ports/amiga/boot/bootf/bootf_script.ld deleted file mode 100644 index b422db9..0000000 --- a/Xisop/src/ports/amiga/boot/bootf/bootf_script.ld +++ /dev/null @@ -1,14 +0,0 @@ -OUTPUT_FORMAT("binary", "binary", "binary") -OUTPUT_ARCH(m68k) - -ENTRY(_start) - -SECTIONS { - . = 0x0020b660; - .text : { - *(.text) - *(.rodata) - *(.data) - *(.bss) - } -} diff --git a/Xisop/src/ports/amiga/boot/clean.sh b/Xisop/src/ports/amiga/boot/clean.sh deleted file mode 100755 index 79f30ca..0000000 --- a/Xisop/src/ports/amiga/boot/clean.sh +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/sh - -# $Id: clean.sh,v 1.2 2004/01/20 20:56:20 mmondor Exp $ - -. ../../../generic_makedefs.sh - -show $L_RM image.bin image.o init.o -show $L_RM tools/aosbblock tools/dumpkern tools/config -show $L_RM bootf/bootf_s.o bootf/bootf_c.o bootf/bootf.o bootf/bootf.bin -show $L_RM bootf/kern.bin xisop.adf diff --git a/Xisop/src/ports/amiga/boot/config.h b/Xisop/src/ports/amiga/boot/config.h deleted file mode 100644 index 537e947..0000000 --- a/Xisop/src/ports/amiga/boot/config.h +++ /dev/null @@ -1,72 +0,0 @@ -/* $Id: config.h,v 1.1 2004/01/20 20:56:20 mmondor Exp $ */ - -/* - * Copyright (C) 2003, Matthew Mondor - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Matthew Mondor. - * 4. The name of Matthew Mondor may not be used to endorse or promote - * products derived from this software without specific prior written - * permission. - * 5. Redistribution of source code may not be released under the terms of - * any GNU Public License derivate. - * - * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -/* Until proper memory auto-detection is made, this file contains the general - * information needed to setup the supervisor stack and kernel image location, - * as well as the available RAM. - * This currently assumes that 4 megs of FAST RAM are located from - * 0x00200000 to 0x005fffff (Card 1, Zorro II), and that 2 megs of CHIP RAM - * are found from 0x00001000 to 0x001fffff. - * All these numbers should be a multiple of 4096 bytes. - */ - - - -/* CONFIGURATION */ - -/* Size of kernel image to read from disk, in bytes */ -#define KERNSIZE 65536 - -/* Size of kernel supervisor stack, in bytes */ -#define STACKSIZE 8192 - -/* Boundaries of FAST RAM */ -#define FMEM_START 0x00200000 -#define FMEM_END 0x00600000 - -/* Boundaries of CHIP RAM */ -#define CMEM_START 0x00001000 -#define CMEM_END 0x00200000 - - -/* These are useful results, used by boot/bootf/bootf_c.c, boot/init.c - * and boot/tools/config.c - */ -#define STACKADDR (FMEM_END - STACKSIZE) -#define KERNADDR (STACKADDR - KERNSIZE) -#define FPOOLADDR (FMEM_START) -#define FPOOLSIZE (KERNADDR - FMEM_START - 1) -#define CPOOLADDR (CMEM_START) -#define CPOOLSIZE (CMEM_END - CMEM_START - 1) diff --git a/Xisop/src/ports/amiga/boot/image_script.ld b/Xisop/src/ports/amiga/boot/image_script.ld deleted file mode 100644 index 8ee2279..0000000 --- a/Xisop/src/ports/amiga/boot/image_script.ld +++ /dev/null @@ -1,13 +0,0 @@ -OUTPUT_FORMAT("binary", "binary", "binary") -OUTPUT_ARCH(m68k) - -ENTRY(_start) - -SECTIONS { - .text : { - *(.text) - *(.rodata) - *(.data) - *(.bss) - } -} diff --git a/Xisop/src/ports/amiga/boot/init.c b/Xisop/src/ports/amiga/boot/init.c deleted file mode 100644 index 1e59866..0000000 --- a/Xisop/src/ports/amiga/boot/init.c +++ /dev/null @@ -1,590 +0,0 @@ -/* $Id: init.c,v 1.7 2004/01/20 21:26:48 mmondor Exp $ */ - -/* - * Copyright (C) 2003, Matthew Mondor - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Matthew Mondor. - * 4. The name of Matthew Mondor may not be used to endorse or promote - * products derived from this software without specific prior written - * permission. - * 5. Redistribution of source code may not be released under the terms of - * any GNU Public License derivate. - * - * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -/* This code entry point consist of _start() which is called by the boot - * loader. We setup the supervisor stack pointer and then jump to _init(), - * which sets up the various port-specifics for Xisop, and finally launches - * Xisop itself calling main(). See the Xisop documentation for more - * information on what port-specific code has to provide in it's - * initialization and support.h. - */ - - - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "config.h" - - - -/* Entry point */ -void _start(u_int32_t *, size_t); - -/* Our initialization functions */ -static void _init(void); -static void _disable_interrupts(void); -static void _stop_floppy_motor(void); -static void _init_memory(void); -static void _init_syscalls(void); -static void _enable_keyboard_interrupt(void); -static void _enable_vblank_interrupt(void); -static void _init_scheduler(void); -static void _init_screen(void); - -/* C exception handlers */ -void _icatch1(u_int16_t); -void _icatch2(u_int16_t); -void _icatch3(u_int16_t); -void _icatch4(u_int16_t); -void _icatch5(u_int16_t); -void _tcatch(int); -void _ecatch(int); - - -/* Port-specific port-initialization, to attach our port-specific shared - * libraries and launch our port-specific tasks - */ -struct initmsg { - message_t node; - u_int16_t color; -}; - -struct taskarg { - u_int16_t color; - u_int32_t rounds; -}; - -static int _task_color_server(void *, void *); -static int _task_color_client(void *, void *); -static void _keyboard_hook(hookid_t, int, void *); -void _panic(u_int16_t); - - - -/* Used to ensure that only the first exception occurs */ -static _rlock_t elock; - - - -/* The role of this function is to go in supervisor mode, set the supervisor - * stack, then call init(). - */ -void _start(u_int32_t *ssp, size_t sz) { - _supervisor(ssp, sz, _init); - /* NOTREACHED */ -} - - -/* Main port-specific initialization */ -static void _init(void) -{ - /* So that ecatch() cannot recurse */ - _rlock_init(&elock); - - _disable_interrupts(); - _stop_floppy_motor(); - - memory_init(); /* MI function */ - _init_memory(); - - facilities_init(); - - _init_exceptions(); - - _init_syscalls(); - - _enable_keyboard_interrupt(); - _enable_vblank_interrupt(); - _init_scheduler(); - _init_screen(); - - /* It is necessary to call this other MI function now, which internally - * will enable interrupts when safe to do so, which should have been - * disabled all along initialization. - */ - xisop_init(); - - /* We finally can switch to usermode and jump to MI main(). - * _usermode() m68k-specific function uses 1024 bytes on the - * current supervisor stack to create a user stack, and jumps to - * the specified function in user mode using that small stack. - */ - _usermode(main); - - /* NOTREACHED */ -} - - -static void _disable_interrupts(void) -{ - /* Turn off scheduling, interrupts and DMA channels */ - Forbid(); - /* Disable interrupts */ - _splhigh(); - CUSTOM->INTENA = INTENA_CLRALL; - CUSTOM->INTREQ = INTREQ_CLRALL; - /* Disable DMA */ - CUSTOM->DMACON = DMACON_CLRALL; - /* Disable CIA interrupt generation */ - CIA_A->ICR = CIA_ICR_CLRALL; - CIA_B->ICR = CIA_ICR_CLRALL; -} - - -static void _stop_floppy_motor(void) -{ - /* Stop floppy0 drive motor */ - CIA_B->PRB &= ~CIAB_PRB_FLOP0; /* Select */ - CIA_B->PRB |= CIAB_PRB_FMOTOR; /* Command */ -} - - -static void _init_memory(void) -{ - mchunk_t *mchunk; - - /* Initialize memory system, facilities_init() needs this to be done. - * XXX Some effort should be made to better detect the available memory, - * but at least it's easy to fix for each system type right now. - * The file to modify consists of ../config.h - */ - mchunk = mchunk_init((void *)FPOOLADDR, FPOOLSIZE); - mchunk_attach(_MEM_FAST, mchunk); - mchunk = mchunk_init((void *)CPOOLADDR, CPOOLSIZE); - mchunk_attach(_MEM_CHIP, mchunk); -} - - -static void _init_syscalls(void) -{ - CUSTOM->INTENA = INT_SETCLR | INT_SOFT; -} - - -static void _enable_keyboard_interrupt(void) -{ - /* Enable keyboard interrupts */ - CIA_A->CRA = CIA_CRA_START | CIA_CRA_INMODE; - CIA_A->ICR = CIA_ICR_SETCLR | CIA_ICR_SERDONE; - CUSTOM->INTENA = INT_SETCLR | INT_CIAA; -} - - -static void _enable_vblank_interrupt(void) -{ - /* Enable vertical blank interrupt */ - CUSTOM->INTENA = INT_SETCLR | INT_VBLANK; -} - - -/* Enable CIA-B TimerA interrupt for scheduler, which has high priority */ -static void _init_scheduler(void) -{ - u_int8_t *p; - u_int16_t v; - - /* Evaluate latch value to use for scheduler frequency */ - v = (u_int16_t)(CIA_COUNTSPEED / SCHEDTIMER_HZ); - p = (u_int8_t *)&v; - /* Stop timer and set latch */ - CIA_B->CRA = 0x00; - CIA_B->TAHI = p[0]; - CIA_B->TALO = p[1]; - /* Start and order to load latch value, in contiguous mode */ - CIA_B->CRA = CIA_CRA_START | CIA_CRA_LOAD; - CIA_B->ICR = CIA_ICR_SETCLR | CIA_ICR_TIMA0; - /* Enable CIA-B interrupts */ - CUSTOM->INTENA = INT_SETCLR | INT_CIAB; -} - - -/* Setup our Amiga Xisop console screen */ -/* XXX I have two choices here. I could use a 16 color screen and emulate - * ANSI-BBS colors, or just use a two color one and use my very small fonts. - * in the second case, I could still support a few control codes. - */ -static void _init_screen(void) -{ - u_int16_t s; - - /* I need to allocate/initialize a bitmap/bitplane, as well as - * chip memory for the copper list. - */ - s = asplvblank(); - /* Screen size and position configuration */ - CUSTOM->DDFSTRT = 0x3C; - CUSTOM->DDFSTOP = 0xD4; - CUSTOM->DIWSTRT = 0x2C81; - CUSTOM->DIWSTOP = 0xF4C1; - CUSTOM->CLXCON = 0x0000; - /* Enable bitplane and copper DMA */ - CUSTOM->DMACON = DMACON_SETCLR | DMACON_BLITTER /*| DMACON_COPPER*/; - asplx(s); -} - - -/* These consist of the various interrupt handlers corresponding to each - * of the 6 maskable hardware interrupt levels. Using _spl*() one can set the - * current task level which may not be interrupted by every lower level. - * may be used to evaluate the cause of the interrupt. - */ - -/* ARGSUSED */ -void _icatch1(u_int16_t intreq) -{ - _ipl_t x; - - x = _spl1(); - if (intreq & INT_SOFT) { - /* Software interrupt */ - } - if (intreq & INT_FLOPBLCK) { - /* Floppy disk block done interrupt */ - facility_exechooks(_FACILITY_FLOPPYBLOCK, 0); - } - if (intreq & INT_SERTBE) { - /* Serial RS-232 trasmit buffer empty interrupt */ - facility_exechooks(_FACILITY_SERIALTBE, 0); - } - _splx(x); -} - -void _icatch2(u_int16_t intreq) -{ - _ipl_t x; - - x = _spl2(); - if (intreq & INT_CIAA) { - /* CIA-A or expansion bus pin 19 interrupt */ - u_int8_t icrmask = CIA_A->ICR; - - /* CIA-A TimerA available. - * CIA-A TimerB used for keyboard. - * CIA-A TOD counter available. - */ - if (icrmask & CIA_ICR_TIMB0) { - /* CIA-A TimerB reached 0. If used in continuous we only need - * to react, otherwise we also should reload a latch value back. - * This counts at a rate of 715909/second for NTSC and - * 709379/second for PAL. - */ - facility_exechooks(_FACILITY_CIATIMB0, 0); - } - if (icrmask & CIA_ICR_SERDONE) { - /* Keyboard intput interrupt */ - u_int8_t c; - - c = CIA_A->SDR; - facility_exechooks(_FACILITY_KEYBOARD, (int)c); - } - } - _splx(x); -} - -void _icatch3(u_int16_t intreq) -{ - _ipl_t x; - - x = _spl3(); - if (intreq & INT_COPPER) { - /* Copper processor generated interrupt */ - facility_exechooks(_FACILITY_COPPER, 0); - } - if (intreq & INT_VBLANK) { - /* Vertical blank (raster) interrupt */ - facility_exechooks(_FACILITY_VBLANK, 0); - } - if (intreq & INT_BLITRDY) { - /* Blitter done/ready interrupt */ - facility_exechooks(_FACILITY_BLITTERREADY, 0); - } - _splx(x); -} - -/* ARGSUSED */ -void _icatch4(u_int16_t intreq) -{ - _ipl_t x; - - x = _spl4(); - if (intreq & INT_AUDIO0) { - /* DMA done for audio channel 0 interrupt */ - facility_exechooks(_FACILITY_AUDIO, 0); - } - if (intreq & INT_AUDIO1) { - /* DMA done for audio channel 1 interrupt */ - facility_exechooks(_FACILITY_AUDIO, 1); - } - if (intreq & INT_AUDIO2) { - /* DMA done for audio channel 2 interrupt */ - facility_exechooks(_FACILITY_AUDIO, 2); - } - if (intreq & INT_AUDIO3) { - /* DMA done for audio channel 3 interrupt */ - facility_exechooks(_FACILITY_AUDIO, 3); - } - _splx(x); -} - -/* ARGSUSED */ -void _icatch5(u_int16_t intreq) -{ - _ipl_t x; - - x = _spl5(); - if (intreq & INT_SERRBF) { - /* Serial RS-232 read buffer full interrupt */ - facility_exechooks(_FACILITY_SERIALRBF, 0); - } - if (intreq & INT_FLOPSYNC) { - /* Floppy disk sync pattern found interrupt */ - facility_exechooks(_FACILITY_FLOPPYSYNC, 0); - } - _splx(x); -} - -/* Level 6 handled by assembly code */ - - -/* And generic C trap handler to catch remaining trap vectors (0 and 1 are - * used by the system calls and _yield(), respectively. - */ -void _tcatch(int vector) -{ - _ipl_t x; - - x = _splhigh(); /* XXX */ - facility_exechooks(_FACILITY_TRAP, vector); - _splx(x); -} - - -/* A hack to display the exception vector number using raster lines */ -void _ecatch(int vector) -{ - static u_int16_t ecolors[2] = {0x0F00, 0x0A00}; - register bool col = FALSE; - - if (_rlock_try(&elock)) { - _splhigh(); - for (;;) { - register int i; - - for (i = 0; i < vector; i++) { - CUSTOM->COLOR[0] = ecolors[col]; - ldelay(2); - CUSTOM->COLOR[0] = 0x0000; - ldelay(2); - } - ldelay(8); - col = !col; - } - } - for (;;) - _idle(); -} - - - -/* Function we supply to Xisop which calls it to allow us to initialize - * our port-specific shared libraries and tasks. - */ -void _port_init(void) -{ - register task_t *task; - - if ((task = task_alloc(_task_color_server, NULL, NULL, 0, 4096, TF_KERNEL)) - != NULL) - task_start(task); - else - _panic(0x0F00); /* Red */ -} - - - -/* These are our port-specific tasks */ - -/* ARGSUSED */ -static int _task_color_server(void *res, void *args) -{ - port_t *port; - - /* Create our public port */ - if ((port = port_create("COLOR")) != NULL) { - struct taskarg targ[3]; - task_t *task; - hookid_t kbdhook; - - /* Setup an interrupt handler (only for fun) */ - kbdhook = hook_attach(_FACILITY_KEYBOARD, 10, 0, _keyboard_hook, NULL); - - /* Start our tasks. As these require our port to communicate through, - * note that we created it first. - */ - targ[0].color = 0x00F0; - targ[0].rounds = 6000; - if ((task = task_alloc(_task_color_client, NULL, &targ[0], 0, 4096, - TF_SHARED)) != NULL) - task_start(task); - targ[1].color = 0x00C0; - targ[1].rounds = 4000; - if ((task = task_alloc(_task_color_client, NULL, &targ[1], 0, 4096, - TF_SHARED)) != NULL) - task_start(task); - targ[2].color = 0x0090; - targ[2].rounds = 2000; - if ((task = task_alloc(_task_color_client, NULL, &targ[2], 0, 4096, - TF_SHARED)) != NULL) - task_start(task); - - /* Main loop. All we do is listen for requests via our port, in the - * form of messages. When we get one, display the requested color, - * and reply. This task should be in STATE_WAIT most of the time, - * and only awakens to answer requests then goes back to sleep. - */ - for (;;) { - port_t *ports[] = { - port - }; - register struct initmsg *msg = NULL; - - /* Black to show that we are sleeping */ - CUSTOM->COLOR[0] = 0x0000; - ldelay(1); - if ((port_wait(ports, 1, NULL)) == port) { - /* We could use while () here instead of if (), however this - * gives a better demonstration. When the screen eventually - * gets purple, all tasks are sleeping (init, reaper and - * this task, since our three children have died already). - * If we use while (), we never go in sleep mode, since there - * are several feeders and they are of equal priority than - * us. - */ - if ((msg = (struct initmsg *)port_get(port)) != NULL) { - CUSTOM->COLOR[0] = msg->color; - if (!port_reply((message_t *)msg)) - _panic(0x0F00); /* Red */ - } - } else - _panic(0x0FF0); /* Yellow */ - } - - port = port_destroy(port); - } - - _panic(0x0FFF); /* White */ - /* NOTREACHED */ - return 0; -} - - -/* ARGSUSED */ -static int _task_color_client(void *res, void *args) -{ - port_t *rport; - - /* Create our reply port */ - if ((rport = port_create(NULL)) != NULL) { - port_t *sport; - - /* Locate task_init()'s public port */ - if ((sport = port_find("COLOR")) != NULL) { - struct initmsg msg; - port_t *ports[] = { - rport - }; - struct taskarg *targ = args; - register u_int32_t rounds = targ->rounds; - - msg.color = targ->color; - /* Send coloring requests via the public COLOR port. - * For each request we wait for a reply, and then continue. - * This task runs most of the time, only sleeping when waiting - * for a result from the slave task. - */ - for (;rounds > 0; rounds--) { - if (port_send(sport, rport, (message_t *)&msg)) { - if ((port_wait(ports, 1, NULL)) == rport) { - /* Just get rid of messages since we don't need to - * verify a result code. - */ - port_flush(rport); - } else - _panic(0x0FF0); /* Yellow */ - } else - _panic(0x0F00); /* Red */ - } - } - - rport = port_destroy(rport); - } - - return 0; -} - - -/* Used in the case of a fatal error we catch */ -void _panic(u_int16_t color) -{ - sched_disable(); - sys_int_disable(); - for (;;) { - CUSTOM->COLOR[0] = color; - ldelay(2); - CUSTOM->COLOR[0] = 0x0000; - ldelay(2); - } - /* NOTREACHED */ -} - - -/* Mainly used as a test for now */ -/* ARGSUSED */ -static void _keyboard_hook(hookid_t hookid, int reason, void *udata) -{ - register u_int16_t col = 0; - - /* Just display a color corresponding to the keyboard code */ - col |= reason; col <<= 8; col |= reason; - CUSTOM->COLOR[0] = col; -} diff --git a/Xisop/src/ports/amiga/boot/make.sh b/Xisop/src/ports/amiga/boot/make.sh deleted file mode 100755 index 820e10d..0000000 --- a/Xisop/src/ports/amiga/boot/make.sh +++ /dev/null @@ -1,33 +0,0 @@ -#!/bin/sh - -# $Id: make.sh,v 1.2 2004/01/20 20:56:20 mmondor Exp $ - -. ../../../makedefs.sh - -# Create tools -show $L_CC -Wall -o tools/aosbblock tools/aosbblock.c -show $L_CC -Wall -o tools/dumpkern tools/dumpkern.c -show $L_CC -Wall -o tools/config tools/config.c -KERNADDR=$(tools/config) - -# Create kernel image -show $C_COMPC -I$SRCDIR init.c -A=`$L_LS ../../../common/kernel/ar/*.a ../ar/*.a ../../../processor/ar/*.a ../../../common/kernlib/ar/*.a` -show $C_LD -nostdlib -e _start -Ttext $KERNADDR -o image.o init.o $A -show $C_LD -T image_script.ld -Ttext $KERNADDR -nostdlib -o image.bin init.o $A - -# Create bootblock loader -show $C_COMPS -I$SRCDIR -o bootf/bootf_s.o bootf/bootf_s.s -show $C_COMPC -I$SRCDIR -o bootf/bootf_c.o bootf/bootf_c.c -show $C_LD -nostdlib -r -Ttext 0x0020b660 -o bootf/bootf.o bootf/bootf_s.o bootf/bootf_c.o -show $C_LD -T bootf/bootf_script.ld -nostdlib -o bootf/bootf.bin bootf/bootf.o -show tools/aosbblock bootf/bootf.bin - -# Create bootable floppy image -show $L_CAT bootf/bootf.bin image.bin >bootf/kern.bin -show $L_DD if=/dev/zero of=xisop.adf bs=901120 count=1 -show tools/dumpkern xisop.adf bootf/kern.bin - -$L_ECHO === -$L_ECHO The floppy image should be $SRCDIR/ports/amiga/boot/xisop.adf -$L_ECHO === diff --git a/Xisop/src/ports/amiga/boot/tools/aosbblock.c b/Xisop/src/ports/amiga/boot/tools/aosbblock.c deleted file mode 100644 index 95cbeae..0000000 --- a/Xisop/src/ports/amiga/boot/tools/aosbblock.c +++ /dev/null @@ -1,107 +0,0 @@ -/* $Id: aosbblock.c,v 1.1 2003/10/26 21:19:57 mmondor Exp $ */ - -/* - * Copyright (C) 2003, Matthew Mondor - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Matthew Mondor. - * 4. The name of Matthew Mondor may not be used to endorse or promote - * products derived from this software without specific prior written - * permission. - * 5. Redistribution of source code may not be released under the terms of - * any GNU Public License derivate. - * - * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -/* - * Fixes a bootblock to be 1024 bytes in size, and sets it's AmigaOS checksum - */ - - - -#include -#include - - - -int main(int, char **); -u_int32_t checksum(u_int32_t *, int); - - - -int main(int argc, char **argv) -{ - FILE *fh; - u_int32_t lblock[256]; - int i; - char *block = (char *)lblock; - - if (argc == 2) { - - if ((fh = fopen(argv[1], "rb"))) { - /* First make sure to 0 pad block, then load block */ - for (i = 0; i < 256; i++) - lblock[i] = 0; - fread(block, 1, 1024, fh); - fclose(fh); - } else { - printf("could not open file \"%s\" for reading\n", argv[1]); - exit(-1); - } - - /* Calculate block checksum and fix it */ - lblock[1] = 0; - lblock[1] = htonl(0xFFFFFFFF - checksum(lblock, 256)); - - /* Write back file over old one */ - if ((fh = fopen(argv[1], "wb"))) { - fwrite(block, 1, 1024, fh); - fclose(fh); - } else { - printf("could not open file \"%s\" for writing\n", argv[1]); - exit(-1); - } - - } else { - printf("usage: aosbblock \n"); - exit(-1); - } - - exit(0); -} - - -u_int32_t checksum(u_int32_t *block, int size) -{ - u_int32_t sum, lastsum; - int i; - - for (sum = 0, i = 0; i < size; i++) { - lastsum = sum; - sum += ntohl(block[i]); - if (sum < lastsum) - ++sum; - } - - return sum; -} diff --git a/Xisop/src/ports/amiga/boot/tools/config.c b/Xisop/src/ports/amiga/boot/tools/config.c deleted file mode 100644 index 116feca..0000000 --- a/Xisop/src/ports/amiga/boot/tools/config.c +++ /dev/null @@ -1,64 +0,0 @@ -/* $Id: config.c,v 1.1 2004/01/20 20:56:20 mmondor Exp $ */ - -/* - * Copyright (C) 2003, Matthew Mondor - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Matthew Mondor. - * 4. The name of Matthew Mondor may not be used to endorse or promote - * products derived from this software without specific prior written - * permission. - * 5. Redistribution of source code may not be released under the terms of - * any GNU Public License derivate. - * - * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -/* This program is useful to know the proper address to use in image_script.ld - * and make.sh for building the kernel image. - */ - - - -#include -#include "../config.h" - - - -int main(void); - - - -int main(void) -{ - printf("0x%08x\n", KERNADDR); - /* - printf("Supervisor stack location (0x%08x bytes): 0x%08x\n", - STACKSIZE, STACKADDR); - printf("Kernel image location (0x%08x bytes): 0x%08x\n", - KERNSIZE, KERNADDR); - printf("Usable multipurpose RAM: (0x%08x bytes) 0x%08x\n", - POOLSIZE, POOLADDR); - */ - - return 0; -} diff --git a/Xisop/src/ports/amiga/boot/tools/dumpkern.c b/Xisop/src/ports/amiga/boot/tools/dumpkern.c deleted file mode 100644 index b39abe0..0000000 --- a/Xisop/src/ports/amiga/boot/tools/dumpkern.c +++ /dev/null @@ -1,80 +0,0 @@ -/* $Id: dumpkern.c,v 1.1 2003/10/26 21:19:57 mmondor Exp $ */ - -/* - * Copyright (C) 2003, Matthew Mondor - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Matthew Mondor. - * 4. The name of Matthew Mondor may not be used to endorse or promote - * products derived from this software without specific prior written - * permission. - * 5. Redistribution of source code may not be released under the terms of - * any GNU Public License derivate. - * - * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -/* Simple program to allow to dump a kernel image over an existing file - * (ususally to serve as a virtual floppy for bochs or to create - * floppy image files), without changing the size of the file. - */ - - - -#include -#include -#include -#include - - - -#define BUFSIZE 4096 - - - -int main(int, char **); - - - -int main(int argc, char **argv) -{ - if (argc == 3) { - int ffd, kfd; - - if ((ffd = open(argv[1], O_RDWR)) != -1) { - if ((kfd = open(argv[2], O_RDONLY)) != -1) { - char buf[BUFSIZE]; - size_t len; - - while ((len = read(kfd, buf, BUFSIZE)) > 0) - if ((write(ffd, buf, len)) != len) { - fprintf(stderr, "Error writing\n"); - break; - } - close(kfd); - } else fprintf(stderr, "Cannot open kernel file '%s'\n", argv[2]); - close(ffd); - } else fprintf(stderr, "Cannot open disk file '%s'\n", argv[1]); - } else fprintf(stderr, "Usage: dumpkern \n"); - - exit(0); -} diff --git a/Xisop/src/ports/amiga/clean.sh b/Xisop/src/ports/amiga/clean.sh deleted file mode 100755 index 7058be2..0000000 --- a/Xisop/src/ports/amiga/clean.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/sh - -# $Id: clean.sh,v 1.1 2003/10/26 21:19:57 mmondor Exp $ - -. ../../generic_makedefs.sh - -show $L_RM support_s.o support_c.o ar/*.a diff --git a/Xisop/src/ports/amiga/make.sh b/Xisop/src/ports/amiga/make.sh deleted file mode 100755 index 9d58b03..0000000 --- a/Xisop/src/ports/amiga/make.sh +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/sh - -# $Id: make.sh,v 1.1 2003/10/26 21:19:57 mmondor Exp $ - -. ../../makedefs.sh - -show $C_COMPS -I$SRCDIR support_s.s -show $C_COMPC -I$SRCDIR support_c.c -show $C_AR ar/support.a support_s.o support_c.o -show $C_RANLIB ar/support.a diff --git a/Xisop/src/ports/amiga/makedefs.sh b/Xisop/src/ports/amiga/makedefs.sh deleted file mode 100644 index b1eef0c..0000000 --- a/Xisop/src/ports/amiga/makedefs.sh +++ /dev/null @@ -1,71 +0,0 @@ -#!/bin/sh - -# $Id: makedefs.sh,v 1.2 2003/12/27 00:01:55 mmondor Exp $ - -# These are various defaults which are used for the building process. -# Change as required, and don't forget to create the ./port and ./processor -# symbolic links to their respective directory. - -# Local non-cross building tools -L_AR='ar qS' -L_AS='as' -L_CC='gcc' -L_LD='ld' -L_NM='nm' -L_OBJDUMP='objdump' -L_RANLIB='ranlib' -L_STRIP='strip' -L_CAT='cat' -L_DD='dd' - -# Cross building tools (set to same values as L_* if wanted) -C_PREFIX='/usr/src/tools/bin' -C_AR="$C_PREFIX/m68k--netbsdelf-ar qS" -C_AS="$C_PREFIX/m68k--netbsdelf-as" -C_CC="$C_PREFIX/m68k--netbsdelf-gcc" -C_LD="$C_PREFIX/m68k--netbsdelf-ld" -C_NM="$C_PREFIX/m68k--netbsdelf-nm" -C_OBJDUMP="$C_PREFIX/m68k--netbsdelf-objdump" -C_RANLIB="$C_PREFIX/m68k--netbsdelf-ranlib" -C_STRIP="$C_PREFIX/m68k--netbsdelf-strip" - -# Other general purpose tools -L_RM='rm -f' -L_LN='ln -s' -L_SED='sed' -L_ECHO='echo' -L_LS='ls' - -# Command to compile a C module using the cross-compiler -#C_COMPC="$C_CC -Wall -ffreestanding -nostdinc -m68000 -O2 -fno-function-cse -fomit-frame-pointer -c" -C_COMPC="$C_CC -Wall -ffreestanding -nostdinc -m68000 -O2 -fno-function-cse -fomit-frame-pointer -c" -# And to compile an assembly module using the cross-assembler -C_COMPS="$C_CC -Wall -ffreestanding -nostdinc -m68000 -c" - -show() -{ - # Only output to stderr, not stdout - $L_ECHO "$@" >&2 - $@ -} - -# Useful to compile all .c and .s files in a directory to .o modules -# $1 = directory -buildlib() -{ - for i in `$L_LS $1/*.s 2>/dev/null`; do - DEST=`$L_ECHO $i | $L_SED 's/\.s/\.o/'` - show $C_COMPS -I$SRCDIR -o $DEST $i - done - for i in `$L_LS $1/*.c 2>/dev/null`; do - DEST=`$L_ECHO $i | $L_SED 's/\.c/\.o/'` - show $C_COMPC -I$SRCDIR -o $DEST $i - done -} - -# Deletes all .o files in a directory -# $1 = directory -cleanlib() -{ - show $L_RM $1/*.o -} diff --git a/Xisop/src/ports/amiga/support.h b/Xisop/src/ports/amiga/support.h deleted file mode 100644 index bdd609b..0000000 --- a/Xisop/src/ports/amiga/support.h +++ /dev/null @@ -1,467 +0,0 @@ -/* $Id: support.h,v 1.1 2003/10/26 21:19:57 mmondor Exp $ */ - -/* - * Copyright (C) 2001-2003, Matthew Mondor - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Matthew Mondor. - * 4. The name of Matthew Mondor may not be used to endorse or promote - * products derived from this software without specific prior written - * permission. - * 5. Redistribution of source code may not be released under the terms of - * any GNU Public License derivate. - * - * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -/* - * This code was not derived from original AmigaOS headerfiles, but rather - * made comparing informations from various books I already had at home, - * such as "Mapping the Amiga", "La Bible de l'Amiga", and "Amiga Intern". - * - * Matt - */ - - - -#ifndef AMIGA_SUPPORT_H -#define AMIGA_SUPPORT_H - - - -#include - - - -/* Adapt this as required for NTSC or PAL Amigas */ - -/* CIA TimerA and TimerB count this amount of ticks per second */ -/* NTSC */ -/*#define CIA_COUNTSPEED 715909*/ -/* PAL */ -#define CIA_COUNTSPEED 709379 - - - -/* The Amiga has two CIA chips, each CIA supports two 8-bit parallel ports - * and one serial port. Each CIA chip also has two timers. Amiga also has - * various custom chips (Agnus, Denise, Paula, Ramsey, the copper parallel - * coprocessor)... - * - * The use of the CIA timers and counters is as follows: - * - CIA-A TOD counter available - * - CIA-A TimerA used for keyboard timing - * - CIA-A TimerB available - * - CIA-B TOD counter available - * - CIA-B TimerA available, we use it for the Xisop scheduler interrupt. - * - CIA-B TimerB available - * - * Xisop kernel itself may not need to use all those registers, however - * devices may, and will be able to use this same headerfile. - */ - -#define CIA_A ((volatile CIA *)0x00BFE001) -#define CIA_B ((volatile CIA *)0x00BFD000) -#define CUSTOM ((volatile CUST *)0x00DFF000) - - -/* Each of the two CIAs have this structure (8520 chip as mapped in Amiga) */ -typedef struct CIA { - volatile u_int8_t PRA; /* Parallel port register A */ - u_int8_t pad1[255]; - volatile u_int8_t PRB; /* Parallel port register B */ - u_int8_t pad2[255]; - volatile u_int8_t DDRA; /* Parallel port A data direction register */ - u_int8_t pad3[255]; - volatile u_int8_t DDRB; /* Parallel port B data direction register */ - u_int8_t pad4[255]; - volatile u_int8_t TALO; /* Timer A lower 8 bits */ - u_int8_t pad5[255]; - volatile u_int8_t TAHI; /* Timer A upper 8 bits */ - u_int8_t pad6[255]; - volatile u_int8_t TBLO; /* Timer B lower 8 bits */ - u_int8_t pad7[255]; - volatile u_int8_t TBHI; /* Timer B upper 8 bits */ - u_int8_t pad8[255]; - volatile u_int8_t E_LSB; /* Event counter bits 0-7 */ - u_int8_t pad9[255]; - volatile u_int8_t E_MID; /* Event counter bits 8-15 */ - u_int8_t pad10[255]; - volatile u_int8_t E_MSB; /* Event counter bits 16-23 */ - u_int8_t pad11[255 + 256]; - volatile u_int8_t SDR; /* Serial port data register */ - u_int8_t pad12[255]; - volatile u_int8_t ICR; /* Interrupt control register */ - u_int8_t pad13[255]; - volatile u_int8_t CRA; /* Control register A */ - u_int8_t pad14[255]; - volatile u_int8_t CRB; /* Control register B */ - u_int8_t pad15[255]; -} CIA; - -/* Here are defined common CIA register bits for code clarity */ -#define CIA_ICR_TIMA0 (1 << 0) -#define CIA_ICR_TIMB0 (1 << 1) -#define CIA_ICR_TODALRM (1 << 2) -#define CIA_ICR_SERDONE (1 << 3) -#define CIA_ICR_SIGFLAG (1 << 4) -#define CIA_ICR_CIAINT (1 << 7) -#define CIA_ICR_SETCLR (1 << 7) -#define CIA_ICR_CLRALL 0x7F - -#define CIA_CRA_START (1 << 0) -#define CIA_CRA_PBON (1 << 1) -#define CIA_CRA_OUTMODE (1 << 2) -#define CIA_CRA_RUNMODE (1 << 3) -#define CIA_CRA_LOAD (1 << 4) -#define CIA_CRA_INMODE (1 << 5) -#define CIA_CRA_SPMODE (1 << 6) - -#define CIA_CRB_START (1 << 0) -#define CIA_CRB_PBON (1 << 1) -#define CIA_CRB_OUTMODE (1 << 2) -#define CIA_CRB_RUNMODE (1 << 3) -#define CIA_CRB_LOAD (1 << 4) -#define CIA_CRB_INMODE (1 << 5 | 1 << 6) -#define CIA_CRB_ALARM (1 << 7) - -/* These are Amiga-specific and are different for each of the two CIAs */ -#define CIAA_PRA_MOVL (1 << 0) /* Memory overlay bit */ -#define CIAA_PRA_LED (1 << 1) /* Pwr LED & aud low pass filt stat */ -#define CIAA_PRA_FCHNG (1 << 2) /* Floppy disk change */ -#define CIAA_PRA_FRONLY (1 << 3) /* Floppy read-only */ -#define CIAA_PRA_FTRK0 (1 << 4) /* Floppy disk track 0 */ -#define CIAA_PRA_FRDY (1 << 5) /* Floppy disk ready for commands */ -#define CIAA_PRA_FIRE0 (1 << 6) /* Button 1 Port 1 pressed */ -#define CIAA_PRA_FIRE1 (1 << 7) /* Button 1 port 2 pressed */ - -#define CIAA_PRB_CDATA 0xFF /* Centronics data pins/bits */ - -#define CIAB_PRA_PBUSY (1 << 0) /* Centronics BUSY */ -#define CIAB_PRA_POUT (1 << 1) /* Centronics paper out */ -#define CIAB_PRA_PSEL (1 << 2) /* Centronics select */ -#define CIAB_PRA_SDSR (1 << 3) /* RS-232 Data Set Ready */ -#define CIAB_PRA_SCTS (1 << 4) /* RS-232 Clear To Send */ -#define CIAB_PRA_SDCD (1 << 5) /* RS-232 Carrier Detect */ -#define CIAB_PRA_SRTS (1 << 6) /* RS-232 Request To Send */ -#define CIAB_PRA_SDTR (1 << 7) /* RS-232 Data Terminal Ready */ - -#define CIAB_PRB_FHSTEP (1 << 0) /* Floppy head step */ -#define CIAB_PRB_FHDIR (1 << 1) /* Floppy head direction */ -#define CIAB_PRB_FSIDE (1 << 2) /* Floppy side */ -#define CIAB_PRB_FLOP0 (1 << 3) /* Select Floppy drive 0 */ -#define CIAB_PRB_FLOP1 (1 << 4) /* Select Floppy drive 1 */ -#define CIAB_PRB_FLOP2 (1 << 5) /* Select Floppy drive 2 */ -#define CIAB_PRB_FLOP3 (1 << 6) /* Select Floppy drive 3 */ -#define CIAB_PRB_FMOTOR (1 << 7) /* Floppy motor status */ - - -/* Useful union to represent 32-bit address split into two 16-bit registers */ -typedef union AADDR { - struct { - void *addr; /* Always 32-bit on m68k */ - } full; - struct { - u_int16_t addr_h; /* Bits 16-31 */ - u_int16_t addr_l; /* Bits 0-15 */ - } parial; -} AADDR; - -/* Each of the 4 native 8-bit Amiga channels are controled with this */ -typedef struct AUDCHAN { - AADDR AUDLC; /* Address of audio data */ - u_int16_t AUDLEN; /* Length of audio data */ - u_int16_t AUDPER; /* Period duration */ - u_int16_t AUDVOL; /* Channel volume */ - u_int16_t AUDDAT; /* Audio data (to D/A converter) */ - u_int16_t pad1[2]; /* Unused */ -} AUDCHAN; - -/* Sprite information */ -typedef struct SPR { - u_int16_t SPRPOS; /* Sprite start position (vert. and horiz.) */ - u_int16_t SPRCTL; /* Control register and vertical stop */ - u_int16_t SPRDATA; /* Data register A (to RGB output) */ - u_int16_t SPRDATB; /* Data register B (to RGB output) */ -} SPR; - - -/* And, the Amiga custom chips registers. */ -typedef struct CUST { - u_int16_t BLTDDAT; /* Blitter output data (Blitter to RAM) */ - u_int16_t DMACONR; /* Read DMA control register */ - u_int16_t VPOSR; /* MSB of vertical position */ - u_int16_t VHPOSR; /* Vertical and horizontal beam position */ - u_int16_t DSKDATR; /* Dist read data (disk to RAM) */ - u_int16_t JOY0DAT; /* Joystick/Mouse position game port 0 */ - u_int16_t JOY1DAT; /* Joystick/Mouse position game port 1 */ - u_int16_t CLXDAT; /* Collision register */ - u_int16_t ADKCONR; /* Read audio/disk control register */ - u_int16_t POT0DAT; /* Read potentiometer game port 0 */ - u_int16_t POT1DAT; /* Read potentiometer game port 1 */ - u_int16_t POTGOR; /* Read potentiometer port data */ - u_int16_t SERDATR; /* Read serial port and status */ - u_int16_t DSKBYTR; /* Read disk data byte and status */ - u_int16_t INTENAR; /* Read interrupt enable */ - u_int16_t INTREQR; /* Read interrupt request */ - AADDR DSKPTR; /* Disk DMA address */ - u_int16_t DSKLEN; /* Disk DMA block length */ - u_int16_t DSKDAT; /* Disk write data (RAM to disk) */ - u_int16_t REFPTR; /* Refresh counter */ - u_int16_t VPOSW; /* Write MSB of vertical beam position */ - u_int16_t VHPOSW; /* Write vertical and horizontal beam position */ - u_int16_t COPCON; /* Copper control register */ - u_int16_t SERDAT; /* Write serial data and stop bits */ - u_int16_t SERPER; /* Serial port control register and baud rate */ - u_int16_t POTGO; /* Write potentiometer port data and start bit */ - u_int16_t JOYTEST; /* Write in both mouse counters */ - u_int16_t STREQU; /* Horizontal sync with VBlank and equal frame */ - u_int16_t STRVBL; /* Horizontal sync wuth vertical blank */ - u_int16_t STRHOR; /* Horizontal synchronization signal */ - u_int16_t STRLONG; /* Long horizontal line marker */ - /* Following can also be accessed by the copper processor if COPCON=1 */ - u_int16_t BLTCON0; /* Blitter control register 0 */ - u_int16_t BLTCON1; /* Blitter control register 1 */ - u_int16_t BLTAFWM; /* Mask for first data word from A */ - u_int16_t BLTALWM; /* Mask for last data word from A */ - AADDR BLTCPTR; /* Address of source data C */ - AADDR BLTBPTR; /* Address of source data B */ - AADDR BLTAPTR; /* Address of source data A */ - AADDR BLTDPTR; /* Address of destination data D */ - u_int16_t BLTSIZE; /* Start bit and size of blitter window */ - u_int16_t BLTCON0L; /* Like BLTCON0, bits 0-7 */ - u_int16_t BLTSIZEV; /* Windth of blitter window */ - u_int16_t BLTSIZEH; /* Height of blitter window */ - u_int16_t BLTCMOD; /* Blitter modulo for source data C */ - u_int16_t BLTBMOD; /* Blitter modulo for source data B */ - u_int16_t BLTAMOD; /* Blitter modulo for source data A */ - u_int16_t BLTDMOD; /* Blitter modulo for destination data D */ - u_int16_t pad1[4]; /* Unused */ - u_int16_t BLTCDAT; /* Blitter source data register C */ - u_int16_t BLTBDAT; /* Blitter source data register B */ - u_int16_t BLTADAT; /* Blitter source data register A */ - u_int16_t pad2[3]; /* Unused */ - u_int16_t DENISEID; /* Chip identification from Denise */ - u_int16_t DSKSYNC; /* Disk sync pattern */ - /* The following registers can always be written to by the Copper */ - AADDR COP1LPTR; /* Address of first copper list */ - AADDR COP2LPTR; /* Address of second copper list */ - u_int16_t COPJMP1; /* Jump to start of first copper list */ - u_int16_t COPJMP2; /* Jump to start of second copper list */ - u_int16_t COPINS; /* Copper command register */ - u_int16_t DIWSTRT; /* Upper left corner of display window */ - u_int16_t DIWSTOP; /* Lower right corner of display window */ - u_int16_t DDFSTRT; /* Start of bitplane DMA (horiz. pos.) */ - u_int16_t DDFSTOP; /* End of bitplane DMA (horiz. pos.) */ - u_int16_t DMACON; /* Write DMA control register */ - u_int16_t CLXCON; /* Write collision control register */ - u_int16_t INTENA; /* Write interrupt enable */ - u_int16_t INTREQ; /* Write interrupt request */ - u_int16_t ADKCON; /* Audio, disk abd UART control register */ - AUDCHAN AUDCH[4]; /* Four digital audio channels */ - AADDR BPLPTR[6]; /* Six bitplane addresses */ - u_int16_t pad3[4]; /* Unused */ - u_int16_t BPLCON0; /* Bitplane control register 0 */ - u_int16_t BPLCON1; /* Bitplane control register 1 (scroll values) */ - u_int16_t BPLCON2; /* Bitplane control register 2 (priority control) */ - u_int16_t BPLCON3; /* Bitplane control register 3 */ - u_int16_t BPL1MOD; /* Bitplane modulo for uneven planes */ - u_int16_t BPL2MOD; /* Bitplane modulo for even planes */ - u_int16_t pad4[2]; /* Unused */ - u_int16_t BPLDAT[6];/* Bitplane data (to RGB output) */ - u_int16_t pad5[2]; /* Unused */ - AADDR SPRDATPTR[8]; /* Sprite data registers */ - SPR SPR[8]; /* Sprite control registers */ - u_int16_t COLOR[32];/* Color pallete registers (color table) */ - u_int16_t HTOTAL; /* Clock count per line (VARBEAM=1) */ - u_int16_t HSSTOP; /* H-sync stop position */ - u_int16_t HBSTRT; /* H-blank start position */ - u_int16_t HBSTOP; /* H-blank stop position */ - u_int16_t VTOTAL; /* Number of lines per picture */ - u_int16_t VSSTOP; /* V-sync stop line */ - u_int16_t VBSTRT; /* V-blank start line */ - u_int16_t VBSTOP; /* V-blank stop line */ - u_int16_t SPRHSTRT; /* UHRES sprite start line */ - u_int16_t SPRHSTOP; /* UHRES sprite stop line */ - u_int16_t BPLHSTRT; /* UHRES bitplane start line */ - u_int16_t BPLHSTOP; /* UHRES bitplane stop line */ - u_int16_t HHPOSW; /* Write DUAL-mode column counter */ - u_int16_t HHPOSR; /* Read DUAL-mode column counter */ - u_int16_t BEAMCON0; /* Raster beam control register */ - u_int16_t HSSTRT; /* H-sync start position */ - u_int16_t VSSTRT; /* V-sync start position */ - u_int16_t HCENTER; /* H-pos. of V-sync in interlace mode */ - u_int16_t DIWHIGH; /* Screen window, upper bits for start/stop */ - u_int16_t BPLHMOD; /* UHRES bitplane modulo */ - AADDR SPRHPTR; /* UHRES sprite pointer */ - AADDR BPLHPTR; /* UHRES bitplane pointer */ - u_int16_t pad6[7]; /* Unused */ -} CUST; - -/* And again some bits definitions for code clarity */ -#define DMACON_AUDIO0 (1 << 0) -#define DMACON_AUDIO1 (1 << 1) -#define DMACON_AUDIO2 (1 << 2) -#define DMACON_AUDIO3 (1 << 3) -#define DMACON_AUDIO (1 << 0 | 1 << 1 | 1 << 2 | 1 << 3) -#define DMACON_FLOPPY (1 << 4) -#define DMACON_SPRITE (1 << 5) -#define DMACON_BLITTER (1 << 6) -#define DMACON_COPPER (1 << 7) -#define DMACON_BITPLANE (1 << 8) -#define DMACON_MASTER (1 << 9) -#define DMACON_PRIORITY (1 << 10) -#define DMACON_ZEROS (1 << 13) -#define DMACON_BLITTING (1 << 14) -#define DMACON_SETCLR (1 << 15) -#define DMACON_CLRALL 0x07FF - -#define ADKCON_0MODVOL1 (1 << 0) -#define ADKCON_1MODVOL2 (1 << 1) -#define ADKCON_2MODVOL3 (1 << 2) -#define ADKCON_3OFF (1 << 3) -#define ADKCON_0MODPER1 (1 << 4) -#define ADKCON_1MODPER2 (1 << 5) -#define ADKCON_2MODPER3 (1 << 6) -#define ADKCON_3OFF2 (1 << 7) -#define ADKCON_FLOP_GCR (1 << 8) -#define ADKCON_GCR_SYNC (1 << 9) -#define ADKCON_SYNC (1 << 10) -#define ADKCON_UARTBRK (1 << 11) -#define ADKCON_FLOP_MFM (1 << 12) -#define ADKCON_SETCLR (1 << 15) - -/* Most of these are shared by INTREQ, INTREQR, INTENA, INTENAR */ -#define INT_SERTBE (1 << 0) -#define INT_FLOPBLCK (1 << 1) -#define INT_SOFT (1 << 2) -#define INT_CIAA (1 << 3) -#define INT_COPPER (1 << 4) -#define INT_VBLANK (1 << 5) -#define INT_BLITRDY (1 << 6) -#define INT_AUDIO0 (1 << 7) -#define INT_AUDIO1 (1 << 8) -#define INT_AUDIO2 (1 << 9) -#define INT_AUDIO3 (1 << 10) -#define INT_AUDIO (1 << 7 | 1 << 8 | 1 << 9 | 1 << 10) -#define INT_SERRBF (1 << 11) -#define INT_FLOPSYNC (1 << 12) -#define INT_CIAB (1 << 13) -#define INT_LEVEL6 (1 << 14) /* INTREQR */ -#define INT_MASTER (1 << 14) /* INTENA */ -#define INT_SETCLR (1 << 15) /* INTENA & INTREQ */ -#define INTENA_CLRALL 0x3FFF -#define INTREQ_CLRALL 0x7FFF - - - - -/* Stuff we need to initialize before calling Xisop's main() */ - -/* -#define MEMF_CHIP 0x0001 -#define MEMF_FAST 0x0002 - -void *OpenLibrary(char *, long); -void CloseLibrary(void *); -long AvailMem(long); -void *AllocMem(long, long); -void *AllocAbs(long, void *); -void FreeMem(void *, long); -void Permit(void); -void Enable(void); -void *SuperState(void); -void UserState(void *); - -void jdelay(u_int32_t); -u_int32_t atime(void); -*/ -void ldelay(u_int32_t); - -void Alert(long, void *); -void Forbid(void); -void Disable(void); -void _supervisor(u_int32_t *, size_t, void (*)(void)); - -/* Set priority level */ -u_int16_t asplvblank(void); -u_int16_t asplsched(void); -/* Set priority level exit */ -void asplx(u_int16_t); - - - -/* Things we export for Xisop - * -------------------------- - */ - - -/* Memory management */ - -/* Our preferred page size. As Xisop does not support MMU, any size which is - * resonable and a multiple of 16 can be used. - */ -#define _PAGE_SIZE 4096 - -/* Definitions we need for mpool_t */ -#define _MPOOLS 7 -#define _MPOOLSTART 16 -#define _MPOOLSTEP 1 - -/* Memory types we setup */ -enum _memtypes { - _MEM_FAST = 0, - _MEM_CHIP, - _MEM_MAX -}; - -/* Scheduler timer rate */ -#define SCHEDTIMER_HZ 200 /* 4 times the vertical blank rate for now */ - -/* Interrupt facilities we provide */ -enum _facilities { - _FACILITY_SCHEDTIMER = 0, - _FACILITY_VBLANK, - _FACILITY_FLOPPYSYNC, - _FACILITY_FLOPPYBLOCK, - _FACILITY_SERIALRBF, - _FACILITY_SERIALTBE, - _FACILITY_AUDIO, - _FACILITY_BLITTERREADY, - _FACILITY_COPPER, - _FACILITY_CIATIMB0, - _FACILITY_KEYBOARD, - _FACILITY_TRAP, - _FACILITY_MAX -}; - - -/* Functions we provide */ -void _init_exceptions(void); -void _syscall(u_int32_t, void *, void *); -void _yield(void *); -void _port_init(void); -void _panic(u_int16_t); /* XXX */ - - - -#endif diff --git a/Xisop/src/ports/amiga/support_c.c b/Xisop/src/ports/amiga/support_c.c deleted file mode 100644 index 71aee29..0000000 --- a/Xisop/src/ports/amiga/support_c.c +++ /dev/null @@ -1,77 +0,0 @@ -/* $Id: support_c.c,v 1.1 2003/10/26 21:19:57 mmondor Exp $ */ - -/* - * Copyright (C) 2003, Matthew Mondor - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Matthew Mondor. - * 4. The name of Matthew Mondor may not be used to endorse or promote - * products derived from this software without specific prior written - * permission. - * 5. Redistribution of source code may not be released under the terms of - * any GNU Public License derivate. - * - * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - - - -#include -#include "support.h" - - - -/* These functions are similar to the _spl()/_splx() and spl()/splx() ones - * although they work directly with the Amiga hardware INTENA register, - * thus only disabling the specified interrupt (rather than setting a level). - * XXX Problem with this is that it's absolutely architecture-dependant. - * The general purpose spl() functions should be common to the various hardware - * Xisop supports. Hmm but the devices will be architecture dependant as well; - * unless kernel provides some basics to access some common things like - * RS-232 and console I/O. - */ - -u_int16_t asplvblank(void) -{ - u_int16_t old; - - old = CUSTOM->INTENAR; - CUSTOM->INTENA = INT_VBLANK; - - return old; -} - -/* Note: also prevents other CIA-B interrupts meanwhile */ -u_int16_t asplsched(void) -{ - u_int16_t old; - - old = CUSTOM->INTENAR; - CUSTOM->INTENA = INT_CIAB; - - return old; -} - -void asplx(u_int16_t old) -{ - CUSTOM->INTENA = old | INT_SETCLR; -} diff --git a/Xisop/src/ports/amiga/support_s.s b/Xisop/src/ports/amiga/support_s.s deleted file mode 100644 index 4f54c6e..0000000 --- a/Xisop/src/ports/amiga/support_s.s +++ /dev/null @@ -1,865 +0,0 @@ -/* $Id: support_s.s,v 1.1 2003/10/26 21:19:57 mmondor Exp $ */ - -/* - * Copyright (C) 2003, Matthew Mondor - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Matthew Mondor. - * 4. The name of Matthew Mondor may not be used to endorse or promote - * products derived from this software without specific prior written - * permission. - * 5. Redistribution of source code may not be released under the terms of - * any GNU Public License derivate. - * - * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - - - -#include "support.h" - - -| exec.library base -ExecBase = 0x0004 -| Various Amiga custom chips registers -VPOSR = 0x00DFF004 -VHPOSR = 0x00DFF006 -TODHI = 0x00BFEA01 -TODMID = 0x00BFE901 -TODLO = 0x00BFE801 -INTREQR = 0x00DFF01E -INTREQ = 0x00DFF09C -INTENAR = 0x00DFF01C -INTENA = 0x00DFF09A -COLOR0 = 0x00DFF180 -CIA_ICR = 0x00BFDD00 - - -.globl Alert, Forbid, Disable, AvailMem, AllocMem, AllocAbs, FreeMem -.globl _supervisor, _init_exceptions, _syscall, _yield -.globl jdelay, ldelay, atime - -.text - - -| AmigaOS minimal library. We include here the various functions which the -| kernel initialization may need. - - -| void [-108]Alert([d7]long alertNum,[a5]char *flags) -| -Alert: - moveml %d7/%a5-%a6, %sp@- - moveal ExecBase:w, %a6 - movel %sp@(16), %d7 - moveal %sp@(20), %a5 - jsr %a6@(-108) - moveml %sp@+, %a6-%a5/%d7 - rts - - -| void [-132]Forbid(void) -| -Forbid: - movel %a6, %sp@- - moveal ExecBase:w, %a6 - jsr %a6@(-132) - moveal %sp@+, %a6 - rts - - -/* -| void [-120]Disable(void) -| -Disable: - movel %a6, %sp@- - moveal ExecBase:w, %a6 - jsr %a6@(-120) - moveal %sp@+, %a6 - rts - - -| [d0]long = [-216]Availmem([d1]long reqs) -| -AvailMem: - moveml %a6/%d1, %sp@- - moveal ExecBase:w, %a6 - movel %sp@(12), %d1 - jsr %a6@(-216) - moveml %sp@+, %d1/%a6 - rts - - -| [d0]void * = [-198]AllocMem([d0]long bytes, [d1]long reqs) -| -AllocMem: - moveml %a6/%d0-%d1, %sp@- - moveal ExecBase:w, %a6 - movel %sp@(16), %d0 - movel %sp@(20), %d1 - jsr %a6@(-198) - moveal %d0, %a0 - moveml %sp@+, %d1-%d0/%a6 - rts - - -| [d0]void * = [-204]AllocAbs([d0]long bytes, [a1]void *addr) -| -AllocAbs: - moveml %a1/%a6/%d0, %sp@- - moveal ExecBase:w, %a6 - movel %sp@(16), %d0 - moveal %sp@(20), %a1 - jsr %a6@(-204) - moveal %d0, %a0 - moveml %sp@+, %d0/%a6/%a1 - rts - - -| void [-210]FreeMem([a1]memptr, [d0]bytesize) -| -FreeMem: - moveml %a1/%a6/%d0, %sp@- - moveal ExecBase:w, %a6 - moveal %sp@(16), %a1 - movel %sp@(20), %d0 - jsr %a6@(-210) - moveml %sp@+, %d0/%a6/%a1 - rts -*/ - - -| void _supervisor(u_int32_t *sp, size_t sz, void (*func)(void)) -| Allows to gain m68k supervisor access, and sets up the supervisor stack -| pointer to the specified address. The supplied entry point function is -| then called. It is expected to never return, we thus don't need to worry -| about the registers we modify, except for A7/SSP. -| -_supervisor: - moveal %sp@(4), %a1 | SSP to set - movel %sp@(8), %d1 | Size to add - moveal %sp@(12), %a2 | Function to call - moveal ExecBase:w, %a6 - jsr %a6@(-150) | AmigaOS SuperState() exec.library call - moveal %a1, %a7 | Set SSP - addal %d1, %a7 | Jump to end of stack buffer - jsr %a2@ | Call famous function - rts | Should never be reached. - - -| void _init_exceptions(void) -| Called from C to initialize interrupts, traps and exception vectors -| -_init_exceptions: - clrl _interrupt_depth - bsrw trap_init - bsrw int_init - bsrw except_init - rts - - -| Other Amiga-specific assembler routines. The following deal with exceptions, -| traps and interrupts. - - -| void trap_init(void) -| Setup our 15 trap vectors -| -trap_init: - moveml %a0/%a1, %sp@- - moveal #0x80, %a0 - lea %pc@(trap_catch0), %a1 - movel %a1, %a0@+ - lea %pc@(trap_catch1), %a1 - movel %a1, %a0@+ - lea %pc@(trap_catch2), %a1 - movel %a1, %a0@+ - lea %pc@(trap_catch3), %a1 - movel %a1, %a0@+ - lea %pc@(trap_catch4), %a1 - movel %a1, %a0@+ - lea %pc@(trap_catch5), %a1 - movel %a1, %a0@+ - lea %pc@(trap_catch6), %a1 - movel %a1, %a0@+ - lea %pc@(trap_catch7), %a1 - movel %a1, %a0@+ - lea %pc@(trap_catch8), %a1 - movel %a1, %a0@+ - lea %pc@(trap_catch9), %a1 - movel %a1, %a0@+ - lea %pc@(trap_catch10), %a1 - movel %a1, %a0@+ - lea %pc@(trap_catch11), %a1 - movel %a1, %a0@+ - lea %pc@(trap_catch12), %a1 - movel %a1, %a0@+ - lea %pc@(trap_catch13), %a1 - movel %a1, %a0@+ - lea %pc@(trap_catch14), %a1 - movel %a1, %a0@ - moveml %sp@+, %a1/%a0 - rts - - -| And our trap handlers - -| This trap is special as it handles the backend for the _syscall() function -| which prototype is as follows: -| void _syscall(u_int32_t code, void *res, void *args) -| _syscall() just puts the arguments onto d0, a0 and a1 registers, and causes -| a trap 0. Our purpose is to copy those arguments on the supervisor -| stack, call _scatch(), free our arguments from the supervisor stack and -| return from the exception. The _syscall() caller will free it's own arguments -| from the user stack. -| -trap_catch0: - addql #1, _interrupt_depth - - | Backup registers on supervisor stack - | _syscall backed up d0 and a0-a1 as it modifies them to store the - | registers for us. - | - moveml %d0-%d7/%a0-%a6, %sp@- - - | Move registers which _syscall() setup for us into the stack and - | call void _scatch(u_int32_t, void *, void *). - | - movel %a1, %sp@- - movel %a0, %sp@- - movel %d0, %sp@- - bsrl _scatch - lea %sp@(12), %sp | faster than addl #12, %sp - - | Restore registers we backed up - | - moveml %sp@+, %a6-%a0/%d7-%d0 - subql #1, _interrupt_depth - rte - - -| This trap is also special in that it triggers an immediate context switch. -| Used to implement _yield(). It is implemented very similarly to the scheduler -| timer interrupt handler which forces task preemption. -| -trap_catch1: - addql #1, _interrupt_depth - - | First save SR and change it so that we be uninterruptible - | - movew %sr, %sp@- - movew #0x2700, %sr - - | Save current CPU context to root->curctx - | - movel %a0, %sp@- | Save A0 internally as we need it - movel %usp, %a0 - movel %a0, %sp@- | And USP - lea root, %a0 | Load root->curctx _ctx_t * - moveal %a0@(44), %a0 - lea %a0@(70), %a0 | Position now after struct's SR - - | Save SR, predecrementing. Normally stored at %sp@, but we saved - | 10 bytes in the stack, and are using %sp@(10). - movew %sp@(10), %a0@- - | Save PC from SS, which is normally stored at %sp@(2), but we saved - | 10 bytes in the stack, so are using %sp@(12). - movel %sp@(12), %a0@- | Save PC, from SS - moveml %a1-%a6/%d0-%d7, %a0@- | Save A6-A1, D7-D0 - movel %sp@+, %a0@- | Save USP/A7 from backup - movel %sp@+, %a0@- | Save A0 from backup - - | Now call schedule(), passing the supplied argument we were passed - | - movel _yieldparam, %sp@- - bsrl schedule - addql #4, %sp - - | Load context from root->curctx, which may have changed - | - lea root, %a0 | Load root->curctx _ctx_t * - moveal %a0@(44), %a0 - addql #4, %a0 | Skip A0 for now as we use it - movel %a0@+, %a1 - movel %a1, %usp | Restore USP/A7 - moveml %a0@+, %d7-%d0/%a6-%a1 | Restore D0-D7, A1-A6 - | Load PC from context and save in SP. Normally it would be %sp@(2) - | but there remains 2 bytes we saved in the stack and are using %sp@(4) - movel %a0@+, %sp@(4) | PC - | Restore SR, making sure that supervisor mode bit is set. Normally - | stored at %sp@, but we saved 2 bytes on the stack, so %sp@(2). - movew %a0@, %sp@(2) - moveal %a0@(-68), %a0 | Restore A0 - - | Restore normal SR - | - movew %sp@+, %sr - - | Return from exception, but to PC we loaded - | - subql #1, _interrupt_depth - rte - - -| These trap handlers consist of a backend for the C _tcatch() function. -| -trap_catch2: - addql #1, _interrupt_depth - moveml %d0-%d7/%a0-%a6, %sp@- - pea 0 - braw 1f -trap_catch3: - addql #1, _interrupt_depth - moveml %d0-%d7/%a0-%a6, %sp@- - pea 1 - braw 1f -trap_catch4: - addql #1, _interrupt_depth - moveml %d0-%d7/%a0-%a6, %sp@- - pea 2 - braw 1f -trap_catch5: - addql #1, _interrupt_depth - moveml %d0-%d7/%a0-%a6, %sp@- - pea 3 - braw 1f -trap_catch6: - addql #1, _interrupt_depth - moveml %d0-%d7/%a0-%a6, %sp@- - pea 4 - braw 1f -trap_catch7: - addql #1, _interrupt_depth - moveml %d0-%d7/%a0-%a6, %sp@- - pea 5 - braw 1f -trap_catch8: - addql #1, _interrupt_depth - moveml %d0-%d7/%a0-%a6, %sp@- - pea 6 - braw 1f -trap_catch9: - addql #1, _interrupt_depth - moveml %d0-%d7/%a0-%a6, %sp@- - pea 7 - braw 1f -trap_catch10: - addql #1, _interrupt_depth - moveml %d0-%d7/%a0-%a6, %sp@- - pea 8 - braw 1f -trap_catch11: - addql #1, _interrupt_depth - moveml %d0-%d7/%a0-%a6, %sp@- - pea 9 - braw 1f -trap_catch12: - addql #1, _interrupt_depth - moveml %d0-%d7/%a0-%a6, %sp@- - pea 10 - braw 1f -trap_catch13: - addql #1, _interrupt_depth - moveml %d0-%d7/%a0-%a6, %sp@- - pea 11 - braw 1f -trap_catch14: - addql #1, _interrupt_depth - moveml %d0-%d7/%a0-%a6, %sp@- - pea 12 -1: - | Call our C function: void _tcatch(int); - | - bsrl _tcatch - addql #4, %sp - moveml %sp@+, %a6-%a0/%d7-%d0 - subql #1, _interrupt_depth - rte - - - -| C frontend to catch interrupts. Because of the way Amiga works with INTREQ -| it is important that they verify and reset their cause bits. This is easily -| done when dispatching all interrupt levels to a main single C function, with -| the interrupt level passed as an argument, and leave it do all the work. -| This is what was previously done here. However, leaving the C function do -| all the work implied wasting some CPU cycles. For instance, the level is -| already known via the handler being called, when we still let the C code -| perform that condition checking again. Moreover, INTREQ handling wasn't -| particularly optimized properly (using GCC 2.95.3 with m68k output). -| As some interrupt levels may be generated very frequently it is best to -| optimize this as much as possible. -| So we separated the handlers and let the assembler frontend handle INTREQ. -| For most interrupt handlers, We call a C function handler per interrupt -| level: -| void _icatch(u_int16_t intreqmask) -| where is obtained from INTREQR, and we make sure to reset -| the bits of the mask in INTREQ. It would also be possible to add some more -| assembly here if wanted to serve special interrupts faster, like RS-232 I/O. -| NOTE: GCC requires a 32-bit stack entry even for a 16-bit argument. - - -| void int_init(void) -| Setup our 6 interrupt vectors. Amiga does not use m68k interrupt level 7 -| and they are non-maskable (and hence are of no use to us anyways). -| -int_init: - moveml %a0-%a1, %sp@- - moveal #0x64, %a0 - lea %pc@(int_catch1), %a1 - movel %a1, %a0@+ - lea %pc@(int_catch2), %a1 - movel %a1, %a0@+ - lea %pc@(int_catch3), %a1 - movel %a1, %a0@+ - lea %pc@(int_catch4), %a1 - movel %a1, %a0@+ - lea %pc@(int_catch5), %a1 - movel %a1, %a0@+ - lea %pc@(int_catch6), %a1 - movel %a1, %a0@ - moveml %sp@+, %a1-%a0 - rts - - -| These handlers call the various C _icatch() functions -int_catch1: - addql #1, _interrupt_depth - moveml %d0-%d7/%a0-%a6, %sp@- - movew INTREQR, %sp@- - clrw %sp@- - bsrl _icatch1 - addql #4, %sp - movew #0x0007, INTREQ - moveml %sp@+, %a6-%a0/%d7-%d0 - subql #1, _interrupt_depth - rte -int_catch2: - addql #1, _interrupt_depth - moveml %d0-%d7/%a0-%a6, %sp@- - movew INTREQR, %sp@- - clrw %sp@- - bsrl _icatch2 - addql #4, %sp - movew #0x0008, INTREQ - moveml %sp@+, %a6-%a0/%d7-%d0 - subql #1, _interrupt_depth - rte -int_catch3: - addql #1, _interrupt_depth - moveml %d0-%d7/%a0-%a6, %sp@- - movew INTREQR, %sp@- - clrw %sp@- - bsrl _icatch3 - addql #4, %sp - movew #0x0070, INTREQ - moveml %sp@+, %a6-%a0/%d7-%d0 - subql #1, _interrupt_depth - rte -int_catch4: - addql #1, _interrupt_depth - moveml %d0-%d7/%a0-%a6, %sp@- - movew INTREQR, %sp@- - clrw %sp@- - bsrl _icatch4 - addql #4, %sp - movew #0x0780, INTREQ - moveml %sp@+, %a6-%a0/%d7-%d0 - subql #1, _interrupt_depth - rte -int_catch5: - addql #1, _interrupt_depth - moveml %d0-%d7/%a0-%a6, %sp@- - movew INTREQR, %sp@- - clrw %sp@- - bsrl _icatch5 - addql #4, %sp - movew #0x1800, INTREQ - moveml %sp@+, %a6-%a0/%d7-%d0 - subql #1, _interrupt_depth - rte - - -| This interrupt handler is special as it also comports CIAB timer A interrupt, -| which is used by Xisop for the preeptive scheduler. We thus handle it -| especially, entirely in assembly. -| Unlike for other interrupt levels, we not just simply save and restore all -| registers. We store the current context into root->curctx, call schedule(), -| then load the context from root->curctx. -| This executes on the Supervisor Stack, and the User Stack Pointer as such -| is not modified except in context switches. -| -int_catch6: - addql #1, _interrupt_depth - - | Raise level to 7 - | - movew %sr, %sp@- - movew #0x2700, %sr - - | Save D0 as we need it - | - movel %d0, %sp@- - - | if (!(intreq & INT_CIAB)) goto 1 - | - movew INTREQR, %d0 - btst #13, %d0 - beqs 1f - - | if (icrmask & CIA_ICR_TIMA0) goto 2 - | - moveb CIA_ICR, %d0 - btst #0, %d0 - bnes 2f -1: - | Not the CIAB Timer A interrupt, Restore D0 and end - | - movel %sp@+, %d0 - braw 4f -2: - | Restore D0 and continue - | - movel %sp@+, %d0 - - | Tasks are expected to not run in supervisor mode. - | We have syscalls (including sys_call() one allowing to execute - | arbitrary code in supervisor mode), and public interrupt facilities - | (to which they can attach hooks) for them which should suffice. - | Moreover, system calls are uninterruptible by the scheduler when - | running. - - | Store current user context into root->curctx buffer - | - movel %a0, %sp@- | Save A0 internally as we need it - movel %usp, %a0 - movel %a0, %sp@- | And USP - lea root, %a0 | Load root->curctx _ctx_t * - moveal %a0@(44), %a0 - lea %a0@(70), %a0 | Position now after struct's SR - | Save SR, predecrementing. Normally stored at %sp@, but we saved - | 10 bytes in the stack, and are using %sp@(10). - movew %sp@(10), %a0@- - | Save PC from SS, which is normally stored at %sp@(2), but we saved - | 10 bytes in the stack, so are using %sp@(12). - movel %sp@(12), %a0@- | Save PC, from SS - moveml %a1-%a6/%d0-%d7, %a0@- | Save A6-A1, D7-D0 - movel %sp@+, %a0@- | Save USP/A7 from backup - movel %sp@+, %a0@- | Save A0 from backup - - | Execute _FACILITY_SCHEDTIMER hooks - | - clrl %sp@- - pea 0 | _FACILITY_SCHEDTIMER - bsrl facility_exechooks - addql #8, %sp - - | The following ensures to only perform a context switch if not - | currently executing a system call trap or other interrupt. The - | reason we need this is that the scheduler interrupt has a very - | high priority (6), which can actually interrupt most other - | exception handlers. - | - | if (_interrupt_depth != 1) goto 3 - | - cmpil #1, _interrupt_depth - bnes 3f - - | Call schedule(), which internally handles scheduling, and can - | modify root->curctx and root->curtask - | - pea 0 - bsrl schedule - addql #4, %sp - -3: - | Load back root->curctx into the current user CPU context - | - lea root, %a0 | Load root->curctx _ctx_t * - moveal %a0@(44), %a0 - addql #4, %a0 | Skip A0 for now as we use it - movel %a0@+, %a1 - movel %a1, %usp | Restore USP/A7 - moveml %a0@+, %d7-%d0/%a6-%a1 | Restore D0-D7, A1-A6 - | Load PC from context and save in SP. Normally it would be %sp@(2) - | but there remains 2 bytes we saved in the stack and are using %sp@(4) - movel %a0@+, %sp@(4) | PC - | Restore SR, making sure that supervisor mode bit is set. Normally - | stored at %sp@, but we saved 2 bytes on the stack, so %sp@(2). - movew %a0@, %sp@(2) - moveal %a0@(-68), %a0 | Restore A0 - -4: - | Exit point - | - | Reset INTREQ like for other levels interrupt handlers - | - movew #0x2000, INTREQ - - | Restore Interrupt Priority Level - | - movew %sp@+, %sr - - | Return using the %sp@(2) address from SS - | - subql #1, _interrupt_depth - rte - - - -| void except_init(void) -| Setup special exception handlers. -| -except_init: - moveml %a0-%a1, %sp@- - moveal #0x8, %a0 - lea %pc@(except_catch1), %a1 - movel %a1, %a0@+ - lea %pc@(except_catch2), %a1 - movel %a1, %a0@+ - lea %pc@(except_catch3), %a1 - movel %a1, %a0@+ - lea %pc@(except_catch4), %a1 - movel %a1, %a0@+ - lea %pc@(except_catch5), %a1 - movel %a1, %a0@+ - lea %pc@(except_catch6), %a1 - movel %a1, %a0@+ - lea %pc@(except_catch7), %a1 - movel %a1, %a0@+ - lea %pc@(except_catch8), %a1 - movel %a1, %a0@+ - lea %pc@(except_catch9), %a1 - movel %a1, %a0@+ - lea %pc@(except_catch10), %a1 - movel %a1, %a0@ - moveal #0x3c, %a0 - lea %pc@(except_catch11), %a1 - movel %a1, %a0@ - moveal #0x60, %a0 - lea %pc@(except_catch12), %a1 - movel %a1, %a0@ - moveal #0x7c, %a0 - lea %pc@(except_catch13), %a1 - movel %a1, %a0@ - moveml %sp@+, %a1-%a0 - rts - - -| Our exception handlers. Internally calling _ecatch() C function. -| -except_catch1: | Bus error - addql #1, _interrupt_depth - moveml %d0-%d7/%a0-%a6, %sp@- - pea 1 - braw 1f -except_catch2: | Address error - addql #1, _interrupt_depth - moveml %d0-%d7/%a0-%a6, %sp@- - pea 2 - braw 1f -except_catch3: | Illegal operation - addql #1, _interrupt_depth - moveml %d0-%d7/%a0-%a6, %sp@- - pea 3 - braw 1f -except_catch4: | Division by zero - addql #1, _interrupt_depth - moveml %d0-%d7/%a0-%a6, %sp@- - pea 4 - braw 1f -except_catch5: | CHK - addql #1, _interrupt_depth - moveml %d0-%d7/%a0-%a6, %sp@- - pea 5 - braw 1f -except_catch6: | TRAPV - addql #1, _interrupt_depth - moveml %d0-%d7/%a0-%a6, %sp@- - pea 6 - braw 1f -except_catch7: | Privilege violation - addql #1, _interrupt_depth - moveml %d0-%d7/%a0-%a6, %sp@- - pea 7 - braw 1f -except_catch8: | Trace - addql #1, _interrupt_depth - moveml %d0-%d7/%a0-%a6, %sp@- - pea 8 - braw 1f -except_catch9: | Line A Emu - addql #1, _interrupt_depth - moveml %d0-%d7/%a0-%a6, %sp@- - pea 9 - braw 1f -except_catch10: | Line F Emu - addql #1, _interrupt_depth - moveml %d0-%d7/%a0-%a6, %sp@- - pea 10 - braw 1f -except_catch11: | Uninitialized interrupt vector - addql #1, _interrupt_depth - moveml %d0-%d7/%a0-%a6, %sp@- - pea 11 - braw 1f -except_catch12: | Unjustified interrupt vector - addql #1, _interrupt_depth - moveml %d0-%d7/%a0-%a6, %sp@- - pea 12 - braw 1f -except_catch13: | NMI ? - addql #1, _interrupt_depth - moveml %d0-%d7/%a0-%a6, %sp@- - pea 13 -1: - | Call our C function: void _ecatch(int); - bsrl _ecatch - addql #4, %sp - moveml %sp@+, %a6-%a0/%d7-%d0 - subql #1, _interrupt_depth - rte - - - -| void _syscall(int function, void *res, void *args) -| C interface to Xisop syscalls. Because of the C prototype, the three -| arguments are already pushed unto the user stack before calling _syscall(). -| We thus place them into registers d0, a0 and a1, and cause a trap 0, -| which resumes execution at trap_catch0 in supervisor mode. -| -_syscall: - | Save registers we modify - moveml %d0/%a0-%a1, %sp@- - - | Prepare arguments - movel %sp@(16), %d0 - moveal %sp@(20), %a0 - moveal %sp@(24), %a1 - | Poof! Through the gate - trap #0 - - | Restore registers - moveml %sp@+, %a1-%a0/%d0 - rts - - -| void _yield(task_t *) -| Allows a task to immediately return control to the scheduler, allowing -| other tasks some CPU time immediately. The optional task_t pointer argument -| permits to suggest a task to run soon again to the scheduler, and should -| consist of another STATE_READY task. -| We use a special static buffer to hold this value because we do not want -| to taint the registers before the context gets saved. Moreover, if we tried -| to save and restore registers from the stack, we probably would be stealing -| bytes from the stack of another task which probably was preempted rather than -| interrupted using _yield() the last time. -| I tried pushing the argument on the stack and having the other side check -| it in via the User Stack Pointer, but it failed for some reason. -| -_yield: - | Use a static buffer since we're not supposed to modify any registers - | and that we can't save them on the stack since at return we won't - | have the same stack, obviously. - movel %sp@(4), _yieldparam - trap #1 - rts - - - -| These consist of various delay functions. These are not particularly -| useful in multitasking environments since they hug the CPU in loops, -| but are great for testing purposes. - - -/* -| void jdelay(long jiffies) -| Waits until completion of the current frame (vblank), 1/60th of a second -| for NTSC and 1/50th for PAL, by checking current hardware raster position. -| Not ideal as using an interrupt, but works. j stands for jiffy, a frame. -| -jdelay: - moveml %d0-%d1, %sp@- - movel %sp@(12), %d0 -1: - movel VPOSR, %d1 | Read both VPOSR and VHPOSR at once - andil #0x0001FF00, %d1 | Mask out the vertical beam position - bnes 1b - dbf %d0, 1b - moveml %sp@+, %d1-%d0 - rts -*/ - - -| void ldelay(long lines) -| Similar to the above function but waits approximately for the start of the -| next video raster line. -| -ldelay: - moveml %d0-%d1, %sp@- - movel %sp@(12), %d0 -1: movew VHPOSR, %d1 - andiw #0x000F, %d1 - bnes 1b - dbf %d0, 1b - moveml %sp@+, %d1-%d0 - rts - - -*/ -| Time related functions. - - -| u_int32_t atime(void) -| Obtains current time in 50/60Hz resolution, from CIA-A TOD counter -| -atime: - clrl %d0 - moveb TODHI, %d0 - lsll #4, %d0 - lsll #4, %d0 - moveb TODMID, %d0 - lsll #4, %d0 - lsll #4, %d0 - moveb TODLO, %d0 - rts -*/ - - -.data -.align 4 - -| This counter is used to count the depth of interrupts, so that the scheduler -| interrupt, which runs at a very high priority, only switches state if the -| depth is 1. All interrupt and trap handlers increase this variable at -| startup and decrease it at exit. -| -_interrupt_depth: - .long 0 - -| This consists of the parameter which is passed to _yield(), because we cannot -| save registers on the stack and expect the same values to be loaded back -| after causing trap #1, because obviously the context (and SP) may have -| changed. -| -_yieldparam: - .long 0 diff --git a/Xisop/src/processors/i386/make.sh b/Xisop/src/processors/i386/make.sh deleted file mode 100755 index 0772960..0000000 --- a/Xisop/src/processors/i386/make.sh +++ /dev/null @@ -1,11 +0,0 @@ -#!/bin/sh - -# $Id: make.sh,v 1.1 2003/10/26 21:19:57 mmondor Exp $ - -. ../../makedefs.sh - -show $C_COMPS -I$SRCDIR support.s -show $C_COMPS udivsi3.s -show $C_COMPS mulsi3.s -show $C_AR ar/support.a support.o udivsi3.o mulsi3.o -show $C_RANLIB ar/support.a diff --git a/Xisop/src/processors/i386/support.h b/Xisop/src/processors/i386/support.h deleted file mode 100644 index 380c7ea..0000000 --- a/Xisop/src/processors/i386/support.h +++ /dev/null @@ -1,83 +0,0 @@ -/* $Id: support.h,v 1.3 2004/01/30 07:01:24 mmondor Exp $ */ - -/* - * Copyright (C) 2003, Matthew Mondor - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Matthew Mondor. - * 4. The name of Matthew Mondor may not be used to endorse or promote - * products derived from this software without specific prior written - * permission. - * 5. Redistribution of source code may not be released under the terms of - * any GNU Public License derivate. - * - * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - - - -#ifndef PROCESSOR_I386_H -#define PROCESSOR_I386_H - - - -#include - - - -#define _LITTLE_ENDIAN - -typedef struct _ctx { - void *esp, *ebp, *eip; /* eip == PC, esp = stack top */ - u_int32_t eax, ebx, ecx, edx; - u_int32_t esi, edi; - u_int32_t eflags; - u_int32_t es, fs, gs, ds; -} volatile _ctx_t; - -typedef volatile int32_t _lock_t; -typedef int32_t _rlock_t; - - - -void _lock_init(_lock_t *); -void _lock_acquire(_lock_t *); -void _lock_release(_lock_t *); -bool _lock_try(_lock_t *); -bool _lock_available(_lock_t *); - -void _rlock_init(_rlock_t *); -void _rlock_acquire(_rlock_t *); -void _rlock_release(_rlock_t *); -bool _rlock_try(_rlock_t *); -bool _rlock_available(_rlock_t *); - -u_int16_t _bswap16(u_int16_t); -u_int32_t _bswap32(u_int32_t); - -void _ctx_init(_ctx_t *, u_int32_t *, size_t, void *); - -void _idle(void); - - - -#endif diff --git a/Xisop/src/processors/i386/support.s b/Xisop/src/processors/i386/support.s deleted file mode 100644 index 34b4564..0000000 --- a/Xisop/src/processors/i386/support.s +++ /dev/null @@ -1,143 +0,0 @@ -/* $Id: support.s,v 1.4 2004/01/30 07:29:14 mmondor Exp $ */ - -/* - * Copyright (C) 2003, Matthew Mondor - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Matthew Mondor. - * 4. The name of Matthew Mondor may not be used to endorse or promote - * products derived from this software without specific prior written - * permission. - * 5. Redistribution of source code may not be released under the terms of - * any GNU Public License derivate. - * - * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - - - -.globl _spl, _splx -.globl _lock_init, _lock_acquire, _lock_try, _lock_available, _lock_release -.globl _rlock_init, _rlock_acquire, _rlock_try, _rlock_release, _rlock_available -.globl _ctx_init -.globl setjmp, longjmp -.globl _idle -.globl _bswap16, _bswap32 - -.text - - -/* _spl_t _spl(_spl_t) _spl_t = ?XXX? -*/ -_spl: -/*XXX*/ - ret - - -_lock_init: - mov 0x4(%esp,1), %eax - movl $0x0, (%eax) - ret - -_lock_acquire: - mov 0x4(%esp,1), %edx - mov $0x1, %eax - lea 0x0(%esi), %esi -1: xchg %eax, (%edx) - test %eax, %eax - jne 1b - ret - -_lock_release: - mov 0x4(%esp,1), %eax - movl $0x0, (%eax) - ret - -_lock_try: - mov 0x4(%esp,1), %edx - mov $0x1, %eax - xchg %eax, (%edx) - test %eax, %eax - je 1f - xor %eax, %eax - ret -1: mov $0x1, %eax - ret - -_lock_available: - mov 0x4(%esp,1), %eax - cmpl $0x0, (%eax) - je 1f - xor %eax, %eax - ret -1: mov $0x1, %eax - ret - - -_rlock_init: - mov 0x4(%esp,1), %eax - movl $0x0, (%eax) - ret - -_rlock_acquire: - mov 0x4(%esp,1), %eax - incl (%eax) - ret - -_rlock_release: - mov 0x4(%esp,1), %eax - decl (%eax) - ret - -_rlock_try: - mov 0x4(%esp,1), %eax - incl (%eax) - cmpl $0x1, (%eax) - je 1f - decl (%eax) - xor %eax, %eax - ret -1: mov $0x1, %eax - ret - -_rlock_available: - mov 0x4(%esp,1), %eax - cmpl $0x0, (%eax) - je 1f - xor %eax, %eax - ret -1: mov $0x1, %eax - ret - - -_bswap16: - mov 0x4(%esp,1), %eax - ror $0x8, %ax - ret - -_bswap32: - mov 0x4(%esp,1), %eax - ror $0x8, %ax - ror $0x10, %eax - ror $0x8, %ax - ret - diff --git a/Xisop/src/processors/m68k/clean.sh b/Xisop/src/processors/m68k/clean.sh deleted file mode 100755 index 63982c8..0000000 --- a/Xisop/src/processors/m68k/clean.sh +++ /dev/null @@ -1,8 +0,0 @@ -#!/bin/sh - -# $Id: clean.sh,v 1.1 2003/10/26 21:19:57 mmondor Exp $ - -. ../../generic_makedefs.sh - -cleanlib math -show $L_RM support.o ar/support.a diff --git a/Xisop/src/processors/m68k/make.sh b/Xisop/src/processors/m68k/make.sh deleted file mode 100755 index 20a99e2..0000000 --- a/Xisop/src/processors/m68k/make.sh +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/sh - -# $Id: make.sh,v 1.1 2003/10/26 21:19:57 mmondor Exp $ - -. ../../makedefs.sh - -show $C_COMPS -I$SRCDIR support.s -buildlib math -show $C_AR ar/support.a support.o math/*.o -show $C_RANLIB ar/support.a diff --git a/Xisop/src/processors/m68k/math/README b/Xisop/src/processors/m68k/math/README deleted file mode 100644 index 74461b6..0000000 --- a/Xisop/src/processors/m68k/math/README +++ /dev/null @@ -1,3 +0,0 @@ -Some math functions are required by GCC m68k compiled code. Those were obtained -from the NetBSD kernel m68k library and adapted. Their BSD license file headers -were unchanged, however. diff --git a/Xisop/src/processors/m68k/math/divsi3.s b/Xisop/src/processors/m68k/math/divsi3.s deleted file mode 100644 index 8c8aa40..0000000 --- a/Xisop/src/processors/m68k/math/divsi3.s +++ /dev/null @@ -1,59 +0,0 @@ -/* $Id: divsi3.s,v 1.1 2003/10/26 21:19:57 mmondor Exp $ */ - -/*- - * Copyright (c) 2002 The NetBSD Foundation, Inc. - * All rights reserved. - * - * This code is derived from software contributed to The NetBSD Foundation - * by Matthew Fredette. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the NetBSD - * Foundation, Inc. and its contributors. - * 4. Neither the name of The NetBSD Foundation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS - * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED - * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS - * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - - - -.globl __divsi3 - - - -| NB: this requires that __udivsi3 preserve %a0: -__divsi3: - movel %sp@(4), %d1 | load the dividend - bpls 1f - negl %sp@(4) | store abs(dividend) -1: movel %sp@(8), %d0 | load the divisor - bpls 2f - negl %sp@(8) | store abs(divisor) -2: eorl %d1, %d0 - bpls 3f | branch if sgn(divisor) == sgn(dividend) - moveal %sp@+, %a0 | pop return address - pea %pc@(Lret) | push our return address -3: jmp __udivsi3 -Lret: negl %d0 | negate quotient - jmp %a0@ diff --git a/Xisop/src/processors/m68k/math/modsi3.s b/Xisop/src/processors/m68k/math/modsi3.s deleted file mode 100644 index b042612..0000000 --- a/Xisop/src/processors/m68k/math/modsi3.s +++ /dev/null @@ -1,60 +0,0 @@ -/* $Id: modsi3.s,v 1.1 2003/10/26 21:19:57 mmondor Exp $ */ - -/*- - * Copyright (c) 2002 The NetBSD Foundation, Inc. - * All rights reserved. - * - * This code is derived from software contributed to The NetBSD Foundation - * by Matthew Fredette. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the NetBSD - * Foundation, Inc. and its contributors. - * 4. Neither the name of The NetBSD Foundation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS - * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED - * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS - * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - - - -.globl __modsi3 - - - -| NB: this requires that __udivsi3 preserve %a0 and return -| the modulus in %d1: -__modsi3: - moveal %sp@+, %a0 | pop return address - movel %sp@(4), %d1 | load the divisor - bpls 1f - negl %sp@(4) | store abs(divisor) -1: movel %sp@, %d0 | load the dividend - pea %pc@(Lret) | push our return address - bpls 2f - negl %sp@(4) | store abs(dividend) - subql #2, %sp@ | adjust return address -2: jmp __udivsi3 - negl %d1 | negate modulus -Lret: movel %d1, %d0 | move modulus into %d0 - jmp %a0@ diff --git a/Xisop/src/processors/m68k/math/mulsi3.s b/Xisop/src/processors/m68k/math/mulsi3.s deleted file mode 100644 index a4cb7c8..0000000 --- a/Xisop/src/processors/m68k/math/mulsi3.s +++ /dev/null @@ -1,55 +0,0 @@ -/* $Id: mulsi3.s,v 1.1 2003/10/26 21:19:57 mmondor Exp $ */ - -/*- - * Copyright (c) 2002 The NetBSD Foundation, Inc. - * All rights reserved. - * - * This code is derived from software contributed to The NetBSD Foundation - * by Matthew Fredette. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the NetBSD - * Foundation, Inc. and its contributors. - * 4. Neither the name of The NetBSD Foundation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS - * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED - * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS - * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -.globl __mulsi3 - -__mulsi3: - movew %sp@(6), %d0 - moveal %d0, %a0 | save B - muluw %sp@(8), %d0 | %d0 holds B * C - movew %sp@(10), %d1 - moveal %d1, %a1 | save D - muluw %sp@(4), %d1 | %d1 holds A * D - addw %d1, %d0 | %d0 holds (B * C) + (A * D) - swap %d0 - clrw %d0 | %d0 holds ((B * C) + (A * D)) << 16 - exg %a0, %d0 | restore B - movel %a1, %d1 | restore D - muluw %d1, %d0 | %d0 holds B * D - addl %a0, %d0 | final result - rts diff --git a/Xisop/src/processors/m68k/math/udivsi3.s b/Xisop/src/processors/m68k/math/udivsi3.s deleted file mode 100644 index 3c3d1da..0000000 --- a/Xisop/src/processors/m68k/math/udivsi3.s +++ /dev/null @@ -1,114 +0,0 @@ -/* $Id: udivsi3.s,v 1.1 2003/10/26 21:19:57 mmondor Exp $ */ - -/*- - * Copyright (c) 2002 The NetBSD Foundation, Inc. - * All rights reserved. - * - * This code is derived from software contributed to The NetBSD Foundation - * by Matthew Fredette. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the NetBSD - * Foundation, Inc. and its contributors. - * 4. Neither the name of The NetBSD Foundation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS - * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED - * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS - * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -.globl __udivsi3 - -__udivsi3: - movel %d2, %sp@- | save %d2 - movel %sp@(12), %d0 | load divisor - movel %sp@(8), %d1 | load dividend - -| first, we divide the divisor and dividend by two until -| the divisor fits into 16 bits: -1: cmpil #0x10000, %d0 - bcss 2f - lsrl #1, %d0 - lsrl #1, %d1 - bras 1b -2: - -| now we can do the divide. to avoid overflow, we have to -| do the divide in two parts, high and low, and add the -| results together: - movew %d1, %d2 | save low(dividend) - clrw %d1 - swap %d1 | %d1 = dividend >> 16 - divuw %d0, %d1 | do the high divide - moveal %d1, %a1 | save high divide result - movew %d2, %d1 | concat(remainder, low(dividend)) - divuw %d0, %d1 | do the low divide - movel %a1, %d0 | recover high divide result - swap %d0 - clrw %d0 | %d0 = finished high divide result - andil #0xffff, %d1 | %d1 = finished low divide result - addl %d1, %d0 | %d0 = quotient guess - -| the quotient we have so far is only a guess. the divide we -| did above was really the divide of some dividendB by some -| divisorB, where the following hold: -| -| (dividend - divisor) <= dividendB <= dividend -| (divisor / 2) < divisorB <= divisor -| -| so our guess quotient cannot be less than our real desired -| quotient. however, it might be one too big. -| -| to adjust this quotient, we multiply it by the original -| divisor and subtract the result from the original dividend. -| if the result is nonnegative, our guessed quotient was -| correct, and the subtraction result is our remainder. -| if the result is negative, our guessed quotient was one -| too big, and the subtraction result plus the original -| divisor is our remainder. -| -| as in mulsi3, we have to do the multiply in stages to avoid -| overflow: - - movel %sp@(12), %d2 | load divisor - swap %d2 - movel %d0, %d1 - muluw %d2, %d1 | high(divisor) * low(guess) - moveal %d1, %a1 | save high(divisor) * low(guess) - swap %d2 - movel %d0, %d1 - swap %d1 - muluw %d2, %d1 | low(divisor) * high(guess) - addl %a1, %d1 - swap %d1 - clrw %d1 | %d1 = finished high multiply result - moveal %d2, %a1 | save original divisor - muluw %d0, %d2 | low(guess) * low(divisor) - addl %d1, %d2 | %d2 = guess * divisor - - movel %sp@(8), %d1 | load original dividend - subl %d2, %d1 | subtract - bccs 3f - subql #1, %d0 | adjust quotient - addl %a1, %d1 | adjust remainder -3: movel %sp@+, %d2 | restore %d2 - rts diff --git a/Xisop/src/processors/m68k/math/umodsi3.s b/Xisop/src/processors/m68k/math/umodsi3.s deleted file mode 100644 index 37898cf..0000000 --- a/Xisop/src/processors/m68k/math/umodsi3.s +++ /dev/null @@ -1,51 +0,0 @@ -/* $Id: umodsi3.s,v 1.1 2003/10/26 21:19:57 mmondor Exp $ */ - -/*- - * Copyright (c) 2002 The NetBSD Foundation, Inc. - * All rights reserved. - * - * This code is derived from software contributed to The NetBSD Foundation - * by Matthew Fredette. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the NetBSD - * Foundation, Inc. and its contributors. - * 4. Neither the name of The NetBSD Foundation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS - * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED - * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS - * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - - - -.globl __umodsi3 - - - -| NB: this requires that __udivsi3 preserve the %a0 -| register, and that it returns the modulus in %d1: -__umodsi3: - moveal %sp@+, %a0 | pop the return address - jsr __udivsi3 - movel %d1, %d0 | move the modulus into %d0 - jmp %a0@ | return diff --git a/Xisop/src/processors/m68k/support.h b/Xisop/src/processors/m68k/support.h deleted file mode 100644 index 4afe2a3..0000000 --- a/Xisop/src/processors/m68k/support.h +++ /dev/null @@ -1,138 +0,0 @@ -/* $Id: support.h,v 1.6 2004/06/03 05:44:05 mmondor Exp $ */ - -/* - * Copyright (C) 2003, Matthew Mondor - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Matthew Mondor. - * 4. The name of Matthew Mondor may not be used to endorse or promote - * products derived from this software without specific prior written - * permission. - * 5. Redistribution of source code may not be released under the terms of - * any GNU Public License derivate. - * - * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -/* - * Derived from informations from the "Mise en oeuvre du 68000" book. - * The spl name was inspired from NetBSD SPL(9) man page. The code for - * these is in support.s - */ - - - -#ifndef PROCESSOR_M68K_H -#define PROCESSOR_M68K_H - - - -#include - - - -/* Definitions for optimizations and network byte order support */ -#define _ARCH_BIG_ENDIAN -#define _ARCH_INT_BITS 32 -/*#define _ARCH_LOWCACHE*/ -/*#define _ARCH_USEINDEXING*/ - - - -/* Abstract datatypes we must provide */ - -/* A CPU context which can also hold the state of user tasks. - * Also used as buffer area for setjmp()/longjmp() functions. - * Note that USP (User Stack Pointer) is actually SP/A7, as seen - * from supervisor code, which SP/A7 stack is set to SSP (Supervisor - * Stack Pointer). This means that from supervisor code the special - * usp related instructions must be used to manipulate the current - * user stack pointer, but that from userspace this simply is sp which - * can be manipulated without supervisor access (like in setjmp()/longjmp()). - */ -typedef struct _ctx { - void *a0, *usp; - u_int32_t d0, d1, d2, d3, d4, d5, d6, d7; - void *a1, *a2, *a3, *a4, *a5, *a6; - void *pc; - u_int16_t sr; - u_int16_t padding; -} volatile _ctx_t; - -/* A simple lock */ -typedef volatile u_int8_t _lock_t; -/* A recursive lock */ -typedef int32_t _rlock_t; - -/* An interrupt priority level context */ -typedef u_int16_t _ipl_t; - - - -/* Set interrupt level control functions */ -_ipl_t _spl(u_int16_t); -#define _spl0() _spl(0x2000) -#define _spl1() _spl(0x2100) -#define _spl2() _spl(0x2200) -#define _spl3() _spl(0x2300) -#define _spl4() _spl(0x2400) -#define _spl5() _spl(0x2500) -#define _spl6() _spl(0x2600) -#define _spl7() _spl(0x2700) -#define _splhigh() _spl(0x2700) -void _splx(_ipl_t); - -/* Atomic locking functions. These are SMP safe. */ -void _lock_init(_lock_t *); -void _lock_acquire(_lock_t *); -void _lock_release(_lock_t *); -bool _lock_try(_lock_t *); -bool _lock_available(_lock_t *); - -/* Atomic recursive locking functions. The same number of release operations - * must be performed for the lock to become available again, and there can - * be any number of concurrent lockers. - */ -void _rlock_init(_rlock_t *); -void _rlock_acquire(_rlock_t *); -void _rlock_release(_rlock_t *); -bool _rlock_try(_rlock_t *); -bool _rlock_available(_rlock_t *); - -/* Efficient byte order conversion functions */ -u_int16_t _bswap16(u_int16_t); -u_int32_t _bswap32(u_int32_t); - -/* Function to create a new CPU context for a task */ -void _ctx_init(_ctx_t *, u_int32_t *, size_t, void *); - -/* Function to put the CPU in idle mode until next interrupt occurs. - * Restricted to supervisor mode. - */ -void _idle(void); - -/* Useful to call Xisop main() from port-specific initialization code */ -void _usermode(int (*)(void)); - - - -#endif diff --git a/Xisop/src/processors/m68k/support.s b/Xisop/src/processors/m68k/support.s deleted file mode 100644 index 990d9a0..0000000 --- a/Xisop/src/processors/m68k/support.s +++ /dev/null @@ -1,321 +0,0 @@ -/* $Id: support.s,v 1.4 2004/01/30 07:08:57 mmondor Exp $ */ - -/* - * Copyright (C) 2003, Matthew Mondor - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Matthew Mondor. - * 4. The name of Matthew Mondor may not be used to endorse or promote - * products derived from this software without specific prior written - * permission. - * 5. Redistribution of source code may not be released under the terms of - * any GNU Public License derivate. - * - * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - - - -.globl _spl, _splx -.globl _lock_init, _lock_acquire, _lock_try, _lock_available, _lock_release -.globl _rlock_init, _rlock_acquire, _rlock_try, _rlock_release, _rlock_available -.globl _bswap16, _bswap32 -.globl _ctx_init -.globl setjmp, longjmp -.globl _idle, _usermode - -.text - - -| _spl_t _spl(_spl_t) _spl_t == u_int16_t -| -_spl: movew %sr, %d0 - movew %sp@(6), %sr - rts - - -| void _splx(_spl_t oldstate) -| -_splx: movew %sp@(6), %sr - rts - - -| void _lock_init(_lock_t *lock) _lock_t == u_int8_t -| Used to create/init new locks -| -_lock_init: - movel %a0, %sp@- - moveal %sp@(8), %a0 - clrb %a0@ - moveal %sp@+, %a0 - rts - - -| void _lock_acquire(_lock_t *lock) -| Blocks current CPU in a loop until lock is obtained -| -_lock_acquire: - movel %a0, %sp@- - moveal %sp@(8), %a0 -1: tas %a0@ - bnes 1b - moveal %sp@+, %a0 - rts - - -| bool _lock_try(_lock_t *lock) -| Attempts to obtain lock, but returns immediately with TRUE/FALSE result -| -_lock_try: - movel %a0, %sp@- - moveal %sp@(8), %a0 - tas %a0@ - beqs 1f - clrl %d0 - moveal %sp@+, %a0 - rts -1: - moveq #1, %d0 - moveal %sp@+, %a0 - rts - - -| bool _lock_available(_lock_t *lock) -| Returns 1/TRUE if the lock is currently free, or 0/FALSE if it is locked. -| Note that this should not be used to attempt to obtain the lock using two -| steps (see _lock_try() above for this). It is useful to simply know if -| it is locked, nothing else. Useful for the scheduler on/off switch. -| -_lock_available: - movel %a0, %sp@- - moveal %sp@(8), %a0 - btst #7, %a0@ - bnes 1f - moveq #1, %d0 - moveal %sp@+, %a0 - rts -1: - clrl %d0 - moveal %sp@+, %a0 - rts - - -| void _lock_release(_lock_t *lock) -| Releases a previously acquired lock -| -_lock_release: - movel %a0, %sp@- - moveal %sp@(8), %a0 - clrb %a0@ - moveal %sp@+, %a0 - rts - - -| void _rlock_init(_rlock_t *rlock) _rlock_t = int32_t -| Initializes a recursive lock -| -_rlock_init: - movel %a0, %sp@- - moveal %sp@(8), %a0 - clrl %a0@ - moveal %sp@+, %a0 - rts - - -| void _rlock_acquire(_rlock_t *rlock) -| Acquires the lock, recursively -| -_rlock_acquire: - movel %a0, %sp@- - moveal %sp@(8), %a0 - addql #1, %a0@ - moveal %sp@+, %a0 - rts - - -| void _rlock_release(_rlock_t *rlock) -| Releases the lock. _rlock_acquire() and _rlock_release() instances must pair -| for the lock to eventually be available again. -| -_rlock_release: - movel %a0, %sp@- - moveal %sp@(8), %a0 - subql #1, %a0@ - moveal %sp@+, %a0 - rts - - -| bool _rlock_try(_rlock_t *rlock) -| Atomically acquires the lock like _rlock_acquire(), but returns TRUE if -| we then are the only locker. Otherwise, releases the lock back and return -| FALSE. Obviously, if we are the only locker, the _rlock_t counter will be -| one (1). This allows to perform an atomic -| _rlock_available() + _rlock_acquire() pair. -| -_rlock_try: - movel %a0, %sp@- - moveal %sp@(8), %a0 - moveq #1, %d0 - addql #1, %a0@ - cmpl %a0@, %d0 - beqs 1f - subql #1, %a0@ - clrl %d0 -1: moveal %sp@+, %a0 - rts - - -| bool _rlock_available(_rlock_t *rlock) -| Returns TRUE if the lock is available, or if FALSE if there are any lockers. -| -_rlock_available: - movel %a0, %sp@- - moveal %sp@(8), %a0 - clrl %d0 - cmpl %a0@, %d0 - bnes 1f - moveq #1, %d0 -1: moveal %sp@+, %a0 - rts - - -| u_int16_t _bswap16(u_int16_t) -| Swaps the order of the two bytes held in the supplied 16-bit word -| -_bswap16: - movew %sp@(6), %d0 - rorw #8, %d0 - rts - - -| u_int32_t _bswap32(u_int32_t) -| Reverses the order of the four bytes held in the supplied 32-bit word -| First cause a 16-bit byte swap, then perform a 16-bit word swap within the -| 32-bit word, and then perform another 16-bit byte swap. -| -_bswap32: - movel %sp@(4), %d0 - rorw #8, %d0 - swap %d0 - rorw #8, %d0 - rts - - -| void _ctx_init(_ctx_t *, u_int32_t *sp, size_t sz, void *pc) -| Creates a new CPU context which will use specified SP and PC pointers. -| Note that SP must be pointing at end of stack area, as it grows upwards. -| -_ctx_init: - moveml %a0/%d0, %sp@- | Save a0/d0 - moveal %sp@(12), %a0 | _ctx_t * - clrl %a0@+ | A0 - movel %sp@(16), %a0@ | A7/USP u_int32_t * - movel %sp@(20), %d0 | size_t - addl %d0, %a0@+ | A7/USP - clrl %a0@+ | D0 - clrl %a0@+ | D1 - clrl %a0@+ | D2 - clrl %a0@+ | D3 - clrl %a0@+ | D4 - clrl %a0@+ | D5 - clrl %a0@+ | D6 - clrl %a0@+ | D7 - clrl %a0@+ | A1 - clrl %a0@+ | A2 - clrl %a0@+ | A3 - clrl %a0@+ | A4 - clrl %a0@+ | A5 - clrl %a0@+ | A6 - movel %sp@(24), %a0@+ | PC void * - clrl %a0@ | SR + padding -| clrw %a0@+ | SR -| clrw %a0@ | 16-bit 32-bit padding - moveml %sp@+, %d0/%a0 | Restore d0/a0 - rts - - -| int setjmp(jmp_buf); -| We can manipulate SP/USP/A7 from user state without problems. We do not need -| to manipulate SR. We can safely run in usermode. -| The supplied jmp_buf is actually a pointer to a _ctx_t. We must return 0 -| normally, but must return the value supplied to longjmp() when it is called. -| To do this, we zero D0, save context to jmp_buf, and return. longjmp() will -| load that context back and set D0 to the wanted value before returning. -| -setjmp: - movel %a0, %sp@- | Save A0 (4 bytes) to current stack - moveal %sp@(8), %a0 | Pointer to _ctx_t - clrl %d0 | Default return value to 0 - lea %a0@(66), %a0 | Pos now after pc - movel %sp@(6), %a0@- | PC norm at %sp@(2), we saved 4 bytes - moveml %a1-%a6/%d0-%d7, %a0@- | Save A6-A1, D7-D0 - movel %sp, %a0@ | Save A7/USP (without saved 4 bytes) - addql #4, %a0@- - movel %sp@+, %a0@- | Restore A0 and save it - rts - - -| void longjmp(jmp_buf, int) -| No need to save registers since we are loading the supplied state. -| We technically never return, we however make sure to load D0 (which -| will become setjmp() return value) with the supplied value argument. -| -longjmp: - moveal %sp@(4), %a0 | _ctx_t pointer - movel %sp@(8), %d0 | Return value for setjmp() - addql #4, %a0 | Now at usp - moveal %a0@+, %sp | Restore context stack pointer - addql #4, %a0 | Now at d1 - moveml %a0@+, %d7-%d1/%a6-%a1 | Load registers from context - movel %a0@, %sp@(2) | Restore return PC - moveal %a0@(-68), %a0 | Restore A0 - rts | Resume at setjmp() saved PC - - -| void _idle(void) -| Puts CPU in sleep mode until next interrupt occurs. Useful to not use 100% -| CPU time and overheat when all tasks are idle. Also saves alot of power -| on battery powered systems. Restricted to supervisor mode. -| -_idle: - movew %sr, %sp@- - stop #0x2000 - movew %sp@+, %sr - rts - - -| void _usermode(int (*)(void)); -| Useful for port-specific init code to call Xisop main() -| Permits to switch from supervisor mode to usermode and jump to the specified -| function. The current supervisor stack is used to setup the user stack (US). -| The user stack we create is 1024 bytes. Obviously, the SS should have enough -| room for this. The function we jump to is expected to only perform minor -| initialization, like to start the first Xisop task, with it's own stack. -| Used to call Xisop's main() function from init.c -| -_usermode: - moveal %sp@(4), %a0 | Address to jump to - movel %sp, %a1 - lea %sp@(-1024), %sp - movel %a1, %usp - andiw #0xdfff, %sr | Switch to usermode - jmp %a0@ diff --git a/mmsoftware/BUGS b/mmsoftware/BUGS deleted file mode 100644 index 5cc781d..0000000 --- a/mmsoftware/BUGS +++ /dev/null @@ -1,5 +0,0 @@ -$Id: BUGS,v 1.8 2003/01/10 06:45:59 mmondor Exp $ - - - -- Fix anoncvs pserver diff --git a/mmsoftware/LICENSE b/mmsoftware/LICENSE deleted file mode 100644 index 786ba63..0000000 --- a/mmsoftware/LICENSE +++ /dev/null @@ -1,30 +0,0 @@ -Copyright (C) 2000-2004, Matthew Mondor -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: -1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. -2. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. -3. All advertising materials mentioning features or use of this software - must display the following acknowledgement: - This product includes software written by Matthew Mondor. -4. The name of Matthew Mondor may not be used to endorse or promote - products derived from this software without specific prior written - permission. -5. Redistribution of source code may not be released under the terms of - any GNU Public License derivate. - -THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR -IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES -OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. -IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, -INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF -USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF -THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/mmsoftware/TODO b/mmsoftware/TODO deleted file mode 100644 index 45c5e3d..0000000 --- a/mmsoftware/TODO +++ /dev/null @@ -1,387 +0,0 @@ -$Id: TODO,v 1.46 2005/03/09 08:55:28 mmondor Exp $ - - - -SITE/ -===== - -- Write a system, maybe using /bin/sh and/or gmake, to create the software - section of the site automatically using a simple to maintain file or set - of files as a database of the projects. It potentially also could be used - to automatically package a new version of the software, after tagging the - CVS repository. -- It would be nice to automate the freshmeat update, I will see if it can - be done safely, otherwise I shall need to update it manually on their - site. - - - -MMLIB/MMREADCFG -=============== - -- Should also allow some keywords to embed other keywords or serve as - delimiters. For instance, for each network interface defined, - options which are now global could be set on an interface-basis. - This probably implies providing the library with a structure to fill - with field offsets, or such. I want the application to afterwards be - able to very quickly access any configuration data and not have to - perform hashtable lookup operations. - An implementation could be provided with various array sets similar to - the current one. Whenever a duplicate entry occurs one those a new - entry would be created with the defaults, and be filled from now on... -- An apache/proftpd style configuration file format is envisagable. - - - -MMFTPD -====== - -- Use a custom stack on the heap to determine the size of a home directory, - rather than using standard recursion on the stack. We would then use an - iterative, safer method. -- Reuse mmfd handle for file_out() -- It would be nice to support at least part of rfc2228, that is, secure - authentication FTP extension. -- Modularize user authentication, abstracting the checkuser() function, and - of course provide a way for the user to specify which one to use at compile - time. An authentication module using MySQL for instance should not require - the software to need libmysqlclient library if plain-file authentication is - used instead (/etc/mmftpdpasswd). Moreover, per-module options will be - required, location of file for plainfile, and DB options for MySQL, etc). -- Work a way out to have per-host/iface options and users -- Implement a global upload directory, common to all users via /upload, which - would be configurable for each account... -- Write an ssl-httpd dedicated to remotely managing mmmail and mmftpd - -- When NetBSD scheduler activations gets released as stable, I should - be able to convert the daemon to use POSIX Threads API instead of GNU Pth - which it currently uses. There however seems to be lacking important - functionality in that API, particularly in the fd multiplexing area. - I will have to provide an efficient message passing system among threads, - and a way to have a thread sleep while waiting for the following conditions: - optional timeout, fd status change, message available. - The only current way which would seem to solve those issues would be to - use more file descriptors and implement everything to affect fds. This - however proves an important lack in POSIX Pthread API. I need to investigate - on more efficient alternatives. pth's poll_ev() and pth_msg*() currently - solve those issues. - A possible alternative would be to have a special thread which could serve - as an events manager, using a unified message system which would describe - events occuring... BSD kqueue would be ideal, but are not portable. - Maybe I could use libevent library or provide my own... - XXX It seems that libevent cannot be expected to be thread-safe (will only be - on some systems, since it can fallback to select()/flock() where necessary). -- If only message passing was required, conditional variables and/or mutexes - could be used to implement waiting, etc. However, we also need to listen for - filedescriptor events... And, we must support a timeout, which would have to - interrupt message port and/or filedescriptor polling. The main problem - involving an additional or several filedescriptors to implement message - passing is that system calls are expensive. If we only needed to pass a very - short atomic message through a single filedescriptor only for required - operations involving polling, perhaps that it would be decent... - POSIX threads condition variables support timeouts... -- Complete libpth event/message replacement to port mmftpd and mmserver to - new NetBSD threads (and POSIX API) - - - -MMMAIL -====== - -- Have mmsmtpd start a daemon process to call the MySQL OPTIMIZE command on - the database tables once in a while, asynchroneously. -- Because initializing a new hashtable_t is rather expensive, we do not want - to have to initialize one per new client connection for RCPT storage. - Either we keep using the linear system for RCPTs like we are doing now, - or that we switch to a system using a shared table for the whole process, - with specialized hash and compare functions so that two fields be taken - into consideration: 1) the unique ID for this connection and 2) the actual - RCPT address. This however could have some disadventages. For instance, - iterating through the whole hashtable_t would be required to post a message, - and the same would apply to free the RCPT entries for a session after it - was transfered... We could run it once only and free those entries during - the same step however. The current linear method is fine as long as we - restrict the number of allowed RCPTs to a sane value and that most posts - do not require a high number of RCPTs. - NOTE: We now have hashtable_init2() in mmhash(3) which is faster. - -- Some basic relaying support should be implemented; simpler than the one - targetted at mmmail2 (which I current fail to have time to implement, to - achieve such a new project from scratch, I would need to have it sponsored). - What I currently need is basic relaying support, as well as a simple - mailing list facility (like to provide support for my software). For this, - a few new tables could be implemented... Mailing list addresses as well as - their subscriber addresses; Relaying queue; Allowed IP address blocks and/or - hostname patterns to allow relaying for. - QUEUE MANAGEMENT - Notes pertaining to mmsmtpd/mmrelayd interaction and the - ================ mail queue. - - mmsmtpd queues mail which is to be relayed (this prevents mail from getting - lost in case of relaying problems, and it appears logical to do because - mmsmtpd is the daemon reading the mail and saving the message file. - mmsmtpd then attempts to notify mmrelayd that a new message was queued. - We do this in order to allow mmrelayd to update its schedule as fast as - possible instead of only at scheduled queue queries. Moreover, it can - prevent mmrelayd from having to potentially query large datasets frequently - in case of large queues (this assumes that one notification message is - transfered per queued message, and that the notification holds the - necessary information to prefent mmrelayd from having to perform an SQL - query to add the message to the schedule). - - mmrelayd would maintain an in-memory schedule as much as possible to - minimize the frequency of SQL queries. An initial large query would be made - to create the schedule when launched. Notification messages from mmsmtpd - would serve to schedule new elements without an SQL query. An SQL query - would however be made to update an entry for a message for which a relay - attempt was made; Either to discard the entry after successful relaying, - or to update its information if it has to be rescheduled due to failing. - Of course, a discard would also occur eventually after too many failures - or specific forms of immediate failures. The sender of the message could - potentially be notified of these error events, by queueing a new message. - - Relaying failure replies could probably be sent through mmsmtpd from - mmrelayd via standard SMTP. Or, for enhanced performance, we may want - to queue the message ourselves and schedule it to avoid unnecessary - overhead. - - The scheduler would consist of a main thread, and a number of dedicated - threads in a ready pool could perform actual relaying of scheduled events. - The main scheduler thread would also be the one monitoring the - notifications socket to schedule new events. If we decide to queue - ourselves error reply messages, this also could be done by the same thread. - Otherwise, threads in the ready pool would be assigned to route the - messages to mmsmtpd in case of failure. - - My current ready threads pool implementation was made for asynchronous - dispatching of tasks. I will need to ensure in this case that the master - thread can dispatch multiple tasks to threads asynchroneously, but to then - also be able to process return results as soon as possible whenever a - thread ends its task. This can be done using interthread messaging as well. - - I am currently evaluating if a custom DNS cache is worth implementing. - With caching DNS servers, the overhead of lookups shouldn't be too bad. - It however is clear that lookup must be done by a concurrent thread rather - than in the main scheduler thread. - - It would be nice to attempt to schedule multiple messages for a common - destination SMTP server together to minimize TCP connection establishment - overhead when possible. It also would be nice to use multiple RCPTs for - messages which need to be routed to multiple destinators which are local - to the same SMTP server. - -- PAGE (and in the future PLIST) should be able to remember the current message - worked on (or LIST position) so that between pages TOP, NEXT, PREV, BOTTOM, - JUMP special commands could be used without having to always specify the - number of lines of the terminal, etc. - -- Fix mmmail timestamps to be "unsigned int(11)" ? - -- no php, even likes that :) - mktime() to get current timestamp and date("r", timestamp) to turn - timestamp into RFC 822 formatted date. - SUGGEST: - mysql> select unix_timestamp(now()); - +-----------------------+ - | unix_timestamp(now()) | - +-----------------------+ - | 1041000120 | - +-----------------------+ - 1 row in set (0.00 sec) - - mysql> select from_unixtime(1041000120); - +---------------------------+ - | from_unixtime(1041000120) | - +---------------------------+ - | 2002-12-27 09:42:00 | - +---------------------------+ - 1 row in set (0.00 sec) - -- Fix mmmail to use table locking rather than global locking? - -- locking tables: - LOCK TABLES `mail` write, `box` write; - INSERT INTO MAIL (...) VALUES (...); - UPDATE BOX (....) VALUES (....); - UNLOCK TABLES; - XXX Unfortunately it seems that this cannot be done in a thread-safe - way; The whole process would lock until lock could be obtained. - So the current method seems best for now. - It would be working if every thread had a connection to the server, - but we currently are using a single persistent connection to MySQL - for the whole process. - -- Perhaps add UNSIGNED attribute to all mmmail SQL columns which could use - it; This would also require C functions modifications however. - - - -MMSTATD -======= - -- Modify the librarian so that it could still continue to process new logs - while a client is still reading a statistical report as it's own pace. - For this, a buffer could atomically be filled with the request, which - would then be spit out at the speed the client reads it while still checking - for new logs using poll(2). Possibly also allow support for multiple clients - requesting statistics at the same time. This would be a good prelude to - eventually allowing statistical reports via TCP. -- ADD SUPPORT FOR BETTER OUTPUT MODES TO MMSTAT CLIENT - Default would be human format with only necessary columns and name sorting. - Then -n could be used for unix timestamps, -? for no top/botom header(s), - -v for verbose +d +c etc... - Or, defaults to the current format and: - -h human etc... -- It would probably be nice to implement namespaces, and support features such - as export/import/merge of key sets additionnal to rotation. -- Some remote functionality should be implemented. It would be nice to be able - to safely allow statistical reports remotely, and to isolate the - key rotation requests into a separate administration socket. - - There then could be three separate type of sockets: update, admin, stats - - Or, remote persistant connections under SSL could be invisaged so that it - be safe on the network, even instead using a datagram socket for updates. - - It also would be possible to let the administrator decide which keys should - be created, and have the update socket only allow to update existing - counters. - - If remote operation was supported, it would be important for the report - data to be endian-independant. This is still a problem using 64-bit - variables as most libc do not comport functions for host to network or - network to host convertion of 64-bit data types (NetBSD does, of course, - but Linux, another target, does not). - -NEW DESIGN - -mmstat(3) and mmstatd(8) would be most useful if the server could be remote, -and if it comported users and authentication. Here are ideas on how this could -be done without degrading performance of the clients much. Because if we used -TCP and the server was remote, there is a possibility for delays caused by -network problems or latency. This must not happen since the clients must be -able to update statistic counters at a high frequency without expecting a -delay. - -Performance issues - -- The client library could start a daemon process, to which atomic requests - would be sent to by mmstat() function calls. The daemon would establish - a persistent connection to the mmstatd(8) server. This connection could then - be TCP. The communication channel could even be encrypted since the daemon - process would dispatch requests as fast as it can, without slowing down - mmstat() calls. The special process could handle encryption, endian byte - order conversions, and the TCP stream. It also could have it's own signal - handlers and timers to regularily attempt to flush the buffers if it cannot - do it right away after a request. -- Another possibility would be to use a non-blocking socket, with buffering. - At every mmstat() call, or when a timeout occurs, an attempt would be made - to flush the buffer. If EAGAIN is returned, we keep the buffer and will - retry later on. This approach may be less complex than the previous one - to implement. However, if encryption is being used, and that TCP is used, - it is certain that mmstat() calls cannot reach the performance which the - current library implementation yields. If we simply used rc4, which is a - very fast cypher, performance may be reasonable for general purpose use. - However, because we deal with multiple systems on a network, endian - conversions will also need to be performed on the values sent over the - protocol, another operation which will reduce performance on i386 and other - little endian byte order architectures. Moreover, if we used a timer to - flush the buffer at regular intervals if it could not be flushed immediately, - we could result in clobbering the caller process by a signal handler or such. - -Data integrity issues - -- Using any of the above methods, it would be tricky to be able to have the - same data integrity than with the current implementation. The reason is that - two local processes sending eachother AF_LOCAL datagrams almost guarantee - that they obtain their data, and with very low latency. The mmstatd(8) - logger process which permits high data integrity with crash recovery - would almost need to be reproduced at the client library level as well - if we were to achieve the same reliability. This also means that persistent - files on the filesystem would need to be maintained by the client library - for each process. -- A possibility would be to use a local mmstatd(8) proxy or similar system - which could provide crash recovery and establish links with other mmstatd(8) - servers. Perhaps that we could derive a distributed system of sorts over this - system. mmstatd servers would have authenticated links with other mmstatd - servers to which it would forward requests intended for them. Either they - would all synchronize to the same data, or that a form of routing would be - used, with addresses. If such a system is to be implemented, it would be - useful to create an API to allow a general purpose messaging network - over this principle. I wrote notes about a CORBA-like system with better - performance and more secure framework somewhere, I should read those notes - again and attempt to implement something out of them. Basically, it allows - to morph network topology, where nodes may be part of a same process, of - other processes on the same computer, where shared memory and/or AF_LOCAL - could be used, of remote computers, where TCP persistent links with - authentication and encryption could be used. -- A simpler system might be to simply allow mmstatd(8) to mirror to other - fixed mmstatd(8) servers when remote functionality is needed... The librarian - would forward the log entries it processes in network endian with the - other mmstatd(8) server it connects to... - -Allowing mmstatd(8) to work in proxy or master mode might be the simplest way -to implement the system. mmstatd(8) consists of two processes, the logger, -getting messages and logging them to file, and the librarian, reading those -logs from file and effecting the database changes. The librarian could easily -be convereted into a forwarder, where instead of being commited to the memory -database, packets would be forwarded over to a master mmsmtpd. It however -would be most useful to support namespaces. This also means that mmsmtpd must -be able to accept remote clients (slave mmsmtpd clients). We then also would -want authentication and encryption. We however still always have a problem -then where user credentials checking should be implemented... Also, counters -reports could only be obtained from the master site, locally, unless -especially implemented with remote functionality as well. - -Ultimately, if the client side (or application-specific side) cacheing, -recovery and forwarding issues are solved, it would be ideal if mmstatd -supported multiple persistent client connections, through which it could allow -both updates and queries. It then would need to support application-specific -user authentication and at least RC4 encryption. Ideally, each user would be -allowed a number of namespaces they could manage, which would further allow -better administration. If applications required to perform both updates and -queries at the same time in parallel, they would probably need a persistent -handle per parallel thread/process. - - - -MMLIB/MMFD -========== - -- Should probably be called mmio -- Abstract read/write/?poll functions to allow the mmfd library to be portable - under other environments (I.E. kernel code)? Hmm this would make it a little - slower but may be worth it. - - - -MMMAIL2 -======= - -- Review design notes -- Design the modularized interface for storage abstraction -- Check dbmail.org design in case it may give good ideas for mmmail2 design, - (PowerMail). -- Write the whole thing - - - -MMLIB/MMSERVER2 -=============== - -- Think about if it would be possible to have per-process caches with a means - to propagate changes among the processes, rather than simply using a single - socket-specific cache and single global hostname cache with locks. This could - provide potential speed optimizations, especially on multiprocessor systems. - The same is true for locks which are used to allocate and free resources with - mmpool(3). Perhaps that per-process caches would be great, however this needs - to be implemented with care. How would another process free a node allocated - by this process are questions which come into play. Check slab allocator - implementations using SMP optimizations with magazines and per-CPU caches. - Of course, my system would not deal with CPU-based caches, but with process - based ones instead, with the same ideas. - - - -MMLIB/MMPOOL -============ - -- CPU specific memory caches and magazines would be nice, but also require - specialized kernel support for such. I won't care about this for now. - It however might be possible to do this on a per-process basis, I'll have to - think about it a bit. It would prevent having to use synchronization locks - when multiple processes are shareing the same pool, in order to gain more - performance. - - - -MMANONCVS -========= - -- Add section in troubleshooting about errors when upgrading glibc diff --git a/mmsoftware/apache-mmstat/GNUmakefile b/mmsoftware/apache-mmstat/GNUmakefile deleted file mode 100644 index fb4b262..0000000 --- a/mmsoftware/apache-mmstat/GNUmakefile +++ /dev/null @@ -1,24 +0,0 @@ -# $Id: GNUmakefile,v 1.2 2004/05/31 06:19:17 mmondor Exp $ - -MMLIBS := $(addprefix ../mmlib/,mmlog.o mmpool.o mmhash.o mmreadcfg.o \ -mmstat.o mmstring.o) - -OBJS := apache-mmstat.o - -CFLAGS += -Wall - - -all: apache-mmstat - -%.o: %.c - cc -c ${CFLAGS} -I. -I../mmlib -o $@ $< - -apache-mmstat: $(MMLIBS) $(OBJS) - cc -o $@ -lc $(OBJS) $(MMLIBS) - -install: - install -cs -o 0 -g 0 -m 550 apache-mmstat /usr/local/libexec - install -c -o 0 -g 0 -m 444 apache-mmstat.8 /usr/local/man/man8 - -clean: - rm -f apache-mmstat $(OBJS) $(MMLIBS) diff --git a/mmsoftware/apache-mmstat/apache-mmstat.8 b/mmsoftware/apache-mmstat/apache-mmstat.8 deleted file mode 100644 index 26be0c6..0000000 --- a/mmsoftware/apache-mmstat/apache-mmstat.8 +++ /dev/null @@ -1,296 +0,0 @@ -.\" $Id: apache-mmstat.8,v 1.5 2004/05/05 10:01:55 mmondor Exp $ -.\" -.\" Copyright (C) 2003, Matthew Mondor -.\" All rights reserved. -.\" -.\" Redistribution and use in source and binary forms, with or without -.\" modification, are permitted provided that the following conditions -.\" are met: -.\" 1. Redistributions of source code must retain the above copyright -.\" notice, this list of conditions and the following disclaimer. -.\" 2. Redistributions in binary form must reproduce the above copyright -.\" notice, this list of conditions and the following disclaimer in the -.\" documentation and/or other materials provided with the distribution. -.\" 3. All advertising materials mentioning features or use of this software -.\" must display the following acknowledgement: -.\" This product includes software written by Matthew Mondor. -.\" 4. The name of Matthew Mondor may not be used to endorse or promote -.\" products derived from this software without specific prior written -.\" permission. -.\" -.\" THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR -.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES -.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. -.\" IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, -.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -.\" BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF -.\" USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF -.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -.\" -.Dd July 1, 2003 -.Dt APACHE-MMSTAT 3 -.Os mmsoftware -.Sh NAME -.Nm apache-mmstat -.Nd mmstat(3) facility support for apache -.Sh SYNOPSIS -.Nm -.Ar user -.Ar group[,group...] -.Ar mmstatconf -.Ar allow -.Sh DESCRIPTION -.Nm -primarily was written to be used with the apache HTTP server but could well be -used with other web server software, as long as they can adapt their logging -output and automatically pipe logs through it. The goal of this utility is to -provide a frontend to the -.Xr mmstat 3 -library for apache to maintain on-the-fly global and virtual-host specific -statistical counters via the -.Xr mmstatd 8 -daemon. This -.Nm mmstat -facility was written by Matthew Mondor and is available at -.Nm http://mmondor.gobot.ca/software.html . -.Pp -Apache comes with an automatic log rotation utility called rotatelogs. It -also supports the capability of piping logs to it, and other applications, -starting the logfile name with a pipe '|' character. Apache also allows to -control the format of the log lines it generates and their scope (global or -vhost specific). -.Nm -exploits this flexibility. -.Pp -Maintaining these counters may serve several purpose, it can be used as-is, -or in conjunction with a graph system such as rrdtool using the -.Xr mmstat 8 -client as input source, etc. -.Pp -.Ar user -specifies the name or ID of the user to which the process should be set when -revoking superuser privileges. -.Pp -.Ar group -consists of a group, or comma-separated list of group names or IDs which -the process should become part of when revoking superuser privileges. -No spaces should separate the comma-separated list if more than one group -is specified. -.Pp -.Ar mmstatconf -should consist of the absolute path to an -.Xr mmstatd.conf 5 -configuration file which will be used to access the proper -.Xr mmstatd 8 -copy. -.Pp -.Ar allow -specifies the verbosity of wanted statistics. Here is a the meaning of each -allowed letter. By default, nothing is done, when each letter will enable -a feature: -.Bl -tag -width indent -offset indent -.It Nm G -Record global statistics such as: -.Bd -literal -offset indent -apache|total|bytes -apache|total|errors -apache|total|requests -apache|total|denied -apache|total|errors -apache|denied|
-.Ed -.Pp -Several of those may be enabled or disabled depending on other specified -options to -.Ar allow . -.It Nm V -Keep virtual-host specific statistics such as: -.Bd -literal -offset indent -apache|vhost||GET| -apache|vhost||agent| -apache|vhost||bytes -apache|vhost||referer| -apache|vhost||requests -apache|vhost||denied -apache|vhost||denied|
-apache|vhost||errors -apache|vhost||errors|
-.Ed -.Pp -Several of these may be enabled and disabled depending on the other specified -.Ar allow -options. -.It Nm R -Record statistics on vhost-specific referers (only possible if -.Nm V -was also enabled). -.It Nm U -Keep counters on vhost-specific user-agent strings (only allowed if -.Nm V -is enabled). -.It Nm A -Allows IP address of offending HTTP clients to be kept. The reports which have -
are affected. -.It Nm F -Enables stats keeping of transfers (the entries with |GET|). Only valid with -.Nm V . -.El -.Pp -.Sh SECURITY CONSIDERATIONS -First, apache launches piped applications, such as -.Nm -as the superuser (user id 0, root). Because of this, several important -steps have to be taken by both the administrator and the application: -.Ss What the application does -.Bl -tag -width indent -offset indent -.It SIGSEGV signal handler -To prevent a possible segmentation fault violation from causing a process core -dump, a signal handler is setup which immediately calls exit(3). -.It File descriptor cleanup -Only the stdin (0) file descriptor is kept open, and stdout as well as stderr -(1, 2) are redirected to /dev/null. -.It Privilege revokation -One of the first steps which -.Nm -performs is to drop it's privileges to the user and group(s) specified on the -command line arguments. -.El -.Bl -tag -width indent -offset indent -.Ss What the administrator should be aware of -.It The command line arguments origin -It becomes obvious that the command line arguments should be trustable, and -as such should be specified in httpd.conf, which should only be modifiable by -the superuser. -.It The command path -The apache configuration file should always ensure to also specify the -absolute full path to the installed -.Nm -application, and that this command cannot be modified by the users. It also -should not bear any of the setuid or setgid bits in it's permissions. It -can itself safely assign itself to the specified user and groups for each -running copy. -.It The target Xr mmstatd 8 -Also specified on the command line arguments by the apache process is the -configuration file of the targetted -.Xr mmstatd 8 -daemon which should receive the request. This allows alot of flexibility; -Users can each run their own -.Xr mmstatd 8 -if wanted, or the administrator can decide to use a specific dedicated one, -or the system one, etc. See the -.Xr mmstat 3 , -.Xr mmstatd 8 -and -.Xr mmstat 8 -man pages for more information. -.El -.Pp -A second step which has to be taken in consideration is that the log lines -which apache generates include fields which are supplied by the HTTP clients, -and as such are untrustable. To cope with this, -.Nm -observes the following strategy: -.Bl -tag -width indent -offset indent -.Ss How the lines read from stdin are parsed -.It Superuser privileges are revoked -The permissions are dropped before even starting to read lines from apache via -stdin. -.It Line buffer not on the stack -The input line buffer into which lines are read from stdin is allocated using -.Xr malloc 3 -so that it is not located on the stack. Although -.Xr fgets 3 -is used with a specified limit to not exceed the buffer, if a line overflow -ever occurs either at input or post processing, it would be much more likely -to cause a segmentation fault generating a SIGSEGV signal which would kill -the application immediately and let apache restart it, rather than allowing -a third party to modify or trash the actual program code and result in -unexpected behavior. -.It Line sanity checking -Any lines which does not have the '\\n' terminator are dropped, which are most -likely the result of a previous line which was unreasonably long or could not -be parsed properly. Apart from the terminating '\\n', any line which has -abnormal control characters (< ASCII 32) is immediately ignored. -.Pp -Special characters which may cause problems either for -.Xr mmstat 3 -key naming -or for C -.Xr stdarg 3 -are substituted by other characters. -.Pp -Because apache guarantees no safe field separation character which could not -be supplied by the HTTP client and cause parsing problems, the '|' character -was chosen as field separator. -.Nm -initially locates and separates the line at '|' characters and then drops it -immediately if it does not have the expected separators. -.Pp -The last expected field consists of the HTTP response code which was sent to -the client. If the number of columns matched, this value is evaluated for -valid common response codes, and is ignored if it appears to not conform. -.Pp -Ultimately, if all the sanity checks have succeeded, the -.Fn mmstat_transact -and -.Fn mmstat -functions of the -.Xr mmstat 3 -library are used to delegate statistics in the form of logs to the -.Xr mmstat 8 -daemon which normally already runs under another unprivileged user -(usually mmstatd user, or an arbitrary normal user which runs mmstat and -requests the administrator to run apache-mmstat for statistical book keeping). -The -.Xr mmstat 3 -library expects no reply packet back from -.Xr mmstatd 8 -for confirmation and as such, a valevolent replacement would not be able to -cause unexpected results other than to close the socket or not accept packets. -.El -.Sh CONFIGURATION -This in no way aims to replace an apache manual, obviously. However, some -basics are necessary for -.Nm -to be used successfully: -.Bl -tag -width indent -offset indent -.It An mmstatd server should be running -This may be any -.Xr mmstatd 8 -running copy, since the application is told which configuration file to use -to access it. -.It The expected logline format -.Nm -expects a specific logline format which apache can be configured to generate. -Here consists of the apache-specific format: -.Bd -literal -# VHost RemoteAddr UserAgent Referer BytesSent RequestMethod Request Status -LogFormat "%v|%a|%{Referer}i|%{User-Agent}i|%B|%m|%U|%>s" apache-mmstat -.Ed -.It How to feed the application with apache -Here is a general global configuration which an administrator may use to -record verbose statistics (global and on all virtual hosts): -.Bd -literal -CustomLog "| /usr/local/libexec/apache-mmstat www www,mmstat /etc/mmstatd.conf GVAF" apache-mmstat -.Ed -.Pp -Another use would be to run it for a local user who runs his own -.Xr mmstatd 8 : -.Bd -literal -CustomLog "| /usr/local/libexec/apache-mmstat mmondor users /home/users/mmondor/root/etc/mmstatd.conf VAF" apache-mmstat -.Ed -.Pp -This would ensure to change the real and effective user IDs to 'mmondor' user, -set the groups as mmondor's primary group, 'users' and to use his own -.Xr mmstatd.conf 5 , - '/home/users/mmondor/root/etc/mmstatd.conf', which for instance may specify -to use the '/home/users/mmondor/root/var/mmstatd/mmstatd_log.sock' socket. -.El -.Sh AUTHOR -apache-mmstatd was written by Matthew Mondor. -.Sh SEE ALSO -.Xr httpd 8 , -.Xr mmstat 3 , -.Xr mmstatd 8 . diff --git a/mmsoftware/apache-mmstat/apache-mmstat.c b/mmsoftware/apache-mmstat/apache-mmstat.c deleted file mode 100644 index 40b2979..0000000 --- a/mmsoftware/apache-mmstat/apache-mmstat.c +++ /dev/null @@ -1,400 +0,0 @@ -/* $Id: apache-mmstat.c,v 1.4 2004/09/28 20:59:11 mmondor Exp $ */ - -/* - * Copyright (C) 2003, Matthew Mondor - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Matthew Mondor. - * 4. The name of Matthew Mondor may not be used to endorse or promote - * products derived from this software without specific prior written - * permission. - * 5. Redistribution of source code may not be released under the terms of - * any GNU Public License derivate. - * - * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - - - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include /* Only used for user/group related functions */ -#include -#include - - - -#define LINESIZ 4096 - - - -int main(int, char **); - -static void log_parse(mmstat_t *, char *); -static void sighandler(int); - - - -/* Our list of command line arguments */ -enum argline { - ARG_COMMAND = 0, - ARG_USER, - ARG_GROUPS, - ARG_CONF, - ARG_OPTIONS, - ARG_MAX -}; - -/* Logline columns which we expect */ -enum logline { - COL_VHOST = 0, - COL_REMOTEADDR, - COL_REFERER, - COL_USERAGENT, - COL_BYTES, - COL_METHOD, - COL_REQUEST, - COL_STATUS, - COL_MAX -}; - - - -/* Globals */ -static bool LOG_GLOBAL; /* G */ -static bool LOG_VHOST; /* V */ -static bool LOG_REFERER; /* R */ -static bool LOG_USERAGENT; /* U */ -static bool LOG_REMOTEADDR; /* A */ -static bool LOG_REQUEST; /* F */ - - - -int -main(int argc, char **argv) -{ - char *linebuf; - mmstat_t mms; - struct sigaction act; - - /* Setup a signal handler for SIGSEGV so that we prevent core dumping if - * we ever crash. - */ - act.sa_handler = sighandler; - act.sa_flags = SA_NOCLDWAIT; - sigemptyset(&act.sa_mask); - sigaction(SIGSEGV, &act, NULL); - - /* We're normally started from apache, and run as the superuser. We - * therefore do all we can to be safe until we drop privileges... Let's - * first redirect unnecessary filedescriptors to /dev/null. But, keep - * stdin, of course, which we'll read logs from later on. - */ - { - int fd; - - if ((fd = open("/dev/null", O_RDWR)) != -1) { - dup2(fd, 1); - dup2(fd, 2); - if (fd > 2) - close(fd); - } - } - - /* Now perform sanity checking on launching mode and user supplied - * arguments. - */ - - /* Apache launches us as uid 0, we'll drop privileges soon however */ - if (getuid() != 0) { - syslog(LOG_NOTICE, "%s: Not started as uid 0 from apache!? (uid %d)", - argv[ARG_COMMAND], getuid()); - exit(EXIT_FAILURE); - } - - /* We only accept a fixed number of arguments so that we restrict the - * need for getopt() and other libraries, or a more complex system when - * we're root. - */ - if (argc != ARG_MAX) { - syslog(LOG_NOTICE, "%s: Started with wrong parameters", - argv[ARG_COMMAND]); - exit(EXIT_FAILURE); - } - - /* Make sure that supplied user and group(s) are valid, and if so, - * drop privileges already. - */ - { - uid_t uid; - gid_t *gids; - int ngids; - - if ((uid = mmgetuid(argv[ARG_USER])) == -1) { - syslog(LOG_NOTICE, "%s: Unknown user '%s'", argv[ARG_COMMAND], - argv[ARG_USER]); - exit(EXIT_FAILURE); - } - - if (!(gids = mmgetgidarray(&ngids, argv[ARG_GROUPS]))) { - syslog(LOG_NOTICE, "%s: One of following groups unknown: '%s'", - argv[ARG_COMMAND], argv[ARG_GROUPS]); - exit(EXIT_FAILURE); - } - - /* NOTE: mmdropprivs() uses setegid(2), setgid(2), setgroups(2), - * seteuid(2), setgid(2), and then verifies that it really changed to - * the expected user permissions, in order to return TRUE on success. - */ - if (!mmdropprivs(uid, gids, ngids)) { - syslog(LOG_NOTICE, "%s: Cannot change uid and gids to safe privs", - argv[ARG_COMMAND]); - exit(EXIT_FAILURE); - } - mmfreegidarray(gids); - } - - /* Et voila, we're no longer the superuser. We can now proceed and - * perform our slave chores as mortals. First set the MMSTATCONF - * environment variable for mmstat(3) API to load the right configuration - * file. Then, call the logging function, just because we want the main - * loop out of main(). - */ - /* Log nothing by default, enable parts which were requested only. */ - LOG_GLOBAL = LOG_VHOST = LOG_REFERER = LOG_USERAGENT = LOG_REMOTEADDR = - LOG_REQUEST = FALSE; - if (mm_strchr(argv[ARG_OPTIONS], 'G')) - LOG_GLOBAL = TRUE; - if (mm_strchr(argv[ARG_OPTIONS], 'V')) - LOG_VHOST = TRUE; - if (mm_strchr(argv[ARG_OPTIONS], 'R')) - LOG_REFERER = TRUE; - if (mm_strchr(argv[ARG_OPTIONS], 'U')) - LOG_USERAGENT = TRUE; - if (mm_strchr(argv[ARG_OPTIONS], 'A')) - LOG_REMOTEADDR = TRUE; - if (mm_strchr(argv[ARG_OPTIONS], 'F')) - LOG_REQUEST = TRUE; - - if (setenv("MMSTATCONF", argv[ARG_CONF], TRUE) != 0) { - syslog(LOG_NOTICE, "%s: Cannot setenv(3)", argv[ARG_COMMAND]); - exit(EXIT_FAILURE); - } - - if (!mmstat_init(&mms, TRUE, TRUE)) { - syslog(LOG_NOTICE, "%s: Cannot initialize mmstat(3)", - argv[ARG_COMMAND]); - exit(EXIT_FAILURE); - } - - /* We preferably don't want the line buffer to be on the stack */ - if ((linebuf = malloc(LINESIZ)) == NULL) { - syslog(LOG_NOTICE, "%s: Cannot allocate line buffer", - argv[ARG_COMMAND]); - exit(EXIT_FAILURE); - } - - log_parse(&mms, linebuf); - - /* NOTREACHED */ - exit(EXIT_SUCCESS); -} - - -/* ARGSUSED */ -static void -sighandler(int sig) -{ - /* We only catch SIGSEGV with this handler, and exit normally. */ - exit(EXIT_SUCCESS); -} - - -/* When we get called, privileges have been revoked and mmstat(3) has been - * successfully initialized. - */ -static void -log_parse(mmstat_t *mms, char *line) -{ - char *cols[COL_MAX + 1]; - - /* We'll exit if the pipe is closed by apache */ - while (fgets(line, LINESIZ - 1, stdin) == line) { - size_t len; - int status; - char *ptr; - - status = 1; - - /* Strip ending "\n". If there are none, we ignore the line as it - * consists of an abnormally long request which exceeds LINESIZ. - * It's next continueing line will then obviously not match the - * expected columns and will as a result also be ignored. It's - * unfortunate that fgets(3) cannot report that the line was not - * terminated in a faster way, without us having to go strip the - * line termination, but oh well, I don't want to use mmfd(3) for - * this. I would if I needed additional rate/bandwidth limits however. - */ - len = mm_strlen(line); - if (len > 0 && line[len - 1] == '\n') - line[len - 1] = '\0'; - else - continue; - - /* Strip dangerous characters from line */ - for (ptr = line; *ptr != '\0'; ptr++) { - if (*ptr < 32) { - status = 0; - break; - } - switch (*ptr) { - case ' ': /* No spaces in key names */ - *ptr = '_'; - break; - case '*': /* Considered as wildcards by mmstat(3) */ - /* FALLTHROUGH */ - case '?': - /* FALLTHROUGH */ - case '%': /* Why not, we use stdarg(3) alot */ - *ptr = '$'; - break; - } - } - if (status == 0) - continue; - - /* Now separate line in columns and verify if the number of columns - * is the expected one. If it's not, ignore it. - */ - if (mm_strspl(cols, line, COL_MAX, '|') != COL_MAX) - continue; - - /* Verify that status is valid, it consists of the last field. If - * a malformed request or a user-supplied entry containing '|' was - * present, this would simply ignore the line, the correct behavior. - */ - if ((status = atoi(cols[COL_STATUS])) == 0) - continue; - - /* Start an mmstat(3) transaction, which makes sure that everything - * be processed atomically. This also is the recovery unit. - * Using a transaction is not a requirement for atomicity in this - * case, but it's more efficient than performing each operation - * independantly, beleive it or not (only one I/O syscall required). - */ - mmstat_transact(mms, TRUE); - - if (LOG_GLOBAL) - mmstat(mms, STAT_UPDATE, 1, "apache|total|requests"); - - switch (status) { - case 200: /* Success */ - { - long bytes; - - bytes = atol(cols[COL_BYTES]); - - if (LOG_GLOBAL) - mmstat(mms, STAT_UPDATE, bytes, "apache|total|bytes"); - if (LOG_VHOST) { - mmstat(mms, STAT_UPDATE, 1, "apache|vhost|%s|requests", - cols[COL_VHOST]); - mmstat(mms, STAT_UPDATE, bytes, "apache|vhost|%s|bytes", - cols[COL_VHOST]); - if (LOG_REQUEST) - mmstat(mms, STAT_UPDATE, 1, "apache|vhost|%s|%s|%s", - cols[COL_VHOST], cols[COL_METHOD], - cols[COL_REQUEST]); - if (LOG_REFERER) - mmstat(mms, STAT_UPDATE, 1, - "apache|vhost|%s|referer|%s", cols[COL_VHOST], - cols[COL_REFERER]); - if (LOG_USERAGENT) - mmstat(mms, STAT_UPDATE, 1, "apache|vhost|%s|agent|%s", - cols[COL_VHOST], cols[COL_USERAGENT]); - } - } - break; - case 404: /* Not found */ - if (LOG_GLOBAL) - mmstat(mms, STAT_UPDATE, 1, "apache|total|errors"); - if (LOG_VHOST) { - mmstat(mms, STAT_UPDATE, 1, "apache|vhost|%s|errors", - cols[COL_VHOST]); - if (LOG_REFERER) - mmstat(mms, STAT_UPDATE, 1, "apache|vhost|%s|referer|%s", - cols[COL_VHOST], cols[COL_REFERER]); - if (LOG_USERAGENT) - mmstat(mms, STAT_UPDATE, 1, "apache|vhost|%s|agent|%s", - cols[COL_VHOST], cols[COL_USERAGENT]); - } - break; - case 400: /* Bad request */ - if (LOG_GLOBAL) - mmstat(mms, STAT_UPDATE, 1, "apache|total|errors"); - if (LOG_VHOST) { - if (LOG_REFERER) - mmstat(mms, STAT_UPDATE, 1, "apache|vhost|%s|referer|%s", - cols[COL_VHOST], cols[COL_REFERER]); - if (LOG_USERAGENT) - mmstat(mms, STAT_UPDATE, 1, "apache|vhost|agent|%s", - cols[COL_VHOST], cols[COL_USERAGENT]); - } - break; - case 403: /* Denied */ - if (LOG_GLOBAL) { - mmstat(mms, STAT_UPDATE, 1, "apache|total|denied"); - if (LOG_REMOTEADDR) - mmstat(mms, STAT_UPDATE, 1, "apache|denied|%s", - cols[COL_REMOTEADDR]); - } - if (LOG_VHOST) { - mmstat(mms, STAT_UPDATE, 1, "apache|vhost|%s|denied", - cols[COL_VHOST]); - if (LOG_REMOTEADDR) - mmstat(mms, STAT_UPDATE, 1, "apache|vhost|%s|denied|%s", - cols[COL_VHOST], cols[COL_REMOTEADDR]); - if (LOG_REFERER) - mmstat(mms, STAT_UPDATE, 1, "apache|vhost|%s|referer|%s", - cols[COL_VHOST], cols[COL_REFERER]); - if (LOG_USERAGENT) - mmstat(mms, STAT_UPDATE, 1, "apache|vhost|%s|agent|%s", - cols[COL_VHOST], cols[COL_USERAGENT]); - } - break; - } - - /* Close transaction, that is, commit any changes. Once this is - * called, the statistics are relayed to the mmstat(8) daemon. - */ - if (!mmstat_transact(mms, FALSE)) - syslog(LOG_NOTICE, "mmstat error - %s", strerror(errno)); - } -} diff --git a/mmsoftware/apache-mmstat/makepart.sh b/mmsoftware/apache-mmstat/makepart.sh deleted file mode 100755 index 1b53154..0000000 --- a/mmsoftware/apache-mmstat/makepart.sh +++ /dev/null @@ -1,25 +0,0 @@ -#!/bin/sh -# $Id: makepart.sh,v 1.1 2003/07/02 17:20:55 mmondor Exp $ - -. ../mmlib/makedefs.sh - -OBJS='apache-mmstat.o' -BIN1='apache-mmstat' - -if [ "$1" = "clean" ]; then - show $RM $OBJS $BIN1 - exit 0 -fi - -INCDIR="-I../mmlib $STDINC" -LIBDIR="$STDLIB" -LIBS='../mmlib/libmmondor.a -lc' - -for obj in $OBJS; do - if [ ! -f $obj ]; then - src=`$ECHO $obj | $SED 's/\.o$/.c/'` - show $CC $CFLAGS -c $INCDIR $src - fi -done - -show $CC $CFLAGS -o $BIN1 $INCDIR $LIBDIR $BIN1.o $LIBS diff --git a/mmsoftware/bot/Makefile b/mmsoftware/bot/Makefile deleted file mode 100644 index d4c6585..0000000 --- a/mmsoftware/bot/Makefile +++ /dev/null @@ -1,36 +0,0 @@ -# $Id: Makefile,v 1.2 2003/01/01 14:54:10 mmondor Exp $ - -CC = gcc -MAKE = make -#STRIP = strip - -CFLAGS += -D_REENTRANT -Wall - -INCDIR = -I../mmlib -I/usr/include -I/usr/pkg/include -LIBDIR = -L/usr/local/lib -L/usr/lib -L/lib -L/usr/pkg/lib - -LIBS = ../mmlib/libmmondor.a -lc - -OBJS = zenbot.o - - - -CCOBJ = $(CC) $(CFLAGS) -c $(INCDIR) - - - - -zenbot: $(OBJS) - $(CC) $(CFLAGS) -o zenbot $(INCDIR) $(LIBDIR) $(OBJS) $(LIBS) -# $(STRIP) -s zenbot - - - - -clean: - -rm -f $(OBJS) zenbot - - -zenbot.o: - $(CCOBJ) zenbot.c - diff --git a/mmsoftware/bot/irc.txt b/mmsoftware/bot/irc.txt deleted file mode 100644 index 359b9ec..0000000 --- a/mmsoftware/bot/irc.txt +++ /dev/null @@ -1,399 +0,0 @@ -:!@ ... : -":*!*@* * *" rest depends on command - -:nanobit!identz@205.205.36.96 JOIN :#xlnx -:Isky!~jorwyn@216.255.216.37 PRIVMSG #xlnx :it turns out Arizona wouldn't ... -:phad_!mmondor@ppp3.arobas.net PRIVMSG nanobit :sup -:phad_!mmondor@ppp3.arobas.net KICK #linux nanobit :because. - - - -: ... : -":* * *" - -:irc.gobot.ca NOTICE nanobit :*** Notice -- motd was last changed at - - - -: ... ": -":* ??? *" rest depends on code - -:irc.gobot.ca 001 nanobit :Welcome to Rubiks IRC nanobit!nanobit@192.168.1.4 -:irc.gobot.ca 002 nanobit :Your host is irc.gobot.ca[@0.0.0.0], running version bahamut(rubiks)-4.2(01) -:irc.gobot.ca 003 nanobit :This server was created Tue Oct 9 2001 at 01:53:06 EDT -:irc.gobot.ca 004 nanobit irc.gobot.ca bahamut(rubiks)-4.2(01) oiwscrknfydaAbghe biklmnoprRstvc -:irc.gobot.ca 005 nanobit NOQUIT WATCH=128 SAFELIST LITH_HOSTMASK SAJOIN :are available on this server -:irc.gobot.ca 251 nanobit :There are 1 users and 0 invisible on 1 servers -:irc.gobot.ca 255 nanobit :I have 1 clients and 0 servers -:irc.gobot.ca 265 nanobit :Current local users: 1 Max: 2 -:irc.gobot.ca 266 nanobit :Current global users: 1 Max: 2 -:irc.gobot.ca 422 nanobit :MOTD File is missing -:irc.gobot.ca 353 nanobit = #netbsd :nanobit -:irc.gobot.ca 366 nanobit #netbsd :End of /NAMES list. -:twisted.ma.us.dal.net 332 nanobit #xlnx :Welcome to #xlnx. | Don't be a dick. | IceWM is for jblack lovers. | EAT SCRYE'S ASS | scrye: I wanted to play with you guys -:twisted.ma.us.dal.net 333 nanobit #xlnx simoriah 1026932695 -:twisted.ma.us.dal.net 353 nanobit @ #xlnx :nanobit uidzero gxx dalej snL20 Isky phad lucca PFY bylzz Dralock Zalamander emann kerx Traktopel OpenSorceror deegan slaker Psi-Jack @simoriah Scrye gezr malkodan Magnus-swe adefa zemo ananke -:twisted.ma.us.dal.net 366 nanobit #xlnx :End of /NAMES list. - - - -PING : -"PING :*" - -PING :twisted.ma.us.dal.net - - - -In interesting way to protect a channel would be having several bots from -several hosts connect to various ircd on a single network, and joining to -a common channel (the first bot joining would make sure to set the invisible -flag to make sure that users do not see which one it is using /whois). - -They then would use that channel for synchronization. When a bot on the other -channel requires op status, it would consult the list of users on the hidden -channel and try to locate in the wanted channel a common one with op status. -It would then message that bot with appropriate passphrase for the bot with -required status to op the requesting bot. If the bot could not obtain status, -it will try again using another bot on the channel. - -The same technique could be used to distribute the load of security enforcing -commands among the bots. A single bot for instance could be there to detect -flood and/or spam anonymously, and communicate to a random bot a command -which should be performed to clean up. Possibly the channel public messages -could be used with read/write throtling so that a request be sent to all -bots, the first one getting it would notice the others that it has it, -and so would somewhat issue a lock for others to not handle it... Because -of possible server desynch I am not sure this would be a viable method. -Distributing actions among the bots would allow a quite large number of -flooding hosts to be banned and kicked out, without causing a single -client to be considered flooding by the server, services or other bots. - -Operators of the channel would be able to use another passphrase and request -op status from any currently opped bot on the channel, and possibly also -to defer some channel actions to them. - - - - - - -After a login, with user and nick, I must wait for those: -:NickServ!service@dal.net NOTICE BritishX :This nick is owned by someone -else. -If those arise, I must register using PRIVMSG NickServ :identify -and then wait for a: -:NickServ!service@dal.net NOTICE BritishX :Password accepted for BritishX. - -Then I may join the wanted channels. - - -after nick: -:lineone.uk.eu.dal.net 372 _Other1_ :- !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -... -:lineone.uk.eu.dal.net 376 _Other1_ :End of /MOTD command. -:lineone.uk.eu.dal.net NOTICE _Other1_ :*** Notice -- This server runs an -... -:_Other1_ MODE _Other1_ :+i - - -after join: -:lineone.uk.eu.dal.net 353 _Other1_ * #linux :_Other1_ tallship Travis|H -labrado -:lineone.uk.eu.dal.net 366 _Other1_ #linux :End of /NAMES list. - -messages: -:BYellZBub!NatRouter@c575326-a.elnsng1.mi.home.com PRIVMSG #Linux :heh, it's -jus -:GenericBoy!punck@dap06-158142.cora.sgi.net PRIVMSG #linux :norculf: after -david -:mEYoW-x!iqxbnd@co512570-a.nimc1.on.home.com PRIVMSG #Linux :*** KMail got -signa -:drummer^!fathafaxxa@202.9.69.138 JOIN :#linux -:The_Hound!popeye@64-39-0-40.dhcp.hq.rackspace.com PRIVMSG #linux :TheInsanity : -:drummer^!fathafaxxa@202.9.69.138 NICK :|sQUaLL| -:rttrtrtrrw!ttrtrrtrat@cs27113-208.houston.rr.com NICK :linux_dumbo -:Scrye!~scrye@sparcie.dynup.net JOIN :#linux -:gezr!~gezr@max01-bt-14.bt.anc.net PRIVMSG #linux :Scrye ! -:The_Hound!popeye@64-39-0-40.dhcp.hq.rackspace.com PRIVMSG #linux :Scrye ! -:kimo_sabe!nick@cx331430-a.tucson1.az.home.com PRIVMSG #linux :linux_dumbo: -noth -:M1ck!mick@1Cust72.tnt7.sfo3.da.uu.net PART #linux -:_Other1_!jomuzyn@1Cust85.tnt6.montreal.qb.da.uu.net PRIVMSG _Other1_ :Test -:The_Hound!popeye@64-39-0-40.dhcp.hq.rackspace.com KICK #Linux BYellZBub -:autoki -:ChanServ!service@dal.net MODE #linux +b *!*@64.182.28.* -:ChanServ!service@dal.net KICK #linux LoLos17 :User has been banned from the -cha -:slak_!poo@DIGI151.GRUNDY.NETSCOPE.NET JOIN :#linux -:kimo_sabe!nick@cx331430-a.tucson1.az.home.com PRIVMSG #linux :QuaCka: what? -:icak!2x@bpp-117.mega.net.id PART #linux -:MoX12!fower@ppp13750.qc.bellglobal.com PRIVMSG _Other1_ :2,4 ya tu du monde -:Guest60775!__________@bha4-s10.mtl.colba.net PRIVMSG #montreal :charmant21????? -:Frankiez`!funky@202.54.122.202 QUIT :Ping timeout -:gezr!~gezr@max01-bt-14.bt.anc.net PRIVMSG #linux :take care GenericBoy -:GenericBoy!punck@dap06-158142.cora.sgi.net QUIT :Quit: [x]chat - -wall: -:Martinos!uhu@1Cust85.tnt6.montreal.qb.da.uu.net NOTICE LrdBritish :BX-Wal#tantris] hello m/l[ - -:sLrdBritih!oqysuma@2Cust105.tnt6.montreal.qb.da.uu.net INVITE _Other1_ :#esoterism - - - - - -350$ Simulateur d'ampli de guitare -POD - - - -#Montreal: JOIN seb23!Absolom@modemcable114.90-200-24.mtl.mc.videotron.net -#?: NICK seb23!Absolom@modemcable114.90-200-24.mtl.mc.videotron.net = Absolom -montreal :y'a tu des gars qui veule parler avec une fille de 13 ans: fille_cool!blabla@ppp8900.qc.bellglobal.com: montreal :y'a tu des gars qui veule parler avec une fille de 13 ans -#?: QUIT samia1!~aa@194.204.206.216 (Read error: Connection reset by peer) -montreal :Suck it bitch: N9thMareZ!~get@qc-mon-pel-ap3-03-15.look.ca: montreal :Suck it bitch -montreal: PART jenval!toby2@ts1-189.f1232.quebectel.com -montreal :tina viens ma fermer ;): N9thMareZ!~get@qc-mon-pel-ap3-03-15.look.ca: montreal :tina viens ma fermer ;) -MOTD: ogDump1_ :- *** This is the short motd *** -NAMES: ogDump1_ = #Montreal :LogDump1_ Guest84486 beaubrun_sensuel Re[A]l_Sli[M] - - - -#?: QUIT sim27!allo@modemcable170.117-201-24.mtl.mc.videotron.net (Quit: Leaving) -#montreal: PART belle_fille14!katymilou@ppp13552.qc.bellglobal.com -#montreal: JOIN [[tina]]!POWER@HSE-Montreal-ppp141376.sympatico.ca -NAMES: LogDump1_ = #Montreal :BnK BananaSplit- girl__15 lol1ta123 -^^conQueror^^ -Neodraxx PRINCE_FRANCISCO_DELAHOYA Eric-24 obskurit DarkElfes froster -[PaRtIii] -cyn13 lscurite fille14 seb22 La_Fille_De_Bouch_Trop_Bronzer KroniKK adtz -andy - -MOTD: LogDump1_ :- -MOTD: LogDump1_ :- Help & Information: -MOTD: LogDump1_ :- -MOTD: LogDump1_ :- DALnet http://www.dal.net - -DCC SEND C:\WINDOWS\LIFE_STAGES.TXT.SHS 2506328647 4705 39936: marie-h34!terre@spc-isp-mtl-58-4-324.sprint.ca: LogDump1_ :DCC SEND C:\WINDOWS\LIFE_STAGES.TXT.SHS 2506328647 4705 39936LogDump1_ : - - -TOPIC changes, NETSPLITS - - -LogDump_: NOTICE FROM NickServ!service@dal.net: This nick is owned by -someone el - - - -whois LogDump_ -:sodre.nj.us.dal.net 311 testing1_ LogDump_ ejelyb -1Cust167.tnt5.montreal.qb.da.uu.net * :LogDump_ -:sodre.nj.us.dal.net 319 testing1_ LogDump_ :#sexe++ #Tantrism #Teleportation #Esoterism -:sodre.nj.us.dal.net 312 testing1_ LogDump_ splitrock.tx.us.dal.net -:Splitrock Internet Services www.splitrock.net -:sodre.nj.us.dal.net 307 testing1_ LogDump_ :has identified for this nick -:sodre.nj.us.dal.net 318 testing1_ LogDump_ :End of /WHOIS list. - - - - - - - - - - -:splitrock.tx.us.dal.net NOTICE AUTH :*** Looking up your hostname... -:splitrock.tx.us.dal.net NOTICE AUTH :*** Checking Ident -:splitrock.tx.us.dal.net NOTICE AUTH :*** Found your hostname -:splitrock.tx.us.dal.net NOTICE AUTH :*** Got Ident response -user me me me logdump_ -nick logdump_ -:splitrock.tx.us.dal.net 001 logdump_ :Welcome to the DALnet IRC Network logdump_!iravovuf@205.205.36.84 -:splitrock.tx.us.dal.net 002 logdump_ :Your host is splitrock.tx.us.dal.net[@0.0.0.0], running version bahamut(pelennor)-1.4(08) -:splitrock.tx.us.dal.net 003 logdump_ :This server was created Sat Nov 18 2000 at 11:14:28 CST -:splitrock.tx.us.dal.net 004 logdump_ splitrock.tx.us.dal.net bahamut(pelennor)-1.4(08) oiwscrknfydaAbghe biklmnoprRstvc -:splitrock.tx.us.dal.net 005 logdump_ NOQUIT WATCH=128 SAFELIST :are available on this server -:splitrock.tx.us.dal.net 251 logdump_ :There are 2418 users and 51902 invisible on 21 servers -:splitrock.tx.us.dal.net 252 logdump_ 54 :IRC Operators online -:splitrock.tx.us.dal.net 254 logdump_ 18765 :channels formed -:splitrock.tx.us.dal.net 255 logdump_ :I have 5604 clients and 1 servers -:splitrock.tx.us.dal.net 265 logdump_ :Current local users: 5604 Max: 6014 -:splitrock.tx.us.dal.net 266 logdump_ :Current global users: 54320 Max: 80132 -:splitrock.tx.us.dal.net NOTICE logdump_ :*** Notice -- motd was last changed at 20/12/2000 4:05 -:splitrock.tx.us.dal.net NOTICE logdump_ :*** Notice -- Please read the motd if you haven't read it -:splitrock.tx.us.dal.net 375 logdump_ :- splitrock.tx.us.dal.net Message of the Day - -:splitrock.tx.us.dal.net 372 logdump_ :- *** This is the short motd *** -:splitrock.tx.us.dal.net 376 logdump_ :End of /MOTD command. -:splitrock.tx.us.dal.net NOTICE logdump_ :*** Notice -- This server runs an open proxy/wingate detection monitor. -:splitrock.tx.us.dal.net NOTICE logdump_ :*** Notice -- If you see a port 1080 or port 23 connection from proxy3.monitor.dal.net -:splitrock.tx.us.dal.net NOTICE logdump_ :*** Notice -- please disregard it, as it is the detector in action. -:splitrock.tx.us.dal.net NOTICE logdump_ :*** Notice -- For more information please see http://kline.dal.net/proxy/wingate.htm -:logdump_ MODE logdump_ :+i -quit -ERROR :Closing Link: 205.205.36.84 (Quit: logdump_) - - - - -:NickServ!service@dal.net NOTICE LrdBritish :This nick is owned by someone else. Please choose another. -:NickServ!service@dal.net NOTICE LrdBritish :If this is your nick, type: /msg NickServ@services.dal.net IDENTIFY -:NickServ!service@dal.net NOTICE LrdBritish :Your nick will be changed in 60 seconds if you do not comply. nickserv :identify mypassword -:NickServ!service@dal.net NOTICE LrdBritish :Password accepted for LrdBritish.:MemoServ!service@dal.net NOTICE LrdBritish :New DALnet news is available! To read, use: /msg MemoServ@services.dal.net NEWS - - -:sniper.tx.us.dal.net 433 * somenick3 :Nickname is already in use. - - - -join :#unices -:logdump_!enyqyjaqa@ppp14.arobas.net JOIN :#unices -:paranoia.se.eu.dal.net 353 logdump_ = #unices :logdump_ @LrdBritish -:paranoia.se.eu.dal.net 366 logdump_ #unices :End of /NAMES list. -mode #unices -:paranoia.se.eu.dal.net 324 logdump_ #unices +tn -:LrdBritish!nytu@ppp14.arobas.net MODE #unices +o logdump_ -:LrdBritish!nytu@ppp14.arobas.net MODE #unices -o logdump_ -:LrdBritish!nytu@ppp14.arobas.net PART #unices -:LrdBritish!nytu@ppp14.arobas.net JOIN :#unices -:LrdBritish!nytu@ppp14.arobas.net QUIT :Quit: Later -:LrdBritish!odolov@ppp14.arobas.net NOTICE logdump_ :[BX-Wall/#unices] hey -mode LrdBritish -:paranoia.se.eu.dal.net 221 LrdBritish +i -mode LrdBritish +s -:LrdBritish MODE LrdBritish :+s - - - -silence :+ -silence :- -silence - -ping : -: PONG : -whois : -...etc... - - - - -mode #linux b -:splitrock.tx.us.dal.net 367 test111 #linux *!*@*.worldonline.nl ChanServ!service@dal.net 978038953 -:splitrock.tx.us.dal.net 367 test111 #linux *!*heldonsat@*.lndn1.on.wave.home.com FAdmThiago!thiago@loki.nw.com.br 978038681 -:splitrock.tx.us.dal.net 367 test111 #linux *!*@*.nj.dial-access.att.net ChanServ!service@dal.net 978037311 -:splitrock.tx.us.dal.net 367 test111 #linux *!*@*.neo.rr.com ChanServ!service@dal.net 978033484 -:splitrock.tx.us.dal.net 367 test111 #linux Guest*!*@* ChanServ!service@dal.net 978031468 -:splitrock.tx.us.dal.net 367 test111 #linux *!*@203.197.* ChanServ!service@dal.net 978029728 -:splitrock.tx.us.dal.net 367 test111 #linux *!*caldera*@* PhadThao!mad@ppp97.arobas.net 978029256 -:splitrock.tx.us.dal.net 367 test111 #linux *!*@*.next-wave.net Psi-Jack!psi-jack@pool-63.52.169.220.dlls.grid.net 978028936 -:splitrock.tx.us.dal.net 367 test111 #linux *!*@*.aol.com Psi-Jack!psi-jack@pool-63.52.169.220.dlls.grid.net 978028879 -:splitrock.tx.us.dal.net 367 test111 #linux *!*@62.25.84.* PhadThao!moput@ppp97.arobas.net 978028388 -:splitrock.tx.us.dal.net 367 test111 #linux *!*ADPanko*@*.next-wave.net Psi-Jack!psi-jack@pool-63.52.169.28.dlls.grid.net 978026959 -:splitrock.tx.us.dal.net 367 test111 #linux *help*!*@* Psi-Jack!psi-jack@pool-63.52.169.28.dlls.grid.net 978026745 -:splitrock.tx.us.dal.net 367 test111 #linux *!*@202.* ChanServ!service@dal.net 978026025 -:splitrock.tx.us.dal.net 367 test111 #linux *!*no*@140.251.* Psi-Jack!psi-jack@pool-63.52.169.28.dlls.grid.net 978025931 -:splitrock.tx.us.dal.net 367 test111 #linux *!*@*.Denver1.Level3.net PhadThao!moput@ppp97.arobas.net 978025923 -:splitrock.tx.us.dal.net 367 test111 #linux *!*@66.38.186.* PhadThao!moput@ppp97.arobas.net 978025908 -:splitrock.tx.us.dal.net 367 test111 #linux *!*@*.ath.bellsouth.net PhadThao!moput@ppp97.arobas.net 978025888 -:splitrock.tx.us.dal.net 367 test111 #linux *!*@*.dublin.iol.ie PhadThao!moput@ppp97.arobas.net 978025844 -:splitrock.tx.us.dal.net 367 test111 #linux *!*@*.cgocable.net ChanServ!service@dal.net 978025319 -:splitrock.tx.us.dal.net 367 test111 #linux *!*y0@*.macam98.ac.il Psi-Jack!psi-jack@pool-63.52.169.28.dlls.grid.net 978025229 -:splitrock.tx.us.dal.net 368 test111 #linux :End of Channel Ban List - - - - - -Events to process: - -: NOTICE AUTH :*** Got Ident % -user me me me -nick - -: 001 :% -: MODE :+i -we can then perform nickserv identification - - -.....identify... -then we can join our channels - - -: 353 = : etc -add to userlist for that channel if not already existing - -:!@ JOIN : -add user to channel userlist - -:!@ PART : -del user from userlist - -:!@ QUIT :% -del user from all channels - -:ChanServ!service@dal.net KICK #linux LoLos17 :User has been banned -:!@ KICK :% -del user from userlist - - - - -:!@ MODE [] -update user's mode in memory, or channel mode -act if the action was performed on ourselves -MODE #unices +o :LrdBritish -MODE #unices +p -MODE #unices +b : - -: MODE -update our own user mode -to set my own user modes: mode -to know our current mode: mode - -also watch out for +oooo modes - - - - -:!@ NICK :% -update to <%> in all channels - -:!@ PRIVMSG :% -consider % as a normal message sent to - -:!@ PRIVMSG :% -consider % as a private message sent to us - -:!@ NOTICE :% -consider % as a notice sent to the channel - -:!@ NOTICE :% -consider % as a private notice sent to us - -:!@ PRIVMSG :^APING %^A -NOTICE :^APING <%>^A - -:!@ PRIVMSG :^AVERSION^A -NOTICE :^A^A - -:!@ INVITE : -consider an invite and act - -PING : -respond with PONG : - -ERROR :Closing Link: % -reconnect - - - - -TODO: - -I need a good pattern matching function, also make sure to use a pattern - that does not match any valid IRC character - -I need a token expander function -Setup standard tokens eg: - &n our nickname - diff --git a/mmsoftware/bot/newbot.c b/mmsoftware/bot/newbot.c deleted file mode 100644 index 78e9fc5..0000000 --- a/mmsoftware/bot/newbot.c +++ /dev/null @@ -1,1259 +0,0 @@ -/* $Id: newbot.c,v 1.10 2004/07/24 17:48:21 mmondor Exp $ */ - -/* - * Copyright (C) 2002-2003, Matthew Mondor - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software written by Matthew Mondor. - * 4. The name of Matthew Mondor may not be used to endorse or promote - * products derived from this software without specific prior written - * permission. - * - * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -/* XXX This is incomplete software. It will probably be converted to a - * real IRC client with support for additionnal programming through dynamic - * modules. It is possible that the ncurses and/or frontend(s) also be - * implemented as modules. - * - * TODO: - * - Make a new library out of the timing functions developped in mmSNGId - * and use them in this program. - * - ... alot of other stuff ... - */ - - - -/* HEADERS */ - -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - - - - -MMCOPYRIGHT("@(#) Copyright (c) 2002-2003\n\ -\tMatthew Mondor. All rights reserved.\n"); -MMRCSID("$Id: newbot.c,v 1.10 2004/07/24 17:48:21 mmondor Exp $"); - - - - -/* DEFINITIONS */ - -/* The following tables were generated automatically using mode_table.c from - * the same author. - */ -/* Channel modes */ -enum chanmode_enum { - CMOD_U = 0, - CMOD_c, - CMOD_i, - CMOD_k, /* Requires string parameter */ - CMOD_l, /* Requires integer parameter */ - CMOD_m, - CMOD_n, - CMOD_p, - CMOD_r, - CMOD_s, - CMOD_t, - CMOD_O, - CMOD_R -}; - -/* User modes */ -enum usermode_enum { - UMOD_U = 0, - UMOD_a, - UMOD_g, - UMOD_i, - UMOD_k, - UMOD_n, - UMOD_o, - UMOD_s, - UMOD_w -}; - -/* Channel user modes */ -enum cusermode_enum { - CUMOD_U = 0, - CUMOD_v, - CUMOD_o, -}; - - -struct network { - pnode_t node; - pool_t servers_pool; - pool_t channels_pool; - hashtable_t channels_table; - list_t servers_list; - fdbuf *fdb; /* NULL if not connected */ - /* When running through servers to connect, and connected server. */ - struct server *server; - char nick[32]; /* Our nickname on this network */ - u_int32_t mode; /* Our user mode on this network */ - struct freqtable freq_nick, freq_ident, freq_host; -}; - -struct server { - pnode_t node; - char name[128]; -}; - -struct channel { - hashnode_t node; - pool_t users_pool; - hashtable_t users_table; - bool joined; - char name[64]; - u_int32_t mode; - int window; - /* Channel l and k mode parameters */ - int mode_limit; - char mode_key[32]; -}; - -struct user { - hashnode_t node; - char name[64]; - u_int32_t mode; -}; - -struct freqtable { - pool_t pool; - hashtable_t table; -}; - -struct freq { - hashtable_t node; - char key[64]; - time_t expires; - bool ignore; - unsigned long freq; -}; - -/* An entry in the delayed output queue XXX to replace */ -struct cron { - pnode_t node; - time_t expires; - char data[512]; -}; - -/* This structure is used so that an irc line can be parsed once, for - * various conditionals that may later on take place on the elements. - * This structure is shared among all states and functions. - */ -struct info { - fdbuf *fdb; /* Socket */ - struct state *state; /* Current state */ - int code; /* Command code or -1 for text command */ - int params; /* Number of extra parameters or 0 */ - char line[512]; /* Actual IRC line we received */ - char work[512]; /* Copy of line, split with \0s */ - /* When appropriate, these will be set, pointing to string elements into - * work[]. - */ - char *server; - char *nick, *ident, *host, *command; - char *param[10]; - char *text; - /* Add other global data here if required */ - char nickname[64]; /* Our own nick */ - time_t zen_last; - time_t ping_last, ver_last; -}; - -/* Defines a state handler */ -struct handler_func { - int handler_code; /* Mutually exclusive with command */ - char *handler_command; - void (*handler_func)(struct info *); -}; - -/* Defines a state */ -struct state { - void (*state_init)(struct info *); - void (*state_exit)(struct info *); - struct handler_func *state_handlers; -}; - -/* Defined states */ -#define STATE_IDENT 0 -#define STATE_MAIN 1 - -/* Configurable options */ -#define INPUT_TIMEOUT 300 -#define FLOOD_HOST_MAX 20 -#define FLOOD_HOST_TIME 60 -#define FLOOD_HOST_IGNORE 300 -#define FLOOD_USER_MAX 10 -#define FLOOD_USER_TIME 60 -#define FLOOD_USER_IGNORE 120 -#define IGN_NOTIFY 1 -#define REJOIN_DELAY 30 -#define ALLOW_PING 1 -#define PING_RATE 10 -#define ALLOW_VER 1 -#define VER_RATE 10 -#define VER_STRING "$Id: newbot.c,v 1.10 2004/07/24 17:48:21 mmondor Exp $" -#define ANSWER_RATE 120 /* For zenbot replies frequency */ -#define INITIAL_UMODE "+iw" - -/*#define DEBUG 1*/ - - - - -/* PROTOTYPES */ - -int main(int, char **); -static void main_loop(struct info *); -static int allow_entry(pool_t *, list_t *, char *, unsigned long, int, long); -static bool irc_match(char *, char *); -static void irc_parse(struct info *); -static void user_add(char *, char *); -static struct channel *user_del(char *, char *); -static void cron_add(int, char *, ...); - -static void ignore_user(struct info *); -static void ignore_host(struct info *); -static void ident_init(struct info *); -static void ident_main(struct info *); -static void main_init(struct info *); -static void main_messaged(struct info *); -static void main_noticed(struct info *); -static void main_joined(struct info *); -static void main_kicked(struct info *); -static void main_parted(struct info *); -static void main_quitted(struct info *); -static void main_nick(struct info *); -static void main_mode(struct info *); -static void main_invited(struct info *); -static void main_names(struct info *); - -static void zen_init(void); -static char *zen(void); - -static bool eintr(void); - - - -/* GLOBALS */ - -/* We keep a list of networks. For each network we also keep a list of servers, - * and of channels we should be joining along with the window the channel - * should be connected on. When we notice that we are not connected anymore to - * a particular network, we reconnect using any decent server. When we notice - * that we are not joined to a channel of a network we should have joined, we - * attempt to join the channel. Of course, we reconnect to servers and join - * to channels respecting a reasonable delay. Every channel has it's own - * pool_t and hashtable_t of users. - */ -pool_t networks_pool; -list_t networks_list; -pool_t servers_pool; -list_t servers_list; -pool_t channels_pool; -hashtable_t channels_table; - -/* Various slab-buffered linked lists */ -static pool_t plusers, plhosts, plcron, plchans; -static list_t lusers, lhosts, lcron, lchans; - -/* The following tables were generated automatically using mode_table.c from - * the same author. They are used for efficieny to prevent any iteration when - * mapping modes, indexing using tables is much faster. - */ -/* To map chanmode enums to characters and vice-versa */ -static const char chanmode_enum_to_char[] = "UciklmnprstOR"; -static const int chanmode_char_to_enum[256] = { - 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, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11, - 0, 0, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, 0, 2, 0, 3, 4, 5, 6, 0, - 7, 0, 8, 9, 10, 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, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 -}; - -/* To map usermode enums to characters and vice-versa */ -static const char usermode_enum_to_char[] = "Uagiknosw"; -static const int usermode_char_to_enum[256] = { - 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, 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, 1, 0, 0, 0, 0, 0, 2, 0, 3, 0, 4, 0, 0, 5, 6, - 0, 0, 0, 7, 0, 0, 0, 8, 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, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 -}; - -/* To map cusermode enums to characters and vice-versa */ -static const char cusermode_enum_to_char[] = "Uvo"; -static const int cusermode_char_to_enum[256] = { - 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, 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, 2, - 0, 0, 0, 0, 0, 0, 1, 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, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 -}; - -/* Here consists of the states definition table. Each state has a series - * of handler functions, which will be called when a particular pattern - * matches (with * and ? wildcards). These functions can optionally request - * to change state by returning another state * than the one it was provided. - */ -static struct handler_func state_ident_handlers[] = { - {376, NULL, ident_main}, /* End of MOTD */ - {422, NULL, ident_main}, /* No MOTD */ - {0, NULL, NULL} -}; -static struct handler_func state_main_handlers[] = { - {0, "PRIVMSG", main_messaged}, - {0, "NOTICE", main_noticed}, - {0, "KICK", main_kicked}, - {0, "PART", main_parted}, - {0, "JOIN", main_joined}, - {0, "QUIT", main_quitted}, - {0, "NICK", main_nick}, - {0, "MODE", main_mode}, - {0, "INVITE", main_invited}, - {353, NULL, main_names}, - {0, NULL, NULL} -}; - -/* The definitions of the states, their optional init and exit code, and - * a pointer to their various function handlers. - */ -static struct state states[] = { - {ident_init, NULL, state_ident_handlers}, - {main_init, NULL, state_main_handlers}, - {NULL, NULL, NULL} -}; - -/* Channels we should join and make sure to remain in */ -static char *channels[] = { - "#c", - NULL -}; - -/* Zen koans */ -static int zen_num; /* set by zen_init() */ -static int zen_offset = 0; /* Current quote offset */ -static char *zen_strings[] = { - "The cypress tree in the yard.", - "Many moons ago.", - "Eat your bowl.", - "A bum on the hand, is better than two in the bush.", - "The definition of the states.", - "It is.", - "Yes.", - "No.", - "Long sessions.", - "Have you finished eating? Then wash your bowl.", - "The butterfly is silent when the eagle walks upon the sand.", - "Tantamount to painting your leg red and walking the dog.", - "Carrots.", - "A guiding light at the bottom of the firepit.", - "A sinking boat picking up people in the middle of the sea.", - "You have a wooden leg, does that make you a table?", - "It puts the lotion on its skin or else it gets the hose again.", - "Watch what cooks in the oven.", - "When he breathes short, he knoes that he is breathing short, when he breathes deeply he also knows that he does.", - "What is the sound of one hand clapping?", - "What is your original face before your parents were born?", - "When the many are reduced to one, what is the one reduced to?", - "What is mu?", - "Bring out your mind here before me, and I will pacify it.", - "There! I have pacified your mind.", - "Here is a tall bamboo; there is a short one.", - "Three pound of flax!", - "Matchbox is a noise. Is this a noise?", - "Every natural fact is a symbol of some spiritual fact.", - "The highest human purpose is always to reinvent and celebrate the sacred.", - "People who take the time to be alone usually have depth, and quiet reserve.", - "Check out http://google.com, it is a very good site.", - "We think in generalities, but we live in details.", - "The oak tree in the garden.", - "Insects, grass and trees you must not hurt.", - "Cultivate the garden within.", - "This cabbage, these carrots, these potatoes, these onions ... will soon become me. Such a tasty fact!", - "Learning how to operate a soul figures to take time.", - "There's nothing much, really, to say.", - "The Tao exists in the crickets... in the grasses... in tiles and bricks... and in shit and piss.", - "A garden is a private world or it is nothing.", - "One real world is enough.", - "Right here, right now.", - "Mu!", - "Before your parents gave birth to you.", - "Neither standing nor sitting will do, now what will you do?", - "What do you do?", - "Not relying on words or letters.", - "The dinner is never complete without some meat in your seat.", - "A clique that seeks power usually through intrigue.", - "NULL.", - "void *(void *)(void *).", - NULL -}; - - - -/* FUNCTIONS */ - -static void ignore_user(struct info *data) -{ - if (IGN_NOTIFY) { - fdbprintf(data->fdb, - "PRIVMSG %s :Ignoring your ident for %d seconds.\r\n", - data->nick, FLOOD_USER_IGNORE); - /* fdbprintf(data->fdb, "WHOIS :%s\r\n", data->nick); */ - } -} - -static void ignore_host(struct info *data) -{ - if (IGN_NOTIFY) { - fdbprintf(data->fdb, - "PRIVMSG %s :Ignoring your hostname for %d seconds.\r\n", - data->nick, FLOOD_HOST_IGNORE); - /* fdbprintf(data->fdb, "WHOIS :%s\r\n", data->nick); */ - } -} - - -static void ident_init(struct info *data) -{ - data->zen_last = time(NULL); - fdbprintf(data->fdb, "USER %s %s %s %s\r\n", data->nickname, - data->nickname, data->nickname, data->nickname); - fdbprintf(data->fdb, "NICK %s\r\n", data->nickname); -} - -static void ident_main(struct info *data) -{ - /* Simply switch to the main state */ - data->state = &states[STATE_MAIN]; -} - - -static void main_init(struct info *data) -{ - int i; - - /* Set our initial umode */ - fdbprintf(data->fdb, "MODE %s :%s\r\n", data->nickname, INITIAL_UMODE); - - /* Join channels */ - for (i = 0; channels[i]; i++) - fdbprintf(data->fdb, "JOIN :%s\r\n", channels[i]); -} - -static void main_messaged(struct info *data) -{ - char *str; - time_t t = time(NULL); - int r; - - /* Handle PING and VERSION CTCP requests */ - if (ALLOW_PING && !mm_strncmp(data->text, "\001PING ", 6)) { - long tim; - if (t - data->ping_last > PING_RATE) { - tim = atol(&data->text[6]); - fdbprintf(data->fdb, "NOTICE %s :\001PING %ld\001\r\n", - data->nick, tim); - data->ping_last = t; - } - return; - } - if (ALLOW_VER && !mm_strcmp(data->text, "\001VERSION\001")) { - if (t - data->ver_last > VER_RATE) { - fdbprintf(data->fdb, "NOTICE %s :\001VERSION %s\001\r\n", - data->nick, VER_STRING); - data->ver_last = t; - } - return; - } - - /* Reply to user via /msg or on channel depending on origin. - * Also make sure to not answer too frequently. - */ - if (t - data->zen_last > ANSWER_RATE) { - if (irc_match(data->text, "*why *") || - irc_match(data->text, "*how *") || - irc_match(data->text, "*who *") || - irc_match(data->text, "*what *") || - irc_match(data->text, "*where *") || - irc_match(data->text, "*when *")) { - data->zen_last = t; - str = zen(); - /* Let's simulate a human delay (2 - 10 secs) */ - r = 2 + (rand() % 8); - if (!mm_strcmp(data->param[0], data->nickname)) - cron_add(r, "PRIVMSG %s :%s\r\n", data->nick, str); - else - cron_add(r, "PRIVMSG %s :%s: %s\r\n", data->param[0], - data->nick, str); - } - } -} - -static void main_noticed(struct info *data) -{ - /* XXX */ -} - -static void main_joined(struct info *data) -{ - struct channel *ch; - - /* data->text consists of channel name, data->nick of nickname */ - if (!mm_strcmp(data->nick, data->nickname)) { - /* We have joined a new channel, create new channel node with it's - * users list - */ - if ((ch = (struct channel *)pool_alloc(&plchans, TRUE))) { - if (pool_init(&ch->pchannel_users, "pchannel_users", - malloc, free, NULL, NULL, sizeof(struct user), - 4096 / sizeof(struct user), 0, 0)) { - DLIST_INIT(&ch->channel_users); - ch->channel_hash = mm_strhash64(data->text); - mm_strcpy(ch->channel_name, data->text); - DLIST_APPEND(&lchans, (node_t *)ch); - } - } - } else { - /* Another user joined one of the channels we are in, update - * corresponding channel's userlist - */ - user_add(data->text, data->nick); - } -} - -static void main_kicked(struct info *data) -{ - struct channel *ch; - - /* data->param[0] consists of channel name, data->param[1] of nick */ - ch = user_del(data->param[0], data->param[1]); - if (!mm_strcmp(data->param[1], data->nickname)) { - /* We have been kicked out of the channel, free all channel - * info and send a delayed join request - */ - DLIST_UNLINK(&lchans, (node_t *)ch); - pool_destroy(&ch->pchannel_users); - pool_free((pnode_t *)ch); - cron_add(REJOIN_DELAY, "JOIN :%s\r\n", data->param[0]); - } -} - -static void main_parted(struct info *data) -{ - struct channel *ch; - - /* data->nick consists of nickname, data->param[0] of channel name */ - ch = user_del(data->param[0], data->nick); - if (!mm_strcmp(data->nick, data->nickname)) { - /* We have left a channel, free all channel info */ - DLIST_UNLINK(&lchans, (node_t *)ch); - pool_destroy(&ch->pchannel_users); - pool_free((pnode_t *)ch); - } -} - -static void main_quitted(struct info *data) -{ - struct channel *ch, *tmp; - - user_del(NULL, data->nick); - if (!mm_strcmp(data->nick, data->nickname)) { - /* We have quitted, free all channel info */ - ch = (struct channel *)lchans.top; - while (ch) { - tmp = (struct channel *)ch->node.node.next; - DLIST_UNLINK(&lchans, (node_t *)ch); - pool_free((pnode_t *)ch); - ch = tmp; - } - } -} - -static void main_nick(struct info *data) -{ - u_int64_t hash, nhash; - struct channel *ch; - struct user *us; - - /* A user changed nickname, make sure to update it in all our channels - * the user is in. If we are the one who changed nick, also update our - * internal own nickname record. - */ - hash = mm_strhash64(data->nick); - nhash = mm_strhash64(data->text); - - ch = (struct channel *)lchans.top; - while (ch) { - us = (struct user *)ch->channel_users.top; - while (us) { - if (us->user_hash == hash) - break; - us = (struct user *)us->node.node.next; - } - if (us) { - us->user_hash = nhash; - mm_strcpy(us->user_nick, data->text); - } - ch = (struct channel *)ch->node.node.next; - } - /* - if (!mm_strcmp(data->nick, data->nickname)) { - mm_strncpy(data->nickname, data->text, 63); - XXX Update hash also? - } - */ -} - -static void main_mode(struct info *data) -{ - /* A mode change occured for either ourself, a channel or another user. - * we only record those for now, but action could be taken also on certain - * mode change events. XXX - */ - /* :PhadThai!mmondor@gobot.xisop MODE #netbsd-devel +o nanobit - * :nanobit MODE nanobit :+iw - */ - if (data->ident && data->param[0] && *data->param[0] == '#') { - /* cmode or cumode change */ - } else { - /* umode change */ -#ifdef DEBUG - printf("UMODE\n"); -#endif - } -} - -static void main_invited(struct info *data) -{ - /* XXX */ -} - -static void main_names(struct info *data) -{ - char *words[64]; /* XXX check how many max */ - int cols, i; - - /* data->param[2] consists of channel name, and data->text of nicks */ - if ((cols = mm_straspl(words, data->text, 63)) > 0) { - for (i = 0; i < cols; i++) - user_add(data->param[2], words[i]); - } -} - - -/* The main startup function */ -int main(int argc, char **argv) -{ - int port, fd; - char *server; - struct sockaddr_in client; - struct in_addr iaddr; - struct info data; - - fdfuncs fdf = { - malloc, - free, - poll, - read, - write, - sleep, - usleep, - NULL, - NULL, - NULL, - NULL, - NULL, - eintr - }; - - if (argc != 3) { - printf("Usage: zenbot \n"); - exit (-1); - } - - mm_memclr(&data, sizeof(data)); - - /* Parse arguments */ - server = argv[2]; - mm_strncpy(data.nickname, argv[1], 63); - port = 6667; - - /* Zen setup */ - zen_init(); - - if (pool_init(&plusers, "plusers", malloc, free, NULL, NULL, - sizeof(struct freq), 8192 / sizeof(struct freq), 0, 0)) { - DLIST_INIT(&lusers); - if (pool_init(&plhosts, "plhosts", malloc, free, NULL, NULL, - sizeof(struct freq), 8192 / sizeof(struct freq), 0, 0)) { - DLIST_INIT(&lhosts); - if (pool_init(&plcron, "plcron", malloc, free, NULL, NULL, - sizeof(struct cron), 8192 / sizeof(struct cron), - 0, 0)) { - DLIST_INIT(&lcron); - if (pool_init(&plchans, "plchans", malloc, free, NULL, NULL, - sizeof(struct channel), - 8192 / sizeof(struct channel), 0, 0)) { - DLIST_INIT(&lchans); - /* Prepare our fd buffering wrapper */ - if ((data.fdb = fdbopen(&fdf, NULL, -1, 4096, 4096, 1024, - 1024, INPUT_TIMEOUT * 1000, - INPUT_TIMEOUT * 1000, FALSE))) { - /* Loop indefinitely */ - for (;;) { - /* Connect to server */ - if ((fd = socket(AF_INET, SOCK_STREAM, 0)) != -1) { - mm_memclr(&client, sizeof(struct sockaddr_in)); - if ((inet_aton(server, &iaddr))) { - client.sin_family = AF_INET; - client.sin_addr.s_addr = iaddr.s_addr; - client.sin_port = htons(port); - if ((connect(fd, - (struct sockaddr *)&client, - sizeof(struct sockaddr_in))) - != -1) { - fcntl(fd, F_SETFL, O_NONBLOCK); - fdbparam_set(data.fdb, fd, FDBP_FD); - /* Serve our purpose */ - data.state = &states[STATE_IDENT]; - main_loop(&data); - fdbflushr(data.fdb); - fdbflushw(data.fdb); - fdbparam_set(data.fdb, -1, FDBP_FD); - } else - sleep(30); - } - close(fd); - } - } - fdbclose(data.fdb); - } - pool_destroy(&plchans); - } - pool_destroy(&plcron); - } - pool_destroy(&plhosts); - } - pool_destroy(&plusers); - } - - exit(0); -} - - -/* This function consists of the main state switcher loop, and basically - * reads lines, calling the matching function handlers for the current - * state. When a state switch occurs, exit code of the current state is - * executed, and init code of the new state, if any. - * We only return if a handler function returns a NULL pointer, or if - * we loose connection with the server. - * We internally take care of PING replies. - */ -static void main_loop(struct info *data) -{ - struct state *curstate; - struct handler_func *fn; - int i; - - if (data->state->state_init) - data->state->state_init(data); - - for (;;) { - struct cron *nod, *tmp; - time_t t; - - /* Verify if any queued data has expired and should be released out. - * unfortunately because of the way we currently work, some IRC - * activity is required to trigger this. XXX Ideally the fdbgets() - * timeout should be set to 1 when there is queued data, and timeout - * should not be assumed to be a server/connection failiure. For now, - * this should work fine on a channel with activity, or if we are - * on multiple channels. - */ - t = time(NULL); - nod = (struct cron *)lcron.top; - while (nod) { - tmp = (struct cron *)nod->node.node.next; - if (nod->expires < t) { - DLIST_UNLINK(&lcron, (node_t *)nod); - fdbputs(data->fdb, nod->data); - pool_free((pnode_t *)nod); - } - nod = tmp; - } - - fdbflushw(data->fdb); - - if ((i = fdbgets(data->fdb, data->line, 511, FALSE)) > -1) { - - /* We got a line, check if there exists any handler to serve - * it within current state. Execute the handler if required, - * and perform sane state switching if requested by a handler. - */ - - if (!mm_strncmp(data->line, "PING :", 6)) { - /* Transform PING to PONG and reply to server */ - data->line[1] = 'O'; - fdbprintf(data->fdb, "%s\r\n", data->line); - continue; - } - - irc_parse(data); - - /* Useful for debugging */ -#ifdef DEBUG - fwrite("\033[1m", 4, 1, stdout); - fwrite(data->line, i, 1, stdout); - fwrite("\r\n", 2, 1, stdout); - fwrite("\033[0m", 4, 1, stdout); - fflush(stdout); - printf("code: %d server: '%s' nick: '%s' ident: '%s' host: '%s' command: '%s' text: '%s' params: %d ", - data->code, data->server, data->nick, data->ident, - data->host, data->command, data->text, data->params); - for (i = 0; i < data->params; i++) - printf("param[%d]: '%s' ", i, data->param[i]); - printf("\r\n\r\n"); - fflush(stdout); -#endif - - if (data->code == -1 && data->ident) { - int res; - bool allow = FALSE; - - /* Check against flood by ident and hostname */ - if (!(res = allow_entry(&plhosts, &lhosts, data->host, - FLOOD_HOST_MAX, FLOOD_HOST_TIME, - FLOOD_HOST_IGNORE))) { - if (!(res = allow_entry(&plusers, &lusers, data->ident, - FLOOD_USER_MAX, FLOOD_USER_TIME, - FLOOD_USER_IGNORE))) - allow = TRUE; - else if (res == 2) - ignore_user(data); - } else if (res == 2) - ignore_host(data); - if (!allow) - continue; - } - - curstate = data->state; - fn = curstate->state_handlers; - while (fn->handler_func) { - bool allow; - - allow = FALSE; - if (fn->handler_code && data->code != -1) { - if (fn->handler_code == data->code) - allow = TRUE; - } else if (fn->handler_command && data->command) { - if (!mm_strcmp(fn->handler_command, data->command)) - allow = TRUE; - } - if (allow) { - /* Execute handler for matching pattern */ - fn->handler_func(data); - if (data->state != curstate) { - /* Perform state switching */ - if (curstate->state_exit) - curstate->state_exit(data); - if (data->state->state_init) - data->state->state_init(data); - } - break; - } - fn++; - } - - } else - break; - } -} - - -/* This function is useful to easily perform flood quota checking on nicks - * and/or hosts. Returns 0 if the entry is allowed, 1 if it is not, 2 if - * it just started to be ignored. - */ -static int -allow_entry(pool_t *pool, list_t *lst, char *entry, unsigned long max, - int secs, long igndelay) -{ - u_int64_t hash = mm_strhash64(entry); - int ret = 0; - struct freq *node, *tmp; - time_t t = time(NULL); - - /* Locate if entry exists */ - node = (struct freq *)lst->top; - while (node) { - tmp = (struct freq *)node->node.node.next; - if (node->expires < t) { - /* Expired, drop this entry */ - DLIST_UNLINK(lst, (node_t *)node); - pool_free((pnode_t *)node); - } else if (node->hash == hash) - break; - node = tmp; - } - if (node) { - /* Entry existed, increase frequency and optionally toggle ignore */ - node->freq++; - if (!node->ignore) { - if (node->freq > max) { - node->ignore = TRUE; - node->expires += igndelay; - ret = 2; - } - } else - ret = 1; - } else { - /* Add new entry */ - if ((node = (struct freq *)pool_alloc(pool, FALSE))) { - node->expires = t + secs; - node->ignore = FALSE; - node->hash = hash; - node->freq = 1; - DLIST_APPEND(lst, (node_t *)node); - } - } - - return (ret); -} - - -/* This function returns weither or not pat matches string. - * We only perform simple * and ? pattern matching, case-sensitive. - */ -static bool irc_match(char *str, char *pat) -{ - while (*pat ^ '*') { - if (!*str) { - if (*pat) - return (FALSE); - else - return (TRUE); - } - if (*str ^ *pat && *pat ^ '?') - return (FALSE); - pat++; - str++; - } - - while (pat[1] == '*') - pat++; - - do { - if (irc_match(str, pat + 1)) - return (TRUE); - } while (*str++); - - return (FALSE); -} - - -/* Some mmondor-style string parsing magic, fill data fields according to - * IRC protocol line - */ -static void irc_parse(struct info *data) -{ - char *ptr, *optr, *words[16]; - size_t cols; - int i, i2; - - /* Init, everything set to NULL, and code to -1 by default */ - mm_strcpy(data->work, data->line); - ptr = data->work; - data->code = -1; - data->params = 0; - data->server = data->nick = data->ident = data->host = data->command = - data->text = NULL; - - if (*ptr == ':') { - /* IRC lines start with ':' */ - ptr++; - optr = ptr; - while (*ptr != '\0' && *ptr != ':') - ptr++; - if (*ptr == ':') { - /* Multiword text field */ - *ptr++ = '\0'; - data->text = ptr; - } - if ((cols = mm_straspl(words, optr, 16)) > 1) { - /* Determine the type of IRC protocol line and fill corresponding - * pointers. - */ - if ((i = atoi(words[1]))) { - /* Code based line */ - data->server = *words; - data->code = i; - } else { - /* Text command based line */ - ptr = *words; - while (*ptr != '\0' && *ptr != '.' && *ptr != '!') - ptr++; - if (*ptr == '.') - /* Server name in first column */ - data->server = *words; - else { - /* Nickname in first column, optional ident and host */ - ptr = data->nick = *words; - while (*ptr != '\0' && *ptr != '!') - ptr++; - if (*ptr == '!') { - *ptr++ = '\0'; - data->ident = ptr; - while (*ptr != '\0' && *ptr != '@') - ptr++; - if (*ptr == '@') { - *ptr++ = '\0'; - data->host = ptr; - } - } - } - data->command = words[1]; - } - if (cols > 2) { - /* Additional parameters before text */ - i = 0; - for (i2 = 2; i2 < cols; i2++) - data->param[i++] = words[i2]; - data->params = i; - } - } - } -} - - -/* Adds a user to a channel, also checks for @ and + in start of nick to - * set according mode. - */ -static void user_add(char *chan, char *nick) -{ - u_int64_t chanhash, nickhash; - struct channel *ch; - struct user *us; - int mode; - - mm_strlower(chan); - chanhash = mm_strhash64(chan); - - mode = 0; - if (*nick) { - if (*nick == '@') { - mode |= (1L << 0); - nick++; - } else if (*nick == '+') { - mode |= (1L << 1); - nick++; - } - } - nickhash = mm_strhash64(nick); - - /* Find channel user should be added to */ - ch = (struct channel *)lchans.top; - while (ch) { - if (ch->channel_hash == chanhash) - break; - ch = (struct channel *)ch->node.node.next; - } - if (ch) { - /* We found channel */ - if ((us = (struct user *)pool_alloc(&ch->pchannel_users, FALSE))) { - mm_strcpy(us->user_nick, nick); - us->user_hash = nickhash; - us->user_mode = mode; -#ifdef DEBUG - printf("ADDED USER '%s' TO CHANNEL '%s'\n", nick, chan); -#endif - DLIST_APPEND(&ch->channel_users, (node_t *)us); - } - } -} - - -/* Deletes a nick from specified channel, or from all channels if chan is NULL - */ -static struct channel *user_del(char *chan, char *nick) -{ - u_int64_t chanhash, nickhash; - struct channel *ch; - struct user *us; - - nickhash = mm_strhash64(nick); - ch = NULL; - - if (chan) { - /* Delete user on specified channel */ - mm_strlower(chan); - chanhash = mm_strhash64(chan); - ch = (struct channel *)lchans.top; - while (ch) { - if (ch->channel_hash == chanhash) - break; - ch = (struct channel *)ch->node.node.next; - } - if (ch) { - /* We found channel */ - us = (struct user *)ch->channel_users.top; - while (us) { - if (us->user_hash == nickhash) - break; - us = (struct user *)us->node.node.next; - } - if (us) { - /* We found nick */ - DLIST_UNLINK(&ch->channel_users, (node_t *)us); - pool_free((pnode_t *)us); -#ifdef DEBUG - printf("DELETED USER '%s' FROM CHANNEL '%s'\n", nick, - chan); -#endif - } - } - } else { - /* Delete user on all channels */ - ch = (struct channel *)lchans.top; - while (ch) { - us = (struct user *)ch->channel_users.top; - while (us) { - if (us->user_hash == nickhash) { - DLIST_UNLINK(&ch->channel_users, (node_t *)us); - pool_free((pnode_t *)us); - break; - } - us = (struct user *)us->node.node.next; - } - ch = (struct channel *)ch->node.node.next; - } -#ifdef DEBUG - printf("DELETED USER '%s' FROM ALL CHANNELS\n", nick); -#endif - } - - return (ch); -} - - -/* Allows to queue data to be sent after a certain delay has been observed. */ -static void cron_add(int secs, char *fmt, ...) -{ - struct cron *nod; - time_t t = time(NULL); - va_list arg_ptr; - - if ((nod = (struct cron *)pool_alloc(&plcron, FALSE))) { - nod->expires = t + secs; - va_start(arg_ptr, fmt); - vsnprintf(nod->data, 1023, fmt, arg_ptr); - va_end(arg_ptr); - DLIST_APPEND(&lcron, (node_t *)nod); - } -} - - - -static void zen_init(void) -{ - int num; - - for (num = 0; zen_strings[num]; num++); - zen_num = num; -} - -static char *zen(void) -{ - /*return( zen_strings[rand() % zen_num] ); */ - if (zen_offset > zen_num) - zen_offset = 0; - return (zen_strings[zen_offset++]); -} - - -static bool eintr(void) -{ - if (errno == EINTR) - return TRUE; - - return FALSE; -} diff --git a/mmsoftware/bot/zenbot.c b/mmsoftware/bot/zenbot.c deleted file mode 100644 index a71ecd2..0000000 --- a/mmsoftware/bot/zenbot.c +++ /dev/null @@ -1,1106 +0,0 @@ -/* $Id: zenbot.c,v 1.12 2004/10/22 14:28:26 mmondor Exp $ */ - -/* - * Copyright (C) 2002-2003, Matthew Mondor - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software written by Matthew Mondor. - * 4. The name of Matthew Mondor may not be used to endorse or promote - * products derived from this software without specific prior written - * permission. - * - * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - - - - -/* HEADERS */ - -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - - - - -MMCOPYRIGHT("@(#) Copyright (c) 2002-2003\n\ -\tMatthew Mondor. All rights reserved.\n"); -MMRCSID("$Id: zenbot.c,v 1.12 2004/10/22 14:28:26 mmondor Exp $"); - - - - -/* DEFINITIONS */ - -/* Defines a flood protection entry in a hash table */ -struct freq { - pnode_t node; - time_t expires; - bool ignore; - u_int64_t hash; - unsigned long freq; -}; - -/* An entry in the delayed output queue */ -struct cron { - pnode_t node; - time_t expires; - char data[512]; -}; - -/* A channel we joined */ -struct channel { - pnode_t node; - u_int64_t channel_hash; - pool_t pchannel_users; - list_t channel_users; - int channel_limit, channel_mode; - char channel_name[64], channel_key[16]; -}; - -/* A user on a channel */ -struct user { - pnode_t node; - u_int64_t user_hash; - int user_mode; - char user_nick[64]; -}; - -/* This structure is used so that an irc line can be parsed once, for - * various conditionals that may later on take place on the elements. - * This structure is shared among all states and functions. - */ -struct info { - fdbuf *fdb; /* Socket */ - struct state *state; /* Current state */ - int code; /* Command code or -1 for text command */ - int params; /* Number of extra parameters or 0 */ - char line[512]; /* Actual IRC line we received */ - char work[512]; /* Copy of line, split with \0s */ - /* When appropriate, these will be set */ - char *server; - char *nick, *ident, *host, *command; - char *param[10]; - char *text; - /* Add other global data here if required */ - char nickname[64]; /* Our own nick */ - time_t zen_last; - time_t ping_last, ver_last; -}; - -/* Defines a state handler */ -struct handler_func { - int handler_code; /* Mutually exclusive with command */ - char *handler_command; - void (*handler_func)(struct info *); -}; - -/* Defines a state */ -struct state { - void (*state_init)(struct info *); - void (*state_exit)(struct info *); - struct handler_func *state_handlers; -}; - -/* Defined states */ -#define STATE_IDENT 0 -#define STATE_MAIN 1 - -/* Configurable options */ -#define INPUT_TIMEOUT 300 -#define FLOOD_HOST_MAX 20 -#define FLOOD_HOST_TIME 60 -#define FLOOD_HOST_IGNORE 300 -#define FLOOD_USER_MAX 10 -#define FLOOD_USER_TIME 60 -#define FLOOD_USER_IGNORE 120 -#define IGN_NOTIFY 1 -#define REJOIN_DELAY 30 -#define ALLOW_PING 1 -#define PING_RATE 10 -#define ALLOW_VER 1 -#define VER_RATE 10 -#define VER_STRING "$Id: zenbot.c,v 1.12 2004/10/22 14:28:26 mmondor Exp $" -#define ANSWER_RATE 120 /* For zenbot replies frequency */ -#define INITIAL_UMODE "+iw" - -/*#define DEBUG 1*/ - - - - -/* PROTOTYPES */ - -int main(int, char **); -static void main_loop(struct info *); -static int allow_entry(pool_t *, list_t *, char *, unsigned long, int, long); -static bool irc_match(char *, char *); -static void irc_parse(struct info *); -static void user_add(char *, char *); -static struct channel *user_del(char *, char *); -static void cron_add(int, char *, ...); - -static void ignore_user(struct info *); -static void ignore_host(struct info *); -static void ident_init(struct info *); -static void ident_main(struct info *); -static void main_init(struct info *); -static void main_messaged(struct info *); -static void main_noticed(struct info *); -static void main_joined(struct info *); -static void main_kicked(struct info *); -static void main_parted(struct info *); -static void main_quitted(struct info *); -static void main_nick(struct info *); -static void main_mode(struct info *); -static void main_invited(struct info *); -static void main_names(struct info *); - -static void zen_init(void); -static char *zen(void); - -static bool eintr(void); - - - -/* GLOBALS */ - -/* Various slab-buffered linked lists */ -static pool_t plusers, plhosts, plcron, plchans; -static list_t lusers, lhosts, lcron, lchans; - -/* To map umode to bit number in umode bitfield */ -/* Modes which can be applied to ourself globally */ -static char *umodes = "agiknosw"; -/* Modes which can be applied to users (and ourself) on a channel */ -static char *cumodes = "ov"; -/* Modes which can be applied on a channel */ -static char *cmodes = "cikmnprstOR"; /* k and l require param */ - -/* Here consists of the states definition table. Each state has a series - * of handler functions, which will be called when a particular pattern - * matches (with * and ? wildcards). These functions can optionally request - * to change state by returning another state * than the one it was provided. - */ -static struct handler_func state_ident_handlers[] = { - {376, NULL, ident_main}, /* End of MOTD */ - {422, NULL, ident_main}, /* No MOTD */ - {0, NULL, NULL} -}; -static struct handler_func state_main_handlers[] = { - {0, "PRIVMSG", main_messaged}, - {0, "NOTICE", main_noticed}, - {0, "KICK", main_kicked}, - {0, "PART", main_parted}, - {0, "JOIN", main_joined}, - {0, "QUIT", main_quitted}, - {0, "NICK", main_nick}, - {0, "MODE", main_mode}, - {0, "INVITE", main_invited}, - {353, NULL, main_names}, - {0, NULL, NULL} -}; -/* The definitions of the states, their optional init and exit code, and - * a pointer to their various function handlers. - */ -static struct state states[] = { - {ident_init, NULL, state_ident_handlers}, - {main_init, NULL, state_main_handlers}, - {NULL, NULL, NULL} -}; - -/* Channels we should join and make sure to remain in */ -static char *channels[] = { - "#c", - NULL -}; - -/* Zen koans */ -static int zen_num; /* set by zen_init() */ -static int zen_offset = 0; /* Current quote offset */ -static char *zen_strings[] = { - "The cypress tree in the yard.", - "Many moons ago.", - "Eat your bowl.", - "A bum on the hand, is better than two in the bush.", - "The definition of the states.", - "It is.", - "Yes.", - "No.", - "Long sessions.", - "The butterfly is silent when the eagle walks upon the sand.", - "Tantamount to painting your leg red and walking the dog.", - "Carrots.", - "A guiding light at the bottom of the firepit.", - "A sinking boat picking up people in the middle of the sea.", - "You have a wooden leg, does that make you a table?", - "It puts the lotion on its skin or else it gets the hose again.", - "Watch what cooks in the oven.", - "When he breathes short, he knows that he is breathing short, when he breathes deeply he also knows that he does.", - "What is the sound of one hand clapping?", - "What is your original face before your parents were born?", - "When the many are reduced to one, what is the one reduced to?", - "What is mu?", - "Bring out your mind here before me, and I will pacify it.", - "There! I have pacified your mind.", - "Here is a tall bamboo; there is a short one.", - "Three pound of flax!", - "Matchbox is a noise. Is this a noise?", - "Every natural fact is a symbol of some spiritual fact.", - "The highest human purpose is always to reinvent and celebrate the sacred.", - "People who take the time to be alone usually have depth, and quiet reserve.", - "Check out http://google.com, it is a very good site.", - "We think in generalities, but we live in details.", - "The oak tree in the garden.", - "Insects, grass and trees you must not hurt.", - "Cultivate the garden within.", - "This cabbage, these carrots, these potatoes, these onions ... will soon become me. Such a tasty fact!", - "Learning how to operate a soul figures to take time.", - "There's nothing much, really, to say.", - "The Tao exists in the crickets... in the grasses... in tiles and bricks... and in shit and piss.", - "A garden is a private world or it is nothing.", - "One real world is enough.", - "Right here, right now.", - "Mu!", - "Before your parents gave birth to you.", - "Neither standing nor sitting will do, now what will you do?", - "What do you do?", - "Not relying on words or letters.", - "The dinner is never complete without some meat in your seat.", - "A clique that seeks power usually through intrigue.", - "NULL.", - "void *(void *)(void *).", - NULL -}; - - - -/* FUNCTIONS */ - -static void ignore_user(struct info *data) -{ - if (IGN_NOTIFY) { - fdbprintf(data->fdb, - "PRIVMSG %s :Ignoring your ident for %d seconds.\r\n", - data->nick, FLOOD_USER_IGNORE); - /* fdbprintf(data->fdb, "WHOIS :%s\r\n", data->nick); */ - } -} - -static void ignore_host(struct info *data) -{ - if (IGN_NOTIFY) { - fdbprintf(data->fdb, - "PRIVMSG %s :Ignoring your hostname for %d seconds.\r\n", - data->nick, FLOOD_HOST_IGNORE); - /* fdbprintf(data->fdb, "WHOIS :%s\r\n", data->nick); */ - } -} - - -static void ident_init(struct info *data) -{ - data->zen_last = time(NULL); - fdbprintf(data->fdb, "USER %s %s %s %s\r\n", data->nickname, - data->nickname, data->nickname, data->nickname); - fdbprintf(data->fdb, "NICK %s\r\n", data->nickname); -} - -static void ident_main(struct info *data) -{ - /* Simply switch to the main state */ - data->state = &states[STATE_MAIN]; -} - - -static void main_init(struct info *data) -{ - int i; - - /* Set our initial umode */ - fdbprintf(data->fdb, "MODE %s :%s\r\n", data->nickname, INITIAL_UMODE); - - /* Join channels */ - for (i = 0; channels[i]; i++) - fdbprintf(data->fdb, "JOIN :%s\r\n", channels[i]); -} - -static void main_messaged(struct info *data) -{ - char *str; - time_t t = time(NULL); - int r; - - /* Handle PING and VERSION CTCP requests */ - if (ALLOW_PING && !mm_strncmp(data->text, "\001PING ", 6)) { - long tim; - if (t - data->ping_last > PING_RATE) { - tim = atol(&data->text[6]); - fdbprintf(data->fdb, "NOTICE %s :\001PING %ld\001\r\n", - data->nick, tim); - data->ping_last = t; - } - return; - } - if (ALLOW_VER && !mm_strcmp(data->text, "\001VERSION\001")) { - if (t - data->ver_last > VER_RATE) { - fdbprintf(data->fdb, "NOTICE %s :\001VERSION %s\001\r\n", - data->nick, VER_STRING); - data->ver_last = t; - } - return; - } - - /* Reply to user via /msg or on channel depending on origin. - * Also make sure to not answer too frequently. - */ - if (t - data->zen_last > ANSWER_RATE) { - if (irc_match(data->text, "*why *") || - irc_match(data->text, "*how *") || - irc_match(data->text, "*who *") || - irc_match(data->text, "*what *") || - irc_match(data->text, "*where *") || - irc_match(data->text, "*when *")) { - data->zen_last = t; - str = zen(); - /* Let's simulate a human delay (2 - 10 secs) */ - r = 2 + (rand() % 8); - if (!mm_strcmp(data->param[0], data->nickname)) - cron_add(r, "PRIVMSG %s :%s\r\n", data->nick, str); - else - cron_add(r, "PRIVMSG %s :%s: %s\r\n", data->param[0], - data->nick, str); - } - } -} - -static void main_noticed(struct info *data) -{ - /* XXX */ -} - -static void main_joined(struct info *data) -{ - struct channel *ch; - - /* data->text consists of channel name, data->nick of nickname */ - if (!mm_strcmp(data->nick, data->nickname)) { - /* We have joined a new channel, create new channel node with it's - * users list - */ - if ((ch = (struct channel *)pool_alloc(&plchans, TRUE))) { - if (pool_init(&ch->pchannel_users, "pchannel_users", - malloc, free, NULL, NULL, sizeof(struct user), - 4096 / sizeof(struct user), 0, 0)) { - DLIST_INIT(&ch->channel_users); - ch->channel_hash = mm_strhash64(data->text); - mm_strcpy(ch->channel_name, data->text); - DLIST_APPEND(&lchans, (node_t *)ch); - } - } - } else { - /* Another user joined one of the channels we are in, update - * corresponding channel's userlist - */ - user_add(data->text, data->nick); - } -} - -static void main_kicked(struct info *data) -{ - struct channel *ch; - - /* data->param[0] consists of channel name, data->param[1] of nick */ - ch = user_del(data->param[0], data->param[1]); - if (!mm_strcmp(data->param[1], data->nickname)) { - /* We have been kicked out of the channel, free all channel - * info and send a delayed join request - */ - DLIST_UNLINK(&lchans, (node_t *)ch); - pool_destroy(&ch->pchannel_users); - pool_free((pnode_t *)ch); - cron_add(REJOIN_DELAY, "JOIN :%s\r\n", data->param[0]); - } -} - -static void main_parted(struct info *data) -{ - struct channel *ch; - - /* data->nick consists of nickname, data->param[0] of channel name */ - ch = user_del(data->param[0], data->nick); - if (!mm_strcmp(data->nick, data->nickname)) { - /* We have left a channel, free all channel info */ - DLIST_UNLINK(&lchans, (node_t *)ch); - pool_destroy(&ch->pchannel_users); - pool_free((pnode_t *)ch); - } -} - -static void main_quitted(struct info *data) -{ - struct channel *ch, *tmp; - - user_del(NULL, data->nick); - if (!mm_strcmp(data->nick, data->nickname)) { - /* We have quitted, free all channel info */ - ch = (struct channel *)lchans.top; - while (ch) { - tmp = (struct channel *)ch->node.node.next; - DLIST_UNLINK(&lchans, (node_t *)ch); - pool_free((pnode_t *)ch); - ch = tmp; - } - } -} - -static void main_nick(struct info *data) -{ - u_int64_t hash, nhash; - struct channel *ch; - struct user *us; - - /* A user changed nickname, make sure to update it in all our channels - * the user is in. If we are the one who changed nick, also update our - * internal own nickname record. - */ - hash = mm_strhash64(data->nick); - nhash = mm_strhash64(data->text); - - ch = (struct channel *)lchans.top; - while (ch) { - us = (struct user *)ch->channel_users.top; - while (us) { - if (us->user_hash == hash) - break; - us = (struct user *)us->node.node.next; - } - if (us) { - us->user_hash = nhash; - mm_strcpy(us->user_nick, data->text); - } - ch = (struct channel *)ch->node.node.next; - } - /* - if (!mm_strcmp(data->nick, data->nickname)) { - mm_strncpy(data->nickname, data->text, 63); - XXX Update hash also? - } - */ -} - -static void main_mode(struct info *data) -{ - /* A mode change occured for either ourself, a channel or another user. - * we only record those for now, but action could be taken also on certain - * mode change events. XXX - */ - /* :PhadThai!mmondor@gobot.xisop MODE #netbsd-devel +o nanobit - * :nanobit MODE nanobit :+iw - */ - if (data->ident && data->param[0] && *data->param[0] == '#') { - /* cmode or cumode change */ - } else { - /* umode change */ -#ifdef DEBUG - printf("UMODE\n"); -#endif - } -} - -static void main_invited(struct info *data) -{ - /* XXX */ -} - -static void main_names(struct info *data) -{ - char *words[64]; /* XXX check how many max */ - int cols, i; - - /* data->param[2] consists of channel name, and data->text of nicks */ - if ((cols = mm_straspl(words, data->text, 63)) > 0) { - for (i = 0; i < cols; i++) - user_add(data->param[2], words[i]); - } -} - - -/* The main startup function */ -int main(int argc, char **argv) -{ - int port, fd; - char *server; - struct sockaddr_in client; - struct in_addr iaddr; - struct info data; - - fdfuncs fdf = { - malloc, - free, - poll, - read, - write, - sleep, - usleep, - NULL, - NULL, - NULL, - NULL, - NULL, - eintr - }; - - if (argc != 3) { - printf("Usage: zenbot \n"); - exit (-1); - } - - mm_memclr(&data, sizeof(data)); - - /* Parse arguments */ - server = argv[2]; - mm_strncpy(data.nickname, argv[1], 63); - port = 6667; - - /* Zen setup */ - zen_init(); - - if (pool_init(&plusers, "plusers", malloc, free, NULL, NULL, - sizeof(struct freq), 8192 / sizeof(struct freq), 0, 0)) { - DLIST_INIT(&lusers); - if (pool_init(&plhosts, "plhosts", malloc, free, NULL, NULL, - sizeof(struct freq), 8192 / sizeof(struct freq), 0, 0)) { - DLIST_INIT(&lhosts); - if (pool_init(&plcron, "plcron", malloc, free, NULL, NULL, - sizeof(struct cron), 8192 / sizeof(struct cron), - 0, 0)) { - DLIST_INIT(&lcron); - if (pool_init(&plchans, "plchans", malloc, free, NULL, NULL, - sizeof(struct channel), - 8192 / sizeof(struct channel), 0, 0)) { - DLIST_INIT(&lchans); - /* Prepare our fd buffering wrapper */ - if ((data.fdb = fdbopen(&fdf, NULL, -1, 4096, 4096, 1024, - 1024, INPUT_TIMEOUT * 1000, - INPUT_TIMEOUT * 1000, FALSE))) { - /* Loop indefinitely */ - for (;;) { - /* Connect to server */ - if ((fd = socket(AF_INET, SOCK_STREAM, 0)) != -1) { - mm_memclr(&client, sizeof(struct sockaddr_in)); - if ((inet_aton(server, &iaddr))) { - client.sin_family = AF_INET; - client.sin_addr.s_addr = iaddr.s_addr; - client.sin_port = htons(port); - if ((connect(fd, - (struct sockaddr *)&client, - sizeof(struct sockaddr_in))) - != -1) { - fcntl(fd, F_SETFL, O_NONBLOCK); - fdbparam_set(data.fdb, fd, FDBP_FD); - /* Serve our purpose */ - data.state = &states[STATE_IDENT]; - main_loop(&data); - fdbflushr(data.fdb); - fdbflushw(data.fdb); - fdbparam_set(data.fdb, -1, FDBP_FD); - } else - sleep(30); - } - close(fd); - } - } - fdbclose(data.fdb); - } - pool_destroy(&plchans); - } - pool_destroy(&plcron); - } - pool_destroy(&plhosts); - } - pool_destroy(&plusers); - } - - exit(0); -} - - -/* This function consists of the main state switcher loop, and basically - * reads lines, calling the matching function handlers for the current - * state. When a state switch occurs, exit code of the current state is - * executed, and init code of the new state, if any. - * We only return if a handler function returns a NULL pointer, or if - * we loose connection with the server. - * We internally take care of PING replies. - */ -static void main_loop(struct info *data) -{ - struct state *curstate; - struct handler_func *fn; - int i; - - if (data->state->state_init) - data->state->state_init(data); - - while (TRUE) { - struct cron *nod, *tmp; - time_t t; - - /* Verify if any queued data has expired and should be released out. - * unfortunately because of the way we currently work, some IRC - * activity is required to trigger this. XXX Ideally the fdbgets() - * timeout should be set to 1 when there is queued data, and timeout - * should not be assumed to be a server/connection failiure. For now, - * this should work fine on a channel with activity, or if we are - * on multiple channels. - */ - t = time(NULL); - nod = (struct cron *)lcron.top; - while (nod) { - tmp = (struct cron *)nod->node.node.next; - if (nod->expires < t) { - DLIST_UNLINK(&lcron, (node_t *)nod); - fdbputs(data->fdb, nod->data); - pool_free((pnode_t *)nod); - } - nod = tmp; - } - - fdbflushw(data->fdb); - - if ((i = fdbgets(data->fdb, data->line, 511, FALSE)) > -1) { - - /* We got a line, check if there exists any handler to serve - * it within current state. Execute the handler if required, - * and perform sane state switching if requested by a handler. - */ - - if (!mm_strncmp(data->line, "PING :", 6)) { - /* Transform PING to PONG and reply to server */ - data->line[1] = 'O'; - fdbprintf(data->fdb, "%s\r\n", data->line); - continue; - } - - irc_parse(data); - - /* Useful for debugging */ -#ifdef DEBUG - fwrite("\033[1m", 4, 1, stdout); - fwrite(data->line, i, 1, stdout); - fwrite("\r\n", 2, 1, stdout); - fwrite("\033[0m", 4, 1, stdout); - fflush(stdout); - printf("code: %d server: '%s' nick: '%s' ident: '%s' host: '%s' command: '%s' text: '%s' params: %d ", - data->code, data->server, data->nick, data->ident, - data->host, data->command, data->text, data->params); - for (i = 0; i < data->params; i++) - printf("param[%d]: '%s' ", i, data->param[i]); - printf("\r\n\r\n"); - fflush(stdout); -#endif - - if (data->code == -1 && data->ident) { - int res; - bool allow = FALSE; - - /* Check against flood by ident and hostname */ - if (!(res = allow_entry(&plhosts, &lhosts, data->host, - FLOOD_HOST_MAX, FLOOD_HOST_TIME, - FLOOD_HOST_IGNORE))) { - if (!(res = allow_entry(&plusers, &lusers, data->ident, - FLOOD_USER_MAX, FLOOD_USER_TIME, - FLOOD_USER_IGNORE))) - allow = TRUE; - else if (res == 2) - ignore_user(data); - } else if (res == 2) - ignore_host(data); - if (!allow) - continue; - } - - curstate = data->state; - fn = curstate->state_handlers; - while (fn->handler_func) { - bool allow; - - allow = FALSE; - if (fn->handler_code && data->code != -1) { - if (fn->handler_code == data->code) - allow = TRUE; - } else if (fn->handler_command && data->command) { - if (!mm_strcmp(fn->handler_command, data->command)) - allow = TRUE; - } - if (allow) { - /* Execute handler for matching pattern */ - fn->handler_func(data); - if (data->state != curstate) { - /* Perform state switching */ - if (curstate->state_exit) - curstate->state_exit(data); - if (data->state->state_init) - data->state->state_init(data); - } - break; - } - fn++; - } - - } else - break; - } -} - - -/* This function is useful to easily perform flood quota checking on nicks - * and/or hosts. Returns 0 if the entry is allowed, 1 if it is not, 2 if - * it just started to be ignored. - */ -static int -allow_entry(pool_t *pool, list_t *lst, char *entry, unsigned long max, - int secs, long igndelay) -{ - u_int64_t hash = mm_strhash64(entry); - int ret = 0; - struct freq *node, *tmp; - time_t t = time(NULL); - - /* Locate if entry exists */ - node = (struct freq *)lst->top; - while (node) { - tmp = (struct freq *)node->node.node.next; - if (node->expires < t) { - /* Expired, drop this entry */ - DLIST_UNLINK(lst, (node_t *)node); - pool_free((pnode_t *)node); - } else if (node->hash == hash) - break; - node = tmp; - } - if (node) { - /* Entry existed, increase frequency and optionally toggle ignore */ - node->freq++; - if (!node->ignore) { - if (node->freq > max) { - node->ignore = TRUE; - node->expires += igndelay; - ret = 2; - } - } else - ret = 1; - } else { - /* Add new entry */ - if ((node = (struct freq *)pool_alloc(pool, FALSE))) { - node->expires = t + secs; - node->ignore = FALSE; - node->hash = hash; - node->freq = 1; - DLIST_APPEND(lst, (node_t *)node); - } - } - - return (ret); -} - - -/* This function returns weither or not pat matches string. - * We only perform simple * and ? pattern matching, case-sensitive. - */ -static bool irc_match(char *str, char *pat) -{ - while (*pat ^ '*') { - if (!*str) { - if (*pat) - return (FALSE); - else - return (TRUE); - } - if (*str ^ *pat && *pat ^ '?') - return (FALSE); - pat++; - str++; - } - - while (pat[1] == '*') - pat++; - - do { - if (irc_match(str, pat + 1)) - return (TRUE); - } while (*str++); - - return (FALSE); -} - - -/* Some mmondor-style string parsing magic, fill data fields according to - * IRC protocol line - */ -static void irc_parse(struct info *data) -{ - char *ptr, *optr, *words[16]; - size_t cols; - int i, i2; - - /* Init, everything set to NULL, and code to -1 by default */ - mm_strcpy(data->work, data->line); - ptr = data->work; - data->code = -1; - data->params = 0; - data->server = data->nick = data->ident = data->host = data->command = - data->text = NULL; - - if (*ptr == ':') { - /* IRC lines start with ':' */ - ptr++; - optr = ptr; - while (*ptr && *ptr != ':') - ptr++; - if (*ptr == ':') { - /* Multiword text field */ - *ptr++ = 0; - data->text = ptr; - } - if ((cols = mm_straspl(words, optr, 16)) > 1) { - /* Determine the type of IRC protocol line and fill corresponding - * pointers. - */ - if ((i = atoi(words[1]))) { - /* Code based line */ - data->server = *words; - data->code = i; - } else { - /* Text command based line */ - ptr = *words; - while (*ptr && *ptr != '.' && *ptr != '!') - ptr++; - if (*ptr == '.') - /* Server name in first column */ - data->server = *words; - else { - /* Nickname in first column, optional ident and host */ - ptr = data->nick = *words; - while (*ptr && *ptr != '!') - ptr++; - if (*ptr == '!') { - *ptr++ = 0; - data->ident = ptr; - while (*ptr && *ptr != '@') - ptr++; - if (*ptr == '@') { - *ptr++ = 0; - data->host = ptr; - } - } - } - data->command = words[1]; - } - if (cols > 2) { - /* Additional parameters before text */ - i = 0; - for (i2 = 2; i2 < cols; i2++) - data->param[i++] = words[i2]; - data->params = i; - } - } - } -} - - -/* Adds a user to a channel, also checks for @ and + in start of nick to - * set according mode. - */ -static void user_add(char *chan, char *nick) -{ - u_int64_t chanhash, nickhash; - struct channel *ch; - struct user *us; - int mode; - - mm_strlower(chan); - chanhash = mm_strhash64(chan); - - mode = 0; - if (*nick) { - if (*nick == '@') { - mode |= (1L << 0); - nick++; - } else if (*nick == '+') { - mode |= (1L << 1); - nick++; - } - } - nickhash = mm_strhash64(nick); - - /* Find channel user should be added to */ - ch = (struct channel *)lchans.top; - while (ch) { - if (ch->channel_hash == chanhash) - break; - ch = (struct channel *)ch->node.node.next; - } - if (ch) { - /* We found channel */ - if ((us = (struct user *)pool_alloc(&ch->pchannel_users, FALSE))) { - mm_strcpy(us->user_nick, nick); - us->user_hash = nickhash; - us->user_mode = mode; -#ifdef DEBUG - printf("ADDED USER '%s' TO CHANNEL '%s'\n", nick, chan); -#endif - DLIST_APPEND(&ch->channel_users, (node_t *)us); - } - } -} - - -/* Deletes a nick from specified channel, or from all channels if chan is NULL - */ -static struct channel *user_del(char *chan, char *nick) -{ - u_int64_t chanhash, nickhash; - struct channel *ch; - struct user *us; - - nickhash = mm_strhash64(nick); - ch = NULL; - - if (chan) { - /* Delete user on specified channel */ - mm_strlower(chan); - chanhash = mm_strhash64(chan); - ch = (struct channel *)lchans.top; - while (ch) { - if (ch->channel_hash == chanhash) - break; - ch = (struct channel *)ch->node.node.next; - } - if (ch) { - /* We found channel */ - us = (struct user *)ch->channel_users.top; - while (us) { - if (us->user_hash == nickhash) - break; - us = (struct user *)us->node.node.next; - } - if (us) { - /* We found nick */ - DLIST_UNLINK(&ch->channel_users, (node_t *)us); - pool_free((pnode_t *)us); -#ifdef DEBUG - printf("DELETED USER '%s' FROM CHANNEL '%s'\n", nick, - chan); -#endif - } - } - } else { - /* Delete user on all channels */ - ch = (struct channel *)lchans.top; - while (ch) { - us = (struct user *)ch->channel_users.top; - while (us) { - if (us->user_hash == nickhash) { - DLIST_UNLINK(&ch->channel_users, (node_t *)us); - pool_free((pnode_t *)us); - break; - } - us = (struct user *)us->node.node.next; - } - ch = (struct channel *)ch->node.node.next; - } -#ifdef DEBUG - printf("DELETED USER '%s' FROM ALL CHANNELS\n", nick); -#endif - } - - return (ch); -} - - -/* Allows to queue data to be sent after a certain delay has been observed. */ -static void cron_add(int secs, char *fmt, ...) -{ - struct cron *nod; - time_t t = time(NULL); - va_list arg_ptr; - - if ((nod = (struct cron *)pool_alloc(&plcron, FALSE))) { - nod->expires = t + secs; - va_start(arg_ptr, fmt); - vsnprintf(nod->data, 1023, fmt, arg_ptr); - va_end(arg_ptr); - DLIST_APPEND(&lcron, (node_t *)nod); - } -} - - - -static void zen_init(void) -{ - int num; - - for (num = 0; zen_strings[num]; num++); - zen_num = num; -} - -static char *zen(void) -{ - /*return( zen_strings[rand() % zen_num] ); */ - if (zen_offset > zen_num) - zen_offset = 0; - return (zen_strings[zen_offset++]); -} - - -static bool eintr(void) -{ - if (errno == EINTR) - return TRUE; - - return FALSE; -} - diff --git a/mmsoftware/clean.sh b/mmsoftware/clean.sh deleted file mode 100755 index a5af627..0000000 --- a/mmsoftware/clean.sh +++ /dev/null @@ -1,33 +0,0 @@ -#!/bin/sh -# $Id: clean.sh,v 1.2 2003/07/02 17:20:52 mmondor Exp $ - -. mmlib/makefuncs.sh - -cd mmlib/ -clean mmlib -cd ../ - -cd mmpasswd/ -clean mmpasswd -cd ../ - -cd mmstatd/src/ -clean mmstatd -cd ../../ -cd apache-mmstat/ -clean apache-mmstat -cd ../ - -#cd mmsucom/ -#clean mmsucom -#cd ../ - -cd mmftpd/src/ -clean mmftpd -cd ../../ - -cd mmmail/src/mmsmtpd/ -clean mmsmtpd -cd ../mmpop3d -clean mmpop3d -cd ../../../ diff --git a/mmsoftware/install.sh b/mmsoftware/install.sh deleted file mode 100755 index 410a986..0000000 --- a/mmsoftware/install.sh +++ /dev/null @@ -1,199 +0,0 @@ -#!/bin/sh -# $Id: install.sh,v 1.9 2003/10/23 01:01:15 mmondor Exp $ - -if [ "$1" = "help" ]; then - echo - echo 'You can optionally set the following environment variables' - echo 'to customize the installation process. For each is shown an' - echo 'example using the default value followed by a breif description.' - echo - echo 'export MMLAUNCH="TRUE"' - echo ' Tells the install process to immediately launch the daemon(s)' - echo ' They are automatically killed before the binaries are copied' - echo ' over the old ones as is necessary with some systems. The' - echo ' default is to not start them back automatically.' - echo - echo 'export MMPREFIX="/usr/local"' - echo ' Allows to set the installation base directory. All files but' - echo ' configuration ones will be installed in directories relative' - echo ' to this one.' - echo - echo 'export MMCONFDIR="/etc"' - echo ' Directory in which configuration files should be stored.' - echo - echo 'export MMDEFAULTUSER="0"' - echo ' User new files and directories should be owned by, using' - echo ' user id or name.' - echo - echo 'export MMDEFAULTGROUP="0"' - echo ' Group new files and directories should be under, using id' - echo ' or name.' - echo - echo 'export MMADMINGROUP="staff"' - echo ' The administrators group, these can for instance view and the' - echo ' rotate mmstat statistics and execute mmpasswd. Will be' - echo ' created automatically if necessary.' - echo - echo 'export MMSTATDIR="/var/mmstatd"' - echo ' The directory in which mmstatd will store the stats database' - echo ' and log files.' - echo - echo 'export MMSTATDUSER="mmstatd"' - echo ' The user mmstatd will run under, to be automatically created.' - echo - echo 'export MMSTATDGROUP="mmstat"' - echo ' Group the mmstatd user should be part of, automatically' - echo ' created.' - echo - echo 'export MMFTPDUSER="mmftpd"' - echo ' The user mmftpd will run under, to be automatically created.' - echo - echo 'export MMFTPDGROUP="mmftpd"' - echo ' Group the mmftpd user should be part of, automatically' - echo ' created. The mmftpdpasswd configuration file will be readable' - echo ' by this group.' - echo - echo 'export MMMAILUSER="mmmail"' - echo ' The user mmmail daemons should run under, will be created' - echo ' automatically if nonexisting.' - echo - echo 'export MMMAILGROUP="mmmail"' - echo ' Group the mmmail user should be part of. Created if needed.' - echo - exit -fi - -# Set defaults if not set -if [ -z "$MMLAUNCH" ]; then - export MMLAUNCH='FALSE' -fi -if [ -z "$MMPREFIX" ]; then - export MMPREFIX='/usr/local' -fi -if [ -z "$MMCONFDIR" ]; then - export MMCONFDIR='/etc' -fi -if [ -z "$MMDEFAULTUSER" ]; then - export MMDEFAULTUSER='0' -fi -if [ -z "$MMDEFAULTGROUP" ]; then - export MMDEFAULTGROUP='0' -fi -if [ -z "$MMADMINGROUP" ]; then - export MMADMINGROUP='staff' -fi -if [ -z "$MMSTATDIR" ]; then - export MMSTATDIR='/var/mmstatd' -fi -if [ -z "$MMSTATDUSER" ]; then - export MMSTATDUSER='mmstatd' -fi -if [ -z "$MMSTATDGROUP" ]; then - export MMSTATDGROUP='mmstat' -fi -if [ -z "$MMFTPDUSER" ]; then - export MMFTPDUSER='mmftpd' -fi -if [ -z "$MMFTPDGROUP" ]; then - export MMFTPDGROUP='mmftpd' -fi -if [ -z "$MMMAILUSER" ]; then - export MMMAILUSER='mmmail' -fi -if [ -z "$MMMAILGROUP" ]; then - export MMMAILGROUP='mmmail' -fi - -. mmlib/makefuncs.sh - -instgroup $MMADMINGROUP - -cd mmlib/ -instman mmfd.3 3 -instman mmfifo.3 3 -instman mmlifo.3 3 -instman mmlist.3 3 -instman mmpool.3 3 -instman mmpath.3 3 -instman mmstat.3 3 -instman mmhash.3 3 -instman mmlimitrate.3 3 -cd ../ -cd apache-mmstat/ -instman apache-mmstat.8 8 -cd ../ - -cd mmpasswd/ -instbin mmpasswd 750 $MMADMINGROUP -instman mmpasswd.8 8 -cd ../ - -cd mmstatd/src/ -instuser $MMSTATDUSER $MMSTATDGROUP -killbin mmstatd -instbin mmstatd 700 -instbin mmstat 750 $MMADMINGROUP -instman mmstat.8 8 -instman mmstatd.8 8 -instman mmstatd.conf.5 5 -cd ../etc/ -instconf mmstatd.conf 640 $MMSTATDGROUP -instdir $MMSTATDIR 750 $MMSTATDUSER $MMSTATDGROUP -if [ "$MMLAUNCH" = "TRUE" ]; then - startbin mmstatd $MMCONFDIR/mmstatd.conf -fi -cd ../../ -cd apache-mmstat/ -instbin apache-mmstat 700 -cd ../ - -cd mmftpd/src/ -instuser $MMFTPDUSER $MMFTPDGROUP -killbin mmftpd -instbin mmftpd 700 -instman mmftpd.8 8 -instman mmftpd.conf.5 5 -instman mmftpdpasswd.5 5 -cd ../etc/ -instconf mmftpd.conf 600 -instconf mmftpdpasswd 640 $MMFTPDGROUP -if [ "$MMLAUNCH" = "TRUE" ]; then - startbin mmftpd $MMCONFDIR/mmftpd.conf -fi -cd ../../ - -cd mmmail/src/mmsmtpd/ -instuser $MMMAILUSER $MMMAILGROUP -killbin mmsmtpd -instbin mmsmtpd 700 -instman mmsmtpd.8 8 -instman mmsmtpd.conf.5 5 -cd ../mmpop3d -killbin mmpop3d -instbin mmpop3d 700 -instman mmpop3d.8 8 -instman mmpop3d.conf.5 5 -cd ../../etc -instconf mmsmtpd.conf 600 -instconf mmpop3d.conf 600 -if [ "$MMLAUNCH" = "TRUE" ]; then - startbin mmsmtpd $MMCONFDIR/mmsmtpd.conf - startbin mmpop3d $MMCONFDIR/mmpop3d.conf -fi -cd ../src -instman mmmail.8 8 -cd ../../ - -echo -echo "*** Please read the following man pages ***" -echo -echo "all users: mmstat(8), mmstatd(8), mmstatd.conf(5), mmpasswd(8)," -echo " apache-mmstat(8)" -echo "mmftpd users: mmftpd(8), mmftpd.conf(5), mmftpdpasswd(5)" -echo "mmmail users: mmmail(8), mmsmtpd(8), mmsmtpd.conf(5), mmpop3d(8)," -echo " mmpop3d.conf(5)" -echo "source auditors: mmstat(3), mmfd(3), mmlist(3), mmpool(3), mmfifo(3)," -echo " mmlifo(3), mmpath(3), mmhash(3), mmlimitrate(3)" -echo -echo "Thank you for using mmsoftware." -echo diff --git a/mmsoftware/js/classes/js_errno.c b/mmsoftware/js/classes/js_errno.c deleted file mode 100644 index e49b2da..0000000 --- a/mmsoftware/js/classes/js_errno.c +++ /dev/null @@ -1,222 +0,0 @@ -/* $Id: js_errno.c,v 1.1 2006/07/22 04:18:47 mmondor Exp $ */ - -/* - * Copyright (c) 2005, Matthew Mondor - * ALL RIGHTS RESERVED. - */ - -/* - * Basic UNIX errno services for ECMAScript - */ - - - -#include - -#include -#include -#include -#include -#include - -#include - - - -/* Utility macros */ -#define QUEUE_EXCEPTION(s) do { \ - JS_SetPendingException(cx, \ - STRING_TO_JSVAL(JS_NewStringCopyZ(cx, (s)))); \ -} while (/* CONSTCOND */0) - - - -/* Prototypes */ -static JSBool errno_sm_strerror(JSContext *, JSObject *, uintN, jsval *, - jsval *); - - - -/* Actual class parameters */ -static JSClass errno_class = { - "Errno", 0, JS_PropertyStub, JS_PropertyStub, - JS_PropertyStub, JS_PropertyStub, JS_EnumerateStub, - JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub -}; - -/* Provided static methods */ -static JSFunctionSpec errno_smethods[] = { - { "strerror", errno_sm_strerror, 1, 0, 0 }, - { NULL, NULL, 0, 0, 0 } -}; - -/* - * Provided static properties. - * We use these to provide ECMAScript with the ability to use system-specific - * standard C constant macros without us having to tidiously map them - * individually, or to require other scripts to be used as headers to define - * them. Another possibility would have been to supply these parameters as - * string, but this would have required even slower remapping because of the - * parsing and string comparisions. - * We only include those which we consider necessary for now, others may be - * added easily as needed, provided that they are added in all three maps. - * I might perhaps develop macros and/or functions to map all these easily - * from a single map. - */ - -struct property_spec { - const char *name; - int value; -}; - -#define SP(n) \ - { #n, n } - -static struct property_spec errno_sprops[] = { - SP(EPERM), - SP(ENOENT), - SP(EINTR), - SP(EIO), - SP(ENXIO), - SP(EBADF), - SP(EACCES), - SP(ENOTBLK), - SP(EBUSY), - SP(EEXIST), - SP(EXDEV), - SP(ENODEV), - SP(ENOTDIR), - SP(EISDIR), - SP(EINVAL), - SP(ENFILE), - SP(EMFILE), - SP(ENOTTY), - SP(ETXTBSY), - SP(EFBIG), - SP(ENOSPC), - SP(ESPIPE), - SP(EROFS), - SP(EMLINK), - SP(EPIPE), - SP(EAGAIN), - SP(EINPROGRESS), - SP(EALREADY), - SP(ENOTSOCK), - SP(EDESTADDRREQ), - SP(EMSGSIZE), - SP(EPROTOTYPE), - SP(EPROTONOSUPPORT), - SP(EOPNOTSUPP), - SP(EPFNOSUPPORT), - SP(EAFNOSUPPORT), - SP(EADDRINUSE), - SP(EADDRNOTAVAIL), - SP(ENETDOWN), - SP(ENETUNREACH), - SP(ENETRESET), - SP(ECONNABORTED), - SP(ECONNRESET), - SP(ENOBUFS), - SP(EISCONN), - SP(ENOTCONN), - SP(ESHUTDOWN), - SP(ETIMEDOUT), - SP(ECONNREFUSED), - SP(ELOOP), - SP(ENAMETOOLONG), - SP(EHOSTDOWN), - SP(EHOSTUNREACH), - SP(ENOTEMPTY), - SP(EDQUOT), - SP(ESTALE), - SP(ENOLCK), - SP(ENOSYS), - SP(EFTYPE), - SP(ENOMSG), - SP(ENOTSUP), - SP(ECANCELED), - SP(EBADMSG), - SP(ENODATA), - SP(ETIME), - - { NULL, 0 } -}; - -#undef SP - - - -/* - * Class control functions - */ - -JSObject * -js_InitErrnoClass(JSContext *cx, JSObject *obj) -{ - JSObject *proto, *ctor; - struct property_spec *sp; - - if ((proto = JS_InitClass(cx, obj, NULL, &errno_class, NULL, - 0, NULL, NULL, NULL, errno_smethods)) == NULL) { - (void) fprintf(stderr, "Error initializing Errno class\n"); - return NULL; - } - - /* Create static properties */ - if ((ctor = JS_GetConstructor(cx, proto)) == NULL) { - (void) fprintf(stderr, "Errno: JS_GetConstructor == NULL\n"); - return NULL; - } - for (sp = errno_sprops; sp->name != NULL; sp++) { - if (JS_DefineProperty(cx, ctor, sp->name, - INT_TO_JSVAL(sp->value), NULL, NULL, - JSPROP_READONLY | JSPROP_PERMANENT) == JS_FALSE) { - (void) fprintf(stderr, - "Errno: Error defining property %s\n", sp->name); - return NULL; - } - } - - return proto; -} - - - -/* - * Static properties functions - */ - - - -/* - * Static methods - */ - -static JSBool -errno_sm_strerror(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - int error; - JSString *string; - - *rval = OBJECT_TO_JSVAL(NULL); - - if (argc != 1) { - QUEUE_EXCEPTION("Wrong number of arguments"); - return JS_FALSE; - } - if (!JSVAL_IS_INT(*argv)) { - QUEUE_EXCEPTION("Argument not an integer"); - return JS_FALSE; - } - error = (int)JSVAL_TO_INT(*argv); - - if ((string = JS_NewStringCopyZ(cx, strerror(error))) == NULL) { - QUEUE_EXCEPTION("Out of memory"); - return JS_FALSE; - } - - *rval = STRING_TO_JSVAL(string); - - return JS_TRUE; -} diff --git a/mmsoftware/js/classes/js_errno.h b/mmsoftware/js/classes/js_errno.h deleted file mode 100644 index c6d7b30..0000000 --- a/mmsoftware/js/classes/js_errno.h +++ /dev/null @@ -1,13 +0,0 @@ -/* $Id: js_errno.h,v 1.1 2006/07/22 04:18:47 mmondor Exp $ */ - -/* - * Copyright (c) 2005, Matthew Mondor - * ALL RIGHTS RESERVED. - */ - -#ifndef JSERRNO_H -#define JSERRNO_H - -extern JSObject *js_InitErrnoClass(JSContext *, JSObject *); - -#endif diff --git a/mmsoftware/js/classes/js_fd.c b/mmsoftware/js/classes/js_fd.c deleted file mode 100644 index 806f2be..0000000 --- a/mmsoftware/js/classes/js_fd.c +++ /dev/null @@ -1,1971 +0,0 @@ -/* $Id: js_fd.c,v 1.1 2006/07/22 04:18:47 mmondor Exp $ */ - -/* - * Copyright (c) 2005, Matthew Mondor - * ALL RIGHTS RESERVED. - */ - -/* - * Basic UNIX filedescriptor and BSD sockets services for ECMAScript - */ - -/* - * XXX TODO XXX - * - Possibly create a byte buffer type - * - Add path based restrictions to open() - * - (maybe) add restrictions to bind() - * - Either add stat() or provide equivalent properties - * - Enhance exception reports. Function name should be provided, and - * errno should be displayed when wanted. Perhaps that jsfd->error could - * also be set automatically when an exception is generated which involves - * errno. - * - Check JS_ReportError(). - * - Moving finalizer freeing and close() freeing code to a common function - * might be a good idea. - * - Add getnameinfo()/getaddrinfo() ? Or should we transparently allow this - * through properties? - * - Add send()/sendto()/sendmsg(), recv()/recvfrom()/recvmsg() ... - * - It is possible that we need to verify that obj is instance of FD in all - * methods, perhaps. I.E. consider an FD method assigned on another object. - * - Add hostname to address and address to hostname resolution facilities - * - Maybe also add properties for local end address/port of socket - * - Would be nice to experiment with a fork(2) heh. If so, if we allow - * fcntl(2) close-on-exec flag, we should make sure to mark FD objects as - * closed too for an execve(2) wrapper. - * - popen(2), would probably need to use custom execve(2) wrapper above... - * - Add support to easily restrict an application's right to functions. - * Path and mode sanity checking functions should also be written and their - * parameters set on a per-application basis. - * - Maybe virtual chdir(2) - * - A stdio FILE extension object might be nice, with stuff like fdopen() to - * create one... This would be most useful for buffered lined based input - * and output. Exporting mmfd library to js might also be nice perhaps, - * either as alternative or addition. - * - mmap(2) - how could I do this without some byte class and associated - * methods? Seems way tricky. - * - lstat(2), fstat(2) - * - dup2() - * - rename(2), unlink(2) etc would be useful, but we need another class - * for this (maybe a VFS static class?) Maybe even something calling - * execve(2) and fork(2), those primitives... popen(3) also. - * - Also opendir(3) and friends wrapper... - */ - - - -#include -#include -#include - -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include - - - -/* Utility macros */ -#define QUEUE_EXCEPTION(s) do { \ - JS_SetPendingException(cx, \ - STRING_TO_JSVAL(JS_NewStringCopyZ(cx, (s)))); \ -} while (/* CONSTCOND */0) - - -/* Internal representation of FD object */ -typedef struct jsfd { - int fd; - int type; - union { - struct { - char *path; - int flags; - mode_t mode; - } file; - struct { - int domain, type, protocol; - struct sockaddr_in caddr; - } socket; - } u; - /* For polling; Interesting events, and occurred events */ - short events, revents; - /* Last error for this descriptor */ - int error; -} jsfd_t; - -enum jsfd_types { - /* Type */ - JSFD_NONE = 0, /* Initial */ - JSFD_STD = (1 << 1), - JSFD_FILE = (1 << 2), - JSFD_SOCKET = (1 << 3) -}; - -/* Used our fd_sm_poll() function */ -struct poll_fdsi { - char *name; /* Property name or NULL */ - jsval fdobj; /* Pointer to FD object */ - jsfd_t *jsfd; /* Pointer to FD object data */ -}; - -struct poll_fds { - struct pollfd *entries; /* Passed to poll(2) */ - struct poll_fdsi *info; - int count, size; -}; - -/* Functions arguments types */ -enum jsarg_types { - JSAT_INTEGER = 1, - JSAT_DOUBLE, - JSAT_STRING, - JSAT_OBJECT -}; - - -/* Prototypes */ -static JSBool fd_constructor(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static void fd_finalize(JSContext *, JSObject *); - -static JSBool fd_getProperty(JSContext *, JSObject *, jsval, jsval *); -static JSBool fd_setProperty(JSContext *, JSObject *, jsval, jsval *); - -static JSBool fd_m_open(JSContext *, JSObject *, uintN, jsval *, jsval *); -static JSBool fd_m_set(JSContext *, JSObject *, uintN, jsval *, jsval *); -static JSBool fd_m_close(JSContext *, JSObject *, uintN, jsval *, jsval *); -static JSBool fd_m_truncate(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool fd_m_put(JSContext *, JSObject *, uintN, jsval *, jsval *); -static JSBool fd_m_get(JSContext *, JSObject *, uintN, jsval *, jsval *); -static JSBool fd_m_socket(JSContext *, JSObject *, uintN, jsval *, jsval *); -static JSBool fd_m_connect(JSContext *, JSObject *, uintN, jsval *, jsval *); -static JSBool fd_m_bind(JSContext *, JSObject *, uintN, jsval *, jsval *); -static JSBool fd_m_listen(JSContext *, JSObject *, uintN, jsval *, jsval *); -static JSBool fd_m_accept(JSContext *, JSObject *, uintN, jsval *, jsval *); -static JSBool fd_m_shutdown(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool fd_m_setsockopt(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool fd_m_getsockopt(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool fd_m_fcntl(JSContext *, JSObject *, uintN, jsval *, jsval *); -static JSBool fd_m_write(JSContext *, JSObject *, uintN, jsval *, jsval *); -static JSBool fd_m_read(JSContext *, JSObject *, uintN, jsval *, jsval *); -static JSBool fd_m_fdatasync(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool fd_m_lseek(JSContext *, JSObject *, uintN, jsval *, jsval *); -static JSBool fd_m_fchmod(JSContext *, JSObject *, uintN, jsval *, jsval *); -static JSBool fd_m_flock(JSContext *, JSObject *, uintN, jsval *, jsval *); -static JSBool fd_m_fstat(JSContext *, JSObject *, uintN, jsval *, jsval *); - -static JSBool fd_sm_poll(JSContext *, JSObject *, uintN, jsval *, jsval *); -static JSBool fd_sm_poll_mkset(JSContext *, jsval *, jsval *, void *); - -static JSBool object_iterate(JSContext *, JSObject *, void *, - JSBool (*)(JSContext *, jsval *, jsval *, void *)); -static int fd_path_allow(const char *); -static mode_t fd_mode_allow(mode_t); -static int fd_flags_allow(int); -static jsfd_t * fd_methods_args_check(JSContext *, JSObject *, const char *, - int, int, jsval *, int); - - - -/* Actual class parameters */ -static JSClass fd_class = { - "FD", JSCLASS_HAS_PRIVATE, JS_PropertyStub, JS_PropertyStub, - fd_getProperty, fd_setProperty, JS_EnumerateStub, JS_ResolveStub, - JS_ConvertStub, fd_finalize -}; - -enum fd_methods_args_enum { - FDMA_SET, - FDMA_CLOSE, - FDMA_TRUNCATE, - FDMA_GET, - FDMA_SOCKET, - FDMA_CONNECT, - FDMA_BIND, - FDMA_LISTEN, - FDMA_ACCEPT, - FDMA_SHUTDOWN, - FDMA_SETSOCKOPT, - FDMA_GETSOCKOPT, - FDMA_FCNTL, - FDMA_READ, - FDMA_WRITE, - FDMA_FDATASYNC, - FDMA_LSEEK, - FDMA_FCHMOD, - FDMA_FLOCK, - FDMA_FSTAT, - FDMA_MAX -}; - -static int fd_methods_args_array[FDMA_MAX][6] = { - { 1, JSAT_INTEGER }, /* SET */ - { 0 }, /* CLOSE */ - { 1, JSAT_DOUBLE }, /* TRUNCATE */ - { 0 }, /* GET */ - { 3, JSAT_INTEGER, JSAT_INTEGER, JSAT_INTEGER },/* SOCKET */ - { 2, JSAT_STRING, JSAT_INTEGER }, /* CONNECT */ - { 2, JSAT_STRING, JSAT_INTEGER }, /* BIND */ - { 1, JSAT_INTEGER }, /* LISTEN */ - { 0 }, /* ACCEPT */ - { 1, JSAT_INTEGER }, /* SHUTDOWN */ - { 2, JSAT_INTEGER, JSAT_INTEGER }, /* SETSOCKOPT */ - { 1, JSAT_INTEGER }, /* GETSOCKOPT */ - { 2, JSAT_INTEGER, JSAT_INTEGER }, /* FCNTL */ - { 1, JSAT_INTEGER }, /* READ */ - { 1, JSAT_STRING }, /* WRITE */ - { 0 }, /* FDATASYNC */ - { 2, JSAT_DOUBLE, JSAT_INTEGER }, /* LSEEK */ - { 1, JSAT_INTEGER }, /* FCHMOD */ - { 1, JSAT_INTEGER }, /* FLOCK */ - { 0 } /* FSTAT */ -}; - -/* Provided methods/functions */ -static JSFunctionSpec fd_methods[] = { - { "open", fd_m_open, 0, 0, 0 }, /* Variable 2-3 parameters */ - { "set", fd_m_set, 1, 0, 0 }, - { "close", fd_m_close, 0, 0, 0 }, - { "truncate", fd_m_truncate, 1, 0, 0 }, - { "put", fd_m_put, 1, 0, 0 }, - { "get", fd_m_get, 0, 0, 0 }, - { "socket", fd_m_socket, 3, 0, 0 }, - { "connect", fd_m_connect, 2, 0, 0 }, - { "bind", fd_m_bind, 2, 0, 0 }, - { "listen", fd_m_listen, 1, 0, 0 }, - { "accept", fd_m_accept, 0, 0, 0 }, - { "shutdown", fd_m_shutdown, 1, 0, 0 }, - { "setsockopt", fd_m_setsockopt, 2, 0, 0 }, - { "getsockopt", fd_m_getsockopt, 1, 0, 0 }, - { "fcntl", fd_m_fcntl, 2, 0, 0 }, - { "read", fd_m_read, 1, 0, 0 }, - { "write", fd_m_write, 1, 0, 0 }, - { "fdatasync", fd_m_fdatasync, 0, 0, 0 }, - { "lseek", fd_m_lseek, 2, 0, 0 }, - { "fchmod", fd_m_fchmod, 1, 0, 0 }, - { "flock", fd_m_flock, 1, 0, 0 }, - { "fstat", fd_m_fstat, 0, 0, 0 }, - { NULL, NULL, 0, 0, 0 } -}; - -/* Provided static methods */ -static JSFunctionSpec fd_smethods[] = { - { "poll", fd_sm_poll, 2, 0, 0 }, - { NULL, NULL, 0, 0, 0 } -}; - -/* Provided properties */ -enum fd_props { - FD_P_PATH = 0, - FD_P_FD, - FD_P_MODE, - FD_P_EVENTS, - FD_P_REVENTS, - FD_P_ERRNO, - FD_P_CLIENT_ADDR, - FD_P_CLIENT_PORT, - FD_P_MAX -}; - -static JSPropertySpec fd_properties[FD_P_MAX + 1] = { - { "path", FD_P_PATH, JSPROP_ENUMERATE | JSPROP_READONLY, NULL, NULL }, - { "fd", FD_P_FD, JSPROP_ENUMERATE | JSPROP_READONLY, NULL, NULL }, - { "mode", FD_P_MODE, JSPROP_ENUMERATE | JSPROP_READONLY, NULL, NULL }, - { "events", FD_P_EVENTS, JSPROP_ENUMERATE, NULL, NULL }, - { "revents", FD_P_REVENTS, JSPROP_ENUMERATE | JSPROP_READONLY, NULL, - NULL }, - { "errno", FD_P_ERRNO, JSPROP_ENUMERATE | JSPROP_READONLY, NULL, - NULL }, - { "client_addr", FD_P_CLIENT_ADDR, JSPROP_ENUMERATE | JSPROP_READONLY, - NULL, NULL }, - { "client_port", FD_P_CLIENT_PORT, JSPROP_ENUMERATE | JSPROP_READONLY, - NULL, NULL }, - { NULL, 0, 0, NULL, NULL } -}; - -/* - * Provided static properties. - * We use these to provide ECMAScript with the ability to use system-specific - * standard C constant macros without us having to tidiously map them - * individually, or to require other scripts to be used as headers to define - * them. Another possibility would have been to supply these parameters as - * string, but this would have required even slower remapping because of the - * parsing and string comparisions. - * We only include those which we consider necessary for now, others may be - * added easily as needed, provided that they are added in all three maps. - * I might perhaps develop macros and/or functions to map all these easily - * from a single map. - */ - -struct property_spec { - const char *name; - int value; -}; - -#define SP(n) \ - { #n, n } - -static struct property_spec fd_sprops[] = { - SP(STDIN_FILENO), - SP(STDOUT_FILENO), - SP(STDERR_FILENO), - - SP(O_RDONLY), - SP(O_WRONLY), - SP(O_RDWR), - SP(O_APPEND), - SP(O_CREAT), - SP(O_TRUNC), - SP(O_NONBLOCK), - - SP(POLLIN), - SP(POLLRDNORM), - SP(POLLRDBAND), - SP(POLLPRI), - SP(POLLOUT), - SP(POLLWRNORM), - SP(POLLWRBAND), - SP(POLLERR), - SP(POLLHUP), - SP(POLLNVAL), - - SP(SHUT_RD), - SP(SHUT_WR), - SP(SHUT_RDWR), - - SP(AF_INET), - SP(SOCK_STREAM), - SP(SOCK_DGRAM), - - SP(SO_REUSEADDR), - SP(SO_REUSEPORT), - SP(SO_KEEPALIVE), - SP(SO_DONTROUTE), - SP(SO_LINGER), - SP(SO_BROADCAST), - SP(SO_OOBINLINE), - SP(SO_SNDBUF), - SP(SO_RCVBUF), - SP(SO_SNDLOWAT), - SP(SO_RCVLOWAT), - SP(SO_SNDTIMEO), - SP(SO_RCVTIMEO), - SP(SO_TIMESTAMP), - SP(SO_TYPE), - SP(SO_ERROR), - SP(TCP_NODELAY), - - SP(F_SETFL), - SP(F_GETFL), - - SP(SEEK_SET), - SP(SEEK_CUR), - SP(SEEK_END), - - SP(S_IRWXU), - SP(S_IRUSR), - SP(S_IWUSR), - SP(S_IXUSR), - SP(S_IRWXG), - SP(S_IRGRP), - SP(S_IXGRP), - SP(S_IRWXO), - SP(S_IROTH), - SP(S_IWOTH), - SP(S_IXOTH), - SP(S_ISUID), - SP(S_ISGID), - SP(S_ISVTX), - SP(S_IFMT), - SP(S_IFIFO), - SP(S_IFCHR), - SP(S_IFDIR), - SP(S_IFBLK), - SP(S_IFREG), - SP(S_IFLNK), - SP(S_IFSOCK), - SP(S_IFWHT), - SP(UF_NODUMP), - SP(UF_IMMUTABLE), - SP(UF_APPEND), - SP(UF_OPAQUE), - SP(SF_ARCHIVED), - SP(SF_IMMUTABLE), - SP(SF_APPEND), - - SP(LOCK_SH), - SP(LOCK_EX), - SP(LOCK_NB), - SP(LOCK_UN), - - { NULL, 0 } -}; - -#undef SP - - - -/* - * Miscelaneous static globals - */ - -/* XXX Should be initialized at main process startup ideally */ -static int tcp_proto = -1; -static char *read_charbuf = NULL; -static size_t read_charbuf_size = 0; - - - -/* - * Class control functions - */ - -JSObject * -js_InitFDClass(JSContext *cx, JSObject *obj) -{ - JSObject *proto, *ctor; - struct property_spec *sp; - - if ((proto = JS_InitClass(cx, obj, NULL, &fd_class, fd_constructor, - 0, fd_properties, fd_methods, NULL, fd_smethods)) - == NULL) { - (void) fprintf(stderr, "Error initializing FD class\n"); - return NULL; - } - - /* Create static properties. Should probably be a function. */ - - if ((ctor = JS_GetConstructor(cx, proto)) == NULL) { - (void) fprintf(stderr, "FD: JS_GetConstructor == NULL\n"); - return NULL; - } - for (sp = fd_sprops; sp->name != NULL; sp++) { - if (JS_DefineProperty(cx, ctor, sp->name, - INT_TO_JSVAL(sp->value), NULL, NULL, - JSPROP_READONLY | JSPROP_PERMANENT) == JS_FALSE) { - (void) fprintf(stderr, - "FD: Error defining property %s\n", sp->name); - return NULL; - } - } - - return proto; -} - -static JSBool -fd_constructor(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - jsfd_t *jsfd = NULL; - - if (argc != 0) { - QUEUE_EXCEPTION("Wrong number of arguments"); - goto err; - } - - /* - * IMPORTANT: We must verify if the caller attempts to execute us as a - * normal function rather than as a constructor. Otherwise, the - * caller can cause the interpreter to abort(3) in an assertion in - * JS_SetPrivate()! - */ - if (!JS_IsConstructing(cx)) { - QUEUE_EXCEPTION("Constructor called as a function"); - goto err; - } - - if ((jsfd = JS_malloc(cx, sizeof(jsfd_t))) == NULL) { - QUEUE_EXCEPTION("Out of memory"); - goto err; - } - - jsfd->fd = -1; - jsfd->type = JSFD_NONE; - jsfd->events = jsfd->revents = 0; - jsfd->error = 0; - - if (!JS_SetPrivate(cx, obj, jsfd)) { - QUEUE_EXCEPTION("Internal error!"); - goto err; - } - - return JS_TRUE; - -err: - if (jsfd != NULL) - JS_free(cx, jsfd); - - *rval = OBJECT_TO_JSVAL(NULL); - return JS_FALSE; -} - -static void -fd_finalize(JSContext *cx, JSObject *obj) -{ - jsfd_t *jsfd; - - if ((jsfd = JS_GetInstancePrivate(cx, obj, &fd_class, NULL)) != NULL) { - /* Only close if not one of std descriptors */ - if (jsfd->fd != -1 && jsfd->type != JSFD_STD) { - (void) close(jsfd->fd); - jsfd->fd = -1; - jsfd->type = JSFD_NONE; - } - if (jsfd->type == JSFD_FILE && jsfd->u.file.path != NULL) { - JS_free(cx, jsfd->u.file.path); - jsfd->u.file.path = NULL; - } - JS_free(cx, jsfd); - (void) JS_SetPrivate(cx, obj, NULL); - } -} - - -/* - * Property functions - */ - -static JSBool -fd_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) -{ - jsfd_t *jsfd; - jsint p; - - if (!JSVAL_IS_INT(id)) - return JS_TRUE; - p = (int)JSVAL_TO_INT(id); - - if ((jsfd = JS_GetInstancePrivate(cx, obj, &fd_class, NULL)) == NULL) - return JS_TRUE; - if (jsfd->fd == -1) - return JS_TRUE; - - switch (p) { - case FD_P_PATH: - if (jsfd->type == JSFD_FILE) { - JSString *string; - - if ((string = JS_NewStringCopyZ(cx, - jsfd->u.file.path)) == NULL) { - QUEUE_EXCEPTION("Out of memory"); - return JS_FALSE; - } - *vp = STRING_TO_JSVAL(string); - } - break; - case FD_P_FD: - *vp = INT_TO_JSVAL(jsfd->fd); - break; - case FD_P_MODE: - if (jsfd->type == JSFD_FILE) - *vp = INT_TO_JSVAL((int)jsfd->u.file.mode); - break; - case FD_P_EVENTS: - *vp = INT_TO_JSVAL((int)jsfd->events); - break; - case FD_P_REVENTS: - *vp = INT_TO_JSVAL((int)jsfd->revents); - break; - case FD_P_ERRNO: - *vp = INT_TO_JSVAL(jsfd->error); - break; - case FD_P_CLIENT_ADDR: - if (jsfd->type == JSFD_SOCKET) { - char addr[16]; - JSString *string; - - if (inet_ntop(AF_INET, &jsfd->u.socket.caddr.sin_addr, - addr, 15) == NULL) { - QUEUE_EXCEPTION(strerror(errno)); - return JS_FALSE; - } - if ((string = JS_NewStringCopyZ(cx, addr)) == NULL) { - QUEUE_EXCEPTION("Out of memory"); - return JS_FALSE; - } - *vp = STRING_TO_JSVAL(string); - } - break; - case FD_P_CLIENT_PORT: - if (jsfd->type == JSFD_SOCKET) { - *vp = INT_TO_JSVAL((int)ntohs(jsfd-> - u.socket.caddr.sin_port)); - } - break; - } - - return JS_TRUE; -} - -static JSBool -fd_setProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) -{ - jsfd_t *jsfd; - jsint p; - - if (!JSVAL_IS_INT(id)) - return JS_TRUE; - p = (int)JSVAL_TO_INT(id); - - if ((jsfd = JS_GetInstancePrivate(cx, obj, &fd_class, NULL)) == NULL) - return JS_TRUE; - if (jsfd->fd == -1) - return JS_TRUE; - - switch (p) { - case FD_P_EVENTS: - if (!JSVAL_IS_INT(*vp)) { - QUEUE_EXCEPTION( - "FD_P_EVENTS property requires an int"); - return JS_FALSE; - } - jsfd->events = (short)JSVAL_TO_INT(*vp); - break; - } - - return JS_TRUE; -} - - -/* - * Static properties functions - */ - - -/* - * Method functions - */ - -static JSBool -fd_m_open(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - jsfd_t *jsfd; - int fd, flags; - mode_t mode = 0644; - char *bytes; - - *rval = OBJECT_TO_JSVAL(NULL); - - /* - * We use custom arguments checking here since we can accept both - * 2 or 3. - */ - if (argc < 2 || argc > 3) { - QUEUE_EXCEPTION("Wrong number of arguments"); - return JS_FALSE; - } - if (!JSVAL_IS_STRING(argv[0])) { - QUEUE_EXCEPTION("First argument must be a string"); - return JS_FALSE; - } - if (!JSVAL_IS_INT(argv[1])) { - QUEUE_EXCEPTION("Second argument must be an integer"); - return JS_FALSE; - } - if (argc == 3 && !JSVAL_IS_INT(argv[2])) { - QUEUE_EXCEPTION("Third argument must be an integer"); - return JS_FALSE; - } - if ((jsfd = JS_GetInstancePrivate(cx, obj, &fd_class, NULL)) == NULL) { - QUEUE_EXCEPTION("Null private data!"); - return JS_FALSE; - } - if (jsfd->type != JSFD_NONE) { - QUEUE_EXCEPTION("Descriptor already open"); - return JS_FALSE; - } - - if (argc == 3) { - /* - * Mode, supplied as an int. - */ - mode = (mode_t)JSVAL_TO_INT(argv[2]); - if ((mode = fd_mode_allow(mode)) == (mode_t)-1) { - QUEUE_EXCEPTION("Mode not permitted"); - return JS_FALSE; - } - } - - /* - * Flags, provided as an int. - */ - flags = JSVAL_TO_INT(argv[1]); - if ((flags = fd_flags_allow(flags)) == -1) { - QUEUE_EXCEPTION("Flag not permitted"); - return JS_FALSE; - } - - /* Path, provided as a string */ - if ((bytes = JS_GetStringBytes(JSVAL_TO_STRING(argv[0]))) == NULL) { - QUEUE_EXCEPTION("Internal error"); - return JS_FALSE; - } - /* Perform path sanity checking */ - if (fd_path_allow(bytes) == -1) { - QUEUE_EXCEPTION("Invalid path"); - return JS_FALSE; - } - if ((jsfd->u.file.path = JS_strdup(cx, bytes)) == NULL) { - QUEUE_EXCEPTION("Out of memory"); - return JS_FALSE; - } - - /* We can finally attempt to open(2) */ - if ((fd = open(bytes, flags, mode)) == -1) { - jsfd->error = errno; - /* - * XXX strerror() seems to always need to load up the - * nls table file, which is way silly for performance. - * This is related to locale stuff, and should be able - * to simply be disabled, even. - * Since this event occurs often in httpd.js, let's just - * output a fixed string for now. - * I should actually fix NetBSD libc on this matter. - */ -/* QUEUE_EXCEPTION(strerror(errno)); */ - QUEUE_EXCEPTION("open() error"); - JS_free(cx, jsfd->u.file.path); - return JS_FALSE; - } - - /* Success! */ - jsfd->fd = fd; - jsfd->type = JSFD_FILE; - jsfd->u.file.flags = flags; - jsfd->u.file.mode = mode; - - return JS_TRUE; -} - -static JSBool -fd_m_set(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - int32_t fd; - jsfd_t *jsfd; - - *rval = OBJECT_TO_JSVAL(NULL); - - if ((jsfd = fd_methods_args_check(cx, obj, "set", FDMA_SET, argc, - argv, JSFD_NONE)) == NULL) - return JS_FALSE; - - fd = JSVAL_TO_INT(*argv); - if (fd < STDIN_FILENO || fd > STDERR_FILENO) { - QUEUE_EXCEPTION("Unknown standard descriptor"); - return JS_FALSE; - } - jsfd->fd = fd; - jsfd->type = JSFD_STD; - - return JS_TRUE; -} - -static JSBool -fd_m_close(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - jsfd_t *jsfd; - int error; - - *rval = OBJECT_TO_JSVAL(NULL); - - if ((jsfd = fd_methods_args_check(cx, obj, "close", FDMA_CLOSE, argc, - argv, JSFD_STD | JSFD_FILE | JSFD_SOCKET)) == NULL) - return JS_FALSE; - - if (jsfd->type == JSFD_STD) { - jsfd->fd = -1; - jsfd->type = JSFD_NONE; - return JS_TRUE; - } - - if (jsfd->type == JSFD_FILE) { - if (jsfd->u.file.path != NULL) { - JS_free(cx, jsfd->u.file.path); - jsfd->u.file.path = NULL; - } - } - - error = close(jsfd->fd); - jsfd->fd = -1; - jsfd->type = JSFD_NONE; - - if (error == -1) { - jsfd->error = errno; - QUEUE_EXCEPTION(strerror(errno)); - return JS_FALSE; - } - - return JS_TRUE; -} - -static JSBool -fd_m_truncate(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - jsfd_t *jsfd; - off_t size; - jsdouble dsize; - - *rval = OBJECT_TO_JSVAL(NULL); - - if ((jsfd = fd_methods_args_check(cx, obj, "truncate", FDMA_TRUNCATE, - argc, argv, JSFD_FILE)) == NULL) - return JS_FALSE; - - if (!JS_ValueToNumber(cx, *argv, &dsize)) { - QUEUE_EXCEPTION("Internal error"); - return JS_FALSE; - } - size = (off_t)dsize; - - if (ftruncate(jsfd->fd, size) == -1) { - jsfd->error = errno; - QUEUE_EXCEPTION(strerror(errno)); - return JS_FALSE; - } - - return JS_TRUE; -} - -static JSBool -fd_m_put(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - jsfd_t *jsfd; - JSString *str; - char *bytes; - ssize_t size; - - *rval = OBJECT_TO_JSVAL(NULL); - - if (argc != 1) { - QUEUE_EXCEPTION("Wrong number of arguments"); - return JS_FALSE; - } - - if ((jsfd = JS_GetInstancePrivate(cx, obj, &fd_class, NULL)) == NULL) { - QUEUE_EXCEPTION("Null private data!"); - return JS_FALSE; - } - - if (jsfd->fd == -1) { - QUEUE_EXCEPTION("Descriptor closed"); - return JS_FALSE; - } - - /* - * Instead of verifying if supplied value really is a JSString, and - * using JSVAL_TO_STRING(), we convert the value to a string in this - * case. - */ - if ((str = JS_ValueToString(cx, *argv)) == NULL || - (bytes = JS_GetStringBytes(str)) == NULL) { - QUEUE_EXCEPTION("Internal error"); - return JS_FALSE; - } - - size = strlen(bytes); - if ((size = write(jsfd->fd, bytes, size)) == -1) { - jsfd->error = errno; - QUEUE_EXCEPTION(strerror(errno)); - return JS_FALSE; - } - - *rval = INT_TO_JSVAL((int)size); - - return JS_TRUE; -} - -static JSBool -fd_m_get(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - jsfd_t *jsfd; - char bytes[4096]; - ssize_t size; - JSString *string; - - *rval = OBJECT_TO_JSVAL(NULL); - - if ((jsfd = fd_methods_args_check(cx, obj, "get", FDMA_GET, argc, - argv, JSFD_STD | JSFD_FILE | JSFD_SOCKET)) == NULL) - return JS_FALSE; - - if ((size = read(jsfd->fd, bytes, 4096)) == -1) { - jsfd->error = errno; - QUEUE_EXCEPTION(strerror(errno)); - return JS_FALSE; - } - if (size == 0) - return JS_TRUE; - - if ((string = JS_NewStringCopyN(cx, bytes, size)) == NULL) { - QUEUE_EXCEPTION("Out of memory"); - return JS_FALSE; - } - *rval = STRING_TO_JSVAL(string); - - return JS_TRUE; -} - -static JSBool -fd_m_socket(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - int domain, type, protocol, error; - jsfd_t *jsfd; - - *rval = OBJECT_TO_JSVAL(NULL); - - if ((jsfd = fd_methods_args_check(cx, obj, "socket", FDMA_SOCKET, - argc, argv, JSFD_NONE)) == NULL) - return JS_FALSE; - - domain = (int)JSVAL_TO_INT(argv[0]); - type = (int)JSVAL_TO_INT(argv[1]); - protocol = (int)JSVAL_TO_INT(argv[2]); - - /* Sanity checking on currently supported protocols */ - if (domain != AF_INET || (type != SOCK_DGRAM && type != SOCK_STREAM) - || protocol != 0) { - QUEUE_EXCEPTION("Unsupported protocol"); - return JS_FALSE; - } - - if ((error = socket(domain, type, protocol)) == -1) { - jsfd->error = errno; - QUEUE_EXCEPTION(strerror(errno)); - return JS_FALSE; - } - - jsfd->fd = error; - jsfd->type = JSFD_SOCKET; - jsfd->u.socket.domain = domain; - jsfd->u.socket.type = type; - jsfd->u.socket.protocol = protocol; - - return JS_TRUE; -} - -/* - * We currently make this rather simple; If the supplied string doesn't - * consist of a valid IPv4 address, we simply attempt to resolve it, and on - * success then attempt connection. - */ -static JSBool -fd_m_connect(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - jsfd_t *jsfd; - char *address; - struct sockaddr_in sinaddr; - - *rval = OBJECT_TO_JSVAL(NULL); - - if ((jsfd = fd_methods_args_check(cx, obj, "connect", FDMA_CONNECT, - argc, argv, JSFD_SOCKET)) == NULL) - return JS_FALSE; - - address = JS_GetStringBytes(JSVAL_TO_STRING(argv[0])); - if (inet_pton(AF_INET, address, &sinaddr.sin_addr) != 1) { - struct hostent *h; - - /* - * Not a valid IPv4 address, consider it as a hostname and - * attempt to resolve it. - * XXX Note: Not thread safe unless a global mutex/rwlock is - * used. Should use getaddrinfo(3) instead. Especially if we - * someday want to support other address families than - * AF_INET. - */ - if ((h = gethostbyname(address)) == NULL) { - jsfd->error = errno; - QUEUE_EXCEPTION("Invalid address or hostname"); - return JS_FALSE; - } - sinaddr.sin_addr.s_addr = - ((struct in_addr *)h->h_addr_list[0])->s_addr; - } - sinaddr.sin_port = htons((in_port_t)JSVAL_TO_INT(argv[1])); - - if (connect(jsfd->fd, (struct sockaddr *)&sinaddr, - sizeof(struct sockaddr_in)) == -1) { - jsfd->error = errno; - QUEUE_EXCEPTION(strerror(errno)); - return JS_FALSE; - } - - return JS_TRUE; -} - -static JSBool -fd_m_bind(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - jsfd_t *jsfd; - struct sockaddr_in sinaddr; - char *address; - - *rval = OBJECT_TO_JSVAL(NULL); - - if ((jsfd = fd_methods_args_check(cx, obj, "bind", FDMA_BIND, argc, - argv, JSFD_SOCKET)) == NULL) - return JS_FALSE; - - address = JS_GetStringBytes(JSVAL_TO_STRING(argv[0])); - if (inet_pton(AF_INET, address, &sinaddr.sin_addr) != 1) { - QUEUE_EXCEPTION("Invalid IP address"); - return JS_FALSE; - } - sinaddr.sin_port = htons((in_port_t)JSVAL_TO_INT(argv[1])); - - if (bind(jsfd->fd, (struct sockaddr *)&sinaddr, - sizeof(struct sockaddr_in)) == -1) { - jsfd->error = errno; - QUEUE_EXCEPTION(strerror(errno)); - return JS_FALSE; - } - - return JS_TRUE; -} - -static JSBool -fd_m_listen(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - jsfd_t *jsfd; - - *rval = OBJECT_TO_JSVAL(NULL); - - if ((jsfd = fd_methods_args_check(cx, obj, "listen", FDMA_LISTEN, - argc, argv, JSFD_SOCKET)) == NULL) - return JS_FALSE; - - if (listen(jsfd->fd, (int)JSVAL_TO_INT(*argv)) == -1) { - jsfd->error = errno; - QUEUE_EXCEPTION(strerror(errno)); - return JS_FALSE; - } - - return JS_TRUE; -} - -static JSBool -fd_m_accept(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - jsfd_t *jsfd, *njsfd; - int sock; - struct sockaddr_in sinaddr; - socklen_t socklen; - JSObject *nobj; - - *rval = OBJECT_TO_JSVAL(NULL); - - if ((jsfd = fd_methods_args_check(cx, obj, "accept", FDMA_ACCEPT, - argc, argv, JSFD_SOCKET)) == NULL) - return JS_FALSE; - - socklen = sizeof(struct sockaddr_in); - if ((sock = accept(jsfd->fd, (struct sockaddr *)&sinaddr, &socklen)) - == -1) { - jsfd->error = errno; - QUEUE_EXCEPTION(strerror(errno)); - return JS_FALSE; - } - - /* - * Success, create new FD object, fill it and return it. - */ - if ((nobj = JS_ConstructObject(cx, &fd_class, obj, obj)) == NULL) { - (void) close(sock); - QUEUE_EXCEPTION("Out of resources"); - return JS_FALSE; - } - njsfd = JS_GetInstancePrivate(cx, nobj, &fd_class, NULL); - njsfd->fd = sock; - njsfd->type = JSFD_SOCKET; - njsfd->u.socket.domain = jsfd->u.socket.domain; - njsfd->u.socket.type = jsfd->u.socket.type; - njsfd->u.socket.protocol = jsfd->u.socket.protocol; - (void) memcpy(&njsfd->u.socket.caddr, &sinaddr, - sizeof(struct sockaddr_in)); - - *rval = OBJECT_TO_JSVAL(nobj); - - return JS_TRUE; -} - -static JSBool -fd_m_shutdown(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - jsfd_t *jsfd; - - *rval = OBJECT_TO_JSVAL(NULL); - - if ((jsfd = fd_methods_args_check(cx, obj, "shutdown", FDMA_SHUTDOWN, - argc, argv, JSFD_SOCKET)) == NULL) - return JS_FALSE; - - if (shutdown(jsfd->fd, (int)JSVAL_TO_INT(*argv)) == -1) { - jsfd->error = errno; - QUEUE_EXCEPTION(strerror(errno)); - return JS_FALSE; - } - - return JS_TRUE; -} - -/* - * XXX Not thread safe ATM as it uses a static int with test-and-set operation - * to only query the protocol database once. This could be done at early - * process initialization alternatively. - * Unlike BSD/POSIX setsockopt(2), always requires a single integer value (-1 - * in the case of SO_LINGER to disable it, or the number of seconds to - * linger to enable it). - */ -static JSBool -fd_m_setsockopt(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - jsfd_t *jsfd; - void *opt; - struct linger l; - int optname, level, optval; - socklen_t optlen; - - *rval = OBJECT_TO_JSVAL(NULL); - - if ((jsfd = fd_methods_args_check(cx, obj, "setsockopt", - FDMA_SETSOCKOPT, argc, argv, JSFD_SOCKET)) == NULL) - return JS_FALSE; - - optname = JSVAL_TO_INT(argv[0]); - optval = JSVAL_TO_INT(argv[1]); - - /* - * Work out special case for SO_LINGER - */ - if (optname == SO_LINGER) { - if (optval == -1) { - l.l_onoff = 0; - l.l_linger = 0; - } else { - l.l_onoff = 1; - l.l_linger = optval; - } - opt = &l; - optlen = sizeof(struct linger); - } else { - opt = &optval; - optlen = sizeof(int); - optval = (optval != 0 ? 1 : 0); - } - - /* - * And for TCP_NODELAY which must use tcp_proto as level - */ - if (optname == TCP_NODELAY) { - if (tcp_proto == -1) { - struct protoent *pent; - - if ((pent = getprotobyname("TCP")) != NULL) - tcp_proto = pent->p_proto; - else - tcp_proto = 4; /* Generally allright */ - } - level = tcp_proto; - } else - level = SOL_SOCKET; - - if (setsockopt(jsfd->fd, level, optname, opt, optlen) == -1) { - jsfd->error = errno; - QUEUE_EXCEPTION(strerror(errno)); - return JS_FALSE; - } - - return JS_TRUE; -} - -/* - * XXX Not thread safe ATM as it uses a static int with test-and-set operation - * to only query the protocol database once. This could be done at early - * process initialization alternatively. - * Unlike BSD/POSIX getsockopt(2), always returns a single integer value (-1 - * in the case of SO_LINGER disabled, or the number of seconds assigned to - * wait if enabled). - */ -static JSBool -fd_m_getsockopt(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - jsfd_t *jsfd; - void *opt; - struct linger l; - int i, optname, level, result; - socklen_t optlen; - - *rval = OBJECT_TO_JSVAL(NULL); - - if ((jsfd = fd_methods_args_check(cx, obj, "getsockopt", - FDMA_GETSOCKOPT, argc, argv, JSFD_SOCKET)) == NULL) - return JS_FALSE; - - optname = JSVAL_TO_INT(*argv); - - /* - * Special case for SO_LINGER which expects a structure rather than an - * integer - */ - if (optname == SO_LINGER) { - opt = &l; - optlen = sizeof(struct linger); - } else { - opt = &i; - optlen = sizeof(int); - } - - /* - * And for TCP_NODELAY which must use TCP protocol number rather than - * SOL_SOCKET level - */ - if (optname == TCP_NODELAY) { - if (tcp_proto == -1) { - struct protoent *pent; - - if ((pent = getprotobyname("TCP")) != NULL) - tcp_proto = pent->p_proto; - else - tcp_proto = 4; /* Generally allright */ - } - level = tcp_proto; - } else - level = SOL_SOCKET; - - if (getsockopt(jsfd->fd, level, optname, opt, &optlen) == -1) { - jsfd->error = errno; - QUEUE_EXCEPTION(strerror(errno)); - return JS_FALSE; - } - - /* - * To simplify the implementation, special case of SO_LINGER result; - * We return -1 if lingering is disabled, or otherwise return the - * number of seconds it should linger for maximum. - */ - if (optname == SO_LINGER) { - if (l.l_onoff != 0) - result = l.l_linger; - else - result = -1; - } else - /* - * These are booleans, so ensure proper return value despite - * several implementations which return a mask rather than 0/1 - */ - result = (i != 0 ? 1 : 0); - - *rval = INT_TO_JSVAL(result); - - return JS_TRUE; -} - -/* - * Unlike POSIX fcntl(2), only currently supports F_GETFL and F_SETFL along - * with O_NONBLOCK and O_APPEND. The flags argument is also mandatory, which - * will serve as a result mask for F_GETFL or to set wanted flags using - * F_SETFL. The previous flags are returned as usual (but will only ever - * include 0, O_NONBLOCK and/or O_APPEND). - */ -static JSBool -fd_m_fcntl(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - jsfd_t *jsfd; - int cmd, flags, error; - - *rval = INT_TO_JSVAL(0); - - if ((jsfd = fd_methods_args_check(cx, obj, "fcntl", FDMA_FCNTL, - argc, argv, JSFD_FILE | JSFD_SOCKET)) == NULL) - return JS_FALSE; - - cmd = JSVAL_TO_INT(argv[0]); - flags = JSVAL_TO_INT(argv[1]); - - if (cmd != F_GETFL && cmd != F_SETFL) { - QUEUE_EXCEPTION("Unimplemented fcntl() command"); - return JS_FALSE; - } - flags &= (O_NONBLOCK | O_APPEND); - if ((flags & O_NONBLOCK) == 0 && (flags & O_APPEND) == 0) { - QUEUE_EXCEPTION("Unimplemented fcntl() flag"); - return JS_FALSE; - } - - if (cmd == F_GETFL) { - if ((error = fcntl(jsfd->fd, cmd, NULL)) == -1) { - jsfd->error = errno; - QUEUE_EXCEPTION(strerror(errno)); - return JS_FALSE; - } - error &= flags; - } else { - if ((error = fcntl(jsfd->fd, cmd, flags)) == -1) { - jsfd->error = errno; - QUEUE_EXCEPTION(strerror(errno)); - return JS_FALSE; - } - error &= (O_NONBLOCK | O_APPEND); - } - - *rval = INT_TO_JSVAL(error); - - return JS_TRUE; -} - -static JSBool -fd_m_read(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - jsfd_t *jsfd; - size_t size; - ssize_t rsize; - JSString *string; - - *rval = OBJECT_TO_JSVAL(NULL); - - if ((jsfd = fd_methods_args_check(cx, obj, "read", FDMA_READ, - argc, argv, JSFD_FILE | JSFD_SOCKET)) == NULL) - return JS_FALSE; - - size = (size_t)JSVAL_TO_INT(*argv); - if (size < 1) { - QUEUE_EXCEPTION("read() requested size smaller than 1"); - return JS_FALSE; - } - - /* - * Ensure that our read buffer is ready, and of a large enough size - * to accomodate read. - */ - if (read_charbuf_size < size) { - if (read_charbuf == NULL) { - /* Never allocated yet, simply allocate */ - if ((read_charbuf = malloc(size)) == NULL) { - QUEUE_EXCEPTION("Cannot allocate read buffer"); - return JS_FALSE; - } - } else { - char *ptr; - - /* Buffer too small, attempt to increase it */ - if ((ptr = realloc(read_charbuf, size)) == NULL) { - QUEUE_EXCEPTION( - "Cannot reallocate read buffer"); - return JS_FALSE; - } - read_charbuf = ptr; - } - read_charbuf_size = size; - } - - if ((rsize = read(jsfd->fd, read_charbuf, size)) == -1) { - /* - * XXX Should we really throw an exception, or simply return - * an error? For instance, if using nonblocking mode and - * expecting EAGAIN, would using an exception clubber - * unnecessarily the code? - */ - jsfd->error = errno; - QUEUE_EXCEPTION(strerror(errno)); - return JS_FALSE; - } - if (size == 0) - return JS_TRUE; - - if ((string = JS_NewStringCopyN(cx, read_charbuf, rsize)) == NULL) { - QUEUE_EXCEPTION("Couldn't allocate read result string"); - return JS_FALSE; - } - *rval = STRING_TO_JSVAL(string); - - return JS_TRUE; -} - -static JSBool -fd_m_write(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - jsfd_t *jsfd; - ssize_t rsize; - JSString *str; - char *bytes; - - *rval = OBJECT_TO_JSVAL(NULL); - - if ((jsfd = fd_methods_args_check(cx, obj, "write", FDMA_WRITE, - argc, argv, JSFD_FILE | JSFD_SOCKET)) == NULL) - return JS_FALSE; - - str = JSVAL_TO_STRING(*argv); - if ((bytes = JS_GetStringBytes(str)) == NULL) { - QUEUE_EXCEPTION("Internal error"); - return JS_FALSE; - } - - if ((rsize = write(jsfd->fd, bytes, JS_GetStringLength(str))) == -1) { - jsfd->error = errno; - QUEUE_EXCEPTION(strerror(errno)); - return JS_FALSE; - } - *rval = INT_TO_JSVAL((int)rsize); - - return JS_TRUE; -} - -static JSBool -fd_m_fdatasync(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - jsfd_t *jsfd; - - *rval = OBJECT_TO_JSVAL(NULL); - - if ((jsfd = fd_methods_args_check(cx, obj, "fdatasync", - FDMA_FDATASYNC, argc, argv, JSFD_FILE)) == NULL) - return JS_FALSE; - - if (fdatasync(jsfd->fd) == -1) { - jsfd->error = errno; - QUEUE_EXCEPTION(strerror(errno)); - return JS_FALSE; - } - - return JS_TRUE; -} - -static JSBool -fd_m_lseek(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - jsfd_t *jsfd; - off_t off, newoff; - int whence; - jsdouble doff; - - *rval = OBJECT_TO_JSVAL(NULL); - - if ((jsfd = fd_methods_args_check(cx, obj, "lseek", FDMA_LSEEK, - argc, argv, JSFD_FILE)) == NULL) - return JS_FALSE; - - if (!JS_ValueToNumber(cx, argv[0], &doff)) { - QUEUE_EXCEPTION("Internal error"); - return JS_FALSE; - } - off = (off_t)doff; - whence = JSVAL_TO_INT(argv[1]); - - if ((newoff = lseek(jsfd->fd, off, whence)) == -1) { - jsfd->error = errno; - QUEUE_EXCEPTION(strerror(errno)); - return JS_FALSE; - } - - if (!JS_NewDoubleValue(cx, (jsdouble)newoff, rval)) { - QUEUE_EXCEPTION("Internal error"); - return JS_FALSE; - } - - return JS_TRUE; -} - -static JSBool -fd_m_fchmod(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - jsfd_t *jsfd; - mode_t mode; - - *rval = OBJECT_TO_JSVAL(NULL); - - if ((jsfd = fd_methods_args_check(cx, obj, "fchmod", FDMA_FCHMOD, - argc, argv, JSFD_FILE)) == NULL) - return JS_FALSE; - - mode = (mode_t)JSVAL_TO_INT(*argv); - if ((mode = fd_mode_allow(mode)) == (mode_t)-1) { - QUEUE_EXCEPTION("Mode not permitted"); - return JS_FALSE; - } - - if (fchmod(jsfd->fd, mode) == -1) { - jsfd->error = errno; - QUEUE_EXCEPTION(strerror(errno)); - return JS_FALSE; - } - - return JS_TRUE; -} - -static JSBool -fd_m_flock(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - jsfd_t *jsfd; - int op; - - *rval = OBJECT_TO_JSVAL(NULL); - - if ((jsfd = fd_methods_args_check(cx, obj, "flock", FDMA_FLOCK, - argc, argv, JSFD_FILE)) == NULL) - return JS_FALSE; - - op = JSVAL_TO_INT(*argv); - if (flock(jsfd->fd, op) == -1) { - jsfd->error = errno; - QUEUE_EXCEPTION(strerror(errno)); - return JS_FALSE; - } - - return JS_TRUE; -} - -static JSBool -fd_m_fstat(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - jsfd_t *jsfd; - struct stat st; - JSObject *array = NULL; - jsval val; - - *rval = OBJECT_TO_JSVAL(NULL); - - if ((jsfd = fd_methods_args_check(cx, obj, "fstat", FDMA_FSTAT, - argc, argv, JSFD_FILE)) == NULL) - return JS_FALSE; - - if (fstat(jsfd->fd, &st) == -1) { - jsfd->error = errno; - QUEUE_EXCEPTION(strerror(errno)); - return JS_FALSE; - } - - /* - * Note: We immediately link newly created objects to avoid GC - * problems. For the simplicity of this task we don't need an - * additional root to be created using JS_AddRoot(), since *rval - * is already rooted. Moreover, the double objects we create are - * immediately added as propery as well. - */ - - if ((array = JS_NewObject(cx, NULL, NULL, NULL)) == NULL) { - QUEUE_EXCEPTION("Out of memory"); - goto err; - } - *rval = OBJECT_TO_JSVAL(array); - -#define DEFINE_INT_PROP(n, i) do { \ - val = INT_TO_JSVAL((int)(i)); \ - if (!JS_DefineProperty(cx, array, (n), val, NULL, NULL, \ - JSPROP_ENUMERATE)) { \ - QUEUE_EXCEPTION("Internal error!"); \ - goto err; \ - } \ -} while (/* CONSTCOND */0) - -#define DEFINE_DOUBLE_PROP(n, d) do { \ - if (!JS_NewDoubleValue(cx, (jsdouble)(d), &val)) \ - goto err; \ - if (!JS_DefineProperty(cx, array, (n), val, NULL, NULL, \ - JSPROP_ENUMERATE)) { \ - QUEUE_EXCEPTION("Internal error!"); \ - goto err; \ - } \ -} while (/* CONSTCOND */0) - - DEFINE_INT_PROP("st_dev", st.st_dev); - DEFINE_INT_PROP("st_ino", st.st_ino); - DEFINE_INT_PROP("st_mode", st.st_mode); - DEFINE_INT_PROP("st_nlink", st.st_nlink); - DEFINE_INT_PROP("st_uid", st.st_uid); - DEFINE_INT_PROP("st_gid", st.st_gid); - DEFINE_INT_PROP("st_rdev", st.st_rdev); - DEFINE_DOUBLE_PROP("st_atime", st.st_atime); - DEFINE_DOUBLE_PROP("st_mtime", st.st_mtime); - DEFINE_DOUBLE_PROP("st_ctime", st.st_ctime); - DEFINE_DOUBLE_PROP("st_size", st.st_size); - DEFINE_DOUBLE_PROP("st_blocks", st.st_blocks); - DEFINE_INT_PROP("st_blksize", st.st_blksize); - DEFINE_INT_PROP("st_flags", st.st_flags); - DEFINE_INT_PROP("st_gen", st.st_gen); - -#undef DEFINE_INT_PROP -#undef DEFINE_DOUBLE_PROP - - return JS_TRUE; - -err: - *rval = OBJECT_TO_JSVAL(NULL); - - return JS_FALSE; -} - - - -/* - * Static methods - */ - -static JSBool -fd_sm_poll(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - struct poll_fds fds; - int nfds, timeout, i; - JSObject *array = NULL; - jsint index; - - *rval = OBJECT_TO_JSVAL(NULL); - - if (argc != 2) { - QUEUE_EXCEPTION("Wrong number of arguments"); - return JS_FALSE; - } - - /* - * First make sure that user supplied object really consists of an - * array. - */ - if (!JSVAL_IS_OBJECT(argv[0]) || - !JS_IsArrayObject(cx, JSVAL_TO_OBJECT(argv[0]))) { - QUEUE_EXCEPTION("First argument must be Array object"); - return JS_FALSE; - } - - /* - * Obtain timeout from argv[1] - */ - if (!JSVAL_IS_INT(argv[1])) { - QUEUE_EXCEPTION("Second argument must be timeout integer"); - return JS_FALSE; - } - timeout = (int)JSVAL_TO_INT(argv[1]); - - /* - * Create our pollfd array, iterating through all FD objects of the - * user-provided array object. - */ - if ((fds.entries = malloc(sizeof(struct pollfd) * 16)) == NULL) { - QUEUE_EXCEPTION("Out of memory"); - goto err; - } - if ((fds.info = malloc(sizeof(struct poll_fdsi) * 16)) == NULL) { - QUEUE_EXCEPTION("Out of memory"); - goto err; - } - fds.count = 0; - fds.size = 16; - if (!object_iterate(cx, JSVAL_TO_OBJECT(argv[0]), &fds, - fd_sm_poll_mkset)) - goto err; - - /* - * Finally perform actual polling - */ - if ((nfds = poll(fds.entries, fds.count, timeout)) == -1) { - QUEUE_EXCEPTION(strerror(errno)); - goto err; - } - - /* - * Now set FD objects event field and create custom array object to - * return to the caller, only holding entries for which events - * occurred. - * Link object immediately to avoid GC problems or needing - * JS_AddRoot(). - */ - if ((array = JS_NewArrayObject(cx, 0, NULL)) == NULL) { - QUEUE_EXCEPTION("Out of memory"); - goto err; - } - *rval = OBJECT_TO_JSVAL(array); - - for (i = 0, index = 0; i < fds.count && nfds != 0; i++) { - if (fds.entries[i].revents != 0) { - nfds--; - fds.info[i].jsfd->revents = fds.entries[i].revents; - /* - * Add an element if numeric index entry, or a - * property if name based/associative entry. - */ - if (fds.info[i].name == NULL) { - if (!JS_DefineElement(cx, array, index++, - fds.info[i].fdobj, NULL, NULL, - JSPROP_ENUMERATE)) { - QUEUE_EXCEPTION("Internal error!"); - goto err; - } - } else { - if (!JS_DefineProperty(cx, array, - fds.info[i].name, fds.info[i].fdobj, NULL, - NULL, JSPROP_ENUMERATE)) { - QUEUE_EXCEPTION("Internal error!"); - goto err; - } - } - } - } - - free(fds.entries); - free(fds.info); - - return JS_TRUE; - -err: - *rval = OBJECT_TO_JSVAL(NULL); - if (fds.entries != NULL) - free(fds.entries); - if (fds.info != NULL) - free(fds.info); - - return JS_FALSE; -} - -static JSBool -fd_sm_poll_mkset(JSContext *cx, jsval *id, jsval *val, void *udata) -{ - struct poll_fds *fds = (struct poll_fds *)udata; - JSObject *o; - jsfd_t *jsfd; - - if (!JSVAL_IS_OBJECT(*val) || - !JS_InstanceOf(cx, (o = JSVAL_TO_OBJECT(*val)), &fd_class, NULL)) { - QUEUE_EXCEPTION("Not FD object"); - return JS_FALSE; - } - if ((jsfd = JS_GetInstancePrivate(cx, o, &fd_class, NULL)) == NULL) { - QUEUE_EXCEPTION("Null private data!"); - return JS_FALSE; - } - - if (fds->count == fds->size) { - void *ptr; - - /* Need to grow entries and names */ - if ((ptr = realloc(fds->entries, - sizeof(struct pollfd) * fds->size * 2)) == NULL) { - QUEUE_EXCEPTION("Out of memory"); - return JS_FALSE; - } - fds->entries = ptr; - if ((ptr = realloc(fds->info, - sizeof(struct poll_fdsi) * fds->size * 2)) == NULL) { - QUEUE_EXCEPTION("Out of memory"); - return JS_FALSE; - } - fds->info = ptr; - fds->size *= 2; - } - - /* - * Add new entry. If it's a property (associative array entry), also - * fill in the name pointer which will be used to recreate the result - * array with those names as well. We set the name to NULL for index - * based array entries. - */ - fds->entries[fds->count].fd = jsfd->fd; - fds->entries[fds->count].events = jsfd->events; - fds->entries[fds->count].revents = 0; - if (JSVAL_IS_STRING(*id)) - fds->info[fds->count].name = - JS_GetStringBytes(JSVAL_TO_STRING(*id)); - else - fds->info[fds->count].name = NULL; - fds->info[fds->count].fdobj = *val; - fds->info[fds->count++].jsfd = jsfd; - - return JS_TRUE; -} - - -/* - * Utility functions - */ - -/* - * Was written to be able to iterate over all elements of an array object, - * despite being an associated array or not, or a mix of both. Unfortunately - * uses marked as private JSIdArray structure. - * This was needed because arrays are using indexes, while associative arrays - * are nothing more than an object with its properties. This function can - * deal with both. - */ -static JSBool -object_iterate(JSContext *cx, JSObject *obj, void *udata, - JSBool (*func)(JSContext *, jsval *, jsval *, void *)) -{ - JSIdArray *a; - jsval id, val; - char *name; - jsint i; - JSBool ret = JS_FALSE; - - if ((a = JS_Enumerate(cx, obj)) != NULL) { - for (i = 0; i < a->length; i++) { - JS_IdToValue(cx, a->vector[i], &id); - if (JSVAL_IS_STRING(id)) { - /* - * Property id is a string, attempt to - * lookup its value by name. - */ - name = JS_GetStringBytes(JSVAL_TO_STRING(id)); - if (!JS_LookupProperty(cx, obj, name, &val)) - continue; - } else { - /* - * Property id is a number, attempt to - * lookup its array element by index. - */ - if (!JS_LookupElement(cx, obj, - JSVAL_TO_INT(id), &val)) - continue; - } - if (!JSVAL_IS_VOID(val)) { - if (!(ret = func(cx, &id, &val, udata))) - break; - } - } - JS_DestroyIdArray(cx, a); - } - - return ret; -} - -/* - * Utility function return 0 if user supplied path should be allowed, or -1 if - * it should be rejected (invalid, or permission denied). This can for - * instance be used to restrict the program in a virtual chroot(2)-like jail. - */ -/* ARGSUSED */ -static int -fd_path_allow(const char *path) -{ - /* XXX */ - - return 0; -} - -/* ARGSUSED */ -static mode_t -fd_mode_allow(mode_t mode) -{ - /* XXX */ - - return mode; -} - -/* ARGSUSED */ -static int -fd_flags_allow(int flags) -{ - /* XXX */ - - return flags; -} - -/* - * Useful to ensure that a function's arguments are as expected, and to - * retrieve the private data associated with the FD object. Implemented to - * minimize code duplication among common functions. - */ -static jsfd_t * -fd_methods_args_check(JSContext *cx, JSObject *obj, const char *fun, int id, - int argc, jsval *argv, int type) -{ - int *p = fd_methods_args_array[id], i; - char line[1024]; - jsfd_t *jsfd; - - if (*p != argc) { - (void) snprintf(line, 1023, - "%s() - Wrong number of arguments (%d), expected %d", - fun, argc, *p); - QUEUE_EXCEPTION(line); - return NULL; - } - - for (p++, i = 0; i < argc; i++) { - switch (p[i]) { - case JSAT_INTEGER: - if (!JSVAL_IS_INT(argv[i])) { - (void) snprintf(line, 1023, - "%s() - argument #%d not an integer", - fun, i + 1); - QUEUE_EXCEPTION(line); - return NULL; - } - break; - case JSAT_DOUBLE: - if (!JSVAL_IS_DOUBLE(argv[i]) && - !JSVAL_IS_INT(argv[i])) { - (void) snprintf(line, 1023, - "%s() - argument #%d not a double", - fun, i + 1); - QUEUE_EXCEPTION(line); - return NULL; - } - break; - case JSAT_STRING: - if (!JSVAL_IS_STRING(argv[i])) { - (void) snprintf(line, 1023, - "%s() - argument #%d not a string", - fun, i + 1); - QUEUE_EXCEPTION(line); - return NULL; - } - break; - default: - (void) snprintf(line, 1023, - "%s() - Unexpected argument type #%d", - fun, i + 1); - QUEUE_EXCEPTION(line); - return NULL; - } - } - - if ((jsfd = JS_GetInstancePrivate(cx, obj, &fd_class, NULL)) - == NULL) { - (void) snprintf(line, 1023, "%s() - NULL private data!", fun); - QUEUE_EXCEPTION(line); - return NULL; - } - - if (type == JSFD_NONE && jsfd->type != JSFD_NONE) { - (void) snprintf(line, 1023, - "%s() - Descriptor is already open", - fun); - QUEUE_EXCEPTION(line); - return NULL; - } else - return jsfd; - - if ((jsfd->type & type) == 0) { - (void) snprintf(line, 1023, - "%s() - Descriptor is closed or of wrong type", - fun); - QUEUE_EXCEPTION(line); - return NULL; - } - - return jsfd; -} diff --git a/mmsoftware/js/classes/js_fd.h b/mmsoftware/js/classes/js_fd.h deleted file mode 100644 index f0d2f77..0000000 --- a/mmsoftware/js/classes/js_fd.h +++ /dev/null @@ -1,15 +0,0 @@ -/* $Id: js_fd.h,v 1.1 2006/07/22 04:18:47 mmondor Exp $ */ - -/* - * Copyright (c) 2005, Matthew Mondor - * ALL RIGHTS RESERVED. - */ - -#ifndef JSFD_H -#define JSFD_H - -#include - -extern JSObject *js_InitFDClass(JSContext *, JSObject *); - -#endif diff --git a/mmsoftware/js/classes/js_global.c b/mmsoftware/js/classes/js_global.c deleted file mode 100644 index 35cdcd1..0000000 --- a/mmsoftware/js/classes/js_global.c +++ /dev/null @@ -1,150 +0,0 @@ -/* $Id: js_global.c,v 1.1 2006/07/22 04:18:47 mmondor Exp $ */ - -/* - * Copyright (c) 2006, Matthew Mondor - * ALL RIGHTS RESERVED. - */ - -/* - * Provide a means to set and access global objects and global properties. - * Basically we can store hashtable objects, which each can store arbitrary - * String/String tuples, but using shared memory instead of a database. - * Internally a hash table would also be used to index hash tables by String. - * There would be a single synchronization lock around the system. - * We would need to initially work with an allocated buffer of shared memory, - * which only needed pages are used. For this, mmpool(3) could be used. - * A pool of hash tables would be necessary, as well as one for the data - * pair items. To be linked among those. - * - * I yet have to find a proper interface. We optionally could have stuff - * like: - * - * Global.getProperty(table, property); - * Global.setProperty(table, property, string); - * - * But would it also be possible to use lazy allocation such that this would - * be possible, although of course enforcing the same internal behavior: - * - * Global.table.property would be read or set as necessary. - * - * Or: - * table = new Global(tablename); - * table.prop = 'string'; - * out.put(table.prop + "\n"); - */ - - - -#include - -#include -#include -#include -#include -#include - -#include - -#include - - - -#define QUEUE_EXCEPTION(s) do { \ - JS_SetPendingException(cx, \ - STRING_TO_JSVAL(JS_NewStringCopyZ(cx, (s)))); \ -} while (/* CONSTCOND */0) - - - -/* - * Static prototypes - */ -static JSBool global_constructor(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static void global_finalize(JSContext *, JSObject *); - -static JSBool global_getProperty(JSContext *, JSObject *, jsval, jsval *); -static JSBool global_setProperty(JSContext *, JSObject *, jsval, jsval *); - - - -/* - * Static globals - */ - -/* Global class */ -static JSClass pg_class = { - "Global", JCLASS_HAS_PRIVATE, JS_PropertyStub, JS_PropertyStub, - global_getProperty, global_setProperty, JS_EnumerateStub, - JS_ResolveStub, JS_ConvertStub, global_finalize -}; - - - -/* - * Global object control - */ - -JSObject * -js_InitGlobalClass(JSContext *cx, JSObject *obj) -{ - JSObject *proto, *ctor; - struct property_spec *sp; - - if ((proto = JS_InitClass(cx, obj, NULL, &global_class, - global_constructor, 0, NULL, NULL, NULL, NULL)) == NULL) { - (void) fprintf(stderr, "Error initializing Global class\n"); - goto err; - } - - /* XXX Initialize shared memory and lock */ - - return proto; - -err: - - return NULL; -} - -static JSBool -global_constructor(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - - if (argc != 1) { - QUEUE_EXCEPTION("Wrong number of arguments"); - goto err; - } - - if (!JS_IsConstructing(cx)) { - QUEUE_EXCEPTION("Constructor called as a function"); - goto err; - } - - /* XXX */ - - return JS_TRUE; - -err: - *rval = OBJECT_TO_JSVAL(NULL); - - return JS_FALSE; -} - -static JSBool -global_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) -{ - - /* XXX */ - - return JS_TRUE; -} - -static JSBool -global_setProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) -{ - - /* XXX */ - - return JS_TRUE; -} diff --git a/mmsoftware/js/classes/js_global.h b/mmsoftware/js/classes/js_global.h deleted file mode 100644 index d4fc6c1..0000000 --- a/mmsoftware/js/classes/js_global.h +++ /dev/null @@ -1,15 +0,0 @@ -/* $Id: js_global.h,v 1.1 2006/07/22 04:18:47 mmondor Exp $ */ - -/* - * Copyright (c) 2006, Matthew Mondor - * ALL RIGHTS RESERVED. - */ - -#ifndef JSGLOBAL_H -#define JSGLOBAL_H - -#include - -extern JSObject *js_InitGlobalClass(JSContext *, JSObject *); - -#endif diff --git a/mmsoftware/js/classes/js_mysql.c b/mmsoftware/js/classes/js_mysql.c deleted file mode 100644 index a29becc..0000000 --- a/mmsoftware/js/classes/js_mysql.c +++ /dev/null @@ -1,6 +0,0 @@ -/* $Id: js_mysql.c,v 1.1 2006/07/22 04:18:47 mmondor Exp $ */ - -/* - * Copyright (c) 2006, Matthew Mondor - * ALL RIGHTS RESERVED. - */ diff --git a/mmsoftware/js/classes/js_mysql.h b/mmsoftware/js/classes/js_mysql.h deleted file mode 100644 index 2a40bd6..0000000 --- a/mmsoftware/js/classes/js_mysql.h +++ /dev/null @@ -1,13 +0,0 @@ -/* $Id: js_mysql.h,v 1.1 2006/07/22 04:18:47 mmondor Exp $ */ - -/* - * Copyright (c) 2006, Matthew Mondor - * ALL RIGHTS RESERVED. - */ - -#ifndef JSMYSQL_H -#define JSMYSQL_H - -extern JSObject *js_InitMySQLClass(JSContext *, JSObject *); - -#endif diff --git a/mmsoftware/js/classes/js_pgsql.c b/mmsoftware/js/classes/js_pgsql.c deleted file mode 100644 index a96ce13..0000000 --- a/mmsoftware/js/classes/js_pgsql.c +++ /dev/null @@ -1,3493 +0,0 @@ -/* $Id: js_pgsql.c,v 1.1 2006/07/22 04:18:47 mmondor Exp $ */ - -/* - * Copyright (c) 2006, Matthew Mondor - * ALL RIGHTS RESERVED. - */ - -/* - * XXX TODO XXX - * - Verify if JS_GetStringLength() really safe to continue using - * - Perhaps provide simpler replacement functions for query functions - * allowing separate parameters to be set (perhaps not necessary considering - * that we can provide null to an array). - * - All functions creating doubles or strings should check if NULL is - * returned. Verify this. - * - (maybe) make reentrant by causing optimization buffers to be part of - * generated objects instances's private data (using structures as necessary - * instead of simply wrapping around the native object's pointer - * (actually PGconn object). - * - 28.10. Notice Processing - * Either place one(s) that use syslog(3) transparently, or somehow allow - * the user to set a custom handler - * - Large objects API - * - Compare to PHP library to verify if missing any nice functions ideas - * - See what to do about the following functions: - * - PQgetssl() (returns an SSL object!) - * - PQprint() (writes to a supplied FILE *) - */ - - - -#include - -#include -#include -#include -#include -#include - -#include - -#include - -#include - - - -/* - * PostgreSQL services for ECMAScript - * - * NOTES: - * We create a parent PG object which allows us to store static first-level - * methods as well as numeric properties required to work with the libpq - * library. Almost all other functionality is available through PGconn and - * PGresult objects afterwards. - * - * If supporting the asynchroneous part of the API, it should also be possible - * for us to return an FD object for a PGconn * so that polling could be used, - * etc. Or at least just return the fd int which can be used easily to create - * an FD object with afterwards by the caller. - */ - - - -#define QUEUE_EXCEPTION(s) do { \ - JS_SetPendingException(cx, \ - STRING_TO_JSVAL(JS_NewStringCopyZ(cx, (s)))); \ -} while (/* CONSTCOND */0) - - -struct property_spec { - const char *name; - int value; -}; - - - -/* - * Static prototypes - */ -static int buffer_grow(size_t); -static int param_grow(int); - -static JSBool pg_constructor(JSContext *, JSObject *, uintN, jsval *, - jsval *); - -static JSBool pg_sm_PQconndefaults(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool pg_sm_PQconnectdb(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool pg_sm_PQconnectStart(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool pg_sm_PQresStatus(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool pg_sm_PQunescapeBytea(JSContext *, JSObject *, uintN, jsval *, - jsval *); - -static JSObject *js_InitPGconnClass(JSContext *, JSObject *); -static JSBool pgconn_constructor(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static void pgconn_finalize(JSContext *, JSObject *); - -static JSBool pgconn_m_PQfinish(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool pgconn_m_PQconnectPoll(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool pgconn_m_PQreset(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool pgconn_m_PQresetStart(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool pgconn_m_PQresetPoll(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool pgconn_m_PQdb(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool pgconn_m_PQuser(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool pgconn_m_PQpass(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool pgconn_m_PQhost(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool pgconn_m_PQport(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool pgconn_m_PQtty(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool pgconn_m_PQoptions(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool pgconn_m_PQstatus(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool pgconn_m_PQtransactionStatus(JSContext *, JSObject *, uintN, - jsval *, jsval *); -static JSBool pgconn_m_PQparameterStatus(JSContext *, JSObject *, uintN, - jsval *, jsval *); -static JSBool pgconn_m_PQprotocolVersion(JSContext *, JSObject *, uintN, - jsval *, jsval *); -static JSBool pgconn_m_PQserverVersion(JSContext *, JSObject *, uintN, - jsval *, jsval *); -static JSBool pgconn_m_PQerrorMessage(JSContext *, JSObject *, uintN, - jsval *, jsval *); -static JSBool pgconn_m_PQsocket(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool pgconn_m_PQbackendPID(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool pgconn_m_PQexec(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool pgconn_m_PQsendQuery(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool pgconn_m_PQexecParams2(JSContext *, JSObject *, uintN, jsval *, - jsval *, int); -static JSBool pgconn_m_PQexecParams(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool pgconn_m_PQsendQueryParams(JSContext *, JSObject *, uintN, - jsval *, jsval *); -static JSBool pgconn_m_PQprepare2(JSContext *, JSObject *, uintN, jsval *, - jsval *, int); -static JSBool pgconn_m_PQprepare(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool pgconn_m_PQsendPrepare(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool pgconn_m_PQexecPrepared2(JSContext *, JSObject *, uintN, - jsval *, jsval *, int); -static JSBool pgconn_m_PQexecPrepared(JSContext *, JSObject *, uintN, - jsval *, jsval *); -static JSBool pgconn_m_PQsendQueryPrepared(JSContext *, JSObject *, uintN, - jsval *, jsval *); -static JSBool pgconn_m_PQmakeEmptyPGresult(JSContext *, JSObject *, uintN, - jsval *, jsval *); -static JSBool pgconn_m_PQescapeStringConn(JSContext *, JSObject *, uintN, - jsval *, jsval *); -static JSBool pgconn_m_PQescapeByteaConn(JSContext *, JSObject *, uintN, - jsval *, jsval *); -static JSBool pgconn_m_PQgetCancel(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool pgconn_m_PQnotifies(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool pgconn_m_PQgetResult(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool pgconn_m_PQconsumeInput(JSContext *, JSObject *, uintN, - jsval *, jsval *); -static JSBool pgconn_m_PQisBusy(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool pgconn_m_PQsetnonblocking(JSContext *, JSObject *, uintN, - jsval *, jsval *); -static JSBool pgconn_m_PQisnonblocking(JSContext *, JSObject *, uintN, - jsval *, jsval *); -static JSBool pgconn_m_PQflush(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool pgconn_m_PQsetErrorVerbosity(JSContext *, JSObject *, uintN, - jsval *, jsval *); -static JSBool pgconn_m_PQtrace(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool pgconn_m_PQuntrace(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool pgconn_m_PQputCopyData(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool pgconn_m_PQputCopyEnd(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool pgconn_m_PQgetCopyData(JSContext *, JSObject *, uintN, jsval *, - jsval *); - -static JSObject *js_InitPGresultClass(JSContext *, JSObject *); -static JSBool pgresult_constructor(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static void pgresult_finalize(JSContext *, JSObject *); - -static JSBool pgresult_m_PQclear(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool pgresult_m_PQresultStatus(JSContext *, JSObject *, uintN, - jsval *, jsval *); -static JSBool pgresult_m_PQresultErrorMessage(JSContext *, JSObject *, uintN, - jsval *, jsval *); -static JSBool pgresult_m_PQresultErrorField(JSContext *, JSObject *, uintN, - jsval *, jsval *); -static JSBool pgresult_m_PQntuples(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool pgresult_m_PQnfields(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool pgresult_m_PQfname(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool pgresult_m_PQfnumber(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool pgresult_m_PQftable(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool pgresult_m_PQftablecol(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool pgresult_m_PQfformat(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool pgresult_m_PQftype(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool pgresult_m_PQfmod(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool pgresult_m_PQfsize(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool pgresult_m_PQbinaryTuples(JSContext *, JSObject *, uintN, - jsval *, jsval *); -static JSBool pgresult_m_PQgetvalue(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool pgresult_m_PQgetisnull(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool pgresult_m_PQgetlength(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool pgresult_m_PQcmdStatus(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool pgresult_m_PQcmdTuples(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool pgresult_m_PQoidValue(JSContext *, JSObject *, uintN, jsval *, - jsval *); - -static JSObject *js_InitPGcancelClass(JSContext *, JSObject *); -static JSBool pgcancel_constructor(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static void pgcancel_finalize(JSContext *, JSObject *); - -static JSBool pgcancel_m_PQfreeCancel(JSContext *, JSObject *, uintN, - jsval *, jsval *); -static JSBool pgcancel_m_PQcancel(JSContext *, JSObject *, uintN, jsval *, - jsval *); - - - -/* - * Static globals - */ - -/* - * General purpose string buffer (note that this is not thread-safe). - * Allows to optimize functions such as PQescapeStringConn(). - */ -static char *buffer = NULL; -static size_t buffer_size = 0; -static Oid *param_types = NULL; -static char **param_values = NULL; -static int *param_lengths = NULL; -static int *param_formats = NULL; -static int param_entries = 0; - -/* PG class */ -static JSClass pg_class = { - "PG", 0, JS_PropertyStub, JS_PropertyStub, - JS_PropertyStub, JS_PropertyStub, JS_EnumerateStub, JS_ResolveStub, - JS_ConvertStub, JS_FinalizeStub -}; - -/* Provided static methods */ -static JSFunctionSpec pg_smethods[] = { - { "connDefaults", pg_sm_PQconndefaults, 0, 0, 0 }, - { "connectDb", pg_sm_PQconnectdb, 1, 0, 0 }, - { "connectStart", pg_sm_PQconnectStart, 1, 0, 0 }, - { "resStatus", pg_sm_PQresStatus, 1, 0, 0 }, - { "unescapeBytea", pg_sm_PQunescapeBytea, 2, 0, 0 }, - { NULL, NULL, 0, 0, 0 } -}; - -/* Provided static properties */ - -#define SP(n) \ - { #n, n } - -static struct property_spec pg_sprops[] = { - SP(PGRES_POLLING_OK), - SP(PGRES_POLLING_READING), - SP(PGRES_POLLING_WRITING), - SP(PGRES_POLLING_FAILED), - SP(PGRES_EMPTY_QUERY), - SP(PGRES_COMMAND_OK), - SP(PGRES_TUPLES_OK), - SP(PGRES_COPY_OUT), - SP(PGRES_COPY_IN), - SP(PGRES_BAD_RESPONSE), - SP(PGRES_NONFATAL_ERROR), - SP(PGRES_FATAL_ERROR), - SP(PG_DIAG_SEVERITY), - SP(PG_DIAG_SQLSTATE), - SP(PG_DIAG_MESSAGE_PRIMARY), - SP(PG_DIAG_MESSAGE_DETAIL), - SP(PG_DIAG_MESSAGE_HINT), - SP(PG_DIAG_STATEMENT_POSITION), - SP(PG_DIAG_INTERNAL_POSITION), - SP(PG_DIAG_INTERNAL_QUERY), - SP(PG_DIAG_CONTEXT), - SP(PG_DIAG_SOURCE_FILE), - SP(PG_DIAG_SOURCE_LINE), - SP(PG_DIAG_SOURCE_FUNCTION), - SP(CONNECTION_OK), - SP(CONNECTION_BAD), - SP(CONNECTION_STARTED), - SP(CONNECTION_MADE), - SP(CONNECTION_AWAITING_RESPONSE), - SP(CONNECTION_AUTH_OK), - SP(CONNECTION_SSL_STARTUP), - SP(CONNECTION_SETENV), - SP(PQTRANS_IDLE), - SP(PQTRANS_ACTIVE), - SP(PQTRANS_INTRANS), - SP(PQTRANS_INERROR), - SP(PQTRANS_UNKNOWN), - SP(InvalidOid), - SP(PQERRORS_TERSE), - SP(PQERRORS_DEFAULT), - SP(PQERRORS_VERBOSE), - { NULL, 0 } -}; - -#undef SP - - -/* PGconn class */ -static JSClass pgconn_class = { - "PGConn", JSCLASS_HAS_PRIVATE, JS_PropertyStub, JS_PropertyStub, - JS_PropertyStub, JS_PropertyStub, JS_EnumerateStub, - JS_ResolveStub, JS_ConvertStub, pgconn_finalize -}; - -/* Provided methods/functions */ -static JSFunctionSpec pgconn_methods[] = { - { "finish", pgconn_m_PQfinish, 0, 0, 0 }, - { "connectPoll", pgconn_m_PQconnectPoll, 0, 0, 0 }, - { "reset", pgconn_m_PQreset, 0, 0, 0 }, - { "resetStart", pgconn_m_PQresetStart, 0, 0, 0 }, - { "resetPoll", pgconn_m_PQresetPoll, 0, 0, 0 }, - { "db", pgconn_m_PQdb, 0, 0, 0 }, - { "user", pgconn_m_PQuser, 0, 0, 0 }, - { "pass", pgconn_m_PQpass, 0, 0, 0 }, - { "host", pgconn_m_PQhost, 0, 0, 0 }, - { "port", pgconn_m_PQport, 0, 0, 0 }, - { "tty", pgconn_m_PQtty, 0, 0, 0 }, - { "options", pgconn_m_PQoptions, 0, 0, 0 }, - { "status", pgconn_m_PQstatus, 0, 0, 0 }, - { "transactionStatus", pgconn_m_PQtransactionStatus, 0, 0, 0 }, - { "parameterStatus", pgconn_m_PQparameterStatus, 1, 0, 0 }, - { "protocolVersion", pgconn_m_PQprotocolVersion, 0, 0, 0 }, - { "serverVersion", pgconn_m_PQserverVersion, 0, 0, 0 }, - { "errorMessage", pgconn_m_PQerrorMessage, 0, 0, 0 }, - { "socket", pgconn_m_PQsocket, 0, 0, 0 }, - { "backendPid", pgconn_m_PQbackendPID, 0, 0, 0 }, - { "exec", pgconn_m_PQexec, 1, 0, 0 }, - { "sendQuery", pgconn_m_PQsendQuery, 1, 0, 0 }, - { "execParams", pgconn_m_PQexecParams, 7, 0, 0 }, - { "sendQueryParams", pgconn_m_PQsendQueryParams, 7, 0, 0 }, - { "prepare", pgconn_m_PQprepare, 4, 0, 0 }, - { "sendPrepare", pgconn_m_PQsendPrepare, 4, 0, 0 }, - { "execPrepared", pgconn_m_PQexecPrepared, 6, 0, 0 }, - { "sendQueryPrepared", pgconn_m_PQsendQueryPrepared, 6, 0, 0 }, - { "makeEmptyPGResult", pgconn_m_PQmakeEmptyPGresult, 1, 0, 0 }, - { "escapeStringConn", pgconn_m_PQescapeStringConn, 1, 0, 0 }, - { "escapeByteaConn", pgconn_m_PQescapeByteaConn, 1, 0, 0 }, - { "getCancel", pgconn_m_PQgetCancel, 0, 0, 0 }, - { "notifies", pgconn_m_PQnotifies, 0, 0, 0 }, - { "getResult", pgconn_m_PQgetResult, 0, 0, 0 }, - { "consumeInput", pgconn_m_PQconsumeInput, 0, 0, 0 }, - { "isBusy", pgconn_m_PQisBusy, 0, 0, 0 }, - { "setNonBlocking", pgconn_m_PQsetnonblocking, 1, 0, 0 }, - { "isNonBlocking", pgconn_m_PQisnonblocking, 0, 0, 0 }, - { "flush", pgconn_m_PQflush, 0, 0, 0 }, - { "setErrorVerbosity", pgconn_m_PQsetErrorVerbosity, 1, 0, 0 }, - { "trace", pgconn_m_PQtrace, 0, 0, 0 }, - { "untrace", pgconn_m_PQuntrace, 0, 0, 0 }, - { "putCopyData", pgconn_m_PQputCopyData, 1, 0, 0 }, - { "putCopyEnd", pgconn_m_PQputCopyEnd, 1, 0, 0 }, - { "getCopyData", pgconn_m_PQgetCopyData, 1, 0, 0 }, - { NULL, NULL, 0, 0, 0 } -}; - - -/* PGresult class */ -static JSClass pgresult_class = { - "PGResult", JSCLASS_HAS_PRIVATE, JS_PropertyStub, JS_PropertyStub, - JS_PropertyStub, JS_PropertyStub, JS_EnumerateStub, - JS_ResolveStub, JS_ConvertStub, pgresult_finalize -}; - -/* Provided methods/functions */ -static JSFunctionSpec pgresult_methods[] = { - { "clear", pgresult_m_PQclear, 0, 0, 0 }, - { "resultStatus", pgresult_m_PQresultStatus, 0, 0, 0 }, - { "resultErrorMessage", pgresult_m_PQresultErrorMessage, 0, 0, 0 }, - { "resultErrorField", pgresult_m_PQresultErrorField, 1, 0, 0 }, - { "nTuples", pgresult_m_PQntuples, 0, 0, 0 }, - { "nFields", pgresult_m_PQnfields, 0, 0, 0 }, - { "fName", pgresult_m_PQfname, 1, 0, 0 }, - { "fNumber", pgresult_m_PQfnumber, 1, 0, 0 }, - { "fTable", pgresult_m_PQftable, 1, 0, 0 }, - { "fTableCol", pgresult_m_PQftablecol, 1, 0, 0 }, - { "fFormat", pgresult_m_PQfformat, 1, 0, 0 }, - { "fType", pgresult_m_PQftype, 1, 0, 0 }, - { "fMod", pgresult_m_PQfmod, 1, 0, 0 }, - { "fSize", pgresult_m_PQfsize, 1, 0, 0 }, - { "binaryTuples", pgresult_m_PQbinaryTuples, 1, 0, 0 }, - { "getValue", pgresult_m_PQgetvalue, 2, 0, 0 }, - { "getIsNull", pgresult_m_PQgetisnull, 2, 0, 0 }, - { "getLength", pgresult_m_PQgetlength, 2, 0, 0 }, - { "cmdStatus", pgresult_m_PQcmdStatus, 0, 0, 0 }, - { "cmdTuples", pgresult_m_PQcmdTuples, 0, 0, 0 }, - { "oidValue", pgresult_m_PQoidValue, 0, 0, 0 }, - { NULL, NULL, 0, 0, 0 } -}; - - -/* PGcancel class */ -static JSClass pgcancel_class = { - "PGCancel", JSCLASS_HAS_PRIVATE, JS_PropertyStub, JS_PropertyStub, - JS_PropertyStub, JS_PropertyStub, JS_EnumerateStub, - JS_ResolveStub, JS_ConvertStub, pgcancel_finalize -}; - -/* Provided methods/functions */ -static JSFunctionSpec pgcancel_methods[] = { - { "freeCancel", pgcancel_m_PQfreeCancel, 0, 0, 0 }, - { "cancel", pgcancel_m_PQcancel, 1, 0, 0 }, - { NULL, NULL, 0, 0, 0 } -}; - - - -static int -buffer_grow(size_t required) -{ - size_t new; - void *ptr; - - if (required <= buffer_size) - return 0; - - for (new = buffer_size; new < required; new *= 2) ; - - if ((ptr = realloc(buffer, new)) == NULL) - return -1; - - buffer = ptr; - buffer_size = new; - - return 0; -} - -static int -param_grow(int required) -{ - int new; - void *types, *values, *lengths, *formats; - - if (required <= param_entries) - return 0; - - for (new = param_entries; new < required; new *= 2) ; - - if ((types = realloc(param_types, sizeof(Oid) * new)) == NULL || - (values = realloc(param_values, sizeof(char *) * new)) == NULL || - (lengths = realloc(param_lengths, sizeof(int) * new)) == NULL || - (formats = realloc(param_formats, sizeof(int) * new)) == NULL) - return -1; - - param_types = types; - param_values = values; - param_lengths = lengths; - param_formats = formats; - param_entries = new; - - return 0; -} - - -/* - * PG object control - */ - -JSObject * -js_InitPGClass(JSContext *cx, JSObject *obj) -{ - JSObject *proto, *ctor; - struct property_spec *sp; - - if ((proto = JS_InitClass(cx, obj, NULL, &pg_class, pg_constructor, 0, - NULL, NULL, NULL, pg_smethods)) == NULL) { - (void) fprintf(stderr, "Error initializing PG class\n"); - goto err; - } - - /* Create static properties */ - if ((ctor = JS_GetConstructor(cx, proto)) == NULL) { - (void) fprintf(stderr, "PG: JS_GetConstructor == NULL\n"); - goto err; - } - for (sp = pg_sprops; sp->name != NULL; sp++) { - if (!JS_DefineProperty(cx, ctor, sp->name, - INT_TO_JSVAL(sp->value), NULL, NULL, - JSPROP_READONLY | JSPROP_PERMANENT)) { - (void) fprintf(stderr, - "PG: Error defining property %s\n", sp->name); - goto err; - } - } - - /* Initialize PGconn class since we'll need to instanciate it from C */ - if (js_InitPGconnClass(cx, obj) == NULL) { - (void) fprintf(stderr, "PG: InitPGconnClass()\n"); - goto err; - } - /* Same for PGresult class */ - if (js_InitPGresultClass(cx, obj) == NULL) { - (void) fprintf(stderr, "PG: InitPGresultClass()\n"); - goto err; - } - /* And PGcancel class */ - if (js_InitPGcancelClass(cx, obj) == NULL) { - (void) fprintf(stderr, "PG: InitPGcancelClass()\n"); - goto err; - } - - /* - * Note that the following buffers, although allowing optimizations, - * cause the functions using them to not be reentrant. For reentrancy - * similar buffers could be attached to the object instances instead. - * This would of course however mean a larger memory footprint. - * If doing this, we would also need a custom structure for the - * private data instead of simply wrapping around the native pointers. - */ - - /* Allocate an initial general purpose buffer */ - if ((buffer = malloc(16384)) == NULL) { - (void) fprintf(stderr, "PG: malloc()\n"); - goto err; - } - buffer_size = 16384; - - /* As well as buffers for the *Params() parameter arrays */ - if ((param_types = malloc(sizeof(Oid) * 16)) == NULL || - (param_values = malloc(sizeof(char *) * 16)) == NULL || - (param_lengths = malloc(sizeof(int) * 16)) == NULL || - (param_formats = malloc(sizeof(int) * 16)) == NULL) { - (void) fprintf(stderr, "PG: malloc()\n"); - goto err; - } - param_entries = 16; - - return proto; - -err: - if (buffer != NULL) - free(buffer); - if (param_types != NULL) - free(param_types); - if (param_values != NULL) - free(param_values); - if (param_lengths != NULL) - free(param_lengths); - if (param_formats != NULL) - free(param_formats); - - return NULL; -} - -/* Non instanciable */ -static JSBool -pg_constructor(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - - QUEUE_EXCEPTION("PG class uninstanciable"); - - return JS_FALSE; -} - - -/* - * PG object static methods - */ - -/* - * Returns an array of objects which each contain the various parameters - * returned by PQconndefaults(). - * XXX Could be more useful if it returned an object of objects using the - * keyword as property in the first object level, perhaps. - */ -static JSBool -pg_sm_PQconndefaults(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - PQconninfoOption *in = NULL, *p; - JSObject *array = NULL; - JSString *str; - int i; - - if (argc != 0) { - QUEUE_EXCEPTION("Wrong number of arguments"); - return JS_FALSE; - } - - if ((in = PQconndefaults()) == NULL) { - QUEUE_EXCEPTION("PQconndefaults() == NULL"); - goto err; - } - if ((array = JS_NewArrayObject(cx, 0, NULL)) == NULL) { - QUEUE_EXCEPTION("Out of memory"); - goto err; - } - /* Root immediately */ - *rval = OBJECT_TO_JSVAL(array); - -#define DEFINE_STRING_PROP(n, s) do { \ - if ((str = JS_NewStringCopyZ(cx, (char *)(s))) == NULL) { \ - QUEUE_EXCEPTION("Out of memory!"); \ - goto err; \ - } \ - if (!JS_DefineProperty(cx, o, (n), STRING_TO_JSVAL(str), NULL, \ - NULL, JSPROP_ENUMERATE)) { \ - QUEUE_EXCEPTION("Internal error!"); \ - goto err; \ - } \ -} while (/* CONSTCOND */0) - -#define DEFINE_STRING_PROP2(n, s, l) do { \ - if ((str = JS_NewStringCopyN(cx, (char *)(s), (l))) == NULL) { \ - QUEUE_EXCEPTION("Out of memory!"); \ - goto err; \ - } \ - if (!JS_DefineProperty(cx, o, (n), STRING_TO_JSVAL(str), NULL, \ - NULL, JSPROP_ENUMERATE)) { \ - QUEUE_EXCEPTION("Internal error!"); \ - goto err; \ - } \ -} while (/* CONSTCOND */0) - -#define DEFINE_INT_PROP(n, i) do { \ - if (!JS_DefineProperty(cx, array, (n), INT_TO_JSVAL((int)(i)), \ - NULL, NULL, JSPROP_ENUMERATE)) { \ - QUEUE_EXCEPTION("Internal error!"); \ - goto err; \ - } \ -} while (/* CONSTCOND */0) - - - /* Polulate array with objects */ - for (i = 0, p = in; p->keyword != NULL; p++, i++) { - JSObject *o; - - if ((o = JS_NewObject(cx, NULL, NULL, NULL)) == NULL) { - QUEUE_EXCEPTION("Out of memory"); - goto err; - } - /* Root immediately by inserting object into array */ - if (!JS_DefineElement(cx, array, i, OBJECT_TO_JSVAL(o), - NULL, NULL, JSPROP_ENUMERATE)) { - QUEUE_EXCEPTION("Internal error!"); - goto err; - } - - /* Populate object with properties */ - DEFINE_STRING_PROP("keyword", p->keyword); - DEFINE_STRING_PROP("envvar", p->envvar); - DEFINE_STRING_PROP("compiled", p->compiled); - DEFINE_STRING_PROP("val", p->val); - DEFINE_STRING_PROP("label", p->label); - DEFINE_STRING_PROP2("dispchar", p->dispchar, 1); - DEFINE_INT_PROP("dispsize", p->dispsize); - } - -#undef DEFINE_STRING_PROP -#undef DEFINE_STRING_PROP2 -#undef DEFINE_INT_PROP - - PQconninfoFree(in); - - return JS_TRUE; - -err: - *rval = OBJECT_TO_JSVAL(NULL); - if (in != NULL) - PQconninfoFree(in); - - return JS_FALSE; -} - -static JSBool -pg_sm_PQconnectdb(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - JSObject *o; - PGconn *pgc = NULL; - char *str; - - if (argc != 1) { - QUEUE_EXCEPTION("Wrong number of arguments"); - goto err; - } - if (!JSVAL_IS_STRING(argv[0])) { - QUEUE_EXCEPTION("Argument not a String"); - goto err; - } - str = JS_GetStringBytes(JSVAL_TO_STRING(argv[0])); - - if ((o = JS_NewObject(cx, &pgconn_class, NULL, NULL)) == NULL) { - QUEUE_EXCEPTION("Internal error!"); - goto err; - } - /* Root immediately */ - *rval = OBJECT_TO_JSVAL(o); - - if ((pgc = PQconnectdb(str)) == NULL) { - QUEUE_EXCEPTION("PQconnectdb"); - goto err; - } - if (!JS_SetPrivate(cx, o, pgc)) { - QUEUE_EXCEPTION("Internal error!"); - goto err; - } - - return JS_TRUE; - -err: - if (pgc != NULL) - PQfinish(pgc); - - *rval = OBJECT_TO_JSVAL(NULL); - - return JS_FALSE; -} - -static JSBool -pg_sm_PQconnectStart(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - JSObject *o; - PGconn *pgc = NULL; - char *str; - - if (argc != 1) { - QUEUE_EXCEPTION("Wrong number of arguments"); - goto err; - } - if (!JSVAL_IS_STRING(argv[0])) { - QUEUE_EXCEPTION("Argument not a String"); - goto err; - } - str = JS_GetStringBytes(JSVAL_TO_STRING(argv[0])); - - if ((o = JS_NewObject(cx, &pgconn_class, NULL, NULL)) == NULL) { - QUEUE_EXCEPTION("Internal error!"); - goto err; - } - /* Root immediately */ - *rval = OBJECT_TO_JSVAL(o); - - if ((pgc = PQconnectStart(str)) == NULL) { - QUEUE_EXCEPTION("PQconnectStart"); - goto err; - } - if (!JS_SetPrivate(cx, o, pgc)) { - QUEUE_EXCEPTION("Internal error!"); - goto err; - } - - return JS_TRUE; - -err: - if (pgc != NULL) - PQfinish(pgc); - - *rval = OBJECT_TO_JSVAL(NULL); - - return JS_FALSE; -} - -static JSBool -pg_sm_PQresStatus(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - char *res; - - if (argc != 1) { - QUEUE_EXCEPTION("Wrong number of arguments"); - goto err; - } - if (!JSVAL_IS_INT(argv[0])) { - QUEUE_EXCEPTION("Argument not an int"); - goto err; - } - - res = PQresStatus(JSVAL_TO_INT(argv[0])); - *rval = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, res)); - - return JS_TRUE; - -err: - *rval = OBJECT_TO_JSVAL(NULL); - - return JS_FALSE; -} - -/* - * Note: Semantics are different from C PQunescapeBytea() in that it is - * supplied a single String and that it returns a resulting String, or null on - * error. Much easier to work with within ECMAScript this way. - */ -static JSBool -pg_sm_PQunescapeBytea(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - PGconn *pgc; - JSString *str; - char *from; - size_t reslen; - unsigned char *res = NULL; - - if (argc != 1) { - QUEUE_EXCEPTION("Wrong number of arguments"); - goto err; - } - if (!JSVAL_IS_STRING(argv[0])) { - QUEUE_EXCEPTION("Argument not a String"); - goto err; - } - - pgc = JS_GetInstancePrivate(cx, obj, &pgconn_class, NULL); - assert(pgc != NULL); - - from = JS_GetStringBytes(JSVAL_TO_STRING(argv[0])); - - if ((res = PQunescapeBytea(from, &reslen)) == NULL) { - QUEUE_EXCEPTION("PQescapeByteaConn()"); - goto err; - } - - if ((str = JS_NewStringCopyN(cx, res, reslen)) == NULL) { - QUEUE_EXCEPTION("Out of memory!"); - goto err; - } - - PQfreemem(res); - *rval = STRING_TO_JSVAL(str); - - return JS_TRUE; - -err: - if (res != NULL) - PQfreemem(res); - - *rval = OBJECT_TO_JSVAL(NULL); - - return JS_FALSE; -} - - -/* - * PGconn object control - */ - -static JSObject * -js_InitPGconnClass(JSContext *cx, JSObject *obj) -{ - - return (JS_InitClass(cx, obj, NULL, &pgconn_class, pgconn_constructor, - 0, NULL, pgconn_methods, NULL, NULL)); -} - -/* Non instanciable */ -static JSBool -pgconn_constructor(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - - QUEUE_EXCEPTION("PGconn class not user-instanciable"); - - return JS_FALSE; -} - -static void -pgconn_finalize(JSContext *cx, JSObject *obj) -{ - PGconn *pgc; - - if ((pgc = JS_GetInstancePrivate(cx, obj, &pgconn_class, NULL)) - != NULL) { - PQfinish(pgc); - (void) JS_SetPrivate(cx, obj, NULL); - } -} - - -/* - * PGconn object methods - */ - -static JSBool -pgconn_m_PQfinish(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - PGconn *pgc; - - if (argc != 0) { - QUEUE_EXCEPTION("Function allows no arguments"); - *rval = OBJECT_TO_JSVAL(NULL); - return JS_FALSE; - } - - if ((pgc = JS_GetInstancePrivate(cx, obj, &pgconn_class, NULL)) - != NULL) { - PQfinish(pgc); - (void) JS_SetPrivate(cx, obj, NULL); - } - - *rval = OBJECT_TO_JSVAL(NULL); - - return JS_TRUE; -} - -static JSBool -pgconn_m_PQconnectPoll(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - PGconn *pgc; - PostgresPollingStatusType s; - - if (argc != 0) { - QUEUE_EXCEPTION("Function allows no arguments"); - *rval = OBJECT_TO_JSVAL(NULL); - return JS_FALSE; - } - - pgc = JS_GetInstancePrivate(cx, obj, &pgconn_class, NULL); - assert(pgc != NULL); - - s = PQconnectPoll(pgc); - *rval = INT_TO_JSVAL((int)s); - - return JS_TRUE; -} - -static JSBool -pgconn_m_PQreset(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - PGconn *pgc; - - if (argc != 0) { - QUEUE_EXCEPTION("Function allows no arguments"); - *rval = OBJECT_TO_JSVAL(NULL); - return JS_FALSE; - } - - pgc = JS_GetInstancePrivate(cx, obj, &pgconn_class, NULL); - assert(pgc != NULL); - - PQreset(pgc); - *rval = OBJECT_TO_JSVAL(NULL); - - return JS_TRUE; -} - -static JSBool -pgconn_m_PQresetStart(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - PGconn *pgc; - - if (argc != 0) { - QUEUE_EXCEPTION("Function allows no arguments"); - *rval = OBJECT_TO_JSVAL(NULL); - return JS_FALSE; - } - - pgc = JS_GetInstancePrivate(cx, obj, &pgconn_class, NULL); - assert(pgc != NULL); - - *rval = INT_TO_JSVAL(PQconnectPoll(pgc)); - - return JS_TRUE; -} - -static JSBool -pgconn_m_PQresetPoll(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - PGconn *pgc; - PostgresPollingStatusType s; - - if (argc != 0) { - QUEUE_EXCEPTION("Function allows no arguments"); - *rval = OBJECT_TO_JSVAL(NULL); - return JS_FALSE; - } - - pgc = JS_GetInstancePrivate(cx, obj, &pgconn_class, NULL); - assert(pgc != NULL); - - s = PQresetPoll(pgc); - *rval = INT_TO_JSVAL((int)s); - - return JS_TRUE; -} - -static JSBool -pgconn_m_PQdb(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - PGconn *pgc; - char *str; - - if (argc != 0) { - QUEUE_EXCEPTION("Function allows no arguments"); - *rval = OBJECT_TO_JSVAL(NULL); - return JS_FALSE; - } - - pgc = JS_GetInstancePrivate(cx, obj, &pgconn_class, NULL); - assert(pgc != NULL); - - str = PQdb(pgc); - *rval = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, str)); - - return JS_TRUE; -} - -static JSBool -pgconn_m_PQuser(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - PGconn *pgc; - char *str; - - if (argc != 0) { - QUEUE_EXCEPTION("Function allows no arguments"); - *rval = OBJECT_TO_JSVAL(NULL); - return JS_FALSE; - } - - pgc = JS_GetInstancePrivate(cx, obj, &pgconn_class, NULL); - assert(pgc != NULL); - - str = PQuser(pgc); - *rval = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, str)); - - return JS_TRUE; -} - -static JSBool -pgconn_m_PQpass(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - PGconn *pgc; - char *str; - - if (argc != 0) { - QUEUE_EXCEPTION("Function allows no arguments"); - *rval = OBJECT_TO_JSVAL(NULL); - return JS_FALSE; - } - - pgc = JS_GetInstancePrivate(cx, obj, &pgconn_class, NULL); - assert(pgc != NULL); - - str = PQpass(pgc); - *rval = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, str)); - - return JS_TRUE; -} - -static JSBool -pgconn_m_PQhost(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - PGconn *pgc; - char *str; - - if (argc != 0) { - QUEUE_EXCEPTION("Function allows no arguments"); - *rval = OBJECT_TO_JSVAL(NULL); - return JS_FALSE; - } - - pgc = JS_GetInstancePrivate(cx, obj, &pgconn_class, NULL); - assert(pgc != NULL); - - str = PQhost(pgc); - *rval = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, str)); - - return JS_TRUE; -} - -static JSBool -pgconn_m_PQport(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - PGconn *pgc; - char *str; - - if (argc != 0) { - QUEUE_EXCEPTION("Function allows no arguments"); - *rval = OBJECT_TO_JSVAL(NULL); - return JS_FALSE; - } - - pgc = JS_GetInstancePrivate(cx, obj, &pgconn_class, NULL); - assert(pgc != NULL); - - str = PQport(pgc); - *rval = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, str)); - - return JS_TRUE; -} - -static JSBool -pgconn_m_PQtty(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - PGconn *pgc; - char *str; - - if (argc != 0) { - QUEUE_EXCEPTION("Function allows no arguments"); - *rval = OBJECT_TO_JSVAL(NULL); - return JS_FALSE; - } - - pgc = JS_GetInstancePrivate(cx, obj, &pgconn_class, NULL); - assert(pgc != NULL); - - str = PQtty(pgc); - *rval = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, str)); - - return JS_TRUE; -} - -static JSBool -pgconn_m_PQoptions(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - PGconn *pgc; - char *str; - - if (argc != 0) { - QUEUE_EXCEPTION("Function allows no arguments"); - *rval = OBJECT_TO_JSVAL(NULL); - return JS_FALSE; - } - - pgc = JS_GetInstancePrivate(cx, obj, &pgconn_class, NULL); - assert(pgc != NULL); - - str = PQoptions(pgc); - *rval = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, str)); - - return JS_TRUE; -} - -static JSBool -pgconn_m_PQstatus(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - PGconn *pgc; - ConnStatusType s = CONNECTION_BAD; - - if (argc != 0) { - QUEUE_EXCEPTION("Function allows no arguments"); - *rval = OBJECT_TO_JSVAL(NULL); - return JS_FALSE; - } - - pgc = JS_GetInstancePrivate(cx, obj, &pgconn_class, NULL); - assert(pgc != NULL); - - s = PQstatus(pgc); - *rval = INT_TO_JSVAL((int)s); - - return JS_TRUE; -} - -static JSBool -pgconn_m_PQtransactionStatus(JSContext *cx, JSObject *obj, uintN argc, - jsval *argv, jsval *rval) -{ - PGconn *pgc; - PGTransactionStatusType s; - - if (argc != 0) { - QUEUE_EXCEPTION("Function allows no arguments"); - *rval = OBJECT_TO_JSVAL(NULL); - return JS_FALSE; - } - - pgc = JS_GetInstancePrivate(cx, obj, &pgconn_class, NULL); - assert(pgc != NULL); - - s = PQtransactionStatus(pgc); - *rval = INT_TO_JSVAL((int)s); - - return JS_TRUE; -} - -static JSBool -pgconn_m_PQparameterStatus(JSContext *cx, JSObject *obj, uintN argc, - jsval *argv, jsval *rval) -{ - PGconn *pgc; - char *param; - const char *str; - - *rval = OBJECT_TO_JSVAL(NULL); - - if (argc != 1) { - QUEUE_EXCEPTION("Wrong number of arguments"); - return JS_FALSE; - } - if (!JSVAL_IS_STRING(argv[0])) { - QUEUE_EXCEPTION("Argument not a String"); - return JS_FALSE; - } - param = JS_GetStringBytes(JSVAL_TO_STRING(argv[0])); - - pgc = JS_GetInstancePrivate(cx, obj, &pgconn_class, NULL); - assert(pgc != NULL); - - if ((str = PQparameterStatus(pgc, param)) != NULL) - *rval = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, str)); - - return JS_TRUE; -} - -static JSBool -pgconn_m_PQprotocolVersion(JSContext *cx, JSObject *obj, uintN argc, - jsval *argv, jsval *rval) -{ - PGconn *pgc; - - if (argc != 0) { - QUEUE_EXCEPTION("Function allows no arguments"); - *rval = OBJECT_TO_JSVAL(NULL); - return JS_FALSE; - } - - pgc = JS_GetInstancePrivate(cx, obj, &pgconn_class, NULL); - assert(pgc != NULL); - - *rval = INT_TO_JSVAL(PQprotocolVersion(pgc)); - - return JS_TRUE; -} - -static JSBool -pgconn_m_PQserverVersion(JSContext *cx, JSObject *obj, uintN argc, - jsval *argv, jsval *rval) -{ - PGconn *pgc; - - if (argc != 0) { - QUEUE_EXCEPTION("Function allows no arguments"); - *rval = OBJECT_TO_JSVAL(NULL); - return JS_FALSE; - } - - pgc = JS_GetInstancePrivate(cx, obj, &pgconn_class, NULL); - assert(pgc != NULL); - - *rval = INT_TO_JSVAL(PQserverVersion(pgc)); - - return JS_TRUE; -} - -static JSBool -pgconn_m_PQerrorMessage(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - PGconn *pgc; - char *str; - - if (argc != 0) { - QUEUE_EXCEPTION("Function allows no arguments"); - *rval = OBJECT_TO_JSVAL(NULL); - return JS_FALSE; - } - - pgc = JS_GetInstancePrivate(cx, obj, &pgconn_class, NULL); - assert(pgc != NULL); - - str = PQerrorMessage(pgc); - *rval = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, str)); - - return JS_TRUE; -} - -static JSBool -pgconn_m_PQsocket(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - PGconn *pgc; - - if (argc != 0) { - QUEUE_EXCEPTION("Function allows no arguments"); - *rval = OBJECT_TO_JSVAL(NULL); - return JS_FALSE; - } - - pgc = JS_GetInstancePrivate(cx, obj, &pgconn_class, NULL); - assert(pgc != NULL); - - *rval = INT_TO_JSVAL(PQsocket(pgc)); - - return JS_TRUE; -} - -static JSBool -pgconn_m_PQbackendPID(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - PGconn *pgc; - - if (argc != 0) { - QUEUE_EXCEPTION("Function allows no arguments"); - *rval = OBJECT_TO_JSVAL(NULL); - return JS_FALSE; - } - - pgc = JS_GetInstancePrivate(cx, obj, &pgconn_class, NULL); - assert(pgc != NULL); - - *rval = INT_TO_JSVAL(PQbackendPID(pgc)); - - return JS_TRUE; -} - -static JSBool -pgconn_m_PQexec(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - JSObject *o; - PGconn *pgc; - PGresult *pgr = NULL; - char *str; - - if (argc != 1) { - QUEUE_EXCEPTION("Wrong number of arguments"); - goto err; - } - - if (!JSVAL_IS_STRING(argv[0])) { - QUEUE_EXCEPTION("Argument not a String"); - goto err; - } - str = JS_GetStringBytes(JSVAL_TO_STRING(argv[0])); - - pgc = JS_GetInstancePrivate(cx, obj, &pgconn_class, NULL); - assert(pgc != NULL); - - if ((o = JS_NewObject(cx, &pgresult_class, NULL, NULL)) == NULL) { - QUEUE_EXCEPTION("Internal error!"); - goto err; - } - /* Root immediately */ - *rval = OBJECT_TO_JSVAL(o); - - if ((pgr = PQexec(pgc, str)) == NULL) { - QUEUE_EXCEPTION("PQexec()"); - goto err; - } - if (!JS_SetPrivate(cx, o, pgr)) { - QUEUE_EXCEPTION("Internal error!"); - goto err; - } - - return JS_TRUE; - -err: - if (pgr != NULL) - PQclear(pgr); - - *rval = OBJECT_TO_JSVAL(NULL); - - return JS_FALSE; -} - -static JSBool -pgconn_m_PQsendQuery(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - PGconn *pgc; - char *str; - - if (argc != 1) { - QUEUE_EXCEPTION("Wrong number of arguments"); - goto err; - } - - if (!JSVAL_IS_STRING(argv[0])) { - QUEUE_EXCEPTION("Argument not a String"); - goto err; - } - str = JS_GetStringBytes(JSVAL_TO_STRING(argv[0])); - - pgc = JS_GetInstancePrivate(cx, obj, &pgconn_class, NULL); - assert(pgc != NULL); - - *rval = INT_TO_JSVAL(PQsendQuery(pgc, str)); - - return JS_TRUE; - -err: - *rval = INT_TO_JSVAL(0); - - return JS_FALSE; -} - -/* - * A fairly hairy function. - */ -static JSBool -pgconn_m_PQexecParams2(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval, int async) -{ - int nargs, i; - JSObject *arrays[4], *o; - JSIdArray *a = NULL; - Oid *types = NULL; - char **values = NULL; - int *lengths = NULL; - int *formats = NULL; - char str[256]; - PGconn *pgc; - PGresult *pgr = NULL; - - if (argc != 7) { - QUEUE_EXCEPTION("Wrong number of arguments"); - goto err; - } - if (!JSVAL_IS_STRING(argv[0])) { - QUEUE_EXCEPTION("Argument 1 not a String"); - goto err; - } - if (!JSVAL_IS_INT(argv[1])) { - QUEUE_EXCEPTION("Argument 2 not an int"); - goto err; - } - for (i = 2; i < 6; i++) { - if (JSVAL_IS_NULL(argv[i])) { - arrays[i - 2] = NULL; - continue; - } - if (!JSVAL_IS_OBJECT(argv[i]) || - !JS_IsArrayObject(cx, - (arrays[i - 2] = JSVAL_TO_OBJECT(argv[i])))) { - (void) snprintf(str, 255, "Argument %d not an Array", - i + 1); - QUEUE_EXCEPTION(str); - goto err; - } - } - if (!JSVAL_IS_INT(argv[6])) { - QUEUE_EXCEPTION("Argument 7 not an int"); - goto err; - } - - /* Array arguments processing */ - nargs = JSVAL_TO_INT(argv[1]); - if (nargs < 0) { - QUEUE_EXCEPTION("Argument 2 negative"); - goto err; - } - - if (arrays[0] != NULL) - types = param_types; - if (arrays[1] != NULL) - values = param_values; - if (arrays[2] != NULL) - lengths = param_lengths; - if (arrays[3] != NULL) - formats = param_formats; - - for (i = 0; i < 4; i++) { - jsint len; - - if (arrays[i] == NULL) - continue; - - if (!JS_GetArrayLength(cx, arrays[i], &len) || len != nargs) { - (void) snprintf(str, 255, - "Argument %d Array not holding %d elements", - i + 2, nargs); - QUEUE_EXCEPTION(str); - goto err; - } - } - - if (param_grow(nargs) == -1) { - QUEUE_EXCEPTION("Out of memory!"); - goto err; - } - - /* param_types */ - if (types != NULL) { - jsval id, val; - int i2; - jsdouble v; - - o = arrays[0]; - if ((a = JS_Enumerate(cx, o)) == NULL) { - QUEUE_EXCEPTION("Internal error!"); - goto err; - } - for (i2 = i = 0; i < a->length; i++) { - JS_IdToValue(cx, a->vector[i], &id); - if (JS_LookupElement(cx, o, JSVAL_TO_INT(id), &val) && - !JSVAL_IS_VOID(val)) { - if (!JSVAL_IS_NUMBER(val)) { - (void) snprintf(str, 255, - "Argument 3 Array's element %d " - "not a number", i2); - QUEUE_EXCEPTION(str); - goto err; - } - if (!JS_ValueToNumber(cx, val, &v)) { - QUEUE_EXCEPTION("Internal error!"); - goto err; - } - types[i2++] = (Oid)v; - } - } - JS_DestroyIdArray(cx, a); - a = NULL; - } - - /* param_values */ - if (values != NULL) { - jsval id, val; - int i2; - - o = arrays[1]; - if ((a = JS_Enumerate(cx, o)) == NULL) { - QUEUE_EXCEPTION("Internal error!"); - goto err; - } - for (i2 = i = 0; i < a->length; i++) { - JS_IdToValue(cx, a->vector[i], &id); - if (JS_LookupElement(cx, o, JSVAL_TO_INT(id), &val) && - !JSVAL_IS_VOID(val)) { - if (JSVAL_IS_NULL(val)) { - values[i2++] = NULL; - continue; - } - if (!JSVAL_IS_STRING(val)) { - (void) snprintf(str, 255, - "Argument 4 Array's element %d " - "not a String", i2); - QUEUE_EXCEPTION(str); - goto err; - } - values[i2++] = JS_GetStringBytes( - JSVAL_TO_STRING(val)); - } - } - JS_DestroyIdArray(cx, a); - a = NULL; - } - - /* param_lengths */ - if (lengths != NULL) { - jsval id, val; - int i2; - - o = arrays[2]; - if ((a = JS_Enumerate(cx, o)) == NULL) { - QUEUE_EXCEPTION("Internal error!"); - goto err; - } - for (i2 = i = 0; i < a->length; i++) { - JS_IdToValue(cx, a->vector[i], &id); - if (JS_LookupElement(cx, o, JSVAL_TO_INT(id), &val) && - !JSVAL_IS_VOID(val)) { - if (!JSVAL_IS_INT(val)) { - (void) snprintf(str, 255, - "Argument 5 Array's element %d " - "not an int", i2); - QUEUE_EXCEPTION(str); - goto err; - } - lengths[i2++] = JSVAL_TO_INT(val); - } - } - JS_DestroyIdArray(cx, a); - a = NULL; - } - - /* param_formats */ - if (formats != NULL) { - jsval id, val; - int i2; - - o = arrays[3]; - if ((a = JS_Enumerate(cx, o)) == NULL) { - QUEUE_EXCEPTION("Internal error!"); - goto err; - } - for (i2 = i = 0; i < a->length; i++) { - JS_IdToValue(cx, a->vector[i], &id); - if (JS_LookupElement(cx, o, JSVAL_TO_INT(id), &val) && - !JSVAL_IS_VOID(val)) { - if (!JSVAL_IS_INT(val)) { - (void) snprintf(str, 255, - "Argument 6 Array's element %d " - "not an int", i2); - QUEUE_EXCEPTION(str); - goto err; - } - formats[i2++] = JSVAL_TO_INT(val); - } - } - JS_DestroyIdArray(cx, a); - a = NULL; - } - - pgc = JS_GetInstancePrivate(cx, obj, &pgconn_class, NULL); - assert(pgc != NULL); - - if (!async) { - - if ((pgr = PQexecParams(pgc, - JS_GetStringBytes(JSVAL_TO_STRING(argv[0])), - nargs, types, (const char * const *)values, lengths, - formats, JSVAL_TO_INT(argv[6]))) == NULL) { - QUEUE_EXCEPTION("PQexecParams()"); - goto err; - } - - if ((o = JS_NewObject(cx, &pgresult_class, NULL, NULL)) - == NULL) { - QUEUE_EXCEPTION("Internal error!"); - goto err; - } - /* Root immediately */ - *rval = OBJECT_TO_JSVAL(o); - - if (!JS_SetPrivate(cx, o, pgr)) { - QUEUE_EXCEPTION("Internal error!"); - goto err; - } - - } else { - - *rval = INT_TO_JSVAL(PQsendQueryParams(pgc, - JS_GetStringBytes(JSVAL_TO_STRING(argv[0])), - nargs, types, (const char * const *)values, lengths, - formats, JSVAL_TO_INT(argv[6]))); - - } - - return JS_TRUE; - -err: - if (pgr != NULL) - PQclear(pgr); - if (a != NULL) - JS_DestroyIdArray(cx, a); - - if (!async) - *rval = OBJECT_TO_JSVAL(NULL); - else - *rval = INT_TO_JSVAL(0); - - return JS_FALSE; -} - -static JSBool -pgconn_m_PQexecParams(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - - return pgconn_m_PQexecParams2(cx, obj, argc, argv, rval, 0); -} - -static JSBool -pgconn_m_PQsendQueryParams(JSContext *cx, JSObject *obj, uintN argc, - jsval *argv, jsval *rval) -{ - - return pgconn_m_PQexecParams2(cx, obj, argc, argv, rval, 1); -} - -static JSBool -pgconn_m_PQprepare2(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval, int async) -{ - int nargs, i; - JSObject *array, *o; - JSIdArray *a = NULL; - Oid *types = NULL; - char str[256]; - PGconn *pgc; - PGresult *pgr = NULL; - - if (argc != 4) { - QUEUE_EXCEPTION("Wrong number of arguments"); - goto err; - } - if (!JSVAL_IS_STRING(argv[0])) { - QUEUE_EXCEPTION("Argument 1 not a String"); - goto err; - } - if (!JSVAL_IS_STRING(argv[1])) { - QUEUE_EXCEPTION("Argument 2 not a String"); - goto err; - } - if (!JSVAL_IS_INT(argv[2])) { - QUEUE_EXCEPTION("Argument 3 not an int"); - goto err; - } - if (JSVAL_IS_NULL(argv[3])) - array = NULL; - else { - if (!JSVAL_IS_OBJECT(argv[3]) || - !JS_IsArrayObject(cx, - (array = JSVAL_TO_OBJECT(argv[3])))) { - QUEUE_EXCEPTION("Argument 4 not an Array"); - goto err; - } - } - - /* Array arguments processing */ - nargs = JSVAL_TO_INT(argv[2]); - if (nargs < 0) { - QUEUE_EXCEPTION("Argument 3 negative"); - goto err; - } - - if (array != NULL) { - jsint len; - - types = param_types; - - if (!JS_GetArrayLength(cx, array, &len) || len != nargs) { - (void) snprintf(str, 255, - "Argument 4 Array not holding %d elements", - nargs); - QUEUE_EXCEPTION(str); - goto err; - } - } - - if (param_grow(nargs) == -1) { - QUEUE_EXCEPTION("Out of memory!"); - goto err; - } - - /* param_types */ - if (types != NULL) { - jsval id, val; - int i2; - jsdouble v; - - o = array; - if ((a = JS_Enumerate(cx, o)) == NULL) { - QUEUE_EXCEPTION("Internal error!"); - goto err; - } - for (i2 = i = 0; i < a->length; i++) { - JS_IdToValue(cx, a->vector[i], &id); - if (JS_LookupElement(cx, o, JSVAL_TO_INT(id), &val) && - !JSVAL_IS_VOID(val)) { - if (!JSVAL_IS_NUMBER(val)) { - (void) snprintf(str, 255, - "Argument 4 Array's element %d " - "not a number", i2); - QUEUE_EXCEPTION(str); - goto err; - } - if (!JS_ValueToNumber(cx, val, &v)) { - QUEUE_EXCEPTION("Internal error!"); - goto err; - } - types[i2++] = (Oid)v; - } - } - JS_DestroyIdArray(cx, a); - a = NULL; - } - - pgc = JS_GetInstancePrivate(cx, obj, &pgconn_class, NULL); - assert(pgc != NULL); - - if (!async) { - - if ((pgr = PQprepare(pgc, - JS_GetStringBytes(JSVAL_TO_STRING(argv[0])), - JS_GetStringBytes(JSVAL_TO_STRING(argv[1])), - nargs, types)) == NULL) { - QUEUE_EXCEPTION("PQprepare()"); - goto err; - } - - if ((o = JS_NewObject(cx, &pgresult_class, NULL, NULL)) - == NULL) { - QUEUE_EXCEPTION("Internal error!"); - goto err; - } - /* Root immediately */ - *rval = OBJECT_TO_JSVAL(o); - - if (!JS_SetPrivate(cx, o, pgr)) { - QUEUE_EXCEPTION("Internal error!"); - goto err; - } - - } else { - - *rval = INT_TO_JSVAL(PQsendPrepare(pgc, - JS_GetStringBytes(JSVAL_TO_STRING(argv[0])), - JS_GetStringBytes(JSVAL_TO_STRING(argv[1])), - nargs, types)); - - } - - return JS_TRUE; - -err: - if (pgr != NULL) - PQclear(pgr); - - if (!async) - *rval = OBJECT_TO_JSVAL(NULL); - else - *rval = INT_TO_JSVAL(0); - - return JS_FALSE; -} - -static JSBool -pgconn_m_PQprepare(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - - return pgconn_m_PQprepare2(cx, obj, argc, argv, rval, 0); -} - -static JSBool -pgconn_m_PQsendPrepare(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - - return pgconn_m_PQprepare2(cx, obj, argc, argv, rval, 1); -} - -/* - * Also rather hairy - */ -static JSBool -pgconn_m_PQexecPrepared2(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval, int async) -{ - int nargs, i; - JSObject *arrays[3], *o; - JSIdArray *a = NULL; - char **values = NULL; - int *lengths = NULL; - int *formats = NULL; - char str[256]; - PGconn *pgc; - PGresult *pgr = NULL; - - if (argc != 6) { - QUEUE_EXCEPTION("Wrong number of arguments"); - goto err; - } - if (!JSVAL_IS_STRING(argv[0])) { - QUEUE_EXCEPTION("Argument 1 not a String"); - goto err; - } - if (!JSVAL_IS_INT(argv[1])) { - QUEUE_EXCEPTION("Argument 2 not an int"); - goto err; - } - for (i = 2; i < 5; i++) { - if (JSVAL_IS_NULL(argv[i])) { - arrays[i - 2] = NULL; - continue; - } - if (!JSVAL_IS_OBJECT(argv[i]) || - !JS_IsArrayObject(cx, - (arrays[i - 2] = JSVAL_TO_OBJECT(argv[i])))) { - (void) snprintf(str, 255, "Argument %d not an Array", - i + 1); - QUEUE_EXCEPTION(str); - goto err; - } - } - if (!JSVAL_IS_INT(argv[5])) { - QUEUE_EXCEPTION("Argument 7 not an int"); - goto err; - } - - /* Array arguments processing */ - nargs = JSVAL_TO_INT(argv[1]); - if (nargs < 0) { - QUEUE_EXCEPTION("Argument 2 negative"); - goto err; - } - - if (arrays[0] != NULL) - values = param_values; - if (arrays[1] != NULL) - lengths = param_lengths; - if (arrays[2] != NULL) - formats = param_formats; - - for (i = 0; i < 3; i++) { - jsint len; - - if (arrays[i] == NULL) - continue; - - if (!JS_GetArrayLength(cx, arrays[i], &len) || len != nargs) { - (void) snprintf(str, 255, - "Argument %d Array not holding %d elements", - i + 2, nargs); - QUEUE_EXCEPTION(str); - goto err; - } - } - - if (param_grow(nargs) == -1) { - QUEUE_EXCEPTION("Out of memory!"); - goto err; - } - - /* param_values */ - if (values != NULL) { - jsval id, val; - int i2; - - o = arrays[0]; - if ((a = JS_Enumerate(cx, o)) == NULL) { - QUEUE_EXCEPTION("Internal error!"); - goto err; - } - for (i2 = i = 0; i < a->length; i++) { - JS_IdToValue(cx, a->vector[i], &id); - if (JS_LookupElement(cx, o, JSVAL_TO_INT(id), &val) && - !JSVAL_IS_VOID(val)) { - if (JSVAL_IS_NULL(val)) { - values[i2++] = NULL; - continue; - } - if (!JSVAL_IS_STRING(val)) { - (void) snprintf(str, 255, - "Argument 3 Array's element %d " - "not a String", i2); - QUEUE_EXCEPTION(str); - goto err; - } - values[i2++] = JS_GetStringBytes( - JSVAL_TO_STRING(val)); - } - } - JS_DestroyIdArray(cx, a); - a = NULL; - } - - /* param_lengths */ - if (lengths != NULL) { - jsval id, val; - int i2; - - o = arrays[1]; - if ((a = JS_Enumerate(cx, o)) == NULL) { - QUEUE_EXCEPTION("Internal error!"); - goto err; - } - for (i2 = i = 0; i < a->length; i++) { - JS_IdToValue(cx, a->vector[i], &id); - if (JS_LookupElement(cx, o, JSVAL_TO_INT(id), &val) && - !JSVAL_IS_VOID(val)) { - if (!JSVAL_IS_INT(val)) { - (void) snprintf(str, 255, - "Argument 4 Array's element %d " - "not an int", i2); - QUEUE_EXCEPTION(str); - goto err; - } - lengths[i2++] = JSVAL_TO_INT(val); - } - } - JS_DestroyIdArray(cx, a); - a = NULL; - } - - /* param_formats */ - if (formats != NULL) { - jsval id, val; - int i2; - - o = arrays[2]; - if ((a = JS_Enumerate(cx, o)) == NULL) { - QUEUE_EXCEPTION("Internal error!"); - goto err; - } - for (i2 = i = 0; i < a->length; i++) { - JS_IdToValue(cx, a->vector[i], &id); - if (JS_LookupElement(cx, o, JSVAL_TO_INT(id), &val) && - !JSVAL_IS_VOID(val)) { - if (!JSVAL_IS_INT(val)) { - (void) snprintf(str, 255, - "Argument 5 Array's element %d " - "not an int", i2); - QUEUE_EXCEPTION(str); - goto err; - } - formats[i2++] = JSVAL_TO_INT(val); - } - } - JS_DestroyIdArray(cx, a); - a = NULL; - } - - pgc = JS_GetInstancePrivate(cx, obj, &pgconn_class, NULL); - assert(pgc != NULL); - - if (!async) { - - if ((pgr = PQexecPrepared(pgc, - JS_GetStringBytes(JSVAL_TO_STRING(argv[0])), - nargs, (const char * const *)values, lengths, formats, - JSVAL_TO_INT(argv[5]))) == NULL) { - QUEUE_EXCEPTION("PQexecPrepared()"); - goto err; - } - - if ((o = JS_NewObject(cx, &pgresult_class, NULL, NULL)) - == NULL) { - QUEUE_EXCEPTION("Internal error!"); - goto err; - } - /* Root immediately */ - *rval = OBJECT_TO_JSVAL(o); - - if (!JS_SetPrivate(cx, o, pgr)) { - QUEUE_EXCEPTION("Internal error!"); - goto err; - } - - } else { - - *rval = INT_TO_JSVAL(PQsendQueryPrepared(pgc, - JS_GetStringBytes(JSVAL_TO_STRING(argv[0])), - nargs, (const char * const *)values, lengths, formats, - JSVAL_TO_INT(argv[5]))); - - } - - return JS_TRUE; - -err: - if (pgr != NULL) - PQclear(pgr); - if (a != NULL) - JS_DestroyIdArray(cx, a); - - if (!async) - *rval = OBJECT_TO_JSVAL(NULL); - else - *rval = INT_TO_JSVAL(0); - - return JS_FALSE; -} - -static JSBool -pgconn_m_PQexecPrepared(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - - return pgconn_m_PQexecPrepared2(cx, obj, argc, argv, rval, 0); -} - -static JSBool -pgconn_m_PQsendQueryPrepared(JSContext *cx, JSObject *obj, uintN argc, - jsval *argv, jsval *rval) -{ - - return pgconn_m_PQexecPrepared2(cx, obj, argc, argv, rval, 1); -} - -static JSBool -pgconn_m_PQmakeEmptyPGresult(JSContext *cx, JSObject *obj, uintN argc, - jsval *argv, jsval *rval) -{ - JSObject *o; - PGconn *pgc; - PGresult *pgr = NULL; - - if (argc != 1) { - QUEUE_EXCEPTION("Wrong number of arguments"); - goto err; - } - if (!JSVAL_IS_INT(argv[0])) { - QUEUE_EXCEPTION("Argument not an int"); - goto err; - } - - pgc = JS_GetInstancePrivate(cx, obj, &pgconn_class, NULL); - assert(pgc != NULL); - - if ((o = JS_NewObject(cx, &pgresult_class, NULL, NULL)) == NULL) { - QUEUE_EXCEPTION("Internal error!"); - goto err; - } - /* Root immediately */ - *rval = OBJECT_TO_JSVAL(o); - - if ((pgr = PQmakeEmptyPGresult(pgc, JSVAL_TO_INT(argv[0]))) == NULL) { - QUEUE_EXCEPTION("PQmakeEmptyPGresult()"); - goto err; - } - - if (!JS_SetPrivate(cx, o, pgr)) { - QUEUE_EXCEPTION("Internal error!"); - goto err; - } - - return JS_TRUE; - -err: - if (pgr != NULL) - PQclear(pgr); - - *rval = OBJECT_TO_JSVAL(NULL); - - return JS_FALSE; -} - -/* - * Note: Semantics are different from C PQescapeStringConn() in that it is - * supplied a single String and that it returns a resulting String, or null on - * error. Much easier to work with within ECMAScript this way. - */ -static JSBool -pgconn_m_PQescapeStringConn(JSContext *cx, JSObject *obj, uintN argc, - jsval *argv, jsval *rval) -{ - PGconn *pgc; - JSString *str; - char *from; - size_t len; - int ret; - - if (argc != 1) { - QUEUE_EXCEPTION("Wrong number of arguments"); - goto err; - } - if (!JSVAL_IS_STRING(argv[0])) { - QUEUE_EXCEPTION("Argument not a String"); - goto err; - } - - pgc = JS_GetInstancePrivate(cx, obj, &pgconn_class, NULL); - assert(pgc != NULL); - - str = JSVAL_TO_STRING(argv[0]); - from = JS_GetStringBytes(str); - len = JS_GetStringLength(str); - - if (buffer_grow((len * 2) + 2) == -1) { - QUEUE_EXCEPTION("Out of memory!"); - goto err; - } - - len = PQescapeStringConn(pgc, buffer, from, len, &ret); - if (ret != 0) { - QUEUE_EXCEPTION("PQescapeStringConn()"); - goto err; - } - - if ((str = JS_NewStringCopyN(cx, buffer, len)) == NULL) { - QUEUE_EXCEPTION("Out of memory!"); - goto err; - } - *rval = STRING_TO_JSVAL(str); - - return JS_TRUE; - -err: - *rval = OBJECT_TO_JSVAL(NULL); - - return JS_FALSE; -} - -/* PQescapeString() deprecated in favor of PQescapeStringConn() */ - -/* - * Note: Semantics are different from C PQescapeByteaConn() in that it is - * supplied a single String and that it returns a resulting String, or null on - * error. Much easier to work with within ECMAScript this way. - */ -static JSBool -pgconn_m_PQescapeByteaConn(JSContext *cx, JSObject *obj, uintN argc, - jsval *argv, jsval *rval) -{ - PGconn *pgc; - JSString *str; - char *from; - size_t fromlen, reslen; - unsigned char *res = NULL; - - if (argc != 1) { - QUEUE_EXCEPTION("Wrong number of arguments"); - goto err; - } - if (!JSVAL_IS_STRING(argv[0])) { - QUEUE_EXCEPTION("Argument not a String"); - goto err; - } - - pgc = JS_GetInstancePrivate(cx, obj, &pgconn_class, NULL); - assert(pgc != NULL); - - str = JSVAL_TO_STRING(argv[0]); - from = JS_GetStringBytes(str); - fromlen = JS_GetStringLength(str); - - if ((res = PQescapeByteaConn(pgc, from, fromlen, &reslen)) == NULL) { - QUEUE_EXCEPTION("PQescapeByteaConn()"); - goto err; - } - - if ((str = JS_NewStringCopyN(cx, res, reslen)) == NULL) { - QUEUE_EXCEPTION("Out of memory!"); - goto err; - } - - PQfreemem(res); - *rval = STRING_TO_JSVAL(str); - - return JS_TRUE; - -err: - if (res != NULL) - PQfreemem(res); - - *rval = OBJECT_TO_JSVAL(NULL); - - return JS_FALSE; -} - -static JSBool -pgconn_m_PQgetCancel(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - JSObject *o; - PGconn *pgc; - PGcancel *pgcn = NULL; - - if (argc != 0) { - QUEUE_EXCEPTION("Function allows no arguments"); - *rval = OBJECT_TO_JSVAL(NULL); - return JS_FALSE; - } - - pgc = JS_GetInstancePrivate(cx, obj, &pgconn_class, NULL); - assert(pgc != NULL); - - if ((o = JS_NewObject(cx, &pgcancel_class, NULL, NULL)) == NULL) { - QUEUE_EXCEPTION("Internal error!"); - goto err; - } - /* Root immediately */ - *rval = OBJECT_TO_JSVAL(o); - - if ((pgcn = PQgetCancel(pgc)) == NULL) { - QUEUE_EXCEPTION("PQgetCancel()"); - goto err; - } - if (!JS_SetPrivate(cx, o, pgcn)) { - QUEUE_EXCEPTION("Internal error!"); - goto err; - } - - return JS_TRUE; - -err: - if (pgcn != NULL) - PQfreeCancel(pgcn); - - *rval = OBJECT_TO_JSVAL(NULL); - - return JS_FALSE; -} - -/* - * Note: Unlike C native PQnotifies(), returns null or a normal object with - * the three properties set, rather than specifically a PQnotify object. - */ -static JSBool -pgconn_m_PQnotifies(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - JSObject *o; - PGconn *pgc; - PGnotify *pgn = NULL; - - if (argc != 0) { - QUEUE_EXCEPTION("Function allows no arguments"); - *rval = OBJECT_TO_JSVAL(NULL); - return JS_FALSE; - } - - pgc = JS_GetInstancePrivate(cx, obj, &pgconn_class, NULL); - assert(pgc != NULL); - - if ((pgn = PQnotifies(pgc)) == NULL) { - *rval = OBJECT_TO_JSVAL(NULL); - return JS_TRUE; - } - - if ((o = JS_NewObject(cx, NULL, NULL, NULL)) == NULL) { - QUEUE_EXCEPTION("Internal error!"); - goto err; - } - /* Root immediately */ - *rval = OBJECT_TO_JSVAL(o); - - if (!JS_DefineProperty(cx, o, "relname", STRING_TO_JSVAL( - JS_NewStringCopyZ(cx, pgn->relname)), NULL, NULL, - JSPROP_ENUMERATE)) { - QUEUE_EXCEPTION("Internal error!"); - goto err; - } - if (!JS_DefineProperty(cx, o, "be_pid", INT_TO_JSVAL(pgn->be_pid), - NULL, NULL, JSPROP_ENUMERATE)) { - QUEUE_EXCEPTION("Internal error!"); - goto err; - } - if (!JS_DefineProperty(cx, o, "extra", STRING_TO_JSVAL( - JS_NewStringCopyZ(cx, pgn->extra)), NULL, NULL, - JSPROP_ENUMERATE)) { - QUEUE_EXCEPTION("Internal error!"); - goto err; - } - - PQfreemem(pgn); - - return JS_TRUE; - -err: - if (pgn != NULL) - PQfreemem(pgn); - - *rval = OBJECT_TO_JSVAL(NULL); - - return JS_FALSE; -} - -static JSBool -pgconn_m_PQgetResult(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - JSObject *o; - PGconn *pgc; - PGresult *pgr = NULL; - - if (argc != 0) { - QUEUE_EXCEPTION("Function allows no arguments"); - *rval = OBJECT_TO_JSVAL(NULL); - return JS_FALSE; - } - - pgc = JS_GetInstancePrivate(cx, obj, &pgconn_class, NULL); - assert(pgc != NULL); - - if ((pgr = PQgetResult(pgc)) == NULL) { - *rval = OBJECT_TO_JSVAL(NULL); - return JS_TRUE; - } - - if ((o = JS_NewObject(cx, &pgresult_class, NULL, NULL)) == NULL) { - QUEUE_EXCEPTION("Internal error!"); - goto err; - } - /* Root immediately */ - *rval = OBJECT_TO_JSVAL(o); - - if (!JS_SetPrivate(cx, o, pgr)) { - QUEUE_EXCEPTION("Internal error!"); - goto err; - } - - return JS_TRUE; - -err: - if (pgr != NULL) - PQclear(pgr); - - *rval = OBJECT_TO_JSVAL(NULL); - - return JS_FALSE; -} - -static JSBool -pgconn_m_PQconsumeInput(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - PGconn *pgc; - - if (argc != 0) { - QUEUE_EXCEPTION("Function allows no arguments"); - *rval = OBJECT_TO_JSVAL(NULL); - return JS_FALSE; - } - - pgc = JS_GetInstancePrivate(cx, obj, &pgconn_class, NULL); - assert(pgc != NULL); - - *rval = INT_TO_JSVAL(PQconsumeInput(pgc)); - - return JS_TRUE; -} - -static JSBool -pgconn_m_PQisBusy(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - PGconn *pgc; - - if (argc != 0) { - QUEUE_EXCEPTION("Function allows no arguments"); - *rval = OBJECT_TO_JSVAL(NULL); - return JS_FALSE; - } - - pgc = JS_GetInstancePrivate(cx, obj, &pgconn_class, NULL); - assert(pgc != NULL); - - *rval = INT_TO_JSVAL(PQisBusy(pgc)); - - return JS_TRUE; -} - -static JSBool -pgconn_m_PQsetnonblocking(JSContext *cx, JSObject *obj, uintN argc, - jsval *argv, jsval *rval) -{ - PGconn *pgc; - - if (argc != 1) { - QUEUE_EXCEPTION("Wrong number of arguments"); - goto err; - } - if (!JSVAL_IS_INT(argv[0])) { - QUEUE_EXCEPTION("Argument 1 not an int"); - goto err; - } - - pgc = JS_GetInstancePrivate(cx, obj, &pgconn_class, NULL); - assert(pgc != NULL); - - *rval = INT_TO_JSVAL(PQsetnonblocking(pgc, JSVAL_TO_INT(argv[0]))); - - return JS_TRUE; - -err: - *rval = OBJECT_TO_JSVAL(NULL); - - return JS_FALSE; -} - -static JSBool -pgconn_m_PQisnonblocking(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - PGconn *pgc; - - if (argc != 0) { - QUEUE_EXCEPTION("Function allows no arguments"); - *rval = OBJECT_TO_JSVAL(NULL); - return JS_FALSE; - } - - pgc = JS_GetInstancePrivate(cx, obj, &pgconn_class, NULL); - assert(pgc != NULL); - - *rval = INT_TO_JSVAL(PQisnonblocking(pgc)); - - return JS_TRUE; -} - -static JSBool -pgconn_m_PQflush(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - PGconn *pgc; - - if (argc != 0) { - QUEUE_EXCEPTION("Function allows no arguments"); - *rval = OBJECT_TO_JSVAL(NULL); - return JS_FALSE; - } - - pgc = JS_GetInstancePrivate(cx, obj, &pgconn_class, NULL); - assert(pgc != NULL); - - *rval = INT_TO_JSVAL(PQflush(pgc)); - - return JS_TRUE; -} - -static JSBool -pgconn_m_PQsetErrorVerbosity(JSContext *cx, JSObject *obj, uintN argc, - jsval *argv, jsval *rval) -{ - PGconn *pgc; - - if (argc != 1) { - QUEUE_EXCEPTION("Wrong number of arguments"); - goto err; - } - if (!JSVAL_IS_INT(argv[0])) { - QUEUE_EXCEPTION("Argument 1 not an int"); - goto err; - } - - pgc = JS_GetInstancePrivate(cx, obj, &pgconn_class, NULL); - assert(pgc != NULL); - - *rval = INT_TO_JSVAL(PQsetErrorVerbosity(pgc, JSVAL_TO_INT(argv[0]))); - - return JS_TRUE; - -err: - *rval = OBJECT_TO_JSVAL(NULL); - - return JS_FALSE; -} - -static JSBool -pgconn_m_PQtrace(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - PGconn *pgc; - - if (argc != 0) { - QUEUE_EXCEPTION("Function allows no arguments"); - *rval = OBJECT_TO_JSVAL(NULL); - return JS_FALSE; - } - - pgc = JS_GetInstancePrivate(cx, obj, &pgconn_class, NULL); - assert(pgc != NULL); - - *rval = OBJECT_TO_JSVAL(NULL); - PQtrace(pgc, stderr); - - return JS_TRUE; -} - -static JSBool -pgconn_m_PQuntrace(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - PGconn *pgc; - - if (argc != 0) { - QUEUE_EXCEPTION("Function allows no arguments"); - *rval = OBJECT_TO_JSVAL(NULL); - return JS_FALSE; - } - - pgc = JS_GetInstancePrivate(cx, obj, &pgconn_class, NULL); - assert(pgc != NULL); - - *rval = OBJECT_TO_JSVAL(NULL); - PQuntrace(pgc); - - return JS_TRUE; -} - -static JSBool -pgconn_m_PQputCopyData(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - PGconn *pgc; - JSString *str; - - if (argc != 1) { - QUEUE_EXCEPTION("Wrong number of arguments"); - goto err; - } - if (!JSVAL_IS_STRING(argv[0])) { - QUEUE_EXCEPTION("Argument not a String"); - goto err; - } - - pgc = JS_GetInstancePrivate(cx, obj, &pgconn_class, NULL); - assert(pgc != NULL); - - str = JSVAL_TO_STRING(argv[0]); - *rval = INT_TO_JSVAL(PQputCopyData(pgc, JS_GetStringBytes(str), - JS_GetStringLength(str))); - - return JS_TRUE; - -err: - *rval = OBJECT_TO_JSVAL(NULL); - - return JS_FALSE; -} - -static JSBool -pgconn_m_PQputCopyEnd(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - PGconn *pgc; - - if (argc != 1) { - QUEUE_EXCEPTION("Wrong number of arguments"); - goto err; - } - if (!JSVAL_IS_STRING(argv[0]) && !JSVAL_IS_NULL(argv[0])) { - QUEUE_EXCEPTION("Argument not a String or null"); - goto err; - } - - pgc = JS_GetInstancePrivate(cx, obj, &pgconn_class, NULL); - assert(pgc != NULL); - - *rval = INT_TO_JSVAL(PQputCopyEnd(pgc, (JSVAL_IS_NULL(argv[0]) ? NULL : - JS_GetStringBytes(JSVAL_TO_STRING(argv[0]))))); - - return JS_TRUE; - -err: - *rval = OBJECT_TO_JSVAL(NULL); - - return JS_FALSE; -} - -/* - * Note: unlike native PQgetCopyData(), which returns an int but is supplied a - * pointer to a pointer to be set, this implementation returns an object which - * holds two elements: result (the integer) and data (null or String), to make - * it easier to use with ECMAScript. - */ -static JSBool -pgconn_m_PQgetCopyData(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - JSObject *o; - PGconn *pgc; - char *data = NULL; - int res; - - if (argc != 1) { - QUEUE_EXCEPTION("Wrong number of arguments"); - goto err; - } - if (!JSVAL_IS_INT(argv[0])) { - QUEUE_EXCEPTION("Argument 1 not an int"); - goto err; - } - - pgc = JS_GetInstancePrivate(cx, obj, &pgconn_class, NULL); - assert(pgc != NULL); - - res = PQgetCopyData(pgc, &data, JSVAL_TO_INT(argv[0])); - - if ((o = JS_NewObject(cx, NULL, NULL, NULL)) == NULL) { - QUEUE_EXCEPTION("Internal error!"); - goto err; - } - /* Root immediately */ - *rval = OBJECT_TO_JSVAL(o); - - if (!JS_DefineProperty(cx, o, "result", INT_TO_JSVAL(res), - NULL, NULL, JSPROP_ENUMERATE)) { - QUEUE_EXCEPTION("Internal error!"); - goto err; - } - if (data != NULL && res > 0) { - if (!JS_DefineProperty(cx, o, "data", STRING_TO_JSVAL( - JS_NewStringCopyN(cx, data, res)), NULL, NULL, - JSPROP_ENUMERATE)) { - QUEUE_EXCEPTION("Internal error!"); - goto err; - } - PQfreemem(data); - } else { - if (!JS_DefineProperty(cx, o, "data", OBJECT_TO_JSVAL(NULL), - NULL, NULL, JSPROP_ENUMERATE)) { - QUEUE_EXCEPTION("Internal error!"); - goto err; - } - } - - return JS_TRUE; - -err: - if (data != NULL) - PQfreemem(data); - - *rval = OBJECT_TO_JSVAL(NULL); - - return JS_FALSE; -} - - -/* - * PGresult object control - */ - -static JSObject * -js_InitPGresultClass(JSContext *cx, JSObject *obj) -{ - - return (JS_InitClass(cx, obj, NULL, &pgresult_class, - pgresult_constructor, 0, NULL, pgresult_methods, NULL, NULL)); -} - -/* Non instanciable */ -static JSBool -pgresult_constructor(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - - QUEUE_EXCEPTION("PGresult class not user-instanciable"); - - return JS_FALSE; -} - -static void -pgresult_finalize(JSContext *cx, JSObject *obj) -{ - PGresult *pgr; - - if ((pgr = JS_GetInstancePrivate(cx, obj, &pgresult_class, NULL)) - != NULL) { - PQclear(pgr); - (void) JS_SetPrivate(cx, obj, NULL); - } -} - - -/* - * PGresult object methods - */ - -static JSBool -pgresult_m_PQclear(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - PGresult *pgr; - - if (argc != 0) { - QUEUE_EXCEPTION("Function allows no arguments"); - *rval = OBJECT_TO_JSVAL(NULL); - return JS_FALSE; - } - - if ((pgr = JS_GetInstancePrivate(cx, obj, &pgresult_class, NULL)) - != NULL) { - PQclear(pgr); - (void) JS_SetPrivate(cx, obj, NULL); - } - - *rval = OBJECT_TO_JSVAL(NULL); - - return JS_TRUE; -} - -static JSBool -pgresult_m_PQresultStatus(JSContext *cx, JSObject *obj, uintN argc, - jsval *argv, jsval *rval) -{ - PGresult *pgr; - ExecStatusType s; - - if (argc != 0) { - QUEUE_EXCEPTION("Function allows no arguments"); - *rval = OBJECT_TO_JSVAL(NULL); - return JS_FALSE; - } - - pgr = JS_GetInstancePrivate(cx, obj, &pgresult_class, NULL); - assert(pgr != NULL); - - s = PQresultStatus(pgr); - *rval = INT_TO_JSVAL((int)s); - - return JS_TRUE; -} - -static JSBool -pgresult_m_PQresultErrorMessage(JSContext *cx, JSObject *obj, uintN argc, - jsval *argv, jsval *rval) -{ - PGresult *pgr; - char *res; - - if (argc != 0) { - QUEUE_EXCEPTION("Function allows no arguments"); - *rval = OBJECT_TO_JSVAL(NULL); - return JS_FALSE; - } - - pgr = JS_GetInstancePrivate(cx, obj, &pgresult_class, NULL); - assert(pgr != NULL); - - res = PQresultErrorMessage(pgr); - *rval = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, res)); - - return JS_TRUE; -} - -static JSBool -pgresult_m_PQresultErrorField(JSContext *cx, JSObject *obj, uintN argc, - jsval *argv, jsval *rval) -{ - PGresult *pgr; - char *res; - - if (argc != 1) { - QUEUE_EXCEPTION("Wrong number of arguments"); - goto err; - } - if (!JSVAL_IS_INT(argv[0])) { - QUEUE_EXCEPTION("Argument not an int"); - goto err; - } - - pgr = JS_GetInstancePrivate(cx, obj, &pgresult_class, NULL); - assert(pgr != NULL); - - res = PQresultErrorField(pgr, JSVAL_TO_INT(argv[0])); - *rval = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, res)); - - return JS_TRUE; - -err: - *rval = OBJECT_TO_JSVAL(NULL); - - return JS_FALSE; -} - -static JSBool -pgresult_m_PQntuples(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - PGresult *pgr; - - if (argc != 0) { - QUEUE_EXCEPTION("Function allows no arguments"); - *rval = OBJECT_TO_JSVAL(NULL); - return JS_FALSE; - } - - pgr = JS_GetInstancePrivate(cx, obj, &pgresult_class, NULL); - assert(pgr != NULL); - - *rval = INT_TO_JSVAL(PQntuples(pgr)); - - return JS_TRUE; -} - -static JSBool -pgresult_m_PQnfields(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - PGresult *pgr; - - if (argc != 0) { - QUEUE_EXCEPTION("Function allows no arguments"); - *rval = OBJECT_TO_JSVAL(NULL); - return JS_FALSE; - } - - pgr = JS_GetInstancePrivate(cx, obj, &pgresult_class, NULL); - assert(pgr != NULL); - - *rval = INT_TO_JSVAL(PQnfields(pgr)); - - return JS_TRUE; -} - -static JSBool -pgresult_m_PQfname(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - PGresult *pgr; - char *res; - - if (argc != 1) { - QUEUE_EXCEPTION("Wrong number of arguments"); - goto err; - } - if (!JSVAL_IS_INT(argv[0])) { - QUEUE_EXCEPTION("Argument not an int"); - goto err; - } - - pgr = JS_GetInstancePrivate(cx, obj, &pgresult_class, NULL); - assert(pgr != NULL); - - res = PQfname(pgr, JSVAL_TO_INT(argv[0])); - *rval = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, res)); - - return JS_TRUE; - -err: - *rval = OBJECT_TO_JSVAL(NULL); - - return JS_FALSE; -} - -static JSBool -pgresult_m_PQfnumber(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - PGresult *pgr; - char *str; - - if (argc != 1) { - QUEUE_EXCEPTION("Wrong number of arguments"); - goto err; - } - if (!JSVAL_IS_STRING(argv[0])) { - QUEUE_EXCEPTION("Argument not a String"); - goto err; - } - str = JS_GetStringBytes(JSVAL_TO_STRING(argv[0])); - - pgr = JS_GetInstancePrivate(cx, obj, &pgresult_class, NULL); - assert(pgr != NULL); - - *rval = INT_TO_JSVAL(PQfnumber(pgr, str)); - - return JS_TRUE; -err: - *rval = OBJECT_TO_JSVAL(NULL); - - return JS_FALSE; -} - -/* - * Note: Oid is a typedef to unsigned int. Thus, we use a double object to - * make sure that we do not loose any precision. - */ -static JSBool -pgresult_m_PQftable(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - PGresult *pgr; - Oid oid; - - if (argc != 1) { - QUEUE_EXCEPTION("Wrong number of arguments"); - goto err; - } - if (!JSVAL_IS_INT(argv[0])) { - QUEUE_EXCEPTION("Argument not an int"); - goto err; - } - - pgr = JS_GetInstancePrivate(cx, obj, &pgresult_class, NULL); - assert(pgr != NULL); - - oid = PQftable(pgr, JSVAL_TO_INT(argv[0])); - - if (!JS_NewDoubleValue(cx, (jsdouble)oid, rval)) { - QUEUE_EXCEPTION("Internal error!"); - goto err; - } - - return JS_TRUE; - -err: - *rval = OBJECT_TO_JSVAL(NULL); - - return JS_FALSE; -} - -static JSBool -pgresult_m_PQftablecol(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - PGresult *pgr; - - if (argc != 1) { - QUEUE_EXCEPTION("Wrong number of arguments"); - goto err; - } - if (!JSVAL_IS_INT(argv[0])) { - QUEUE_EXCEPTION("Argument not an int"); - goto err; - } - - pgr = JS_GetInstancePrivate(cx, obj, &pgresult_class, NULL); - assert(pgr != NULL); - - *rval = INT_TO_JSVAL(PQftablecol(pgr, JSVAL_TO_INT(argv[0]))); - - return JS_TRUE; - -err: - *rval = OBJECT_TO_JSVAL(NULL); - - return JS_FALSE; -} - -/* - * Note: we possibly could return a boolean value instead of an integer - * but the documentation says that other values are reserved for possible - * future use. I am surprised that they do not return enumerated values. - */ -static JSBool -pgresult_m_PQfformat(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - PGresult *pgr; - - if (argc != 1) { - QUEUE_EXCEPTION("Wrong number of arguments"); - goto err; - } - if (!JSVAL_IS_INT(argv[0])) { - QUEUE_EXCEPTION("Argument not an int"); - goto err; - } - - pgr = JS_GetInstancePrivate(cx, obj, &pgresult_class, NULL); - assert(pgr != NULL); - - *rval = INT_TO_JSVAL(PQfformat(pgr, JSVAL_TO_INT(argv[0]))); - - return JS_TRUE; - -err: - *rval = OBJECT_TO_JSVAL(NULL); - - return JS_FALSE; -} - -/* - * Note: Oid is a typedef to unsigned int. Thus, we use a double object to - * make sure that we do not loose any precision. - */ -static JSBool -pgresult_m_PQftype(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - PGresult *pgr; - Oid oid; - - if (argc != 1) { - QUEUE_EXCEPTION("Wrong number of arguments"); - goto err; - } - if (!JSVAL_IS_INT(argv[0])) { - QUEUE_EXCEPTION("Argument not an int"); - goto err; - } - - pgr = JS_GetInstancePrivate(cx, obj, &pgresult_class, NULL); - assert(pgr != NULL); - - oid = PQftype(pgr, JSVAL_TO_INT(argv[0])); - - if (!JS_NewDoubleValue(cx, (jsdouble)oid, rval)) { - QUEUE_EXCEPTION("Internal error!"); - goto err; - } - - return JS_TRUE; - -err: - *rval = OBJECT_TO_JSVAL(NULL); - - return JS_FALSE; -} - -static JSBool -pgresult_m_PQfmod(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - PGresult *pgr; - - if (argc != 1) { - QUEUE_EXCEPTION("Wrong number of arguments"); - goto err; - } - if (!JSVAL_IS_INT(argv[0])) { - QUEUE_EXCEPTION("Argument not an int"); - goto err; - } - - pgr = JS_GetInstancePrivate(cx, obj, &pgresult_class, NULL); - assert(pgr != NULL); - - *rval = INT_TO_JSVAL(PQfmod(pgr, JSVAL_TO_INT(argv[0]))); - - return JS_TRUE; - -err: - *rval = OBJECT_TO_JSVAL(NULL); - - return JS_FALSE; -} - -static JSBool -pgresult_m_PQfsize(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - PGresult *pgr; - - if (argc != 1) { - QUEUE_EXCEPTION("Wrong number of arguments"); - goto err; - } - if (!JSVAL_IS_INT(argv[0])) { - QUEUE_EXCEPTION("Argument not an int"); - goto err; - } - - pgr = JS_GetInstancePrivate(cx, obj, &pgresult_class, NULL); - assert(pgr != NULL); - - *rval = INT_TO_JSVAL(PQfsize(pgr, JSVAL_TO_INT(argv[0]))); - - return JS_TRUE; - -err: - *rval = OBJECT_TO_JSVAL(NULL); - - return JS_FALSE; -} - -static JSBool -pgresult_m_PQbinaryTuples(JSContext *cx, JSObject *obj, uintN argc, - jsval *argv, jsval *rval) -{ - PGresult *pgr; - - if (argc != 0) { - QUEUE_EXCEPTION("Function allows no arguments"); - *rval = OBJECT_TO_JSVAL(NULL); - return JS_FALSE; - } - - pgr = JS_GetInstancePrivate(cx, obj, &pgresult_class, NULL); - assert(pgr != NULL); - - *rval = INT_TO_JSVAL(PQbinaryTuples(pgr)); - - return JS_TRUE; -} - -/* - * Note: The semantics of PGresult.PQgetvalue() is different from the actual - * libpq C function PQgetvalue() in that we return null on NULL fields, and - * that we always return either the binary or text data into the field as a - * String (since ECMAScript Strings objects allow NUL characters). - */ -static JSBool -pgresult_m_PQgetvalue(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - PGresult *pgr; - JSString *str; - char *res; - int row, col; - - if (argc != 2) { - QUEUE_EXCEPTION("Wrong number of arguments"); - goto err; - } - if (!JSVAL_IS_INT(argv[0])) { - QUEUE_EXCEPTION("Argument 1 not an int"); - goto err; - } - if (!JSVAL_IS_INT(argv[1])) { - QUEUE_EXCEPTION("Argument 2 not an int"); - goto err; - } - - pgr = JS_GetInstancePrivate(cx, obj, &pgresult_class, NULL); - assert(pgr != NULL); - - row = JSVAL_TO_INT(argv[0]); - col = JSVAL_TO_INT(argv[1]); - - if (PQgetisnull(pgr, row, col)) { - *rval = OBJECT_TO_JSVAL(NULL); - return JS_TRUE; - } - - if ((res = PQgetvalue(pgr, row, col)) == NULL) { - QUEUE_EXCEPTION("PQgetvalue() == NULL"); - goto err; - } - - if ((str = JS_NewStringCopyN(cx, res, PQgetlength(pgr, row, col))) - == NULL) { - QUEUE_EXCEPTION("Out of memory!"); - goto err; - } - *rval = STRING_TO_JSVAL(str); - - return JS_TRUE; - -err: - *rval = OBJECT_TO_JSVAL(NULL); - - return JS_FALSE; -} - -/* - * Note: unlike the C PQgetisnull() function, which returns 0 or 1, we return - * true or false, since ECMAScript supports booleans. - * Moreover, one no longer needs to call this function in JS if it is to - * subsequently use PQgetvalue(), since ours can return null. - * May still be useful in some situations. - */ -static JSBool -pgresult_m_PQgetisnull(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - PGresult *pgr; - - if (argc != 2) { - QUEUE_EXCEPTION("Wrong number of arguments"); - goto err; - } - if (!JSVAL_IS_INT(argv[0])) { - QUEUE_EXCEPTION("Argument 1 not an int"); - goto err; - } - if (!JSVAL_IS_INT(argv[1])) { - QUEUE_EXCEPTION("Argument 2 not an int"); - goto err; - } - - pgr = JS_GetInstancePrivate(cx, obj, &pgresult_class, NULL); - assert(pgr != NULL); - - *rval = BOOLEAN_TO_JSVAL(PQgetisnull(pgr, JSVAL_TO_INT(argv[0]), - JSVAL_TO_INT(argv[1]))); - - return JS_TRUE; - -err: - *rval = OBJECT_TO_JSVAL(NULL); - - return JS_FALSE; -} - -static JSBool -pgresult_m_PQgetlength(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - PGresult *pgr; - - if (argc != 2) { - QUEUE_EXCEPTION("Wrong number of arguments"); - goto err; - } - if (!JSVAL_IS_INT(argv[0])) { - QUEUE_EXCEPTION("Argument 1 not an int"); - goto err; - } - if (!JSVAL_IS_INT(argv[1])) { - QUEUE_EXCEPTION("Argument 2 not an int"); - goto err; - } - - pgr = JS_GetInstancePrivate(cx, obj, &pgresult_class, NULL); - assert(pgr != NULL); - - *rval = INT_TO_JSVAL(PQgetlength(pgr, JSVAL_TO_INT(argv[0]), - JSVAL_TO_INT(argv[1]))); - - return JS_TRUE; - -err: - *rval = OBJECT_TO_JSVAL(NULL); - - return JS_FALSE; -} - -static JSBool -pgresult_m_PQcmdStatus(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - PGresult *pgr; - - if (argc != 0) { - QUEUE_EXCEPTION("Function allows no arguments"); - *rval = OBJECT_TO_JSVAL(NULL); - return JS_FALSE; - } - - pgr = JS_GetInstancePrivate(cx, obj, &pgresult_class, NULL); - assert(pgr != NULL); - - *rval = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, PQcmdStatus(pgr))); - - return JS_TRUE; -} - -/* - * We could return a double easily, but like the C API are returning a string - */ -static JSBool -pgresult_m_PQcmdTuples(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - PGresult *pgr; - - if (argc != 0) { - QUEUE_EXCEPTION("Function allows no arguments"); - *rval = OBJECT_TO_JSVAL(NULL); - return JS_FALSE; - } - - pgr = JS_GetInstancePrivate(cx, obj, &pgresult_class, NULL); - assert(pgr != NULL); - - *rval = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, PQcmdTuples(pgr))); - - return JS_TRUE; -} - -/* - * Note: Oid is a typedef to unsigned int. Thus, we use a double object to - * make sure that we do not loose any precision. - */ -static JSBool -pgresult_m_PQoidValue(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - PGresult *pgr; - Oid oid; - - if (argc != 0) { - QUEUE_EXCEPTION("Function allows no arguments"); - *rval = OBJECT_TO_JSVAL(NULL); - return JS_FALSE; - } - - pgr = JS_GetInstancePrivate(cx, obj, &pgresult_class, NULL); - assert(pgr != NULL); - - oid = PQoidValue(pgr); - - if (!JS_NewDoubleValue(cx, (jsdouble)oid, rval)) { - QUEUE_EXCEPTION("Internal error!"); - goto err; - } - - return JS_TRUE; - -err: - *rval = OBJECT_TO_JSVAL(NULL); - - return JS_FALSE; -} - - - -/* - * PGcancel object control - */ - -static JSObject * -js_InitPGcancelClass(JSContext *cx, JSObject *obj) -{ - - return (JS_InitClass(cx, obj, NULL, &pgcancel_class, - pgcancel_constructor, 0, NULL, pgcancel_methods, NULL, NULL)); -} - -/* Non instanciable */ -static JSBool -pgcancel_constructor(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - - QUEUE_EXCEPTION("PGcancel class not user-instanciable"); - - return JS_FALSE; -} - -static void -pgcancel_finalize(JSContext *cx, JSObject *obj) -{ - PGcancel *pgc; - - if ((pgc = JS_GetInstancePrivate(cx, obj, &pgcancel_class, NULL)) - != NULL) { - PQfreeCancel(pgc); - (void) JS_SetPrivate(cx, obj, NULL); - } -} - - -/* - * PGcancel object methods - */ - -static JSBool -pgcancel_m_PQfreeCancel(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - PGcancel *pgc; - - if (argc != 0) { - QUEUE_EXCEPTION("Function allows no arguments"); - *rval = OBJECT_TO_JSVAL(NULL); - return JS_FALSE; - } - - if ((pgc = JS_GetInstancePrivate(cx, obj, &pgcancel_class, NULL)) - != NULL) { - PQfreeCancel(pgc); - (void) JS_SetPrivate(cx, obj, NULL); - } - - *rval = OBJECT_TO_JSVAL(NULL); - - return JS_TRUE; -} - -static JSBool -pgcancel_m_PQcancel(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - PGcancel *pgc; - char *str; - size_t len; - JSString *s; - - if (argc != 1) { - QUEUE_EXCEPTION("Wrong number of arguments"); - goto err; - } - if (!JSVAL_IS_STRING(argv[0])) { - QUEUE_EXCEPTION("Argument 1 not a String"); - goto err; - } - - pgc = JS_GetInstancePrivate(cx, obj, &pgcancel_class, NULL); - assert(pgc != NULL); - - s = JSVAL_TO_STRING(argv[0]); - str = JS_GetStringBytes(s); - len = JS_GetStringLength(s); - - *rval = INT_TO_JSVAL(PQcancel(pgc, str, len)); - - return JS_TRUE; - -err: - *rval = OBJECT_TO_JSVAL(NULL); - - return JS_FALSE; -} diff --git a/mmsoftware/js/classes/js_pgsql.h b/mmsoftware/js/classes/js_pgsql.h deleted file mode 100644 index b9e2878..0000000 --- a/mmsoftware/js/classes/js_pgsql.h +++ /dev/null @@ -1,15 +0,0 @@ -/* $Id: js_pgsql.h,v 1.1 2006/07/22 04:18:47 mmondor Exp $ */ - -/* - * Copyright (c) 2006, Matthew Mondor - * ALL RIGHTS RESERVED. - */ - -#ifndef JSPGSQL_H -#define JSPGSQL_H - -#include - -extern JSObject *js_InitPGClass(JSContext *, JSObject *); - -#endif diff --git a/mmsoftware/js/classes/js_signal.c b/mmsoftware/js/classes/js_signal.c deleted file mode 100644 index 7ba66cf..0000000 --- a/mmsoftware/js/classes/js_signal.c +++ /dev/null @@ -1,231 +0,0 @@ -/* $Id: js_signal.c,v 1.1 2006/07/22 04:18:47 mmondor Exp $ */ - -/* - * Copyright (c) 2005, Matthew Mondor - * ALL RIGHTS RESERVED. - */ - -/* - * Basic UNIX signal services for ECMAScript - * - * XXX - * I have to see what interface I want to export. There are several - * possibilities we could use: - * - Have the shell register signal handlers fore interesting events at - * startup and allow scripts to provide a handler function, which if - * exists upon reception of a signal, gets executed. We might then - * need to add extra custom checks in the shell for special signals - * which if the script doesn't end, might still end the process - * i.e. function would be expected to cause the script to exit upon - * reception of a SIGTERM signal... - * - Provide a sigaction-style interface so that the script would be - * able to define functions to execute upon reception of certain - * signals, or null or such for the signal to be ignored. - * This solution might allow better application customization. - * If assuming this interface, the following would be exported: - * - Signal class, through which static signal properties could be - * accessed for signal numbers I.E. Signal.SIGTERM. - * - A static method to create/set/unset/ignore a signal/handler - * Signal.sigaction()? - * It could be provided with the parameters: - * Signal.sigaction(FD.SIG*, obj); - * Where obj would contain fields sa_handler, sa_mask, sa_flags? - * Signal.kill(pid, sig); - * Signal.sigaltstack(...) ? - * Signal.sigprocmask(...) - * Signal.sigsuspend(mask) - * And maybe provide the sigsetops(3)? We possibly could just allow an - * array or such instead of sigset_t though if wanted. - * I'm not sure I want to provide sigsetjmp()/siglongjmp(). JavaScript has - * exceptions anyways. If allowing sigaltstack(), C would need to allocate - * the stacks, so a stack object would need to be exported or such. I - * don't think I want to support this as it's probably not needed by any of - * the applications I'll write, I don't want to write a threading library - * in JS. - * - I wonder if it would be safe to invoke a JS function in a signal handler. - * If it wasn't, I could simply queue the signal events and then call the - * functions for the queue in normal process context. - * If doing this, sigaction has to be called to catch any signal the - * application wishes, and we would be rolling our own signal handling, - * so if wanted we could potentially provide another interface than - * sigaction to ECMAScript... - */ - - - -#include - -#include -#include -#include -#include -#include - -#include - - - -/* Utility macros */ -#define QUEUE_EXCEPTION(s) do { \ - JS_SetPendingException(cx, \ - STRING_TO_JSVAL(JS_NewStringCopyZ(cx, (s)))); \ -} while (/* CONSTCOND */0) - - - -/* Prototypes */ -static JSBool signal_sm_strerror(JSContext *, JSObject *, uintN, jsval *, - jsval *); - - - -/* Actual class parameters */ -static JSClass signal_class = { - "Signal", 0, JS_PropertyStub, JS_PropertyStub, - JS_PropertyStub, JS_PropertyStub, JS_EnumerateStub, - JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub -}; - -/* Provided static methods */ -static JSFunctionSpec signal_smethods[] = { - { "strerror", signal_sm_strerror, 1, 0, 0 }, - { NULL, NULL, 0, 0, 0 } -}; - -/* - * Provided static properties. - * We use these to provide ECMAScript with the ability to use system-specific - * standard C constant macros without us having to tidiously map them - * individually, or to require other scripts to be used as headers to define - * them. Another possibility would have been to supply these parameters as - * string, but this would have required even slower remapping because of the - * parsing and string comparisions. - * We only include those which we consider necessary for now, others may be - * added easily as needed, provided that they are added in all three maps. - * I might perhaps develop macros and/or functions to map all these easily - * from a single map. - */ - -struct property_spec { - const char *name; - int value; -}; - -#define SP(n) \ - { #n, n } - -static struct property_spec signal_sprops[] = { - SP(SIGHUP), - SP(SIGINT), - SP(SIGQUIT), - SP(SIGILL), - SP(SIGTRAP), - SP(SIGABRT), - SP(SIGEMT), - SP(SIGFPE), - SP(SIGKILL), - SP(SIGBUS), - SP(SIGSEGV), - SP(SIGSYS), - SP(SIGPIPE), - SP(SIGALRM), - SP(SIGTERM), - SP(SIGURG), - SP(SIGSTOP), - SP(SIGTSTP), - SP(SIGCONT), - SP(SIGCHLD), - SP(SIGTTIN), - SP(SIGTTOU), - SP(SIGIO), - SP(SIGXCPU), - SP(SIGXFSZ), - SP(SIGVTALRM), - SP(SIGPROF), - SP(SIGWINCH), - SP(SIGINFO), - SP(SIGUSR1), - SP(SIGUSR2), - SP(SIGPWR), - - { NULL, 0 } -}; - -#undef SP - - - -/* - * Class control functions - */ - -JSObject * -js_InitSignalClass(JSContext *cx, JSObject *obj) -{ - JSObject *proto, *ctor; - struct property_spec *sp; - - if ((proto = JS_InitClass(cx, obj, NULL, &signal_class, NULL, - 0, NULL, NULL, NULL, signal_smethods)) == NULL) { - (void) fprintf(stderr, "Error initializing Signal class\n"); - return NULL; - } - - /* Create static properties */ - if ((ctor = JS_GetConstructor(cx, proto)) == NULL) { - (void) fprintf(stderr, "Signal: JS_GetConstructor == NULL\n"); - return NULL; - } - for (sp = signal_sprops; sp->name != NULL; sp++) { - if (JS_DefineProperty(cx, ctor, sp->name, - INT_TO_JSVAL(sp->value), NULL, NULL, - JSPROP_READONLY | JSPROP_PERMANENT) == JS_FALSE) { - (void) fprintf(stderr, - "Signal: Error defining property %s\n", sp->name); - return NULL; - } - } - - return proto; -} - - - -/* - * Static properties functions - */ - - - -/* - * Static methods - */ - -static JSBool -signal_sm_strerror(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - int error; - JSString *string; - - *rval = OBJECT_TO_JSVAL(NULL); - - if (argc != 1) { - QUEUE_EXCEPTION("Wrong number of arguments"); - return JS_FALSE; - } - if (!JSVAL_IS_INT(*argv)) { - QUEUE_EXCEPTION("Argument not an integer"); - return JS_FALSE; - } - error = (int)JSVAL_TO_INT(*argv); - - if ((string = JS_NewStringCopyZ(cx, "testXXX")) == NULL) { - QUEUE_EXCEPTION("Out of memory"); - return JS_FALSE; - } - - *rval = STRING_TO_JSVAL(string); - - return JS_TRUE; -} diff --git a/mmsoftware/js/classes/js_signal.h b/mmsoftware/js/classes/js_signal.h deleted file mode 100644 index a393ed1..0000000 --- a/mmsoftware/js/classes/js_signal.h +++ /dev/null @@ -1,13 +0,0 @@ -/* $Id: js_signal.h,v 1.1 2006/07/22 04:18:47 mmondor Exp $ */ - -/* - * Copyright (c) 2005, Matthew Mondor - * ALL RIGHTS RESERVED. - */ - -#ifndef JSSIGNAL_H -#define JSSIGNAL_H - -extern JSObject *js_InitSignalClass(JSContext *, JSObject *); - -#endif diff --git a/mmsoftware/js/js-sh/src/GNUmakefile b/mmsoftware/js/js-sh/src/GNUmakefile deleted file mode 100644 index fb699dc..0000000 --- a/mmsoftware/js/js-sh/src/GNUmakefile +++ /dev/null @@ -1,29 +0,0 @@ -# $Id: GNUmakefile,v 1.1 2006/07/22 04:43:20 mmondor Exp $ - -#CFLAGS += -g -CFLAGS += -Wall - -JS_CFLAGS := $(shell spidermonkey-config -dc) -JS_LDFLAGS := $(shell spidermonkey-config -dl) - -PG_CFLAGS := $(shell pg_config --cppflags) -PG_LDFLAGS := $(shell pg_config --ldflags) -PG_LDFLAGS += -lpq - -OBJS := $(addprefix ../../classes/,js_fd.o js_errno.o js_signal.o js_pgsql.o) -OBJS += js-sh.o - -CFLAGS += $(JS_CFLAGS) $(PG_CFLAGS) -I../../classes -Wall -LDFLAGS += $(JS_LDFLAGS) $(PG_LDFLAGS) - - -all: js-sh - -%.o: %.c - cc -c ${CFLAGS} -o $@ $< - -js-sh: $(OBJS) - cc -o $@ -lc ${LDFLAGS} $(OBJS) - -clean: - rm -f js-sh $(OBJS) diff --git a/mmsoftware/js/js-sh/src/js-sh.c b/mmsoftware/js/js-sh/src/js-sh.c deleted file mode 100644 index 7a7bfc2..0000000 --- a/mmsoftware/js/js-sh/src/js-sh.c +++ /dev/null @@ -1,328 +0,0 @@ -/* $Id: js-sh.c,v 1.1 2006/07/22 04:43:20 mmondor Exp $ */ - -/* - * Copyright (c) 2004-2005, Matthew Mondor - */ - -/* - * TODO: - * - * - Verify with Brendan Eich: - * - If reusing the context to execute several other scripts, it is - * important that they not be able to add global properties or methods. - * This seems to currently work using a custom api_class_property_add(). - * This however also required standard properties to be shared - * (JS_PROP_SHARED), otherwise api_class_property_add() would be called - * and even setting values to existing API system properties would fail in - * user scripts. Scealing was also too strict. - * I assumed that JS_AddNamedRoot() was required for the API class to - * never be freed, so that it can be reused after a call to - * js_context_reset(). Perhaps this is not necessary. - */ - - - -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#include - -#include -#include -#include -#include - - - -/* - * DEFINITIONS - */ - -/* Size runtime objects must take to run the GC */ -#define GCBYTES 1048576 /* 1MB */ - -/* Size of stack to allocate for every context */ -#define STACKBYTES 8192 /* 8KB */ - -/* - * Structure used to link a context with custom objects we need to perform - * some cleanup from before destroying the context. Ideally managed via - * mmpool(3) in a real world application for slap management and recycling. - * We'll have one of these per process in our pool of processes. - */ -typedef struct { - JSRuntime *rt; - JSContext *ctx; - JSObject *global, *class_fd, *class_errno, *class_signal, - *class_pgsql; -} js_context_t; - -/* - * To hold loaded file objects (actually mmap(2)ed) - */ -typedef struct { - void *data; - size_t size; -} file_t; - -/* - * Defaults for the global class - */ -static JSClass global_class = { - "global", 0, JS_PropertyStub, JS_PropertyStub, - JS_PropertyStub, JS_PropertyStub, JS_EnumerateStub, - JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub -}; - - - -/* - * PROTOTYPES - */ - -int main(int, char **); -static JSBool branch_callback(JSContext *, JSScript *); - -static file_t *file_load(const char *); -static void file_free(file_t *); - -/* Could be an exported API later on */ -static js_context_t *js_context_init(size_t, size_t); -static void js_context_destroy(js_context_t *); -/* XXX static void js_context_reset(js_context_t *);*/ - - - - - -int -main(int argc, char **argv) -{ - file_t *file; - js_context_t *cctx; - - if (argc != 2) { - (void) fprintf(stderr, "Usage: js-sh \n"); - exit(EXIT_FAILURE); - } - if ((file = file_load(argv[1])) == NULL) { - (void) fprintf(stderr, "Error loading '%s'\n", argv[1]); - exit(EXIT_FAILURE); - } - - /* - * We always need at least one runtime per process, at least one - * context per thread and at least a global object per context - * (standard classes, like Date). - */ - if ((cctx = js_context_init(GCBYTES, STACKBYTES)) == NULL) { - file_free(file); - (void) fprintf(stderr, "js_context_init()\n"); - exit(EXIT_FAILURE); - } - - /* - * This is a very useful and important feature, enable our callback - * function which will get called whenever the script branches - * backwards, returns from a function or exits. It allows us to - * even maintain control in cases where the script loops endlessly. - */ - (void) JS_SetBranchCallback(cctx->ctx, branch_callback); - - /* - * Now enable addProperty() protection for all classes using our - * custom api_class_property_add() function. This will prevent user - * code from adding properties or methods to the API class for - * instance. - * This however requires that properties use the JSPROP_SHARED flag - * since addProperty() method would internally get called to create - * shadow copies for the runtime otherwise. - */ - /* - api_class_protect = JS_TRUE; - */ - - /* - * Now execute script loaded into our file_t. - * We simplify this process by calling JS_EvaluateScript() which - * will first tokenize/compile the result, and then interpret/run it. - * Moreover, it allows the script to optionally return a value - * directly like if it was a function. - * Alternatively, we could use JS_CompileFile() or JS_CompileScript() - * to pre-tokenize the script, and JS_ExecuteScript() to interpret it. - */ - { - jsval rval, pval; - JSString *str; - int i; - - if (JS_EvaluateScript(cctx->ctx, cctx->global, file->data, - file->size, argv[1], 1, &rval)) { - str = JS_ValueToString(cctx->ctx, rval); - (void) printf("Script result: %s\n", - JS_GetStringBytes(str)); - /* - * Attempt to call JS function "callMe" if the script - * created it. - */ - for (i = 0; i < 10; i++) { - pval = INT_TO_JSVAL(i); - if (!JS_CallFunctionName(cctx->ctx, - cctx->global, "callMe", 1, &pval, &rval)) - break; - } - } else { - /* XXX how to obtain error and stack backtrace? */ - } - } - - /* Cleanup */ - file_free(file); - js_context_destroy(cctx); - - exit(EXIT_SUCCESS); -} - -/* - * This function is called during the execution of the script so that we can - * remain in control of the application. If we only allow the scripts to - * define functions for callbacks, we can use the first instance if this event - * to abort the script if wanted, as well. We can then set an alternative - * callback function and execute the script provided functions at specific - * events. Of course, it also would be possible to use setitimer(2) to have - * a SIGALRM signal trigger a function at regular set intervals. - */ -/* ARGSUSED */ -static JSBool -branch_callback(JSContext *ctx, JSScript *script) -{ - static int count = 0; - - if (++count > 1000) { - count = 0; - JS_MaybeGC(ctx); - } - - /* Returning JS_FALSE here aborts the script */ - return JS_TRUE; -} - - - -/* - * Could be an exported API - */ - -static js_context_t * -js_context_init(size_t gc_size, size_t stack_size) -{ - js_context_t *cctx; - - if ((cctx = malloc(sizeof(js_context_t))) == NULL || - (cctx->rt = JS_NewRuntime(gc_size)) == NULL || - (cctx->ctx = JS_NewContext(cctx->rt, stack_size)) == NULL || - (cctx->global = JS_NewObject(cctx->ctx, &global_class, NULL, - NULL)) == NULL || - !JS_InitStandardClasses(cctx->ctx, cctx->global) || - (cctx->class_fd = js_InitFDClass(cctx->ctx, cctx->global)) - == NULL || - (cctx->class_errno = js_InitErrnoClass(cctx->ctx, cctx->global)) - == NULL || - (cctx->class_signal = js_InitSignalClass(cctx->ctx, cctx->global)) - == NULL || - (cctx->class_pgsql = js_InitPGClass(cctx->ctx, cctx->global)) - == NULL) { - /* An error, free any partially allocated resources */ - if (cctx != NULL) - js_context_destroy(cctx); - - return NULL; - } - - return cctx; -} - -static void -js_context_destroy(js_context_t *cctx) -{ - - assert(cctx != NULL); - - if (cctx->ctx != NULL) - JS_DestroyContext(cctx->ctx); - if (cctx->rt != NULL) - JS_DestroyRuntime(cctx->rt); - - free(cctx); -} - -/* - * This function should permit to restore the context to a consistent, known - * state before a new script can be executed using the same context instead of - * having to destroy and recreate contexts everytime. - */ -/* ARGSUSED */ -/* XXX -static void -js_context_reset(js_context_t *cctx) -{ - -} -*/ - - -/* - * Loads specified file and returns a file_t pointer. Note that we only - * internally keep the memory map for read access to the file, rather than an - * open filedescriptor. - */ -static file_t * -file_load(const char *filename) -{ - int fd; - struct stat st; - file_t *file; - - assert(filename != NULL); - - if ((fd = open(filename, O_RDONLY)) != -1) { - if (fstat(fd, &st) == 0) { - if ((file = malloc(sizeof(file_t))) != NULL) { - file->size = (size_t)st.st_size; - - if ((file->data = mmap(NULL, file->size, - PROT_READ, MAP_FILE | MAP_PRIVATE, fd, 0)) - != MAP_FAILED) { - (void) close(fd); - return file; - } - - free(file); - } - } - (void) close(fd); - } - - return NULL; -} - -/* - * Frees a file_t which was returned by a previous file_load() call. - */ -static void -file_free(file_t *file) -{ - - assert(file != NULL); - - (void) munmap(file->data, file->size); - free(file); -} diff --git a/mmsoftware/js/util/spidermonkey-config b/mmsoftware/js/util/spidermonkey-config deleted file mode 100755 index 359d2d0..0000000 --- a/mmsoftware/js/util/spidermonkey-config +++ /dev/null @@ -1,51 +0,0 @@ -#!/bin/sh -# -# $Id: spidermonkey-config,v 1.1 2006/07/22 04:36:16 mmondor Exp $ -# -# Configure script to help build scripts -# by Matthew Mondor - -CFLAGS='-I/usr/local/spidermonkey/include -DXP_UNIX' -LDFLAGS='-Wl,-R/usr/local/spidermonkey/lib -L/usr/local/spidermonkey/lib -ljs' - -DLDFLAGS='-Wl,-R/usr/local/spidermonkey/lib -L/usr/local/spidermonkey/lib -ljs_dbg' - -VERSION='SpiderMonkey 1.5-rc6A' - -usage() -{ - echo - echo 'Usage: spidermonkey-config [-v] [-c] [-l] [-d]' - echo - echo ' -v : Shows SpiderMonkey version' - echo ' -c : Shows flags suitable to append to $CFLAGS' - echo ' -l : Shows flags suitable to append to $LDFLAGS' - echo ' -d : Shows debugging versions of the flags' - echo -} - -if [ -z $@ ]; then - usage - exit 0 -fi - -while getopts dclv c; do - case $c in - d) - LDFLAGS="$DLDFLAGS" - ;; - c) - echo $CFLAGS - ;; - l) - echo $LDFLAGS - ;; - v) - echo $VERSION - ;; - *) - usage - exit 0 - ;; - esac -done diff --git a/mmsoftware/make.sh b/mmsoftware/make.sh deleted file mode 100755 index 95d71ce..0000000 --- a/mmsoftware/make.sh +++ /dev/null @@ -1,40 +0,0 @@ -#!/bin/sh -# $Id: make.sh,v 1.2 2003/07/02 17:20:52 mmondor Exp $ - -. mmlib/makefuncs.sh - -cd mmlib/ -clean mmlib -makebin mmlib -cd ../ - -cd mmpasswd/ -clean mmpasswd -makebin mmpasswd -cd ../ - -cd mmstatd/src/ -clean mmstatd -makebin mmstatd -cd ../../ -cd apache-mmstat/ -clean apache-mmstat -makebin apache-mmstat -cd ../ - -cd mmftpd/src/ -clean mmftpd -makebin mmftpd -cd ../../ - -cd mmmail/src/mmsmtpd/ -clean mmsmtpd -makebin mmsmtpd -cd ../mmpop3d -clean mmpop3d -makebin mmpop3d -cd ../../ - -echo -echo 'You may now ./install.sh help or ./install.sh to install/upgrade' -echo diff --git a/mmsoftware/mmanoncvs/GNUmakefile b/mmsoftware/mmanoncvs/GNUmakefile deleted file mode 100644 index 817ce65..0000000 --- a/mmsoftware/mmanoncvs/GNUmakefile +++ /dev/null @@ -1,26 +0,0 @@ -# $Id: GNUmakefile,v 1.5 2004/05/31 03:58:36 mmondor Exp $ - -MMLIBS := $(addprefix ../mmlib/,mmpool.o mmlog.o mmreadcfg.o mmstring.o \ -mmhash.o) - -OBJS := mmanoncvs.o - -CFLAGS += -Wall - - -all: mmanoncvs - -%.o: %.c - cc -c ${CFLAGS} -I. -I../mmlib -o $@ $< - -mmanoncvs: $(MMLIBS) $(OBJS) - cc -o $@ $(OBJS) -lc $(MMLIBS) - -install: all - install -cs -o 0 -g 0 -m 555 mmanoncvs /usr/local/sbin - install -c -o 0 -g 0 -m 444 mmanoncvs.conf.5 /usr/local/man/man5 - install -c -o 0 -g 0 -m 444 mmanoncvs.list.5 /usr/local/man/man5 - install -c -o 0 -g 0 -m 444 mmanoncvs.8 /usr/local/man/man8 - -clean: - rm -f mmanoncvs $(OBJS) $(MMLIBS) diff --git a/mmsoftware/mmanoncvs/mmanoncvs.8 b/mmsoftware/mmanoncvs/mmanoncvs.8 deleted file mode 100644 index 6acfad1..0000000 --- a/mmsoftware/mmanoncvs/mmanoncvs.8 +++ /dev/null @@ -1,720 +0,0 @@ -.\" $Id: mmanoncvs.8,v 1.10 2004/10/01 15:26:21 mmondor Exp $ -.\" -.\" Copyright (C) 2003-2004, Matthew Mondor -.\" All rights reserved. -.\" -.\" Redistribution and use in source and binary forms, with or without -.\" modification, are permitted provided that the following conditions -.\" are met: -.\" 1. Redistributions of source code must retain the above copyright -.\" notice, this list of conditions and the following disclaimer. -.\" 2. Redistributions in binary form must reproduce the above copyright -.\" notice, this list of conditions and the following disclaimer in the -.\" documentation and/or other materials provided with the distribution. -.\" 3. All advertising materials mentioning features or use of this software -.\" must display the following acknowledgement: -.\" This product includes software written by Matthew Mondor. -.\" 4. The name of Matthew Mondor may not be used to endorse or promote -.\" products derived from this software without specific prior written -.\" permission. -.\" 5. Redistribution of source code may not be released under the terms of -.\" any GNU Public License derivate. -.\" -.\" THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR -.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES -.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. -.\" IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, -.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -.\" BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF -.\" USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF -.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -.\" -.Dd November 9, 2003 -.Dt MMANONCVS 8 -.Os mmsoftware -.Sh NAME -.Nm mmanoncvs -.Nd -Companion to -.Xr mmspawnd 8 -to offer secure public CVS read-only pserver access -.Sh SYNOPSIS -.Nm mmanoncvs Op Fl f Ar -.Sh DESCRIPTION -.Nm -was written to allow providing a common CVS pserver for various CVS -repositories maintained by different people (or projects) on the same -server. Offering pserver access is important for many opensource projects. -This utility provides a safe and comfortable way to create and update a -single read-only repository (which will usually be accessed as /cvsroot -from a pserver user standpoint), while allowing space for a number of -independent repositories pertaining to various users or projects, merging -them in the read-only mirror. -.Pp -Support is implemented for CVS-friendly read access locking when creating -a backup of a CVS repository, so that CVS concurrency is allowed. For instance, -a user may be commiting changes to the repository while the cron event -scheduler runs -.Xr mmanoncvs 8 -to back it up to read-only storage. Both will then behave properly and the -repository will remain consistent for the public users. -.Pp -Another type of synchronization was also implemented, so that while replacing -the previous pserver backup with the new one it appears atomic to the pserver -users. This is done via the special -.Xr mmspawnd 8 -.Nm ALOCK -support. -.Pp -In practice, this software is best used with a corresponding -.Xr mmspawnd 8 -setup providing safe read-only CVS pserver support under a -.Xr chroot 2 -environment. It however can be used standalone. -.Pp -A special effort was made so that it be safe to run -.Nm -by the superuser. It can however also be ran from an unprivileged user, -or can alternatively revoke privileges to a normal one after launching -by the superuser, providing that the user has the required access to -.Nm REPDIR -and -.Nm TMPDIR , -to the source repositories if -.Nm CVS_LOCKING -is enabled, and to -.Nm ALOCK_PATH -if -.Nm ALOCK_LOCKING -is enabled. -.Pp -When the need to recursively delete directory trees (I.E. to delete the -contents of the old copy after successful replacement, to delete temporary -files), a safe method is used so that only wanted files are deleted, and -that recursion be limited to -.Nm MAX_RECURSE . -Special care is done to not be able to be redirected out of the wanted -directory via a symbolic link. -.Pp -When performing a backup from an administrator-specified directory, -special care is done to prevent abnormally high recursion levels, -and to only copy valid RCS files and CVS directories, while setting -their permissions appropriately on the destination repository. We -do not process special files, including symbolic links, and are logging -the event to stderr (which will usually result in being mailed to the -administrator by the cron event scheduler). Nothing is ouput if everything -is successful. -.Pp -When performing backups, if any of the administrator specified elements in -.Xr mmanoncvs.list 5 -fails, we abort the backup, free any held locks, delete temporary files and -leave the last successful destination repository as-is, to prevent common -errors from disabling correct public pserver access. We however also log -such problems to stderr. -.Pp -The CVSROOT directory is automatically written in the destination repository -copy to not be available via CVS checkout, and to have the right options for -use with -.Xr mmspawnd 8 -and -.Xr cvs 1 -pserver in read-only mode. -.Sh INSTRUCTIONS TO SETUP WITH MMSPAWND(8) -.Ss Group and user(s) creation -.Bl -tag -width indent -offset indent -.It Nm Creating the 'anoncvs' group -The -.Xr cvs 1 -command has special requirements to function, even in pserver mode. -This group will allow it write access to the '/cvsroot/CVSROOT/val-tags', -as well as to the '/tmp' directory where it will create its CVS read access -locks, and to '/dev/null'. It will also allow it read-only access to -everything in '/cvsroot'. -.It Nm Creating the unprivileged pserver administrator user -This step is necessary if -.Xr mmanoncvs 8 -will not run as the superuser. This would then generally consist of a disabled -user, without a valid shell, with the home directory set to '/var/anoncvs'. -This user should be part of the previously created 'anoncvs' -group. A typical name for this user is 'anoncvsa'. -.Pp -Note that when not running -.Xr mmanoncvs 8 -as the superuser, special care must be taken for permissions. See the -.Xr mmanoncvs.conf 5 -manual page about the -.Nm DROP_PRIVS , -.Nm USER , -.Nm GROUPS , -.Nm ALOCK_LOCKING -and -.Nm CVS_LOCKING -configuration parameters for more details. -.El -.Ss Root directory setup for pserver with mmspawnd(8) -Note that most of the files and directories here are created using a -.Xr umask 2 -of 227. You however will need to use the -.Xr chown 8 -and -.Xr chmod 8 -commands at several occasions. -.Bl -tag -width indent -offset indent -.It Nm Creating the alternate root directory for chroot(2) -The new alternate root directory should be created, typically -.Nm /var/anoncvs . -This file should be owned by the pserver repository administrator user -(the user under which permissions runs -.Xr mmanoncvs 8 ) . -The group of this directory should be set to the previously created 'anoncvs' -group. The mode of this directory should be 750. -.It Nm Creation of the '/dev/null' device -.Xr cvs 1 -requires this device to exist. Thus, the -.Nm /var/anoncvs/dev -directory should be created, of 'root' owner, 'anoncvs' group, and mode 550. -Then the -.Nm /var/anoncvs/dev/null -special character device file should be created, of owner 'root', -group 'anoncvs', mode 660. This can be done using the -.Xr mknod 8 -command. The major and minor device numbers for it are operating system -dependent. You can verify by looking at your -.Nm /dev/null -file with the -.Xr ls 1 -command and -l option. -.It Nm Installing the cvs(1) command into the new root -The -.Nm /var/anoncvs/bin -directory should first be created, of owner 'root', group 'anoncvs', mode 550. -The cvs command should then be copied as -.Nm /var/anoncvs/bin/cvs , -with owner 'root', group 'anoncvs', mode 550. -Note that -.Xr mmspawnd 8 -will only allow to execute ELF format binaries. Also, it is recommended to use -a statically linked executable for both simplicity and efficiency. -.Pp -If using a dynamicly linked executable, additional directories and files -will need to be created, like directories: -.Nm /var/anoncvs/lib , -.Nm /var/anoncvs/usr , -.Nm /var/anoncvs/etc , -and -.Nm /var/anoncvs/libexec -of owner 'root', group 'anoncvs', mode 550, -and system executable -.Nm /usr/libexec/ld.elf_so -copied as -.Nm /var/anoncvs/libexec/ld.elf_so -and set to owner 'root', group 'anoncvs' and mode 550. -It may also be necessary to create -.Nm /var/anoncvs/etc/ld.so.conf -of owner 'root', group 'anoncvs', mode 440, with a single line in it: -.Bd -literal -offset indent -/lib -.Ed -.Pp -The libraries required by -.Xr cvs 1 -should then be verified using the -.Xr ldd 1 -command, and each of the required libraries copied into -.Nm /var/anoncvs/lib/ , -with owner 'root', group 'anoncvs', mode 440. -Note that the requirements for dynamic loading are system-dependent and vary; -One of the many reasons to choose a statically linked ELF executable, -as opposed to a dynamically linked ELF executable for this. -.It Nm Allowing cvs(1) to map 'anoncvs' user/group -Now that we created the 'anoncvs' user and group for the current system, -it is also necessary to make them map to the same IDs (identification numbers) -from within the new root after -.Xr chroot 2 . -Although this is also system-dependent, I will show some example which was -tested to work on BSD. I will also add notes for Linux-specific requirements. -.Pp -To allow the groups to map, first ensure that the -.Nm /var/anoncvs/etc -directory is created, of owner 'root', group 'anoncvs', mode 550. -You then should create the -.Nm /var/anoncvs/etc/group -file, of owner 'root', group 'anoncvs', mode 440. -This file will generally only map two groups: 'wheel' (or 'root' on Linux), -and 'anoncvs', as follows: -.Bd -literal -offset indent -wheel:*:0: -anoncvs:*:1017: -.Ed -.Pp -Note that the 'wheel' name may be replaced by 'root' on Linux if desired. -The ID for that group will always be 0 on any unix. However, the 'anoncvs' -group ID (1017 in this example) should correspond to the ID of your -own 'anoncvs' group, as shown in -.Nm /etc/group . -.Pp -We then also want the 'root' and 'anoncvs' users to map properly for the -new root. On NetBSD, this can be achieved easily using the -.Xr vipw 8 -command with the -.Nm -d/var/anoncvs -parameter. An example of the lines to type in follows: -.Bd -literal -offset indent -root:*:0:0::0:0::/:/notexists -anoncvs:*:1012:1017::0:0::/cvsroot:/notexists -anoncvsa:*:1013:1017::0:0::/:/notexists -.Ed -.Pp -This above example maps user 'root' to ID 0, which will be the case on -any unix. The 'anoncvs' and 'anoncvsa' should however have their UID -(User ID) and GID (primary Group ID) set to the ones matching the rest -of your system. This would normally create the following files in -.Nm /var/anoncvs/etc . -You can then ensure that they are set to the proper permissions: -.Nm passwd , -.Nm pwd.db : -user 'root', group 'anoncvs', mode 440. -.Nm master.passwd , -.Nm spwd.db : -user 'root', group 'anoncvs', mode 400. -Obviously, the 'anoncvsa' user is only required if it was chosen to -run -.Xr mmanoncvs 8 -as an unprivileged user. -.Pp -On Linux, creating the files -.Nm /var/anoncvs/etc/passwd -of owner 'root', group 'anoncvs', mode 440 and -.Nm /var/anoncvs/etc/shadow -of owner 'root', group 'anoncvs', mode 400 -should be done instead of running -.Xr vipw 8 . -An example contents for -.Nm passwd : -.Bd -literal -offset indent -root:*:0:0::/:/notexists -anoncvs:*:1012:1017::/cvsroot:/notexists -anoncvsa:*:1013:1017::/:/notexists -.Ed -.Pp -And for -.Nm shadow : -.Bd -literal -offset indent -root:*:0:0:10000:::: -anoncvs:*:0:0:10000:::: -anoncvsa:*:0:0:10000:::: -.Ed -.Pp -Note that if using glibc, it appears to require to be able to dynamically -load some parts of it even in a static executable. This means that the -libc library will also need to be present under the alternate root's -.Nm /lib -directory. Moreover, it appears to also require the -.Nm libnss_dns -and -.Nm libnss_files -dynamic libraries (at least on debian). This also means that the -.Nm ld-linux.so -library is required as well. This is unfortunate and for this reason it -might not be harder to use a fully dynamically linked -.Xr cvs 1 -when using glibc. If you are using Linux and want to have a statically linked -version without needing external libraries, you will most probably need to -use another libc library to build it, such as -.Nm diet-libc -or -.Nm uclibc . -.It Nm The temporary working directory -It is now necessary to create the temporary working directory which will -be used both by -.Xr cvs 1 -for the read lock files and for -.Xr mmanoncvs 8 -to create temporary mirror backups before replacing the official mirror -on success. It is thus necessary to create the -.Nm /var/anoncvs/tmp -directory of owner 'root', group 'anoncvs', and mode 770. -The -.Nm TMPDIR -parameter in -.Xr mmanoncvs.conf 5 -will be set to this directory. -.It Nm The actual public CVS pserver repository root -This is the actual mirror, which is maintained by -.Xr mmanoncvs 8 -and corresponds to the -.Nm REPDIR -parameter of -.Xr mmanoncvs.conf 5 . -This is also the -.Nm /cvsroot -directory as seen by CVS pserver users. The -.Nm /var/anoncvs/cvsroot -directory should be created. It's owner should correspond to the one under -which -.Xr mmanoncvs 8 -is set to execute it's tasks. In practive, this usually means 'root' owner -or 'anoncvsa' owner. It's group should be 'anoncvs', and it's mode 750. -Please see the -.Xr mmanoncvs.conf 5 -manual page for more information about the issues involved in using an -unprivileged user. -.It Nm Additional tips and tricks -It is possible on NetBSD to use the NULLFS filesystem to mount various -directories of arbitrary filesystems into the wanted locations of the new -.Nm /var/anoncvs -root (and the noexec flag can be used). This can allow for instance to have -a system mounted with the noexec flag, except for /bin where -.Xr cvs 1 -resides. On most unix systems, alternatives can be used using real filesystems -to achieve the same results. -.Xr mmanoncvs 8 , -if allowed to keep the executable bit on files when copying using the -.Nm KEEP_EXEC -parameter in -.Xr mmanoncvs.conf 5 , -ensures to only set the executable bit of the owner of the files. -However, if a new -.Xr cvs 1 -vulnerability was discovered which allowed to upload arbitrary files into -.Nm /var/anoncvs/tmp -(the only directory -.Xr mmspawnd 8 -allow it arbitrary write access), it would be nice to have a noexec -.Nm /tmp . -.Pp -If enabling hostname resolution in -.Xr mmspawnd 8 , -it might be necessary to create the -.Nm /var/anoncvs/resolv.conf -file of owner 'root', group 'anoncvs' and mode 440, and to specify the -IP addresses of the DNS servers in it. Otherwise hostname resolution will -fail on many systems using the -.Xr bind 3 -libraries. -.El -.Ss Example configuration files -Because -.Xr mmspawnd 8 -and -.Xr mmanoncvs 8 -depend on eachother, it is a good idea to show a few example configuration -files which were used successfully for particular setups. Here is a setup -to run -.Xr mmspawnd 8 -as unprivileged user 'anoncvs' (which should always be done), and to run -.Xr mmanoncvs 8 -via a cron event as root, to drop privileges to user 'anoncvsa' (which -requires write access to the directories to copy, thus adding the 'cvs' -group in -.Nm GROUPS ) . -A simpler setup is to run -.Xr mmanoncvs 8 -as the superuser from a cron event setting -.Nm DROP_PRIVS -to FALSE. If an existing, enabled unprivileged user wants to run it -as a cron event, -.Nm DROP_PRIVS -should be FALSE, and the user should have the necessary write permissions -to the source repository(ies), as well as access to lock the -.Nm ALOCK_PATH . -See the -.Xr mmanoncvs.conf 5 -and -.Xr mmspawnd.conf 5 -manual pages for details. -.Bl -tag -width indent -offset indent -.It Nm /usr/local/etc/mmspawnd-anoncvs.conf -.Bd -literal -offset left -# See mmspawnd.conf(5) manual page for more information -# and default values - -PID_PATH /var/run/mmspawnd-anoncvs.pid -LOCK_PATH /var/run/mmspawnd-anoncvs.lock - -# The following lock is useful for mmanoncvs(8) -ALOCK_PATH /var/run/mmspawnd-anoncvs.alock -# root if mmanoncvs(8) DROP_PRIVS=FALSE -ALOCK_USER anoncvsa # ^ -ALOCK_GROUP anoncvs -ALOCK_MODE 400 - -USER anoncvs -GROUPS anoncvs - -CHROOT_DIR /var/anoncvs - -PROCTITLE "CVS pserver" -LISTEN_PORT 2401 -COMMAND "/bin/cvs -z6 -f --allow-root=/cvsroot pserver" - -RESOLVE_ADDRESSES FALSE - -KILL_CHILDREN FALSE - -# Note: It is generally a bad idea to kill cvs while it -# runs (since it holds lock files). So we set these limits -# but put them high enough to not interfere with normal CVS -# operation. -COMMAND_TIMEOUT 1200 -RLIMITS TRUE -RLIMIT_CORE 0 -RLIMIT_CPU 1200 -.Ed -.It Nm /usr/local/etc/mmanoncvs.conf -.Bd -literal -offset left -DROP_PRIVS TRUE -USER anoncvsa -GROUPS anoncvs,cvs -REPGROUP anoncvs - -LOCK_PATH /var/run/mmanoncvs.lock -ALOCK_LOCKING TRUE -ALOCK_PATH /var/run/mmspawnd-anoncvs.alock -CVS_LOCKING TRUE -CVS_LOCK_DELAY 30 -CVS_LOCK_TIMEOUT 300 - -REPDIR /var/anoncvs/cvsroot -TMPDIR /var/anoncvs/tmp -LISTFILE /usr/local/etc/mmanoncvs.list -MAX_RECURSION 20 -KEEP_EXEC TRUE -.Ed -.El -.Ss Using the public CVS pserver -Although this does not consist of a CVS manual, because some of the aspects -are configuration-dependant, it is a good idea to post on the HTTP site -of a project how users can query the anonymous server. -If the examples were followed, access would be as followed to checkout: -.Pp -.Bd -literal -offset indent -$ cvs -d:pserver:anoncvs@:/cvsroot login -password: - -$ cvs -z6 -d:pserver:anoncvs@:/cvsroot co -.Ed -.Ss Troubleshooting -If there are access problems such as -.Nm anoncvs: no such user , -either that the username chosen during configuration was different than -user 'anoncvs', which the -.Nm CVSROOT/passwd -and -.Nm CVSROOT/readers -files are set to use by -.Xr mmanoncvs 8 . -Another possibility is that the -.Nm /etc/passwd -or other equivalent files were not setup properly for your OS in the new root -(typically -.Nm /var/anoncvs/etc/passwd -and friend(s)). -.Pp -If a complaint about -.Nm /dev/null: no such file or directory -arises, make sure to create the special character device file and to set it's -permissions as described in the configuration section above. -.Pp -Although -.Xr mmanoncvs 8 -attempts to make repository updates as atomic as possible, if a user complains -that inconsistent CVS checkouts or updates happen occasionally, make sure that -the -.Nm ALOCK_* -related parameters are set right in both -.Xr mmspawnd.conf 5 -and -.Xr mmanoncvs.conf 5 -configuration files to work with eachother. Also, there are two possible -problems which can occur with concurrency and CVS-friendly locking, if not -setup properly: The source repositories' -.Nm CVSROOT/config -file should not set an alternate LockDir option. If it does, it is possible -for -.Xr cvs 1 -and -.Xr mmanoncvs 8 -to mistakenly rely on different locations for locking. Also, it is important -for -.Xr mmanoncvs 8 -to be able to create directories and files in the source -repositories if consistancy is wanted with concurrency, because this is -where it needs to create lock directories and files. This is how the CVS -protocol works. Disabling -.Nm CVS_LOCKING -in -.Xr mmanoncvs.conf 5 -or running -.Xr mmanoncvs 8 -as an unprivileged user can often lead to problems in that -area. The unprivileged user in that case should be part of a group set -in -.Nm GROUPS -which is allowed write access to the source repositories. -.Pp -If you get other problems, it is possible that the cvs executable is linked -dynamically, and that it cannot properly load it's required library files. -Make sure to use the -.Xr ldd 1 -command to detect which libraries should be copied to the library directory. -Many operating systems also require a special executable to be present on the -system for the dynamic loader to be invoked by other binaries. Using -.Xr ktrace 1 , -.Xr strace 1 -command or equivalent would help detect which system calls failed during the -cvs startup. To waste less time and avoid alot of problems, using a statically -linked executable for cvs is a very good idea. No -.Nm /lib -directory is required then. -.Pp -When working with a dynamically linked setup, it may be good to temporary -place a shell into the new root's -.Nm /bin , -and to use the -.Xr chroot 8 -command to test that the cvs executable indeed runs. It is recommended to -delete that shell from the new root afterwards. -.Pp -If other permissions problem occur, verify that your configuration comforms -to the instructions of this file. You can alternatively use the -.Xr su 1 -command to test that the files are accessible under the anoncvs user. It is a -good idea to have the 'anoncvs' user map to the same UID on both the actual -system and the new root in their passwd files, which makes configuration -easier. A common issue not setting -.Nm /cvsroot -to have the unprivileged user as it's owner with write permissions, and -.Nm /tmp -to not have write access for 'anoncvs' group. Or, the unprivileged user is -not part of the 'anoncvs' group. It is also possible that -.Xr mmspawnd 8 -was not configured to set the -.Nm ALOCK_USER -parameter in -.Xr mmspawnd.conf 5 -to the right unprivileged user. -.Pp -Make sure that the 'anoncvs' and 'anoncvsa' users cannot be used by other users -using the -.Xr su 1 -command. They should consist of disabled accounts. If your system does not -allow passwordless disabled users, make sure to use the -.Xr passwd 1 -command for each to specify a hard to guess password. It is important that the -sensitive files in the new root's -.Nm /etc -do not allow the anoncvs user to read those password hashes then. -.Pp -If you get -.Nm Cannot drop privileges -errors from -.Xr mmanoncvs 8 , -you either should set -.Nm DROP_PRIVS -to FALSE in -.Xr mmanoncvs.conf 5 -and ensure that the unprivileged user which launches the program has the -appropriate permissions for the mirror process to work. Another solution is -to launch -.Xr mmanoncvs 8 -as root, where it can optionally change it's permissions to the wanted -.Nm USER -and -.Nm GROUPS -if -.Nm DROP_PRIVS -is TRUE, or run as the superuser all along setting -.Xr DROP_PRIVS -to FALSE. -.Pp -If -.Xr mmspawnd 8 -complains that your -.Xr cvs 1 -executable is invalid, make sure that it has the mode described in the above -configuration, and that it indeed consists of an ELF executable. This can -be done by using the -.Xr hexdump 1 , -.Xr od 1 -or -.Xr xxd 1 -command to ensure that the ELF string is indeed located in the first four -bytes of the file. -.Xr mmspawnd 8 -does not currently allow to execute other types of binary files. If support -for a.out is important for you, please send me a copy of your CVS executable -so that I can make it support a.out, too. -.Pp -Note that -.Xr mmanoncvs 8 -creates temporary directories in -.Nm TMPDIR , -as well as CVS-friendly locks in the repositories. If broken in operation by -a signal, it attempts to delete both locks and temporary directories. If you -do notice that it often complains about already held CVS locks on repositories, -and that -.Nm #cvs.lock -and -.Nm #cvs.rfl.* -files exist in each directory of the repository, owned by the user running -.Xr mmanoncvs 8 , -it probably could not free those locks for some reason; This can happen as well -for -.Xr cvs 1 , -and is usually the result of a system crash during operation. If this occurs, -you can use the following commands: -.Bd -literal -offset indent -# cd -# find . -name '#cvs.lock' | xargs rmdir -# find . -name '#cvs.rfl.*' | xargs rm -# cd /var/anoncvs/tmp -# ls -d mmanoncvs.* | xargs rm -rf -.Ed -.Pp -When things do not work as intended, it may be a good idea to enable logging -of stderr to a temporary file using the -.Nm STDERR_FILE -.Xr mmspawnd.conf -parameter. Make sure to not use this feature on a production system however, -as the file will grow indefinitely (and using a program to rotate the file -will require to restart -.Xr mmspawnd 8 ) . -This also can be useful in conjunction with a program like -.Xr ktrace 1 -or -,Xr strace 1 , -which can be provided as the first part of the -.Nm COMMAND -parameter, provided that it is available in the new root, and is either -statically linked, or has the required dynamic modules installed in the new -root to function properly. You also will want to remove the tracing command -before putting the system on production. -.Sh FILES -.Bl -tag -width indent -offset indent -.It Pa /usr/local/sbin/mmanoncvs -The binary of this program -.It Pa /usr/local/etc/mmanoncvs.conf -The default -.Xr mmanoncvs.conf 5 -configuration file to read -.It Pa /usr/local/etc/mmanoncvs.list -The default -.Xr mmanoncvs.list 5 -configuration file to read -.El -.Sh AUTHOR -.Nm -was written by Matthew Mondor, and is -Copyright (c) 2003-2004, Matthew Mondor, All Rights Reserved. -.Sh SEE ALSO -.Xr mmanoncvs.conf 5 , -.Xr mmanoncvs.list 5 , -.Xr elf 5 , -.Xr mmspawnd 8 , -.Xr cvs 1 , -.Xr ldd 1 . -.Sh BUGS -Please report any bug to mmondor@accela.net diff --git a/mmsoftware/mmanoncvs/mmanoncvs.c b/mmsoftware/mmanoncvs/mmanoncvs.c deleted file mode 100644 index 1d23017..0000000 --- a/mmsoftware/mmanoncvs/mmanoncvs.c +++ /dev/null @@ -1,1425 +0,0 @@ -/* $Id: mmanoncvs.c,v 1.28 2004/10/05 15:39:04 mmondor Exp $ */ - -/* - * Copyright (C) 2003-2004, Matthew Mondor - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Matthew Mondor. - * 4. The name of Matthew Mondor may not be used to endorse or promote - * products derived from this software without specific prior written - * permission. - * 5. Redistribution of source code may not be released under the terms of - * any GNU Public License derivate. - * - * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -/* - * Program to setup and update a read-only mirror repository for anoncvs - * pserver access using mmspawnd(8). Carefully read the mmanoncvs(8) manual - * page, as well as mmanoncvs.conf(5) for details and setup instructions. - * Was made in an attempt to be as secure as possible, even when running as - * the superuser. In fact, most setups will require to run this as root - * via a cron event scheduler, so that CVS read locks may be created in - * the user CVS repositories. If you can read C, please take care to audit - * this code and report any discovered security issue to mmondor@accela.net - * so that we update the official distribution as fast as possible. - * In a sense, this is paranoiaware :) - */ - - - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include /* strerror(3) */ -#include /* PATH_MAX */ -#include -#include - -/* See mmlib/ directory for corresponding manual pages for more information */ -#include -#include -#include -#include - - - -MMCOPYRIGHT("@(#) Copyright (c) 2003\n\ -\tMatthew Mondor. All rights reserved.\n"); -MMRCSID("$Id: mmanoncvs.c,v 1.28 2004/10/05 15:39:04 mmondor Exp $"); - - - -/* DEFINITIONS */ - -#define PROGRAM_NAME "mmanoncvs" -#define PROGRAM_VERSION "0.0.2/mmondor" - -struct configuration { - char LOCK_PATH[256], USER[32], GROUPS[256], ALOCK_PATH[256], TMPDIR[256], - REPDIR[256], LISTFILE[256], REPGROUP[32]; - long CVS_LOCK_DELAY, CVS_LOCK_TIMEOUT, MAX_RECURSION, MAX_FILESIZE; - bool DROP_PRIVS, CVS_LOCKING, ALOCK_LOCKING, KEEP_EXEC; -}; - -/* This is used to create the files in CVSROOT/. If contents is NULL, a - * directory is created. - */ -struct cvsroot_file { - mode_t mode; - const char *name; - const char *contents; -}; - -/* Used internally to store the contents of a directory after locks have been - * checked for and sanity checking performed to reject invalid files. Lock - * files will not be included. - */ -struct dir_node { - pnode_t node; - size_t size, namelen; - int flags; - char name[PATH_MAX]; -}; - -/* dir_node->flags */ -#define DNF_DIRECTORY (1 << 0) -#define DNF_EXECUTABLE (1 << 1) - -/* Because of the way CVS works, we must obtain a read access lock in the - * whole repository, recursively, before we can proceed. Moreover, in a case - * where we need to retry because if a lock which could not be obtained, - * we must free all successful locks in the same order, and then try again - * recursively on the whole tree, to prevent deadlocks. The following - * represents a successfully locked directory in a tree. This way we can - * easily revert and delete all obtained locks. - */ -struct tree_node { - pnode_t node; - size_t namelen; - list_t dir; - char name[PATH_MAX]; -}; - -/* Errors returned by tree_open() and dir_open() */ -enum errs { - E_OK = 0, - E_LOCKED, - E_NOMEM, - E_NOTFOUND, - E_PERM, - E_WRITE, - E_RLEVEL, - E_MAX -}; - -/* Useful macro to map enum errs codes to strings */ -#define STRERROR(e) (errs_strs[(e)]) - -/* The size of the file copy buffer in bytes */ -#define COPYBUF_SIZE 65536 - - - -/* PROTOTYPES */ - -int main(int, char **); -static void main_mirror(void); - -static void sighandler(int); -static int lock_check(const char *); - -static bool tmpdir_create(char *); -static void tmpdir_delete(char *); -static void tmpdir_save(char *); - -static bool cvsroot_build(const char *); -static bool cvsroot_delete(const char *); - -static bool dir_valid(char *); -static bool dir_make(const char *, const char *); -static int dir_open(list_t *, const char *, bool); -static void dir_close(list_t *); -static int tree_open(list_t *, const char *, bool); -static int tree_open_r(list_t *, const char *, bool); -static void tree_close(list_t *, bool); -static bool tree_copy(const char *, const char *); -static bool file_copy(struct dir_node *, const char *, size_t); -static bool tree_delete(const char *); - -static bool mm_chgrp(const char *); - - - -/* GLOBALS */ - -/* Map of E_* errors to strings */ -static const char *errs_strs[E_MAX] = { - "Success", - "Lock exists", - "Out of memory", - "No such file or directory", - "Permission denied", - "Write I/O error", - "Maximum recursion level exceeded" -}; - -/* Contents of CVSROOT directory */ -static const struct cvsroot_file cvsroot_files[] = { - {0755, "Emptydir", NULL}, - {0644, "checkoutlist", ""}, - {0644, "commitinfo", ""}, - {0644, "config", "SystemAuth=no\nLockDir=/tmp\nLogHistory=\n"}, - {0644, "cvswrappers", ""}, - {0644, "editinfo", ""}, - {0644, "history", ""}, - {0644, "loginfo", ""}, - {0644, "modules", ""}, - {0644, "notify", ""}, - {0644, "passwd", "anoncvs::anoncvs\n"}, - {0644, "rcsinfo", ""}, - {0644, "readers", "anoncvs\n"}, - {0644, "taginfo", ""}, - {0664, "val-tags", ""}, - {0644, "verifymsg", ""}, - {0000, NULL, NULL} -}; - -/* Various required globals */ -static struct configuration CONF; -static FILE *fh_reps = NULL; -static int runlock = -1; -static pool_t dir_pool, tree_pool; -static pid_t pid; -static long recursion_level = 0; -static off_t ssize_max; -static gid_t repgroup; -/* Used by main_mirror(), tree_copy(), tmpdir_save() and sighandler() */ -static list_t gtree; -static char gtmpdir[PATH_MAX], gtmpdir2[PATH_MAX]; -static char *copybuf; - - - -/* CODE */ - -int main(int argc, char **argv) -{ - char *conf_file = "/usr/local/etc/mmanoncvs.conf"; - pid_t uid; - gid_t *gids; - int ngids, c; - cres_t cres; - carg_t *cargp; - carg_t cargs[] = { - {CAT_STR, CAF_NONE, 1, 255, "LOCK_PATH", CONF.LOCK_PATH}, - {CAT_STR, CAF_NONE, 1, 31, "USER", CONF.USER}, - {CAT_STR, CAF_NONE, 1, 255, "GROUPS", CONF.GROUPS}, - {CAT_STR, CAF_NONE, 1, 255, "ALOCK_PATH", CONF.ALOCK_PATH}, - {CAT_STR, CAF_NONE, 1, 255, "TMPDIR", CONF.TMPDIR}, - {CAT_STR, CAF_NONE, 1, 255, "REPDIR", CONF.REPDIR}, - {CAT_STR, CAF_NONE, 1, 255, "LISTFILE", CONF.LISTFILE}, - {CAT_STR, CAF_NONE, 1, 31, "REPGROUP", CONF.REPGROUP}, - {CAT_VAL, CAF_NONE, 1, 600, "CVS_LOCK_DELAY", &CONF.CVS_LOCK_DELAY}, - {CAT_VAL, CAF_NONE, 1, 99999, "CVS_LOCK_TIMEOUT", - &CONF.CVS_LOCK_TIMEOUT}, - {CAT_VAL, CAF_NONE, 1, 99, "MAX_RECURSION", &CONF.MAX_RECURSION}, - {CAT_VAL, CAF_NONE, 1, 2147483647, "MAX_FILESIZE", &CONF.MAX_FILESIZE}, - {CAT_BOOL, CAF_NONE, 0, 0, "DROP_PRIVS", &CONF.DROP_PRIVS}, - {CAT_BOOL, CAF_NONE, 0, 0, "CVS_LOCKING", &CONF.CVS_LOCKING}, - {CAT_BOOL, CAF_NONE, 0, 0, "ALOCK_LOCKING", &CONF.ALOCK_LOCKING}, - {CAT_BOOL, CAF_NONE, 0, 0, "KEEP_EXEC", &CONF.KEEP_EXEC}, - {CAT_END, CAF_NONE, 0, 0, NULL, NULL} - }; - - /* Parse command line arguments */ - while ((c = getopt(argc, argv, "f:")) != -1) { - switch (c) { - case 'f': - conf_file = optarg; - break; - case '?': - /* FALLTHROUGH */ - default: - (void) fprintf(stderr, "usage: mmanoncvs [-f ]\n"); - exit(EXIT_FAILURE); - } - } - argc -= optind; - argv += optind; - - /* We only need stderr for logging/errors. */ - (void) setvbuf(stdin, NULL, _IOFBF, 0); - (void) setvbuf(stdout, NULL, _IOFBF, 0); - (void) setvbuf(stderr, NULL, _IOFBF, 0); - (void) freopen("/dev/null", "r", stdin); - (void) freopen("/dev/null", "w", stdout); - - /* Set configuration defaults */ - (void) mm_strcpy(CONF.LOCK_PATH, "/tmp/mmanoncvs.lock"); - (void) mm_strcpy(CONF.USER, "anoncvsa"); - (void) mm_strcpy(CONF.GROUPS, "anoncvs,users"); - (void) mm_strcpy(CONF.ALOCK_PATH, - "/var/run/mmspawnd-anoncvs.alock"); - (void) mm_strcpy(CONF.TMPDIR, "/var/anoncvs/tmp"); - (void) mm_strcpy(CONF.REPDIR, "/var/anoncvs/cvsroot"); - (void) mm_strcpy(CONF.LISTFILE, "/usr/local/etc/mmanoncvs.list"); - (void) mm_strcpy(CONF.REPGROUP, "anoncvs"); - CONF.CVS_LOCK_DELAY = 30; - CONF.CVS_LOCK_TIMEOUT = 300; - CONF.MAX_RECURSION = 20; - CONF.MAX_FILESIZE = 2147483647; - CONF.DROP_PRIVS = FALSE; - CONF.CVS_LOCKING = TRUE; - CONF.ALOCK_LOCKING = TRUE; - CONF.KEEP_EXEC = TRUE; - - /* Read config file */ - if (!mmreadcfg(&cres, cargs, conf_file)) { - /* Error parsing configuration file, report which */ - (void) fprintf(stderr, "\nError parsing '%s'\n", conf_file); - (void) fprintf(stderr, "Error : %s\n", mmreadcfg_strerr(cres.CR_Err)); - if (*(cres.CR_Data)) - (void) fprintf(stderr, "Data : %s\n", cres.CR_Data); - if ((cargp = cres.CR_Keyword) != NULL) { - (void) fprintf(stderr, "Keyword: %s\n", cargp->CA_Keyword); - (void) fprintf(stderr, "Minimum: %ld\n", cargp->CA_Min); - (void) fprintf(stderr, "Maximum: %ld\n", cargp->CA_Max); - } - if (cres.CR_Line != -1) - (void) fprintf(stderr, "Line : %d\n", cres.CR_Line); - (void) fprintf(stderr, "\n"); - exit(EXIT_FAILURE); - } - - /* Post parsing */ - if (CONF.DROP_PRIVS) { - if ((uid = mmgetuid(CONF.USER)) == -1) { - (void) fprintf(stderr, "\nUnknown USER '%s'\n\n", CONF.USER); - exit(EXIT_FAILURE); - } - if (!(gids = mmgetgidarray(&ngids, CONF.GROUPS))) { - (void) fprintf(stderr, - "\nOne of following GROUPS unknown: '%s'\n\n", - CONF.GROUPS); - exit(EXIT_FAILURE); - } - } else { - uid = 0; - gids = NULL; - } - if ((repgroup = mmgetgid(CONF.REPGROUP)) == -1) { - (void) fprintf(stderr, "\nREPGROUP '%s' unknown\n\n", CONF.REPGROUP); - exit(EXIT_FAILURE); - } - if (!dir_valid(CONF.REPDIR)) { - (void) fprintf(stderr, "\nREPDIR '%s' should begin with '/'\n\n", - CONF.REPDIR); - exit(EXIT_FAILURE); - } - if (!dir_valid(CONF.TMPDIR)) { - (void) fprintf(stderr, "\nTMPDIR '%s' should begin with '/'\n\n", - CONF.TMPDIR); - exit(EXIT_FAILURE); - } - if ((fh_reps = fopen(CONF.LISTFILE, "r")) == NULL) { - (void) fprintf(stderr, "\nCannot open LISTFILE '%s' - (%s)\n\n", - CONF.LISTFILE, strerror(errno)); - exit(EXIT_FAILURE); - } - - /* Initialization */ - - /* The modes for open(2) and mkdir(2) are relative to the umask(2) for - * the process - */ - (void) umask(0); - - if (!pool_init(&dir_pool, "dir_pool", malloc, free, NULL, NULL, - sizeof(struct dir_node), 65536 / sizeof(struct dir_node), - 1, 0) || - !pool_init(&tree_pool, "tree_pool", malloc, free, NULL, NULL, - sizeof(struct tree_node), 65536 / sizeof(struct tree_node), - 1, 0) || - (copybuf = malloc(COPYBUF_SIZE)) == NULL) { - (void) fprintf(stderr, "\nOut of memory\n\n"); - exit(EXIT_FAILURE); - } - - /* A few constants for the duration of the program which we record now */ - pid = getpid(); - ssize_max = (off_t)CONF.MAX_FILESIZE; - - /* Make sure that only one instance is running to prevent self-recursion - * issues. If it takes a considerable time to perform our tasks because - * of CVS locks on a repository, this would prevent undefined behavior. - * We need to do this before we drop privileges. - */ - if ((runlock = lock_check(CONF.LOCK_PATH)) == -1) { - (void) fprintf(stderr, "\n%s already running\n\n", PROGRAM_NAME); - exit(EXIT_FAILURE); - } - - /* Finally drop privileges if requested. Although this is a nice feature, - * We need to both be able to access the mmspawnd ALOCK for the CVS - * pserver, as well as to have permissions to create files and directories - * in the source repositories to be mirrorred (this is required for proper - * cooperation with CVS under concurrency). - */ - if (CONF.DROP_PRIVS) { - if (!mmdropprivs(uid, gids, ngids)) { - (void) fprintf(stderr, "\nCould not drop privileges!\n\n"); - exit(EXIT_FAILURE); - } - } - - /* Verify if we can access the mmspawnd ALOCK, with our new permissions. - * If we just obtained a filedescriptor to it now with open(2) we would - * need to assume that it never be restarted during our runtime, which - * we want to avoid doing. - */ - if (CONF.ALOCK_LOCKING && (access(CONF.ALOCK_PATH, R_OK)) == -1) { - (void) fprintf(stderr, "Cannot access '%s' mmspawnd lock file\n\n", - CONF.ALOCK_PATH); - exit(EXIT_FAILURE); - } - - /* Setup our signal handler which will call tree_close(>ree, TRUE) if - * CONF.CVS_LOCKING is TRUE - */ - { - struct sigaction act; - - DLIST_INIT(>ree); - *gtmpdir = *gtmpdir2 = '\0'; - act.sa_handler = sighandler; - act.sa_flags = 0; - (void) sigemptyset(&act.sa_mask); - (void) sigaction(SIGTERM, &act, NULL); - (void) sigaction(SIGINT, &act, NULL); - (void) sigaction(SIGSEGV, &act, NULL); - } - - /* Finally perform our work */ - main_mirror(); - /* NOTREACHED */ - - exit(EXIT_SUCCESS); -} - - -/* This is the glue of the system. */ -static void main_mirror(void) -{ - char line[1024], buf[PATH_MAX], *cols[3]; - int lines; - bool ok = TRUE; - - if (!tmpdir_create(gtmpdir)) - exit(EXIT_FAILURE); - if (!cvsroot_build(gtmpdir)) { - tmpdir_delete(gtmpdir); - exit(EXIT_FAILURE); - } - - for (lines = 1; fgets(line, 1023, fh_reps) != NULL; lines++) { - char *cptr; - - /* Make sure to ignore comments and empty lines */ - for (cptr = line; *cptr == ' ' || *cptr == '\t'; cptr++) ; - if (*cptr == '\r' || *cptr == '\n' || *cptr == '#' || *cptr == ';') - continue; - - /* Now isolate the two columns, stripping ending '\n' or '\r\n' too */ - if (mm_strasplq(cols, line, 2) != 2) { - (void) fprintf(stderr, "Invalid line %d in '%s'\n", lines, - CONF.LISTFILE); - continue; - } - - /* Make sure that each path begins with a '/', and strip any trailing - * ones. - */ - if (!dir_valid(cols[0])) { - (void) fprintf(stderr, - "Invalid destination path '%s' at line %d in %s\n", - cols[0], lines, CONF.LISTFILE); - continue; - } - if (!dir_valid(cols[1])) { - (void) fprintf(stderr, - "Invalid source path '%s' at line %d in %s\n", - cols[1], lines, CONF.LISTFILE); - continue; - } - - /* Both pathnames seem valid; We now need to create the directories - * for the destination as required in . We then can attempt - * the copy. If it fails, we abandon to leave the last successful - * backup. - */ - (void) snprintf(buf, PATH_MAX - 1, "%s%s", gtmpdir, cols[0]); - if (!dir_make(gtmpdir, cols[0]) || !tree_copy(buf, cols[1])) { - (void) fprintf(stderr, "Errors to mirror '%s' to '%s%s' " - "(line %d). Aborting mirror to keep previous " - "successful backup.\n", - cols[1], CONF.REPDIR, cols[0], lines); - ok = FALSE; - break; - } - } - (void) fclose(fh_reps); - - if (ok) { - /* Everything successful; Replace old repository by new one */ - tmpdir_save(gtmpdir); - exit(EXIT_SUCCESS); - } else { - /* Delete temporary directory */ - tmpdir_delete(gtmpdir); - exit(EXIT_FAILURE); - } -} - - -/* This signal handler attempts to unlink all acquired locks as well as\ - * temporary work directories. - */ -static void sighandler(int sig) -{ - sigset_t set; - - /* A signal handler may be broken by another signal. Because we perform - * the same operation for all three signals we handle, and that we only - * want to perform it once, block signals we expect to receive. - */ - (void) sigemptyset(&set); - (void) sigaddset(&set, SIGTERM); - (void) sigaddset(&set, SIGINT); - (void) sigaddset(&set, SIGSEGV); - (void) sigprocmask(SIG_BLOCK, &set, NULL); - - switch (sig) { - case SIGTERM: - /* FALLTHROUGH */ - case SIGINT: - /* FALLTHROUGH */ - case SIGSEGV: - /* FALLTHROUGH */ - default: - (void) fprintf(stderr, "Received signal %d, attempting to cleanup\n", - sig); - tree_close(>ree, CONF.CVS_LOCKING); - tmpdir_delete(gtmpdir); - tmpdir_delete(gtmpdir2); - exit(EXIT_FAILURE); - } -} - - -/* Used to prevent self-recursion. Returns -1 if the lock is already held, or - * the filedescriptor to the lock file (which we should not close until we - * exit). - */ -static int lock_check(const char *file) -{ - int fd; - - if ((fd = open(file, O_CREAT | O_TRUNC | O_WRONLY, 0600)) != -1) { - if ((flock(fd, LOCK_EX | LOCK_NB)) == 0) - return fd; - (void) close(fd); - } else - (void) fprintf(stderr, "lock_check() - open(%s) - (%s)\n", file, - strerror(errno)); - - return -1; -} - - -/* Attempts to create a unique directory in CONF.TMPDIR, in which it will - * mirror the repositories, until we either delete it on failure, or move it - * over the old official CVS pserver repository REPDIR on success. Obtains an - * unsigned long worth of "/dev/urandom" device, and uses 4.2BSD srandom(3)/ - * random(3) which are better than ANSI rand(3)/srand(3), and available on - * most unices. On success, returns TRUE and sets to the absolute - * fullpath to the new directory. - */ -static bool tmpdir_create(char *tmpdir) -{ - int fd; - bool ok = FALSE; - unsigned long seed; - char buf[PATH_MAX]; - int i; - - *tmpdir = '\0'; - if ((fd = open("/dev/urandom", O_RDONLY)) != -1) { - if (read(fd, &seed, sizeof(unsigned long)) == sizeof(unsigned long)) { - srandom(seed); - /* We stop after 64 failed mkdir(2) attempts with EEXIST */ - for (i = 0; i < 64; i++) { - (void) snprintf(buf, PATH_MAX - 1, "%s/%s.%ld", - CONF.TMPDIR, PROGRAM_NAME, random()); - if (mkdir(buf, 0755) == 0) { - if (mm_chgrp(buf)) { - ok = TRUE; - (void) mm_strncpy(tmpdir, buf, PATH_MAX - 1); - } else - (void) rmdir(buf); - break; - } else if (errno != EEXIST) { - (void) fprintf(stderr, - "tmpdir_create() - mkdir(%s) - (%s)\n", - buf, strerror(errno)); - break; - } - } - if (i == 64) - (void) fprintf(stderr, - "tmpdir_create() - Failed after 64 attempts\n"); - } - (void) close(fd); - } - - return ok; -} - - -/* Deletes everything we may have created in , as well as - * itself. - */ -static void tmpdir_delete(char *tmpdir) -{ - if (*tmpdir != '\0') { - (void) cvsroot_delete(tmpdir); - (void) tree_delete(tmpdir); - (void) rmdir(tmpdir); - *tmpdir = '\0'; - } -} - - -/* Attempts to atomically use rename(2) to move the tmpdir to REPDIR. - * This is supposed to either repace the current REPDIR instantly, or - * to create it if it does not exist. We ensure to hold the mmspawnd(8) - * ALOCK synchronization lock for a very short moment during the move. - * This will wait until no more pserver processes are running, and will - * prevent any from being started while we hold the lock. This mmspawnd(8) - * feature, along with mmanoncvs(8)'s CVS friendly locking support, ensure - * clean operation with concurrency. We wouldn't want users to complain about - * partial or conflicting CVS updates. - */ -static void tmpdir_save(char *tmpdir) -{ - int fd = -1; - bool ok = FALSE; - bool locked; - - if (tmpdir_create(gtmpdir2)) { - if (CONF.ALOCK_LOCKING) { - locked = FALSE; - if ((fd = open(CONF.ALOCK_PATH, O_RDONLY)) != -1) { - if ((flock(fd, LOCK_EX)) == 0) - locked = TRUE; - else - (void) fprintf(stderr, - "tmpdir_save() - flock(%s) - (%s)\n", - CONF.ALOCK_PATH, strerror(errno)); - } else - (void) fprintf(stderr, "tmpdir_save() - open(%s) - (%s)\n", - CONF.ALOCK_PATH, strerror(errno)); - } else - locked = TRUE; - if (locked) { - if (rename(CONF.REPDIR, gtmpdir2) == 0) { - if (rename(tmpdir, CONF.REPDIR) == 0) - ok = TRUE; - else - (void) fprintf(stderr, - "tmpdir_save() - rename(%s,%s) - (%s)\n", - tmpdir, CONF.REPDIR, strerror(errno)); - } else - (void) fprintf(stderr, - "tmpdir_save() - rename(%s,%s) - (%s)\n", - CONF.REPDIR, gtmpdir2, strerror(errno)); - if (CONF.ALOCK_LOCKING) - (void) flock(fd, LOCK_UN); - } - if (fd != -1) - (void) close(fd); - tmpdir_delete(gtmpdir2); - } - - if (!ok) { - (void) fprintf(stderr, "Deleting temporary directory '%s' since we " - "could not update '%s' with it. This cancels the " - "update, the previous anoncvs repository remains " - "unchanged.\n", - tmpdir, CONF.REPDIR); - tmpdir_delete(tmpdir); - } -} - - -/* There are no user-supplied pathnames here, we can just use snprintf(3) - * safely. is provided by the program, previously generated and - * set to a directory name in the temporary directory. This function creates - * the anonymous CVS repository's CVSROOT directory with it's contents. The - * setup it creates is to work with the mmspawnd(8) daemon with a chroot(2) - * based configuration. The LockDir option in the CVSROOT/config file is - * set to "/tmp", so that repository access may be read-only for the anoncvs - * user. - */ -static bool cvsroot_build(const char *rootpath) -{ - const struct cvsroot_file *f; - bool ok = TRUE; - char buf[PATH_MAX]; - - /* First attempt to create the CVSROOT directory */ - (void) snprintf(buf, PATH_MAX - 1, "%s/CVSROOT", rootpath); - if (mkdir(buf, 0755) == -1) { - (void) fprintf(stderr, "cvsroot_build() - mkdir(%s) - (%s)\n", buf, - strerror(errno)); - return FALSE; - } - if (!mm_chgrp(buf)) - return FALSE; - - /* Then build the CVSROOT/ file structure */ - for (f = cvsroot_files; f->name != NULL; f++) { - (void) snprintf(buf, PATH_MAX - 1, "%s/CVSROOT/%s", rootpath, f->name); - if (f->contents == NULL) { - /* Directory creation attempt */ - if (mkdir(buf, f->mode) == -1) { - (void) fprintf(stderr, "cvsroot_build() - mkdir(%s) - (%s)\n", - buf, strerror(errno)); - ok = FALSE; - } - if (!mm_chgrp(buf)) - ok = FALSE; - } else { - int fd; - size_t len; - - /* File creation attempt */ - if ((fd = open(buf, O_CREAT | O_WRONLY, f->mode)) != -1) { - len = mm_strlen(f->contents); - if (len != 0) { - if (write(fd, f->contents, len) != len) { - (void) fprintf(stderr, - "cvsroot_build() - write(%s) - (%s)\n", - buf, strerror(errno)); - ok = FALSE; - } - } - if (close(fd) == -1) { - (void) fprintf(stderr, - "cvsroot_build() - close(%s) - (%s)\n", - buf, strerror(errno)); - ok = FALSE; - } - if (!mm_chgrp(buf)) - ok = FALSE; - } else { - (void) fprintf(stderr, "cvsroot_build() - open(%s) - (%s)\n", - buf, strerror(errno)); - ok = FALSE; - } - } - } - - return ok; -} - - -/* This deletes the CVSROOT directory previously created using cvsroot_build(). - * This is required to delete the temporary data if a mirror operation fails, - * as the tree_delete() function will ignore CVSROOT, and will consider the - * files in it as invalid RCS files. - */ -static bool cvsroot_delete(const char *rootpath) -{ - const struct cvsroot_file *f; - bool ok = TRUE; - char buf[PATH_MAX]; - - /* First delete the CVSROOT/ file structure */ - for (f = cvsroot_files; f->name != NULL; f++) { - (void) snprintf(buf, PATH_MAX - 1, "%s/CVSROOT/%s", rootpath, f->name); - if (f->contents == NULL) { - /* Directory */ - if (rmdir(buf) != 0) { - /* - (void) fprintf(stderr, - "cvsroot_delete() - rmdir(%s) - (%s)\n", - buf, strerror(errno)); - */ - ok = FALSE; - } - } else { - /* File */ - if (unlink(buf) != 0) { - /* - (void) fprintf(stderr, - "cvsroot_delete() - unlink(%s) - (%s)\n", - buf, strerror(errno)); - */ - ok = FALSE; - } - } - } - - /* Then attempt to delete the CVSROOT directory */ - (void) snprintf(buf, PATH_MAX - 1, "%s/CVSROOT", rootpath); - if (rmdir(buf) != 0) { - /* - (void) fprintf(stderr, "cvsroot_delete() - rmdir(%s) - (%s)\n", - buf, strerror(errno)); - */ - ok = FALSE; - } - - return ok; -} - - -/* Returns TRUE if the supplied directory begins with a '/', and strips the - * tailing '/' (if any). Returns FALSE otherwise. - */ -static bool dir_valid(char *dirpath) -{ - if (*dirpath == '/') { - size_t len; - - len = mm_strlen(dirpath) - 1; - while (len > 0 && dirpath[len] == '/') { - dirpath[len] = '\0'; - len--; - } - - return TRUE; - } - - return FALSE; -} - - -/* Attempts to create the directories, if necessary, so that writing files - * to /// be possible. Returns TRUE on success, or FALSE - * on failure. Of course, we ignore EEXIST mkdir(2) errors. It assumes - * to consist of an existing directory. - * Both path elements must previously have been validated using dir_valid(). - */ -static bool dir_make(const char *rootpath, const char *dirpath) -{ - char buf[PATH_MAX], *cptr, *sptr; - size_t len; - - (void) snprintf(buf, PATH_MAX - 1, "%s%s/", rootpath, dirpath); - sptr = &buf[mm_strlen(rootpath) + 1]; - for (cptr = sptr; *cptr != '\0'; cptr++) { - if (*cptr == '/') { - if ((len = (cptr - sptr)) > 1) { - *cptr = '\0'; - if (mkdir(buf, 0755) != 0 && errno != EEXIST) { - (void) fprintf(stderr, "dir_make() - mkdir(%s) - (%s)\n", - buf, strerror(errno)); - return FALSE; - } else if (!mm_chgrp(buf)) - return FALSE; - *cptr = '/'; - sptr = cptr + 1; - } - } - } - - return TRUE; -} - - -/* This function consists of an opendir(3) replacement, which locks the - * directory in a CVS friendly manner, eliminates invalid files, and stores - * the fullpath of every file, along with their sizes. Requires the dir_pool - * pool_t to previously be initialized. On fatal errors, such as out of memory, - * permission denied, invalid directory, the lock is released and the - * corresponding error code is returned (as defined by enum errs). Returns - * E_OK on success, in which case the lock is held in place. - * Note: This function is intended to be called by tree_open(), since - * tree_close() is the only way to release the obtained locks if we returned - * with E_OK. The supplied list_t should have been initialized using - * DLIST_INIT(). - * Note that we do not support alternate LockDir option in CVSROOT/config, - * we assume that the repositories do not specify an alternate value. - * Otherwise we would be limited to copying complete repositories (we could - * not handle normal directories which may be part of a repository). Moreover, - * we would permit the maintainer of the repository to cause us to write - * anywhere. They are thus advised to not specify an alternate LockDir unless - * they are to expect occasionnal inconsistancies in the mirror (if they are - * commiting changes at the same time the copying process occurs). - */ -static int dir_open(list_t *dir, const char *dirpath, bool locking) -{ - DIR *dptr; - - /* Only is allowed to be a symbolic link, for convenience. */ - if ((dptr = opendir(dirpath)) != NULL) { - struct dirent *eptr; - struct dir_node *dnode; - char buf[PATH_MAX]; - - if (locking) { - int fd, error; - - /* Attempt to obtain a CVS read lock. Note that mkdir(2) is - * atomic and that this is the official CVS way. - */ - (void) snprintf(buf, PATH_MAX - 1, "%s/#cvs.lock", dirpath); - if (mkdir(buf, 0755) == -1) { - /* Failed to obtain the master lock */ - switch (errno) { - case EEXIST: - error = E_LOCKED; - break; - case EACCES: - /* FALLTHROUGH */ - case EROFS: - error = E_PERM; - break; - default: - error = E_WRITE; - break; - } - (void) closedir(dptr); - dir_close(dir); - return error; - } - (void) snprintf(buf, PATH_MAX - 1, "%s/#cvs.rfl.%d", dirpath, - (int)pid); - if ((fd = open(buf, O_CREAT | O_TRUNC | O_WRONLY, 0644)) == -1) { - /* Failed to write the CVS read lock, release master lock */ - (void) snprintf(buf, PATH_MAX - 1, "%s/#cvs.locl", dirpath); - (void) rmdir(buf); - (void) closedir(dptr); - dir_close(dir); - return E_PERM; - } - (void) close(fd); - } - - /* We could obtain our CVS read lock, keep it and proceed. */ - while ((eptr = readdir(dptr)) != NULL) { - struct stat st; - int flags; - - flags = 0; - /* Perform sanity checking on file, and make sure to verify for - * active write access locks, if any. - */ - if (*eptr->d_name == '.' || - mm_strncmp(eptr->d_name, "CVS", 3) == 0) - continue; - (void) snprintf(buf, PATH_MAX - 1, "%s/%s", dirpath, eptr->d_name); - if (lstat(buf, &st) == -1) { - /* Release everything cleanly */ - (void) fprintf(stderr, "dir_open(%s) - lstat(%s) - (%s)", - dirpath, buf, strerror(errno)); - (void) closedir(dptr); - dir_close(dir); - if (locking) { - (void) snprintf(buf, PATH_MAX - 1, "%s/#cvs.rfl.%d", - dirpath, (int)pid); - (void) unlink(buf); - (void) snprintf(buf, PATH_MAX - 1, "%s/#cvs.lock", dirpath); - (void) rmdir(buf); - } - return E_NOTFOUND; - } - if (S_ISREG(st.st_mode)) { - size_t len; - const char *path; - - /* Officially a regular file, eliminate invalid ones. */ - if (*eptr->d_name == '#') - continue; - if (st.st_size > ssize_max) { - (void) fprintf(stderr, "dir_open() - Ignoring '%s' " - "(abnormally large file)\n", - buf); - continue; - } - path = eptr->d_name; - len = mm_strlen(path); - if (len < 3 || - (path[len - 1] != 'v' && path[len - 2] != ',')) { - (void) fprintf(stderr, "dir_open() - Ignoring '%s' " - "(not a valid RCS filename)\n", - buf); - continue; - } - if (CONF.KEEP_EXEC) { - /* If we are allowed to set the executable bit on backups, - * we have to set the DNF_EXECUTABLE flag bit - */ - if ((st.st_mode & ~S_IFMT) & (S_IXUSR | S_IXGRP | S_IXOTH)) - flags |= DNF_EXECUTABLE; - } - } else if (S_ISDIR(st.st_mode)) { - /* Definitely a directory. Make sure that it has a valid - * name, or eliminate it. - */ - if (*eptr->d_name == '#') - continue; - flags |= DNF_DIRECTORY; - } else { - /* A special file or symbolic link. Ignore, and log. - * It could be a nice feature to use a hashtable_t with - * mmhash(3) instead of an mmlist(3) list_t and record the - * file/directory inodes, then verifying if any duplicate - * exists, revealing hard links (which can be the cause of - * recursive loops). However, we provide a recursive level - * limit, which is just as good, and it makes this section - * faster. Moreover, because of CVS-style locking, it would - * be impossible to acquire the lock twice on the same - * directory. - */ - (void) fprintf(stderr, "dir_open() - Ignoring '%s' (not " - "a regular file or directory)\n", - buf); - continue; - } - /* Passed sanity checking; add entry in list */ - if ((dnode = (struct dir_node *)pool_alloc(&dir_pool, FALSE)) - == NULL) { - /* Release everything cleanly */ - (void) fprintf(stderr, "dir_open() - Out of memory\n"); - (void) closedir(dptr); - dir_close(dir); - if (locking) { - (void) snprintf(buf, PATH_MAX - 1, "%s/#cvs.rfl.%d", - dirpath, (int)pid); - (void) unlink(buf); - (void) snprintf(buf, PATH_MAX - 1, "%s/#cvs.lock", - dirpath); - (void) rmdir(buf); - } - return E_NOMEM; - } - /* Trunkating to a size_t is safe as we eliminated too large - * files already the value may in fact fit safely into an ssize_t. - */ - dnode->size = (size_t)st.st_size; - dnode->flags = flags; - dnode->namelen = mm_strncpy(dnode->name, buf, PATH_MAX - 1); - DLIST_APPEND(dir, (node_t *)dnode); - } - (void) closedir(dptr); - } else - return (errno == EACCES ? E_PERM : E_NOTFOUND); - - return E_OK; -} - - -/* Frees all directory entries which dir_open() possibly recorded in the - * specified list_t. Requires the dir_pool pool_t to previously have been - * initialized. Safe to use on empty lists, as long as they at least have - * previously been initialized once using DLIST_INIT(). - */ -static void dir_close(list_t *dir) -{ - struct dir_node *dnode, *tmp; - - /* We don't just use DLIST_FOREACH() since we also are freeing nodes. - * If we didn't, we simply would need to DLIST_INIT(), actually. However, - * we want allocating and freeing of directory file contents to be - * reentrant so that it may safely be used recursively, while shareing - * the same global dir_pool pool_t for efficient memory management and - * minimizing calls to malloc(3)/free(3). - */ - for (dnode = DLIST_TOP(dir); dnode != NULL; dnode = tmp) { - tmp = DLIST_NEXT(dnode); - (void) pool_free((pnode_t *)dnode); - } - DLIST_INIT(dir); -} - - -/* Wrapper for tree_open_r() */ -static int tree_open(list_t *tree, const char *dirpath, bool locking) -{ - recursion_level = 0; - DLIST_INIT(tree); - return tree_open_r(tree, dirpath, locking); -} - - -/* Attempts to recursively lock, in a CVS friendly way, every directory within - * the specified , and obtain the file list of all the tree. If this - * fails, tree_close() is automatically called. On error, one of (E_LOCKED, - * E_NOMEM, E_NOTFOUND, E_PERM, E_RLEVEL) is returned (and everything freed). - * Returns E_OK on success, in which case all directories are locked, and - * where tree_copy() may be used before calling tree_close(). We ensure to not - * exceed CONF.MAX_RECURSION recursive levels. The supplied list_t - * pointer should have been initialized using DLIST_INIT(). The tree_pool - * pool_t should also have previously been initialized. Make sure to set the - * global variable recursion_level to 0 before calling this function. - */ -static int tree_open_r(list_t *tree, const char *dirpath, bool locking) -{ - list_t dir; - int error; - - DLIST_INIT(&dir); - if ((error = dir_open(&dir, dirpath, locking)) == E_OK) { - struct tree_node *tnode; - - if ((tnode = (struct tree_node *)pool_alloc(&tree_pool, FALSE)) - != NULL) { - struct dir_node *dnode; - - /* Append new tree node, from now on we are allowed to simply call - * tree_close() to safely release all acquired CVS locks. - */ - tnode->namelen = mm_strncpy(tnode->name, dirpath, PATH_MAX - 1); - mm_memcpy(&tnode->dir, &dir, sizeof(list_t)); - DLIST_APPEND(tree, (node_t *)tnode); - /* Recurse as necessary (and allowed) */ - DLIST_FOREACH(&dir, dnode) { - if ((dnode->flags & DNF_DIRECTORY) != 0) { - if (++recursion_level > CONF.MAX_RECURSION) { - tree_close(tree, locking); - return E_RLEVEL; - } - if ((error = tree_open_r(tree, dnode->name, locking)) - != E_OK) { - tree_close(tree, locking); - return error; - } - recursion_level--; - } - } - } else { - /* We could not record the entry for tree_close() yet, make sure - * to clear the CVS locks if needed for this dir_open(). - */ - if (locking) { - char buf[PATH_MAX]; - - (void) snprintf(buf, PATH_MAX - 1, "%s/#cvs.rfl.%d", - dirpath, (int)pid); - (void) unlink(buf); - (void) snprintf(buf, PATH_MAX - 1, "%s/#cvs.lock", dirpath); - (void) rmdir(buf); - } - dir_close(&dir); - tree_close(tree, locking); - return E_NOMEM; - } - } else { - dir_close(&dir); - return error; - } - - return E_OK; -} - - -/* Releases all successfully locked directories in the specified tree, as well - * as all memory resources in use to cache it's filenames. The tree must - * previously have been loaded using tree_open(). The tree_pool pool_t must - * previously have been initialized. - */ -static void tree_close(list_t *tree, bool locking) -{ - struct tree_node *tnode, *tmp; - char buf[PATH_MAX]; - - /* Can't use DLIST_FOREACH() as we're also freeing nodes */ - for (tnode = DLIST_TOP(tree); tnode != NULL; tnode = tmp) { - tmp = DLIST_NEXT(tnode); - dir_close(&tnode->dir); - if (locking) { - (void) snprintf(buf, PATH_MAX - 1, "%s/#cvs.rfl.%d", tnode->name, - (int)pid); - (void) unlink(buf); - (void) snprintf(buf, PATH_MAX - 1, "%s/#cvs.lock", tnode->name); - (void) rmdir(buf); - } - (void) pool_free((pnode_t *)tnode); - } - DLIST_INIT(tree); -} - - -/* Copies the contents of to , performing CVS locking - * if CONF.CVS_LOCKING is enabled. Waits as required in bunches of - * CONF.CVS_LOCK_DELAY seconds, upto a maximum of CONF.CVS_LOCK_TIMEOUT - * seconds if necessary to obtain the read lock in a CVS-friendly manner. - * Returns TRUE on success, or FALSE on error (which may leave a partial - * tree copy dangling, this has to be handled by the caller). - * Because this function is the one which may leave dangling lock files - * if broken, we use gtree for "global tree" instead of a local tree variable - * here, so that our SIGTERM handler may call tree_close() on it. - */ -static bool tree_copy(const char *dstpath, const char *srcpath) -{ - int error; - long seconds = 0; - size_t srcpathlen = mm_strlen(srcpath); - bool cvs_locking = CONF.CVS_LOCKING; - bool displayed = FALSE; - - for (;;) { - if ((error = tree_open(>ree, srcpath, cvs_locking)) == E_LOCKED) { - if (seconds < CONF.CVS_LOCK_TIMEOUT) { - unsigned int s; - - if (!displayed) { - (void) fprintf(stderr, "Retrying for '%s' every %ld " - "seconds, for a maximum of %ld seconds, " - "because a CVS lock exists\n", - srcpath, CONF.CVS_LOCK_DELAY, - CONF.CVS_LOCK_TIMEOUT); - (void) fflush(stderr); - displayed = TRUE; - } - s = (unsigned int)CONF.CVS_LOCK_DELAY; - while (s != 0) - s = sleep(s); - seconds += CONF.CVS_LOCK_DELAY; - continue; - } else { - (void) fprintf(stderr, "Exceeded maximum delay, proceeding " - "anyways for '%s' despite the existing CVS " - "lock\n", - srcpath); - (void) fflush(stderr); - cvs_locking = FALSE; - continue; - } - } else if (error == E_OK) { - struct tree_node *tnode; - - DLIST_FOREACH(>ree, tnode) { - struct dir_node *dnode; - - DLIST_FOREACH(&tnode->dir, dnode) { - if (!file_copy(dnode, dstpath, srcpathlen)) { - tree_close(>ree, cvs_locking); - return FALSE; - } - } - } - tree_close(>ree, cvs_locking); - break; - } else { - (void) fprintf(stderr, "tree_copy() - tree_open(%s) - (%s)\n", - srcpath, STRERROR(error)); - return FALSE; - } - } - - return TRUE; -} - - -/* Copies the file specified by the dir_node entry, assumed to be - * an absolute pathname starting with characters, into the - * specified . It can also create a directory instead if the entry - * consists of one. It ensures to set safe creation permission modes. - * The way we transform the absolute pathname to migrate from , - * which was the directory supplied at tree_open(), to , is as - * follows: - * - is used to know how many characters to strip from - * dnode->name. This pathname never has a trailing '/', but always starts - * with one. - * - A buffer is created with the new destination consisting of - * followed by the remaining characters of dnode->name. will - * consist of an absolute pathname to a temporary directory. - * never has a trailing '/', but always starts with one. - */ -static bool file_copy(struct dir_node *dnode, const char *destroot, - size_t origrootlen) -{ - char buf[PATH_MAX]; - bool ok = FALSE; - - (void) snprintf(buf, PATH_MAX - 1, "%s%s", destroot, - &dnode->name[(int)origrootlen]); - if ((dnode->flags & DNF_DIRECTORY) != 0) { - /* Directory entry, create it */ - if (mkdir(buf, 0755) == -1) { - (void) fprintf(stderr, "file_copy() - mkdir(%s) - (%s)\n", - buf, strerror(errno)); - } else - ok = TRUE; - if (!mm_chgrp(buf)) - ok = FALSE; - } else { - int sfd, dfd; - - /* File entry, copy it */ - if ((sfd = open(dnode->name, O_RDONLY)) != -1) { - if ((dfd = open(buf, O_CREAT | O_WRONLY, - (((dnode->flags & DNF_EXECUTABLE) != 0) ? - 0744 : 0644))) - != -1) { - ssize_t len; - - ok = TRUE; - while ((len = read(sfd, copybuf, COPYBUF_SIZE)) != 0) { - if (len == -1) { - (void) fprintf(stderr, "file_copy() - read(%s) - (%s)", - dnode->name, strerror(errno)); - ok = FALSE; - break; - } - if (write(dfd, copybuf, len) != len) { - (void) fprintf(stderr, - "file_copy() - write(%s) - (%s)", - buf, strerror(errno)); - ok = FALSE; - break; - } - } - if (close(dfd) == -1) { - (void) fprintf(stderr, "file_copy() - close(%s) - (%s)", - buf, strerror(errno)); - ok = FALSE; - } - } - (void) close(sfd); - } - } - - return ok; -} - - -/* Attempts to recursively delete the specified directory along with it's - * contents. It relies on the same tree_open() function which deals with the - * only files which should ever be created in our temporary directories. This - * is also subject to the CONF.MAX_RECURSION limit for more safety. As we - * created the temporary directory with the same system, this should work - * fine, while being safer than a standard recursive delete operation. We - * obviously do not use CVS locking here, however. This is pretty safe as it - * will only accept to delete "*,v" RCS files and will not follow symbolic - * links. It always uses the fullpath for unlink(2)/rmdir(2). - * Only used to delete the temporary directory when the mirror operation - * fails for a reason or another. Normally, a single rename(2) operation - * replaces the old cvsroot by the temporary directory instead. - */ -static bool tree_delete(const char *dirpath) -{ - list_t tree; - int error; - struct tree_node *tnode; - struct dir_node *dnode; - long oldcnt, newcnt; - - if ((error = tree_open(&tree, dirpath, FALSE)) != E_OK) { - (void) fprintf(stderr, "tree_delete() - tree_open(%s) - (%s)", - dirpath, STRERROR(error)); - return FALSE; - } - - /* First delete all files, but also count directories. If an error - * occurs, there is no point in continueing, just log and stop then. - */ - oldcnt = 0; - newcnt = -1; - DLIST_FOREACH(&tree, tnode) { - DLIST_FOREACH(&tnode->dir, dnode) { - if ((dnode->flags & DNF_DIRECTORY) == 0) { - if (unlink(dnode->name) == -1) { - (void) fprintf(stderr, - "tree_delete() - unlink(%s) - (%s)\n", - dnode->name, strerror(errno)); - goto end; - } - } else - oldcnt++; - } - } - - /* And now delete all directories. To avoid needing to sort a tree, - * we only re-iterate if directory not empty errors occur, but - * stopping if we notice that the number of directories in the tree - * does not decrease. We stop if any other error than ENOTEMPTY occurs. - */ - for (; oldcnt > 0; oldcnt = newcnt) { - newcnt = 0; - DLIST_FOREACH(&tree, tnode) { - DLIST_FOREACH(&tnode->dir, dnode) { - if ((dnode->flags & DNF_DIRECTORY) != 0) { - newcnt++; - if (rmdir(dnode->name) == 0) { - newcnt--; - dnode->flags &= ~DNF_DIRECTORY; - } else { - if (errno != ENOTEMPTY) { - (void) fprintf(stderr, - "tree_delete() - rmdir(%s) - (%s)\n", - dnode->name, strerror(errno)); - goto end; - } - } - } - } - } - if (newcnt == oldcnt) { - /* There obviously is a problem here, stop iterating. */ - break; - } - } - -end: - tree_close(&tree, FALSE); - - return (newcnt == 0 ? TRUE : FALSE); -} - - -/* Used to ensure that the group of the created readonly repository files - * be set to CONF.REPGROUP. This is necessary for instance to restrict the - * writers to the val-tags file in CVSROOT/ which CVS pserver requires write - * access to (but should not be allowed to delete). - */ -static bool mm_chgrp(const char *filepath) -{ - if (chown(filepath, -1, repgroup) == -1) { - (void) fprintf(stderr, "mm_chgrp() - chown(%s, -1, %d) - (%s)\n", - filepath, repgroup, strerror(errno)); - return FALSE; - } - - return TRUE; -} diff --git a/mmsoftware/mmanoncvs/mmanoncvs.conf.5 b/mmsoftware/mmanoncvs/mmanoncvs.conf.5 deleted file mode 100644 index 0e68739..0000000 --- a/mmsoftware/mmanoncvs/mmanoncvs.conf.5 +++ /dev/null @@ -1,293 +0,0 @@ -.\" $Id: mmanoncvs.conf.5,v 1.6 2004/04/30 00:01:18 mmondor Exp $ -.\" -.\" Copyright (C) 2003-2004, Matthew Mondor -.\" All rights reserved. -.\" -.\" Redistribution and use in source and binary forms, with or without -.\" modification, are permitted provided that the following conditions -.\" are met: -.\" 1. Redistributions of source code must retain the above copyright -.\" notice, this list of conditions and the following disclaimer. -.\" 2. Redistributions in binary form must reproduce the above copyright -.\" notice, this list of conditions and the following disclaimer in the -.\" documentation and/or other materials provided with the distribution. -.\" 3. All advertising materials mentioning features or use of this software -.\" must display the following acknowledgement: -.\" This product includes software written by Matthew Mondor. -.\" 4. The name of Matthew Mondor may not be used to endorse or promote -.\" products derived from this software without specific prior written -.\" permission. -.\" 5. Redistribution of source code may not be released under the terms of -.\" any GNU Public License derivate. -.\" -.\" THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR -.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES -.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. -.\" IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, -.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -.\" BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF -.\" USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF -.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -.\" -.Dd November 9, 2003 -.Dt MMANONCVS.CONF 5 -.Os mmsoftware -.Sh NAME -.Nm mmanoncvs.conf -.Nd -.Xr mmanoncvs.conf 5 -configuration file for -.Xr mmanoncvs 8 -.Sh DESCRIPTION -The -.Nm /usr/local/etc/mmanoncvs.conf -file may contain one or more keyword/value pairs per line, empty lines or -comments. A ';' or '#' character causes all other characters to be considered -as a comment, and to be ignored, until the end of the current line. It is -very important to enclose the value for a keyword in double quotes ('"' -characters) if the following characters are found in it: ';', '#', space and -tab. It is allowed to use an equal sign '=' between the keyword name and it's -argument. Here are documented the various possible keywords and their -allowed values, as well as their defaults. -.Ss Privilege revokation parameters -.Bl -tag -width indent -offset indent -.It Nm DROP_PRIVS Ar "boolean" -This is most useful when -.Xr mmanoncvs 8 -is launched by the superuser. This is often the case when launched by the -system cron event scheduler for instance. Should be TRUE or FALSE. Set to -FALSE if the program is launched by an unprivileged user. -.It Nm USER Ar "user" -Only taken in consideration if -.Nm DROP_PRIVS -is TRUE. Specifies the user which should be revoked superuser privileges to. -.It Nm GROUPS Ar "group,..." -Also only of interest if -.Nm DROP_PRIVS -is TRUE, specifies a comma-separated list of groups which we should be part of -after revoking privileges. -.It Nm REPGROUP Ar "group" -Always taken into consideration, this specifies the group which should be set -to all created files in the destination repository. Obviously, if not running -as the superuser, this group also should be specified in -.Nm GROUPS -if -.Nm DROP_PRIVS -is TRUE, or the unprivileged user launching -.Xr mmanoncvs 8 -should be part of that group. Otherwise, permission denied errors will occur. -.El -.Ss Lock related parameters -.Bl -tag -width indent -offset indent -.It Nm LOCK_PATH Ar "fullpath" -Specifies the full path to a file to be used to prevent self-recursion. -.Xr mmanoncvs 8 -refuses to run if this lock is held by an already executing instance. -.It Nm ALOCK_LOCKING Ar "boolean" -If TRUE, the program will attempt to use -.Xr flock 2 -on the -.Nm ALOCK_PATH -file (see below). This is provided so that if FALSE is used, this program -can run standalone (without requireing -.Xr mmspawnd 8 ) . -Enabling concurrency locking and using -.Xr mmspawnd 8 -is highly recommended. -.It Nm ALOCK_PATH Ar "fullpath" -This is for -.Xr mmanoncvs 8 -to work in conjunction with -.Xr mmspawnd 8 -for live setups with concurrency. When updating the destination anoncvs -pserver repository, locking this file using -.Xr flock 2 -permits to make the operation atomic to pserver users. This way we prevent -inconsistent CVS repository checkouts/updates. -.It Nm CVS_LOCKING Ar "boolean" -If TRUE, CVS-friendly locking will be performed on the source repositories -when doing the mirror, so that concurrency with existing CVS commands on -the read/write repository is safe while running -.Xr mmanoncvs 8 -to create/update the read-only pserver repository. Use of this option is -recommended to prevent the pserver repository from being inconsistent. -With this, a source repository maintainer may commit change safely, and -the backup will occur after it completed the commit, for instance, and -then prevent commits until the backup is complete. -.Pp -Although using this option is encouraged, there are two things to take -into consideration. First, The -.Xr mmanoncvs 8 -program must run with the appropriate permissions to be able to write -to the source repositories to create the temporary directory and file -locks. There are various ways to achieve this, either running it as -the superuser or as an unprivileged user. -.Pp -The second fact to consider is that source repositories should not set an -alternate LockDir option in their CVSROOT/config file (which would cause the -locks to not be able to compete with eachother, relying on different -locations). Because -.Xr mmanoncvs 8 -works with CVS repository directories rather than actual CVS repositories, -allowing it to be more powerful about what to merge together into the -destination pserver repository, it cannot track which CVSROOT directory -belongs to which source directory to be able to also honor alternate LockDir. -Fortunately, this LockDir option is very seldom used, and the CVS locks -are always created in the repository directories by default. -.It Nm CVS_LOCK_DELAY Ar "seconds" -When -.Nm CVS_LOCKING -is enabled, this specifies the number of seconds to wait until re-attempting, -if the source repository is currently locked. The way CVS locking works is as -follows: An attempt to obtain locks on all involved directories recursively -is made. If a lock exists in any of those directories, we must release all -successfully acquired locks to prevent deadlocks. We then wait -.Nm CVS_LOCK_DELAY -seconds, and retry, upto a maximum of -.Nm CVS_LOCK_TIMEOUT , -at which point the offending lock(s) are considered to be stalled locks and -normal operations resume. -.Pp -See the CVS documentation for more information, in the concurrency and locking -sections, and about #cvs.lock master lock directories and #cvs.rfl.* read -locks files. -.It Nm CVS_LOCK_TIMEOUT Ar "seconds" -When -.Nm CVS_LOCKING -is enabled, this specifies the maximum allowed number of seconds to wait on -a existing lock in a source repository before considering the lock(s) stalled, -when we should be resuming the backup anyways. -.El -.Ss Directory related parameters -.Bl -tag -width indent -offset indent -.It Nm REPDIR Ar "directory" -Should specify an absolute directory pathname to the directory. All destination -directories specified in -.Xr mmanoncvs.list 5 -will be inside this directory. This typically is "/var/anoncvs/cvsroot", for -instance. It is important that -.Xr mmanoncvs 8 -be able to write to this directory. It in fact should be owned by the user -under which it will run. Moreover, the directory in which -.Nm REPDIR -is located should also be writable. See the -.Xr mmanoncvs 8 -manual page section about -.Xr chroot 2 -setup with -.Xr mmspawnd 8 -for more details. -Note that a CVSROOT directory is automatically created with the proper -configuration for -.Xr mmspawnd 8 -and -.Xr cvs 1 -pserver in that directory when a successful mirror is done. -.It Nm TMPDIR Ar "directory" -An absolute directory pathname to a temporary directory on the same filesystem -as -.Xr REPDIR , -used to store temporary directories and files when performing a mirror of -the CVS repositories. This directory should be writable by -.Xr mmanoncvs 8 -as well. -.El -.Ss Other backup control parameters -.Bl -tag -width indent -offset indent -.It Nm LISTFILE Ar "fullpath" -An absolute pathname to a file which should be in the -.Xr mmanoncvs.list 5 -format. This file will be read to tell what to copy and where. Please -see the corresponding manual page for more details. -.It Nm MAX_RECURSION Ar "number" -Specifies the maximum number of directory levels allowed for recursive -copy of source directories specified in -.Nm LISTFILE . -This should be high enough to allow normal operation, but low enough to -prevent hugging the system for a long while with high CPU load caused by -malicious directory hard links in the source repository. -.It Nm MAX_FILESIZE Ar "number" -Specifies the maximum allowed files to copy, in bytes. Files which exceed -this value are ignored (and a message is logged). -.It Nm KEEP_EXEC Ar "boolean" -If TRUE, this parameter specifies that the executable bit on RCS files within -a CVS repository to be copied should be preserved in the destination. This is -most useful because of shell building and configuration scripts which are often -found in projects, such as -.Nm build.sh -or -.Nm configure . -Note that if this feature is enabled, only the owner of the file will be -set the executable bit (which consists of the user running -.Xr mmanoncvs 8 -and owning the -.Nm /cvsroot -directory into the anonymous pserver root directory). -.Pp -This prevents the commands ran into the chroot setup, such as -.Xr cvs 1 -executable, or other users which have read access to the backup tree, from -being able to execute those files. This way it is safe to preserve the -executable bit of arbitrary files without causing security issues. -However, it is the responsibility of the administrator to ensure that the -user running -.Xr mmanoncvs 8 -consists of the superuser or of a dedicated user without shell access, and -that -.Xr mmspawnd 8 -is set to run under a different unprivileged user than that owning the -.Nm /cvsroot -directory, as usual. -.El -.Sh DEFAULTS -The following defaults are used: -.Pp -.Bd -literal -offset indent -DROP_PRIVS FALSE -USER anoncvsa -GROUPS anoncvs,users -REPGROUP anoncvs - -LOCK_PATH /tmp/mmanoncvs.lock -ALOCK_LOCKING TRUE -ALOCK_PATH /var/run/mmspawnd-anoncvs.alock -CVS_LOCKING TRUE -CVS_LOCK_DELAY 30 -CVS_LOCK_TIMEOUT 300 - -REPDIR /var/anoncvs/cvsroot -TMPDIR /var/anoncvs/tmp - -LISTFILE /usr/local/etc/mmanoncvs.list -MAX_RECURSION 20 -MAX_FILESIZE 2147483647 -KEEP_EXEC TRUE -.Ed -.Sh AUTHOR -.Nm mmanoncvs -was written by Matthew Mondor, and is -Copyright (c) 2003-2004, Matthew Mondor, All rights reserved. -.Sh FILES -.Bl -tag -width indent -offset indent -.It Pa /usr/local/etc/mmanoncvs.conf -This file -.It Pa /usr/local/etc/mmanoncvs.list -The -.Xr mmanoncvs.list 5 -configuration file -.It Pa /usr/local/sbin/mmanoncvs -The -.Xr mmanoncvs 8 -binary itself. -.El -.Sh SEE ALSO -.Xr mmanoncvs 8 , -.Xr mmanoncvs.list 5 , -.Xr mmspawnd 8 , -.Xr chroot 2 , -.Xr flock 2 , -.Xr cvs 1 . -.Sh BUGS -Please report any bug to mmondor@accela.net diff --git a/mmsoftware/mmanoncvs/mmanoncvs.list.5 b/mmsoftware/mmanoncvs/mmanoncvs.list.5 deleted file mode 100644 index 0d67e49..0000000 --- a/mmsoftware/mmanoncvs/mmanoncvs.list.5 +++ /dev/null @@ -1,130 +0,0 @@ -.\" $Id: mmanoncvs.list.5,v 1.5 2004/04/30 00:01:18 mmondor Exp $ -.\" -.\" Copyright (C) 2003-2004, Matthew Mondor -.\" All rights reserved. -.\" -.\" Redistribution and use in source and binary forms, with or without -.\" modification, are permitted provided that the following conditions -.\" are met: -.\" 1. Redistributions of source code must retain the above copyright -.\" notice, this list of conditions and the following disclaimer. -.\" 2. Redistributions in binary form must reproduce the above copyright -.\" notice, this list of conditions and the following disclaimer in the -.\" documentation and/or other materials provided with the distribution. -.\" 3. All advertising materials mentioning features or use of this software -.\" must display the following acknowledgement: -.\" This product includes software written by Matthew Mondor. -.\" 4. The name of Matthew Mondor may not be used to endorse or promote -.\" products derived from this software without specific prior written -.\" permission. -.\" 5. Redistribution of source code may not be released under the terms of -.\" any GNU Public License derivate. -.\" -.\" THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR -.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES -.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. -.\" IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, -.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -.\" BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF -.\" USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF -.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -.\" -.Dd November 9, 2003 -.Dt MMANONCVS.LIST 5 -.Os mmsoftware -.Sh NAME -.Nm mmanoncvs.list -.Nd -.Xr mmanoncvs.list 5 -configuration file for -.Xr mmanoncvs 8 . -.Sh DESCRIPTION -The -.Nm /usr/local/etc/mmanoncvs.list -file serves to describe which CVS repositories (or which directories of which -repositories) to backup into the anoncvs read-only repository, usually for -access using -.Xr mmspawnd 8 . -.Ss File format -.Pp -The format of this file is very simple. Blank lines and lines which have the -first printable character as ';' or '#' are ignored. Other lines are expected -to only contain two space or tab separated columns. Every of the two columns -may use single quotes (') as required if spaces are found in the elements. -.Pp -Each column element must consist of an absolute directory pathname, beginning -with '/'. Trailing '/', if any, are ignored, and multiple consecutive '/' into -a pathname are considered like a single one like POSIX suggests. -.Pp -The first column specifies the destination directory into -.Nm REPDIR -(See -.Xr mmanoncvs.conf 5 ) , -and the second column from which directory to recursively copy files into -the directory represented by the first column. It is valid to use "/" in the -first (destination) column, to specify that the files in the second (source) -column directory be copied recursively into -.Nm REPDIR -directly. Otherwise, the specified extra directories are created in -.Nm REPDIR , -and the files in the source directory copied in that directory. -.Pp -Each line is thus considered as a copy operation which will be performed, -sequencially, in the specified order. It is the responsibility of the -administrator to avoid directory name conflicts or duplicate copies. -.Pp -Note that files and directories starting with 'CVS' are ignored, which -allows to use any directory, be it the root to several CVS repositories -each with their CVSROOT, a single directory of a repository, or an actual -repository root directory. -.Ss Example configuration files -.Pp -Here is a first example: -.Bd -literal -offset indent -# We have many projects in /var/cvs which each have their -# directory. We want them to be accessible directly under -# /cvsroot for pserver access: -/ /var/cvs -.Ed -.Pp -And another example: -.Bd -literal -offset indent -# Lucca's projects -/users/lucca /home/admin/lucca/cvsroot - -# Matt's -/users/mmondor /home/users/mmondor/cvsroot - -# Rohit's -/users/rohit /home/users/rohit/cvsroot - -# Common projects -/common /var/cvs -.Ed -.Sh DEFAULTS -None -.Sh AUTHOR -.Nm mmanoncvs -was written by Matthew Mondor, and is -Copyright (c) 2003-2004, Matthew Mondor, All rights reserved. -.Sh FILES -.Bl -tag -width indent -offset indent -.It Pa /usr/local/etc/mmanoncvs.list -This file -.It Pa /usr/local/etc/mmanoncvs.conf -.Xr mmanoncvs.conf 5 -file for -.Xr mmanoncvs 8 -.It Pa /usr/local/sbin/mmanoncvs -The -.Xr mmanoncvs 8 -binary itself. -.El -.Sh SEE ALSO -.Xr mmanoncvs 8 , -.Xr mmspawnd 8 , -.Xr mmanoncvs.conf 5 . -.Sh BUGS -Please report any bug to mmondor@accela.net diff --git a/mmsoftware/mmftpd/ChangeLog b/mmsoftware/mmftpd/ChangeLog deleted file mode 100644 index 564bcef..0000000 --- a/mmsoftware/mmftpd/ChangeLog +++ /dev/null @@ -1,692 +0,0 @@ -$Id: ChangeLog,v 1.50 2005/11/17 07:38:07 mmondor Exp $ - - - -Release: mmftpd 0.0.18 devl -Date : November 17, 2005 -By : Matthew Mondor - -* New features - - Addition of PASV_REMAP wich can optionally be used in cases where the FTP - server is behind a firewall. This allows the firewall rules to simply have - to redirect port 21 as well as a specified range of high ports to the - FTP server box, in conjunction with PASV_RANGE, PASV_RANGE_MIN and - PASV_RANGE_MAX. Patterns are used to use proper address to bind(2) - passive listening connections to, as well as address to advertize via - PASV/LPSV, based on the client addresses. -* Performance enhancements - - When using mmftpd virtual user specific home directory tree quotas, - the tree had to recursively be scanned as soon as the cache entry for - that user had been expunged (which happened when no more current logins - for that user existed). Because this is a frequent event and that a new - login for an uncached user caused a recursive lookup, it could slow down - the server potentially. A new longer-term cache is now being maintained - with a timestamp which allows to determine if the directory tree was - modified comparing the directory and cache entry timestamps. The traversal - will as a result only be performed when strictly necessary. - Although that recursive tree scan is done in a thread-safe manner via - another process, it is still always better to avoid it as possible. - - The configuration file parser was rewritten to be more efficient and - to hold cleaner code than the previous one which was migrated from - one of my first C programs many years ago :) The configuration files - format is now more flexible as well, although the current files will - parse properly without modifications still. - - mmhash(3) is now used by the command parser for more speed. Any such - speed improvement is always welcome when using userspace threads. - - mmpath(3) path_ls2() function performance was considerably enhanced - using wiser bit operations to create the human readable permissions. - - Port conversions are now using bitwise operations instead of arithmetic. - Those are done in network byte order (big endian), and BYTEORDER(3) is - used to perform the appropriate conversions for i386 and other little - endian architectures. This effects in a performance boost on big endian - architectures, and similar speed code on little endian ones. - - Now using fchown(2) rather than lchown(2) after creating a file to set - the file group as needed. - - mmpool(3) was reimplemented for better efficiency. Also was added support - for object constructor/destrutor functions. - - In situations where the system has to scale up and down often, performance - improvements should be noticed because of the implementation of - mm_pth_pool(3), taking advantage of the new mmpool(3) facilities. This - causes a pool of threads to already be available, to which client - connections can be dispatched, rather than having to launch a new thread - per client. We also use this support in the allocation of client-specific - nodes which can recycle the objects. - - mmserver(3) was enhanced as to use proper 4.2BSD setsockopt(2) to enhance - performance of sends and eliminate unnecessary TCP delays. - - In the transfer manager thread, connection establishment would require - ASCII IP address representation to internal BSD sockaddr_in. This is no - longer done since we are also storing this binary representation into the - client environment structure. -* Bug fixes - - mmstatd(8) and the mmstat(3) library had a bugfix. - - One mmstat(3) key was still using '.' characters instead of '|' - - A free(3) bug was found in mmpool(3)'s pool_free(). - - The 350 Restart message lacked a cast to convert off_t to int64_t which - caused the number of bytes printed in the result string to be unrealistic - on systems on which off_t is 32-bit (I.E. Linux). - - The old string library which had functions copied as-is from my old code - of the 80's was througly revised. Many functions were either rewritten or - modified for better style and performance. Moreover, all library functions - have been throughly tested using a program especially made to test them. - A bug in mm_memcmp() and one in mm_memmove() were fixed along the way. - - CONNECTION_PERIOD was allowed to be 0, which caused the server to take - alot of CPU time. It can now only start at 1 (larger values recommended). -* Other - - The key names used for statistics via mmstat(3) were refined for better - consistency. - - BSD-style mdoc manual pages were enhanced. - - The mmlimitrate(3) library was written to restrict the amount of code - duplication among my various software which require rate limiting in - various situations. The software now uses this API for rate throttling - (except the bandwidth shaping which is handled by mmfd(3)). - - mmpath(3) was audited, cleaned up, and a few minor bugs were fixed. - - mmftpdpasswd(5) file parsing is now converting the numbers more - appropriately according to the expected internal format. - - - -Release: mmftpd 0.0.17 devl -Date : July 1, 2003 -By : Matthew Mondor - -* Bug fixes - - The IP-address based connection rate and DNS cache expire thread used to - sometimes waste CPU cycles looping more often that it should, sleeping for - 0 seconds between loops. Fixed. - - mmstatd(8) had a bugfix and was upgraded to 0.0.8 - - If the specified port range for passive data connections was small and - that alot of transfers would currently being served, it was possible to - not be able to bind(2) a new socket. This is less likely now that mmftpd - uses 4.2BSD setsockopt(2) to set the SO_REUSEADDR flag on the socket. - The new system will also wait up to a maximum of 30 seconds while - performing up to 64 retries per second on unsuccessful bind(2) attempts, - running among ports within the allowed range. The previous system would - simply always fail after 64 tries. - - Because mmftpd used to enclose alot of mmstat() operations into a - transaction using mmstat_transact() it was possible to have problems - sending the request to the mmstatd(8) server on some systems where the - default AF_UNIX DGRAM socket buffer was too small. In such circumstances - the "connection" to the server would be reset and re-established, which - in turn could cause problems if mmftpd was set to chroot(2) at startup - and the mmstatd(8) socket was no longer available from within the jail. - 4.2BSD setsockopt(2) is now used by mmstat(3) and mmstatd(8) to solve this - issue. -* Important - - The mmstat(3) key names were modified to be '|' separated rather than - '.' separated. Although I have been trying to avoid such a change which - obviously requires fixing alot of scripts I am using, it was proven with - time that the '.' character was too widely used and that '|' was ideal - as a replacement. It is not hard to use a script to use the mmstat(8) - reset command and convert all old entries to the new type if necessary. - As a result, filenames which comport '.' characters for which counters - are maintained are much better to handle and to isolate. The same applies - to IP addresses. - - Per-user mmstat statistics are now formated as mmftpd|user||* - instead of mmftpd..* so that it is easier to isolate the actual - user statistics from the total ones from scripts. - - When per-file download statistics are wanted, the new mmstat key format - becomes mmftpd|user||GET| instead of the previous - mmftpd.. - - The maximum key name length now was bumped from 128 to 256 bytes. As - a result, a special log file synchronization will be required as was - the case for mmstatd 0.0.7 upgrade (and mmftpd 0.0.16 upgrade). This also - requires mmstat clients using the mmstat(3) API to be recompiled. - - - -Release: mmftpd 0.0.16 devl -Date : June 19, 2003 -By : Matthew Mondor - -* Important - - Important changes to mmstat(3) and mmstatd(8) were made which require a - database synchronization procedure to be performed before upgrading. - See mmstatd/ChangeLog for details. - - The GROUPS directive in mmftpd.conf(5) and mmstatd.conf(5) now expect - groups to be comma-separated instead of space-separated. The quotes then - of course also become optional if multiple groups are specified. This was - made because mmreadcfg(3) library is also used by other of my projects for - which comma-separated groups were also required. -* Miscelaneous new features - - When user has no rights to modify or upload (i.e. usually anonymous user) - all files now appear as read-only and non-executable, and directories - as read-only, when using LIST. - - mmftpd now reports the number of bytes transfered in both directions for - each user via the mmstat(3) facility. - - Via the mmstat(3) facility can also be optionally recorded various - failures such as bad logins, permission denied errors, illegal PORT, etc. - - The new configuration file keywords PASV_RANGE, PASV_RANGE_MIN and - PASV_RANGE_MAX were added which permit to limit mmftpd to use a specified - port range for the PASV/LPSV/EPSV commands. This can also be used to - randomize the port number used within the normal allowed high port range - rather than letting the system automatically choose a free port. -* Bug fixes - - Fixed ALIGN_CEIL alignment macros which used to always add bytes even - if the value was already aligned properly. A few bytes were often wasted - as the result of this. - - The STAT command would not properly format the off_t type on systems such - as Linux/i386 on which off_t is 32-bit. This was fixed by using type - casting to the expected 64-bit type. - - On systems using glibc (such as Linux), the headerfile has to - be explicitely included to use the crypt(3) function. - - There is an issue where some versions of glibc do not define nfds_t - in . mmlib/mmfd.h was introduced a basic check for this, which - may be uncommented if this occurs. -* Other - - The FIFO library performance was improved and mmftpd now allocates FIFO - buffers more efficiently using a dynamic pool, it used to be one of - rare occations where malloc()/free() still had to be called at connection - startup and disconnection. These FIFOs are used to remember which - directories were visited recently to not flood the user with README file - contents, when enabled. - - Interface of various lower layer functions and macros were improved, and - mmftpd was migrated to use the new ones. - - Where appropriate and possible, const keyword was added to the function - parameters and global data types. - - Some more source cleanups - - mmstatd will now perform much better when used with a large number of - keys, and will now retain the real time of the operation if it had to - be killed before it can incorporate the recovery logs and sync. - - Now uses mmhash(3) for user lookup which should make mmftpd faster when - many simultaneous users are logged in at the same time (this is used to - verify user login limits and link a new user to the shared tree quota - structure). - - mmhash(3) is now used by mmserver(3) as well, which should enhance - connection validity and DNS hostname cache performance. - - - -Release: mmftpd 0.0.15 devl -Date : January 9, 2003 -By : Matthew Mondor - -* Important change - - When desired, it is important for frontend scripts to be able to generate - required password hashes. This has not been easy with the previous method, - which did not even use standard base64 encoding but a custom one on the - MD5 result. Calling external binaries such as mmpasswd is not ideal. - Moreover, I wanted to get rid of the mhash library dependancy. - The solution was simple: password hashes now consist of standard - crypt(3) generated strings. Both MD5 and DES modes are supported. - However, mmpasswd will only generate MD5 ones with crypt(3). - This also allows administrators to easily migrate from real system - users, as this is the way system user password hashes are stored. - An attempt was made to provide a utility to easily convert old hashes to - new ones; This however is impossible due to a bug in the previous method - which caused the last few bytes of the MD5 results to be lost during the - base64 conversion. - A recommendation is to generate random passwords for the users, notify - them via email and then activate the new version using the new passwords - say, a week afterwards. This password format will then remain unchanged - for next versions. -* Bugfixes - - When DISPLAY_FILE was set, the file in the / directory would not be - displayed at login, only when a CWD was made to it. Fixed. - - treesize() and treesize_edit() now use off_t type which corresponds - best to a system's type size for filesystem related offsets/sizes. - On BSD systems this becomes 64-bit. - - If an invalid path was used for CWD the server would not output the - expected 550 error line and the client would freeze while waiting - for the reply. This bug was introduced when path sanity checking was - rewritten for exists() in mmftpd 0.0.12 devl. -* Real asynchroneous functions support - - The Pth library has limits in that only one process is used for all - threads. The various pth_*() functions, and special care has to be taken - to prevent locking the whole process when a single thread needs to - perform operations, because of the non-preemptive nature of Pth. - Another side effect of using Pth is that SMP systems gain no performance - over single CPU systems. Moreover, some functions which can take a while - can lock the whole process when no pth_*() wrapper function exists to - do it. An obvious example is hostname resolving, which can lock the whole - process for long periods on slow networks. - - A solution was worked out to allow threads to execute real asynchroneous - functions without blocking the main process (and therefore allowing other - threads to remain responsive), and to even take advantage of - multi-processor systems where available. - - The technique uses an AmigaOS-like device task/thread which works over the - Pth thread-safe message passing mechanism, to serve the various - asynchroneous functions like a daemon would. This device internally uses - a pool of real asynchroneous processes to which it dispatches the requests - in a distributed manner via unix datagram sockets. - - The new ASYNC_PROCESSES configuration file option was added as a result, - which allows to specify the number of slave asynchroneous processes to run. - - This facility is currently used by mmftpd to generate password hashes, - to resolve hostnames (when enabled), to evaluate user home directory - tree size (for accounts with quota limits), and to obtain a line - corresponding to a user from mmftpdpasswd. -* Miscelaneous new features - - It is now possible to set "*" for the password of various users to allow - password-less logins. These will accept any given password or empty ones. - - mmstatd can now be specified configuration file to use at startup - via command-line argument. - - mmstat library will now first check for MMSTATCONF environment variable - for the configuration file to use instead of the default - "/etc/mmstatd.conf". This can be useful to non-privileged users who want - to run mmstatd. - - If the daemon is compiled with -DNODETACH, the main process will not - fork(). Useful for debugging. - - If compiled with -DNODROPPRIVS, daemon will make sure that it is not - started by the superuser to accept running, but will then not attempt - to perform any modifications on the current permissions. Useful for - non-privileged users. - The default is to only accept to be started by the superuser and then - drop privileges. - - mmstat user client utility now supports hreport for more human readable - results (although less verbose). - - Configuration file option DELAY_ON_ERROR was added to allow to pause - at every user command error. Thanks to Jeroen Oostendorp for the idea. - - mmstat facility now allows wildcard pattern matching for UPDATE, RESET - and DELETE operations which operates atomically on all matching keys - for that uid. -* Other - - Optimized the command matching loop by using fast packed hashes - - Other optimizations were performed by moving some variables in - closer scope, usually resulting in compilers using registers for - appropriate variables. This is especially useful for GCC which - ignores register directives. - - Significant code cleanups - - Several environment variables can now be set to modify the behavior - of the install.sh script (eg: change installation prefix, default - group/user, etc). - - The memory pool allocation system was optimized even more, now keeping - statistics on the average number of pages in use, so that it scales - better. Previously used pages are now privileged as well, which causes - unix kernels to allow processes using it to perform more efficiently, - as well as the underlaying allocator. - - Better synchronization with mmstat service, now using persistant keys - and has possibility to delete old temporary keys at startup using - wildcard matching. - - - -Release: mmftpd 0.0.14 devl -Date : November 8, 2002 -By : Matthew Mondor - -* New features - - Added new CHROOT_DIR configuration parameter to optionally allow the - server to run enclosed into a chroot(2) jail. - - Added PASSWD_FILE configuration parameter to allow the administrator - to override the name of the /etc/mmftpdpasswd file by a custom one. - - When starting the server it is now possible to specify the configuration - file to read rather than /etc/mmftpd.conf. This allows to start several - copies using different configurations with the new CHROOT_DIR option. -* Bug fixes - - When multiple groups feature was introduced in 0.0.10 devl, mmftpd stopped - setting the initial group using setgid(2), assuming that setgroups(2) - would. This was not the case, and mmftpd would then remain part of the - wheel group. It will now set the real and effective primary group, the - secondary groups, and then the real and effective user. The first group - of the GROUPS configuration file parameter is used for the primary group. - Although the virtual chroot jail is safe, now that it is possible to - disable file ownership checking for some accounts, and possibly use - read-only accounts in wider areas when wanted, this needed to be fixed. - - Removed the annoying LOOP: debugging which used to flood syslog. -* Other - - Now uses getnameinfo(3) instead of gethostbyaddr(3) to resolve hostnames - if RESOLVE_HOSTS is TRUE. This is more suitable with threads, since - gethostbyaddr(3) uses static data and required a mutex to be thread-safe. - - - -Release: mmftpd 0.0.13 devl -Date : October 28, 2002 -By : Matthew Mondor - -* Important bug fix - - omission of an unlinknode() in the new rate limit feature was fixed. - The daemons could lock in a loop taking 100% CPU time. Fixed. - - - -Release: mmftpd 0.0.12 devl -Date : October 26, 2002 -By : Matthew Mondor - -* New features - - Anti-DoS connection rate limit feature was added in mmserver library, - consequently new CONNECTION_RATE and CONNECTION_PERIOD configuration - file options were added. These are on a per-IP address basis. - - A new /etc/mmftpdpasswd (see mmftpdpasswd(5) man page) column has been - added which can be used to enable/disable checking for file ownership. - This sanity checking used to be obligatory, but it may be useful to - disable it for some read-only accounts, for instance. - - LIST and NLST will now properly evaluate each file for ownership and - regularity before displaying it, no longer subject to glibc issue about - it's incomplete opendir(3) implementation. In the case where ownership - verification is performed for an account, files owned by another user - than mmftpd will silently remain hidden from the user, but will still be - logged via the syslog facility for the administrator. - - Will now accept to resume really large files on systems which allow it. - - The bytes statistic counters were bumped to 64-bit. - - Globbing/pattern matching now uses fnmatch(3) although only operating - on the last path element as it used to. As a result more complex patterns - are allowed than '*' and '?' matching. At my great satisfaction, I can - now host my netbsd package repository using mmftpd. -* Bug fixes - - Yet again a build/install script bug; Permissions would not be applied - properly to newly installed files, the problem started to occur when - automatic directory creation was added. This was now fixed. Users should - consider upgrading as fast as possible, or to make sure that their - configuration files in /etc/ have safe permissions. - See the following man page: mmftpdpasswd(5). - The install script will force safe permissions on the configuration files - while preserving them if they already exist. - - The staff group would not be created by install.sh if it did not exist. - Thanks to Jeroen Oostendorp for reporting this. - - mmpath(3) library's exists() function used to return the same 0 result - if the file/directory did not exist or if it existed but was owned by - another user than the one mmftpd ran under. This, under very particular - circumstances could have lead to unexpected behavior. exists() now returns - specific return codes for each situation, which mmftpd evaluates. - Example of a situation in which this could have been harmful: STOU needs - to make sure that a file does not exist before creating it. If a file - owned by another user existed, and was writable by the mmftpd user, it - would have been overwritten. A similar situation could have existed for - RNFR/RNTO, where a file could have been copied/moved over another one. - Of course a warning would still be issued in the logs. Normally, this - never happened as files owned by another user (if any) usually were not - writeable by others. But who knows, let's not assume anything. - See the mmpath(3) man page for the new exists() semantics. - - If a very long filename existed (of MMPATH_MAX length for instance) - LIST would not be able to display the whole filename. Fixed. - - The bandwidth shaping system was not accurate enough, as it used to - throttle by 4096 bytes blocks, requireing too much CPU time in syscalls - for high limits. It was rewritten using a new design which now only sleeps - if required after the maximum number of bytes allowed into a second were - transfered. So it now uses a second's worth of data resolution rather than - a 4096 bytes resolution. Some parts of mmfd library internals and API - were modified as a result (See mmfd(3) man page for details). Another - side effect is that it takes far less CPU time with high limits. - Thanks to Darren Price for the extensive testing and reports, - to Eric Weisenhaus and agrajag for reporting these earlier. - - There existed a potential race condition when hostname resolving was - enabled, which could cause a memory leak. This was fixed. - Thanks to Jeroen Oostendorp for reporting it. -* Other - - The mmpath(3) exists() and ls() functions were optimized to achieve better - speed. - - Binary file transfers are no longer using the buffered fdbread() and - fdbwrite() functions but rather fdbrread() and fdbrwrite() directly, - saving some CPU cycles and avoiding buffer size splits. - - Some source code auditing and cleaning was made, several optimizations - were performed, some buffers were aligned to favorize word-copy/move - operations as opposed to single-byte moves, in mmstring library). - - Finally this ftpd starts to be decent. - - I thank everyone who are testing this software, and am again requesting - that it be evaluated, as I intend to eventually release mmftpd v1 stable. - For this to happen it has to be free of any issues, it is therefore - important to report any bug, to mmondor@gobot.ca. It also would be nice - if I could release with it a list of systems it is known to work on, - so if betatesters find some time to describe their setup to me via email - it would be appreciated. I yet have to test it again on ultrasparc with - Linux and Solaris for instance. - - - -Release: mmftpd 0.0.11 devl -Date : October 11, 2002 -By : Matthew Mondor - -* Bug fixes - - Although mmftpdpasswd(5) man page specified that the mfs column was - expressed in bytes, the default etc/mmftpdpasswd and mmftpd daemon - still considered it to be in kilobytes. This was now fixed to really - use bytes for that entry. A block size could be 512 bytes, for instance. - - The build scripts did not behave as expected on systems which are using - bash for /bin/sh. Some script sections needed modifications to work - on both. Now seems to build fine on linux systems. - - Various new GCC versions warning issues were fixed. - - When installing, some directories were assumed to exist, they will - now be created when missing. - - A bug in mm_memmov() was fixed. - - - -Release: mmftpd 0.0.10 devl -Date : October 6, 2002 -By : Matthew Mondor - -* New features - - Now uses mmstatd(8) for persistant statistics storage and volatile who - database. This includes mmstat(8) administration facility. - This daemon also comports crash recovery using internal logging. - See mmstatd(8) man page for more information. - - A new column was added to mmftpdpasswd(5) to optionally allow download - statistics counters on files available under a specific user. - This is currently being used to record statistics of mmsoftware, files - are stored under mmftpd, and the HTTP frontend links to files with FTP - URLs under the mmsoftware user. - - Daemon can now be part of more than one group, which is a requirement - to access mmstatd. - - It is now possible to set global maximum download and upload limits for - the whole server. - - Improved installation/upgrade make.sh and install.sh scripts, and use - of /bin/sh rather than make because of GNU/BSD make inconsistencies. - - mmftpd(8), mmftpd.conf(5), mmftpdpasswd(5), - mmpasswd(8), mmstatd(8), mmstat(8), mmstat(3), mmstatd.conf(5), mmpath(3) - man pages were written. -* Bug fixes - - On several systems, if mmftpd's group did not match with a virtual user's - group new files would not be created with the right permissions. This - was fixed by the new feature where mmftpd process can be part of several - groups. - - Spaces were not allowed in passwords, and empty passwords were not allowed - for anonymous logins. This was fixed. - - Since mmpath library bugfix that was made in mmftpd 0.0.7, pattern - matching stopped working. valid_path() was now completely re-written - around a cleaner design and is more solid. Moreover, multiple consecutive - '/' in pathnames are now accepted and considered as a single '/', - conforming to IEEE 1003.1. - - On various systems, including OpenBSD, the NetBSD-style __RCSID and - __COPYRIGHT, although useful, caused problems. They were now replaced - by custom macros, which should fix compiling issues. - Thanks to Dinos Costanti for reporting the issue. -* Other - - The code used to be compatible with older C compilers, it now was converted - to ANSI C. Use of __P() macro is no longer made. - - Some mmlist node manipulation functions were replaced by macros. - mmlist(3) now privileges pre-allocated nodes when inserting them back - in the free nodes queue, making a better useage of memory. - - - -Release: mmftpd 0.0.9 devl -Date : July 22, 2002 -By : Matthew Mondor - -* Bug fixes - - Fixed bandwidth shaping library, recent versions would totally ignore - bandwidth rate specified, because of erroneous zero initialization. - Also fixed a bug where bandwidth was always lower than specified speed. - Thanks to Eric Weisenhaus, Darren Price and agrajag for reporting these. - - When clients send ABOR, they often send control sequences which resulted - the command to not be recognized, this was fixed by adding a sevenbit - boolean parameter to fdbgets() function. - - Configuration value LOG_LEVEL would not accept values below 1 - - Fixed strnicmp() which would sometimes cause various problems - - - -Release: mmftpd 0.0.8 devl -Date : June 4, 2002 -By : Matthew Mondor - -* Improvements - - INPUT_TIMEOUT has been replaced by CONTROL_TIMEOUT and DATA_TIMEOUT so - it is now possible to specify independant transfer i/o timeout. - - Various optimizations -* Bug fixes - - A pretty serious bug was fixed (which only affected glibc-based systems), - where syslog() would potentially be called with user supplied parts, - including fmt sequences. Thanks to Benoît Roussel for providing me with - a very detailed security advisory and to Guillaume Pelat for discovering - the problem. - - - -Release: mmftpd 0.0.7 devl -Date : May 19, 2002 -By : Matthew Mondor - -* Config file support - - Server now reads /etc/mmftpd.conf which obsoletes previous mmversion.h - pre-compilation issue. - - Defaults are set, configuration file is parsed, then command line - arguments parsed which may override some of the configuration file - settings. - - Now possible to make install without overwriting the configuration - file. - - Syslog facility can now be specified. -* Written script to ease compiling and upgrading faster. -* Introduced a 64-bit hash function - - Internally used to improve various table lookups (last visited - directories and logged in users tables) - - Hash function was tested against duplicates with /usr/share/dict/words - and find / inputs with success -* More verbose optional logging - - When level 3 logging verbosity is used, replies are now logged as well, - this is especially useful for beta-testers and debugging. -* Added various motd-like features - - WELCOME_FILE displayed at user connection if specified - - MOTD_FILE displayed at successful login if specified - - DISPLAY_FILE if specified will cause CWD into a directory to check for - specified file name, and display it, if present. -* Bug fixes - - The mmpath library contained a bug which could crash the daemon, - inapropriately using snprintf(), of which return values should not - be trusted. This did not permit remote execution of arbritrary code - from the client however, since mmfd's fdbgets() does not permit to read - binary data. - - Harmless, but still a bug, once into another CWD than /, the user could - indefinitely CWD . which would cause PWD to become for instance something - like this: /tmp/////// (upto MMPATH_MAX bytes long). Obviously any file - command would thereafter fail with a 5xx error until CWD was changed. - - The function which strips possible - parameters in commands used to - prevent using filenames containing -. It now only strips options before - any other parameters, as is standard on BSD systems. - Thanks to Lior Marantenboim for reporting this bug. -* Server too busy message introduced - - When the maximum number of IP addresses have been reached, or when too - many connections from the same address occurs, a protocol-friendly message - is now sent before closing the connections. Some clients have been - confused with previous behavior. -* Multiple interfaces support - - Adminstrator may now specify several IP addresses to bind() to, as well - as a server hostname for each, using LISTEN_IPS and SERVER_NAMES config - file parameters. -* Now using getpwnam() and getgrnam() for more configurability - - Used to read /etc/passwd and /etc/group directly. - - - -Release: mmftpd 0.0.6 devl -Date : April 22, 2002 -By : Matthew Mondor - -* Fixed a nasty bug - - As the mmfd library was lately fully redesigned some daemon code would - not properly use the status results when a connection was unexpectedly - lost. - - This rendered easy to crash the daemon flooding with alot of connections - not exiting using standard QUIT command. -* Better disconnection logging - - The the general status/reason of the disconnection is now also logged - with the statistics via syslog. -* Compilation issues resolved - - Fixed a conflict which occurred between the BSD and linux usleep() - function not using the same argument type (unsigned long vs useconds_t) - - mmpasswd would not compile properly on linux - - - -Release: mmftpd 0.0.5 devl -Date : April 17, 2002 -By : Matthew Mondor - -* Implemented bandwidth shaping support - - mmfd library was modified alot, it now supports bandwidth shaping - capabilities which mmftpd finally takes adventage of. - - Two new fields were added in the mmftpdpasswd user configuration file - for download and upload speed limits on a per-user basis. - - A new mmversion.h global parameter was added to set read and write - allowed speeds on the control connection. - - Speeds are specified in KB/s and values as low as 1 will work, but - 0 disables bandwidth shaping allowing maximum available transfer speeds. -* Implemeted transfer statistics - - Now records the number of bytes in and out of control and data connections, - as well as number of files transfers in both directions. - - Will show statistics at STAT and via syslog at logout. -* More descriptive logging on DoS attempts - - When a connection is rejected because the maximum number of IP addresses - is reached, or that maximum number of connections for an IP address - is exceeded, mmserver library now reports the offending IP address and - reason via syslog. - - - -Release: mmftpd 0.0.4 devl -Date : March 24, 2002 -By : Matthew Mondor - -* Now unified in a CVS tree with various other daemons and main libraries - - This allows a better development method, preventing inconsistencies - and thus eliminating some bug sources, ideal for security oriented - software, and following the general BSD way of coding. -* All code now distributed under the BSD license. - - I almost started to despise glibc already, and am coding almost - exclusively on NetBSD lately. Moreover this license tends to be - less restrictive. Almost all very useful glibc and linux features - were BSD derived anyways, working fine since quite a while. - - I however still intend to keep the code portable to linux -* New buffering mmfd library - - All I/O operations have been greatly improoved -* Will now be faster to restart - - TIME_WAIT lingering sockets would previously prevent mmftpd from starting - until it timeouts, fixed. - - - -Release: mmftpd 0.0.3 devl -Date : Mars 8, 2002 -By : Matthew Mondor - -* Works better with some broken clients - - Some clients send - options to various FTP commands, notably LIST, - those will now be ignored instead of returning an error message. - - More efficient and secure syslog calling on systems supporting vsyslog() - (mostly BSDs as glibc doesn't have it) - - - -Release: mmftpd 0.0.2 devl -Date : January 6, 2002 -By : Matthew Mondor - -* Wrote new linked list library - - More efficient node pre-buffering technique with allocnode()/freenode() - only calling malloc()/free() occasionally - - All of mmftpd core now uses those for efficiency - - Added a few new options in mmversion.h configuration file, related to - the page size that should be used. These values can be multiplied on - servers with high load. -* Implemented maximum login limit per user - - A new column was added in the config file -* Home directory size limit support - - Two new columns in config file, for optional maximum limit, and minimum - file size for filesystem - - User shared structure remembering current home directory limit, so that - even if there are simultanious logins of the same user quota is respected - - Tree size is only re-evaluated from scatch if no current logins of that - same user exists -* Updated the README file - - Added more details, fixed a few mistakes - - - -Release: mmftpd 0.0.1 devl -Date : December 17, 2001 -By : Matthew Mondor - -* Initial development release - diff --git a/mmsoftware/mmftpd/GNUmakefile b/mmsoftware/mmftpd/GNUmakefile deleted file mode 100644 index 22a96d9..0000000 --- a/mmsoftware/mmftpd/GNUmakefile +++ /dev/null @@ -1,30 +0,0 @@ -# $Id: GNUmakefile,v 1.4 2004/06/11 00:23:38 mmondor Exp $ - -MMLIBS := $(addprefix ../mmlib/,mmarch.o mmfd.o mmhash.o mmlimitrate.o \ -mmlog.o mmpath.o mmpool.o mmreadcfg.o mm_pth_pool.o mmserver.o mmstat.o \ -mmstr.o mmstring.o) - -PTHINCDIR := $(shell pth-config --cflags) -PTHLIBDIR := $(shell pth-config --libdir) - -OBJS := src/mmftpd.o - -CFLAGS += -Wall - - -all: src/mmftpd - -%.o: %.c - cc -c ${CFLAGS} -I. -I../mmlib $(PTHINCDIR) -o $@ $< - -src/mmftpd: $(MMLIBS) $(OBJS) - cc -o $@ -L$(PTHLIBDIR) -lc -lcrypt -lpth $(OBJS) $(MMLIBS) - -install: - install -cs -o 0 -g 0 -m 500 src/mmftpd /usr/local/sbin - install -c -o 0 -g 0 -m 444 src/mmftpd.8 /usr/local/man/man8 - install -c -o 0 -g 0 -m 444 src/mmftpd.conf.5 /usr/local/man/man5 - install -c -o 0 -g 0 -m 444 src/mmftpdpasswd.5 /usr/local/man/man5 - -clean: - rm -f src/mmftpd $(OBJS) $(MMLIBS) diff --git a/mmsoftware/mmftpd/README b/mmsoftware/mmftpd/README deleted file mode 100644 index 9e72bb7..0000000 --- a/mmsoftware/mmftpd/README +++ /dev/null @@ -1,284 +0,0 @@ -$Id: README,v 1.6 2003/10/08 19:51:00 mmondor Exp $ - -MMFTPD(8) NetBSD System Manager's Manual MMFTPD(8) - -NAME - mmftpd - FTP server with virtual users and bandwidth shaping support - -SYNOPSIS - mmftpd [config_file] - -DESCRIPTION - mmftpd probably consists of one of the most secure FTP servers available. - Secure here does not mean that encryption is supported; It mostly means - that the service is very unlikely to be used to gain higher privileges - remotely. It runs under the privileges of a normal Unix user, and never - needs to become the superuser again. Most other FTP servers generally - have to execute alot of operations as the superuser, and are only tem- - porarily dropping their privileges when a successful login has been es- - tablished. This generally consists of a bad idea for public internet ser- - vices. - - mmftpd calls setgroups(2) and setuid(2) once when it starts, and remains - unprivileged for it's whole lifetime. Moreover, the server can be en- - closed into a real chroot(2) jail if wanted, and all ftp user home direc- - tories will be stored under this new root directory (as well as - /etc/mmftpdpasswd configuration file). - - It then of course becomes up to the administrator to ensure that the user - the service runs under has no access to any unwanted resources, and that - PHP enabled HTTP virtual hosts be only accessed under SSL if high securi- - ty is wanted, since PHP has many vulnerability issues, and FTP passwords - are non-encrypted; It would be possible to login normally to upload want- - ed scripts and have access to the rest of the system. - - Each FTP user is given the impression to be running as a normal Unix user - enclosed into a chroot(2) jail. This actually consists of an illusion, as - all files mmftpd can work with must be owned by the main Unix user the - daemon runs as (typically the mmftpd user), except in the case of read- - only accounts where it is possible if wanted to use files owned by other - users. No Unix users are required for the various FTP users, only virtual - ones specified in mmftpdpasswd(5) file. The mmpath(3) library provides - the required features for path sanity checking and manipulation. The ac- - tual location of the user's home directory is never disclosed. - - For security considerations related to a virtual users service, special - files such as symbolic links are not allowed to be created, viewed or ac- - cessed via the service. Files which are not owned by the user the server - runs under will also not be accessible (unless otherwise specified). A - log entry of level 0 will be generated for any of those events. Addition- - ally, files starting with '.' are not allowed to be created or accessed. - - mmftpd is not vulnerable to glob(3) issues which often have been exploit- - ed in other FTP servers to effect Denial of Service attacks, as it pro- - vides it's own simpler globbing. - - Another interesting feature of mmftpd consists of it's bandwidth shaping - capability, without the need for special kernel support. Each user can be - set with custom maximum download and upload speeds. Additionally, the I/O - speed of the control connection can also be controlled (through which - commands are sent) and global server I/O speed limits can also be set. - The mmfd(3) library is used to provide this functionality for the mmftpd - server. - - Also can be set custom maximum home directory size or quota per user - (even safe with multiple simultaneous logins of the same user), custom - permissions to restrict the available operations, maximum number of al- - lowed logins per user, connections per IP address, maximum connection - rate per address, etc. Please see the mmftpdpasswd(5) and mmftpd.conf(5) - man pages for details. - - mmftpd uses the mmstat(3) interface to record various statistics, and to - maintain it's current who database. Please see the mmstatd(8) and - mmstat(8) man pages for information on how to use them. - - Other than that, mmftpd provides all necessary standard important FTP - commands that are expected of a generally RFC compliant FTP server. This - includes active and passive file transfers, with resume capability in - both directions. It also handles the new alternate EPSV, LPSV, EPRT and - LPRT extentions, although only support for IPv4 is currently available, - and the permissions manipulation commands (CHMOD, UMASK) under the SITE - extention command. - - Here is a list of all currently implemented commands (processed case in- - sensitively): - - Command Description - USER Specify user to login as - PASS Specify password for user - CWD Change current working directory - CDUP Change to parent directory - QUIT Disconnects - PORT Select port to connect - LPRT Select port to connect (long format) - EPRT Select port to connect (extended format) - PASV Start passive mode - LPSV Start passive mode (long format) - EPSV Start passive mode (extended format) - TYPE Set type of file transfer (ASCII, IMAGE, LOCAL) - STRU Specify file structure (FILE) - MODE Select file transfer mode (STREAM) - RETR Retreive file - STOR Upload file - STOU Store unique file - APPE Permits upload to append on file - ALLO Allocate storage (actually NOOP) - REST Restore download at offset - RNFR Rename from - RNTO Rename to - ABOR Abort command - DELE Delete file - RMD Delete directory - MKD Create directory - MDTM Show file last modification time - PWD Print working directory - LIST List files (long format) - NLST List file names (short format) - SIZE Show file size - SYST System information (UNIX) - STAT Account info or list files - HELP Help - NOOP No operation - SITE CHMOD Change file mode - SITE UMASK Change creation mode mask - BEER *cough* - -INSTALLATION - To compile and run mmftpd you will need to have the following library in- - stalled: - - libpth 1.4.1 or later (GNU Pth), - http://www.gnu.org/software/pth/pth.html - - Upgrading from mmftpd 0.0.14 or older to mmftpd 0.0.15 or later: an im- - portant change has been performed on the way password hashes are stored. - Main reasons for this are that frontend scripts would not easily be able - to generate these, and that they were not compatible with system ones, - making migration process from real users to virtual ones harder. Unfor- - tunately, it was impossible to provide a utility to convert old password - hashes to new ones because of a bug in the previous base64 conversion - which would loose some of the last bytes of the MD5 hash. New hashes gen- - erated by mmpasswd(8) now consist of case-sensitive ASCII salted MD5 - hashes provided by crypt(3). This will remain the same for future ver- - sions. - - The mmftpd daemon should be easily compiled using the make.sh script pro- - vided with the mmsoftware CVS distribution, or the mmftpd distribution, - and installed using the install.sh script. This also should install re- - quired mmstatd daemon automatically as part of the process. The install - script does not overwrite any existing configuration files, and it often - consists of a good idea to see the man pages for any changes when upgrad- - ing. Note that there are several configurable options which can be set - prior to calling install.sh script; Use the './install.sh help' command - to see a description of the environment variables which you may want to - set. The mmlib/makedefs.sh file contains defaults for building commands, - library and include paths, and may be modified if compilation issues oc- - cur. The building process is no longer dependant on BSD or GNU make, only - /bin/sh is required. - - # cd mmftpd/ - # ./make.sh - # ./install.sh - - The required binaries now should have been installed in /usr/local/sbin - directory. - - Previously, all user home directories specified in mmftpdpasswd(5) file - had to be owned by the user the mmftpd daemon runs under (usually mmftpd - user), as well as all files within those directories. As a general rule - for read/write accounts, this should still be done. This is due to the - virtual nature of mmftpd server. It is also important then that the owner - have write permissions to all those files and directories, and executable - permission on all directories. This is automatically forced by mmftpd - when new files and directories are created, despite the specified - umask(2) in the mmftpdpasswd(5) file for the user, or specified mode to - the SITE CHMOD command. - - It now is possible to provide read-only accounts using files owned by an- - other user, which may be useful for some setups (See the mmftpdpasswd(5) - man page for more information). - - mmftpd configurable parameters are found in /etc/mmftpd.conf configura- - tion file (See mmftpd.conf(5) man page for details). It also uses the - /etc/mmftpdpasswd file as it's users database and their permissions (See - mmftpdpasswd(5) man page). - - As mmftpd uses mmstat(3) library, which requires mmstatd(8) to be run- - ning, it is suggested to start the mmstatd daemon before starting the - mmftpd server in the startup scripts of your system: - - /usr/local/sbin/mmstatd - /usr/local/sbin/mmftpd - - If anything seems to not work as expected I suggest to increase the log- - ging level to 3 or 4 and watching syslog files. - -INSTALLING WITHOUT SUPERUSER ACCESS - Here are some additionnal notes which may be useful to users who want to - install mmftpd but don't have superuser access. - - If the Pth library was installed as a normal user, it obviously will have - been installed using a user-suplied --prefix, within the user's home di- - rectory. The LD_LIBRARY_PATH environment variable will need to specify - the path where the Pth library resides then. - - Before executing the make.sh script, the -DNODROPPRIVS option should be - added to the CFLAGS environment variable. This will cause the daemon to - accept not being launched by the superuser, and will also prevent it from - attempting any changes to the current permissions it is started with. - Moreover, you may want to edit the mmlib/makedefs.sh to specify the loca- - tion of the Pth library and it's headerfiles. - - Before installing using the install.sh script, the MMPREFIX environment - variable will need to be set to a user-supplied root directory as well, - like for Pth installation. For instance, both could use the $HOME/root. - Additionally, MMCONFDIR should consist of the user /etc directory, such - as $HOME/root/etc. MMDEFAULTUSER, MMSTATDUSER and MMFTPDUSER should - specify the unprivileged user name or ID files should be owned by. - MMDEFAULTGROUP, MMADMINGROUP, MMSTATDGROUP and MMFTPDGROUP should corre- - spond to the user group or ID for file ownership. MMSTATDIR should be - set to the wanted directory to hold the mmstat database and logs, such as - $HOME/root/var/mmstat. All these variables are no longer necessary after - installation. - - It is important that the configuration files (and if wanted others) don't - be accessible by the other users (care should especially be taken when - various users use the same system group). The configuration files for mm- - stat(d) and mmftpd should then be modified accordingly. - - To allow mmstat services to work properly, the MMSTATCONF environment - variable should be set to the mmstatd.conf file fullpath. This is re- - quired at runtime like for LD_LIBRARY_PATH. - - The daemons should then normally be started specifying on the command - line the location of their configuration file, as they will otherwise at- - tempt to read those in /etc/ by default. - - Obviously, unprivileged users usually do not have access to bind to a - port below 1024. An alternative port will have to be configured in the - mmftpd configuration file for it to successfully start up if such is the - case. A very common alternative port is 2121. It is possible, if the ad- - ministrator of the system agrees to, to redirect the standard 21 port to - your unprivileged one, if required. You don't run this daemon anyways - without the administrator of the system agreeing to, right? :) - -SECURITY CONSIDERATIONS - The /etc/mmftpdpasswd file should only be readable by the superuser and - by mmftpd because all FTP user password hashes are stored in it. This is - usually done using a permission mode of 0640, with root owner, and mmftpd - group on that file: - - -rw-r----- 1 root mmftpd 2544 Sep 8 2002 /etc/mmftpdpasswd - - HTTP virtual hosts which have access to maintain CGIs, mod_perl or PHP, - any dynamic content method allowing the remote user to execute scripts on - the server, should only be remotely maintained under SSL encryption, - which a standard FTP server cannot provide. Otherwise it is possible for - a remote attacker to sniff the clear-text passwords and upload their own - scripts on the server, which usually have much more power than generating - dynamic content. - -FILES - /usr/local/sbin/mmftpd The actual FTP server binary - - /etc/mmftpd.conf mmftpd global configuration file through which - all configurable parameters may be managed - (See mmftpd.conf(5) man page). - - /etc/mmftpdpasswd Virtual users database for mmftpd with their - settings (See mmftpdpasswd(5) man page). - -AUTHOR - mmftpd was designed and written by Matthew Mondor, and is Copyright (c) - 2001-2003, Matthew Mondor, All Rights Reserved. It was primarily devel- - opped on NetBSD but has also been tested on Linux prior to release. - -SEE ALSO - mmftpd.conf(5), mmftpdpasswd(5), mmstat(8), mmstatd(8), mmpasswd(8), - mmstat(3), mmpath(3), mmfd(3), glob(3), crypt(3), umask(2), setuid(2), - setgroups(2), chroot(2). - -BUGS - Please report any bug to mmondor@gobot.ca - -NetBSD 1.6.1 8 Oct, 2003 5 diff --git a/mmsoftware/mmftpd/clean.sh b/mmsoftware/mmftpd/clean.sh deleted file mode 100755 index 3411c02..0000000 --- a/mmsoftware/mmftpd/clean.sh +++ /dev/null @@ -1,20 +0,0 @@ -#!/bin/sh -# $Id: clean.sh,v 1.1 2002/12/11 10:11:20 mmondor Exp $ - -. ../mmlib/makefuncs.sh - -cd ../mmlib/ -clean mmlib -cd ../ - -cd mmpasswd/ -clean mmpasswd -cd ../ - -cd mmstatd/src/ -clean mmstatd -cd ../../ - -cd mmftpd/src/ -clean mmftpd -cd ../../ diff --git a/mmsoftware/mmftpd/etc/mmftpd.conf b/mmsoftware/mmftpd/etc/mmftpd.conf deleted file mode 100644 index d41a180..0000000 --- a/mmsoftware/mmftpd/etc/mmftpd.conf +++ /dev/null @@ -1,146 +0,0 @@ -; $Id: mmftpd.conf,v 1.6 2004/09/19 10:59:07 mmondor Exp $ -; -; mmftpd configuration file (/etc/mmftpd.conf) -; and # are considered comments, and can happen at start or end of line. -; Read mmftpd.conf(5) man page for details. - - -; Daemon administration options -; ----------------------------- -; -; Number of asynchroneous slave processes that should be launched and used -; to perform tasks such as hostname resolving, tree size evaluation and -; MD5 hashing. -ASYNC_PROCESSES 3 -; -; Optional location of directory we should chroot(2) to. Note that special -; configuration is requried for this, /etc/hosts, /etc/resolv.conf and -; /etc/mmftpdpasswd files will be required in the new root for instance. -;CHROOT_DIR "" -; -; Name of the virtual ftp users configuration file, -; (usually "/etc/mmftpdpasswd"). -PASSWD_FILE "/etc/mmftpdpasswd" -; -; Location of the path where to store our process ID -PID_PATH "/var/run/mmftpd.pid" -; -; User mmftpd should run as -USER "mmftpd" -; Groups process should be part of -GROUPS mmftpd,mmstat -; -; Increase this to higher values if high server load is expected -ALLOC_BUFFERS 1 -; -; Logging verbosity level for syslog -; 0 = critical and important messages only -; 1 = connections are logged -; 2 = informational logging (transfers are logged) -; 3 = verbose logging (all commands except PASS, plus replies) -; 4 = even PASS is logged, with the user passwords -LOG_FACILITY LOG_AUTHPRIV LOG_LEVEL 3 - - -; TCP service general and security options -; ---------------------------------------- -; -; IP addresses we should bind() to, separated by spaces -LISTEN_IPS "127.0.0.1" -; -; Advertized server hostnames, separated by spaces, there should be the same -; number of entries than for LISTEN_IPS -SERVER_NAMES "ftp.localhost" -; -; Port we listen for connections on -LISTEN_PORT 21 -; -; Resolving hostnames for logging can make the system slow depending on -; DNS server reliability and service load -RESOLVE_HOSTS FALSE -; -; Maximum number of bad commands user may issue per session -MAX_ERRORS 16 -; -; If TRUE, the server will cause the clients to wait before sending a command -; after an error was issued. After each error an additional second delay is -; added. -DELAY_ON_ERROR FALSE -; -; Total maximum number of simultanious different IP addresses we allow -MAX_IPS 64 -; Maximum simultanious connections per single IP address we accept -MAX_PER_IP 1 -; -; Maximum number of connections per IP address to accept within -; CONNECTION_PERIOD seconds (0 to disable connection rate limit) -CONNECTION_RATE 10 -; Period in seconds in which a maximum of CONNECTION_RATE connections are -; allowed -CONNECTION_PERIOD 30 -; -; This consists of the read/write bandwidth limits for the FTP CONTROL -; connection, specified in kilobytes/second. 0 will disable bandwidth shaping. -; IN is how fast we accept data from the client, and OUT for how fast we may -; send data to the client. This is on a per-connection basis. -; See /etc/mmftpdpasswd file for DATA transfer speed limits settings. -BANDWIDTH_IN 1 BANDWIDTH_OUT 1 -; -; The global maximum bandwidth speed limit, in kilobytes per second, for the -; whole server, at which it is allowed to read and write from/to clients. -; 0 for no limit. -GBANDWIDTH_IN 0 GBANDWIDTH_OUT 0 - - -; FTP related options -; ------------------- -; -; Maximum input/output timeout in seconds for the FTP control connection. Note -; that this timeout will not cause the connection to be dropped if a transfer -; is still ongoing however. -CONTROL_TIMEOUT 300 -; -; Maximum input/output timeout in secods for the FTP DATA connections (file -; transfers). -DATA_TIMEOUT 300 -; -; If specified, tells which file to display to the user just after connection. -;WELCOME_FILE "/etc/mmftpdwelcome.txt" -; -; If specified, will be displayed to the user after a successful login. -;MOTD_FILE "/etc/mmftpdmotd.txt" -; -; If specified, will cause any file by that name to be displayed to the user -; after a CWD (change current directory) occurs. -DISPLAY_FILE "README" -; -; If DISPLAY_FILE is used, number of last visited directories to remember -; in the FIFO; These are used to prevent sending the directory message too -; often over and over again. -REMEMBER_CWDS 16 -; -; Useful in conjunction with PASV_RANGE, PASV_RANGE_MIN and PASV_RANGE_MAX -; when mmftpd(8) is behind a firewall. This allows the gateway to redirect -; ports 21 and a selected range of high ports to the box on which mmftpd(8) -; runs, without the need for a special FTPd proxy daemon running on the -; gateway. See mmftpd.conf(5) man page for more information. -;PASV_REMAP "" -; -; If one wants to randomize assigned ports for PASV/LPSV/EPSV commands, or -; to define only a certain port range to use for those commands, this feature -; should be enabled. -PASV_RANGE FALSE -PASV_RANGE_MIN 1025 -PASV_RANGE_MAX 65534 -; -; Record invalid user name events via mmstat(3) -STATFAIL_LOGIN FALSE -; -; Record invalid password for existing user name events via mmstat(3) -STATFAIL_PASSWORD FALSE -; -; Record permission denied errors via mmstat(3) -STATFAIL_PERMISSION FALSE -; -; Record invalid PORT errors via mmstat(3) -STATFAIL_PORT FALSE diff --git a/mmsoftware/mmftpd/etc/mmftpdpasswd b/mmsoftware/mmftpd/etc/mmftpdpasswd deleted file mode 100644 index a8409ed..0000000 --- a/mmsoftware/mmftpd/etc/mmftpdpasswd +++ /dev/null @@ -1,16 +0,0 @@ -; $Id: mmftpdpasswd,v 1.2 2002/12/22 11:17:21 mmondor Exp $ -; -; This consists of the /etc/mmftpdpasswd configuration file for mmftpd(8). -; Please read the mmftpdpasswd(5) man page for details. - -; un gr pw s c u m M um l mhds mfs dl ul hd - -; Main anonymous user and an admin to manage the anonymous tree -;anonymous mmftpd * 1 1 0 0 0 022 0 0 0 16 16 /home/mmftpd/anonymous -;admin mmftpd $1$SAyWTo$relJnlF0.uU1gBdNeAZkg. 0 1 1 1 1 022 5 0 0 0 0 /home/mmftpd/anonymous -;guest mmftpd * 1 1 0 0 0 022 0 0 0 0 0 /home/mmftpd/anonymous - -; Some other user for an apache HTTP vhost -;test www $1$2bPDyc$noY.g4gtgGU5fwKKMKFKP. 0 1 1 1 0 027 5 1024 512 4 4 /home/www/test - -; vim:ts=8:nowrap: diff --git a/mmsoftware/mmftpd/install.sh b/mmsoftware/mmftpd/install.sh deleted file mode 100755 index 05dc6e7..0000000 --- a/mmsoftware/mmftpd/install.sh +++ /dev/null @@ -1,154 +0,0 @@ -#!/bin/sh -# $Id: install.sh,v 1.7 2003/10/23 01:01:20 mmondor Exp $ - -if [ "$1" = "help" ]; then - echo - echo 'You can optionally set the following environment variables' - echo 'to customize the installation process. For each is shown an' - echo 'example using the default value followed by a breif description.' - echo - echo 'export MMLAUNCH="TRUE"' - echo ' Tells the install process to immediately launch the daemon(s)' - echo ' They are automatically killed before the binaries are copied' - echo ' over the old ones as is necessary with some systems. The' - echo ' default is to not start them back automatically.' - echo - echo 'export MMPREFIX="/usr/local"' - echo ' Allows to set the installation base directory. All files but' - echo ' configuration ones will be installed in directories relative' - echo ' to this one.' - echo - echo 'export MMCONFDIR="/etc"' - echo ' Directory in which configuration files should be stored.' - echo - echo 'export MMDEFAULTUSER="0"' - echo ' User new files and directories should be owned by, using' - echo ' user id or name.' - echo - echo 'export MMDEFAULTGROUP="0"' - echo ' Group new files and directories should be under, using id' - echo ' or name.' - echo - echo 'export MMADMINGROUP="staff"' - echo ' The administrators group, these can for instance view and the' - echo ' rotate mmstat statistics and execute mmpasswd. Will be' - echo ' created automatically if necessary.' - echo - echo 'export MMSTATDIR="/var/mmstatd"' - echo ' The directory in which mmstatd will store the stats database' - echo ' and log files.' - echo - echo 'export MMSTATDUSER="mmstatd"' - echo ' The user mmstatd will run under, to be automatically created.' - echo - echo 'export MMSTATDGROUP="mmstat"' - echo ' Group the mmstatd user should be part of, automatically' - echo ' created.' - echo - echo 'export MMFTPDUSER="mmftpd"' - echo ' The user mmftpd will run under, to be automatically created.' - echo - echo 'export MMFTPDGROUP="mmftpd"' - echo ' Group the mmftpd user should be part of, automatically' - echo ' created. The mmftpdpasswd configuration file will be readable' - echo ' by this group.' - echo - exit -fi - -# Set defaults if not set -if [ -z "$MMLAUNCH" ]; then - export MMLAUNCH='FALSE' -fi -if [ -z "$MMPREFIX" ]; then - export MMPREFIX='/usr/local' -fi -if [ -z "$MMCONFDIR" ]; then - export MMCONFDIR='/etc' -fi -if [ -z "$MMDEFAULTUSER" ]; then - export MMDEFAULTUSER='0' -fi -if [ -z "$MMDEFAULTGROUP" ]; then - export MMDEFAULTGROUP='0' -fi -if [ -z "$MMADMINGROUP" ]; then - export MMADMINGROUP='staff' -fi -if [ -z "$MMSTATDIR" ]; then - export MMSTATDIR='/var/mmstatd' -fi -if [ -z "$MMSTATDUSER" ]; then - export MMSTATDUSER='mmstatd' -fi -if [ -z "$MMSTATDGROUP" ]; then - export MMSTATDGROUP='mmstat' -fi -if [ -z "$MMFTPDUSER" ]; then - export MMFTPDUSER='mmftpd' -fi -if [ -z "$MMFTPDGROUP" ]; then - export MMFTPDGROUP='mmftpd' -fi - -. ../mmlib/makefuncs.sh - -instgroup $MMADMINGROUP - -cd ../mmlib/ -instman mmfd.3 3 -instman mmfifo.3 3 -instman mmlist.3 3 -instman mmpool.3 3 -instman mmpath.3 3 -instman mmstat.3 3 -instman mmhash.3 3 -instman mmlimitrate.3 3 -cd ../ - -cd mmpasswd/ -instbin mmpasswd 750 $MMADMINGROUP -instman mmpasswd.8 8 -cd ../ - -cd mmstatd/src/ -instuser $MMSTATDUSER $MMSTATDGROUP -killbin mmstatd -instbin mmstatd 700 -instbin mmstat 750 $MMADMINGROUP -instman mmstat.8 8 -instman mmstatd.8 8 -instman mmstatd.conf.5 5 -cd ../etc/ -instconf mmstatd.conf 640 $MMSTATDGROUP -instdir $MMSTATDIR 750 $MMSTATDUSER $MMSTATDGROUP -if [ "$MMLAUNCH" = "TRUE" ]; then - startbin mmstatd $MMCONFDIR/mmstatd.conf -fi -cd ../../ - -cd mmftpd/src/ -instuser $MMFTPDUSER $MMFTPDGROUP -killbin mmftpd -instbin mmftpd 700 -instman mmftpd.8 8 -instman mmftpd.conf.5 5 -instman mmftpdpasswd.5 5 -cd ../etc/ -instconf mmftpd.conf 600 -instconf mmftpdpasswd 640 $MMFTPDGROUP -if [ "$MMLAUNCH" = "TRUE" ]; then - startbin mmftpd $MMCONFDIR/mmftpd.conf -fi -cd ../../ - -echo -echo "*** Please read the following man pages ***" -echo -echo "mmstat(8), mmstatd(8), mmstatd.conf(5) mmpasswd(8)" -echo "mmftpd(8), mmftpd.conf(5), mmftpdpasswd(5)" -echo "source auditors: mmstat(3), mmfd(3), mmlist(3), mmpool(3), mmfifo(3)," -echo " mmpath(3), mmhash(3), mmlimitrate(3)" -echo -echo "Thank you for using mmsoftware." -echo diff --git a/mmsoftware/mmftpd/make.sh b/mmsoftware/mmftpd/make.sh deleted file mode 100755 index 87071b4..0000000 --- a/mmsoftware/mmftpd/make.sh +++ /dev/null @@ -1,28 +0,0 @@ -#!/bin/sh -# $Id: make.sh,v 1.2 2003/01/01 14:54:10 mmondor Exp $ - -. ../mmlib/makefuncs.sh - -cd ../mmlib/ -clean mmlib -makebin mmlib -cd ../ - -cd mmpasswd/ -clean mmpasswd -makebin mmpasswd -cd ../ - -cd mmstatd/src/ -clean mmstatd -makebin mmstatd -cd ../../ - -cd mmftpd/src/ -clean mmftpd -makebin mmftpd -cd ../../ - -echo -echo 'You may now ./install.sh help or ./install.sh to install/upgrade' -echo diff --git a/mmsoftware/mmftpd/scripts/mmftpd.sh b/mmsoftware/mmftpd/scripts/mmftpd.sh deleted file mode 100755 index 358648c..0000000 --- a/mmsoftware/mmftpd/scripts/mmftpd.sh +++ /dev/null @@ -1,32 +0,0 @@ -#!/bin/sh -# -# /etc/init.d/mmftpd: start or stop daemon (Mondor) -# -# $Id: mmftpd.sh,v 1.1 2002/12/11 10:12:35 mmondor Exp $ - -PATH=/bin:/sbin:/usr/bin:/usr/sbin - -case "$1" in - start) - echo -n "Starting up mmftpd daemon" - /usr/local/sbin/mmftpd - echo "." - ;; - stop) - echo -n "Shutting down mmftpd daemon" - /bin/kill `cat /var/run/mmftpd.pid` - echo "." - ;; - restart|force-reload) - /etc/init.d/mmftpd.sh stop - /bin/sleep 10s - /etc/init.d/mmftpd.sh start - ;; - *) - echo "Usage: /etc/init.d/mmftpd.sh {start|stop|restart|force-reload}" - exit 1 - ;; -esac - -exit 0 - diff --git a/mmsoftware/mmftpd/src/Makefile b/mmsoftware/mmftpd/src/Makefile deleted file mode 100644 index e6bc3e2..0000000 --- a/mmsoftware/mmftpd/src/Makefile +++ /dev/null @@ -1,35 +0,0 @@ -# $Id: Makefile,v 1.4 2003/07/17 22:26:50 mmondor Exp $ - -CC = gcc -MAKE = make -RM = rm -f -ECHO = echo - -CFLAGS += -D_REENTRANT -DDEBUG -Wall - -PTHINCDIR != $(ECHO) `pth-config --cflags` -PTHLIBDIR != $(ECHO) -L`pth-config --libdir` -INCDIR = -I/usr/include -I/usr/local/include -I. -I../../mmlib $(PTHINCDIR) -LIBDIR = -L/usr/local/lib -L/usr/lib -L/lib -L/usr/pkg/lib $(PTHLIBDIR) -LIBS = ../../mmlib/libmmondor.a -lc -lcrypt -lpth - -OBJS = mmftpd.o - -CCOBJ = $(CC) $(CFLAGS) -c $(INCDIR) - - - -all: $(OBJS) - $(CC) $(CFLAGS) -o mmftpd $(INCDIR) $(LIBDIR) $(OBJS) $(LIBS) - - - - -clean: - -rm -f $(OBJS) mmftpd - - - -mmftpd.o: - $(CCOBJ) mmftpd.c - diff --git a/mmsoftware/mmftpd/src/makepart.sh b/mmsoftware/mmftpd/src/makepart.sh deleted file mode 100755 index 8a4419b..0000000 --- a/mmsoftware/mmftpd/src/makepart.sh +++ /dev/null @@ -1,27 +0,0 @@ -#!/bin/sh -# $Id: makepart.sh,v 1.3 2003/01/01 14:54:10 mmondor Exp $ - -. ../../mmlib/makedefs.sh - -OBJS='mmftpd.o' -BIN1='mmftpd' - -if [ "$1" = "clean" ]; then - show $RM $OBJS $BIN1 - exit 0 -fi - -PTHINCDIR="`pth-config --cflags`" -PTHLIBDIR="-L`pth-config --libdir`" -INCDIR="-I../../mmlib $STDINC $PTHINCDIR" -LIBDIR="$STDLIB $PTHLIBDIR" -LIBS='../../mmlib/libmmondor.a -lc -lcrypt -lpth' - -for obj in $OBJS; do - if [ ! -f $obj ]; then - src=`$ECHO $obj | $SED 's/\.o$/.c/'` - show $CC $CFLAGS -c $INCDIR $src - fi -done - -show $CC $CFLAGS -o $BIN1 $INCDIR $LIBDIR $BIN1.o $LIBS diff --git a/mmsoftware/mmftpd/src/mmftpd.8 b/mmsoftware/mmftpd/src/mmftpd.8 deleted file mode 100644 index bddef5a..0000000 --- a/mmsoftware/mmftpd/src/mmftpd.8 +++ /dev/null @@ -1,436 +0,0 @@ -.\" $Id: mmftpd.8,v 1.7 2004/05/05 23:59:55 mmondor Exp $ -.\" -.\" Copyright (C) 2001-2004, Matthew Mondor -.\" All rights reserved. -.\" -.\" Redistribution and use in source and binary forms, with or without -.\" modification, are permitted provided that the following conditions -.\" are met: -.\" 1. Redistributions of source code must retain the above copyright -.\" notice, this list of conditions and the following disclaimer. -.\" 2. Redistributions in binary form must reproduce the above copyright -.\" notice, this list of conditions and the following disclaimer in the -.\" documentation and/or other materials provided with the distribution. -.\" 3. All advertising materials mentioning features or use of this software -.\" must display the following acknowledgement: -.\" This product includes software written by Matthew Mondor. -.\" 4. The name of Matthew Mondor may not be used to endorse or promote -.\" products derived from this software without specific prior written -.\" permission. -.\" -.\" THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR -.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES -.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. -.\" IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, -.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -.\" BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF -.\" USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF -.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -.\" -.Dd 8 Oct, 2003 -.Dt MMFTPD 8 -.Os -.Sh NAME -.Nm mmftpd -.Nd FTP server with virtual users and bandwidth shaping support -.Sh SYNOPSIS -.Nm mmftpd Op Ar config_file -.Sh DESCRIPTION -.Nm mmftpd -probably consists of one of the most secure FTP servers available. Secure -here does not mean that encryption is supported; It mostly means that the -service is very unlikely to be used to gain higher privileges remotely. -It runs under the privileges of a normal Unix user, and never needs to become -the superuser again. Most other FTP servers generally have to execute alot -of operations as the superuser, and are only temporarily dropping their -privileges when a successful login has been established. This generally -consists of a bad idea for public internet services. -.Pp -.Nm mmftpd -calls -.Xr setgroups 2 -and -.Xr setuid 2 -once when it starts, and remains unprivileged for it's whole lifetime. -Moreover, the server can be enclosed into a real -.Xr chroot 2 -jail if wanted, and all ftp user home directories will be stored under this -new root directory (as well as -.Nm /etc/mmftpdpasswd -configuration file). -.Pp -It then of course becomes up to the administrator to ensure that the user -the service runs under has no access to any unwanted resources, and that -PHP enabled HTTP virtual hosts be only accessed under SSL if high security -is wanted, since PHP has many vulnerability issues, and FTP passwords are -non-encrypted; It would be possible to login normally to upload wanted -scripts and have access to the rest of the system. -.Pp -Each FTP user is given the impression to be running as a normal Unix user -enclosed into a -.Xr chroot 2 -jail. This actually consists of an illusion, as all files mmftpd can work -with must be owned by the main Unix user the daemon runs as (typically the -mmftpd user), except in the case of read-only accounts where it is possible -if wanted to use files owned by other users. No Unix users are required for -the various FTP users, only virtual ones specified in -.Xr mmftpdpasswd 5 -file. The -.Xr mmpath 3 -library provides the required features for path sanity checking and -manipulation. The actual location of the user's home directory is never -disclosed. -.Pp -For security considerations related to a virtual users service, special -files such as symbolic links are not allowed to be created, viewed or accessed -via the service. Files which are not owned by the user the server runs under -will also not be accessible (unless otherwise specified). A log entry of level -0 will be generated for any of those events. Additionally, files starting -with '.' are not allowed to be created or accessed. -.Pp -.Nm mmftpd -is not vulnerable to -.Xr glob 3 -issues which often have been exploited in other FTP servers to effect Denial -of Service attacks, as it provides it's own simpler globbing. -.Pp -Another interesting feature of -.Nm mmftpd -consists of it's bandwidth shaping capability, without the need for special -kernel support. Each user can be set with custom maximum download and upload -speeds. Additionally, the I/O speed of the control connection can also be -controlled (through which commands are sent) and global server I/O speed -limits can also be set. The -.Xr mmfd 3 -library is used to provide this functionality for the -.Nm mmftpd -server. -.Pp -Also can be set custom maximum home directory size or quota per user -(even safe with multiple simultaneous logins of the same user), -custom permissions to restrict the available operations, maximum number -of allowed logins per user, connections per IP address, maximum connection -rate per address, etc. Please see the -.Xr mmftpdpasswd 5 -and -.Xr mmftpd.conf 5 -man pages for details. -.Pp -.Nm mmftpd -uses the -.Xr mmstat 3 -interface to record various statistics, and to maintain it's current who -database. Please see the -.Xr mmstatd 8 -and -.Xr mmstat 8 -man pages for information on how to use them. -.Pp -Other than that, -.Nm mmftpd -provides all necessary standard important FTP commands that are expected of a -generally RFC compliant FTP server. This includes active and passive file -transfers, with resume capability in both directions. It also handles the new -alternate -.Sy EPSV , LPSV , EPRT -and -.Sy LPRT -extentions, although only support for IPv4 is currently available, and the -permissions manipulation commands -.Sy ( CHMOD , UMASK ) -under the -.Sy SITE -extention command. -.Pp -Here is a list of all currently implemented commands (processed case -insensitively): -.Bl -column XXXXXXXXXXXX -offset indent -.It Fa Command Ta Fa Description -.It Sy USER Ta Specify user to login as -.It Sy PASS Ta Specify password for user -.It Sy CWD Ta Change current working directory -.It Sy CDUP Ta Change to parent directory -.It Sy QUIT Ta Disconnects -.It Sy PORT Ta Select port to connect -.It Sy LPRT Ta Select port to connect (long format) -.It Sy EPRT Ta Select port to connect (extended format) -.It Sy PASV Ta Start passive mode -.It Sy LPSV Ta Start passive mode (long format) -.It Sy EPSV Ta Start passive mode (extended format) -.It Sy TYPE Ta Set type of file transfer -.Sy ( ASCII , IMAGE , LOCAL ) -.It Sy STRU Ta Specify file structure -.Sy ( FILE ) -.It Sy MODE Ta Select file transfer mode -.Sy ( STREAM ) -.It Sy RETR Ta Retreive file -.It Sy STOR Ta Upload file -.It Sy STOU Ta Store unique file -.It Sy APPE Ta Permits upload to append on file -.It Sy ALLO Ta Allocate storage (actually Sy NOOP ) -.It Sy REST Ta Restore download at offset -.It Sy RNFR Ta Rename from -.It Sy RNTO Ta Rename to -.It Sy ABOR Ta Abort command -.It Sy DELE Ta Delete file -.It Sy RMD Ta Delete directory -.It Sy MKD Ta Create directory -.It Sy MDTM Ta Show file last modification time -.It Sy PWD Ta Print working directory -.It Sy LIST Ta List files (long format) -.It Sy NLST Ta List file names (short format) -.It Sy SIZE Ta Show file size -.It Sy SYST Ta System information -.Sy ( UNIX ) -.It Sy STAT Ta Account info or list files -.It Sy HELP Ta Help -.It Sy NOOP Ta "No operation" -.It Sy SITE CHMOD Ta Change file mode -.It Sy SITE UMASK Ta Change creation mode mask -.It Sy BEER Ta *cough* -.El -.Sh INSTALLATION -To compile and run mmftpd you will need to have the following library -installed: -.Bd -literal -offset indent -libpth 1.4.1 or later (GNU Pth), - http://www.gnu.org/software/pth/pth.html -.Ed -.Pp -.Nm "Upgrading from mmftpd 0.0.14 or older to mmftpd 0.0.15 or later:" -an important change has been performed on the way password hashes are stored. -Main reasons for this are that frontend scripts would not easily be able to -generate these, and that they were not compatible with system ones, making -migration process from real users to virtual ones harder. -Unfortunately, it was impossible to provide a utility to convert old password -hashes to new ones because of a bug in the previous base64 conversion which -would loose some of the last bytes of the MD5 hash. New hashes generated by -mmpasswd(8) now consist of case-sensitive ASCII salted MD5 hashes provided -by -.Xr crypt 3 . -This will remain the same for future versions. -.Pp -The -.Nm mmftpd -daemon should be easily compiled using the -.Nm make.sh -script provided with the mmsoftware CVS distribution, or the mmftpd -distribution, and installed using the -.Nm install.sh -script. This also should install required -.Nm mmstatd -daemon automatically as part of the process. The install script does not -overwrite any existing configuration files, and it often consists of a good -idea to see the man pages for any changes when upgrading. -Note that there are several configurable options which can be set prior to -calling -.Nm install.sh -script; Use the -.Nm './install.sh help' -command to see a description of the environment variables which you may want -to set. -The -.Nm mmlib/makedefs.sh -file contains defaults for building commands, library and include paths, and -may be modified if compilation issues occur. The building process is no longer -dependant on BSD or GNU make, only /bin/sh is required. -.Bd -literal -offset indent -# cd mmftpd/ -# ./make.sh -# ./install.sh -.Ed -.Pp -The required binaries now should have been installed in /usr/local/sbin -directory. -.Pp -Previously, all user home directories specified in -.Xr mmftpdpasswd 5 -file had to be owned by the user the -.Nm mmftpd -daemon runs under (usually mmftpd user), as well as all files within those -directories. As a general rule for read/write accounts, this should still -be done. This is due to the virtual nature of -.Nm mmftpd -server. It is also important then that the owner have write permissions to -all those files and directories, and executable permission on all directories. -This is automatically forced by -.Nm mmftpd -when new files and directories are created, despite the specified -.Xr umask 2 -in the -.Xr mmftpdpasswd 5 -file for the user, or specified mode to the -.Sy SITE CHMOD -command. -.Pp -It now is possible to provide read-only accounts using files owned by another -user, which may be useful for some setups (See the -.Xr mmftpdpasswd 5 -man page for more information). -.Pp -.Nm mmftpd -configurable parameters are found in -.Nm /etc/mmftpd.conf -configuration file (See -.Xr mmftpd.conf 5 -man page for details). It also uses the -.Nm /etc/mmftpdpasswd -file as it's users database and their permissions (See -.Xr mmftpdpasswd 5 -man page). -.Pp -As -.Nm mmftpd -uses -.Xr mmstat 3 -library, which requires -.Xr mmstatd 8 -to be running, it is suggested to start the -.Nm mmstatd -daemon before starting the -.Nm mmftpd -server in the startup scripts of your system: -.Bd -literal -offset indent -/usr/local/sbin/mmstatd -/usr/local/sbin/mmftpd -.Ed -.Pp -If anything seems to not work as expected I suggest to increase the logging -level to 3 or 4 and watching syslog files. -.Sh INSTALLING WITHOUT SUPERUSER ACCESS -Here are some additionnal notes which may be useful to users who want to -install mmftpd but don't have superuser access. -.Pp -If the Pth library was installed as a normal user, it obviously will have -been installed using a user-suplied --prefix, within the user's home directory. -The -.Nm LD_LIBRARY_PATH -environment variable will need to specify the path where the Pth library -resides then. -.Pp -Before executing the -.Nm make.sh -script, the -.Nm -DNODROPPRIVS -option should be added to the -.Nm CFLAGS -environment variable. This will cause the daemon to accept not being launched -by the superuser, and will also prevent it from attempting any changes to -the current permissions it is started with. Moreover, you may want to edit -the -.Nm mmlib/makedefs.sh -to specify the location of the Pth library and it's headerfiles. -.Pp -Before installing using the -.Nm install.sh -script, the -.Nm MMPREFIX -environment variable will need to be set to a user-supplied root directory -as well, like for Pth installation. For instance, both could use the -.Nm $HOME/root . -Additionally, -.Nm MMCONFDIR -should consist of the user /etc directory, such as -.Nm $HOME/root/etc . -.Nm MMDEFAULTUSER , -.Nm MMSTATDUSER -and -.Nm MMFTPDUSER -should specify the unprivileged user name or ID files should be owned by. -.Nm MMDEFAULTGROUP , -.Nm MMADMINGROUP , -.Nm MMSTATDGROUP -and -.Nm MMFTPDGROUP -should correspond to the user group or ID for file ownership. -.Nm MMSTATDIR -should be set to the wanted directory to hold the mmstat database and logs, -such as -.Nm $HOME/root/var/mmstat . -All these variables are no longer necessary after installation. -.Pp -It is important that the configuration files (and if wanted others) don't -be accessible by the other users (care should especially be taken when -various users use the same system group). The configuration files for -mmstat(d) and mmftpd should then be modified accordingly. -.Pp -To allow mmstat services to work properly, the -.Nm MMSTATCONF -environment variable should be set to the mmstatd.conf file fullpath. -This is required at runtime like for -.Nm LD_LIBRARY_PATH . -.Pp -The daemons should then normally be started specifying on the command line -the location of their configuration file, as they will otherwise attempt to -read those in /etc/ by default. -.Pp -Obviously, unprivileged users usually do not have access to bind to a port -below 1024. An alternative port will have to be configured in the mmftpd -configuration file for it to successfully start up if such is the case. -A very common alternative port is 2121. It is possible, if the administrator -of the system agrees to, to redirect the standard 21 port to your unprivileged -one, if required. You don't run this daemon anyways without the administrator -of the system agreeing to, right? :) -.Sh SECURITY CONSIDERATIONS -The -.Nm /etc/mmftpdpasswd -file should only be readable by the superuser and by -.Nm mmftpd -because all FTP user password hashes are stored in it. -This is usually done using a permission mode of 0640, with root owner, and -mmftpd group on that file: -.Bd -literal -offset indent --rw-r----- 1 root mmftpd 2544 Sep 8 2002 /etc/mmftpdpasswd -.Ed -.Pp -HTTP virtual hosts which have access to maintain CGIs, mod_perl or PHP, -any dynamic content method allowing the remote user to execute scripts -on the server, should only be remotely maintained under SSL encryption, -which a standard FTP server cannot provide. Otherwise it is possible for -a remote attacker to sniff the clear-text passwords and upload their own -scripts on the server, which usually have much more power than generating -dynamic content. -.Sh FILES -.Bl -tag -width XXXXXXXXXXXXXXXXXXXXXXXXX -compact -.It Pa /usr/local/sbin/mmftpd -The actual FTP server binary -.Pp -.It Pa /etc/mmftpd.conf -.Nm mmftpd -global configuration file through which all configurable parameters may be -managed (See -.Xr mmftpd.conf 5 -man page). -.Pp -.It Pa /etc/mmftpdpasswd -Virtual users database for -.Nm mmftpd -with their settings (See -.Xr mmftpdpasswd 5 -man page). -.El -.Sh AUTHOR -.Nm mmftpd -was designed and written by Matthew Mondor, and is -Copyright (c) 2001-2004, Matthew Mondor, All Rights Reserved. -It was primarily developped on NetBSD but has also been tested on Linux -prior to release. -.Sh SEE ALSO -.Xr mmftpd.conf 5 , -.Xr mmftpdpasswd 5 , -.Xr mmstat 8 , -.Xr mmstatd 8 , -.Xr mmpasswd 8 , -.Xr mmstat 3 , -.Xr mmpath 3 , -.Xr mmfd 3 , -.Xr glob 3 , -.Xr crypt 3 , -.Xr umask 2 , -.Xr setuid 2 , -.Xr setgroups 2 , -.Xr chroot 2 . -.Sh BUGS -Please report any bug to mmondor@accela.net diff --git a/mmsoftware/mmftpd/src/mmftpd.c b/mmsoftware/mmftpd/src/mmftpd.c deleted file mode 100644 index 6e71147..0000000 --- a/mmsoftware/mmftpd/src/mmftpd.c +++ /dev/null @@ -1,4840 +0,0 @@ -/* $Id: mmftpd.c,v 1.66 2004/09/19 11:02:45 mmondor Exp $ */ - -/* - * Copyright (C) 2001-2004, Matthew Mondor - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software written by Matthew Mondor. - * 4. The name of Matthew Mondor may not be used to endorse or promote - * products derived from this software without specific prior written - * permission. - * - * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - - - - -/* HEADERS */ - -#include -#include -#include -#include -#include -#include -#ifdef __GLIBC__ -#include -#endif - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "mmftpd.h" - - - - -MMCOPYRIGHT("@(#) Copyright (c) 2001-2004\n\ -\tMatthew Mondor. All rights reserved.\n"); -MMRCSID("$Id: mmftpd.c,v 1.66 2004/09/19 11:02:45 mmondor Exp $"); - - - - -/* - * We only support: - * - * TYPE - ASCII, IMAGE, LOCAL - * STRU - FILE - * MODE - STREAM - * - * Other modes and types are uncommon, some do not apply to TCP networking - * and others apply only to closed source mainframe operating systems. - */ - - - - -/* GLOBAL VARIABLES */ - -/* Configuration options read from a file */ -static CONFIG CONF; - -/* Used to start transfer threads */ -static pth_attr_t tthreadattr; - -/* List of logged in users and optionally current home directory size */ -static pth_mutex_t lusers_lock; -static pool_t lusers_pool; -static hashtable_t lusers_table; - -/* This is used so that clientenv structures be allocated/freed fast */ -static pth_mutex_t clenvs_lock; -static pool_t clenvs_pool; -static pool_t fifos_pool; - -/* Pool used to optimize creating/destroying mmfd mutexes */ -static pth_mutex_t mutexes_lock; -static pool_t mutexes_pool; - -/* Used for the longer-term directory size cache */ -static pth_mutex_t quota_lock; -static pool_t quota_pool; -static hashtable_t quota_table; - -/* For fast command lookup */ -static pool_t command_pool; -static hashtable_t command_table; - -/* Global bandwidth shaping context */ -static fdbcontext fdbc; - -/* And the commands mapping for help and ascii->cmd conversion */ -static struct command commands[] = { - /* LogLevel, Cmd, Args, Description (NULL=unimplemented) */ - {3, "USER", "", "Specify user to login as"}, - {4, "PASS", "", "Specify password for user"}, - {3, "ACCT", "", NULL}, - {3, "CWD", "", "Change current working directory"}, - {3, "CDUP", "", "Change to parent directory"}, - {3, "SMNT", "", NULL}, - {3, "QUIT", "", "Disconnects"}, - {3, "REIN", "", NULL}, /* Could be implementable */ - {3, "PORT", "", "Select port to connect"}, - {3, "LPRT", "", - "Select port to connect"}, - {3, "EPRT", "||
||", "Select port to connect"}, - {3, "PASV", "", "Start passive mode"}, - {3, "LPSV", "", "Start long passive mode"}, - {3, "EPSV", "", "Start extended passive mode"}, - {3, "TYPE", "]>", "Set type of file transfer"}, - {3, "STRU", "", "Specify file structure"}, - {3, "MODE", "", "Select file transfer mode"}, - {2, "RETR", "", "Retreive file"}, - {2, "STOR", "", "Upload file"}, - {2, "STOU", "[]", "Store unique file"}, - {2, "APPE", "", "Permits upload to append on file"}, - {3, "ALLO", " []", "Allocate storage"}, - {2, "REST", "", "Restart/resume command"}, - {3, "RNFR", "", "Rename from"}, - {3, "RNTO", "", "Rename to"}, - {3, "ABOR", "", "Abort command"}, - {3, "DELE", "", "Delete file"}, - {3, "RMD", "", "Delete directory"}, - {3, "MKD", "", "Create directory"}, - {3, "MDTM", "", "Show file last modification time"}, - {3, "PWD", "", "Print working directory"}, - {3, "LIST", "[]", "List files"}, - {3, "NLST", "[]", "List file names"}, - {3, "SITE", "(CHMOD |UMASK )", - "Execute a site command"}, - {3, "SIZE", "", "Show file size"}, - {3, "SYST", "", "System information"}, - {3, "STAT", "[]", "Account info or list files"}, - {3, "HELP", "[]", "Help"}, - {3, "NOOP", "", "No operation"}, - {3, "FEAT", "", NULL}, - {5, "BEER", NULL, /* HELP won't show, and won't be logged */ ""}, - {0, NULL, NULL, NULL} -}; - -/* This consists of the auth state */ -static int (*state_auth[])(clientenv *) = { - auth_user, /* USER */ - auth_pass, /* PASS */ - NULL, /* ACCT */ - NULL, /* CWD */ - NULL, /* CDUP */ - NULL, /* SMNT */ - all_quit, /* QUIT */ - NULL, /* REIN */ - NULL, /* PORT */ - NULL, /* LPRT */ - NULL, /* EPRT */ - NULL, /* PASV */ - NULL, /* LPSV */ - NULL, /* EPSV */ - NULL, /* TYPE */ - NULL, /* STRU */ - NULL, /* MODE */ - NULL, /* RETR */ - NULL, /* STOR */ - NULL, /* STOU */ - NULL, /* APPE */ - NULL, /* ALLO */ - NULL, /* REST */ - NULL, /* RNFR */ - NULL, /* RNTO */ - NULL, /* ABOR */ - NULL, /* DELE */ - NULL, /* RMD */ - NULL, /* MKD */ - NULL, /* MDTM */ - NULL, /* PWD */ - NULL, /* LIST */ - NULL, /* NLST */ - NULL, /* SITE */ - NULL, /* SIZE */ - all_syst, /* SYST */ - NULL, /* STAT */ - all_help, /* HELP */ - all_noop, /* NOOP */ - NULL, /* FEAT */ - all_beer /* BEER */ -}; - -/* This consists of the main state */ -static int (*state_main[])(clientenv *) = { - all_user, /* USER */ - all_pass, /* PASS */ - NULL, /* ACCT */ - main_cwd, /* CWD */ - main_cdup, /* CDUP */ - NULL, /* SMNT */ - all_quit, /* QUIT */ - NULL, /* REIN */ - main_port, /* PORT */ - main_lprt, /* LPRT */ - main_eprt, /* EPRT */ - main_pasv, /* PASV */ - main_lpsv, /* LPSV */ - main_epsv, /* EPSV */ - main_type, /* TYPE */ - main_stru, /* STRU */ - main_mode, /* MODE */ - main_retr, /* RETR */ - main_stor, /* STOR */ - main_stou, /* STOU */ - main_appe, /* APPE */ - main_allo, /* ALLO */ - main_rest, /* REST */ - main_rnfr, /* RNFR */ - main_rnto, /* RNTO */ - main_abor, /* ABOR */ - main_dele, /* DELE */ - main_rmd, /* RMD */ - main_mkd, /* MKD */ - main_mdtm, /* MDTM */ - main_pwd, /* PWD */ - main_list, /* LIST */ - main_nlst, /* NLST */ - main_site, /* SITE */ - main_size, /* SIZE */ - all_syst, /* SYST */ - main_stat, /* STAT */ - all_help, /* HELP */ - all_noop, /* NOOP */ - NULL, /* FEAT */ - all_beer /* BEER */ -}; - -/* Here consists of the list array of states, each consisting of an array - * of state functions/commands, and a message code and text that gets issued - * when a NULL is found for a function. - */ -static const struct state states[] = { - {state_auth, 530, "Authenticate first"}, - {state_main, 502, "Unimplemented"} -}; - -static int LOGLEVEL; - -/* Quick index to end of transfer messages */ -static const struct tr_messages tr_msg[] = { - {226, "Transfer complete"}, - {426, "Transfer aborted"}, - {426, "Transfer timeout"}, - {426, "Quota exceeded"}, - {426, "Connection lost"} -}; - -/* Pth support for mmfd(3) library */ -static fdfuncs gfdf = { - malloc, - free, - pth_poll, - pth_read, - pth_write, - pth_sleep, - pth_usleep, - _pth_mutex_create, - _pth_mutex_destroy, - _pth_mutex_lock, - _pth_mutex_unlock, - _pth_thread_yield, - _pth_eintr -}; - -/* async_getuserline() uses this for each asynchroneous process */ -static fdbuf *getuserline_fdb = NULL; - -static struct pasv_remap *pasv_map = NULL; -static int pasv_map_size = 0; -static char *pasv_map_string = NULL; - - - -/* Functions common to all states */ - -static int -all_quit(clientenv *clenv) -{ - reply(clenv->fdb, 221, FALSE, "%s Closing connection", - clenv->iface->hostname); - - return (STATE_END); -} - - -static int -all_help(clientenv *clenv) -{ - char *args[3], *tmp, *tmp3; - register int col; - fdbuf *fdb = clenv->fdb; - register char *tmp2; - - if ((mm_straspl(args, clenv->buffer, 2)) == 2) { - register struct commandnode *nod; - u_int32_t chash; - - /* Help requested on a topic */ - nod = NULL; - if ((chash = mm_strpack32(args[1], 3)) != 0) - nod = (struct commandnode *)hashtable_lookup(&command_table, - &chash, sizeof(u_int32_t)); - if (nod != NULL) { - tmp = nod->command->name; - if ((tmp2 = nod->command->args) == NULL) { - REGISTER_ERROR(); - reply(fdb, 214, FALSE, - "No help information for this command"); - } else { - if ((tmp3 = nod->command->desc) == NULL) - reply(fdb, 214, FALSE, - "Syntax: %s %s ; (not implemented)", - tmp, tmp2); - else - reply(fdb, 214, FALSE, "Syntax: %s %s ; %s", tmp, - tmp2, tmp3); - } - } else { - REGISTER_ERROR(); - reply(fdb, 502, FALSE, "Unknown command"); - } - - } else { - register int i; - - /* Show all available topics */ - reply(fdb, 214, TRUE, - "Available commands ('-' = not implemented)"); - fdbwrite(fdb, " ", 4); - - for (col = 0, i = 0; (tmp = commands[i].name) != NULL; i++) { - if (commands[i].args != NULL) { /* Only show these */ - if (commands[i].desc != NULL) { - if (!fdbprintf(fdb, "%-4s ", tmp)) - break; - } else { - if (!fdbprintf(fdb, "%-4s- ", tmp)) - break; - } - col++; - } - if (col == 8 && commands[i + 1].name != NULL && - commands[i + 1].args != NULL) { - fdbwrite(fdb, "\r\n ", 6); - col = 0; - } - } - - fdbwrite(fdb, "\r\n", 2); - reply(fdb, 214, FALSE, "HELP for more information"); - - } - - return (STATE_CURRENT); -} - - -static int -all_noop(clientenv *clenv) -{ - reply(clenv->fdb, 200, FALSE, "Nothing performed"); - - return (STATE_CURRENT); -} - - -static int -all_beer(clientenv *clenv) -{ - reply(clenv->fdb, 420, FALSE, "Here, enjoy!"); - - return (STATE_CURRENT); -} - - -static int -all_user(clientenv *clenv) -{ - REGISTER_ERROR(); - reply(clenv->fdb, 530, FALSE, "Can't change user"); - - return (STATE_CURRENT); -} - - -static int -all_pass(clientenv *clenv) -{ - REGISTER_ERROR(); - reply(clenv->fdb, 503, FALSE, "Login with USER first"); - - return (STATE_CURRENT); -} - - -static int -all_syst(clientenv *clenv) -{ - fdbuf *fdb = clenv->fdb; - char *args[2]; - - if ((mm_straspl(args, clenv->buffer, 1)) == 1) { - reply(fdb, 215, FALSE, - "UNIX Type: L8 - IEEE Std 1003 (``POSIX'')"); - } else { - reply(fdb, 550, FALSE, "Command syntax error"); - REGISTER_ERROR(); - } - - return (STATE_CURRENT); -} - - - - -/* Auth state functions */ - -static int -auth_user(clientenv *clenv) -{ - int nextstate = STATE_CURRENT; - fdbuf *fdb = clenv->fdb; - char *args[3]; - - if (clenv->user || clenv->tuser) { - if (clenv->user) clenv->user = mmstrfree(clenv->user); - if (clenv->tuser) clenv->tuser = mmstrfree(clenv->tuser); - REGISTER_ERROR(); - } - - if ((mm_straspl(args, clenv->buffer, 2)) == 2) { - - /* First verify if anonymous access is requested, if so - * ensure that anonymous account exists - */ - if (!(mm_strcmp(args[1], "anonymous"))) { - - if (checkuser(clenv, args[1])) { - /* If this succeeded once a password is entered (any) - * the system is ready to continue further - */ - clenv->anonymous = TRUE; - clenv->tuser = NULL; - reply(fdb, 331, FALSE, "Enter your name for password"); - } else { - reply(fdb, 530, FALSE, - "No anonymous access on this server"); - if (CONF.STATFAIL_LOGIN) - mmstat(&clenv->pstat, STAT_UPDATE, 1, - "mmftpd|failed|login|anonymous|%s", - clenv->c_ipaddr); - REGISTER_ERROR(); - } - - } else { - - /* We don't want the user to know immediately if user exists - * or not, we will ask for password first, and let PASS do the - * rest. - */ - if (!(clenv->tuser = mmstrdup(args[1]))) { - DEBUG_PRINTF("auth_user", "mmstrdup()"); - nextstate = STATE_ERROR; - } - clenv->anonymous = FALSE; - reply(fdb, 331, FALSE, "Password required for this user"); - - } - - } else { - reply(fdb, 500, FALSE, "Command syntax error"); - REGISTER_ERROR(); - } - - return (nextstate); -} - - -static int -auth_pass(clientenv *clenv) -{ - int nextstate = STATE_CURRENT; - fdbuf *fdb = clenv->fdb; - char *pw; - - if (!clenv->user && !clenv->tuser) { - - reply(fdb, 503, FALSE, "Specify username first"); - REGISTER_ERROR(); - - } else { - - /* We previously used mm_straspl() here, which prevented use of space - * in passwords, or empty passwords for anonymous access. - * Now simply find space or end of string, and compare from there. - */ - for (pw = clenv->buffer; *pw != '\0' && *pw > 32; pw++) ; - if (*pw != '\0') - pw++; - - /* We slow down password guessing programs */ - if (clenv->attempts > 0) - pth_sleep(clenv->attempts * 2); - clenv->attempts++; - - if (clenv->anonymous) { - - /* We don't care about the password, simply allow anonymous - * access immediately - */ - clenv->passwd = mmstrfree(clenv->passwd); - clenv->login = TRUE; - nextstate = STATE_MAIN; - mmsyslog(1, LOGLEVEL, "%08X Successful anonymous login", - clenv->id); - mmstat(&clenv->vstat, STAT_UPDATE, 1, "mmftpd|who|anonymous"); - mmstat_transact(&clenv->pstat, TRUE); - mmstat(&clenv->pstat, STAT_UPDATE, 1, - "mmftpd|user|anonymous|logins"); - mmstat(&clenv->pstat, STAT_UPDATE, 1, "mmftpd|total|logins"); - mmstat_transact(&clenv->pstat, FALSE); - - } else { - - /* clenv->tuser was set for us, check user existance and - * password accuracy. - */ - if (checkuser(clenv, clenv->tuser)) { - bool nopass = FALSE; - - if (clenv->passwd[0] == '*' && clenv->passwd[1] == '\0') - nopass = TRUE; - if (nopass || checkpw(clenv, pw, clenv->passwd)) { - /* Password matches, don't remember it */ - clenv->passwd = mmstrfree(clenv->passwd); - clenv->login = TRUE; - nextstate = STATE_MAIN; - mmsyslog(1, LOGLEVEL, - "%08X Successful login for %s", - clenv->id, clenv->user); - mmstat(&clenv->vstat, STAT_UPDATE, 1, - "mmftpd|who|%s", clenv->user); - mmstat_transact(&clenv->pstat, TRUE); - mmstat(&clenv->pstat, STAT_UPDATE, 1, - "mmftpd|user|%s|logins", clenv->user); - mmstat(&clenv->pstat, STAT_UPDATE, 1, - "mmftpd|total|logins"); - mmstat_transact(&clenv->pstat, FALSE); - } else { - mmsyslog(0, LOGLEVEL, - "%08X Failed login for %s (existing)", - clenv->id, clenv->tuser); - mmstat_transact(&clenv->pstat, TRUE); - if (CONF.STATFAIL_LOGIN) - mmstat(&clenv->pstat, STAT_UPDATE, 1, - "mmftpd|failed|login|%s", - clenv->c_ipaddr); - if (CONF.STATFAIL_PASSWORD) - mmstat(&clenv->pstat, STAT_UPDATE, 1, - "mmftpd|failed|password|%s|%s", - clenv->tuser, clenv->c_ipaddr); - mmstat_transact(&clenv->pstat, FALSE); - } - } else { - mmsyslog(0, LOGLEVEL, - "%08X Failed login for %s (unexisting)", - clenv->id, clenv->tuser); - if (CONF.STATFAIL_LOGIN) - mmstat(&clenv->pstat, STAT_UPDATE, 1, - "mmftpd|failed|login|%s", clenv->c_ipaddr); - } - - clenv->tuser = mmstrfree(clenv->tuser); - } - - if (!clenv->login) { - REGISTER_ERROR(); - if (clenv->user) clenv->user = mmstrfree(clenv->user); - if (clenv->tuser) clenv->tuser = mmstrfree(clenv->tuser); - reply(fdb, 530, FALSE, "Invalid login"); - } - - } - - /* Make sure we respect maximum logins for this user, and store a structure - * for it, with current treesize - */ - if (nextstate == STATE_MAIN) { - lusernode *lun; - size_t len; - - if (*CONF.MOTD_FILE) { - reply(fdb, 230, TRUE, NULL); - if (!file_out(fdb, CONF.MOTD_FILE)) - mmsyslog(0, LOGLEVEL, "%08X Error opening MOTD_FILE \"%s\"", - clenv->id, CONF.MOTD_FILE); - } - directory_message(clenv, 230); - - pth_mutex_acquire(&lusers_lock, FALSE, NULL); - - /* Verify if user in list */ - len = mm_strlen(clenv->user) + 1; - if ((lun = (lusernode *)hashtable_lookup(&lusers_table, clenv->user, - len)) != NULL) { - bool lok = TRUE; - - /* Yes, make sure that we observe limits, if any */ - pth_mutex_acquire(&lun->lock, FALSE, NULL); - if (clenv->maxlogins != 0) { - if (lun->logins < clenv->maxlogins) - lun->logins++; - else lok = FALSE; - } else - lun->logins++; - pth_mutex_release(&lun->lock); - if (lok) { - clenv->unode = lun; - if (clenv->anonymous) - reply(fdb, 230, FALSE, "Restricted anonymous login ok"); - else - reply(fdb, 230, FALSE, "User login ok"); - } else { - reply(fdb, 530, FALSE, - "Maximum allowed logins exceeded for this user"); - nextstate = STATE_CURRENT; - } - } else { - if (clenv->maxlogins == 0 || 1 <= clenv->maxlogins) { - /* Add new user entry, and optionally record current tree size - */ - if ((lun = (lusernode *)pool_alloc(&lusers_pool, FALSE))) { - if (clenv->maxhomesize) { - if ((lun->homesize = treesize(clenv, clenv->home, - clenv->minfilesize)) == -1) { - mmsyslog(0, LOGLEVEL, - "%08X Cannot evaluate tree size for '%s')", - clenv->id, clenv->home); - nextstate = STATE_ERROR; - lun = (lusernode *)pool_free((pnode_t *)lun); - } - } else - lun->homesize = 0; - } else { - DEBUG_PRINTF("auth_pass", "pool_alloc(lusers_pool)"); - nextstate = STATE_ERROR; - } - if (lun != NULL) { - pth_mutex_init(&lun->lock); - lun->logins = 1; - mm_memcpy(lun->user, clenv->user, len); - clenv->unode = lun; - hashtable_link(&lusers_table, (hashnode_t *)lun, lun->user, - len, FALSE); - if (clenv->anonymous) - reply(fdb, 230, FALSE, - "Restricted anonymous login ok"); - else - reply(fdb, 230, FALSE, "User login ok"); - } - } else { - reply(fdb, 530, FALSE, - "Maximum allowed logins exceeded for this user"); - nextstate = STATE_CURRENT; - } - } - - pth_mutex_release(&lusers_lock); - } - - return (nextstate); -} - - - - -/* Main state functions */ - -static int -main_cwd(clientenv *clenv) -{ - char path[MMPATH_MAX], path2[MMPATH_MAX]; - int t = MMPATH_DENIED; - - if (path_valid(path, NULL, clenv->home, clenv->cwd, &clenv->buffer[3], - FALSE, TRUE, FALSE)) { - /* Make sure directory exists and can be accessed securely */ - snprintf(path2, MMPATH_MAX - 1, "%s%s", clenv->home, path); - if ((t = path_exists(path2, NULL, NULL, clenv->checkowner)) != - MMPATH_DENIED) { - if (t == MMPATH_DIR) { - mm_strncpy(clenv->cwd, path, MMPATH_MAX - 1); - if (!directory_message(clenv, 250)) return (STATE_ERROR); - reply(clenv->fdb, 250, FALSE, "Command successful"); - - return (STATE_CURRENT); - } else - reply(clenv->fdb, 550, FALSE, "No such file or directory"); - } else { - reply(clenv->fdb, 502, FALSE, "Permission denied"); - REGISTER_PERMISSION(); - } - } else - reply(clenv->fdb, 550, FALSE, "Invalid pathname"); - - REGISTER_ERROR(); - - return (STATE_CURRENT); -} - - -static int -main_cdup(clientenv *clenv) -{ - int nextstate = STATE_CURRENT; - char *args[2]; - - if ((mm_straspl(args, clenv->buffer, 1)) == 1) { - path_parent(clenv->cwd); - if (!directory_message(clenv, 250)) - nextstate = STATE_ERROR; - reply(clenv->fdb, 250, FALSE, "Command successful"); - } else { - reply(clenv->fdb, 550, FALSE, "Command syntax error"); - REGISTER_ERROR(); - } - - return (nextstate); -} - - -static int -main_port(clientenv *clenv) -{ - int i; - u_int16_t port; - fdbuf *fdb = clenv->fdb; - char *args[7], ipaddr[16]; - u_int8_t p[7]; - transfermsg *msg = &(clenv->tmsg); - - /* PORT 127,0,0,1,255,26 */ - - /* First make sure that the command holds two space separated sections */ - if ((mm_straspl(args, clenv->buffer, 2)) == 2) { - /* Now make sure that the second section holds 6 values */ - if ((mm_strspl(args, args[1], 6, ',')) == 6) { - for (i = 0; i < 6; i++) - p[i] = (u_int8_t)atoi(args[i]); - /* Get port number */ - port = BYTEORDER_HOST16(p[4] | (p[5] << 8)); - /* port = (256 * p[4]) + p[5]; */ - /* Get address */ - sprintf(ipaddr, "%u.%u.%u.%u", p[0], p[1], p[2], p[3]); - /* Perform sanity check */ - if ((port > 1023) && (!mm_strcmp(ipaddr, clenv->c_ipaddr))) { - /* Everything seems valid make sure no transfers in progress */ - if (transfer_request(REQ_STATUS, TRUE, clenv)) { - if (!msg->ongoing) { - /* Send our PORT transfer request */ - msg->port = port; - msg->ipaddr = ipaddr; - msg->passive = FALSE; - if (transfer_request(REQ_NEWPORT, TRUE, clenv)) - reply(fdb, 200, FALSE, "PORT command successful"); - else { - reply(fdb, 500, FALSE, "Error"); - REGISTER_ERROR(); - DEBUG_PRINTF("main_port", - "transfer_request(REQ_NEWPORT)"); - } - } else { - reply(fdb, 500, FALSE, "Transfer ongoing"); - REGISTER_ERROR(); - } - } else { - reply(fdb, 500, FALSE, "Error"); - REGISTER_ERROR(); - DEBUG_PRINTF("main_port", "transfer_reqiest(REQ_STATUS)"); - } - } else { - mmsyslog(0, LOGLEVEL, "%08X Illegal port %s:%u", - clenv->id, ipaddr, port); - reply(fdb, 500, FALSE, "Illegal port or address"); - REGISTER_PORT(); - REGISTER_ERROR(); - } - } else { - reply(fdb, 550, FALSE, "Command syntax error"); - REGISTER_ERROR(); - } - } else { - reply(fdb, 550, FALSE, "Command syntax error"); - REGISTER_ERROR(); - } - - return (STATE_CURRENT); -} - - -static int -main_lprt(clientenv *clenv) -{ - int i; - u_int16_t port; - fdbuf *fdb = clenv->fdb; - char *args[10], ipaddr[16]; - u_int8_t p[10]; - transfermsg *msg = &(clenv->tmsg); - - /* LPRT 4,4,127,0,0,1,2,204,36 */ - - /* First make sure that the command holds two space separated sections */ - if ((mm_straspl(args, clenv->buffer, 2)) == 2) { - /* Now make sure that the second section holds 9 values */ - if ((mm_strspl(args, args[1], 9, ',')) == 9) { - for (i = 0; i < 9; i++) - p[i] = (u_int8_t)atoi(args[i]); - if (p[0] == 4 && p[1] == 4 && p[6] == 2) { - /* Get port number */ - port = BYTEORDER_HOST16(p[7] | (p[8] << 8)); - /* port = (256 * p[7]) + p[8]; */ - /* Get address */ - sprintf(ipaddr, "%u.%u.%u.%u", p[2], p[3], p[4], p[5]); - /* Perform sanity check */ - if ((port > 1023) && (!mm_strcmp(ipaddr, clenv->c_ipaddr))) { - /* Everything seems valid make sure no transfers in - * progress - */ - if (transfer_request(REQ_STATUS, TRUE, clenv)) { - if (!msg->ongoing) { - /* Send our PORT transfer request */ - msg->port = port; - msg->ipaddr = ipaddr; - msg->passive = FALSE; - if (transfer_request(REQ_NEWPORT, TRUE, clenv)) - reply(fdb, 200, FALSE, - "LPRT command successful"); - else { - reply(fdb, 500, FALSE, "Error"); - REGISTER_ERROR(); - DEBUG_PRINTF("main_lprt", - "transfer_request(REQ_NEWPORT)"); - } - } else { - reply(fdb, 500, FALSE, "Transfer ongoing"); - REGISTER_ERROR(); - } - } else { - reply(fdb, 500, FALSE, "Error"); - REGISTER_ERROR(); - DEBUG_PRINTF("main_lptr", - "transfer_request(REQ_STATUS)"); - } - } else { - mmsyslog(0, LOGLEVEL, "%08X Illegal port %s:%u", - clenv->id, ipaddr, port); - reply(fdb, 500, FALSE, "Illegal port or address"); - REGISTER_PORT(); - REGISTER_ERROR(); - } - } else { - reply(fdb, 522, FALSE, "Unimplemented protocol"); - REGISTER_ERROR(); - } - } else { - reply(fdb, 550, FALSE, "Command syntax error"); - REGISTER_ERROR(); - } - } else { - reply(fdb, 550, FALSE, "Command syntax error"); - REGISTER_ERROR(); - } - - return (STATE_CURRENT); -} - - -static int -main_eprt(clientenv *clenv) -{ - int port; - fdbuf *fdb = clenv->fdb; - char *args[5], ipaddr[16]; - transfermsg *msg = &(clenv->tmsg); - - /* EPRT |1|127.0.0.1|32035| */ - - /* First make sure that the command holds two space separated sections */ - if ((mm_straspl(args, clenv->buffer, 2)) == 2) { - /* Now make sure that the second section holds 4 values */ - if ((mm_strspl(args, args[1], 4, '|')) == 4) { - if (atoi(args[1]) == 1) { - /* Get port number, address is args[2] */ - port = atoi(args[3]); - /* Perform sanity check */ - if ((port > 1023 && port < 65536) - && (!mm_strcmp(args[2], clenv->c_ipaddr))) { - /* Everything seems valid, make sure no transfers in - * progress - */ - if (transfer_request(REQ_STATUS, TRUE, clenv)) { - if (!msg->ongoing) { - /* Send our PORT transfer request */ - msg->port = (u_int16_t)port; - msg->ipaddr = args[2]; - msg->passive = FALSE; - if (transfer_request(REQ_NEWPORT, TRUE, clenv)) - reply(fdb, 200, FALSE, - "EPRT command successful"); - else { - reply(fdb, 500, FALSE, "Error"); - REGISTER_ERROR(); - DEBUG_PRINTF("main_eprt", - "transfer_request(REQ_NEWPORT)"); - } - } else { - reply(fdb, 500, FALSE, "Transfer ongoing"); - REGISTER_ERROR(); - } - } else { - reply(fdb, 500, FALSE, "Error"); - REGISTER_ERROR(); - DEBUG_PRINTF("main_eprt", - "transfer_request(REQ_STATUS)"); - } - } else { - mmsyslog(0, LOGLEVEL, "%08X Illegal port %s:%d", - clenv->id, ipaddr, port); - reply(fdb, 500, FALSE, "Illegal port or address"); - REGISTER_PORT(); - REGISTER_ERROR(); - } - } else { - reply(fdb, 522, FALSE, "Unimplemented protocol"); - REGISTER_ERROR(); - } - } else { - reply(fdb, 550, FALSE, "Command syntax error"); - REGISTER_ERROR(); - } - } else { - reply(fdb, 550, FALSE, "Command syntax error"); - REGISTER_ERROR(); - } - - return (STATE_CURRENT); -} - - -static int -main_pasv(clientenv *clenv) -{ - fdbuf *fdb = clenv->fdb; - transfermsg *msg = &(clenv->tmsg); - u_int8_t a, b; - - if (!clenv->buffer[4]) { - /* Make sure no transfers are in progress */ - if (transfer_request(REQ_STATUS, TRUE, clenv)) { - if (!msg->ongoing) { - /* Send our PASV transfer request */ - msg->passive = TRUE; - if (transfer_request(REQ_NEWPORT, TRUE, clenv)) { - u_int16_t v = BYTEORDER_NETWORK16(msg->port); - - a = (v & 0x00FF); - b = (v & 0xFF00) >> 8; - /* - a = msg->port / 256; - b = msg->port % 256; - */ - reply(fdb, 227, FALSE, - "Entering passive mode (%s,%u,%u)", - clenv->sipaddr, a, b); - } else { - reply(fdb, 500, FALSE, "Error"); - REGISTER_ERROR(); - DEBUG_PRINTF("main_pasv", - "transfer_request(REQ_NEWPORT)"); - } - } else { - reply(fdb, 500, FALSE, "Transfer ongoing"); - REGISTER_ERROR(); - } - } else { - reply(fdb, 500, FALSE, "Error"); - REGISTER_ERROR(); - DEBUG_PRINTF("main_pasv", "transfer_request(REQ_STATUS)"); - } - } else { - reply(fdb, 501, FALSE, "Command syntax error"); - REGISTER_ERROR(); - } - - return (STATE_CURRENT); -} - - -static int -main_lpsv(clientenv *clenv) -{ - fdbuf *fdb = clenv->fdb; - transfermsg *msg = &(clenv->tmsg); - u_int8_t a, b; - - if (!clenv->buffer[4]) { - /* Make sure no transfers are in progress */ - if (transfer_request(REQ_STATUS, TRUE, clenv)) { - if (!msg->ongoing) { - /* Send our PASV transfer request */ - msg->passive = TRUE; - if (transfer_request(REQ_NEWPORT, TRUE, clenv)) { - u_int16_t v = BYTEORDER_NETWORK16(msg->port); - - a = (v & 0x00FF); - b = (v & 0xFF00) >> 8; - /* - a = msg->port / 256; - b = msg->port % 256; - */ - reply(fdb, 228, FALSE, - "Entering long passive mode (4,4,%s,2,%u,%u)", - clenv->sipaddr, a, b); - } else { - reply(fdb, 500, FALSE, "Error"); - REGISTER_ERROR(); - DEBUG_PRINTF("main_lpsv", - "transfer_request(REQ_NEWPORT)"); - } - } else { - reply(fdb, 500, FALSE, "Transfer ongoing"); - REGISTER_ERROR(); - } - } else { - reply(fdb, 500, FALSE, "Error"); - REGISTER_ERROR(); - DEBUG_PRINTF("main_lpsv", "transfer_request(REQ_STATUS)"); - } - } else { - reply(fdb, 501, FALSE, "Command syntax error"); - REGISTER_ERROR(); - } - - return (STATE_CURRENT); -} - - -static int -main_epsv(clientenv *clenv) -{ - fdbuf *fdb = clenv->fdb; - transfermsg *msg = &(clenv->tmsg); - - if (!clenv->buffer[4]) { - /* Make sure no transfers are in progress */ - if (transfer_request(REQ_STATUS, TRUE, clenv)) { - if (!msg->ongoing) { - /* Send our PASV transfer request */ - msg->passive = TRUE; - if (transfer_request(REQ_NEWPORT, TRUE, clenv)) { - reply(fdb, 229, FALSE, - "Entering extended passive mode (|||%u|)", - msg->port); - } else { - reply(fdb, 500, FALSE, "Error"); - REGISTER_ERROR(); - DEBUG_PRINTF("main_epsv", - "transfer_request(REQ_NEWPORT)"); - } - } else { - reply(fdb, 500, FALSE, "Transfer ongoing"); - REGISTER_ERROR(); - } - } else { - reply(fdb, 500, FALSE, "Error"); - REGISTER_ERROR(); - DEBUG_PRINTF("main_epsv", "transfer_request(REQ_STATUS)"); - } - } else { - reply(fdb, 501, FALSE, "Command syntax error"); - REGISTER_ERROR(); - } - - return (STATE_CURRENT); -} - - -static int -main_type(clientenv *clenv) -{ - int a; - fdbuf *fdb = clenv->fdb; - char *args[4]; - - if ((a = mm_straspl(args, clenv->buffer, 3)) == 2) { - if (!(mm_strcasecmp(args[1], "A"))) { - clenv->type = TYPE_ASCII; - reply(fdb, 200, FALSE, "Type ASCII set"); - } else if (!(mm_strcasecmp(args[1], "I"))) { - clenv->type = TYPE_IMAGE; - reply(fdb, 200, FALSE, "Type IMAGE set"); - } else if (!(mm_strcasecmp(args[1], "L"))) { - clenv->type = TYPE_LOCAL; - reply(fdb, 200, FALSE, "Type LOCAL set (8 bits per byte)"); - } else { - reply(fdb, 504, FALSE, "Unimplemented type"); - REGISTER_ERROR(); - } - } else if (a == 3) { - if (!(mm_strcasecmp(args[1], "L"))) { - if (atoi(args[2]) == 8) { - clenv->type = TYPE_LOCAL; - reply(fdb, 200, FALSE, "Type LOCAL set (8 bits per byte)"); - } else { - reply(fdb, 504, FALSE, "Only 8-bit bytes allowed"); - REGISTER_ERROR(); - } - } else { - reply(fdb, 550, FALSE, "Command syntax error"); - REGISTER_ERROR(); - } - } else { - reply(fdb, 550, FALSE, "Command syntax error"); - REGISTER_ERROR(); - } - - return (STATE_CURRENT); -} - - -static int -main_stru(clientenv *clenv) -{ - fdbuf *fdb = clenv->fdb; - char *args[3]; - - if ((mm_straspl(args, clenv->buffer, 2)) == 2) { - if (!(mm_strcasecmp(args[1], "F"))) - reply(fdb, 200, FALSE, "Structure FILE set"); - else { - reply(fdb, 504, FALSE, "Unimplemented structure"); - REGISTER_ERROR(); - } - } else { - reply(fdb, 550, FALSE, "Command syntax error"); - REGISTER_ERROR(); - } - - return (STATE_CURRENT); -} - - -static int -main_mode(clientenv *clenv) -{ - fdbuf *fdb = clenv->fdb; - char *args[3]; - - if ((mm_straspl(args, clenv->buffer, 2)) == 2) { - if (!(mm_strcasecmp(args[1], "S"))) - reply(fdb, 200, FALSE, "Mode STREAM set"); - else { - reply(fdb, 502, FALSE, "Unimplemented mode"); - REGISTER_ERROR(); - } - } else { - reply(fdb, 550, FALSE, "Command syntax error"); - REGISTER_ERROR(); - } - - return (STATE_CURRENT); -} - - -static int -main_retr(clientenv *clenv) -{ - int file; - fdbuf *fdb = clenv->fdb; - char path[MMPATH_MAX], cpath[MMPATH_MAX]; - transfermsg *msg = &(clenv->tmsg); - - if (path_valid(path, cpath, clenv->home, clenv->cwd, &clenv->buffer[4], - FALSE, FALSE, FALSE)) { - if (path_exists(path, NULL, NULL, clenv->checkowner) == MMPATH_FILE) { - if (transfer_request(REQ_STATUS, TRUE, clenv)) { - if (msg->port != 0) { - if (!msg->ongoing) { - if ((file = open(path, O_RDONLY)) != -1) { - msg->file = file; - msg->list = 0; - msg->download = TRUE; - msg->path = cpath; - msg->rrate = clenv->bw_out; - msg->wrate = clenv->bw_in; - if (!transfer_request(REQ_TRANSFER, TRUE, clenv)) { - DEBUG_PRINTF("main_retr", - "transfer_request(REQ_TRANSFER)"); - close(file); - } - if (clenv->stats) { - mmstat(&clenv->pstat, STAT_UPDATE, 1, - "mmftpd|user|%s|GET|%s", clenv->user, - cpath); - } - } else { - reply(fdb, 425, FALSE, "Error"); - REGISTER_ERROR(); - DEBUG_PRINTF("main_retr", "open(%s)", path); - } - } else { - reply(fdb, 425, FALSE, "Transfer already ongoing"); - REGISTER_ERROR(); - } - } else { - reply(fdb, 425, FALSE, "Can't establish data connection"); - REGISTER_ERROR(); - } - } else { - reply(fdb, 425, FALSE, "Error"); - REGISTER_ERROR(); - DEBUG_PRINTF("main_retr", "transfer_request(REQ_STATUS)"); - } - } else { - reply(fdb, 550, FALSE, "Not a plain file"); - REGISTER_ERROR(); - } - } else { - reply(fdb, 550, FALSE, "No such file or directory"); - REGISTER_ERROR(); - } - - return (STATE_CURRENT); -} - - -/* For functions that can create files, such as STOR, STOU, APPE, MKD and RNTO, - * we make sure to verify the length of the fullpathname before creating the - * file. This way, if the user supplied too long paths, or STOU would cause it - * to be too long, and thus truncated, the name should be considered invalid. - */ -static int -main_stor(clientenv *clenv) -{ - int file, i; - fdbuf *fdb = clenv->fdb; - char path[MMPATH_MAX], cpath[MMPATH_MAX]; - transfermsg *msg = &(clenv->tmsg); - mode_t mode; - long fsize; - - /* We only permit uploading if user has upload permissions. We however only - * allow overwriting an existing file if user also has modify permissions. - */ - if (clenv->upload) { - if (path_valid(path, cpath, clenv->home, clenv->cwd, - &clenv->buffer[4], FALSE, FALSE, FALSE) - && (mm_strlen(path) < MMPATH_MAX - 2)) { - if ((i = path_exists(path, &fsize, NULL, clenv->checkowner)) != - MMPATH_DIR && i != MMPATH_DENIED) { - if ((i != MMPATH_NONE && clenv->modify) || i == MMPATH_NONE) { - if (transfer_request(REQ_STATUS, TRUE, clenv)) { - if (msg->port != 0) { - if (!msg->ongoing) { - mode = ~clenv->umask; - mode &= ~0711; - mode |= 0600; - /* If file exists and was successfully - * truncated, we have to update the current - * tree size. A new file will be considered - * minfilesize bytes. We open the file first - * to prevent possible exploits a user may - * use to exceed the quota, but, to make sure, - * check for minfilesize first. - */ - if (treesize_edit(clenv, - clenv->minfilesize)) { - if ((file = open(path, - O_WRONLY | O_CREAT | O_TRUNC, - mode)) != -1) { - if (i != MMPATH_NONE && fsize > 0) - treesize_edit(clenv, - (clenv->minfilesize - fsize)); - if ((fchown(file, -1, clenv->gid)) - == -1) - DEBUG_PRINTF("main_stor", - "fchown(%s)", path); - msg->file = file; - msg->list = 0; - msg->download = FALSE; - msg->path = cpath; - msg->rrate = clenv->bw_out; - msg->wrate = clenv->bw_in; - if (!transfer_request(REQ_TRANSFER, - TRUE, clenv)) { - DEBUG_PRINTF("main_stor", - "transfer_request(REQ_TRANSFER"); - close(file); - } - } else { - reply(fdb, 425, FALSE, "Error"); - REGISTER_ERROR(); - DEBUG_PRINTF("main_stor", - "open(%s)", path); - } - } else { - reply(fdb, 502, FALSE, "Quota exceeded"); - REGISTER_ERROR(); - mmsyslog(0, LOGLEVEL, - "%08X Quota exceeded", - clenv->id); - } - } else { - reply(fdb, 425, FALSE, - "Transfer already ongoing"); - REGISTER_ERROR(); - } - } else { - reply(fdb, 425, FALSE, - "Can't establish data connection"); - REGISTER_ERROR(); - } - } else { - reply(fdb, 425, FALSE, "Error"); - REGISTER_ERROR(); - DEBUG_PRINTF("main_stor", - "transfer_request(REQ_STATUS)"); - } - } else { - reply(fdb, 502, FALSE, "Permission denied"); - REGISTER_PERMISSION(); - REGISTER_ERROR(); - } - } else { - reply(fdb, 550, FALSE, "Not a plain file"); - REGISTER_ERROR(); - } - } else { - reply(fdb, 550, FALSE, "Invalid filename"); - REGISTER_ERROR(); - } - } else { - reply(fdb, 502, FALSE, "Permission denied"); - REGISTER_PERMISSION(); - REGISTER_ERROR(); - } - - return (STATE_CURRENT); -} - - -static int -main_stou(clientenv *clenv) -{ - int file, i, i2, i3; - fdbuf *fdb = clenv->fdb; - char path[MMPATH_MAX], cpath[MMPATH_MAX], prefix[MMPATH_MAX], - uniquepath[MMPATH_MAX], *tmp; - transfermsg *msg = &(clenv->tmsg); - mode_t mode; - unsigned int unique; - - /* If user doesn't specify a name or path, a unique filename is generated - * on the current directory. Otherwise, the unique file ID will be appended - * as a postfix after the specified name at specified location. - */ - if (clenv->upload) { - if (!clenv->buffer[4]) tmp = "."; - else tmp = &(clenv->buffer[4]); - if (path_valid(path, cpath, clenv->home, clenv->cwd, tmp, FALSE, - FALSE, TRUE)) { - /* Verify if specified path consists of a directory only, in which - * case we prefix the filename by "unique" - */ - if (path_exists(path, NULL, NULL, clenv->checkowner) == - MMPATH_DIR) { - /* Directory specified, generate our own filename prefix */ - i = mm_strlen(path); - if (i && path[i - 1] != '/') { - *prefix = '/'; - prefix[1] = '\0'; - } else - *prefix = '\0'; - mm_strcat(prefix, "unique"); - } else { - /* Keep the specified name as prefix */ - *prefix = '\0'; - } - /* We are now looking for a unique filename we can create */ - unique = rand(); - i2 = 0; - do { - i2++; - i3 = rand() % 16; - for (i = 0; i < i3; i++) unique += rand(); - snprintf(uniquepath, MMPATH_MAX - 1, "%s%s.%08X", path, - prefix, unique); - i = mm_strlen(uniquepath); - if (!(i < MMPATH_MAX - 2)) break; - } while ((path_exists(uniquepath, NULL, NULL, clenv->checkowner) - != MMPATH_NONE) && i2 < 50); - if ((i2 < 50) && (i < MMPATH_MAX - 2)) { - /* We have found unique filename to create */ - if (transfer_request(REQ_STATUS, TRUE, clenv)) { - if (msg->port != 0) { - if (!msg->ongoing) { - mode = ~clenv->umask; - mode &= ~0711; - mode |= 0600; - if (treesize_edit(clenv, clenv->minfilesize)) { - if ((file = open(uniquepath, - O_WRONLY | O_CREAT, - mode)) != -1) { - if ((fchown(file, -1, clenv->gid)) == -1) - DEBUG_PRINTF("main_stou", - "fchown(%s)", uniquepath); - msg->file = file; - msg->list = 0; - msg->download = FALSE; - snprintf(uniquepath, MMPATH_MAX - 1, - "%s%s.%08X", cpath, prefix, - unique); - msg->path = uniquepath; - msg->rrate = clenv->bw_out; - msg->wrate = clenv->bw_in; - if (!transfer_request(REQ_TRANSFER, TRUE, - clenv)) { - DEBUG_PRINTF("main_stou", - "transfer_request(REQ_TRANSFER)"); - close(file); - } - } else { - reply(fdb, 425, FALSE, "Error"); - REGISTER_ERROR(); - DEBUG_PRINTF("main_stou", "open(%s)", - uniquepath); - } - } else { - reply(fdb, 502, FALSE, "Quota exceeded"); - REGISTER_ERROR(); - mmsyslog(0, LOGLEVEL, - "%08X Quota exceeded", clenv->id); - } - } else { - reply(fdb, 425, FALSE, "Transfer already ongoing"); - REGISTER_ERROR(); - } - } else { - reply(fdb, 425, FALSE, - "Can't establish data connection"); - REGISTER_ERROR(); - } - } else { - reply(fdb, 425, FALSE, "Error"); - REGISTER_ERROR(); - DEBUG_PRINTF("main_stou", - "transfer_request(REQ_STATUS)"); - } - } else { - reply(fdb, 425, FALSE, "Coundn't generate unique filename"); - REGISTER_ERROR(); - DEBUG_PRINTF("main_stou", - "Couldn't generate unique filename"); - } - } else { - reply(fdb, 550, FALSE, "Invalid filename"); - REGISTER_ERROR(); - } - } else { - reply(fdb, 502, FALSE, "Permission denied"); - REGISTER_PERMISSION(); - REGISTER_ERROR(); - } - - return (STATE_CURRENT); -} - - -static int -main_appe(clientenv *clenv) -{ - int file = -1, i; - fdbuf *fdb = clenv->fdb; - char path[MMPATH_MAX], cpath[MMPATH_MAX]; - transfermsg *msg = &(clenv->tmsg); - mode_t mode; - - /* We only permit uploading if user has upload permissions. We however only - * allow appending to an existing file if user also has modify permissions. - */ - if (clenv->upload) { - if (path_valid(path, cpath, clenv->home, clenv->cwd, - &clenv->buffer[4], FALSE, FALSE, FALSE) - && (mm_strlen(path) < MMPATH_MAX - 2)) { - if ((i = path_exists(path, NULL, NULL, clenv->checkowner)) != - MMPATH_DIR && i != MMPATH_DENIED) { - if ((i != MMPATH_NONE && clenv->modify) - || i == MMPATH_NONE) { - if (transfer_request(REQ_STATUS, TRUE, clenv)) { - if (msg->port != 0) { - if (!msg->ongoing) { - if (i == MMPATH_NONE) { - mode = ~clenv->umask; - mode &= ~0711; - mode |= 0600; - if (treesize_edit(clenv, - clenv->minfilesize)) { - file = open(path, O_WRONLY | O_CREAT, - mode); - } else { - reply(fdb, 502, FALSE, - "Quota exceeded"); - REGISTER_ERROR(); - mmsyslog(0, LOGLEVEL, - "%08X Quota exceeded", - clenv->id); - } - } else - file = open(path, O_WRONLY | O_APPEND); - if (file != -1) { - if (i == MMPATH_NONE) { - if ((fchown(file, -1, clenv->gid)) - == -1) - DEBUG_PRINTF("main_appe", - "fchown(%s)", path); - } - msg->file = file; - msg->list = 0; - msg->download = FALSE; - msg->path = cpath; - msg->rrate = clenv->bw_out; - msg->wrate = clenv->bw_in; - if (!transfer_request(REQ_TRANSFER, TRUE, - clenv)) { - DEBUG_PRINTF("main_appe", - "transfer_request(REQ_TRANSFER)"); - close(file); - } - } else { - reply(fdb, 425, FALSE, "Error"); - REGISTER_ERROR(); - DEBUG_PRINTF("main_appe", - "open(%s)", path); - } - } else { - reply(fdb, 425, FALSE, - "Transfer already ongoing"); - REGISTER_ERROR(); - } - } else { - reply(fdb, 425, FALSE, - "Can't establish data connection"); - REGISTER_ERROR(); - } - } else { - reply(fdb, 425, FALSE, "Error"); - REGISTER_ERROR(); - DEBUG_PRINTF("main_appe", - "transfer_request(REQ_STATUS"); - } - } else { - reply(fdb, 502, FALSE, "Permission denied"); - REGISTER_PERMISSION(); - REGISTER_ERROR(); - } - } else { - reply(fdb, 550, FALSE, "Not a plain file"); - REGISTER_ERROR(); - } - } else { - reply(fdb, 550, FALSE, "Invalid filename"); - REGISTER_ERROR(); - } - } else { - reply(fdb, 502, FALSE, "Permission denied"); - REGISTER_PERMISSION(); - REGISTER_ERROR(); - } - - return (STATE_CURRENT); -} - - -static int -main_allo(clientenv *clenv) -{ - char *args[3]; - - if ((mm_straspl(args, clenv->buffer, 2)) == 2) - reply(clenv->fdb, 202, FALSE, "Nothing done"); - else { - reply(clenv->fdb, 550, FALSE, "Command syntax error"); - REGISTER_ERROR(); - } - - return (STATE_CURRENT); -} - - -static int -main_rest(clientenv *clenv) -{ - char *args[3]; - - if ((mm_straspl(args, clenv->buffer, 2)) == 2) { - clenv->rest = (off_t)atoll(args[1]); - reply(clenv->fdb, 350, FALSE, - "Restarting at %lld, waiting for STOR/RETR", - (int64_t)clenv->rest); - } else { - reply(clenv->fdb, 550, FALSE, "Command syntax error"); - REGISTER_ERROR(); - } - - return (STATE_CURRENT); -} - - -static int -main_rnfr(clientenv *clenv) -{ - int t = MMPATH_DENIED; - fdbuf *fdb = clenv->fdb; - char path[MMPATH_MAX]; - - if (clenv->modify) { - - /* First forget last filename we remembered if any */ - if (clenv->rnfr) clenv->rnfr = mmstrfree(clenv->rnfr); - - if (path_valid(path, NULL, clenv->home, clenv->cwd, - &clenv->buffer[4], FALSE, FALSE, FALSE)) { - /* Make sure directory exists and can be accessed securely */ - if ((t = path_exists(path, NULL, NULL, clenv->checkowner)) != - MMPATH_NONE && t != MMPATH_DENIED) { - if (!(clenv->rnfr = mmstrdup(path))) - DEBUG_PRINTF("main_rnfr", "mmstrdup(%s)", path); - } - } - - if (clenv->rnfr) - reply(fdb, 350, FALSE, "Exists, specify new name"); - else { - reply(fdb, 550, FALSE, "No such file or directory"); - REGISTER_ERROR(); - } - - } else { - reply(fdb, 502, FALSE, "Permission denied"); - REGISTER_PERMISSION(); - REGISTER_ERROR(); - } - - return (STATE_CURRENT); -} - - -static int -main_rnto(clientenv *clenv) -{ - fdbuf *fdb = clenv->fdb; - char path[MMPATH_MAX]; - - if (clenv->modify) { - if (clenv->rnfr) { - if (path_valid(path, NULL, clenv->home, clenv->cwd, - &clenv->buffer[4], FALSE, FALSE, FALSE) - && (mm_strlen(path) < MMPATH_MAX - 2)) { - /* Make sure file does not exist */ - if (path_exists(path, NULL, NULL, clenv->checkowner) == - MMPATH_NONE) { - /* Rename file */ - if ((rename(clenv->rnfr, path)) != -1) - reply(fdb, 250, FALSE, "Command successful"); - else { - reply(fdb, 521, FALSE, "Error renaming file"); - REGISTER_ERROR(); - mmsyslog(0, LOGLEVEL, - "%08X main_rnto() - rename(%s,%s)", - clenv->id, clenv->rnfr, path); - } - } else { - reply(fdb, 521, FALSE, "File exists"); - REGISTER_ERROR(); - } - } else { - reply(fdb, 550, FALSE, "Command syntax error"); - REGISTER_ERROR(); - } - clenv->rnfr = mmstrfree(clenv->rnfr); - } else { - reply(fdb, 503, FALSE, "RNFR expected first"); - REGISTER_ERROR(); - } - } else { - reply(fdb, 502, FALSE, "Permission denied"); - REGISTER_PERMISSION(); - REGISTER_ERROR(); - } - - return (STATE_CURRENT); -} - - -static int -main_abor(clientenv *clenv) -{ - char *args[2]; - transfermsg *msg = &(clenv->tmsg); - - if ((mm_straspl(args, clenv->buffer, 1)) == 1) { - if (clenv->rest) clenv->rest = 0; - if (clenv->rnfr) clenv->rnfr = mmstrfree(clenv->rnfr); - reply(clenv->fdb, 226, FALSE, "Aborted"); - if (transfer_request(REQ_STATUS, TRUE, clenv)) { - if (msg->ongoing) { - if (!transfer_request(REQ_ABORT, TRUE, clenv)) - DEBUG_PRINTF("main_abor", "transfer_request(REQ_ABORT)"); - } - } else - DEBUG_PRINTF("main_abor", "transfer_request(REQ_STATUS)"); - } else { - reply(clenv->fdb, 550, FALSE, "Command syntax error"); - REGISTER_ERROR(); - } - - return (STATE_CURRENT); -} - - -static int -main_dele(clientenv *clenv) -{ - fdbuf *fdb = clenv->fdb; - char path[MMPATH_MAX]; - register int i, t; - long fsize; - - if (clenv->modify) { - if (path_valid(path, NULL, clenv->home, clenv->cwd, - &clenv->buffer[4], FALSE, FALSE, FALSE)) { - if ((i = path_exists(path, &fsize, NULL, clenv->checkowner)) != - MMPATH_NONE && i != MMPATH_DENIED) { - if (i == MMPATH_FILE) t = unlink(path); - else t = rmdir(path); - if (t != -1) { - if (i == MMPATH_FILE) - treesize_edit(clenv, -fsize); - else - treesize_edit(clenv, -clenv->minfilesize); - reply(fdb, 250, FALSE, "Command successful"); - } else { - reply(fdb, 521, FALSE, "Error deleting file"); - REGISTER_ERROR(); - mmsyslog(0, LOGLEVEL, "%08X main_dele() - unlink(%s)", - clenv->id, path); - } - } else { - reply(fdb, 550, FALSE, "No such file or directory"); - REGISTER_ERROR(); - } - } else { - reply(fdb, 550, FALSE, "Command syntax error"); - REGISTER_ERROR(); - } - } else { - reply(fdb, 502, FALSE, "Permission denied"); - REGISTER_PERMISSION(); - REGISTER_ERROR(); - } - - return (STATE_CURRENT); -} - - -static int -main_rmd(clientenv *clenv) -{ - fdbuf *fdb = clenv->fdb; - char path[MMPATH_MAX]; - - if (clenv->modify) { - if (path_valid(path, NULL, clenv->home, clenv->cwd, - &clenv->buffer[3], FALSE, FALSE, FALSE)) { - if (path_exists(path, NULL, NULL, clenv->checkowner) == - MMPATH_DIR) { - if (!(rmdir(path))) { - treesize_edit(clenv, -clenv->minfilesize); - reply(fdb, 250, FALSE, "Command successful"); - } else { - reply(fdb, 521, FALSE, - "Error deleting directory (not empty?)"); - REGISTER_ERROR(); - mmsyslog(0, LOGLEVEL, "%08X main_rmd() - rmdir(%s)", - clenv->id, path); - } - } else { - reply(fdb, 550, FALSE, "No such file or directory"); - REGISTER_ERROR(); - } - } else { - reply(fdb, 550, FALSE, "No such file or directory"); - REGISTER_ERROR(); - } - } else { - reply(fdb, 502, FALSE, "Permission denied"); - REGISTER_PERMISSION(); - REGISTER_ERROR(); - } - - return (STATE_CURRENT); -} - - -static int -main_mdtm(clientenv *clenv) -{ - fdbuf *fdb = clenv->fdb; - char path[MMPATH_MAX], timebuf[16]; - register int t; - - if (path_valid(path, NULL, clenv->home, clenv->cwd, &clenv->buffer[4], - FALSE, FALSE, FALSE)) { - if ((t = path_exists(path, NULL, timebuf, clenv->checkowner)) != - MMPATH_NONE && t != MMPATH_DENIED) { - if (t == MMPATH_FILE) - reply(fdb, 213, FALSE, timebuf); - else { - reply(fdb, 550, FALSE, "Not a regular file"); - REGISTER_ERROR(); - } - } else { - reply(fdb, 550, FALSE, "No such file or directory"); - REGISTER_ERROR(); - } - } else { - reply(fdb, 550, FALSE, "No such file or directory"); - REGISTER_ERROR(); - } - - return (STATE_CURRENT); -} - - -static int -main_pwd(clientenv *clenv) -{ - char *args[2]; - - if ((mm_straspl(args, clenv->buffer, 1)) == 1) - reply(clenv->fdb, 257, FALSE, "\"%s\"", clenv->cwd); - else { - reply(clenv->fdb, 550, FALSE, "Command syntax error"); - REGISTER_ERROR(); - } - - return (STATE_CURRENT); -} - - -static int -main_mkd(clientenv *clenv) -{ - fdbuf *fdb = clenv->fdb; - char path[MMPATH_MAX]; - mode_t mode; - - if (clenv->modify) { - if (path_valid(path, NULL, clenv->home, clenv->cwd, - &clenv->buffer[3], FALSE, FALSE, FALSE) && - (mm_strlen(path) < MMPATH_MAX - 2)) { - if (path_exists(path, NULL, NULL, clenv->checkowner) == - MMPATH_NONE) { - mode = ~clenv->umask; - mode |= 0700; - if (treesize_edit(clenv, clenv->minfilesize)) { - if ((mkdir(path, mode)) != -1) { - if ((lchown(path, -1, clenv->gid)) == -1) - DEBUG_PRINTF("main_mkd", "lchown(%s)", path); - reply(fdb, 250, FALSE, "Command successful"); - } else { - reply(fdb, 521, FALSE, "Error creating directory"); - REGISTER_ERROR(); - DEBUG_PRINTF("main_mkd", "mkdir(%s)", path); - } - } else { - reply(fdb, 502, FALSE, "Quota exceeded"); - REGISTER_ERROR(); - mmsyslog(0, LOGLEVEL, "%08X Quota exceeded", clenv->id); - } - } else { - reply(fdb, 550, FALSE, "File exists"); - REGISTER_ERROR(); - } - } else { - reply(fdb, 550, FALSE, "Command syntax error"); - REGISTER_ERROR(); - } - } else { - reply(fdb, 502, FALSE, "Permission denied"); - REGISTER_PERMISSION(); - REGISTER_ERROR(); - } - - return (STATE_CURRENT); -} - - -static int -main_list(clientenv *clenv) -{ - fdbuf *fdb = clenv->fdb; - transfermsg *msg = &(clenv->tmsg); - char path[MMPATH_MAX]; - - /* Make sure that there is no ongoing transfer, and that PORT or PASV - * was used first - */ - if (transfer_request(REQ_STATUS, TRUE, clenv)) { - if (msg->port != 0) { - if (!msg->ongoing) { - if (!path_valid(path, NULL, clenv->home, clenv->cwd, - &clenv->buffer[4], TRUE, FALSE, TRUE)) { - /* We need to tell the transfer thread to not send - * anything. XXX Should we simply error here with - * invalid pathname instead? If so, the port condition - * would only be verified later on. - */ - msg->list = 3; - msg->path = NULL; - } else { - /* Normal LIST */ - msg->list = 1; - msg->path = path; - } - msg->rrate = clenv->bw_out; - msg->wrate = clenv->bw_in; - /* Send transfer request */ - if (!transfer_request(REQ_TRANSFER, TRUE, clenv)) - DEBUG_PRINTF("main_list", - "transfer_request(REQ_TRANSFER)"); - } else { - reply(fdb, 425, FALSE, "Transfer already ongoing"); - REGISTER_ERROR(); - } - } else { - reply(fdb, 425, FALSE, "Can't establish data connection"); - REGISTER_ERROR(); - } - } else { - reply(fdb, 425, FALSE, "Error"); - REGISTER_ERROR(); - DEBUG_PRINTF("main_list", "transfer_request(REQ_STATUS)"); - } - - return (STATE_CURRENT); -} - - -static int -main_nlst(clientenv *clenv) -{ - fdbuf *fdb = clenv->fdb; - transfermsg *msg = &(clenv->tmsg); - char path[MMPATH_MAX]; - - /* Make sure that there is no ongoing transfer, and that PORT or PASV - * was used first - */ - if (transfer_request(REQ_STATUS, TRUE, clenv)) { - if (msg->port != 0) { - if (!msg->ongoing) { - if (!path_valid(path, NULL, clenv->home, clenv->cwd, - &clenv->buffer[4], TRUE, FALSE, TRUE)) { - /* We need to tell the transfer thread to not send - * anything. XXX Should we error instead? - */ - msg->list = 3; - msg->path = NULL; - } else { - /* Normal LIST */ - msg->list = 2; - msg->path = path; - } - msg->rrate = clenv->bw_out; - msg->wrate = clenv->bw_in; - /* Send transfer request */ - if (!transfer_request(REQ_TRANSFER, TRUE, clenv)) - DEBUG_PRINTF("main_nlst", - "transfer_request(REQ_TRANSFER)"); - } else { - reply(fdb, 425, FALSE, "Transfer already ongoing"); - REGISTER_ERROR(); - } - } else { - reply(fdb, 425, FALSE, "Can't establish data connection"); - REGISTER_ERROR(); - } - } else { - reply(fdb, 425, FALSE, "Error"); - REGISTER_ERROR(); - DEBUG_PRINTF("main_nlst", "transfer_request(REQ_STATUS)"); - } - - return (STATE_CURRENT); -} - - -static int -main_site(clientenv *clenv) -{ - int i; - fdbuf *fdb = clenv->fdb; - char *args[5], path[MMPATH_MAX]; - mode_t mode; - - if ((i = mm_straspl(args, clenv->buffer, 4)) >= 2) { - if (!(mm_strcasecmp(args[1], "UMASK"))) { - if (clenv->modify && clenv->mumask) { - if (i == 2) - reply(fdb, 200, FALSE, "Current UMASK is %04o", - clenv->umask); - else if (i == 3) { - sscanf(args[2], "%o", &clenv->umask); - if (clenv->umask > 07000) clenv->umask = 0; - clenv->umask &= ~0700; - clenv->umask |= 07000; - reply(fdb, 200, FALSE, "UMASK set to %04o", clenv->umask); - } else { - reply(fdb, 550, FALSE, "Command syntax error"); - REGISTER_ERROR(); - } - } else { - reply(fdb, 502, FALSE, "Permission denied"); - REGISTER_PERMISSION(); - REGISTER_ERROR(); - } - } else if (!(mm_strcasecmp(args[1], "CHMOD"))) { - if (clenv->modify && clenv->mumask) { - if (i == 4) { - if (path_valid(path, NULL, clenv->home, clenv->cwd, - args[3], FALSE, FALSE, FALSE)) { - if ((i = path_exists(path, NULL, NULL, - clenv->checkowner)) - != MMPATH_NONE && i != MMPATH_DENIED) { - sscanf(args[2], "%o", &mode); - if (mode > 07000) mode = 0; - mode &= ~07000; - if (i == MMPATH_FILE) { - /* Normal file, force 6 for user/owner */ - mode &= ~0700; - mode |= 0600; - } else { - /* Directory, force 7 for user/owner */ - mode |= 0700; - } - /* On linux symbolic links don't support - * permissions - */ -#ifdef __GLIBC__ - if ((chmod(path, mode)) == -1) - mmsyslog(0, LOGLEVEL, - "%08X main_site() - chmod(%s)", - path); -#else - if ((lchmod(path, mode)) == -1) - mmsyslog(0, LOGLEVEL, - "%08X main_site() - lchmod(%s)", - path); -#endif - reply(fdb, 200, FALSE, - "CHMOD %04o command successful", - mode); - } else { - reply(fdb, 550, FALSE, - "No such file or directory"); - REGISTER_ERROR(); - } - } else { - reply(fdb, 550, FALSE, - "No such file or directory"); - REGISTER_ERROR(); - } - } else { - reply(fdb, 550, FALSE, "Command syntax error"); - REGISTER_ERROR(); - } - } else { - reply(fdb, 502, FALSE, "Permission denied"); - REGISTER_PERMISSION(); - REGISTER_ERROR(); - } - } else { - reply(fdb, 500, FALSE, "Unknown command"); - REGISTER_ERROR(); - } - } else { - reply(fdb, 500, FALSE, "Unknown command"); - REGISTER_ERROR(); - } - - return (STATE_CURRENT); -} - - -static int -main_size(clientenv *clenv) -{ - fdbuf *fdb = clenv->fdb; - char path[MMPATH_MAX]; - register int t; - long size; - - if (path_valid(path, NULL, clenv->home, clenv->cwd, &clenv->buffer[4], - FALSE, FALSE, FALSE)) { - if ((t = path_exists(path, &size, NULL, clenv->checkowner)) != - MMPATH_NONE - && t != MMPATH_DENIED) { - if (t == MMPATH_FILE) - reply(fdb, 213, FALSE, "%ld", size); - else { - reply(fdb, 550, FALSE, "Not a regular file"); - REGISTER_ERROR(); - } - } else { - reply(fdb, 550, FALSE, "No such file or directory"); - REGISTER_ERROR(); - } - } else { - reply(fdb, 550, FALSE, "No such file or directory"); - REGISTER_ERROR(); - } - - return (STATE_CURRENT); -} - - -static int -main_stat(clientenv *clenv) -{ - fdbuf *fdb = clenv->fdb; - char path[MMPATH_MAX], cpath[MMPATH_MAX], *str; - transfermsg *msg = &(clenv->tmsg); - - if (path_valid(path, cpath, clenv->home, clenv->cwd, &clenv->buffer[4], - TRUE, FALSE, FALSE)) { - /* A pathname was specified, we thus act like LIST but on control port - */ - int flags = MMLS_LONG | MMLS_GLOB, i; - - i = path_exists(path, NULL, NULL, clenv->checkowner); - if (i == MMPATH_FILE || i == MMPATH_DIR) { - reply(fdb, 211, TRUE, "Status of '%s':", cpath); - if (clenv->checkowner) - flags |= MMLS_OWNERONLY; - if (!(clenv->modify || clenv->upload)) - flags |= MMLS_READONLY; - if (!path_ls(fdb, path, clenv->user, clenv->group, flags)) - DEBUG_PRINTF("main_stat", "path_ls(%s)", path); - reply(fdb, 211, FALSE, "End of status information"); - } else { - reply(clenv->fdb, 550, FALSE, "No such file or directory"); - REGISTER_ERROR(); - } - } else { - /* Normal ftpd status report */ - reply(fdb, 211, TRUE, "FTP server status:"); - fdbprintf(fdb, " Version: %s (%s)\r\n", DAEMON_NAME, - DAEMON_VERSION); - if (clenv->c_hostname) - fdbprintf(fdb, " Connected to %s (%s)\r\n", - clenv->c_hostname, clenv->c_ipaddr); - else - fdbprintf(fdb, " Connected to %s\r\n", clenv->c_ipaddr); - - { - long logins; - - pth_mutex_acquire(&clenv->unode->lock, FALSE, NULL); - logins = clenv->unode->logins; - pth_mutex_release(&clenv->unode->lock); - fdbprintf(fdb, " Logged in as %s (%ld concurrent logins)\r\n", - clenv->user, logins); - } - - fdbwrite(fdb, " TYPE: ", 10); - /* An index would not be worth it for three possibilities */ - switch (clenv->type) { - case TYPE_ASCII: - str = "ASCII, FORM: Nonprint"; - break; - case TYPE_IMAGE: - str = "Image"; - break; - case TYPE_LOCAL: - str = "Local 8"; - break; - default: - str = "UNKNOWN"; - break; - } - fdbputs(fdb, str); - - fdbwrite(fdb, "; STRUcture: File; transfer MODE: Stream\r\n", 42); - fdbputs(fdb, clenv->anonymous ? " Class: guest, Type: GUEST\r\n" : - " Class: real, Type: REAL\r\n"); - fdbwrite(fdb, " Check PORT/LPRT/EPRT commands: enabled\r\n", 44); - fdbprintf(fdb, " Idle timeout: %d, maximum timeout: disabled\r\n", - clenv->timeout / 1000); - fdbwrite(fdb, " Maximum file size: unlimited\r\n", 34); - fdbwrite(fdb, " MotD file: motd\r\n", 21); - fdbprintf(fdb, " Modify commands (APPE, CHMOD, DELE, MKD, RMD, " - "RNFR, RNTO, UMASK): %s", - clenv->modify ? "enabled" : "disabled"); - fdbprintf(fdb, "\r\n Modify umask (UMASK): %s", clenv->mumask ? - "enabled" : "disabled"); - fdbprintf(fdb, "\r\n Upload commands (APPE, STOR, STOU): %s", - clenv->upload ? "enabled" : "disabled"); - fdbwrite(fdb, "\r\n Sanitize file names: enabled\r\n", 36); - fdbwrite(fdb, " PASV/LPSV/EPSV connections: enabled\r\n", 41); - fdbwrite(fdb, " PORT/LPRT/EPRT connections: enabled\r\n", 41); - - fdbwrite(fdb, " Home directory size limit: ", 31); - if (clenv->maxhomesize) { - off_t size; - - fdbprintf(fdb, " %lld bytes", (int64_t)clenv->maxhomesize); - fdbwrite(fdb, "\r\n Current home directory size: ", 36); - pth_mutex_acquire(&clenv->unode->lock, FALSE, NULL); - size = clenv->unode->homesize; - pth_mutex_release(&clenv->unode->lock); - fdbprintf(fdb, "%lld bytes", (int64_t)size); - } else - fdbwrite(fdb, "disabled", 8); - - fdbwrite(fdb, "\r\n Control rate get limit: ", 30); - if (CONF.BANDWIDTH_OUT) - fdbprintf(fdb, "%ldKB/sec", CONF.BANDWIDTH_OUT); - else - fdbwrite(fdb, "disabled", 8); - - fdbwrite(fdb, "\r\n Control rate put limit: ", 30); - if (CONF.BANDWIDTH_IN) - fdbprintf(fdb, "%ldKB/sec", CONF.BANDWIDTH_IN); - else - fdbwrite(fdb, "disabled", 8); - - fdbwrite(fdb, "\r\n Data rate get limit: ", 30); - if (clenv->bw_out) - fdbprintf(fdb, "%ldKB/sec", clenv->bw_out); - else - fdbwrite(fdb, "disabled", 8); - - fdbwrite(fdb, "\r\n Data rate put limit: ", 30); - if (clenv->bw_in) - fdbprintf(fdb, "%ldKB/sec", clenv->bw_in); - else - fdbwrite(fdb, "disabled", 8); - - if (transfer_request(REQ_STATUS, TRUE, clenv)) - fdbprintf(fdb, - "\r\n Bytes transfered: (control in: %lld out: " - "%lld),\r\n (data in: %lld " - "out: %lld),\r\n (files in: " - "%lld, out: %lld)", - FDBBYTESR(fdb), FDBBYTESW(fdb), msg->rbytes, - msg->wbytes, msg->ulfiles, msg->dlfiles); - else - return (STATE_ERROR); - - fdbprintf(fdb, "\r\n Umask: %04o\r\n", clenv->umask); - reply(fdb, 211, FALSE, "End of status information"); - } - - return (STATE_CURRENT); -} - - - - -/* MAIN */ -int -main(int argc, char **argv) -{ - char *conf_file = "/etc/mmftpd.conf"; - pid_t uid; - gid_t *gids; - int ngids, ret = EXIT_SUCCESS; - long facility; - cres_t cres; - carg_t *cargp; - carg_t cargs[] = { - {CAT_STR, CAF_NONE, 1, 255, "CHROOT_DIR", CONF.CHROOT_DIR}, - {CAT_STR, CAF_NONE, 1, 255, "PASSWD_FILE", CONF.PASSWD_FILE}, - {CAT_STR, CAF_NONE, 1, 255, "PID_PATH", CONF.PID_PATH}, - {CAT_STR, CAF_NONE, 1, 31, "USER", CONF.USER}, - {CAT_STR, CAF_NONE, 1, 255, "GROUPS", CONF.GROUPS}, - {CAT_STR, CAF_NONE, 1, 31, "LOG_FACILITY", CONF.LOG_FACILITY}, - {CAT_STR, CAF_NONE, 1, 1023, "SERVER_NAMES", CONF.SERVER_NAMES}, - {CAT_STR, CAF_NONE, 1, 1023, "LISTEN_IPS", CONF.LISTEN_IPS}, - {CAT_STR, CAF_NONE, 1, 255, "WELCOME_FILE", CONF.WELCOME_FILE}, - {CAT_STR, CAF_NONE, 1, 255, "MOTD_FILE", CONF.MOTD_FILE}, - {CAT_STR, CAF_NONE, 1, 63, "DISPLAY_FILE", CONF.DISPLAY_FILE}, - {CAT_STR, CAF_NONE, 1, 1023, "PASV_REMAP", CONF.PASV_REMAP}, - {CAT_VAL, CAF_NONE, 1, 32, "ASYNC_PROCESSES", &CONF.ASYNC_PROCESSES}, - {CAT_VAL, CAF_NONE, 1, 9999, "ALLOC_BUFFERS", &CONF.ALLOC_BUFFERS}, - {CAT_VAL, CAF_NONE, 0, 4, "LOG_LEVEL", &CONF.LOG_LEVEL}, - {CAT_VAL, CAF_NONE, 1, 65535, "LISTEN_PORT", &CONF.LISTEN_PORT}, - {CAT_VAL, CAF_NONE, 1, 1000, "MAX_ERRORS", &CONF.MAX_ERRORS}, - {CAT_VAL, CAF_NONE, 1, 99999, "MAX_IPS", &CONF.MAX_IPS}, - {CAT_VAL, CAF_NONE, 1, 99999, "MAX_PER_IP", &CONF.MAX_PER_IP}, - {CAT_VAL, CAF_NONE, 0, 99999, "CONNECTION_RATE", - &CONF.CONNECTION_RATE}, - {CAT_VAL, CAF_NONE, 1, 99999, "CONNECTION_PERIOD", - &CONF.CONNECTION_PERIOD}, - {CAT_VAL, CAF_NONE, 1, 99999, "CONTROL_TIMEOUT", - &CONF.CONTROL_TIMEOUT}, - {CAT_VAL, CAF_NONE, 1, 99999, "DATA_TIMEOUT", &CONF.DATA_TIMEOUT}, - {CAT_VAL, CAF_NONE, 0, 99999, "BANDWIDTH_IN", &CONF.BANDWIDTH_IN}, - {CAT_VAL, CAF_NONE, 0, 99999, "BANDWIDTH_OUT", &CONF.BANDWIDTH_OUT}, - {CAT_VAL, CAF_NONE, 0, 99999, "GBANDWIDTH_IN", &CONF.GBANDWIDTH_IN}, - {CAT_VAL, CAF_NONE, 0, 99999, "GBANDWIDTH_OUT", &CONF.GBANDWIDTH_OUT}, - {CAT_VAL, CAF_NONE, 4, 256, "REMEMBER_CWDS", &CONF.REMEMBER_CWDS}, - {CAT_VAL, CAF_NONE, 1025, 65535, "PASV_RANGE_MIN", - &CONF.PASV_RANGE_MIN}, - {CAT_VAL, CAF_NONE, 1025, 65535, "PASV_RANGE_MAX", - &CONF.PASV_RANGE_MAX}, - {CAT_BOOL, CAF_NONE, 0, 0, "RESOLVE_HOSTS", &CONF.RESOLVE_HOSTS}, - {CAT_BOOL, CAF_NONE, 0, 0, "DELAY_ON_ERROR", &CONF.DELAY_ON_ERROR}, - {CAT_BOOL, CAF_NONE, 0, 0, "PASV_RANGE", &CONF.PASV_RANGE}, - {CAT_BOOL, CAF_NONE, 0, 0, "STATFAIL_LOGIN", &CONF.STATFAIL_LOGIN}, - {CAT_BOOL, CAF_NONE, 0, 0, "STATFAIL_PASSWORD", - &CONF.STATFAIL_PASSWORD}, - {CAT_BOOL, CAF_NONE, 0, 0, "STATFAIL_PERMISSION", - &CONF.STATFAIL_PERMISSION}, - {CAT_BOOL, CAF_NONE, 0, 0, "STATFAIL_PORT", &CONF.STATFAIL_PORT}, - {CAT_END, CAF_NONE, 0, 0, NULL, NULL} - }; - cmap_t cmap[] = { - {"LOG_AUTH", LOG_AUTH}, - {"LOG_AUTHPRIV", LOG_AUTHPRIV}, - {"LOG_CRON", LOG_CRON}, - {"LOG_DAEMON", LOG_DAEMON}, - {"LOG_FTP", LOG_FTP}, - {"LOG_LPR", LOG_LPR}, - {"LOG_MAIL", LOG_MAIL}, - {"LOG_NEWS", LOG_NEWS}, - {"LOG_SYSLOG", LOG_SYSLOG}, - {"LOG_USER", LOG_USER}, - {"LOG_UUCP", LOG_UUCP}, - {NULL, 0} - }; - struct async_func afuncs[] = { - {async_checkpw, sizeof(struct async_checkpw_msg)}, - {async_treesize, sizeof(struct async_treesize_msg)}, - {async_getuserline, sizeof(struct async_getuserline_msg)}, - {NULL, 0} - }; - mmstat_t vstat; - - /* Advertize */ - printf("\r\n+++ %s (%s)\r\n\r\n", DAEMON_NAME, DAEMON_VERSION); - - /* Set defaults */ - *CONF.CHROOT_DIR = '\0'; - mm_strcpy(CONF.PASSWD_FILE, "/etc/mmftpdpasswd"); - mm_strcpy(CONF.PID_PATH, "/var/run/mmftpd.pid"); - mm_strcpy(CONF.USER, "mmftpd"); - mm_strcpy(CONF.GROUPS, "mmftpd,mmstat"); - mm_strcpy(CONF.LOG_FACILITY, "LOG_AUTHPRIV"); - mm_strcpy(CONF.SERVER_NAMES, "ftp.localhost"); - mm_strcpy(CONF.LISTEN_IPS, "127.0.0.1"); - *CONF.PASV_REMAP = '\0'; - mm_strcpy(CONF.DISPLAY_FILE, "README"); - *CONF.WELCOME_FILE = '\0'; - *CONF.MOTD_FILE = '\0'; - CONF.ASYNC_PROCESSES = 3; - CONF.ALLOC_BUFFERS = 1; - CONF.LOG_LEVEL = 3; - CONF.LISTEN_PORT = 21; - CONF.MAX_ERRORS = 16; - CONF.MAX_IPS = 64; - CONF.MAX_PER_IP = 1; - CONF.CONNECTION_RATE = 10; - CONF.CONNECTION_PERIOD = 30; - CONF.CONTROL_TIMEOUT = 300; - CONF.DATA_TIMEOUT = 300; - CONF.BANDWIDTH_IN = 1; - CONF.BANDWIDTH_OUT = 1; - CONF.GBANDWIDTH_IN = 0; - CONF.GBANDWIDTH_OUT = 0; - CONF.REMEMBER_CWDS = 16; - CONF.PASV_RANGE_MIN = 1025; - CONF.PASV_RANGE_MAX = 65534; - CONF.RESOLVE_HOSTS = FALSE; - CONF.DELAY_ON_ERROR = FALSE; - CONF.PASV_RANGE = FALSE; - CONF.STATFAIL_LOGIN = FALSE; - CONF.STATFAIL_PASSWORD = FALSE; - CONF.STATFAIL_PERMISSION = FALSE; - CONF.STATFAIL_PORT = FALSE; - - /* Read config file */ - if (argc == 2) - conf_file = argv[1]; - if (!mmreadcfg(&cres, cargs, conf_file)) { - /* Error parsing configuration file, report which */ - printf("\nError parsing '%s'\n", conf_file); - printf("Error : %s\n", mmreadcfg_strerr(cres.CR_Err)); - if (*(cres.CR_Data)) printf("Data : %s\n", cres.CR_Data); - if ((cargp = cres.CR_Keyword) != NULL) { - printf("Keyword: %s\n", cargp->CA_Keyword); - printf("Minimum: %ld\n", cargp->CA_Min); - printf("Maximum: %ld\n", cargp->CA_Max); - } - if (cres.CR_Line != -1) - printf("Line : %d\n", cres.CR_Line); - printf("\n"); - exit(EXIT_FAILURE); - } - - /* Post parsing */ - if (!mmmapstring(cmap, CONF.LOG_FACILITY, &facility)) { - printf("\nUnknown syslog facility %s\n\n", CONF.LOG_FACILITY); - exit(EXIT_FAILURE); - } - LOGLEVEL = CONF.LOG_LEVEL; - if ((uid = mmgetuid(CONF.USER)) == -1) { - printf("\nUnknown user '%s'\n\n", CONF.USER); - exit(EXIT_FAILURE); - } - if (!(gids = mmgetgidarray(&ngids, CONF.GROUPS))) { - printf("\nOne of following groups unknown: '%s'\n\n", CONF.GROUPS); - exit(EXIT_FAILURE); - } - if (CONF.PASV_RANGE_MAX <= CONF.PASV_RANGE_MIN) { - printf("\nPASV_RANGE_MAX should be higher than PASV_RANGE_MIN!\n\n"); - exit(EXIT_FAILURE); - } - if (*CONF.PASV_REMAP != '\0') { - if (!pasv_remap_parse()) - exit(EXIT_FAILURE); - } - - /* Finally init everything */ - openlog(DAEMON_NAME, LOG_PID | LOG_NDELAY, facility); - -#ifndef NODROPPRIVS - if ((getuid())) { - printf("\nOnly the super user may start this daemon\n\n"); - mmsyslog(0, LOGLEVEL, "* Only superuser can start me"); - exit(EXIT_FAILURE); - } -#else /* NODROPPRIVS */ - if ((getuid()) == 0) { - printf("\nCompiled with NODROPPRIVS, refusing to run as uid 0\n\n"); - mmsyslog(0, LOGLEVEL, "* NODROPPRIVS, refusing to run as uid 0"); - exit(EXIT_FAILURE); - } -#endif /* !NODROPPRIVS */ - - /* Things we want to do now in case we chroot(2) */ - tzset(); - res_init(); - mmstat_initialize(); - mmstat_init(&vstat, TRUE, TRUE); - mmstat_transact(&vstat, TRUE); - mmstat(&vstat, STAT_DELETE, 0, "mmftpd|current|connections"); - mmstat(&vstat, STAT_DELETE, 0, "mmftpd|who|*"); - mmstat_transact(&vstat, FALSE); - - /* Become a daemon, optionally chrooting */ - make_daemon(CONF.PID_PATH, CONF.CHROOT_DIR); - - /* Part of mmserver(3)'s async system which needs to be initialized before - * any threading system - */ - async_init(afuncs, CONF.ASYNC_PROCESSES, uid, gids, ngids); - - /* Threading system initialization */ - pth_init(); - - /* Part of mmserver(3)'s async system which has to be initialized after - * the threading system - */ - async_init_pth(); - - pth_mutex_init(&lusers_lock); - pth_mutex_init(&clenvs_lock); - pth_mutex_init(&mutexes_lock); - pth_mutex_init("a_lock); - - srandom(getpid() + time(NULL)); - - /* We use those for our transfer ASYNC thread, joinable because when we - * eventually request it to quit via a message we want to make sure it - * ended properly waiting for it with pth_join(). I have decided to use - * a second thread dedicated to file transfers, this way I can respect - * the RFC on listening to ABOR and STAT easily on the control connection - * while transfers are ongoing, with ease and modularity. - */ - tthreadattr = pth_attr_new(); - pth_attr_set(tthreadattr, PTH_ATTR_JOINABLE, TRUE); - - /* Initialize our pools */ - /* Client environment nodes */ - pool_init(&clenvs_pool, "clenvs_pool", malloc, free, NULL, NULL, - sizeof(clientenv), - (65536 * CONF.ALLOC_BUFFERS) / sizeof(clientenv), 0, 0); - /* User-shared state nodes */ - pool_init(&lusers_pool, "lusers_pool", malloc, free, NULL, NULL, - sizeof(lusernode), - (32768 * CONF.ALLOC_BUFFERS) / sizeof(lusernode), 0, 0); - hashtable_init(&lusers_table, "lusers_table", HT_DEFAULT_CAPACITY, - HT_DEFAULT_FACTOR, malloc, free, mm_memcmp, mm_memhash32, TRUE); - /* Mutexes pool for mmfd */ - pool_init(&mutexes_pool, "mutexes_pool", malloc, free, NULL, NULL, - sizeof(struct mutexnode), - (16384 * CONF.ALLOC_BUFFERS) / sizeof(struct mutexnode), 0, 0); - /* FIFO buffer nodes */ - if (*CONF.DISPLAY_FILE) - pool_init(&fifos_pool, "fifos_pool", malloc, free, NULL, NULL, - sizeof(struct fifonode) + - (CONF.REMEMBER_CWDS * sizeof(u_int64_t)), - CONF.ALLOC_BUFFERS * 64, 0, 0); - /* Tree size cache */ - pool_init("a_pool, "quota_pool", malloc, free, NULL, NULL, - sizeof(struct quotanode), 16384 / sizeof(struct quotanode), 0, 0); - hashtable_init("a_table, "quota_table", HT_DEFAULT_CAPACITY, - HT_DEFAULT_FACTOR, malloc, free, mm_memcmp, mm_memhash32, TRUE); - - if (hash_commands(commands, 3) && POOL_VALID(&clenvs_pool) && - POOL_VALID(&lusers_pool) && HASHTABLE_VALID(&lusers_table) && - POOL_VALID(&mutexes_pool) && POOL_VALID("a_pool) && - HASHTABLE_VALID("a_table) && - (!*CONF.DISPLAY_FILE || POOL_VALID(&fifos_pool))) { - fdbcinit(&gfdf, &fdbc, CONF.GBANDWIDTH_IN * 1024, - CONF.GBANDWIDTH_OUT * 1024); - /* Finally start our TCP main daemon */ - tcp_server("421 Server too busy, try again\r\n", CONF.SERVER_NAMES, - CONF.LISTEN_IPS, uid, gids, ngids, CONF.MAX_IPS, - CONF.MAX_PER_IP, CONF.CONNECTION_RATE, CONF.CONNECTION_PERIOD, - CONF.CONTROL_TIMEOUT, CONF.LISTEN_PORT, CONF.RESOLVE_HOSTS, - handleclient); - fdbcdestroy(&fdbc); - } else { - mmsyslog(0, LOGLEVEL, "* Out of memory"); - ret = EXIT_FAILURE; - } - - mmfreegidarray(gids); - if (HASHTABLE_VALID(&command_table)) - hashtable_destroy(&command_table, FALSE); - if (POOL_VALID(&command_pool)) - pool_destroy(&command_pool); - if (POOL_VALID(&fifos_pool)) - pool_destroy(&fifos_pool); - if (POOL_VALID(&mutexes_pool)) - pool_destroy(&mutexes_pool); - if (POOL_VALID(&lusers_pool)) - pool_destroy(&lusers_pool); - if (HASHTABLE_VALID(&lusers_table)) - hashtable_destroy(&lusers_table, FALSE); - if (POOL_VALID(&clenvs_pool)) - pool_destroy(&clenvs_pool); - if (POOL_VALID("a_pool)) - pool_destroy("a_pool); - if (HASHTABLE_VALID("a_table)) - hashtable_destroy("a_table, FALSE); - - exit(ret); -} - - -/* Used to initialize command hash table */ -static bool -hash_commands(struct command *cmd, size_t min) -{ - int i; - - /* We do not care for any unfreed resources, the program will free them - * and exit if we return FALSE. - */ - if (!pool_init(&command_pool, "command_pool", malloc, free, NULL, NULL, - sizeof(struct commandnode), 64, 1, 0) || - !hashtable_init(&command_table, "command_table", 64, 1, malloc, - free, commandnode_keycmp, commandnode_keyhash, TRUE)) - return FALSE; - - for (i = 0; cmd->name != NULL; cmd++, i++) { - struct commandnode *nod; - - if ((nod = (struct commandnode *)pool_alloc(&command_pool, FALSE)) - == NULL) - return FALSE; - if ((nod->hash = mm_strpack32(cmd->name, min)) == 0) - return FALSE; - nod->command = cmd; - nod->index = i; - if (!hashtable_link(&command_table, (hashnode_t *)nod, &nod->hash, - sizeof(u_int32_t), TRUE)) { - DEBUG_PRINTF("hash_commands", "hashtable_link(%s)", cmd->name); - return FALSE; - } - } - - return TRUE; -} - - -/* A quick hashtable_hash() replacement which already deals with unique - * 32-bit values - */ -/* ARGSUSED */ -static u_int32_t -commandnode_keyhash(const void *data, size_t len) -{ - return *((u_int32_t *)data); -} - - -/* A quick memcmp() replacement which only needs to compare two 32-bit values - */ -/* ARGSUSED */ -static int -commandnode_keycmp(const void *src, const void *dst, size_t len) -{ - return *((u_int32_t *)src) - *((u_int32_t *)dst); -} - - -/* Function used to return standard RFC result strings to the client, - * and returns FALSE on error. - * NOTE: As we are no longer calling write() directly, but fdbprintf() - * buffering function instead, it is no longer necessary to check the reply() - * return value each time it is called. We made sure to ignore SIGPIPE, - * and we let the main state switcher loop check connection state via - * fdbgets(). - */ -static bool -reply(fdbuf *fdb, int code, bool cont, char *fmt, ...) -{ - char buf[1024]; - va_list arg_ptr; - bool ok = TRUE; - - if (cont) snprintf(buf, 6, "%03d-", code); - else snprintf(buf, 6, "%03d ", code); - - if (fmt) { - va_start(arg_ptr, fmt); - vsnprintf(&buf[4], 1018, fmt, arg_ptr); - va_end(arg_ptr); - if (!fdbprintf(fdb, "%s\r\n", buf)) ok = FALSE; - /* XXX Should be able to show the clenv->id */ - mmsyslog(3, LOGLEVEL, "> %s", buf); - } else if (!fdbprintf(fdb, "%s\r\n", buf)) - ok = FALSE; - - return (ok); -} - - -/* This function reads a valid (non-blank, non-comment) line from fdb, - * and parses the columns of that line, filling array with the pointers - * to each trim string. It returns 0 if the number of columns of that line - * is out of mincols,maxcols bounds, the number of columns of the line on - * success, or -1 on EOF. str string provided should be at least 256 bytes - * long. - */ -static int -readconfline(char *str, fdbuf *fdb, int mincols, int maxcols, char **array) -{ - char *ptr; - int len, col; - - while ((len = fdbgets(fdb, str, 255, FALSE)) > -1) { - if (len) { - /* Comment or empty line? */ - for (ptr = str; *ptr != '\0' && (*ptr == ' ' || *ptr == '\t'); - ptr++) ; - if (*ptr == '\0' || *ptr == '#' || *ptr == ';') - continue; - - col = mm_straspl(array, str, maxcols); - - if ((col <= maxcols) && (col >= mincols)) - return (col); - - return (0); - } - } - - return (-1); -} - - -/* Given a command string, it replaces by spaces any - parameters as some - * broken clients tend to specify some to FTP commands, notably with LIST. - * Note that we only strip options specified before anything else, to permit - * filenames using space followed by -. Of course this function should be - * called on a string starting with optional parameters, after the command. - */ -static void -stripoptions(char *str) -{ - bool space; - - /* First locate end of command */ - for (; *str != '\0' && !isspace(*str); str++) ; - - if (*str != '\0') { - space = FALSE; - while (*str != '\0') { - /* Find first printable char */ - for (; *str != '\0' && isspace(*str); str++) - space = TRUE; - /* Is it an option? */ - if (space && *str == '-') { - /* Option found, replace chars by spaces until not a printable - * char - */ - space = FALSE; - while (*str != '\0' && !isspace(*str)) - *str++ = ' '; - } else - break; - } - } -} - - -/* This function is given an absolute path to a directory which the user - * has access to, and calculates the size of it's whole contents in bytes. - * Returns size of total contents, or -1 on error. - * NOTE: alloca() allocates using the stack, contrary to malloc(). Any stack - * the function uses is automatically freed when function ends, freeing it - * using libc free(3) would be a bug. - * XXX This function can take alot of stack space, especially that it - * allocates strings on the stack as well. I could provide a safer function - * in this respect if it was proven necessary. This currently has the - * advantage of being quite fast. Moreover, it is not called by the PTH - * threads, which have a static stack buffer size, but rather from our - * non-threaded asynchroneous processes, which not only have their stack - * size managed by the OS, but also can provide some support for SMP systems. - * If we called this from within a PTH thread, it would be a vulnerability. - * XXX I actually should provide a custom stack of strings for this. - */ -static off_t -recursive_treesize(char *path, off_t minfilesize) -{ - off_t size = 0, tmp; - DIR *dh; - struct dirent *de; - struct stat st; - int len; - char *newpath; - - len = mm_strlen(path); - len--; - if (len && path[len] == '/') path[len] = '\0'; - - if ((dh = opendir(path))) { - - while ((de = readdir(dh)) != NULL) { - - if ((mm_strcmp(de->d_name, ".")) - && (mm_strcmp(de->d_name, ".."))) { - len = mm_strlen(path) + mm_strlen(de->d_name) + 2; - if ((newpath = (char *)alloca(len + 2))) { - snprintf(newpath, len, "%s/%s", path, de->d_name); - if (!(lstat(newpath, &st))) { - if (st.st_size < minfilesize) size += minfilesize; - else size += st.st_size; - - if (S_ISDIR(st.st_mode)) { - if ((tmp = recursive_treesize(newpath, - minfilesize)) != -1) - size += tmp; - else { - closedir(dh); - return (-1); - } - } - - } - } else { - closedir(dh); - return (-1); - } - } - - } - - closedir(dh); - } else - return (-1); - - return (size); -} - - -/* This function permits easy editing and checking around the current user's - * home directory. An optional modifyer is provided (negative or positive - * number), which is applied to the current limit if permitted. TRUE is - * returned on success or FALSE if limits would be exceeded. - */ -static bool -treesize_edit(clientenv *clenv, off_t modify) -{ - off_t cur; - bool ret = TRUE; - - if (clenv->maxhomesize && modify != 0) { - pth_mutex_acquire(&clenv->unode->lock, FALSE, NULL); - cur = clenv->unode->homesize; - cur += modify; - if (cur < 0) - cur = 0; - if (cur <= clenv->maxhomesize) - clenv->unode->homesize = cur; - else - ret = FALSE; - pth_mutex_release(&clenv->unode->lock); - } - - return (ret); -} - - -/* Check if a user is in our passwd file, if so fill in clenv. - * Also sets the current directory to / - * XXX It now would be possible to do this asynchroneously using another - * process. Well at least, part of it, like getting the user data line. - * Possibly even along with an index array of offsets to the various fields. - */ -static bool -checkuser(clientenv *clenv, const char *name) -{ - char *line, *columns[CONF_COLUMNS + 1]; - - if (clenv->user) clenv->user = mmstrfree(clenv->user); - if (clenv->passwd) clenv->passwd = mmstrfree(clenv->passwd); - if (clenv->home) clenv->home = mmstrfree(clenv->home); - if (clenv->group) clenv->group = mmstrfree(clenv->group); - - if (getuserline(clenv, columns, &line, name)) { - if ((clenv->user = mmstrdup(columns[CONF_USER]))) { - if ((clenv->passwd = mmstrdup(columns[CONF_PASS]))) { - if ((clenv->home = mmstrdup(columns[CONF_HOME]))) { - if ((clenv->group = mmstrdup(columns[CONF_GROUP]))) { - /* XXX This possibly should also be asynchroneous */ - if ((clenv->gid = mmgetgid(columns[CONF_GROUP])) - != -1) { - - mm_strcpy(clenv->cwd, "/"); - - /* Boolean values */ -#define TOBOOL(i) ((i) != 0 ? TRUE : FALSE) - clenv->stats = - TOBOOL(atoi(columns[CONF_STATS])); - clenv->checkowner = - TOBOOL(atoi(columns[CONF_CHKOWN])); - clenv->upload = - TOBOOL(atoi(columns[CONF_UPLOAD])); - clenv->modify = - TOBOOL(atoi(columns[CONF_MODIFY])); - clenv->mumask = - TOBOOL(atoi(columns[CONF_MUMASK])); -#undef TOBOOL - - /* mode_t */ - clenv->umask = - (mode_t)strtoul(columns[CONF_UMASK], - NULL, 8); - if (clenv->umask > 07000) - clenv->umask = 0; - clenv->umask &= ~0700; - clenv->umask |= 07000; - - /* long */ - clenv->maxlogins = atol(columns[CONF_MAXLOGINS]); - - /* off_t */ - clenv->maxhomesize = - (off_t)strtoull(columns[CONF_MAXHOMESIZE], - NULL, 10); - clenv->maxhomesize *= 1024; - clenv->minfilesize = - (off_t)strtoull(columns[CONF_MINFILESIZE], - NULL, 10); - - /* int */ - clenv->bw_in = atoi(columns[CONF_BW_IN]); - clenv->bw_out = atoi(columns[CONF_BW_OUT]); - - return (TRUE); - } else - mmsyslog(0, LOGLEVEL, - "checkuser() - Unknown group %s", - columns[CONF_GROUP]); - clenv->group = mmstrfree(clenv->group); - } - clenv->home = mmstrfree(clenv->home); - } - clenv->passwd = mmstrfree(clenv->passwd); - } - clenv->user = mmstrfree(clenv->user); - } - DEBUG_PRINTF("checkuser", "mmstrdup()"); - } - - return (FALSE); -} - - -/* Allocate and prepare a clenv. Returns NULL on error. Because what we are - * doing here is rather fast, I currently see no use for mmpool(3)'s - * constructor/destructor support here. mmstat_init() only initializes a few - * fields of the supplied structure. Moreover, most of the fields of the - * clientenv structure need to be reset to a clean state for each client (see - * free_clientenv()). - */ -static clientenv * -alloc_clientenv(void) -{ - clientenv *clenv; - struct fifonode *fnode = NULL; - - pth_mutex_acquire(&clenvs_lock, FALSE, NULL); - clenv = (clientenv *)pool_alloc(&clenvs_pool, TRUE); - if (*CONF.DISPLAY_FILE) - fnode = (struct fifonode *)pool_alloc(&fifos_pool, FALSE); - pth_mutex_release(&clenvs_lock); - - if (clenv) { - if (!(*CONF.DISPLAY_FILE) || fnode) { - if (fnode) { - clenv->fifobuf = fnode; - FIFO_INIT(&clenv->visited, fnode->buf, CONF.REMEMBER_CWDS); - } else clenv->fifobuf = NULL; - mmstat_init(&clenv->vstat, TRUE, TRUE); - mmstat_init(&clenv->pstat, TRUE, FALSE); - return (clenv); - } - free_clientenv(clenv); - } else { - if (fnode) { - pth_mutex_acquire(&clenvs_lock, FALSE, NULL); - pool_free((pnode_t *)fnode); - pth_mutex_release(&clenvs_lock); - } - } - - return (NULL); -} - - -/* Frees all resources allocated by a clenv */ -static clientenv * -free_clientenv(clientenv *clenv) -{ - struct fifonode *fnode = NULL; - char *home = NULL; - - if (clenv->fifobuf) fnode = clenv->fifobuf; - if (clenv->user) mmstrfree(clenv->user); - if (clenv->tuser) mmstrfree(clenv->tuser); - if (clenv->passwd) mmstrfree(clenv->passwd); - if (clenv->home) home = clenv->home; - if (clenv->group) mmstrfree(clenv->group); - if (clenv->rnfr) mmstrfree(clenv->rnfr); - if (clenv->unode) { - bool zero = FALSE; - - pth_mutex_acquire(&lusers_lock, FALSE, NULL); - pth_mutex_acquire(&clenv->unode->lock, FALSE, NULL); - if ((--clenv->unode->logins) < 1) - zero = TRUE; - pth_mutex_release(&clenv->unode->lock); - if (zero) { - hashtable_unlink(&lusers_table, (hashnode_t *)clenv->unode); - /* May be required by some POSIX thread implementations */ - /* pth_mutex_destroy(&clenv->unode->lock); */ - - /* We now need to either update the cache entry for this home - * directory size or to create a new entry for it, but only if - * the user had quota limits. - */ - if (clenv->maxhomesize != 0 && home != NULL) { - size_t len; - quotanode *qnod; - time_t t; - - /* Note: mmstrdup() only allows strings of 255 characters max, - * so we can assume that the admin-supplied path is safe here. - */ - len = mm_strlen(home) + 1; - t = time(NULL); - pth_mutex_acquire("a_lock, FALSE, NULL); - if ((qnod = (quotanode *)hashtable_lookup("a_table, home, - len)) != NULL) { - qnod->homesize = clenv->unode->homesize; - qnod->updated = t; - } else { - if ((qnod = (quotanode *)pool_alloc("a_pool, FALSE)) - != NULL) { - mm_memcpy(qnod->dir, home, len); - qnod->homesize = clenv->unode->homesize; - qnod->updated = t; - hashtable_link("a_table, (hashnode_t *)qnod, - qnod->dir, len, FALSE); - } - } - pth_mutex_release("a_lock); - } - pool_free((pnode_t *)clenv->unode); - } - pth_mutex_release(&lusers_lock); - } - - pth_mutex_acquire(&clenvs_lock, FALSE, NULL); - if (fnode != NULL) pool_free((pnode_t *)fnode); - if (home != NULL) mmstrfree(home); - pool_free((pnode_t *)clenv); - pth_mutex_release(&clenvs_lock); - - return (NULL); -} - - -/* Starts the transfer thread of the client thread */ -static bool -start_transfer_thread(clientenv *clenv) -{ - /* I do not understand why libpth doesn't provide a function to set the - * reply port, or to init the message structure. Moreover, it theoretically - * shouldn't need the message size if it simply swaps messages around port - * linked list FIFOs via the message node. This currently seems the best - * way to go about it - */ - clenv->tmsg.msg.m_size = sizeof(transfermsg); - clenv->tmsg.msg.m_replyport = clenv->rport; - - /* - if ((clenv->tthread = pth_spawn(tthreadattr, (void *)transferthread, - clenv))) { - if (transfer_request(REQ_NONE, TRUE, clenv)) return (TRUE); - pth_join(clenv->tthread, NULL); - } - */ - thread_object_call(NULL, transferthread, clenv); - if (transfer_request(REQ_NONE, TRUE, clenv)) - return TRUE; - - return (FALSE); -} - - -/* Orders the transfer thread to quit, and waits until it exits */ -static void -stop_transfer_thread(clientenv *clenv) -{ - if (clenv->sport) { - /* Send a quit request and wait for reply */ - if (!transfer_request(REQ_QUIT, TRUE, clenv)) - DEBUG_PRINTF("stop_transfer_thread", - "transfer_request(REQ_QUIT)"); - /* If the transfer thread refused to quit for whatever reason - * (unlikely) this could freeze the current thread indefinitely. - * Join thread as we want to make sure it exits before we do. - */ - /* - pth_join(clenv->tthread, NULL); - */ - } -} - - -/* Can be used to either send a request to our transfer thread, - * wait for results from it, or both. Note that this will wait for a maximum - * delay of 120 seconds for an incoming results reply message. This normally - * should never occur however. - */ -static bool -transfer_request(int req, bool waitreply, clientenv *clenv) -{ - int res = FALSE; - transfermsg *msg = &(clenv->tmsg); - pth_event_t ring; - - if (req != REQ_NONE) { - msg->request = req; - msg->result = FALSE; - pth_msgport_put(clenv->sport, (pth_message_t *)msg); - } - if (waitreply) { - ring = pth_event(PTH_EVENT_TIME, pth_timeout(120, 0)); - pth_event_concat(clenv->rring, ring, NULL); - for (;;) { - if ((pth_wait(clenv->rring))) { - if ((pth_event_occurred(ring))) { - /* Timeout occured waiting for reply */ - res = FALSE; - break; - } - if ((pth_event_occurred(clenv->rring))) { - if ((msg = (transfermsg *)pth_msgport_get(clenv->rport))) { - res = msg->result; - break; - } - } - } - } - pth_event_isolate(clenv->rring); - pth_event_free(ring, PTH_FREE_ALL); - } else - res = TRUE; - - return (res); -} - - -/* Reads a text file and outputs it's contents to specified fdb. - * XXX It would be possible to reuse the fdb handle for performance, or at - * least to use mmpool(3) constructor/destructor system to create/destroy - * mmfd(3) handles. - */ -static bool -file_out(fdbuf *fdb, char *filename) -{ - bool res = FALSE; - fdbuf *fh; - int fd, len; - char tline[256]; - - if ((fd = open(filename, O_RDONLY)) != -1) { - if ((fh = fdbopen(&gfdf, NULL, fd, FDB_BUFSIZE, 0, 0, 0, -1, -1, - FALSE)) != NULL) { - res = TRUE; - while ((len = fdbgets(fh, tline, 255, FALSE)) > -1) - if (!fdbprintf(fdb, " %s\r\n", tline)) break; - fdbclose(fh); - } else - DEBUG_PRINTF("file_out", "dbopen() - Out of memory"); - close(fd); - } - - return (res); -} - - -/* This function consists of an attempt to save bandwidth and not spam the - * user constantly by possible directory messages, if any. We maintain a FIFO - * buffer with a maximum of last CONF.REMEMBER_CWDS visited directory hashes. - */ -static bool -directory_message(clientenv *clenv, int code) -{ - bool res = TRUE; - char path[MMPATH_MAX]; - u_int64_t dhash; - - if (*CONF.DISPLAY_FILE) { - if (path_valid(path, NULL, clenv->home, clenv->cwd, - CONF.DISPLAY_FILE, FALSE, FALSE, FALSE) - && path_exists(path, NULL, NULL, clenv->checkowner) - == MMPATH_FILE) { - u_int64_t *ptr; - - /* Make sure that this directory was not visited recently. */ - dhash = mm_strhash64(clenv->cwd); - FIFO_FIND(&clenv->visited, &ptr, &dhash); - if (ptr == NULL) { - /* This directory was not visited recently, send out file */ - if (!reply(clenv->fdb, code, TRUE, NULL)) - res = FALSE; - file_out(clenv->fdb, path); - } - /* Store current directory hash at end of FIFO, freeing the oldest - * entry automatically if required (buffer full). - */ - FIFO_PUT(&clenv->visited, &dhash); - } - } - - return (res); -} - - -/* Returns -1 if does not match '*' and '?' wildcards pattern. - * Otherwise returns > -1, a value representing the number of literal - * characters in which exactly matched . This us useful to evaluate - * the best match among a list of patterns. - */ -static int -best_match(const char *str, const char *pat) -{ - int lit = 0; - - for (; *pat != '*'; pat++, str++) { - if (*str == '\0') { - if (*pat != '\0') - return -1; - else - return lit; - } - if (*str == *pat) - lit++; - else - if(*pat != '?') - return -1; - } - while (pat[1] == '*') - pat++; - do { - register int tmp; - - if ((tmp = best_match(str, pat + 1)) != -1) - return (lit + tmp); - } while (*str++ != '\0'); - - return -1; -} - - -/* Returns TRUE if supplied IP address string is valid, FALSE otherwise */ -static bool -valid_ipaddress(const char *addr) -{ - char unit[5], *uptr, *utptr; - int units; - - for (units = 0, uptr = unit, utptr = unit + 4; uptr < utptr; addr++) { - if (*addr == '\0' || *addr == '.') { - if (uptr > unit && units < 4) { - register int n; - - *uptr = '\0'; - n = atoi(unit); - if (n < 0 || n > 255) - break; - uptr = unit; - units++; - } else - return FALSE; - if (*addr == '\0') - break; - } else if (isdigit(*addr)) - *uptr++ = *addr; - else - return FALSE; - } - if (!(units == 4 && *addr == '\0')) - return FALSE; - - return TRUE; -} - - -/* Parses CONF.PASV_REMAP, returns TRUE on success, or FALSE on error, - * printing the problem to stderr. Fills necessary struct pasv_remap - * structure for pasv_remap(). - */ -static bool -pasv_remap_parse(void) -{ - char *cols[64], *cols2[4], *ptr; - int i, entries; - struct sockaddr_in inaddr; - - if ((pasv_map_string = _mm_strdup(CONF.PASV_REMAP)) == NULL) { - (void) fprintf(stderr, "pasv_remap_parse() - mm_strdup()\n"); - return FALSE; - } - - /* First split string into :: entries and allocate - * pasv_remap array of necessary size - */ - if ((entries = mm_straspl(cols, pasv_map_string, 63)) < 1) { - free(pasv_map_string); - (void) fprintf(stderr, - "No entries in PASV_REMAP although string was not empty\n"); - return FALSE; - } - if ((pasv_map = malloc(sizeof(struct pasv_remap) * entries)) == NULL) { - free(pasv_map_string); - (void) fprintf(stderr, "pasv_remap_parse() - malloc()\n"); - return FALSE; - } - pasv_map_size = entries; - - /* Now parse each entry, verifying validity and filling them */ - for (i = 0; i < entries; i++) { - if (mm_strspl(cols2, cols[i], 3, ':') != 3) { - free(pasv_map); - free(pasv_map_string); - (void) fprintf(stderr, - "All PASV_REMAP entries must have three components" - " separated by colons (':')\n"); - return FALSE; - } - if (!valid_ipaddress(cols2[1]) || !valid_ipaddress(cols2[2])) { - free(pasv_map); - free(pasv_map_string); - (void) fprintf(stderr, - "all PASV_REMAP entries must have valid IPv4 " - "addresses for both second and third entries\n"); - return FALSE; - } - /* Fill apparently valid entry */ - pasv_map[i].pattern = cols2[0]; - for (ptr = cols2[2]; *ptr != '\0'; ptr++) { - if (*ptr == '.') - *ptr = ','; - } - pasv_map[i].advertize = cols2[2]; - if (inet_pton(AF_INET, cols2[1], &inaddr.sin_addr) != 1) { - free(pasv_map); - free(pasv_map_string); - (void) fprintf(stderr, - "Apparently unvalid address '%s' in PASV_REMAP\n", - cols2[1]); - return FALSE; - } - pasv_map[i].bind = inaddr.sin_addr.s_addr; - } - - return TRUE; -} - - -/* Allows to remapping of client address pattern to bind/advertize address - * pair using a best-matching algorithm. Returns TRUE if the entry was - * remapped, or FALSE otherwise. and are set if remapped. - */ -static bool -pasv_remap(u_int32_t *bind, char **advertize, const char *client) -{ - int i, cur, max; - struct pasv_remap *entry = NULL; - - /* Run through patterns to find the best matching entry. Because the - * number of entries should be rather small in general, we do not need to - * perform any verification calling yield. If no patterns match, we'll - * return FALSE. - */ - for (cur = 0, max = -1, i = 0; i < pasv_map_size; i++) { - if ((cur = best_match(client, pasv_map[i].pattern)) != -1) { - if (cur > max) { - max = cur; - entry = &pasv_map[i]; - } - } - } - - if (entry != NULL) { - *bind = entry->bind; - *advertize = (char *)entry->advertize; - return TRUE; - } - - return FALSE; -} - - - -/* This is the main function that is called to serve a client. - * It comports the main loop and state switcher. The system I use here should - * be quite fast, as only looping once though the commands list is required, - * because each state has it's jump table. - */ -static int -handleclient(unsigned long cid, int fd, clientlistnode *clientlnode, - struct iface *iface, struct async_clenv *aclenv) -{ - char buffer[1024], ipaddr[20], sipaddr[20]; - register char *tmp; - int state, nstate, timeout, dstatus; - clientenv *clenv = NULL; - struct sockaddr_in *sinaddr, sname; - socklen_t slen; - fdbuf *fdb; - transfermsg *msg; - unsigned long time_total; - int64_t control_in, control_out, data_in, data_out, dl_files, ul_files; - time_t time_start, time_end; - bool remapped; - - timeout = clientlnode->timeout; - control_in = control_out = data_in = data_out = dl_files = ul_files = 0; - dstatus = MMS_RESOURCE_ERROR; - - /* Obtain IP address of client */ - sinaddr = (struct sockaddr_in *)&clientlnode->client; - mm_strcpy(ipaddr, "0.0.0.0"); - inet_ntop(AF_INET, &(sinaddr->sin_addr), ipaddr, 19); - - if (clientlnode->hostname) - /* Log user's IP and hostname */ - mmsyslog(1, LOGLEVEL, "%08X Connect: [%s] - (%s)", cid, ipaddr, - clientlnode->hostname); - else - /* Log user's IP */ - mmsyslog(1, LOGLEVEL, "%08X Connect: [%s]", cid, ipaddr); - - time_start = time(NULL); - - if ((fdb = fdbopen(&gfdf, &fdbc, fd, FDB_BUFSIZE, FDB_BUFSIZE, - CONF.BANDWIDTH_IN * 1024, CONF.BANDWIDTH_OUT * 1024, - timeout, timeout, FALSE)) != NULL) { - - /* Allocate our clientenv to share with state functions */ - if ((clenv = alloc_clientenv())) { - - /* Verify if proxy-mapping is enabled, and if so find mapping - * matching the most closely to the client originator address. - * This way, we will use the wanted address for binding the - * passive ports, as well as to advertize it to the client at - * PASV/LPSV. We will still be performing client originator - * address verification for accepting clients after listen(2) - * for security. - * clenv->sipaddr consists of string to use as advertizing - * address for PASV and LPSV. Since EPSV can be - * used without needing to specify an address to - * connect to (wiser), we don't need any special - * treatment for it. - * clenv->uipaddr consists of 32-bit IP address to bind(2) - * listening passive ports with. - */ - remapped = FALSE; - if (*CONF.PASV_REMAP != '\0') { - /* We must perform remapping as necessary using - * pattern:bind:advertize. We already parsed the entries at - * daemon launch time for efficiency. Find closest matching - * entry, if any. - */ - remapped = pasv_remap(&clenv->uipaddr, &clenv->sipaddr, - ipaddr); - } - if (!remapped) { - /* Default case, and fallback for no mapping pattern: - * Obtain IP address of server to not have to do this - * everytime a PASV/LPSV command is issued. sipaddr is stored - * in n,n,n,n format. Set uipaddr as well for port to bind to. - */ - slen = sizeof(struct sockaddr_in); - mm_strcpy(sipaddr, "0.0.0.0"); - clenv->sipaddr = sipaddr; - sname.sin_addr.s_addr = 0; - if (getsockname(fd, (struct sockaddr *)&sname, &slen) == 0) { - inet_ntop(AF_INET, &(sname.sin_addr), sipaddr, 19); - for (tmp = sipaddr; *tmp != '\0'; tmp++) - if (*tmp == '.') - *tmp = ','; - clenv->uipaddr = sname.sin_addr.s_addr; - } - } - - /* Open our reply port and start our file transfer thread */ - if ((clenv->rport = pth_msgport_create("XXX(NULL)"))) { - clenv->rring = pth_event(PTH_EVENT_MSG, clenv->rport); - - /* Init our clientenv as required */ - clenv->fdb = fdb; - clenv->buffer = buffer; - clenv->type = TYPE_ASCII; - clenv->errors = 0; - clenv->timeout = timeout; - clenv->c_hostname = clientlnode->hostname; - clenv->c_ipaddr = ipaddr; - clenv->cipaddr = sinaddr->sin_addr.s_addr; - clenv->id = cid; - clenv->iface = iface; - clenv->aclenv = aclenv; - - mmstat(&clenv->vstat, STAT_UPDATE, 1, - "mmftpd|current|connections"); - mmstat(&clenv->pstat, STAT_UPDATE, 1, - "mmftpd|total|connections"); - - if (start_transfer_thread(clenv)) { - - msg = &(clenv->tmsg); - - if (*CONF.WELCOME_FILE) { - reply(fdb, 220, TRUE, NULL); - if (!file_out(fdb, CONF.WELCOME_FILE)) - mmsyslog(0, LOGLEVEL, - "%08X Error opening WELCOME_FILE \"%s\"", - clenv->id, CONF.WELCOME_FILE); - } - - reply(fdb, 220, FALSE, "%s FTP server (%s (%s)) ready", - iface->hostname, DAEMON_NAME, DAEMON_VERSION); - state = STATE_AUTH; - dstatus = MMS_NORMAL; - - /* Main state switcher loop */ - for (;;) { - register ssize_t len; - register struct commandnode *nod; - u_int32_t chash; - - fdbflushw(fdb); - if ((len = fdbgets(fdb, buffer, 1000, TRUE)) > -1) { - - /* If there were too many errors, exit */ - if (clenv->errors > CONF.MAX_ERRORS) { - reply(fdb, 421, FALSE, "Too many errors"); - dstatus = MMS_MANY_ERRORS; - break; - } - - /* Verify if command matches one of our commands */ - nod = NULL; - if ((chash = mm_strpack32(buffer, 3)) != 0) - nod = (struct commandnode *)hashtable_lookup( - &command_table, &chash, - sizeof(u_int32_t)); - if (nod != NULL) { - register int (*func)(clientenv *); - - mmsyslog(nod->command->loglevel, LOGLEVEL, - "%08X < %s", cid, buffer); - - /* First eliminate any - options after - * command and before parameters, we should - * ignore them all. - */ - stripoptions(buffer); - - if ((func = states[state].functions[ - nod->index])) { - - /* Process command in current state */ - nstate = func(clenv); - /* Check new state */ - if (nstate == STATE_END || - nstate == STATE_ERROR) - break; - if (nstate != STATE_CURRENT) - state = nstate; - - } else { - /* Unimplemented command for this state */ - REGISTER_ERROR(); - if (!reply(fdb, states[state].errcode, - FALSE, states[state].errtext)) - break; - } - - } else { - mmsyslog(3, LOGLEVEL, "%08X < %s", cid, - buffer); - REGISTER_ERROR(); - if (!reply(fdb, 500, FALSE, "Unknown command")) - break; - } - - } else { - /* Input error */ - if (len == FDB_TIMEOUT) { - /* Make sure no transfers are ongoing, only - * timeout if none. - */ - if (transfer_request(REQ_STATUS, TRUE, - clenv)) { - if (msg->ongoing) continue; - } else - DEBUG_PRINTF("handleclient", - "transfer_request(REQ_STATUS)"); - dstatus = MMS_INPUT_TIMEOUT; - break; - } else if (len == FDB_ERROR) { - dstatus = MMS_INPUT_ERROR; - break; - } else { - dstatus = MMS_UNKNOWN; - break; - } - } - - } - - /* Obtain status information from transfer thread and - * kill it - */ - if (transfer_request(REQ_STATUS, TRUE, clenv)) { - data_in = msg->rbytes; - data_out = msg->wbytes; - dl_files = msg->dlfiles; - ul_files = msg->ulfiles; - } - stop_transfer_thread(clenv); - } else - DEBUG_PRINTF("handleclient", "start_transfer_thread()"); - - pth_event_free(clenv->rring, PTH_FREE_ALL); - while ((pth_msgport_get(clenv->rport)) != NULL) ; - pth_msgport_destroy(clenv->rport); - } else - DEBUG_PRINTF("handleclient", "pth_msgport_create()"); - - control_in = FDBBYTESR(fdb); - control_out = FDBBYTESW(fdb); - - mmstat_transact(&clenv->vstat, TRUE); - if (clenv->login) - mmstat(&clenv->vstat, STAT_UPDATE, -1, "mmftpd|who|%s", - clenv->user); - mmstat(&clenv->vstat, STAT_UPDATE, -1, - "mmftpd|current|connections"); - mmstat_transact(&clenv->vstat, FALSE); - - mmstat_transact(&clenv->pstat, TRUE); - mmstat(&clenv->pstat, STAT_UPDATE, dl_files, - "mmftpd|total|files-out"); - mmstat(&clenv->pstat, STAT_UPDATE, ul_files, - "mmftpd|total|files-in"); - if (clenv->login) { - mmstat(&clenv->pstat, STAT_UPDATE, dl_files, - "mmftpd|user|%s|files-out", clenv->user); - mmstat(&clenv->pstat, STAT_UPDATE, ul_files, - "mmftpd|user|%s|files-in", clenv->user); - mmstat(&clenv->pstat, STAT_UPDATE, data_in + control_in, - "mmftpd|user|%s|bytes-in", clenv->user); - mmstat(&clenv->pstat, STAT_UPDATE, data_out + control_out, - "mmftpd|user|%s|bytes-out", clenv->user); - } - mmstat(&clenv->pstat, STAT_UPDATE, data_in + control_in, - "mmftpd|total|bytes-in"); - mmstat(&clenv->pstat, STAT_UPDATE, data_out + control_out, - "mmftpd|total|bytes-out"); - mmstat(&clenv->pstat, STAT_UPDATE, data_in, - "mmftpd|total|bytes-data-in"); - mmstat(&clenv->pstat, STAT_UPDATE, data_out, - "mmftpd|total|bytes-data-out"); - mmstat(&clenv->pstat, STAT_UPDATE, control_in, - "mmftpd|total|bytes-control-in"); - mmstat(&clenv->pstat, STAT_UPDATE, control_out, - "mmftpd|total|bytes-control-out"); - mmstat_transact(&clenv->pstat, FALSE); - - /* Free our state-shared clenv */ - free_clientenv(clenv); - } else - DEBUG_PRINTF("handleclient", "alloc_clientenv()"); - - fdbclose(fdb); - } else - DEBUG_PRINTF("handleclient", "open_fdbuf()"); - - time_end = time(NULL); - time_total = time_end - time_start; - mmsyslog(1, LOGLEVEL, - "%08X Closed: [%s] - (Seconds: %ld, Control in: %lld out: %lld, " - "Data in: %lld out: %lld, Files in: %lld out: %lld, Status: %s)", - cid, ipaddr, time_total, control_in, control_out, data_in, - data_out, ul_files, dl_files, MMS_RSTRING(dstatus)); - - return (0); -} - - - -/* This function is useful for the transfer thread to bind passive ports, - * which may be within a range. Returns TRUE on success, with socket and - * port set into the specified pointers, or FALSE. It uses the supplied - * clenv and sockaddr_in. - */ -static bool -pasv_bind(clientenv *clenv, struct sockaddr_in *server, int *sock, int *port) -{ - int fd; - - if ((fd = socket(AF_INET, SOCK_STREAM, 0)) != -1) { - int opt; - - opt = 1; - if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(int)) == -1) - DEBUG_PRINTF("pasv_bind", "setsockopt(SO_REUSEADDR)"); - mm_memclr(server, sizeof(struct sockaddr_in)); - server->sin_family = AF_INET; - server->sin_addr.s_addr = clenv->uipaddr; - if (!CONF.PASV_RANGE) { - /* Just let the system assign us a free high port, which is very - * fast and most likely to succeed. - */ - server->sin_port = 0; - if (bind(fd, (struct sockaddr *)server, sizeof(struct sockaddr_in)) - != -1) { - socklen_t len; - - /* Get port number we were able to bind */ - len = sizeof(struct sockaddr_in); - if (getsockname(fd, (struct sockaddr *)server, - (socklen_t *)&len) != -1) { - if (listen(fd, 0) == 0) { - *sock = fd; - *port = ntohs(server->sin_port); - - return TRUE; - } else - DEBUG_PRINTF("pasv_bind", "listen(%d)", fd); - } else - DEBUG_PRINTF("pasv_bind", "getsockname(%d)", fd); - } else - DEBUG_PRINTF("pasv_bind", "bind(%d)", fd); - } else { - /* We need to ensure to obtain a port between CONF.PASV_RANGE_MIN - * and CONF.PASV_RANGE_MAX - */ - int i, p, maxp, maxs, err, range; - - range = CONF.PASV_RANGE_MAX - CONF.PASV_RANGE_MIN; - /* Start at a random port within range */ - p = CONF.PASV_RANGE_MIN + (random() % range); - /* We will quickly perform up to 64 bind(2) operations per - * second (or run through the range per second if it's smaller than - * 64), as required to obtain the port, or until 30 seconds - * elapsed, in which case we hopelessly fail. Of course, this - * only suspends the current client's transfer manager thread, - * from which only that client is awaiting a reply message from. - */ - maxp = range < 64 ? range : 64; - for (err = -1, maxs = 0; err == -1 && maxs < 30; maxs++) { - for (i = 0; i < maxp; i++) { - server->sin_port = htons(p); - if ((err = bind(fd, (struct sockaddr *)server, - sizeof(struct sockaddr_in))) != -1) - break; - p++; - if (p > CONF.PASV_RANGE_MAX) - p = CONF.PASV_RANGE_MIN; - } - if (err == -1) - pth_sleep(1); - } - if (err != -1) { - /* Successful bind(2) */ - if (listen(fd, 0) == 0) { - *sock = fd; - *port = p; - - return TRUE; - } else - mmsyslog(0, LOGLEVEL, "%08X pasv_bind() - listen(%d)", - clenv->id, fd); - } else - mmsyslog(0, LOGLEVEL, "%08X pasv_bind() - bind(%d)", - clenv->id, fd); - } - close(fd); - } else - mmsyslog(0, LOGLEVEL, "%08X pasv_bind() - socket()", clenv->id); - - return FALSE; -} - - - -/* This consists of the transfer server thread. One is spawned for each ftp - * client we are serving, and the main client server thread uses it as a slave - * daemon, managing PORT/PASV transfers. This permits the client to continue - * serving basic FTP commands, including ABOR, asynchronously, except of course - * other transfer requests when one is ongoing already. - */ -static void -transferthread(struct thread_object *obj, void *args) -{ - clientenv *clenv = args; - fdbuf *fdb; - bool ok = FALSE; - transfermsg *msg; - - /* Pth 1.4.1 and older did not accept NULL supplied for message port name - * if locating it by name later on was not necessary (it in fact almost - * never is as every memory is shared between threads :). AmigaOS, from - * which the Pth message port API was inspired, allowed NULL here, - * and future Pth releases also will. We just use a fixed string for now. - */ - /* The fd, as well as input/output bandwidth parameters are unusable at - * this time. We set them internally when we receive transfer requests. - */ - if ((fdb = fdbopen(&gfdf, &fdbc, -1, FDB_BUFSIZE, FDB_BUFSIZE, 1024, 1024, - CONF.DATA_TIMEOUT * 1000, CONF.DATA_TIMEOUT * 1000, - FALSE)) != NULL) { - if ((clenv->sport = pth_msgport_create("XXX(NULL)"))) { - /* Reply that we started thread properly */ - ok = TRUE; - clenv->tmsg.result = TRUE; - pth_msgport_put(clenv->rport, (pth_message_t *)&(clenv->tmsg)); - - /* Process our tasks */ - transferthread_main(clenv, fdb); - - while ((msg = (transfermsg *)pth_msgport_get(clenv->sport)) - != NULL) { - msg->result = FALSE; - pth_msgport_reply((pth_message_t *)msg); - } - pth_msgport_destroy(clenv->sport); - } else - DEBUG_PRINTF("transferthread", "pth_msgport_create()"); - fdbclose(fdb); - } else - DEBUG_PRINTF("transferthread", "fdbopen()"); - - if (!ok) { - /* Reply that thread couldn't be setup properly */ - clenv->tmsg.result = FALSE; - pth_msgport_put(clenv->rport, (pth_message_t *)&(clenv->tmsg)); - } - - /* - pth_exit(NULL); - */ -} - - -/* This device provides a few states and commands: - * - * STATES - * ------ - * 1) Waiting for requests, no transfer ongoing, no connection establishing, - * listening to REQ_QUIT, REQ_ABORT, REQ_STATUS, REQ_NEWPORT, REQ_TRANSFER. - * Of course this also has to handle the PASV listening port - * (TTSTATE_INITIAL) - * 2) Establishing a connection and listening to REQ_QUIT, REQ_ABORT and - * REQ_STATUS (we will still report ongoing=TRUE at this point) - * (TTSTATE_CONNECT) - * 3) Ongoing transfer, and listening to REQ_QUIT, REQ_ABORT and REQ_STATUS - * (TTSTATE_TRANSFER) - * - * REQUESTS - * -------- - * REQ_QUIT) May be used anytime, causes the transfer thread to abort ongoing - * transfer, if any, and to cleanly exit - * REQ_ABORT) Anytime, forces ongoing transfer to end, if any, and abort any - * pending REQ_TRANSFER (PORT/PASV) - * REQ_STATUS) Anytime, permits to know if any ongoing transfers in progress, - * and if a PORT/PASV connection is pending - * REQ_NEWPORT) Only allowed in state 1, tells the transfer thread that - * next transfer will be in PORT or PASV mode, etc - * REQ_TRANSFER) Tells the thread, only after REQ_NEWPORT was used, to attempt - * a transfer. Direction has to be specified, as well as the file - * descriptor of the file to be read from or written to. If - * list > 0 however, this means that the client will be sent a - * file listing instead, list=1 for LIST, list=2 for NLST. - * - * This consists of a pretty large messy function, but it works (-: - * It is to be noted that it itself writes a few replies on the control port - * when appropriate, to simplify the ASYNC design between the control and data - * threads/ports. This way the control thread does not need to be notified - * when a data transfer ends. Moreover, the control thread does all necessary - * sanity checking before these functions are called (eg: port range limit, - * files). The exception is that when a passive data connection is established - * a necessary check is performed to ensure that only the expected client - * connected (bind port on right interface, and check client IP address, as - * the FTP protocol does not provide any better means). - */ -static void -transferthread_main(clientenv *clenv, fdbuf *fdb) -{ - int state = TTSTATE_INITIAL, l, reason; - register char *from, *to; - char ipaddr[20], buffer[T_BUFSIZE], *from2; - pth_event_t ring, ring1; - transfermsg *msg; - struct sockaddr_in server, client, addr; - socklen_t addrl; - bool cont; - - /* We need our own copy of these */ - int port = 0, fdpasv = -1, fd = -1, file = -1; - char path[MMPATH_MAX]; - bool passive = FALSE, ongoing = FALSE, download = TRUE, list = 0; - int64_t dlfiles = 0, ulfiles = 0; - *path = '\0'; - fdbparam_set(fdb, -1, FDBP_FD); - - /* Transfer device entry setup */ - ring = pth_event(PTH_EVENT_MSG, clenv->sport); - - /* Main device loop */ - while (state != TTSTATE_DONE) { - - if (state == TTSTATE_INITIAL) { /* INITIAL STATE */ - - /* State entry setup */ - /* Make sure everything is reset */ - port = 0; - passive = ongoing = FALSE; - download = TRUE; - list = 0; - *path = '\0'; - if (fd != -1) { - fdbflushw(fdb); - shutdown(fd, SHUT_RDWR); - close(fd); - fd = -1; - fdbparam_set(fdb, -1, FDBP_FD); - } - if (file != -1) { - close(file); - file = -1; - } - if (fdpasv != -1) { - close(fdpasv); - fdpasv = -1; - } - - /* State main loop */ - while (state == TTSTATE_INITIAL) { - if ((pth_wait(ring))) { - if ((pth_event_occurred(ring))) { - while ((msg = (transfermsg *)pth_msgport_get( - clenv->sport)) != NULL) { - /* Process request and reply */ - switch (msg->request) { - case REQ_QUIT: - msg->result = TRUE; - state = TTSTATE_DONE; - break; - case REQ_ABORT: - /* As we handle PASV port, do this */ - if (fdpasv != -1) { - close(fdpasv); - fdpasv = -1; - } - port = 0; - passive = ongoing = FALSE; - download = TRUE; - list = 0; - *path = '\0'; - msg->result = TRUE; - break; - case REQ_STATUS: - msg->port = (u_int16_t)port; - msg->passive = passive; - msg->ongoing = ongoing; - msg->download = download; - msg->list = list; - msg->rbytes = FDBBYTESR(fdb); - msg->wbytes = FDBBYTESW(fdb); - msg->dlfiles = dlfiles; - msg->ulfiles = ulfiles; - msg->result = TRUE; - break; - case REQ_NEWPORT: - port = 0; - if (fd != -1) { - fdbflushw(fdb); - shutdown(fd, SHUT_RDWR); - close(fd); - fd = -1; - fdbparam_set(fdb, -1, FDBP_FD); - } - /* As we handle PASV port, do this */ - if (fdpasv != -1) { - close(fdpasv); - fdpasv = -1; - } - if (msg->passive) { - /* PASV request, we need to open a high - * port and start listening for a - * connection on it - */ - msg->result = FALSE; /* Default */ - if (pasv_bind(clenv, &server, &fdpasv, - &port)) { - msg->port = (u_int16_t)port; - passive = msg->result = TRUE; - break; - } - } else { - /* Normal PORT, just remember necessary - * things. The caller should have - * performed address and port sanity - * checking already. - */ - port = (int)msg->port; - passive = FALSE; - msg->result = TRUE; - } - break; - case REQ_TRANSFER: - msg->result = FALSE; /* Default */ - if (port != 0) { - fdbparam_set(fdb, msg->rrate * 1024, - FDBP_RRATE); - fdbparam_set(fdb, msg->wrate * 1024, - FDBP_WRATE); - if (msg->path) mm_strncpy(path, msg->path, - MMPATH_MAX - 1); - else *path = '\0'; - if (msg->list) { - list = msg->list; - msg->result = TRUE; - } else { - if (msg->file != -1) { - file = msg->file; - download = msg->download; - msg->result = TRUE; - } - } - if (msg->result) - state = TTSTATE_CONNECT; - } else - msg->result = FALSE; - break; - default: - msg->result = FALSE; - } - pth_msgport_reply((pth_message_t *)msg); - } - } - } - } - - /* State exit cleanup */ - - } else if (state == TTSTATE_CONNECT) { /* CONNECTION STATE */ - - /* State entry setup */ - ongoing = TRUE; - /* Setup our connect timeout event */ - ring1 = pth_event(PTH_EVENT_TIME, pth_timeout(CONF.DATA_TIMEOUT, - 0)); - pth_event_concat(ring, ring1, NULL); - - /* State main loop */ - while (state == TTSTATE_CONNECT) { - if (passive) { - /* Accept connection from client */ - addrl = sizeof(struct sockaddr); - fd = pth_accept_ev(fdpasv, (struct sockaddr *)&addr, - &addrl, ring); - } else { - /* Start connection to the client */ - if ((fd = socket(AF_INET, SOCK_STREAM, 0)) != -1) { - mm_memclr(&client, sizeof(struct sockaddr_in)); - client.sin_family = AF_INET; - client.sin_addr.s_addr = clenv->cipaddr; - client.sin_port = htons(port); - /* Finally attempt connection */ - if ((pth_connect_ev(fd, (struct sockaddr *)&client, - sizeof(struct sockaddr_in), - ring)) == -1) { - mmsyslog(1, LOGLEVEL, - "%08X PORT data connection failiure", - clenv->id); - reply(clenv->fdb, 425, FALSE, - "Can't establish data connection"); - fdbflushw(clenv->fdb); - REGISTER_ERROR(); - state = TTSTATE_INITIAL; - break; - } - } else { - DEBUG_PRINTF("transferthread_main-2", "socket()"); - reply(clenv->fdb, 425, FALSE, - "Can't establish data connection"); - fdbflushw(clenv->fdb); - REGISTER_ERROR(); - state = TTSTATE_INITIAL; - break; - } - } - /* Verify if connection timeout occured */ - if ((pth_event_occurred(ring1))) { - mmsyslog(1, LOGLEVEL, "%08X Data connection timeout", - clenv->id); - reply(clenv->fdb, 425, FALSE, - "Can't establish data connection"); - fdbflushw(clenv->fdb); - REGISTER_ERROR(); - state = TTSTATE_INITIAL; - break; - } - /* Check for any request events and process them if any */ - if ((pth_event_occurred(ring))) { - while ((msg = (transfermsg *)pth_msgport_get( - clenv->sport)) != NULL) { - /* Process request and reply */ - switch (msg->request) { - case REQ_QUIT: - msg->result = TRUE; - state = TTSTATE_DONE; - break; - case REQ_ABORT: - msg->result = TRUE; - state = TTSTATE_INITIAL; - break; - case REQ_STATUS: - msg->port = (u_int16_t)port; - msg->passive = passive; - msg->ongoing = ongoing; - msg->download = download; - msg->list = list; - msg->rbytes = FDBBYTESR(fdb); - msg->wbytes = FDBBYTESW(fdb); - msg->dlfiles = dlfiles; - msg->ulfiles = ulfiles; - msg->result = TRUE; - break; - default: - msg->result = FALSE; - } - pth_msgport_reply((pth_message_t *)msg); - } - } - if (state == TTSTATE_CONNECT) { - /* Verify if connection was established */ - if (fd != -1) { - fdbparam_set(fdb, fd, FDBP_FD); - if (passive) { - /* Make sure that it's really the client we - * expect. If it isn't, just immediately drop - * connected client and continue listening on this - * socket for the expected client. - */ - if (addr.sin_addr.s_addr == clenv->cipaddr) - state = TTSTATE_TRANSFER; - else { - (void) shutdown(fd, SHUT_RDWR); - (void) close(fd); - fd = -1; - *ipaddr = '\0'; - inet_ntop(AF_INET, &(addr.sin_addr), ipaddr, - 19); - mmsyslog(0, LOGLEVEL, - "%08X Failed passive data connection" - "from invalid address (%s != %s)", - clenv->id, ipaddr, clenv->c_ipaddr); - continue; - } - } else - state = TTSTATE_TRANSFER; - break; - } - } - } - - /* State exit cleanup */ - pth_event_isolate(ring); - pth_event_free(ring1, PTH_FREE_ALL); - - } else if (state == TTSTATE_TRANSFER) { /* TRANSFER STATE */ - - /* State entry setup */ - ongoing = TRUE; - reason = TR_OK; - - /* State main loop */ - while (state == TTSTATE_TRANSFER) { - - /* We transfer the data while still answering a few requests */ - if (list) { - int flags = MMLS_GLOB; - - shutdown(fd, SHUT_RD); - reply(clenv->fdb, 150, FALSE, - "Opening ASCII mode data connection for '/bin/ls'"); - fdbflushw(clenv->fdb); - /* List request, simply send the data to the connected - * client - */ - if (list != 3) { - if (clenv->checkowner) - flags |= MMLS_OWNERONLY; - if (!(clenv->modify || clenv->upload)) - flags |= MMLS_READONLY; - if (list == 1) - flags |= MMLS_LONG; - if (!path_ls(fdb, path, clenv->user, clenv->group, - flags)) - mmsyslog(0, LOGLEVEL, - "%08X transferthread_main(3) - path_ls(%s)", - clenv->id, path); - } - state = TTSTATE_INITIAL; - break; - } - - /* Normal file transfer with an inactivity timeout, - * to transfer bytes between file and fd, in specified - * direction - */ - if (clenv->type == TYPE_ASCII) - reply(clenv->fdb, 150, FALSE, - "Opening ASCII mode data connection for '%s'", - path); - else - reply(clenv->fdb, 150, FALSE, - "Opening BINARY mode data connection for '%s'", - path); - fdbflushw(clenv->fdb); - - if (download) { /* SERVER TO CLIENT TRANSFER */ - - dlfiles++; - shutdown(fd, SHUT_RD); /* Prevent read from socket */ - if (clenv->rest) { - lseek(file, clenv->rest, SEEK_SET); - clenv->rest = 0; - } - - cont = TRUE; - if (clenv->type == TYPE_IMAGE) fdbflushw(fdb); - while (cont) { - if ((clenv->type == - TYPE_IMAGE ? fdbrcleartosend(fdb) : - fdbcleartosend(fdb))) { - /* Client ready to be written some bytes to */ - if ((l = pth_read(file, buffer, T_BUFSIZE)) > 0) { - if (clenv->type == TYPE_ASCII) { - /* ASCII xfer, perform \n to \r\n - * conversion - */ - from = from2 = to = buffer; - to += l; - while (from < to && from2 < to) { - if (*from == '\n') { - if ((l = fdbwrite(fdb, from2, - from - - from2)) < 0) - break; - if ((l = fdbwrite(fdb, "\r\n", 2)) - < 0) - break; - from2 = from + 1; - } - from++; - } - if (from2 < to) - l = fdbwrite(fdb, from2, to - from2); - if (l < 1) { - state = TTSTATE_INITIAL; - cont = FALSE; - if ((FDBERRNO(fdb)) == FDB_TIMEOUT) - reason = TR_TIMEOUT; - else if ((FDBERRNO(fdb)) == FDB_ERROR) - reason = TR_LOST; - break; - } - } else { - /* IMAGE/binary transfer, write as-is */ - if ((fdb->fdberrno = fdbrwrite(fdb, - buffer, l)) != l) { - state = TTSTATE_INITIAL; - cont = FALSE; - if ((FDBERRNO(fdb)) == FDB_TIMEOUT) - reason = TR_TIMEOUT; - else if ((FDBERRNO(fdb)) == FDB_ERROR) - reason = TR_LOST; - break; - } - } - } else { - /* EOF reading local file */ - state = TTSTATE_INITIAL; - cont = FALSE; - break; - } - } else { - state = TTSTATE_INITIAL; - cont = FALSE; - if ((FDBERRNO(fdb)) == FDB_TIMEOUT) - reason = TR_TIMEOUT; - else if ((FDBERRNO(fdb)) == FDB_ERROR) - reason = TR_LOST; - break; - } - /* Verify if we received any important events */ - if ((pth_event_occurred(ring))) { - while ((msg = (transfermsg *)pth_msgport_get( - clenv->sport)) != NULL) { - /* Process request and reply */ - switch (msg->request) { - case REQ_QUIT: - msg->result = TRUE; - state = TTSTATE_DONE; - cont = FALSE; - break; - case REQ_ABORT: - msg->result = TRUE; - state = TTSTATE_INITIAL; - cont = FALSE; - reason = TR_ABORTED; - break; - case REQ_STATUS: - msg->port = (u_int16_t)port; - msg->passive = passive; - msg->ongoing = ongoing; - msg->download = download; - msg->list = list; - msg->rbytes = FDBBYTESR(fdb); - msg->wbytes = FDBBYTESW(fdb); - msg->dlfiles = dlfiles; - msg->ulfiles = ulfiles; - msg->result = TRUE; - break; - default: - msg->result = FALSE; - } - pth_msgport_reply((pth_message_t *)msg); - } - } - } - - } else { /* CLIENT TO SERVER TRANSFER */ - - ulfiles++; - cont = TRUE; - fdbflushr(fdb); - while (cont) { - if (fdbrdataready(fdb)) { - /* Data ready to read from client */ - if ((l = fdbrread(fdb, buffer, T_BUFSIZE)) > 0) { - - if (clenv->type == TYPE_ASCII) { - - /* ASCII transfer, perform \r\n to \n - * conversion, stripping all \r, and check - * for \0 as an EOF substitute. - */ - from = from2 = to = buffer; - to += l; - while (from < to && from2 < to) { - if (*from == '\r') { - if (treesize_edit(clenv, - from - from2)) { - if ((l = pth_write(file, - from2, from - from2)) - != from - from2) { - mmsyslog(0, LOGLEVEL, - "%08X Error " - "writing to " - "disk!", - clenv->id); - l = -1; - reason = TR_QUOTA; - break; - } - } else { - mmsyslog(0, LOGLEVEL, - "%08X Quota exceeded", - clenv->id); - l = -1; - reason = TR_QUOTA; - break; - } - from2 = from + 1; - } else if (*from == 0) { - l = 0; - break; - } - from++; - } - if (l && from2 < to) { - if (treesize_edit(clenv, to - from2)) { - if ((l = pth_write(file, from2, - to - from2)) - != to - from2) { - mmsyslog(0, LOGLEVEL, - "%08X Error writing " - "to disk!", - clenv->id); - l = -1; - reason = TR_QUOTA; - } - } else { - mmsyslog(0, LOGLEVEL, - "%08X Quota exceeded", - clenv->id); - l = -1; - reason = TR_QUOTA; - } - } - if (l < 1) { - state = TTSTATE_INITIAL; - cont = FALSE; - break; - } - - } else { - - /* IMAGE/binary transfer, write data as-is - */ - if (treesize_edit(clenv, l)) { - if ((pth_write(file, buffer, l)) != - l) { - mmsyslog(0, LOGLEVEL, - "%08X Error writing to " - "disk!", clenv->id); - state = TTSTATE_INITIAL; - reason = TR_QUOTA; - cont = FALSE; - break; - } - } else { /* Quota exceeded */ - mmsyslog(0, LOGLEVEL, - "%08X Quota exceeded", - clenv->id); - state = TTSTATE_INITIAL; - cont = FALSE; - reason = TR_QUOTA; - break; - } - - } - - } else { - fdb->fdberrno = l; - if ((FDBERRNO(fdb)) == FDB_TIMEOUT) - reason = TR_TIMEOUT; - /* EOF from user socket */ - state = TTSTATE_INITIAL; - cont = FALSE; - break; - } - } else { - state = TTSTATE_INITIAL; - cont = FALSE; - if ((FDBERRNO(fdb)) == FDB_TIMEOUT) - reason = TR_TIMEOUT; - break; - } - /* Verify if we received any important events */ - if ((pth_event_occurred(ring))) { - while ((msg = (transfermsg *)pth_msgport_get( - clenv->sport)) != NULL) { - /* Process request and reply */ - switch (msg->request) { - case REQ_QUIT: - msg->result = TRUE; - state = TTSTATE_DONE; - cont = FALSE; - break; - case REQ_ABORT: - msg->result = TRUE; - state = TTSTATE_INITIAL; - cont = FALSE; - reason = TR_ABORTED; - break; - case REQ_STATUS: - msg->port = (u_int16_t)port; - msg->passive = passive; - msg->ongoing = ongoing; - msg->download = download; - msg->list = list; - msg->rbytes = FDBBYTESR(fdb); - msg->wbytes = FDBBYTESW(fdb); - msg->dlfiles = dlfiles; - msg->ulfiles = ulfiles; - msg->result = TRUE; - break; - default: - msg->result = FALSE; - } - pth_msgport_reply((pth_message_t *)msg); - } - } - } - } - - } - - /* State exit cleanup */ - reply(clenv->fdb, tr_msg[-reason].code, FALSE, - tr_msg[-reason].msg); - fdbflushw(clenv->fdb); - /* State 1's setup will ensure to close the fds */ - - } - - } - - /* Transfer device exit cleanup */ - if (fd != -1) { - fdbflushw(fdb); - shutdown(fd, SHUT_RDWR); - close(fd); - fd = -1; - fdbparam_set(fdb, -1, FDBP_FD); - } - if (file != -1) { - close(file); - file = -1; - } - if (fdpasv != -1) close(fdpasv); - - pth_event_free(ring, PTH_FREE_ALL); -} - - -/* mmfd library thread support functions */ - - -static void * -_pth_mutex_create(void) -{ - struct mutexnode *mnod; - - pth_mutex_acquire(&mutexes_lock, FALSE, NULL); - mnod = (struct mutexnode *)pool_alloc(&mutexes_pool, FALSE); - pth_mutex_release(&mutexes_lock); - - if (mnod) - pth_mutex_init(&mnod->mutex); - - return ((void *)mnod); -} - - -static void * -_pth_mutex_destroy(void *mtx) -{ - /* struct mutexnode *mnod = mtx; */ - - /* pth_mutex_destroy(&mnod->mutex); */ - pth_mutex_acquire(&mutexes_lock, FALSE, NULL); - pool_free(mtx); - pth_mutex_release(&mutexes_lock); - - return (NULL); -} - - -static void -_pth_mutex_lock(void *mtx) -{ - struct mutexnode *mnod = mtx; - - pth_mutex_acquire(&mnod->mutex, FALSE, NULL); -} - - -static void -_pth_mutex_unlock(void *mtx) -{ - struct mutexnode *mnod = mtx; - - pth_mutex_release(&mnod->mutex); -} - - -static void -_pth_thread_yield(void) -{ - pth_yield(NULL); -} - - -static bool -_pth_eintr(void) -{ - if (errno == EINTR) - return TRUE; - - return FALSE; -} - - -static bool -eintr(void) -{ - if (errno == EINTR) - return TRUE; - - return FALSE; -} - - -/* Here are our real asynchroneous functions, called by the slave processes */ - - -/* crypt(3) is not thread-safe, it uses lame static buffer. - * Let's change this. - */ -static void -async_checkpw(struct async_msg *msg) -{ - struct async_checkpw_msg *amsg = (void *)msg; - char *hash; - - if ((hash = crypt(amsg->un.args.passwd, amsg->un.args.hash)) != NULL) { - if ((mm_strcmp(hash, amsg->un.args.hash)) == 0) - amsg->un.res.matching = TRUE; - else - amsg->un.res.matching = FALSE; - } else - amsg->un.res.matching = FALSE; -} - - -static void -async_treesize(struct async_msg *msg) -{ - struct async_treesize_msg *amsg = (void *)msg; - - amsg->un.res.size = recursive_treesize(amsg->un.args.path, - amsg->un.args.min); -} - - -/* We use mmfd library without threading support here, which allows us to - * share pre-allocated buffers for fast I/O. fdbgets() is also much more - * decent than fgets(). - */ -static void -async_getuserline(struct async_msg *msg) -{ - struct async_getuserline_msg *amsg = (void *)msg; - char *line = amsg->un.res.line; - bool ok = FALSE; - - if (getuserline_fdb == NULL) { - /* First time we were called by this process. Setup our buffered - * fdb handler which we will maintain across calls. - */ - fdfuncs fdf = { - malloc, - free, - poll, - read, - write, - sleep, - usleep, - NULL, - NULL, - NULL, - NULL, - NULL, - eintr - }; - - getuserline_fdb = fdbopen(&fdf, NULL, -1, FDB_BUFSIZE, 0, 0, 0, - -1, -1, FALSE); - } - - if (getuserline_fdb != NULL) { - char username[32]; - int fd; - - mm_strncpy(username, amsg->un.args.username, 31); - if ((fd = open(CONF.PASSWD_FILE, O_RDONLY)) != -1) { - char *columns[CONF_COLUMNS + 1]; - int cols; - - fdbparam_set(getuserline_fdb, fd, FDBP_FD); - fdbflushr(getuserline_fdb); - while ((cols = readconfline(line, getuserline_fdb, CONF_COLUMNS, - CONF_COLUMNS, columns)) != -1) { - if (cols == CONF_COLUMNS) { - if ((mm_strcmp(columns[CONF_USER], username)) == 0) { - /* User matches. Because we want to pass pointers - * to the various fields of the user configuration - * line to another process, map columns to index - * offsets instead, so we can relocate the pointers - * in the other process. - */ - int i, *index = amsg->un.res.cols; - - for (i = 0; i < CONF_COLUMNS; i++) - index[i] = columns[i] - line; - ok = TRUE; - break; - } - } else - syslog(LOG_NOTICE, - "async_getuserline() - malformed user " - "configuration file '%s'", - CONF.PASSWD_FILE); - } - close(fd); - fdbflushr(getuserline_fdb); - fdbparam_set(getuserline_fdb, -1, FDBP_FD); - } else - syslog(LOG_NOTICE, "async_getuserline() - open(%s)", - CONF.PASSWD_FILE); - } else - DEBUG_PRINTF("async_getuserline", "Out of memory"); - - if (!ok) *line = '\0'; -} - - -/* And our wrapper functions calling the asynchroneous device */ - - -static bool -checkpw(clientenv *clenv, const char *passwd, const char *hash) -{ - struct async_checkpw_msg *amsg = (void *)clenv->aclenv->msg; - - mm_strncpy(amsg->un.args.passwd, passwd, 255); - mm_strncpy(amsg->un.args.hash, hash, 47); - async_call(clenv->aclenv, ASYNC_CHECKPW); - - return (amsg->un.res.matching); -} - - -static off_t -treesize(clientenv *clenv, const char *path, off_t min) -{ - struct async_treesize_msg *amsg = (void *)clenv->aclenv->msg; - quotanode *qnod; - size_t len; - bool found; - off_t size; - - /* First verify if we have a cached entry for this directory, so that - * we don't have to always recursively run through the tree. If so, - * then evaluate if the updated timestamp for the actual directory - * is older than the cached entry one before accepting to use the cached - * value. - */ - found = FALSE; - size = 0; - len = mm_strlen(path) + 1; - pth_mutex_acquire("a_lock, FALSE, NULL); - if ((qnod = (quotanode *)hashtable_lookup("a_table, path, len)) - != NULL) { - struct stat st; - struct tm tm1, tm2; - - if (lstat(path, &st) != -1) { - if (S_ISDIR(st.st_mode)) { - /* Verify directory modification time with cache time */ - localtime_r(&st.st_mtime, &tm1); - localtime_r(&qnod->updated, &tm2); - if (mktime(&tm2) >= mktime(&tm1)) { - size = qnod->homesize; - found = TRUE; - } - } - } - } - pth_mutex_release("a_lock); - - if (found) - return (size); - - mm_strncpy(amsg->un.args.path, path, 255); - async_call(clenv->aclenv, ASYNC_TREESIZE); - - /* If this function was invoked it means that at least one user is logged - * in. We therefore do not need to update or store a cache entry - * immediately, since the entry will be cached already until all instances - * of that user exit, in which case we will then make sure to remember the - * size into our long term cache. - */ - - return (amsg->un.res.size); -} - - -/* The index array and line pointers we return are static (pointers within the - * actual async message), but thread safe/specific. The data will remain safe - * to access until the same thread performs another async call. - */ -static bool -getuserline(clientenv *clenv, char **cols, char **line, const char *username) -{ - struct async_getuserline_msg *amsg = (void *)clenv->aclenv->msg; - bool ok = FALSE; - - mm_strncpy(amsg->un.args.username, username, 31); - async_call(clenv->aclenv, ASYNC_GETUSERLINE); - if (*(amsg->un.res.line)) { - int *index = amsg->un.res.cols, i; - char *ln = amsg->un.res.line; - - /* Perform index offset to pointer conversion, relocating the - * pointers the other process set for us. - */ - for (i = 0; i < CONF_COLUMNS; i++) - cols[i] = ln + index[i]; - *line = ln; - ok = TRUE; - } - - return (ok); -} diff --git a/mmsoftware/mmftpd/src/mmftpd.conf.5 b/mmsoftware/mmftpd/src/mmftpd.conf.5 deleted file mode 100644 index f88bab9..0000000 --- a/mmsoftware/mmftpd/src/mmftpd.conf.5 +++ /dev/null @@ -1,459 +0,0 @@ -.\" $Id: mmftpd.conf.5,v 1.10 2004/09/19 10:59:08 mmondor Exp $ -.\" -.\" Copyright (C) 2001-2004, Matthew Mondor -.\" All rights reserved. -.\" -.\" Redistribution and use in source and binary forms, with or without -.\" modification, are permitted provided that the following conditions -.\" are met: -.\" 1. Redistributions of source code must retain the above copyright -.\" notice, this list of conditions and the following disclaimer. -.\" 2. Redistributions in binary form must reproduce the above copyright -.\" notice, this list of conditions and the following disclaimer in the -.\" documentation and/or other materials provided with the distribution. -.\" 3. All advertising materials mentioning features or use of this software -.\" must display the following acknowledgement: -.\" This product includes software written by Matthew Mondor. -.\" 4. The name of Matthew Mondor may not be used to endorse or promote -.\" products derived from this software without specific prior written -.\" permission. -.\" -.\" THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR -.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES -.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. -.\" IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, -.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -.\" BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF -.\" USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF -.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -.\" -.Dd June 18, 2003 -.Dt MMFTPD.CONF 5 -.Os mmsoftware -.Sh NAME -.Nm mmftpd.conf -.Nd -.Xr mmftpd.conf 5 -configuration file for -.Xr mmftpd 8 -.Sh DESCRIPTION -The -.Nm /etc/mmftpd.conf -file may contain one or more keyword/value pairs per line, empty lines or -comments. A ';' or '#' character causes all other characters to be considered -as a comment, and to be ignored, until the end of the current line. It is -very important to enclose the value for a keyword in double quotes ('"' -characters) if the following characters are found in it: ';', '#', space and -tab. Here are documented the various possible keywords and their -allowed values, as well as their defaults. -.Pp -.Nm Daemon administration parameters -.Pp -.Bl -tag -width XXXXXXXX -offset indent -compact -.It Nm ASYNC_PROCESSES Ar "number" -The server internally launches a pool of asynchroneous processes to serve -functions which could block the main process for too long (this in fact -also means all threads with userspace threads such as provided by the Pth -library). This includes resolving hostnames, calculating MD5 hashes and -evaluating home directory sizes recursively. This also allows multiprocessor -systems to have an advantage when executing these functions. It should be -noted that two additional file descriptors are required in the main process -for each async process of the pool. The administrator will hence generally -configure -.Fa MAX_IPS , -.Fa MAX_PER_IP -and/or the maximum allowed number of file descriptors allowed for a process by -the kernel, accordingly. -.Pp -.It Nm CHROOT_DIR Ar "directory" -Location of the directory where the daemon optionally should -.Xr chroot 2 -in, or "" if it should not. Note that special configuration is generally -required for this. All files and directories the server will need access to -should also reside in that directory, like -.Nm /etc/mmftpdpasswd -and all the virtual user home directories. Other files may also need to -be copied to the new root in -.Nm /etc/ -such as -.Nm /etc/resolv.conf , /etc/hosts -and -.Nm /etc/group , -depending on the system's libc. The -.Xr mmstat 3 -and -.Xr syslog 3 -facilities will be initialized before -.Xr chroot 2 -is called to ensure that they continue working properly. -.Pp -.It Nm PASSWD_FILE Ar "file" -Location of the ftp virtual users configuration file, generally -.Nm "/etc/mmftpdpasswd" . -.Pp -.It Nm PID_PATH Ar "file" -Location where -.Nm mmftpd -writes it's pid file. This consists of a small file holding the process ID of -the main process, which can be used by scripts or the administrator to kill -the daemon properly. This file is safed before calling -.Xr chroot 2 -if -.Fa CHROOT_DIR -was set. -.Pp -.It Nm USER Ar "user" -Unprivileged user server process should run as. -.Pp -.It Nm GROUPS Ar "group,..." -Groups process should be part of, separated by commas, without any spaces. -It is important to also include here the 'mmstat' group, as well as all groups -which users of -.Xr mmftpdpasswd 5 -are allowed to use. -.Pp -.It Nm ALLOC_BUFFERS Ar "number" -On systems which are expected to operate under high load, serving a large -number of connections simultaneously, it may be ideal to increase this -variable. This pre-allocates buffers using the -.Xr mmlist 3 -library. For a system serving 64 connections, a suitable value may be 4. This -feature will mostly cause noticeable speed increase on systems which do not -provide very efficient memory allocators, such as glibc on Linux. The best -is to perform various tests and evaluate the required number to use here. -.Pp -.It Nm LOG_FACILITY Ar "facility" -Syslog facility which should be used for error logging. Should normally be -one of -.Sy LOG_AUTH , LOG_AUTHPRIV , LOG_CRON, LOG_DAEMON , -.Sy LOG_FTP , LOG_KERN , LOG_LPR , LOG_MAIL , -.Sy LOG_NEWS , LOG_SYSLOG , LOG_USER -or -.Sy LOG_UUCP . -See -.Xr syslog 3 -man page for more information. -.Pp -.It Nm LOG_LEVEL Ar "number" -This consists of the logging verbosity level, controlling which gravity of -messages should be logged through the -.Xr syslog 3 -.Ar LOG_FACILITY -facility. Here consists of the allowable levels and their meaning: -.Bd -literal -offset indent -0 = critical and important messages only -1 = connections are logged -2 = informational logging (transfers are logged) -3 = verbose logging (all commands except PASS, - plus replies) -4 = even PASS is logged, with the user passwords -.Ed -.El -.Pp -.Nm TCP server parameters -.Pp -.Bl -tag -width XXXXXXXX -offset indent -compact -.It Nm LISTEN_IPS Ar "n.n.n.n ..." -IPv4 addresses we should -.Xr bind 2 -to, separated by spaces. These typically consist of the addresses bound to -a wanted interface to accept connections from. 0.0.0.0 may be used if -connections from all interfaces should be allowed. Obviously, if more than -a single address is specified, they should be enclosed in double quotes. -.Pp -.It Nm SERVER_NAMES Ar "name ..." -Advertized server hostnames, separated by spaces, there should be the same -number of entries than for -.Ar LISTEN_IPS -, and of course also should be enclosed in double quotes if more than one -hostname is specified. These can be any wanted hostnames. -.Pp -.It Nm LISTEN_PORT Ar "number" -inet TCP port we should be listening for connections on. -.Pp -.It Nm RESOLVE_HOSTS Ar "boolean" -Resolving clients hostnames for logging can make the system slow depending on -DNS server reliability and service load. boolean should be TRUE or FALSE, -respectively. -.Pp -.It Nm MAX_ERRORS Ar "number" -Maximum number of bad commands user may issue per session before being dropped. -.Pp -.It Nm DELAY_ON_ERROR Ar "boolean" -If set to TRUE, at each bad command or error (see -.Ar MAX_ERRORS ) -a delay will be forced before accepting another command from that client. -At each error the delay will increase by one second. -.Pp -.It Nm MAX_IPS Ar "number" -Total maximum number of simultanious different IP addresses we allow -connections from. -.Pp -.It Nm MAX_PER_IP Ar "number" -Maximum simultanious connections per single IP address we accept. -.Pp -.It Nm CONNECTION_RATE Ar "number" -Maximum amount of connections to accept per IP address within -.Fa CONNECTION_PERIOD . -Can be 0 if no connection rate limits are wanted. -.Pp -.It Nm CONNECTION_PERIOD Ar "seconds" -The number of seconds within which a maximum of -.Fa CONNECTION_RATE -connections are accepted for an IP address. -.Pp -.It Nm BANDWIDTH_IN Ar "kilobytes" -This consists of the maximum bandwidth speed limit, in kilobytes per second, -to allow reading at from the FTP CONTROL connection. 0 disables bandwidth -shaping for this entry. Note that this does not affect the speed of DATA -FTP transfers (files) but only the speed at which commands from the user can be -received on the DATA connection. See -.Xr mmftpdpasswd 5 -file for DATA FTP transfer bandwidth shaping parameters. -.Pp -.It Nm BANDWIDTH_OUT Ar "kilobytes" -This consists of the maximum bandwidth speed limit, in kilobytes per second, -to allow writing to the FTP CONTROL connection. 0 disables bandwidth -shaping for this entry. Note that this does not affect the speed of DATA -FTP transfers (files) but only the speed at which we can send reply results -to user commands on the DATA connection. See -.Xr mmftpdpasswd 5 -file for DATA FTP transfer bandwidth shaping parameters. -.Pp -.It Nm GBANDWIDTH_IN Ar "kilobytes" -The global maximum bandwidth speed limit, in kilobytes per second, for the -whole server, at which it is allowed to read from clients. 0 for no limit. -.Pp -.It Nm GBANDWIDTH_OUT Ar "kilobytes" -The global maximum bandwidth speed limit, in kilobytes per second, for the -whole server, at which it is allowed to write to clients. 0 for no limit. -.Pp -.It Nm CONTROL_TIMEOUT Ar "seconds" -Maximum input/output timeout in seconds for the FTP control connection. Note -that this timeout will not cause the connection to be dropped if a transfer -is still currently ongoing. -.El -.Pp -.Nm FTP protocol related parameters -.Pp -.Bl -tag -width XXXXXXXX -offset indent -compact -.It Nm DATA_TIMEOUT Ar "seconds" -Maximum input/output timeout in seconds for the FTP DATA connections (file -transfers). -.Pp -.It Nm WELCOME_FILE Ar "file" -If specified, tells which file to display to the user just after connection. -.Pp -.It Nm MOTD_FILE Ar "file" -If specified, will be displayed to the user after a successful login. -.Pp -.It Nm DISPLAY_FILE Ar "file" -If specified, will cause any file by that name in the new directory to be -displayed to the user after a -.Sy CWD -(change current directory) or -.Sy CDUP -(return to previous directory level) occur. -.Pp -.It Nm REMEMBER_CWDS Ar "number" -If -.Ar DISPLAY_FILE -is used, number of last visited directories to remember in a FIFO; These are -used to prevent sending the directory message too often, over and over again -when it already has been displayed for this directory recently. -.Pp -.It Nm PASV_REMAP Ar ":: ..." -When set, this allows to configure -.Xr mmftpd 8 -to remap the address to -.Xr bind 2 -to when creating listening passive connections sockets, as well as the address -to advertize via FTP PASV/LPSV commands, based on client address patterns. -This is most useful to allow an FTP server to be behind a firewall without the -need for special FTPd proxying software on it. Which such settings, it is -possible for the firewall to simply redirect port 21, as well as a specific -range of high ports to the FTPd server box. The range of high ports addresses -may be specified by enabling -.Nm PASV_RANGE -and specifying the range through -.Nm PASV_RANGE_MIN -and -.Nm PASV_RANGE_MAX . -.Pp -Each remapping entry must be followed by a space, and each entry must be in -the following format: ::. -An example would be: "127.0.0.1:127.0.0.1:127.0.0.1 -192.168.1.*:192.168.1.10:192.168.1.10 *:0.0.0.0:66.11.161.166". In this -example, this allows connections from localhost to have listening sockets -bound to the localhost interface only, and to advertize to the client to -connect to localhost, for connections from the local LAN (192.168.1.*) to -cause binding of listening passive sockets on the LAN interface (address of -that host in the LAN), and to advertize to clients to connect to it's LAN -address as well. Finally, connections from any other address (i.e. redirected -by the firewall to 192.168.1.10 FTPd server) should cause listening passive -sockets to bind to 0.0.0.0 (all), and advertize to clients to connect to -the public IP address of the server, that is, 66.11.161.166. -.Pp -Note that a best pattern matching is performed. Patterns may use '?' wildcard -to match a single character and '*' wildcard to match any number of -characters. In case where no pattern matches the client address, default -behavior is used. The default behavior is to bind listening sockets to the -local interface of the server only, and to advertize that server address to -the clients. The default behavior is usually enough for FTP servers which are -not behind a firewall. -.Pp -Although some mapping configurations may need listening sockets to be bound to -0.0.0.0 (all), the server will always verify after a connection that the -originator address is proper for stability and security of the service. -Note also that EPSV does not need this, since it being wiser supports an empty -address to tell the client to connect at the same location it connected to -open the FTP control connection, but many clients only support PASV or LPSV. -.Pp -.It Nm PASV_RANGE Ar "boolean" -If -.Nm PASV_RANGE -is TRUE, the -.Nm PASV_RANGE_MIN -and -.Nm PASV_RANGE_MAX -parameters are taken into account when binding a port for use by the -.Sy PASV , -.Sy LPSV -and -.Sy EPSV -commands. This can especially be useful when used in conjunction with firewall -software such as ipf which can be told how to behave according to which range -of ports. Configurations for FTP servers with passive connections behind -firewalls then become possible without the need for a special userspace FTP -transparent proxy daemon to run on the firewall machine. Another useful -usage of this function is to ensure that ports do not automatically be -assigned by the system in a way that they be predictable, because a random -operation is used to obtain an available port in the range. Although mmftpd -will not allow an external peer which does not have the same IP address to -connect to another client's currently listening passive port, it still would -be possible to generally slow down FTP access from a peer who can easily -determine the ports being chosen. This is not a major concern however, because -they are many other ways to be a nuisance to any IP-based servers. If you -are concerned about this issue, use a range of 1025 to 65534 (the default) -and turn the feature on. -.Pp -.It Nm PASV_RANGE_MIN Ar "number" -If -.Nm PASV_RANGE -was set to TRUE, this specifies the lowest allowed port within the range of -ports to choose for passive connections. Values allowed are between 1025 and -65534. -.Pp -.It Nm PASV_RANGE_MAX Ar "number" -This is the upper range used for passive connections if -.Nm PASV_RANGE -is TRUE. Allowed values are from 1025 to 65534. Note that obviously, this -value should also be higher or equal to -.Nm PASV_RANGE_MIN . -Moreover, specifying too small a range will limit the number of possible -data (file and file list) transfers. A reasonable range should be chosen. -.Pp -.It Nm STATFAIL_LOGIN Ar "boolean" -If TRUE, failed logins (for which a user name does not exist) will be recorded -using the -.Xr mmstat 3 -facility, with a key in the form: -.Nm mmftpd|failed|login| -.Pp -.It Nm STATFAIL_PASSWORD Ar "boolean" -If TRUE, failed passwords for existing user names will be recorded using the -.Xr mmstat 3 -facility, with a key in the form: -.Nm mmftpd|failed|password|| -.Pp -.It Nm STATFAIL_PERMISSION Ar "boolean" -If TRUE, every permission denied errors which a user may stumble unto will be -recorded via the -.Xr mmstat 3 -facility, with a key as follows: -.Nm mmftpd|failed|permission|| -.Pp -.It Nm STATFAIL_PORT Ar "boolean" -If TRUE, illegal port errors generated from the -.Sy PORT -command will be recorded using -.Xr mmstat 3 , -in the form: -.Nm mmftpd|failed|port|| -.El -.Sh DEFAULTS -The following defaults are used: -.Pp -.Bd -literal -offset indent -compact -ASYNC_PROCESSES 3 -CHROOT_DIR "" -PASSWD_FILE "/etc/mmftpdpasswd" -PID_PATH "/var/run/mmftpd.pid" -USER mmftpd -GROUPS mmftpd,mmstat -LOG_FACILITY LOG_AUTHPRIV -LOG_LEVEL 3 -ALLOC_BUFFERS 1 -SERVER_NAMES "ftp.localhost" -LISTEN_IPS "127.0.0.1" -LISTEN_PORT 21 -RESOLVE_HOSTS FALSE -MAX_ERRORS 16 -DELAY_ON_ERROR FALSE -MAX_IPS 64 -MAX_PER_IP 1 -CONNECTION_RATE 10 -CONNECTION_PERIOD 30 -BANDWIDTH_IN 1 -BANDWIDTH_OUT 1 -GBANDWIDTH_IN 0 -GBANDWIDTH_OUT 0 -CONTROL_TIMEOUT 300 -DATA_TIMEOUT 300 -PASV_REMAP "" -PASV_RANGE FALSE -PASV_RANGE_MIN 1025 -PASV_RANGE_MAX 65534 -WELCOME_FILE "" -MOTD_FILE "" -DISPLAY_FILE "README" -REMEMBER_CWDS 16 -STATFAIL_LOGIN FALSE -STATFAIL_PASSWORD FALSE -STATFAIL_PERMISSION FALSE -STATFAIL_PORT FALSE -.Ed -.Sh AUTHOR -.Nm mmftpd -was written by Matthew Mondor, and is -Copyright (c) 2001-2004, Matthew Mondor, All rights reserved. -.Sh FILES -.Bl -tag -width XXXXXXXXXXXXXXXXXXXXXX -compact -.It Pa /etc/mmftpd.conf -This file -.Pp -.It Pa /etc/mmftpdpasswd -The -.Xr mmftpdpasswd 5 -user database file. -.Pp -.It Pa /usr/local/sbin/mmftpd -The -.Xr mmftpd 8 -FTP server binary itself. -.El -.Sh SEE ALSO -.Xr mmftpd 8 , -.Xr mmftpdpasswd 5 , -.Xr mmstat 8 , -.Xr mmstat 3 , -.Xr syslog 3 , -.Xr chroot 2 , -.Xr bind 2 . -.Sh BUGS -Not really a bug, but tied to each interface could be most connection control -options. -.Pp -Please report any bug to mmondor@accela.net diff --git a/mmsoftware/mmftpd/src/mmftpd.h b/mmsoftware/mmftpd/src/mmftpd.h deleted file mode 100644 index a3dacd4..0000000 --- a/mmsoftware/mmftpd/src/mmftpd.h +++ /dev/null @@ -1,444 +0,0 @@ -/* $Id: mmftpd.h,v 1.25 2004/09/19 10:59:08 mmondor Exp $ */ - -/* - * Copyright (C) 2001-2004, Matthew Mondor - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software written by Matthew Mondor. - * 4. The name of Matthew Mondor may not be used to endorse or promote - * products derived from this software without specific prior written - * permission. - * - * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - - - - -#ifndef MMFTPD_H -#define MMFTPD_H - - - - -/* HEADERS */ - -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - - - - -/* DEFINITIONS */ - -#define DAEMON_NAME "mmftpd" -#define DAEMON_VERSION "0.0.18/mmondor" - -/* Transfer buffer size */ -#define T_BUFSIZE 16384 -/* fdb buffering size */ -#define FDB_BUFSIZE 8192 - -/* Configuration file columns */ -enum columns { - CONF_USER = 0, - CONF_GROUP, - CONF_PASS, - CONF_STATS, - CONF_CHKOWN, - CONF_UPLOAD, - CONF_MODIFY, - CONF_MUMASK, - CONF_UMASK, - CONF_MAXLOGINS, - CONF_MAXHOMESIZE, - CONF_MINFILESIZE, - CONF_BW_IN, - CONF_BW_OUT, - CONF_HOME, - CONF_COLUMNS -}; - -enum transferthread_states { - TTSTATE_DONE = 0, - TTSTATE_INITIAL, - TTSTATE_CONNECT, - TTSTATE_TRANSFER -}; - -/* Negative states are used by the state swapper, others are real states */ -#define STATE_ERROR -3 -#define STATE_END -2 -#define STATE_CURRENT -1 -#define STATE_AUTH 0 -#define STATE_MAIN 1 - -/* Transfer types */ -#define TYPE_ASCII 1 -#define TYPE_IMAGE 2 -#define TYPE_LOCAL 3 - -/* Transfer requests */ -#define REQ_NONE 0 -#define REQ_STATUS 1 -#define REQ_NEWPORT 2 -#define REQ_TRANSFER 3 -#define REQ_ABORT 4 -#define REQ_QUIT 5 - -/* Transfer end reason */ -#define TR_OK 0 -#define TR_ABORTED -1 -#define TR_TIMEOUT -2 -#define TR_QUOTA -3 -#define TR_LOST -4 - -/* Our true asynchroneous functions */ -#define ASYNC_CHECKPW 1 -#define ASYNC_TREESIZE 2 -#define ASYNC_GETUSERLINE 3 - -/* Error registration macro */ -#define REGISTER_ERROR() do { \ - clenv->errors++; \ - if (CONF.DELAY_ON_ERROR) \ - pth_sleep(clenv->errors); \ -} while (FALSE) - -#define REGISTER_PERMISSION() do { \ - if (CONF.STATFAIL_PERMISSION) \ - mmstat(&clenv->pstat, STAT_UPDATE, 1, \ - "mmftpd|failed|permission|%s|%s", \ - clenv->user, clenv->c_ipaddr); \ -} while (FALSE) - -#define REGISTER_PORT() do { \ - if (CONF.STATFAIL_PORT) \ - mmstat(&clenv->pstat, STAT_UPDATE, 1, \ - "mmftpd|failed|port|%s|%s", \ - clenv->user, clenv->c_ipaddr); \ -} while (FALSE) - - - -/* STRUCTURES */ - -/* We store config file read results in this structure */ -typedef struct config { - char CHROOT_DIR[256], PASSWD_FILE[256], PID_PATH[256], USER[32], - GROUPS[256], LOG_FACILITY[32], SERVER_NAMES[1024], LISTEN_IPS[1024], - WELCOME_FILE[256], MOTD_FILE[256], DISPLAY_FILE[64], PASV_REMAP[1024]; - long ALLOC_BUFFERS, LOG_LEVEL, LISTEN_PORT, MAX_ERRORS, MAX_IPS, - MAX_PER_IP, CONNECTION_RATE, CONNECTION_PERIOD, CONTROL_TIMEOUT, - DATA_TIMEOUT, BANDWIDTH_IN, BANDWIDTH_OUT, GBANDWIDTH_IN, - GBANDWIDTH_OUT, REMEMBER_CWDS, ASYNC_PROCESSES, PASV_RANGE_MIN, - PASV_RANGE_MAX; - bool RESOLVE_HOSTS, DELAY_ON_ERROR, PASV_RANGE, STATFAIL_LOGIN, - STATFAIL_PASSWORD, STATFAIL_PERMISSION, STATFAIL_PORT; -} CONFIG; - -/* We communicate with our transfer thread using this message structure */ -typedef struct transfermsg { - pth_message_t msg; /* Internal message node */ - int request; /* Request to send to device */ - bool result; /* Result status after device request */ - u_int16_t port; /* PORT or PASV port for next transfer */ - int file; /* File descriptor to read/write from/to */ - int rrate, wrate; /* Transfer dl/ul rate */ - char *ipaddr; /* If PORT, address to connect to */ - char *path; /* Path to be passed to ls() if list=TRUE */ - bool passive; /* Either next xfer is PORT or PASV mode */ - bool ongoing; /* TRUE when a transfer is in progress */ - bool download; /* TRUE if transfer direction is to user */ - char list; /* 0=File xfer, 1=LIST, 2=NLST, 3=empty */ - int64_t rbytes, wbytes; /* Number of bytes transfered */ - int64_t dlfiles, ulfiles; /* Number of files transfered */ -} transfermsg; - -/* This structure holds the current environment for a user, shared among all - * functions of all states - */ -typedef struct clientenv { - pnode_t node; - unsigned long id; /* Our unique connection ID */ - int timeout; /* Control connection timeout in ms */ - int gid; /* Group set to new files */ - int bw_in, bw_out; /* Maximum dl/up bandwidth in KB/s */ - fdbuf *fdb; /* Buffered handle to fd */ - char *buffer; /* Our command line buffer */ - char *c_hostname; /* Pointer to client's hostname */ - char *c_ipaddr; /* Pointer to client's IP address string */ - char *sipaddr; /* Ptr to IP addr for PASV/LPSV replies */ - char *user; /* Username of logged user */ - char *tuser; /* Temporary username used by state_auth() */ - char *passwd; /* Temporary, used by auth code */ - char *home; /* User's real home directory path */ - char *group; /* Text representation of gid */ - char *rnfr; /* Used for RNFR/RNTO */ - struct lusernode *unode; /* User's lusernode, for quick lookup */ - char cwd[MMPATH_MAX]; /* User's current directory (chrooted) */ - struct transfermsg tmsg; /* Only need one shared msg with tthread */ - pth_msgport_t rport, sport; /* Message ports (tthread and ours) */ - pth_event_t rring; /* Ring of events we wait for */ - pth_t tthread; /* Thread ID of our transfer thread */ - long errors; /* Total number of errors that occured */ - long attempts; /* Auth state temporary counter */ - long maxlogins; /* Allowed simultanious logins for user */ - off_t maxhomesize; /* Maximum home directory size (or -1) */ - off_t minfilesize; /* If limits used, minimum file size */ - off_t rest; /* Restore offset */ - mode_t umask; /* Current umask */ - bool anonymous; /* TRUE if anonymous login acount */ - bool login; /* TRUE if user currently logged in */ - bool upload; /* Right to upload */ - bool modify; /* Right to modify */ - bool mumask; /* Right to change umask */ - bool stats; /* If download counters are wanted */ - bool checkowner; /* If we should check for file ownership */ - unsigned char type; /* Current transfer TYPE */ - fifo64_t visited; /* FIFO buffer of visited dirs hashes */ - u_int32_t uipaddr; /* Used for bind() on passive ports */ - u_int32_t cipaddr; /* Client's IP address */ - struct fifonode *fifobuf; /* Internal buffer for visited */ - struct iface *iface; /* To obtain our server hostname */ - struct async_clenv *aclenv; /* Thread context for async_call() support */ - mmstat_t vstat, pstat; /* Handles for mmstat */ -} clientenv; - -/* This structure is shared by simultanious logins of a same user. It thus - * allows to control how many connections of the same user are allowed, and - * to share a common current home directory size value. - */ -typedef struct lusernode { - hashnode_t node; - char user[32]; /* User name (key) */ - pth_mutex_t lock; /* For treesize_edit() */ - long logins; /* Simultanious current logins for user */ - off_t homesize; /* Current home directory size for user */ -} lusernode; - -/* This structure is used for a longer term cache of user home directory - * sizes so that we can revert to recursive traversal only when strictly - * necessary. - */ -typedef struct quotanode { - hashnode_t node; - char dir[256]; /* Directory name (key) */ - off_t homesize; /* Size in bytes */ - time_t updated; /* Last cache update */ -} quotanode; - -/* This serves for fast command lookup */ -struct commandnode { - hashnode_t node; - u_int32_t hash; - int index; - struct command *command; -}; - -/* Used for mmfd thread support delegation/abstraction */ -struct mutexnode { - pnode_t node; - pth_mutex_t mutex; -}; - -/* Used to efficiently allocate FIFO buffers for recently visited directories -*/ -struct fifonode { - pnode_t node; - u_int64_t buf[1]; - /* Will be expanded here */ -}; - -/* This defines a state */ -typedef struct state { - int (**functions)(clientenv *); /* array of commands for state */ - int errcode; /* Code to reply if a command is NULL */ - char *errtext; /* Text to reply if a command is NULL */ -} state; - -/* A command of a state */ -typedef struct command { - int loglevel; /* Log level required to log this command */ - char *name; /* Command name string */ - char *args; /* Used for HELP, arguments of command */ - char *desc; /* Used for HELP, command description */ -} command; - -/* Used to hold index mapping TR_* reasons to information for reply() */ -struct tr_messages { - int code; - char *msg; -}; - -/* Used by our asynchoneous functions */ -struct async_checkpw_msg { - struct async_msg msg; - union { - struct { - bool matching; - } res; - struct { - char hash[48]; - char passwd[256]; - } args; - } un; -}; - -struct async_treesize_msg { - struct async_msg msg; - union { - struct { - off_t size; - } res; - struct { - char path[256]; - off_t min; - } args; - } un; -}; - -struct async_getuserline_msg { - struct async_msg msg; - union { - struct { - int cols[CONF_COLUMNS + 1]; - char line[256]; - } res; - struct { - char username[32]; - } args; - } un; -}; - -struct pasv_remap { - const char *pattern; - const char *advertize; - u_int32_t bind; -}; - - - -/* PROTOTYPES */ - -/* State functions */ -static int all_quit(clientenv *); -static int all_help(clientenv *); -static int all_noop(clientenv *); -static int all_beer(clientenv *); -static int all_user(clientenv *); -static int all_pass(clientenv *); -static int all_syst(clientenv *); -static int auth_user(clientenv *); -static int auth_pass(clientenv *); -static int main_cwd(clientenv *); -static int main_cdup(clientenv *); -static int main_port(clientenv *); -static int main_lprt(clientenv *); -static int main_eprt(clientenv *); -static int main_pasv(clientenv *); -static int main_lpsv(clientenv *); -static int main_epsv(clientenv *); -static int main_type(clientenv *); -static int main_stru(clientenv *); -static int main_mode(clientenv *); -static int main_retr(clientenv *); -static int main_stor(clientenv *); -static int main_stou(clientenv *); -static int main_appe(clientenv *); -static int main_allo(clientenv *); -static int main_rest(clientenv *); -static int main_rnfr(clientenv *); -static int main_rnto(clientenv *); -static int main_abor(clientenv *); -static int main_dele(clientenv *); -static int main_rmd(clientenv *); -static int main_mkd(clientenv *); -static int main_mdtm(clientenv *); -static int main_pwd(clientenv *); -static int main_list(clientenv *); -static int main_nlst(clientenv *); -static int main_site(clientenv *); -static int main_size(clientenv *); -static int main_stat(clientenv *); - -int main(int, char **); - -static bool hash_commands(struct command *, size_t); -static u_int32_t commandnode_keyhash(const void *, size_t); -static int commandnode_keycmp(const void *, const void *, size_t); -static bool reply(fdbuf *, int, bool, char *, ...); -static int readconfline(char *, fdbuf *, int, int, char **); -static void stripoptions(char *); -static off_t recursive_treesize(char *, off_t); -static bool treesize_edit(clientenv *, off_t); -static bool checkuser(clientenv *, const char *); - -static clientenv *alloc_clientenv(void); -static clientenv *free_clientenv(clientenv *); -static bool start_transfer_thread(clientenv *); -static void stop_transfer_thread(clientenv *); -static bool transfer_request(int, bool, clientenv *); -static bool file_out(fdbuf *, char *); -static bool directory_message(clientenv *, int); -static int best_match(const char *, const char *); -static bool valid_ipaddress(const char *); -static bool pasv_remap_parse(void); -static bool pasv_remap(u_int32_t *, char **, const char *); - -static int handleclient(unsigned long, int, clientlistnode *, struct iface *, - struct async_clenv *); - -static bool pasv_bind(clientenv *, struct sockaddr_in *, int *, int *); -static void transferthread(struct thread_object *, void *); -static void transferthread_main(clientenv *, fdbuf *); - -static void *_pth_mutex_create(void); -static void *_pth_mutex_destroy(void *); -static void _pth_mutex_lock(void *); -static void _pth_mutex_unlock(void *); -static void _pth_thread_yield(void); -static bool _pth_eintr(void); -static bool eintr(void); - -static void async_checkpw(struct async_msg *); -static void async_treesize(struct async_msg *); -static void async_getuserline(struct async_msg *); -static bool checkpw(clientenv *, const char *, const char *); -static off_t treesize(clientenv *, const char *, off_t); -static bool getuserline(clientenv *, char **, char **, const char *); - - - - -#endif diff --git a/mmsoftware/mmftpd/src/mmftpdpasswd.5 b/mmsoftware/mmftpd/src/mmftpdpasswd.5 deleted file mode 100644 index 80ecfae..0000000 --- a/mmsoftware/mmftpd/src/mmftpdpasswd.5 +++ /dev/null @@ -1,218 +0,0 @@ -.\" $Id: mmftpdpasswd.5,v 1.5 2004/05/05 23:59:55 mmondor Exp $ -.\" -.\" Copyright (C) 2001-2004, Matthew Mondor -.\" All rights reserved. -.\" -.\" Redistribution and use in source and binary forms, with or without -.\" modification, are permitted provided that the following conditions -.\" are met: -.\" 1. Redistributions of source code must retain the above copyright -.\" notice, this list of conditions and the following disclaimer. -.\" 2. Redistributions in binary form must reproduce the above copyright -.\" notice, this list of conditions and the following disclaimer in the -.\" documentation and/or other materials provided with the distribution. -.\" 3. All advertising materials mentioning features or use of this software -.\" must display the following acknowledgement: -.\" This product includes software written by Matthew Mondor. -.\" 4. The name of Matthew Mondor may not be used to endorse or promote -.\" products derived from this software without specific prior written -.\" permission. -.\" -.\" THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR -.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES -.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. -.\" IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, -.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -.\" BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF -.\" USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF -.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -.\" -.Dd 1 Jan, 2003 -.Dt MMFTPDPASSWD 5 -.Os -.Sh NAME -.Nm mmftpdpasswd -.Nd -.Xr mmftpdpasswd 5 -user database configuration file for -.Xr mmftpd 8 -.Sh SECURITY CONSIDERATIONS -The -.Nm /etc/mmftpdpasswd -file should only be readable by the superuser and by -.Nm mmftpd -because all FTP user password hashes are stored in it. -This is usually done using a permission mode of 0640, with root owner, and -mmftpd group on that file: -.Bd -literal -offset indent --rw-r----- 1 root mmftpd 2544 Sep 8 2002 /etc/mmftpdpasswd -.Ed -.Sh DESCRIPTION -The -.Nm /etc/mmftpdpasswd -file holds the database of allowed virtual FTP users for the -.Xr mmftpd 8 -server. These consist of one user entry per line, with a specific number -of columns which represent various customizable options for the user. Lines -starting by ';' or '#' characters are ignored, as well as blank lines. -.Pp -Each line should have exactly 15 columns which may be separated by spaces -and/or tabs. There is no need to signal the server when this file is modified. -The columns are described as follows, in the order they should appear: -.Bl -column XXXXXXXXX XXXX -offset indent -.It Sy Name Ta Sy Abrev. Ta Sy Description -.Pp -.It UserName Ta un Ta -Login name of this FTP user. Anonymous logins are only allowed if an entry -exists with the 'anonymous' UserName. A name should be no longer than 31 -characters in length (extraneous characters are ignored). -.Pp -.It Group Ta gr Ta -Group which should be set to newly created files. Note that -.Nm /etc/mmftpd.conf -should also include this group in it's -.Ar GROUPS -parameter. (See -.Xr mmftpd.conf 5 -man page). -.Pp -.It Password Ta pw Ta -.Nm mmpasswd -generated hash of user password. Note that for the 'anonymous' user, anything -can be used here (usually '*'). It also is possible to use '*' for accounts -which are allowed to login without password, or using any password. These -password hashes should be compatible with the system ones, it thus would -be possible to place here an entry found in /etc/shadow on Linux, or from -vipw on BSD, for instance. -.Xr crypt 3 -is used to generate and match these. See -.Xr mmpasswd 8 -for more information. -.Pp -.It Statistics Ta s Ta -Should be set to 1 if download statistics counters are wanted for all available -files under this user's home directory. These will be kept using the -.Xr mmstat 3 -facility, with a key like -.Nm mmftpd../full/chrooted/path/to/file . -Should be set to 0 if no download statistics counter is wanted for this user. -.Pp -.It CheckOwner Ta c Ta -If 1, will deny operation on files and directories wich owner do not match the -current effective ID (the user the mmftpd daemon runs under, usually mmftpd). -Otherwise, 0 can be used to disable checking, which may be useful for some -setups eg: read-only account using a directory with files which belong to -another user, but which mmftpd user can read. This will also prevent files -owned by another user from being shown with -.Sy LIST -when 1, and will prevent -.Sy CWD -into directories which are not readable and executable by other users when 0. -.Pp -.It Upload Ta u Ta -1 if user is allowed to use upload commands -.Sy ( APPE , STOR , STOU ) , -or 0 if not -.Pp -.It Modify Ta m Ta -1 if user is permitted to execute modification commands -.Sy ( APPE , SITE CHMOD , DELE , -.Sy MKD , RMD , RNFR , RNTO ) , -or 0 otherwise. -.Pp -.It ModifyUMask Ta M Ta -Should be 1 if user should be able to modify his default umask -.Sy ( SITE UMASK ) , -or 0. -.Pp -.It UMask Ta um Ta -The default umask (See -.Xr umask 2 -man page) which will affect creation of new files, if any. This umask should be -a three digit octal number, beginning with '0'. Internally, it will always be -fixed so that the owner permissions of newly created files be '6' (rw) and new -directories '7' (rwx) because of the virtual nature of -.Nm mmftpd -server. A generally good umask consists of 027, which would allow other -processes of the same group (eg: apache) only to access the files. -.Pp -.It Logins Ta l Ta -Maximum number of simultaneous logins allowed for this user, or 0 for no limit. -.Pp -.It MaxHomeSize Ta mhds Ta -Maximum size user's virtual home directory should expand to, or 0 for no limit. -Of course, this has no effect if -.Ar Upload -was disabled. This value should be expressed in kilobytes. -.Pp -.It MinFileSize Ta mfs Ta -Minimum file size required for a new directory or file creation, which is -generally filesystem-specific, in bytes. When accounting quotas, any file -smaller than this is considered this size, same for directory entries. -This column is only taken into account if -.Ar MinFileSize -was set to a non-zero value. -.Pp -.It DownSpeed Ta dl Ta -Maximum speed, specified in kilobytes per second, at which this user may -download files (this does not affect the control connection, only data ones. -See -.Xr mmftpd.conf 5 -for details about bandwidth shaping the control connection). If 0 is specified -here, no download speed limit is applied. -.Pp -.It UpSpeed Ta ul Ta -Maximum speed, specified in kilobytes per second, at which this user may -upload files (does not affect control connection speed, only data transfers. -See -.Xr mmftpd.conf 5 -for details about bandwidth shaping for the control connection). If 0 is -specifid here, no upload speed limit is applied. -.Pp -.It HomeDir Ta hd Ta -Specifies the absolute pathname to the user's home directory, which of course -should be owned by the user the server process runs under. The user will have -the impression to be locked under a -.Xr chroot 2 -jail into this directory. -.El -.Sh EXAMPLES -Here are a few user examples: -.Pp -.Bd -literal -; un gr pw s c u m M um l mhds mfs dl ul hd -anonymous mmftpd * 1 1 0 0 0 027 16 0 0 16 0 /home/mmftpd/anonymous -johndoe www $1$m5zXKU$.pRmT7idwVLKdq/83YOa8. 0 1 1 1 0 027 1 16384 4 8 8 /home/www/johndoe -.Ed -.Sh AUTHOR -.Nm mmftpd -was written by Matthew Mondor, and is -Copyright (c) 2001-2004, Matthew Mondor, All rights reserved. -.Sh FILES -.Bl -tag -width XXXXXXXXXXXXXXXXXXXXXX -compact -.It Pa /etc/mmftpd.conf -The global -.Nm mmftpd -.Xr mmftpd.conf 5 -configuration file where all general parameters can be customized. -.Pp -.It Pa /etc/mmftpdpasswd -This file. -.Pp -.It Pa /usr/local/sbin/mmftpd -The -.Xr mmftpd 8 -FTP server binary itself. -.El -.Sh SEE ALSO -.Xr mmftpd 8 , -.Xr mmpasswd 8 , -.Xr mmftpd.conf 5 , -.Xr mmstat 8 , -.Xr chroot 2 , -.Xr umask 2 , -.Xr chmod 2 . -.Sh BUGS -Please report any bug to mmondor@accela.net diff --git a/mmsoftware/mmidentd/GNUmakefile b/mmsoftware/mmidentd/GNUmakefile deleted file mode 100644 index fafd9ec..0000000 --- a/mmsoftware/mmidentd/GNUmakefile +++ /dev/null @@ -1,21 +0,0 @@ -# $Id: GNUmakefile,v 1.1 2004/05/05 10:08:46 mmondor Exp $ - -OBJS := mmidentd.o - -CFLAGS += -Wall - - -all: mmidentd - -%.o: %.c - cc -c ${CFLAGS} -I. -o $@ $< - -mmidentd: $(MMLIBS) $(OBJS) - cc -o $@ -lc $(OBJS) - -install: - install -cs -o 0 -g 0 -m 550 mmidentd /usr/local/libexec -# install -c -o 0 -g 0 -m 444 mmidentd.8 /usr/local/man/man8 - -clean: - rm -f mmidentd $(OBJS) diff --git a/mmsoftware/mmidentd/mmidentd.c b/mmsoftware/mmidentd/mmidentd.c deleted file mode 100644 index 042f732..0000000 --- a/mmsoftware/mmidentd/mmidentd.c +++ /dev/null @@ -1,323 +0,0 @@ -/* $Id: mmidentd.c,v 1.3 2004/04/30 00:01:18 mmondor Exp $ */ - -/* - * Copyright (C) 2003, Matthew Mondor - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Matthew Mondor. - * 4. The name of Matthew Mondor may not be used to endorse or promote - * products derived from this software without specific prior written - * permission. - * 5. Redistribution of source code may not be released under the terms of - * any GNU Public License derivate. - * - * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -/* - * mmidentd - A simple random ident server - * Copyright (c) 2003, Matthew Mondor, - * ALL RIGHTS RESERVED. - * - * This is expected to be installed as /usr/local/libexec/mmidentd and to be - * launched as required by the inetd server, under an unprivileged user. - * The only required argument consists of the dictionary to extract random - * words from. - * An example configuration line for /etc/inetd.conf follows: - * - * ident stream tcp nowait nobody:nogroup /usr/local/libexec/mmidentd \ - * mmidentd /usr/share/dict/words - * - * The way this program works is simple. It uses the supplied dictionary and - * to efficiently choose a random word, then reverses it and converts it to - * lowercase. Only words within 3 to 10 characters in length will be used. - * Words are expected to be separated by any ASCII character below 33, which - * includes spaces, tabs, '\r' and '\n' characters. The size of the dictionary - * file can reach the maximum limit which the filesystem or OS allows. On BSD - * systems, 64-bit is used by default for filesystem offsets which allows - * very large files to be used. - * - * This program uses the "/dev/urandom" device, if present, to seed the pseudo - * random number generator, but will fall back to using time(2) for seeding - * if that device cannot be found or used. The 4.2BSD random(3) PRNG is used - * over rand(3) for best results. Using this technique, it is unlikely that - * a peer could evaluate the current state of the OS PRNG device, and the - * results are rather good still. - * - * If errors occur, the 'unknown' user response will always be issued. - * This can happen if the two required file parameter is missing or wrong, - * if the file cannot be found, or if mapping problems exist. - * - * I previously wrote other random ident servers, the first one used to - * alternate one vowel and one consonant, using random for the response length - * and which of the character classes to begin with. The second was similar - * but occasionally allowed two contiguous non-identical vowels plus two - * occasional contiguous 'o' vowels, so that it generated more plausible - * "words". However, if a dictionary is available, the words are bound to - * even be more plausible :), and when reverted it prevents some of those - * words from being offensive (which could be the case using a standard - * dictionary), and provides an interesting effect. - * - * For optimum performance, this program uses a single open(2)/mmap(2) pass - * to access the dictionary. This allows mapping the whole file virtually - * into the process, but the pages which actually need to be imported into - * the process space are the ones we perform accesses to (generally one page - * only being faulted in when a proper dictionary is provided). Moreover, - * only pages which weren't recently used need to be read from disk, and that's - * also generally one single page. If you experience that this is not the case, - * perhaps consider using a proper 3-10 character words dictionary, and running - * NetBSD :) - * - * It would not have been possible to use the ANSI stdio and achieve the same - * performance. First, it uses it's own memory buffers, which obviously need - * to be allocated into the process's space. Second, it attempts to use that - * buffering, resulting in many unnecessary memory copy operations. Moreover, - * we would need to perform random access reads, which mostly discard the - * purpose of using stdio. It would work, however. It would just have been - * both more of a hassle to write and slower. Anyways, libc stdio is just - * another layer which needs to use the unix system calls underneath, and - * this program is intended for unix systems. We actually don't use stdio - * at all in this very simple program. - * - * This program should be fairly small when compiled statically. This may be - * wanted to speed up application launching time, or to make it independant - * of the C library. Results in a 16k static executable on NetBSD after - * stripping. This small size is again caused by the stdio avoidance. - */ - - - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - - - -#define RANDDEV "/dev/urandom" -#define NULLDEV "/dev/null" -#define DEFAULT "unknown" -#define BUFSIZE 32 -#define RBUFSIZE 64 -#define RETRIES 64 -#define TIMEOUT 30 -#define ISCHAR(c) isprint(c) - - - -int main(int, char **); -static void randinit(void); -static void strrevtolower(char *); - - - -int main(int argc, char **argv) -{ - int fd, nfd; - size_t ilen; - char ident[BUFSIZE], buf[BUFSIZE]; - struct pollfd pfd[] = { - {0, POLLIN | POLLHUP | POLLERR, 0} - }; - - nfd = open(NULLDEV, O_RDWR); - - /* For safety, get rid of stderr. */ - if (nfd != -1) - (void) dup2(nfd, 2); - - /* Initialize the default response and the PRNG. */ - (void) memcpy(ident, DEFAULT, sizeof(DEFAULT)); - ident[sizeof(DEFAULT)] = '\0'; - ilen = sizeof(DEFAULT); - randinit(); - - /* If any of the following block fails, DEFAULT response will be issued. - */ - if (argc == 2 && (fd = open(argv[1], O_RDONLY)) != -1) { - struct stat st; - u_int32_t fsize; - char *dict; - - if (fstat(fd, &st) == 0) { - fsize = (u_int32_t)st.st_size; - if ((dict = mmap(NULL, (size_t)fsize, PROT_READ, - MAP_FILE | MAP_PRIVATE, fd, (off_t)0)) - != MAP_FAILED) { - int tries; - size_t len; - /* Start, Center and End pointers */ - register char *sptr, *cptr, *eptr, *tptr; - -#ifndef __GLIBC__ - /* On BSD systems, inform the VM of the fact that we'll - * access random pages, for optimum performance. - */ - (void) madvise(dict, (size_t)fsize, MADV_RANDOM); -#endif /* __GLIBC__ */ - - /* Not to reach end pointer */ - tptr = dict + fsize; - /* Loop until we exceeded the number of retries or until we - * obtained a valid word to use - */ - for (tries = 0, len = 0; - tries < RETRIES && (len < 3 || len > 10); tries++) { - /* Slice the file randomly with cptr */ - cptr = dict + (random() % fsize); - /* Run left from cptr with sptr */ - for (sptr = cptr; sptr >= dict && ISCHAR(*sptr); sptr--) ; - if (!ISCHAR(*sptr)) - sptr++; - /* And right from cptr with eptr */ - for (eptr = cptr; eptr < tptr && ISCHAR(*eptr); eptr++) ; - /* We now have a word, evaluate it's length. */ - len = eptr - sptr; - } - if (tries < RETRIES) { - /* It seems that we isolated a proper word. Generate the - * ident response string with it, to replace the default - * DEFAULT one. - */ - (void) memcpy(ident, sptr, len); - ident[len] = '\0'; - strrevtolower(ident); - ilen = len; - } - (void) munmap(dict, (size_t)fsize); - } - } - (void) close(fd); - } - - /* Now query other end for the 'port, port' string request. - * We could setup a timer here, and use fgets(2). But as unix uses line - * buffering by default, and that poll(2) will nicely respect a timeout, - * let's again avoid stdio, which by the way calls extraneous syscalls, - * including initialization of the malloc(3) system, which in turn can - * call a bunch of brk(2) :) So let's add a few minutes of coding to save - * many minutes of accumulated CPU time during the very successfull - * years of serving using this identd! (sarcasm) - */ - if ((poll(pfd, 1, TIMEOUT * 1000)) == 1 && ((*pfd).revents & POLLIN)) { - size_t len; - - if ((len = read(0, buf, BUFSIZE - 1)) > 0) { - register char *ptr, *tptr, *aptr, *bptr; - size_t alen, blen; - char out[64]; - - buf[len] = '\0'; - /* We don't need input anymore */ - if (nfd != -1) - (void) dup2(nfd, 0); - - /* Parse port numbers. We do not even need to perform any ASCII to - * decimal convertion. - */ - tptr = buf + len; - for (ptr = buf; ptr < tptr && !isdigit(*ptr); ptr++) ; - for (aptr = ptr; ptr < tptr && isdigit(*ptr); ptr++) ; - for (alen = ptr - aptr; ptr < tptr && !isdigit(*ptr); ptr++) ; - for (bptr = ptr; ptr < tptr && isdigit(*ptr); ptr++) ; - blen = ptr - bptr; - - /* Now generate our very authentic output response, and send it. - * We substitute 0 for reply port numbers if the parsed ones were - * illegal or missing. - */ - ptr = out; - if (alen > 0 && alen < 6) { - (void) memcpy(ptr, aptr, alen); - ptr += alen; - } else - *ptr++ = '0'; - *ptr++ = ' '; - *ptr++ = ','; - *ptr++ = ' '; - if (blen > 0 && blen < 6) { - (void) memcpy(ptr, bptr, blen); - ptr += blen; - } else - *ptr++ = '0'; - (void) memcpy(ptr, " : USERID : UNIX : ", 19); - ptr += 19; - (void) memcpy(ptr, ident, ilen); - ptr += ilen; - *ptr++ = '\r'; - *ptr++ = '\n'; - *ptr = '\0'; - (void) write(1, out, (size_t)(ptr - out)); - } - } - - if (nfd != -1) - (void) close(nfd); - - return 0; -} - - -/* First attempts to seed the 4.2BSD random number generator using the - * "/dev/urandom" device. If we cannot, falls back to simple time-dependent - * seeding. - */ -static void randinit(void) -{ - int fd, ok = -1; - - if ((fd = open(RANDDEV, O_RDONLY)) != -1) { - unsigned long s; - - if ((read(fd, &s, sizeof(unsigned long))) == sizeof(unsigned long)) { - srandom(s); - ok = 0; - } - (void) close(fd); - } - if (ok == -1) - srandom((unsigned long)time(NULL)); -} - - -/* Reverses the supplied string, but also converts it to lowercase. */ -static void strrevtolower(char *str) -{ - register char *p1, *p2, t; - - for (p1 = p2 = str; *p2 != '\0'; p2++) - *p2 = (char)tolower(*p2); - if (p2 > p1) - p2--; - - for (; p1 < p2; p1++, p2--) { - t = *p1; - *p1 = *p2; - *p2 = t; - } -} diff --git a/mmsoftware/mmlib/Makefile b/mmsoftware/mmlib/Makefile deleted file mode 100644 index 1fc2fe8..0000000 --- a/mmsoftware/mmlib/Makefile +++ /dev/null @@ -1,91 +0,0 @@ -# $Id: Makefile,v 1.11 2004/03/13 02:26:04 mmondor Exp $ - -CC = gcc -MAKE = make -AR = ar -RANLIB = ranlib -RM = rm -f -ECHO = echo -SHELL = /bin/sh - -CFLAGS += -D_REENTRANT -DDEBUG -Wall - -PTH != $(ECHO) `pth-config --cflags` -MYSQL != $(ECHO) `mysql_config --cflags` -OPENSSL = -I/usr/pkg/include -I/usr/local/include -I/usr/include -INCDIR = -I/usr/include -I/usr/local/include -I. $(PTH) $(MYSQL) $(OPENSSL) - -OBJS = mmpool.o mmfd.o mmrc4.o mmserver.o mmpath.o mmlog.o mmstr.o mmsql.o mmreadcfg.o mmstring.o mmloadarray.o mmstat.o mmbstring.o mmhash.o mmalarm.o mmheap.o mmrc4util.o mmlimitrate.o mmserver2.o - -CCOBJ = $(CC) $(CFLAGS) -c $(INCDIR) - - - -all: $(OBJS) - $(RM) libmmondor.a - $(AR) qS libmmondor.a $(OBJS) - $(RANLIB) libmmondor.a - - -clean: - $(RM) $(OBJS) libmmondor.a - - - - -mmpool.o: - $(CCOBJ) mmpool.c - -mmfd.o: - $(CCOBJ) mmfd.c - -mmrc4.o: - $(CCOBJ) mmrc4.c - -mmserver.o: - $(CCOBJ) mmserver.c - -mmpath.o: - $(CCOBJ) mmpath.c - -mmlog.o: - $(CCOBJ) mmlog.c - -mmstr.o: - $(CCOBJ) mmstr.c - -mmsql.o: - $(CCOBJ) mmsql.c - -mmreadcfg.o: - $(CCOBJ) mmreadcfg.c - -mmstring.o: - $(CCOBJ) mmstring.c - -mmloadarray.o: - $(CCOBJ) mmloadarray.c - -mmstat.o: - $(CCOBJ) mmstat.c - -mmbstring.o: - $(CCOBJ) mmbstring.c - -mmhash.o: - $(CCOBJ) mmhash.c - -mmalarm.o: - $(CCOBJ) mmalarm.c - -mmheap.o: - $(CCOBJ) mmheap.c - -mmrc4util.o: - $(CCOBJ) mmrc4util.c - -mmlimitrate.o: - $(CCOBJ) mmlimitrate.c - -mmserver2.o: - $(CCOBJ) mmserver2.c diff --git a/mmsoftware/mmlib/makedefs.sh b/mmsoftware/mmlib/makedefs.sh deleted file mode 100644 index 1463e0b..0000000 --- a/mmsoftware/mmlib/makedefs.sh +++ /dev/null @@ -1,18 +0,0 @@ -# $Id: makedefs.sh,v 1.2 2003/07/17 09:13:00 mmondor Exp $ - -CC='gcc' -AR='ar qS' -RANLIB='ranlib' -RM='rm -f' -SED='sed' -ECHO='echo' - -CFLAGS="$CFLAGS -D_REENTRANT -DDEBUG -Wall" -STDINC='-I/usr/local/include -I/usr/include -I/usr/pkg/include -I.' -STDLIB='-L/usr/local/lib -L/usr/lib -L/lib -L/usr/pkg/lib' - -show() -{ - $ECHO "$@" - $@ -} diff --git a/mmsoftware/mmlib/makefuncs.sh b/mmsoftware/mmlib/makefuncs.sh deleted file mode 100755 index 7c8d70c..0000000 --- a/mmsoftware/mmlib/makefuncs.sh +++ /dev/null @@ -1,138 +0,0 @@ -#!/bin/sh -# $Id: makefuncs.sh,v 1.1 2002/12/11 10:12:36 mmondor Exp $ - -PATH="$PATH:/bin:/sbin:/usr/bin:/usr/sbin" - -show() -{ - echo "$@" - $@ -} - -killbin() -{ - echo " Making sure $1 binary is not running" - pid=`ps axo pid,ucomm | grep $1 | awk '{print $1}'` - if [ ! -z "$pid" ]; then - kill $pid 2>/dev/null - fi -} - -startbin() -{ - echo " Starting $1 binary" - $MMPREFIX/sbin/$1 $2 >/dev/null -} - -makebin() -{ - echo - echo " Building $1" - ./makepart.sh -} - -clean() -{ - echo - echo " Cleaning $1" - ./makepart.sh clean -} - -instbin() -{ - f2="$MMPREFIX/sbin/$1" - instdir "$MMPREFIX/sbin" - echo " Installing $1 binary as $f2" - cp -f "$1" "$f2" - if [ -z "$3" ]; then - chown $MMDEFAULTUSER.$MMDEFAULTGROUP "$f2" - else - chown $MMDEFAULTUSER.$3 "$f2" - fi - if [ -z "$2" ]; then - chmod 755 "$f2" - else - chmod $2 "$f2" - fi - ls -l "$f2" -} - -instconf() -{ - f2="$MMCONFDIR/$1" - instdir "$MMCONFDIR" - if [ ! -f "$f2" ]; then - echo " Installing $1 configuration file as $f2" - cp "$1" "$f2" - else - echo " Preserving existing $f2 configuration file" - fi - if [ -z "$3" ]; then - chown $MMDEFAULTUSER.$MMDEFAULTGROUP "$f2" - else - chown $MMDEFAULTUSER.$3 "$f2" - fi - if [ -z "$2" ]; then - chmod 644 "$f2" - else - chmod $2 "$f2" - fi - ls -l "$f2" -} - -instman() -{ - f2="$MMPREFIX/man/man$2/$1" - instdir "$MMPREFIX/man/man$2" - echo " Installing $1 man page as $f2" - cp "$1" "$f2" - chown $MMDEFAULTUSER.$MMDEFAULTGROUP "$f2" - chmod 644 "$f2" - ls -l "$f2" -} - -instdir() -{ - if [ ! -d "$1" ]; then - echo " Creating $1 directory" - mkdir "$1" - else - echo " Preserving existing $1 directory" - fi - if [ -z "$4" ]; then g=$MMDEFAULTGROUP; else g=$4; fi - if [ -z "$3" ]; then o=$MMDEFAULTUSER; else o=$3; fi - chown $o.$g "$1" - if [ -z "$2" ]; then - chmod 755 "$1" - else - chmod $2 "$1" - fi - ls -ld "$1" -} - -instgroup() -{ - g=`grep "$1:" /etc/group` - if [ -z "$g" ]; then - echo " Creating group $1" - groupadd $1 - else - echo " Preserving existing group $1" - fi -} - -instuser() -{ - u=`grep "$1:" /etc/passwd` - if [ -z "$u" ]; then - instgroup $2 - echo " Creating user $1 of group $2" - if [ -f /sbin/nologin ]; then - useradd -d /unexisting -g $2 -s /sbin/nologin $1 - else - useradd -d /unexisting -g $2 -s /bin/false $1 - fi - else - echo " Preserving existing user $1" - fi -} diff --git a/mmsoftware/mmlib/makepart.sh b/mmsoftware/mmlib/makepart.sh deleted file mode 100755 index 58cc651..0000000 --- a/mmsoftware/mmlib/makepart.sh +++ /dev/null @@ -1,29 +0,0 @@ -#!/bin/sh -# $Id: makepart.sh,v 1.10 2004/03/13 02:26:04 mmondor Exp $ - -. ./makedefs.sh - -OBJS="mmpool.o mmfd.o mmrc4.o mmserver.o mmpath.o mmlog.o mmstr.o \ -mmsql.o mmreadcfg.o mmstring.o mmloadarray.o mmstat.o mmbstring.o mmhash.o \ -mmalarm.o mmheap.o mmrc4util.o mmlimitrate.o mmserver2.o" - -if [ "$1" = "clean" ]; then - show $RM $OBJS libmmondor.a - exit 0 -fi - -PTH="`pth-config --cflags`" -MYSQL="`mysql_config --cflags | $SED s/\'//g`" -OPENSSL="-I/usr/pkg/include -I/usr/local/include -I/usr/include" -INCDIR="$STDINC $PTH $MYSQL $OPENSSL" - -for obj in $OBJS; do - if [ ! -f $obj ]; then - src=`$ECHO $obj | $SED 's/\.o$/.c/'` - show $CC $CFLAGS -c $INCDIR $src - fi -done - -show $RM libmmondor.a -show $AR libmmondor.a $OBJS -show $RANLIB libmmondor.a diff --git a/mmsoftware/mmlib/mm_pth_pool.c b/mmsoftware/mmlib/mm_pth_pool.c deleted file mode 100644 index 39f3c90..0000000 --- a/mmsoftware/mmlib/mm_pth_pool.c +++ /dev/null @@ -1,383 +0,0 @@ -/* $Id: mm_pth_pool.c,v 1.10 2004/08/07 23:08:56 mmondor Exp $ */ - -/* - * Copyright (C) 2004, Matthew Mondor - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Matthew Mondor. - * 4. The name of Matthew Mondor may not be used to endorse or promote - * products derived from this software without specific prior written - * permission. - * 5. Redistribution of source code may not be released under the terms of - * any GNU Public License derivate. - * - * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - - - -/* This system allows very efficient thread allocation and freeing, using the - * mmpool(3) allocator, with it's optional constructor/destructor support. - * These are only freed back when statistics determine that extra chunks of - * pre-allocated objects are definitely not needed any time soon. This means - * that most of the time, threads are already launched and waiting when we - * need them. We then simply need to allocate one of the pool and order it to - * perform the wanted tasks. Once the thread is done executing it, it can - * return in waiting mode again in the pool once freed. - * - * Since it is possible for the destructor function to be called when freeing - * a thread object at pool_free(), we are using a special reaper thread, which - * receives messages in a queue about which threads to pool_free(). - * Actually, we do not currently use the reaper anymore, since it appears to - * be working allright without it. The object destroy function only queues a - * message for the thread, so it's safe without one. And it's faster this way. - * (there is some overhead in the Pth(3) library mutex and message passing - * implementation when multiple threads exist, probably because of the single - * select(2) based scheduler, or because the waiting queue processing needs a - * better algorithm perhaps). - * - * If a function needs to be cancellable, it should listen over the incomming - * message port of it's object for messages. This port is provided at - * thread_object_call(). Of course, the application can have the thread - * allocate a new message port and it's possible to use a custom message - * format, other than that provided. - * - * It is the responsibility of the functions called by the threads to release - * the resources they allocate before returning to prevent memory leaks. They - * also are responsible for freeing the messages they receive on their port if - * they are allocated by the sender which cannot have them on the stack. - */ - - - -/* HEADERFILES */ - -#include - -#include - - - -MMCOPYRIGHT("@(#) Copyright (c) 2004\n\ -\tMatthew Mondor. All rights reserved.\n"); -MMRCSID("$Id: mm_pth_pool.c,v 1.10 2004/08/07 23:08:56 mmondor Exp $"); - - - -/* STATIC FUNCTIONS PROTOTYPES */ - -inline static struct thread_object * thread_object_alloc(void); -inline static void thread_object_free( - struct thread_object *); -static bool thread_object_constructor(pnode_t *); -static void thread_object_destructor(pnode_t *); -static void * thread_object_main(void *); -/*static void * thread_object_reaper(void *);*/ - - - -/* GLOBALS */ - -static pth_attr_t thread_object_attr; -/*static pth_msgport_t thread_object_reaper_port;*/ -static pool_t thread_object_pool; -static pool_t thread_object_message_pool; -static pth_mutex_t thread_object_pool_mutex; -static pth_mutex_t thread_object_message_pool_mutex; - - - -/* EXPORTED PUBLIC FUNCTIONS */ - -/* Call to initialize sybsystem before any other calls to this library */ -int thread_object_init(void) -{ - /* Create attributes which will be used for threads of the pool */ - thread_object_attr = pth_attr_new(); - (void) pth_attr_set(thread_object_attr, PTH_ATTR_JOINABLE, TRUE); - - /* Create threads and messages pool_t. We first launch 8 threads here, - * more will be created as needed, in bunches of 8. Using too many - * actually degrades performance using pth(3), unfortunately. - * Threads will only be killed when statistics show that they have not be - * needed for some time. A minimum if 8 threads will always remain. - * We currently do not set any maximum limit. This means that your - * application is responsible for concurrency sanity checking before - * invoking thread_object_call() (I.E. number of maximum concurrently - * served clients at a time, etc). - */ - if (!pool_init(&thread_object_pool, "thread_object_pool", malloc, free, - thread_object_constructor, thread_object_destructor, - sizeof(struct thread_object), 8, 1, 0)) - goto err; - if (!pool_init(&thread_object_message_pool, "thread_object_message_pool", - malloc, free, NULL, NULL, - sizeof(struct thread_object_message), - 32768 / sizeof(struct thread_object_message), 1, 0)) - goto err; - - /* Initialize our mutexes */ - (void) pth_mutex_init(&thread_object_pool_mutex); - (void) pth_mutex_init(&thread_object_message_pool_mutex); - - /* Launch our reaper thread. We never kill this thread afterwards. */ - /* - if (pth_spawn(thread_object_attr, thread_object_reaper, NULL) == NULL) - goto err; - */ - - return 0; - -err: - if (POOL_VALID(&thread_object_message_pool)) - pool_destroy(&thread_object_message_pool); - if (POOL_VALID(&thread_object_pool)) - pool_destroy(&thread_object_pool); - - return -1; -} - -struct thread_object_message *thread_object_message_alloc(void) -{ - struct thread_object_message *msg; - - (void) pth_mutex_acquire(&thread_object_message_pool_mutex, FALSE, NULL); - msg = (struct thread_object_message *) - pool_alloc(&thread_object_message_pool, FALSE); - (void) pth_mutex_release(&thread_object_message_pool_mutex); - - return msg; -} - -void thread_object_message_free(struct thread_object_message *msg) -{ - (void) pth_mutex_acquire(&thread_object_message_pool_mutex, FALSE, NULL); - (void) pool_free((pnode_t *)msg); - (void) pth_mutex_release(&thread_object_message_pool_mutex); -} - -/* Assigns a thread of the pool to execute a function. If port is not NULL, - * it is set to the port of the assigned thread, so that it may be used to - * send messages to the thread. This may be wanted for various reasons, - * including to be able to abort the function and cause the thread to - * be freed back to the pool. Returns 0 on success, or -1 on error, in which - * case a resource shortage occured. - */ -int thread_object_call(pth_msgport_t *port, - void (*function)(struct thread_object *, void *), void *args) -{ - struct thread_object *obj; - struct thread_object_message *msg; - int ret = -1; - - if (function != NULL) { - /* Allocate thread from pool, order it to call our function, using a - * message. This message cannot be on the stack. - */ - if ((obj = thread_object_alloc()) != NULL) { - if ((msg = thread_object_message_alloc()) != NULL) { - msg->command = TOMC_CALL; - msg->u.call.function = function; - msg->u.call.arguments = args; - (void) pth_msgport_put(obj->port, &msg->message); - /* If caller wants port of the thread, supply it */ - if (port != NULL) - *port = obj->port; - /* Success */ - ret = 0; - } - } - } - - return ret; -} - - - -/* INTERNAL STATIC FUNCTIONS */ - -inline static struct thread_object *thread_object_alloc(void) -{ - struct thread_object *obj; - - (void) pth_mutex_acquire(&thread_object_pool_mutex, FALSE, NULL); - obj = (struct thread_object *)pool_alloc(&thread_object_pool, FALSE); - (void) pth_mutex_release(&thread_object_pool_mutex); - - return obj; -} - -inline static void thread_object_free(struct thread_object *obj) -{ - (void) pth_mutex_acquire(&thread_object_pool_mutex, FALSE, NULL); - (void) pool_free((pnode_t *)obj); - (void) pth_mutex_release(&thread_object_pool_mutex); -} - -/* Internally called to create a thread object */ -static bool thread_object_constructor(pnode_t *pnode) -{ - struct thread_object *obj = (struct thread_object *)pnode; - - /* Note that we leave thread_object_main() initialize the port field */ - if ((obj->thread = pth_spawn(thread_object_attr, thread_object_main, obj)) - == NULL) - return FALSE; - - return TRUE; -} - -/* Internally called to destroy a thread object */ -static void thread_object_destructor(pnode_t *pnode) -{ - struct thread_object *obj = (struct thread_object *)pnode; - struct thread_object_message *msg; - - /* To be freed, the thread has to be terminated. We thus send it a quit - * message and then wait for it to exit using pth_join(). Note that we - * let the thread destroy the port field. Although we theoretically could - * use a message on the stack here, let's be safe. - */ - if ((msg = thread_object_message_alloc()) != NULL) { - msg->command = TOMC_QUIT; - (void) pth_msgport_put(obj->port, &msg->message); - } - (void) pth_join(obj->thread, NULL); -} - -/* Actual thread's main loop. We create a message port and listen for command - * messages (quit and call). When we obtain a quit request, we destroy the - * port and exit cleanly. The quit event can never occur during the execution - * of a call command, since it is only called on already freed thread nodes. - */ -static void *thread_object_main(void *args) -{ - struct thread_object *obj = args; - pth_msgport_t port; - pth_event_t ring; - pth_message_t *imsg; - struct thread_object_message *msg; - - /* Create message port and associated ring */ - port = pth_msgport_create("XXX(NULL)"); - ring = pth_event(PTH_EVENT_MSG, port); - - /* Advertize our port address */ - obj->port = port; - - for (;;) { - /* Wait for messages to be available */ - if (pth_wait(ring) > 0) { - while ((imsg = pth_msgport_get(port)) != NULL) { - /* Process message and free it */ - msg = (struct thread_object_message *) - (&((pnode_t *)imsg)[-1]); - - if (msg->command == TOMC_QUIT) { - /* We are ordered to exit by the object destructor */ - thread_object_message_free(msg); - goto end; - } - if (msg->command == TOMC_CALL && - msg->u.call.function != NULL) { - /* We are ordered to execute a function */ - msg->u.call.function(obj, msg->u.call.arguments); - thread_object_message_free(msg); - /* We now should free us back to the pool. Note that this - * can cause the destructor function to be called, which - * would have the effect of queueing a TOMC_QUIT message - * to our port. This isn't a problem, since the destructor - * function does not cause us to be destroyed, it will - * wait until we exit (see goto end). - */ - thread_object_free(obj); - } - } - /* Tell the reaper to free this thread node back to the - * pool. We can't use a message on the stack for this. - * We cannot pool_free() us ourselves, since it could cause - * us to be destroyed while we are running. - */ - /* - if ((msg = thread_object_message_alloc()) != NULL) { - msg->command = TOMC_QUIT; - msg->u.quit = obj; - (void) pth_msgport_put(thread_object_reaper_port, - &msg->message); - } - */ - } - } - -end: - /* If we reach this, we need to cleanly exit. This only occurs when our - * object destructor destroys us, because we were freed for some time - * already. Free all messages pending, if any, destroy our port and exit. - * The destructor is waiting for us at pth_join(). - */ - while ((imsg = pth_msgport_get(port)) != NULL) { - msg = (struct thread_object_message *)(&((pnode_t *)imsg)[-1]); - thread_object_message_free(msg); - } - pth_event_free(ring, PTH_FREE_ALL); - pth_msgport_destroy(port); - pth_exit(NULL); - - /* NOTREACHED */ - return NULL; -} - -/* This consists of the reaper thread. It receives messages of - * thread_object_main() based threads before they exit. This allows us to - * automatically restore threads to their pool after they exit. This provides - * the caller of thread_object_call() with an API for detached asynchroneous - * threads which auto-cleanup on exit. - */ -/* ARGSUSED */ -/* -static void *thread_object_reaper(void *args) -{ - pth_msgport_t port; - pth_event_t ring; - pth_message_t *imsg; - struct thread_object_message *msg; - - port = pth_msgport_create("XXX(NULL)"); - ring = pth_event(PTH_EVENT_MSG, port); - - thread_object_reaper_port = port; - - for (;;) { - if (pth_wait(ring) > 0) { - while ((imsg = pth_msgport_get(port)) != NULL) { - msg = (struct thread_object_message *) - (&((pnode_t *)imsg)[-1]); - if (msg->command == TOMC_QUIT && msg->u.quit != NULL) - thread_object_free(msg->u.quit); - thread_object_message_free(msg); - } - } - } - - pth_exit(NULL); -} -*/ diff --git a/mmsoftware/mmlib/mm_pth_pool.h b/mmsoftware/mmlib/mm_pth_pool.h deleted file mode 100644 index 11632ce..0000000 --- a/mmsoftware/mmlib/mm_pth_pool.h +++ /dev/null @@ -1,104 +0,0 @@ -/* $Id: mm_pth_pool.h,v 1.1 2004/06/10 22:56:29 mmondor Exp $ */ - -/* - * Copyright (C) 2004, Matthew Mondor - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Matthew Mondor. - * 4. The name of Matthew Mondor may not be used to endorse or promote - * products derived from this software without specific prior written - * permission. - * 5. Redistribution of source code may not be released under the terms of - * any GNU Public License derivate. - * - * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef MM_PTH_POOL_H -#define MM_PTH_POOL_H - - - -/* HEADERFILES */ - -#include -#include - -#include -#include - - - -/* DEFINITIONS */ - -/* An actual thread object for mmpool(3) */ -struct thread_object { - pnode_t node; - pth_t thread; - pth_msgport_t port; -}; - -/* Communication message for pth_msgport_send() */ -struct thread_object_message { - pnode_t node; - pth_message_t message; - int command; - union { - /* TOMC_CALL, sent to thread_object_main() */ - struct { - void (*function)(struct thread_object *, void *); - void * arguments; - } call; - /* TOMC_QUIT, sent to thread_object_reaper() */ - struct thread_object *quit; - /* TOMC_USER messages */ - struct { - int user_command; - void * user_data; - } user; - } u; -}; - -enum thread_object_message_commands { - TOMC_CALL, - TOMC_QUIT, - TOMC_USER, - TOMC_MAX -}; - - - -/* FUNCTION PROTOTYPES */ - -extern int thread_object_init(void); - -extern struct thread_object_message *thread_object_message_alloc(void); -extern void thread_object_message_free( - struct thread_object_message *); - -extern int thread_object_call(pth_msgport_t *, - void (*)(struct thread_object *, - void *), void *); - - - -#endif diff --git a/mmsoftware/mmlib/mm_thread_abstraction.h b/mmsoftware/mmlib/mm_thread_abstraction.h deleted file mode 100644 index 25632dc..0000000 --- a/mmsoftware/mmlib/mm_thread_abstraction.h +++ /dev/null @@ -1,46 +0,0 @@ -/* XXX - * Abstraction framework which can provide thread-safe alternatives to common - * functions. These could be used by mmfd(3), mmsql(3) and mmstat(3) - * libraries wich all need special thread support. - * mm_thread_abstraction_pthread, mm_thread_abstraction_pth and - * mm_pthread_abstraction_none modules could be written as well. - * Hmm might it be preferable to switch everything to pthread and drop this? - */ - -struct mmstat_threadsupport { - /* Once per process execution */ - int (*once)(void *, void (*)(void *), void *); - void *once_udata, *once_control; - /* Support for thread yielding */ - void (*yield)(void); - /* Lock related support */ - void * (*mutex_init)(void); - void (*mutex_destroy)(void *); - void (*mutex_lock)(void *); - void (*mutex_unlock)(void *); - void * (*rwlock_init)(void); - void (*rwlock_destroy)(void *); - void (*rwlock_unlock)(void *); - void (*rwlock_rdlock)(void *); - void (*rwlock_rwlock)(void *); - /* Other funtions which may need thread-safe equivalents to be used */ - /* I/O */ - int (*poll)(struct pollfd *, nfds_t, int); - int (*connect)(int, const struct sockaddr *, socklen_t); - int (*accept)(int, struct sockaddr *, socklen_t *); - ssize_t (*recv)(int, void *, size_t, int); - ssize_t (*send)(int, const void *, size_t, int); - ssize_t (*recvfrom)(int, void *, size_t, int, - struct sockaddr *, socklen_t *); - ssize_t (*sendto)(int, const void *, size_t, int, - const struct sockaddr *, socklen_t); - ssize_t (*recvmsg)(int, struct msghdr *, int); - ssize_t (*sendmsg)(int, const struct msghdr *, int); - ssize_t (*read)(int, void *, size_t); - ssize_t (*write)(int, const void *, size_t); - /* Time */ - unsigned int (*sleep)(unsigned int); - int (*usleep)(unsigned int); - /* Error/Status */ - int (*errno)(void); -}; diff --git a/mmsoftware/mmlib/mmalarm.3 b/mmsoftware/mmlib/mmalarm.3 deleted file mode 100644 index dbb2dd4..0000000 --- a/mmsoftware/mmlib/mmalarm.3 +++ /dev/null @@ -1,245 +0,0 @@ -.\" $Id: mmalarm.3,v 1.6 2004/05/05 23:59:55 mmondor Exp $ -.\" -.\" Copyright (C) 2003-2004, Matthew Mondor -.\" All rights reserved. -.\" -.\" Redistribution and use in source and binary forms, with or without -.\" modification, are permitted provided that the following conditions -.\" are met: -.\" 1. Redistributions of source code must retain the above copyright -.\" notice, this list of conditions and the following disclaimer. -.\" 2. Redistributions in binary form must reproduce the above copyright -.\" notice, this list of conditions and the following disclaimer in the -.\" documentation and/or other materials provided with the distribution. -.\" 3. All advertising materials mentioning features or use of this software -.\" must display the following acknowledgement: -.\" This product includes software developed by Matthew Mondor. -.\" 4. The name of Matthew Mondor may not be used to endorse or promote -.\" products derived from this software without specific prior written -.\" permission. -.\" 5. Redistribution of source code may not be released under the terms of -.\" any GNU Public License derivate. -.\" -.\" THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR -.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES -.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. -.\" IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, -.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -.\" BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF -.\" USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF -.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -.\" -.Dd August 2, 2003 -.Dt MMALARM 3 -.Os mmsoftware -.Sh NAME -.Nm mmalarm -.Nd General purpose second-resolution timer events support -.Sh SYNOPSIS -.Fd #include -.Fd #include -.Fd #include -.Ft bool -.Fn timer_ctx_init "timerctx_t *ctx" "void *(*mallocfunc)(size_t)" \ -"void (*freefunc)(void *)" "time_t (*timefunc)(time_t *)" \ -"unsigned int (*alarmfunc)(unsigned int)" -.Ft void -.Fn timer_ctx_destroy "timerctx_t *ctx" -.Ft timerid_t -.Fn timer_init "timerctx_t *ctx" "time_t now" "u_int32_t delay" \ -"void (*func)(timerid_t, void *)" "void *udata" "bool repeat" -.Ft void -.Fn timer_destroy "timerctx_t *ctx" "time_t now" "timerid_t timer" -.Ft void -.Fn timer_ctx_execute "timerctx_t *ctx" "timer_t now" -.Sh DESCRIPTION -The -.Nm -library provides a useful set of functions to easily implement simultaneous -second-resolution timers. There can be multiple sets, each corresponding to -an independant context, if wanted. Each timer can be in one-time or contiguous -mode, and will trigger the execution of a function when it expires, being -automatically restarted if in contiguous mode. This systems basically consists -of a multiplexer around a single alarm timer. For instance, one may use -.Xr alarm 3 -with a simple -.Sy SIGALRM -handler, and implement multiple timers around that system using this library. -.Pp -.Ss FUNCTIONS -.Bl -tag -width indent -offset indent -.It bool Fn timer_ctx_init "timerctx_t *ctx" "void *(*mallocfunc)(size_t)" \ -"void (*freefunc)(void *)" "time_t (*timefunc)(time_t *)" \ -"unsigned int (*alarmfunc)(unsigned int)" -Initializes the specified timer context object -.Fa ctx -to be useable with the other functions. -.Pp -.Fa mallocfunc -and -.Fa freefunc -specify the wanted -.Xr malloc 3 -and -.Xr free 3 -replacements to use to manage the necessary internal memory, and may be -.Fn malloc -and -.Fn free , -respectively. -.Pp -.Fa timefunc -allows to specify a custom replacement function to -.Xr time 3 , -and can be -.Fn time . -.Pp -.Fa alarmfunc -permits to specify a custom replacement function to -.Xr alarm 3 , -and can of course be -.Fn alarm . -.It void Fn timer_ctx_destroy "timerctx_t *ctx" -Aborts and destroys any current timers pending into the specified -.Fa ctx -context, and invalidates the context, freeing any internal resources which it -was using. -.It timerid_t Fn timer_init "timerctx_t *ctx" "time_t now" "u_int32_t delay" \ -"void (*func)(timerid_t, void *)" "void *udata" "bool repeat" -Creates and attaches a new timer to the specified -.Fa ctx -context. -.Pp -.Fa now -permits to optimize operations passing the current time if it is known by the -caller function. If 0 is specified, -.Fn timer_init -will automatically query the -.Fa timefunc -specified for that context to obtain the current time. -.Pp -.Fa delay -specifies the number of seconds in which the timer will expire. -.Pp -.Fa func -tells which function should be called when the timer expires. This function -will be passed the timerid_t of the timer, which is guaranteed to be unique -to that timer, and the supplied -.Ar udata -pointer, which can be used to share a global state or context with other -timers or software. This -.Fa udata -pointer may be NULL if wanted. Note that this handler function should NOT -call any other function of this library, which would cause recursion problems. -It should instead trigger flags using variables or similar system for the -indended software components to know that the timer has expired, and perform -the appropriate actions, in the main state switcher loop for instance. -.Pp -.Fa repeat -tells if the timer is contiguous, that is, automatically restarts when it -expires and that -.Fa func -was called. If TRUE, the timer will automatically restart, but specifying FALSE will -cause the timer to only expire once and automatically be destroyed after the -handler function was called. -.Pp -Note that the returned timerid_t is guarenteed to be unique within the -.Fa ctx -context. This ID may safely be used to attempt to use -.Fn timer_destroy -anytime on this timer, even if it expired and was destroyed. 0 will be returned -if there is an internal problem to create the timer (I.E. an out of memory -condition). -.It void Fn timer_destroy "timerctx_t *ctx" "time_t now" "timerid_t timer" -Attempts to destroy the specified -.Fa timer -timer within the -.Fa ctx -context. If the timer was already destroyed, either using this function or -as the result of expiration, nothing is done. -.Pp -.Fa now -permits to optimize operations if the current time is known by the caller -function. If 0 is specified, -.Fn timer_destroy -will obtain the time using the supplied -.Fa timefunc -for the context. -.It void Fn timer_ctx_execute "timerctx_t *ctx" "time_t now" -This function is internally called by the -.Fn timer_init -and -.Fn timer_destroy -functions. It also is expected to be called by the user application whenever -a timer started with the specified -.Fa alarmfunc -expires. -.Pp -Typically, an application using -.Xr alarm 3 -calls this function within the -.Sy SIGALRM -signal handler of the process, typically setup using 4.0BSD -.Xr signal 3 -or POSIX -.Xr sigaction 2 . -.Pp -All active timers of the supplied -.Fa ctx -are verified for expiration, and the handler function is called for each one -which expires. The non-contiguous timers which expire are then destroyed. -The -.Fa alarmfunc -for this context is then executed with a delay corresponding to the time of -the next (soonest) to expire timer. -.Pp -.Fa now -can be used to optimize the execution if the current time is already known -by the timer. If 0 is specified, -.Fn timer_ctx_execute -will automatically call the supplied -.Fa timefunc -for this context to obtain the current time. -.Pp -The handler functions for the expireing timers are expected to return quickly -and to not call any functions of this library. -.El -.Sh RETURN VALUES -.Bl -tag -width indent -offset indent -.It Fn timer_ctx_init -returns TRUE on success, or FALSE on internal resource allocation problems -or invalid parameters. -.It Fn timer_init -returns a unique timer ID corresponding to the newly created timer for the -context, or 0 on error (internal resource allocation problems or invalid -parameters). -.It Xo -.Fn timer_ctx_destroy , -.Fn timer_destroy , -.Fn timer_ctx_execute -.Xc -return nothing. -.El -.Sh SEE ALSO -.Xr malloc 3 , -.Xr free 3 , -.Xr time 3 , -.Xr alarm 3 , -.Xr signal 3 , -.Xr sigaction 2 , -.Xr signal 7 . -.Sh STANDARDS -None -.Sh HISTORY -.Nm -was primarily developped for use with the PSFDEPd server project from -Matthew Mondor, which is closed source, but the library has been exported -publically. -.Sh AUTHORS -.Nm -was written by Matthew Mondor and is -Copyright (c) 2003-2004, Matthew Mondor, All rights reserved. -.Sh BUGS -Please report any bug to mmondor@accela.net diff --git a/mmsoftware/mmlib/mmalarm.c b/mmsoftware/mmlib/mmalarm.c deleted file mode 100644 index 22d086d..0000000 --- a/mmsoftware/mmlib/mmalarm.c +++ /dev/null @@ -1,218 +0,0 @@ -/* $Id: mmalarm.c,v 1.10 2004/06/04 01:02:32 mmondor Exp $ */ - -/* - * Copyright (C) 2003-2004, Matthew Mondor - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Matthew Mondor. - * 4. The name of Matthew Mondor may not be used to endorse or promote - * products derived from this software without specific prior written - * permission. - * 5. Redistribution of source code may not be released under the terms of - * any GNU Public License derivate. - * - * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - - - -#include -#include - -#include -#include - - - -MMCOPYRIGHT("@(#) Copyright (c) 2003-2004\n\ -\tMatthew Mondor. All rights reserved.\n"); -MMRCSID("$Id: mmalarm.c,v 1.10 2004/06/04 01:02:32 mmondor Exp $"); - - - -struct timer_ctx_execute_iterator_udata { - timerctx_t *ctx; - time_t current, soonest; -}; - - - -static bool timer_ctx_execute_iterator(hashnode_t *, void *); -static u_int32_t key_hash32(const void *, size_t); -static int key_cmp32(const void *, const void *, size_t); - - - -bool timer_ctx_init(timerctx_t *ctx, void *(*mallocfunc)(size_t), - void (*freefunc)(void *), time_t (*timefunc)(time_t *), - unsigned int (*alarmfunc)(unsigned int)) -{ - if ((pool_init(&ctx->pool, "alarm_pool", mallocfunc, freefunc, - NULL, NULL, sizeof(struct timer_node), - 4096 / sizeof(struct timer_node), 0, 0))) { - if ((hashtable_init(&ctx->table, "alarm_table", 16, HT_DEFAULT_FACTOR, - mallocfunc, freefunc, key_cmp32, key_hash32, TRUE))) { - ctx->magic = MAGIC_TIMER_CTX; - ctx->id = 0; - ctx->time = timefunc; - ctx->alarm = alarmfunc; - alarmfunc(0); - return TRUE; - } else - syslog(LOG_NOTICE, "timer_ctx_init() - hashtable_init()"); - pool_destroy(&ctx->pool); - } else - syslog(LOG_NOTICE, "timer_ctx_init() - pool_init()"); - - return FALSE; -} - - -void timer_ctx_destroy(timerctx_t *ctx) -{ - if (DEBUG_TRUE(TIMER_CTX_VALID(ctx))) { - ctx->alarm(0); - if (DEBUG_TRUE(HASHTABLE_VALID(&ctx->table))) - hashtable_destroy(&ctx->table, FALSE); - if (DEBUG_TRUE(POOL_VALID(&ctx->pool))) - pool_destroy(&ctx->pool); - } else - DEBUG_PRINTF("timer_ctx_destroy", "Wrong ctx pointer %p", ctx); -} - - -timerid_t timer_init(timerctx_t *ctx, time_t now, u_int32_t delay, - void (*func)(timerid_t, void *), void *udata, bool repeat) -{ - struct timer_node *tn; - timerid_t t = 0; - - if (now == 0) - now = ctx->time(NULL); - - if ((tn = (struct timer_node *)pool_alloc(&ctx->pool, FALSE)) != NULL) { - t = tn->id = ++(ctx->id); - tn->delay = delay; - tn->expiration = now + delay; - tn->function = func; - tn->udata = udata; - tn->repeat = repeat; - if (!hashtable_link(&ctx->table, (hashnode_t *)tn, &tn->id, - sizeof(timerid_t), TRUE)) - DEBUG_PRINTF("timer_init", "hashtable_link()"); - timer_ctx_execute(ctx, now); - } else - syslog(LOG_NOTICE, "timer_init() - pool_alloc()"); - - return t; -} - - -void timer_destroy(timerctx_t *ctx, time_t now, timerid_t t) -{ - struct timer_node *tn; - - if ((tn = (struct timer_node *)hashtable_lookup(&ctx->table, &t, - sizeof(timerid_t))) != NULL) { - ctx->alarm(0); - hashtable_unlink(&ctx->table, (hashnode_t *)tn); - pool_free((pnode_t *)tn); - if (now == 0) - now = ctx->time(NULL); - timer_ctx_execute(ctx, now); - } -} - - -/* Runs through all registered timers. For each which expired, run their - * associated handler function, then reset or expunge the timer. Also - * determines which timer will expire the soonest, and calls alarm(3) - * which is expected to trigger a SIGALRM signal as soon as a timer expires. - * Generally, the SIGALRM signal handler for the process (setup using - * 4.0BSD signal(3) or POSIX sigaction(2)) executes this function and - * returns. - */ -void timer_ctx_execute(timerctx_t *ctx, time_t now) -{ - struct timer_ctx_execute_iterator_udata data; - - if (now == 0) - now = ctx->time(NULL); - ctx->alarm(0); - - /* Run through timers. For each which expired, run their handler function, - * and reset or expunge it. For the others, we determine which should - * next expire so that we can set the new timer alarm. - */ - data.ctx = ctx; - data.current = now; - data.soonest = now + 259200; /* 72 hours maximum delay */ - if (HASHTABLE_NODES(&ctx->table)) - hashtable_iterate(&ctx->table, timer_ctx_execute_iterator, &data); - ctx->alarm((unsigned int)(data.soonest - now)); -} - - -/* Internally used by the timer_ctx_execute() function. */ -static bool timer_ctx_execute_iterator(hashnode_t *hnod, void *udata) -{ - struct timer_ctx_execute_iterator_udata *data = udata; - struct timer_node *tn = (struct timer_node *)hnod; - - if (tn->expiration <= data->current) { - /* Expired entry, call handler, then reset or delete. */ - tn->function(tn->id, tn->udata); - if (tn->repeat) - tn->expiration = data->current + tn->delay; - else { - hashtable_unlink(&(data->ctx->table), (hashnode_t *)tn); - pool_free((pnode_t *)tn); - tn = NULL; - } - } - if (tn != NULL) { - /* Determine next to expire timer */ - if (data->soonest > tn->expiration) - data->soonest = tn->expiration; - } - - return TRUE; -} - - -/* A quick hashtable_hash() replacement which already deals with unique - * 32-bit values. - */ -/* ARGSUSED */ -static u_int32_t key_hash32(const void *data, size_t len) -{ - return *((u_int32_t *)data); -} - - -/* A quick memcmp() replacement which only needs to compare two 32-bit values. - */ -/* ARGSUSED */ -static int key_cmp32(const void *src, const void *dst, size_t len) -{ - return *((u_int32_t *)src) - *((u_int32_t *)dst); -} diff --git a/mmsoftware/mmlib/mmalarm.h b/mmsoftware/mmlib/mmalarm.h deleted file mode 100644 index 3be5d58..0000000 --- a/mmsoftware/mmlib/mmalarm.h +++ /dev/null @@ -1,91 +0,0 @@ -/* $Id: mmalarm.h,v 1.4 2004/06/01 19:11:56 mmondor Exp $ */ - -/* - * Copyright (C) 2003-2004, Matthew Mondor - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Matthew Mondor. - * 4. The name of Matthew Mondor may not be used to endorse or promote - * products derived from this software without specific prior written - * permission. - * 5. Redistribution of source code may not be released under the terms of - * any GNU Public License derivate. - * - * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef MM_ALARM_H -#define MM_ALARM_H - - - -#include - -#include -#include -#include - - - -typedef struct timer_context timerctx_t; -typedef u_int32_t timerid_t; - - - -#define MAGIC_TIMER_CTX 0x54435458 /* TCTX */ -#define TIMER_CTX_VALID(c) ((c) != NULL && (c)->magic == MAGIC_TIMER_CTX) - - - -struct timer_context { - u_int32_t magic; - pool_t pool; - hashtable_t table; - timerid_t id; - time_t (*time)(time_t *); - unsigned int (*alarm)(unsigned int); -}; - -struct timer_node { - hashnode_t node; - timerid_t id; - u_int32_t delay; - time_t expiration; - void (*function)(timerid_t, void *); - void *udata; - bool repeat; -}; - - - -extern bool timer_ctx_init(timerctx_t *, void *(*)(size_t), - void (*)(void *), time_t (*)(time_t *), - unsigned int (*)(unsigned int)); -extern void timer_ctx_destroy(timerctx_t *); -extern timerid_t timer_init(timerctx_t *, time_t, u_int32_t, - void (*)(timerid_t, void *), void *, bool); -extern void timer_destroy(timerctx_t *, time_t, timerid_t); -extern void timer_ctx_execute(timerctx_t *, time_t); - - - -#endif diff --git a/mmsoftware/mmlib/mmarch.c b/mmsoftware/mmlib/mmarch.c deleted file mode 100644 index 8fb9acf..0000000 --- a/mmsoftware/mmlib/mmarch.c +++ /dev/null @@ -1,74 +0,0 @@ -/* $Id: mmarch.c,v 1.2 2004/08/06 15:20:27 mmondor Exp $ */ - -/* - * Copyright (C) 2004, Matthew Mondor - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Matthew Mondor. - * 4. The name of Matthew Mondor may not be used to endorse or promote - * products derived from this software without specific prior written - * permission. - * 5. Redistribution of source code may not be released under the terms of - * any GNU Public License derivate. - * - * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - - - -#include - -#include "mmtypes.h" -#include "mmarch.h" - - - -#if defined(ARCH_LITTLE_ENDIAN) - -inline u_int16_t byteorder_bswap16(u_int16_t w) -{ - register u_int8_t *p = (u_int8_t *)&w; - - return (u_int16_t)(p[0] << 8 | p[1]); -} - -inline u_int32_t byteorder_bswap32(u_int32_t w) -{ - register u_int8_t *p = (u_int8_t *)&w; - - return (u_int32_t)(p[0] << 24 | p[1] << 16 | p[2] << 8 | p[3]); -} - -inline u_int64_t byteorder_bswap64(u_int64_t w) -{ - /* To not break strict aliasing rules with GCC 3 */ - register u_int8_t *tp = (u_int8_t *)&w; - register u_int32_t *p = (u_int32_t *)tp, t; - - t = byteorder_bswap32(p[0]); - p[0] = byteorder_bswap32(p[1]); - p[1] = t; - - return w; -} - -#endif diff --git a/mmsoftware/mmlib/mmarch.h b/mmsoftware/mmlib/mmarch.h deleted file mode 100644 index a9420fb..0000000 --- a/mmsoftware/mmlib/mmarch.h +++ /dev/null @@ -1,124 +0,0 @@ -/* $Id: mmarch.h,v 1.3 2005/01/31 18:11:57 mmondor Exp $ */ - -/* - * Copyright (C) 2004, Matthew Mondor - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Matthew Mondor. - * 4. The name of Matthew Mondor may not be used to endorse or promote - * products derived from this software without specific prior written - * permission. - * 5. Redistribution of source code may not be released under the terms of - * any GNU Public License derivate. - * - * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - - - -#ifndef MM_ARCH_H -#define MM_ARCH_H - - - -#include - - - -/* XXX Architecture specific configuration. Ideally, building scripts should - * set these at autoconfiguration time rather than us having to define those - * here. - */ - -/* The number of bits held into a standard long for this architecture and - * compiler. - */ -#define ARCH_LONG_BITS 32 - -/* Define if no loop unrolling should be performed. Although performance can - * be enhanced with loop unrolling on some architectures, it is usually - * useless, and can potentially be slower on ones which have no or very little - * instruction cache. - */ -/* #define ARCH_LOWCACHE */ - -/* Some architecture and compiler combinations will generate faster code when - * using indexing (like for arrays) rather than pre-decrement/post-increment - * operations on pointers. The i386 architecture, with the GCC2 compiler is an - * example. On the other hand, architectures like m68k will usually perform - * better without this option set. Then again, this depends on compiler and on - * optimization parameters passed to it. - */ -#define ARCH_USEINDEXING - -/* The endian byte order of the architecture. This for example is little - * endian on i386, and big endian (network byte order) on m68k. Must be - * defined as either ARCH_LITTLE_ENDIAN or ARCH_BIG_ENDIAN. This is required - * for byte order conversion macros and functions, which are required when - * multiple architectures need to share a portable binary format (I.E. data - * saved in files, or passed over the network). Big endian architectures - * already using the network format will not have to perform any type of - * conversions and performance will be higher on them. - */ -#define ARCH_LITTLE_ENDIAN -/* #define ARCH_BIG_ENDIAN */ - - - - -/* No user serviceable parts below this point */ - -#ifndef ARCH_LONG_BITS -error "Undefined ARCH_LONG_BITS (8, 16, 32, 64)"; -#endif - -#if defined(ARCH_LITTLE_ENDIAN) - -/* Little endian is not network order, we need conversions */ -#define BYTEORDER_NETWORK16 byteorder_bswap16 -#define BYTEORDER_HOST16 byteorder_bswap16 -#define BYTEORDER_NETWORK32 byteorder_bswap32 -#define BYTEORDER_HOST32 byteorder_bswap32 -#define BYTEORDER_NETWORK64 byteorder_bswap64 -#define BYTEORDER_HOST64 byteorder_bswap64 - -extern u_int16_t byteorder_bswap16(u_int16_t); -extern u_int32_t byteorder_bswap32(u_int32_t); -extern u_int64_t byteorder_bswap64(u_int64_t); - -#elif defined(ARCH_BIG_ENDIAN) - -/* Big endian is network order, no need to do anything */ -#define BYTEORDER_NETWORK16(w) (w) -#define BYTEORDER_HOST16(w) (w) -#define BYTEORDER_NETWORK32(w) (w) -#define BYTEORDER_HOST32(w) (w) -#define BYTEORDER_NETWORK64(w) (w) -#define BYTEORDER_HOST64(w) (w) - -#else -error "Undefined byte order (ARCH_LITTLE_ENDIAN, ARCH_BIG_ENDIAN)"; -#endif - - - -#endif diff --git a/mmsoftware/mmlib/mmbit.h b/mmsoftware/mmlib/mmbit.h deleted file mode 100644 index f6c219b..0000000 --- a/mmsoftware/mmlib/mmbit.h +++ /dev/null @@ -1,172 +0,0 @@ -/* $Id: mmbit.h,v 1.1 2004/06/01 17:32:26 mmondor Exp $ */ - -/* - * Copyright (C) 1991-2004, Matthew Mondor - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Matthew Mondor. - * 4. The name of Matthew Mondor may not be used to endorse or promote - * products derived from this software without specific prior written - * permission. - * 5. Redistribution of source code may not be released under the terms of - * any GNU Public License derivate. - * - * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -/* Example macros to demonstrate the capabilities of C pertaining to bit - * manipulation. These macros could even be used in actual code. However, - * they often reveal useless because the C bit operators are quite simple to - * use and actually require less characters to type than using these :) - * - * Unfortunately, standard C (C89 and C99) do not define an operator for - * bit rotation, contrary to byte shifting, although most processors support - * such instructions. For this reason, bit rotation will generally be slower - * than bit shifting, except in cases where the compiler will detect the - * wanted effect to substitute bit rotation machine instructions, or where - * it deals with values that it considers to be constants, in which case it - * may simply use the result values rather than calculating them at runtime. - * If your particular compiler supports the unstandard <<< and >>> rotation - * operators, #define HAS_BITROTATE for this code to use it. - */ - - - -#ifndef BIT_H -#define BIT_H - - - -/* Assumed type for bit masks, change to wanted type if needed */ -typedef unsigned int mask_t; - -/* Use when constant mask values are supplied */ -#define MASK_CONST(n) ((mask_t)(n)) - -/* Defines the size of a bitmask in bits */ -#define MASK_SIZE (sizeof(mask_t) * 8) - - - -/* C bit operators are best used with masks. Here are mask related operations - * demonstrated in the form of macros. Note that a bitmask variable should be - * of mask_t type for these macros to safely be used. - */ - -/* Sets specified bit mask to v, using OR. */ -#define S_MASK_SET(v, m) (v) |= (m) - -/* Clears specified bit mask from v, using AND and one's complement. */ -#define S_MASK_CLR(v, m) (v) &= ~(m) - -/* Toggles specified bit mask from v, using XOR. */ -#define S_MASK_TOGGLE(v, m) (v) ^= (m) - -/* Clears v to zero, switching off all bits. */ -#define S_MASK_ZERO(v, m) (v) = MASK_CONST(0) - -/* Shifts bits of v left by n bits. */ -#define S_MASK_SHIFT_LEFT(v, n) (v) <<= (n) - -/* Shifts bits of v right by n bits. */ -#define S_MASK_SHIFT_RIGHT(v, n) (v) >>= (n) - -/* Rotates bits of v left by n bits. */ -#ifdef HAS_BITROTATE -#define S_MASK_ROTATE_LEFT(v, n) (v) <<<= (n) -#else /* !HAS_BITROTATE */ -#define S_MASK_ROTATE_LEFT(v, n) \ - (v) = (((v) << (n)) | ((v) >> (MASK_SIZE-(n)))) -#endif /* HAS_BITROTATE */ - -/* Rotates bits of v right by n bits. */ -#ifdef HAS_BITROTATE -#define S_MASK_ROTATE_RIGHT(v, n) (v) >>>= (n) -#else /* !HAS_BITROTATE */ -#define S_MASK_ROTATE_RIGHT(v, n) \ - (v) = (((v) >> (n)) | ((v) << (MASK_SIZE-(n)))) -#endif /* HAS_BITROTATE */ - -/* Evaluates if any bit in specified bit mask is set to 1 in v. */ -#define MASK_ISANY(v, m) (((v) & (m)) != 0) - -/* Evaluates if all bits in specified bit mask are set to 1 in v. */ -#define MASK_ISSET(v, m) (((v) & (m)) == (m)) - -/* Evaluates if value is even. Won't work with negative integers if - * representation is one's complement. - */ -#define MASK_ISEVEN(m) (((m) & 1) == 0) - -/* These versions are similar to the above ones except that they return the - * modified mask rather than changing it. They thus can be used at places - * where the previous ones cannot be used, as functions rather than - * statements. - */ -#define MASK_SET(v, m) ((v) | (m)) -#define MASK_CLR(v, m) ((v) & ~(m)) -#define MASK_TOGGLE(v, m) ((v) ^ (m)) -#define MASK_ZERO(v, m) (MASK_CONST(0)) -#define MASK_SHIFT_LEFT(v, n) ((v) << (n)) -#define MASK_SHIFT_RIGHT(v, n) ((v) >> (n)) -#ifdef HAS_BITROTATE -#define MASK_ROTATE_LEFT(v, n) ((v) <<< (n)) -#define MASK_ROTATE_RIGHT(v, n) ((v) >>> (n)) -#else /* !HAS_BITROTATE */ -#define MASK_ROTATE_LEFT(v, n) \ - (((v) << (n)) | ((v) >> (MASK_SIZE-(n)))) -#define MASK_ROTATE_RIGHT(v, n) \ - (((v) >> (n)) | ((v) << (MASK_SIZE-(n)))) -#endif /* HAS_BITROTATE */ - - -/* And here are bit number specific example operations demonstrated as macros, - * internally implemented using mask related operations. The variables they - * operate on should consist of the same types as used to hold masks, in this - * case unsigned int. - */ - -/* Returns a bit mask from specified bit number using left bit shift. */ -#define BIT_MASK(b) (MASK_CONST(1) << (b)) - -/* Switches specified bit number to 1 in v. */ -#define S_BIT_SET(v, b) S_MASK_SET((v), BIT_MASK((b))) - -/* Switches specified bit number to 0 in v. */ -#define S_BIT_CLR(v, b) S_MASK_CLR((v), BIT_MASK((b))) - -/* Toggles specified bit number in v. */ -#define S_BIT_TOGGLE(v, b) S_MASK_TOGGLE((v), BIT_MASK((b))) - -/* Evaluates if specified bit is set to 1 in v. */ -#define BIT_ISSET(v, b) (MASK_ISANY((v), BIT_MASK((b)))) - -/* And here are again versions which return the result rather than being used - * strictly as statements. - */ -#define BIT_SET(v, b) (MASK_SET((v), BIT_MASK((b)))) -#define BIT_CLR(v, b) (MASK_CLR((v), BIT_MASK((b)))) -#define BIT_TOGGLE(v, b) (MASK_TOGGLE((v), BIT_MASK((b)))) - - - -#endif /* !BIT_H */ diff --git a/mmsoftware/mmlib/mmbstring.c b/mmsoftware/mmlib/mmbstring.c deleted file mode 100644 index c25fe76..0000000 --- a/mmsoftware/mmlib/mmbstring.c +++ /dev/null @@ -1,332 +0,0 @@ -/* $Id: mmbstring.c,v 1.3 2004/03/22 06:59:34 mmondor Exp $ */ - -/* - * Copyright (C) 2001-2004, Matthew Mondor - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Matthew Mondor. - * 4. The name of Matthew Mondor may not be used to endorse or promote - * products derived from this software without specific prior written - * permission. - * 5. Redistribution of source code may not be released under the terms of - * any GNU Public License derivate. - * - * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -/* XXX Several of these should be optimized to call malloc() once only. - * moreover, most of these have not been entirely tested, they were taken - * from my Xisop project but only a few functions were actually being used. - */ - - - - -#include -#include - -#include -#include -#include - - - - -MMCOPYRIGHT("@(#) Copyright (c) 2001-2004\n\ -\tMatthew Mondor. All rights reserved.\n"); -MMRCSID("$Id: mmbstring.c,v 1.3 2004/03/22 06:59:34 mmondor Exp $"); - - - - -size_t bstrcat(bstring *bstr, char *str) -{ - size_t len; - - len = mm_strlen(str); - len += bstr->len; - if (len > bstr->size) - len = bstr->size; - - mm_memcpy(bstr->string + bstr->len, str, len - bstr->len); - bstr->len = len; - - return (len); -} - - -size_t bstrcat2(bstring *bstr, bstring *obstr) -{ - size_t len; - - len = obstr->len; - len += bstr->len; - if (len > bstr->size) - len = bstr->size; - - mm_memcpy(bstr->string + bstr->len, obstr->string, len - bstr->len); - bstr->len = len; - - return (len); -} - - -bool bstrcmp2(bstring *bstr, bstring *obstr) -{ - if (bstr->len == obstr->len) { - return (mm_memcmp(bstr->string, obstr->string, bstr->len) == 0); - } - - return (FALSE); -} - - -size_t bstrcpy(bstring *bstr, char *str) -{ - size_t len; - - len = mm_strlen(str); - if (len > bstr->size) - len = bstr->size; - - mm_memcpy(bstr->string, str, len); - bstr->len = len; - - return (len); -} - - -size_t bstrcpy2(bstring *bstr, bstring *obstr) -{ - size_t len; - - len = obstr->len; - if (len > bstr->size) - len = bstr->size; - - mm_memcpy(bstr->string, obstr->string, len); - bstr->len = len; - - return (len); -} - - -bstring *bstrdup(char *str) -{ - bstring *bstr; - size_t len; - - len = mm_strlen(str); - - if (len) { - if ((bstr = malloc(sizeof(bstring)))) { - if ((bstr->string = malloc(len))) { - mm_memcpy(bstr->string, str, len); - bstr->len = bstr->size = len; - - return (bstr); - } - free(bstr); - } - } - - return (NULL); -} - - -bstring *bstrdup2(bstring *obstr) -{ - bstring *bstr; - - if ((bstr = malloc(sizeof(bstring)))) { - if ((bstr->string = malloc(obstr->len))) { - mm_memcpy(bstr->string, obstr->string, obstr->len); - bstr->len = bstr->size = obstr->len; - - return (bstr); - } - free(bstr); - } - - return (NULL); -} - - -/* NOTE: For effeciency reasons, no sanity checking is performed. - * It is up to the programmer to never free anything twice. - */ - -bstring *bstrfree(bstring *bstr) -{ - free(bstr->string); - free(bstr); - - return (NULL); -} - - -size_t bstrlen(bstring *bstr) -{ - return (bstr->len); -} - - -size_t bstrncat(bstring *bstr, char *str, size_t max) -{ - size_t len; - - len = mm_strlen(str); - len += bstr->len; - if (len > bstr->size) - len = bstr->size; - if (len > max) - len = max; - - mm_memcpy(bstr->string + bstr->len, str, len - bstr->len); - bstr->len = len; - - return (len); -} - - -size_t bstrncat2(bstring *bstr, bstring *obstr, size_t max) -{ - size_t len; - - len = obstr->len; - len += bstr->len; - if (len > bstr->size) - len = bstr->size; - if (len > max) - len = max; - - mm_memcpy(bstr->string + bstr->len, obstr->string, len - bstr->len); - bstr->len = len; - - return (len); -} - - -size_t bstrncpy(bstring *bstr, char *str, size_t max) -{ - size_t len; - - len = mm_strlen(str); - if (len > bstr->size) - len = bstr->size; - if (len > max) - len = max; - - mm_memcpy(bstr->string, str, len); - bstr->len = len; - - return (len); -} - - -size_t bstrncpy2(bstring *bstr, bstring *obstr, size_t max) -{ - size_t len; - - len = obstr->len; - if (len > bstr->size) - len = bstr->size; - if (len > max) - len = max; - - mm_memcpy(bstr->string, obstr->string, len); - bstr->len = len; - - return (len); -} - - -bstring *bstrndup(char *str, size_t max) -{ - bstring *bstr; - size_t len; - - len = mm_strlen(str); - - if (len) { - if (len > max) - len = max; - if ((bstr = malloc(sizeof(bstring)))) { - if ((bstr->string = malloc(len))) { - mm_memcpy(bstr->string, str, len); - bstr->len = bstr->size = len; - - return (bstr); - } - free(bstr); - } - } - - return (NULL); -} - - -bstring *bstrndup2(bstring *obstr, size_t max) -{ - bstring *bstr; - size_t len; - - len = obstr->len; - if (len > max) - len = max; - - if ((bstr = malloc(sizeof(bstring)))) { - if ((bstr->string = malloc(len))) { - mm_memcpy(bstr->string, obstr->string, len); - bstr->len = len; - - return (bstr); - } - free(bstr); - } - - return (NULL); -} - - -bstring *bstrnew(size_t size) -{ - bstring *bstr; - - if ((bstr = malloc(sizeof(bstring)))) { - mm_memclr(bstr, sizeof(bstr)); - if ((bstr->string = malloc(size))) - return (bstr); - free(bstr); - } - - return (NULL); -} - - -size_t bstrnlen(bstring *bstr, size_t max) -{ - if (bstr->len > max) - return (max); - - return (bstr->len); -} diff --git a/mmsoftware/mmlib/mmbstring.h b/mmsoftware/mmlib/mmbstring.h deleted file mode 100644 index ecf3b84..0000000 --- a/mmsoftware/mmlib/mmbstring.h +++ /dev/null @@ -1,92 +0,0 @@ -/* $Id: mmbstring.h,v 1.4 2004/06/01 19:11:56 mmondor Exp $ */ - -/* - * Copyright (C) 2001-2004, Matthew Mondor - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Matthew Mondor. - * 4. The name of Matthew Mondor may not be used to endorse or promote - * products derived from this software without specific prior written - * permission. - * 5. Redistribution of source code may not be released under the terms of - * any GNU Public License derivate. - * - * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - - - - -#ifndef MMBSTRING_H -#define MMBSTRING_H - - - - -#include -#include - - - - -typedef struct bstring { - size_t len, size; - char *string; -} bstring; - - - - -extern bstring * bstrnew(size_t); -extern bstring * bstrfree(bstring *); - -extern size_t bstrlen(bstring *); -extern size_t bstrnlen(bstring *, size_t); - -extern bstring * bstrdup(char *); -extern bstring * bstrdup2(bstring *); -extern bstring * bstrndup(char *, size_t); -extern bstring * bstrndup2(bstring *, size_t); - -extern size_t bstrcpy(bstring *, char *); -extern size_t bstrcpy2(bstring *, bstring *); -extern size_t bstrncpy(bstring *, char *, size_t); -extern size_t bstrncpy2(bstring *, bstring *, size_t); - -extern size_t bstrcat(bstring *, char *); -extern size_t bstrcat2(bstring *, bstring *); -extern size_t bstrncat(bstring *, char *, size_t); -extern size_t bstrncat2(bstring *, bstring *, size_t); - -extern bool bstrcmp(bstring *, char *); -extern bool bstrcmp2(bstring *, bstring *); -extern bool bstrcasecmp(bstring *, char *); -extern bool bstrcasecmp2(bstring *, bstring *); -extern bool bstrncmp(bstring *, char *, size_t); -extern bool bstrncmp2(bstring *, bstring *, size_t); -extern bool bstrncasecmp(bstring *, char *, size_t); -extern bool bstrncasecmp2(bstring *, bstring *, size_t); - - - - -#endif diff --git a/mmsoftware/mmlib/mmfd.3 b/mmsoftware/mmlib/mmfd.3 deleted file mode 100644 index 12701f5..0000000 --- a/mmsoftware/mmlib/mmfd.3 +++ /dev/null @@ -1,979 +0,0 @@ -.\" $Id: mmfd.3,v 1.12 2004/11/12 16:26:01 mmondor Exp $ -.\" -.\" Copyright (C) 2001-2004, Matthew Mondor -.\" All rights reserved. -.\" -.\" Redistribution and use in source and binary forms, with or without -.\" modification, are permitted provided that the following conditions -.\" are met: -.\" 1. Redistributions of source code must retain the above copyright -.\" notice, this list of conditions and the following disclaimer. -.\" 2. Redistributions in binary form must reproduce the above copyright -.\" notice, this list of conditions and the following disclaimer in the -.\" documentation and/or other materials provided with the distribution. -.\" 3. All advertising materials mentioning features or use of this software -.\" must display the following acknowledgement: -.\" This product includes software developed by Matthew Mondor. -.\" 4. The name of Matthew Mondor may not be used to endorse or promote -.\" products derived from this software without specific prior written -.\" permission. -.\" 5. Redistribution of source code may not be released under the terms of -.\" any GNU Public License derivate. -.\" -.\" THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR -.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES -.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. -.\" IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, -.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -.\" BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF -.\" USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF -.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -.\" -.Dd August 1, 2003 -.Os mmsoftware -.Dt MMFD 3 -.Sh NAME -.Nm mmfd -.Nd Thread-safe buffering and traffic shaping fd wrapper library. -.Sh SYNOPSIS -.Fd #include -.Fd #include -.Fd #include -.Ft int -.Fn fddataready "fdfuncs *fdf" "int fd" "int ms" "bool eintr" -.Ft int -.Fn fdcleartosend "fdfuncs *fdf" "int fd" "int ms" "bool eintr" -.Ft int -.Fn fdputs "fdfuncs *fdf" "int fd" "const char *buf" -.Ft int -.Fn fdprintf "fdfuncs *fdf" "int fd" "const char *fmt" "..." -.Ft bool -.Fn fdmode "int fd" "bool blocking" -.Ft bool -.Fn fdbcinit "fdfuncs *fdf" "fdbcontext *fdbc" "long rrate" "long wrate" -.Ft void -.Fn fdbcdestroy "fdbcontext *fdbc" -.Ft bool -.Fn fdbcparam_get "fdbcontext *fdbc" "int *val" "int param" -.Ft bool -.Fn fdbcparam_set "fdbcontext *fdbc" "int val" "int param" -.Ft int64_t -.Fn FDBCBYTESR "fdbcontext *fdbc" -.Ft int64_t -.Fn FDBCBYTESW "fdbcontext *fdbc" -.Ft fdbuf * -.Fo fdbopen -.Fa "fdfuncs *fdf" "fdbcontext *fdbc" "int fd" "size_t rbuf" -.Fa "size_t wbuf" "int rrate" "int wrate" "int rmstimeout" "int wmstimeout" -.Fa "bool eintr" -.Fc -.Ft fdbuf * -.Fn fdbclose "fdbuf *fdb" -.Ft bool -.Fn fdbparam_get "fdbuf *fdb" "int *val" "int param" -.Ft bool -.Fn fdbparam_set "fdbuf *fdb" "int val" "int param" -.Ft int64_t -.Fn FDBBYTESR "fdbuf *fdb" -.Ft int64_t -.Fn FDBBYTESW "fdbuf *fdb" -.Ft bool -.Fn fdbdataready "fdbuf *fdb" -.Ft bool -.Fn fdbcleartosend "fdbuf *fdb" -.Ft void -.Fn fdbflushr "fdbuf *fdb" -.Ft bool -.Fn fdbflushw "fdbuf *fdb" -.Ft ssize_t -.Fn fdbread "fdbuf *fdb" "void *buf" "size_t size" -.Ft ssize_t -.Fn fdbwrite "fdbuf *fdb" "const void *buf" "size_t size" -.Ft ssize_t -.Fn fdbgets "fdbuf *fdb" "char *buf" "size_t max" "bool sevenbit" -.Ft bool -.Fn fdbputs "fdbuf *fdb" "const char *str" -.Ft bool -.Fn fdbprintf "fdbuf *fdb" "const char *fmt" "..." -.Ft int -.Fn fdbpoll "struct pollfdb *fdbs" "int num" "int timeout" -.Ft int -.Fn fdbreadbuf "struct fdbrb_buffer **bptr" "fdbuf *fdb" "size_t initial" \ -"size_t linesize" "size_t maxsize" "long maxlines" \ -"int (*validate)(char *line, ssize_t *len, int *res, void *udata)" \ -"bool sevenbits" -.Ft void -.Fn fdbfreebuf "struct fdbrb_buffer **bptr" -.Ft bool -.Fn fdbrdataready "fdbuf *fdb" -.Ft bool -.Fn fdbrcleartosend "fdbuf *fdb" -.Ft ssize_t -.Fn fdbrread "fdbuf *fdb" "void *buf" "size_t size" -.Ft ssize_t -.Fn fdbrwrite "fdbuf *fdb" "const void *buf" "size_t size" -.Ft int -.Fn FDBERRNO "fdbuf *fdb" -.Ft char * -.Fn FDBERRSTR "int fdberrno" -.Ft u_int64_t -.Fn microtime "void" -.Sh DESCRIPTION -This library mainly consists of a more powerful stdio replacement and is -especially suited for implementing TCP daemons. Statistics on transfered -bytes in each direction are maintained, and buffering helps to improve I/O -performance on a filedescriptor. Optional bandwidth shaping allows to -prevent users from hugging up the whole system, and timeouts prevent -most locking issues. The library is thread-safe and can be supplied -customized locking primitives to cope with the various threading -(or no-threading) situations. -.Pp -The -.Fa fdfuncs -structure allows the user to specify custom functions to be used internally -as defined in -.Aq Pa mmfd.h -(described below). -.Bd -literal -offset indent -typedef struct fdfuncs { - void * (*malloc) (size_t); - void (*free) (void *); - int (*poll) (struct pollfd *, nfds_t, int); - ssize_t (*read) (int, void *, size_t); - ssize_t (*write) (int, const void *, size_t); - unsigned int (*sleep) (unsigned int); - int (*usleep) (unsigned int); - void * (*mutex_init)(void); - void * (*mutex_destroy)(void *); - void (*mutex_lock)(void *); - void (*mutex_unlock)(void *); - void (*thread_yield)(void); - bool (*eintr)(void); -} fdfuncs; -.Ed -.Pp -The various mutex and thread related functions can be supplied NULL pointers -if desired (i.e. no thread-safe support is required at all). It is also -possible to specify functions for the mutex functions and NULL for -.Fn thread_yield -if the threading system in use is known to be preemptive (i.e. will still -allow other threads to execute properly when the current thread hugs the -CPU for some period of time). -.Pp -The user-supplied mutex creation/destroy functions (if any) should allocate -and free the memory required to create the lock, and should ensure to set -proper attributes. The mutex system behavior is assumed to be as follows: -Locks the thread requesting a lock if it is currently being held by any other -thread, until it is free in which case the current thread obtains it and -unblocks. Allows the same thread to lock the same mutex (once obtained) again -several times, in which case the same number of unlocking instances must occur -for any other thread to be able to obtain access to the mutex. It is important -to properly configure the mutexes of the threading system in use to observe -this behavior (i.e. using -.Fn pthread_mutexattr_settype -with PTHREAD_MUTEX_RECURSIVE in the case of POSIX threads API (pthreads)). The -.Fn mutex_destroy -function should always return NULL. -.Pp -The -.Fn eintr -can be NULL if wanted, but should be used when it is important to know if a -failed call to the supplied -.Fn poll -or -.Fn read -function returned as the result of an interrupt or signal which was received -(EINTR). If non-NULL, it should point to a function which returns TRUE if the -last call to the supplied -.Fn poll -or -.Fn read -function returned as the result of an interrupt or signal -(usually errno == EINTR but this may vary with a threading system for instance, -or if -.Xr mmfd -is used as part of a kernel) or FALSE otherwise. When it returns TRUE, the -FDB_EINTR error will be set for the corresponding handle instead of FDB_ERROR, -if EINTR support for the handle is enabled. If the EINTR detection support for -the handle is disabled, and that a -.Fn eintr -function is still provided, it will be used internally to transparently restart -the function as required, thus ignoring occuring EINTR events. -.Pp -Here are first described unbuffered functions which can be used directly -on standard filedescriptors, but which use the specified functions into -their -.Ar fdf -arguments: -.Pp -.Bl -tag -width XXXXXXXXXXXXXXXXX -compact -.It Fn fddataready , -.It Fn fdcleartosend -These functions allow to know if data can be read from or written to specified -.Fa fd . -They will wait upto -.Fa ms -milliseconds for the event to become true. The semantics of -.Fa ms -are like those of -.Xr poll 2 , -where -1 means to wait indefinitely and 0 to verify and return immediately. -If an -.Fn eintr -function pointer exists in the supplied -.Fa fdf , -.Fa eintr -specifies if FDB_EINTR code should be returned upon EINTR events if TRUE, -or if those EINTR events should be transparently ignored (FALSE). See the -.Xr poll 2 -manual page for more information about EINTR. -.Pp -.It Fn fdputs -operates similarily to stdio -.Xr fputs 3 , -although it performs it's operation on a file descriptor, and of course uses -the supplied -.Xr write 2 -replacement. Contrary to stdio -.Xr fputs 3 , -This function will not append '\\n' at the end of the string. -.Pp -.It Fn fdprintf -behaves like stdio -.Xr fprintf 3 -but on a filedescriptor. -.Pp -.It Fn fdmode -allows to set a filedescriptor mode to blocking or non-blocking. TRUE attempts -to set blocking mode, while FALSE attempts to set non-blocking mode. -.Xr fcntl 2 -is used internally. -.El -.Pp -Here are then described the -.Fa fdb -buffered and bandwidth shaping wrappers related functions. These will set -the -.Fa fdberrno -return code on the handle they operate on, suitable to use with error -functions (further detailed below). In addition, they use the supplied -functions for the handle they operate on, and respect it's bandwidth -and timeout limits. -.Bl -tag -width XXXXXXXXXXXXXXXXX -compact -.Pp -.It Fn fdbcinit -can be used to initialize a global bandwidth shaping context which several -fdbuf handles can be part of. This allows to set global bandwidth speed limits -for a server for instance, rather than connection-specific limits only. -.Fa fdbc -points to the fdbcontext to initialize, -.Fa rrate -and -.Fa wrate -specify the number of bytes per second maximum which are allowed to be -transfered by the total of all fdbuf handles tied to this context (if any). -These can be 0 if no speed limit is wanted in the corresponding direction. -It is recommended to destroy this context using -.Fn fdbcdestroy -function when it is no longer needed (and of course all fdbuf handles tied -to the context were closed). -.Fa fdf -points to the structure of user-supplied functions, the same ones which are -passed to -.Fn fdbopen . -.Pp -.It Fn fdbcdestroy -destroys the specified -.Fa fdbc -global context, on which of course no fdbuf handles should be tied anymore. -This mainly prevents a memory leak which could occur with some POSIX thread -implementations where mutexes need to be destroyed. -.Pp -.It Fn fdbcparam_get , -.It Fn fdbcparam_set -allow to obtain from, or set to, using -.Fa val , -information about the specified -.Fa fdbc -global context handle in a thread-safe manner, without having to access the -structure fields directly. The allowed parameter types for -.Fa param -are: -.Bl -tag -width XXXXXXXXXX -.It Fa FDBP_RRATE -The maximum bandwidth read speed limit for this context, specified in bytes -per second (0 for no limit). -.It Fa FDBP_WRATE -The maximum speed limit for writing in bytes per second for this context -(0 for no limit). -.El -.Pp -.It Fn FDBCBYTESR , -.It Fn FDBCBYTESW -retreive statistics on the total number of bytes that were read and written -through the specified -.Fa fdbc -global context. -.Pp -.It Fn fdbopen -permits to obtain an fdbuf handle to wrap the -.Fa fd -file descriptor into the buffering and bandwidth shaping subsystem. All other -.Fn fdb* -functions operate on this handle similarly to how stdio functions operate on -a FILE * handle. -.Pp -.Fa fdbc -specifies the optional global fdbcontext which this handle should be tied to, -or NULL. -.Fa rbuf -and -.Fa wbuf -consist of the read and write buffer sizes (in bytes, typical values are 4096 -or 8192). It is possible to specify 0 for a buffer size if it is known that no -transfers will be done in a direction and memory saving is desireable. -.Fa rmstimeout -and -.Fa wmstimeout -specify the maximum allowed input/output timeouts for reading and writing, -in milliseconds. -1 means no limit, and 0 that no waiting should be performed -at all, like for -.Xr poll 2 . -The -.Fa fdf -parameter assigns the various functions that should internally be used to -perform all required operations on the file descriptor for this handle. -.Fa rrate -and -.Fa wrate -specify the optional bandwidth speed limit for read and write operations via -this handle, specified in bytes per second (0 disables speed throtling in the -specified direction). -If -.Fa eintr -is TRUE, this enables EINTR detection support for this handle, if an -.Fn eintr -function was also provided in -.Fa fdf . -If it is FALSE and that such an EINTR detection function still exists, -it will cause those events to be transparently ignored and polling will -be restarted as necessary by the lower level functions. See the -.Xr poll 2 -and -.Xr read 2 -man pages for more information about EINTR events. Note that such an event, -if occuring, will be considered like if an error occured on the filedescriptor -if no EINTR detection function is provided via -.Fa fdf . -.Pp -Internally, the -.Fn fdbrread -and -.Fn fdbrwrite -functions will be called for read and write operations thus respecting -specified speeds. Internal separated read and write mutexes/locks are used -to ensure that asynchroneous I/O operations be performed at once on the handle, -and that simultaneous operations in the same direction be queued, in a -thread-safe manner. The buffering allows to considerably speed up operations -when many small read/writes are performed, minimizing the frequency of -syscalls. When desireable (eg for binary block transfers), it is possible to -call the unbuffered -.Fn fdbrread -and -.Fn fdbrwrite -functions, but it becomes the caller's responsibility to first flush the -buffers as required, using -.Fn fdbflushr -and/or -.Fn fdbflushw -functions. Using the supplied file descriptor directly is discouraged for -as long as it is wrapped into an fdbuf handle. -.Pp -.It Fn fdbclose -releases all wrapping and resources around the internal file descriptor. Any -pending buffers are first flushed. The internal file descriptor is NOT closed. -It will be available again for normal syscalls. -.Pp -.It Fn fdbparam_get , -.It Fn fdbparam_set -permit to obtain/set parameters on opened fdbuf handle -.Fa fdb , -Using -.Fa val , -in a thread-safe manner. Allowed parameter types for -.Fa param -are (as specified in -.Aq Pa mmfd.h ) : -.Bl -tag -width XXXXXXXXXXXXX -.It Fa FDBP_FD -The file descriptor on which operations are performed on. It is allowed to -change the fd wrapped into an fdbuf handle using this function, but -it has to be done safely (eg: appropriately flushing buffers first). -.It Fa FDBP_RTIMEOUT -The maximum read input timeout in milliseconds (-1 for infinite, 0 for none). -.It Fa FDBP_WTIMEOUT -The maximum write output timeout in milliseconds (-1 for infinite, 0 for none). -.It Fa FDBP_RRATE -The bandwidth read speed limit for this handle in bytes per second (0 for no -limit). -.It Fa FDBP_WRATE -The bandwidth speed limit for writing, in bytes per second (0 for no limit). -.It Fa FDBP_EINTR -Only taken in consideration if an -.Fn eintr -function was specified to detect EINTR events -in -.Fa fdf -at -.Fn fdbopen . -Specifies weither EINTR events should be taken in consideration to return -FDB_EINTR error code, if TRUE, or if those events should be ignored and -the -.Fn poll -or -.Fn read -functions of the -.Fa fdf -are automatically called again, if FALSE. See the -.Xr poll -and -.Xr read -man pages for more information on EINTR. -.El -.Pp -.It Fn FDBBYTESR , -.It Fn FDBBYTESW -retreive statistics on the number of bytes read from and written to the -specified -.Fa fdb -handle. -.Pp -.It Fn fdbdataready , -.It Fn fdbcleartosend -similarly to -.Fn fddataready -and -.Fn fdcleartosend , -these allow to know if bytes are ready to read, or if it is safe to write -more data into the supplied buffered -.Fa fdb -handle, which the previous functions would not be suitable for. -.Pp -.It Fn fdbflushr , -.It Fn fdbflushw -functions permit to completely flush any pending I/O buffers in the specified -direction for the supplied -.Fa fdb -handle. It should be noted that these are important to use before using -raw/unbuffered -.Fn fdbr* -functions on a handle. -.Pp -.It Fn fdbread , -.It Fn fdbwrite -behave like -.Xr read 2 -and -.Xr write 2 -syscall functions although they operate on the supplied buffered -.Fa fdb -handle. These are considerably faster than their syscall counterparts when -used often on small data, because of the internal buffering minimizing the -frequency of system calls. The bandwidth speed limits and I/O timeouts for -the supplied handle and it's global context (if any) are respected. -Moreover, the supplied functions via the -.Fa fdf -parameter to -.Fn fdbopen -are used, like for the other -.Fn fdb* -functions. Under some circumstances it may be useful to use the raw/unbuffered -.Fn fdbrread -and -.Fn fdbrwrite -functions, when writing many large blocks of data sequencially for instance. -It is then important to first flush any pending buffers using the -.Fn fdbflushr -and/or -.Fn fdbflushw -functions. -.Pp -.It Fn fdbgets -is very useful to read a text line of data from a buffered handle. This will -not allow reading binary data, but will accept whitespace characters -(tab '\\t'), escape (^[ or 0x1B) and ctrl-a (^A or 0x01). If -.Fa sevenbit -is TRUE, it will only accept 7-bit characters (< 128). When FALSE, other -valid ASCII characters will be accepted. Data will be read until the maximum -number of bytes -.Fa max -is reached or linefeed encountered ('\\n'). Any carriage return and linefeeds -('\\n', '\\r', "\\r\\n") are not appended as part of the returned string. -Of course, read timeout and bandwidth speed limit of the handle are respected. -Because support for possibly non-preemtive threading systems, and as binary -data is ignored, special care was taken to prevent locking other threads if -the user was flooding with binary data indefinitely (in which case -.Fa fdf->thread_yield -should be set). -.Pp -.It Fn fdbputs , -.It Fn fdbprintf -behave as their -.Fn fdputs -(which does not append '\\n') and -.Fn fdprintf -counterparts (an stdio -.Xr fprintf 3 -clone), but operate on supplied -.Fa fdb -buffered handle. -.Pp -.It Fn fdbpoll -function is similar to the system call -.Xr poll 2 -although it operates on buffered fdbuf handles. Special care has to be taken -when using this function, as it assumes that all handles in the supplied set -use the same custom functions (notably -.Fn malloc , -.Fn free -and -.Fn poll ) . -It will then use the functions specified for the first fdbuf of the set, -applying them to all handles. -.Fa fdbs -consists of a pointer to an array of -.Fa struct pollfdb -type (described below). Like for -.Xr poll 2 , -the number of elements in the array should be specified -.Fa ( num ) -and a timeout supplied -.Fa ( timeout ) , -behaving with the same semantics. -.Bd -literal -offset indent -struct pollfdb { - fdbuf *fdb; - int events, revents; -}; -.Ed -.Pp -.It Fn fdbreadbuf -consists of a general-purpose multi-line reading function, filling and -returning a buffer. It was mainly designed for -.Xr mmsmtpd 8 . -It will initially allocate a -.Fa struct fdbrb_buffer -which pointer will be assigned to -.Fa bptr , -and a character buffer of -.Fa initial -bytes length, which it will dynamically grow as required by doubling it's size -using -.Xr realloc 3 , -while reading lines from supplied -.Fa fdb -of a maximum of -.Fa linesize -length, and validating them through the custom-provided -.Fa validate -function. The buffer size will be shrinked if required before returning it -to the caller to not use more memory than currently is required to hold the -buffer contents. -.Fa sevenbits -is internally passed to -.Fn fdbgets . -.Ar maxsize -will be respected as well as -.Fa maxlines , -but they can be passed 0 if no limits are wanted. -It is important to call -.Fn fdbfreebuf -on the supplied -.Fa bptr -pointer after calling -.Fn fdbreadbuf -no matter it's return code. -The supplied -.Fa validate -function, if not NULL, will be called after reading each line, and passed -The -.Fa char *line -pointer to the line that just was read, the -.Fa ssize_t *len -pointer to the length in bytes of that line, an -.Fa int *res -pointer to a future result code that will be returned by -.Fn fdbreadbuf -and the supplied user data pointer (or NULL) -.Fa void *udata . -The validation functions may reject the line, causing it to be ignored, -by returning -.Bq Er FDBRB_REJECT -and may tell -.Fn fdbreadbuf -to stop reading lines and return, by returning -.Bq Er FDBRB_STOP -(ignore current line and stop reading) or -.Bq Er FDBRB_ADDSTOP -(add current line and stop reading). -If it returns -.Bq Er FDBRB_OK , -the line will be accepted. This line may have been modified by -.Fa validate -via the -.Fa line -pointer, and if so the length will have to be modified accordingly via the -.Fa len -pointer. It can at anytime set a custom result code (which should be a negative -value to not conflict with the library result codes) via the -.Fa res -pointer, and can then cause the -.Fn fdbreadbuf -caller to know the reason of -.Bq Er FDBRB_STOP . -The -.Fa udata -user data pointer is only provided for convenience so that the -.Fa validate -function can keep a persistent state although remaining reentrant and -thread-safe. The -.Fa struct fdbrb_buffer -structure is described below, as follows (only interesting fields are shown): -.Bd -literal -offset indent -struct fdbrb_buffer { - char *array; - void *udata; - size_t current; - long lines; -}; -.Ed -.Pp -.Ar array -points to the buffer of "\\r\\n" terminated lines, which ends with NUL. -.Ar udata -points to the state or user data which was supplied by the caller. -.Ar current -specifies the size of bytes in -.Ar array , -and -.Ar lines -the number of lines which were read. -.Pp -.It Fn fdbfreebuf -allows to free a buffer allocated by -.Fn fdbreadbuf . -It is important to call this function after calling -.Fn fdbreadbuf -no matter it's return code. Of course we shouldn't free the buffer -in case of -.Bq Er FDBRB_OK -for as long as we need it, however. This function also automatically -resets the supplied pointer to NULL, and is safe to call if there was -no allocated buffer (but supplied pointer pointed to a pointer set to NULL). -.El -.Pp -Here are described the raw/unbuffered functions which can be called on -the normally buffered fdbuf handles. It is the responsibility of the -caller to first flush any I/O buffers as required using -.Fn fdbflushr -and/or -.Fn fdbflushw -functions before using these. These are internally used by the other -.Fn fdb* -I/O functions, and are provided for performance considerations, where -for instance, many large bocks of data are to be written sequencially while -still respecting bandwidth limits, and buffering would cause a slowdown while -internally splitting the buffer into smaller parts, performing memory copy -operations. -.Pp -.Bl -tag -width XXXXXXXXXXXXXXXXX -compact -.It Fn fdbrdataready , -.It Fn fdbrcleartosend -functions operate like the -.Fn fdbdataready -and -.Fn fdbcleartosend -functions, although they do not take into account the buffers, they provide -information on the internal file descriptor directly. They also will set -.Fa fdberrno -on the specified handle. -.Pp -.It Fn fdbrread , -.It Fn fdbrwrite -behave like the -.Fn fdbread -and -.Fn fdbwrite -functions, but do not take into consideration the buffers, operations are -performed on the file descriptor directly, while still respecting bandwidth -limits. These will NOT set -.Fa fdberrno -on the specified handle, but will rather return directly the -.Fa fdberrno -code on error (which will be nagative). -.Pp -.El -And finally, are described the error management functions for the -.Fn fdb* -functions: -.Pp -.Bl -tag -width XXXXXXXXXXXXXXXXX -compact -.It Fn FDBERRNO -returns the -.Fa fdberrno -code value which resulted from the last -.Fn fdb* -function call operation performed on the supplied -.Fa fdb -handle. See the -.Sy ERRORS -section for more information on the possible codes. -.Pp -.It Fn FDBERRSTR -returns a character pointer to a read-only text string description for the -supplied -.Fa fdberrno -error code. -.El -.Pp -This is mostly a miscelaneous function, which was only exported in the -public API as it was internally used by the -.Xr mmfd 3 -library already, but could be useful to applications. -.Pp -.Bl -tag -width XXXXXXXXXXXXXXXXX -compact -.It Fn microtime -obtains the current time with microseconds resolution (1,000,000 ticks/second). -In fact the resolution may be rounded by the operating system scheduler time -esolution, typically 10 microseconds on most unix systems for instance. -.El -.Sh RETURN VALUES -Here is described the return values of the various functions of the -.Xr mmfd 3 -library. See the -.Sy ERRORS -section for more information on the available -.Fa fdberrno -error codes. -.Pp -.Bl -tag -width XXXXXXXXXXXXXXXXX -compact -.It Fn fdmode -returns TRUE on success, or FALSE on error ( -.Xr fcntl 2 -returned -1). -.It Fn fdbcdestroy , -.It Fn fdbflushr -return nothing. -.Pp -.It Fn fdbparam_get , -.It Fn fdbparam_set , -.It Fn fdbdataready , -.It Fn fdbcleartosend , -.It Fn fdbflushw , -.It Fn fdbputs , -.It Fn fdbprintf , -.It Fn fdbrdataready , -.It Fn fdbrcleartosend -return TRUE on success, FALSE on error (in which case -.Fa fdberrno -is set on the specified -.Fa fdb -handle. -.Pp -.It Fn fdbcinit , -.It Fn fdbcparam_set , -.It Fn fdbcparam_get -return TRUE on success, FALSE on error (no -.Fa fdberrno -can be set). -.Pp -.It Fn fdputs , -.It Fn fdprintf , -.It Fn fdbrread , -.It Fn fdbrwrite -functions return directly an -.Fa fdberrno -code value on error (negative), or the length of bytes that could be processed. -.Fa fdberrno -is not set for any fdbuf handle. -.Pp -.It Fn fddataready , -.It Fn fdcleartosend -return directly an -.Fa fdberrno -code on error, and -.Bq Er FDB_OK -if the file descriptor is ready to process more data. -.Pp -.It Fn fdbopen -returns a pointer to a new fdbuf handle, or NULL on error (out of memory). -.Pp -.It Fn fdbclose -always returns NULL. -.Pp -.It Fn fdbread , -.It Fn fdbwrite -functions return the number of bytes that could be processed, or -1 on error, -to be consistant with -.Xr read 2 -and -.Xr write 2 -behavior, although the -.Fa fdberrno -is set for the specified -.Fa fdb -handle on error. -.Pp -.It Fn fdbgets -returns the number of bytes that were read, or a direct -.Fa fdberrno -error code on error (negative). -.Fa fdberrno -is also set on the specified handle. -.Pp -.It Fn FDBCBYTESR , -.It Fn FDBCBYTESW , -.It Fn FDBBYTESR , -.It Fn FDBBYTESW -return a value of type int64_t, consisting in a number of bytes. -.Pp -.It Fn fdbpoll -returns -1 on error, or the number of fdb handles on which change of status -occured. 0 will be returned if -.Fa timeout -elapsed before any handle could change state. It therefore remains consistant -with the -.Xr poll 2 -semantics. In case of error, the -.Fa fdberrno -is also set on the first handle of the set. -.Pp -.It Fn fdbreadbuf -returns -.Bq Er FDBRB_OK -on success, -.Bq Er FDBRB_MEM -if not enough memory was available to read in the whole buffer, -.Bq Er FDBRB_OVERFLOW -if maximum buffer size or number of lines was reached, -.Bq Er FDBRB_TIMEOUT -if -.Fn fdbgets -timed out reading a line, -.Bq Er FDBRB_EOF -if end of file or connection loss occured, -.Bq Er FDBRB_EINTR -if an -.Fn eintr -function was provided to detect if -.Fn poll -or -.Fn read -returned -1 as the result of an interrupt or signal (EINTR), and that -.Fn eintr -returned TRUE, or a custom negative return code set by the validation function. -.Pp -.It Fn fdbfreebuf -returns nothing, but resets the supplied pointer to NULL. -.Pp -.It Fn FDBERRNO -returns the -.Fa fdberrno -code associated with specified -.Fa fdb -handle, consisting of the result code obtained from the last -.Fn fdb* -function call. -.Pp -.It Fn FDBERRSTR -returns a character pointer to a C string describing the supplied -.Fa fdberrno -code. -.Pp -.It Fn microtime -returns the current time in microsecond resolution (1,000,000 ticks/second). -.El -.Sh ERRORS -mmfd library handles it's own -.Fa fdberrno -errno-like variable. It is handle-specific rather than a global variable. -The error code value often needs to be extracted from the specified -fdbuf handle using the -.Fn FDBERRNO -function. The error codes are all negative values and are described in -.Aq Pa mmfd.h -as follows: -.Bl -tag -width XXXXXXXXXXXXXXX -.It Bq Er FDB_OK -Last mmfd operation was successful. -.It Bq Er FDB_ERROR -An error has occured reading or writing to the internal fd which is handled by -specified fdbuf handle in the last library call. In case of sockets it usually -means that connection was lost. It is also possible to occur for EINTR events -if those can happen and that no EINTR support function was specified for the -handle ( -.Fn eintr -via -.Fa fdf ) . -.It Bq Er FDB_TIMEOUT -A timeout has occured while attempting to read data from or to write to -specified fdbuf handle. -.It Bq Er FDB_MEMORY -An out of memory condition occured while attempting to perform the last -function. -.It Bq Er FDB_ARGUMENTS -Supplied arguments to function were invalid. -.It Bq Er FDB_PARAM -Unknown user-customizeable parameter while using -.Fn fdbparam_set -or -.Fn fdbparam_get -on an fdbuf handle. -.It Bq Er FDB_EINTR -An -.Fn eintr -function was specified to detect if the supplied -.Fn poll -or -.Fn read -functions returned as the result of a signal or interrupt (traditionally with -errno set to EINTR), and this event occured, because the EINTR detection -support for the handle was TRUE. Note that if EINTR events occur and that -no detection function was specified to evaluate them, FDB_ERROR will occur -instead. If EINTR detection is TRUE, and that an -.Fn eintr -function was supplied in the fdf, FDB_EINTR is returned when such an event -occurs. -.El -.Sh SEE ALSO -.Xr read 2 , -.Xr write 2 , -.Xr poll 2 , -.Xr sleep 3 , -.Xr usleep 3 , -.Xr malloc 3 , -.Xr free 3 , -.Xr realloc 3 , -.Xr pth 3 , -.Xr pthread 3 . -.Sh STANDARDS -None whatsoever, it was custom-designed from scratch. It however was written -to comply with most ANSI C programming norms, and is expected to run on a system -conforming to IEEE Std 1003.1-1990 (``POSIX''). -.Sh HISTORY -mmfd was originally developped on NetBSD 1.5 for the mmftpd and mmmail suite -of TCP daemons from Matthew Mondor. It is released under a BSD-style license -with advertising clause. -.Pp -It is now being used by various other software from the same author, mainly -running on NetBSD 1.6.1, and may still undertake enhancements from time to -time as required. -.Sh AUTHORS -The -.Nm mmfd -library was designed and written by Matthew Mondor, and is -Copyright (c) 2001-2004, Matthew Mondor, All rights reserved. -.Sh BUGS -Please report any bug to mmondor@accela.net diff --git a/mmsoftware/mmlib/mmfd.c b/mmsoftware/mmlib/mmfd.c deleted file mode 100644 index 834db4b..0000000 --- a/mmsoftware/mmlib/mmfd.c +++ /dev/null @@ -1,1167 +0,0 @@ -/* $Id: mmfd.c,v 1.23 2005/09/12 12:04:25 mmondor Exp $ */ - -/* - * Copyright (C) 2001-2004, Matthew Mondor - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Matthew Mondor. - * 4. The name of Matthew Mondor may not be used to endorse or promote - * products derived from this software without specific prior written - * permission. - * 5. Redistribution of source code may not be released under the terms of - * any GNU Public License derivate. - * - * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -/* Note: As I maintain a man page for all functions descriptions I removed - * the comments that previously were placed before each function declaration. - * See mmfd(3) man page for function semantic details. - */ - - - - -/* HEADERS */ - -#include -#include -#include -#include -#include -#include - -#include -#include -#include - - - - -MMCOPYRIGHT("@(#) Copyright (c) 2001-2004\n\ -\tMatthew Mondor. All rights reserved.\n"); -MMRCSID("$Id: mmfd.c,v 1.23 2005/09/12 12:04:25 mmondor Exp $"); - - - - -/* GLOBALS */ - -const char *fdberrstrings[] = { - "Operation successful", - "I/O error (disconnection?)", - "I/O timeout occured", - "Unknown field type", - NULL -}; - - - - -/* FUNCTIONS */ - -u_int64_t -microtime(void) -{ - struct timeval tv; - - return (gettimeofday(&tv, NULL) == 0 ? - (u_int64_t)(tv.tv_sec * 1000000) + tv.tv_usec - : 0); -} - - -int -fddataready(fdfuncs *fdf, int fd, int ms, bool eintr) -{ - int sel; - struct pollfd polldata[] = { - {-1, POLLIN | POLLERR | POLLHUP | POLLNVAL, 0} - }; - - polldata[0].fd = fd; - if (!eintr && fdf->eintr != NULL) - while ((sel = fdf->poll(polldata, 1, ms)) == -1 && - fdf->eintr()) ; - else - sel = fdf->poll(polldata, 1, ms); - if (sel == -1) { - if (fdf->eintr != NULL) - if (fdf->eintr()) - return (FDB_EINTR); - return (FDB_ERROR); - } - if (sel > 0) { - sel = polldata[0].revents; - if (sel & POLLERR || sel & POLLHUP || sel & POLLNVAL) - return (FDB_ERROR); - if (sel & POLLIN) - return (FDB_OK); - } - - return (FDB_TIMEOUT); -} - - -int -fdcleartosend(fdfuncs *fdf, int fd, int ms, bool eintr) -{ - int sel; - struct pollfd polldata[] = { - {-1, POLLOUT | POLLERR | POLLHUP | POLLNVAL, 0} - }; - - polldata[0].fd = fd; - if (!eintr && fdf->eintr != NULL) - while ((sel = fdf->poll(polldata, 1, ms)) == -1 && - fdf->eintr()) ; - else - sel = fdf->poll(polldata, 1, ms); - if (sel == -1) { - if (fdf->eintr != NULL) - if (fdf->eintr()) - return (FDB_EINTR); - return (FDB_ERROR); - } - if (sel > 0) { - sel = polldata[0].revents; - if (sel & POLLERR || sel & POLLHUP || sel & POLLNVAL) - return (FDB_ERROR); - if (sel & POLLOUT) - return (FDB_OK); - } - - return (FDB_TIMEOUT); -} - - -int -fdputs(fdfuncs *fdf, int fd, const char *buf) -{ - int len = FDB_ERROR; - - if (buf && fd) - len = fdf->write(fd, buf, mm_strlen(buf)); - - return (len); -} - - -int -fdprintf(fdfuncs *fdf, int fd, const char *fmt, ...) -{ - char buf[1024]; - va_list arg_ptr; - - *buf = '\0'; - va_start(arg_ptr, fmt); - vsnprintf(buf, 1023, fmt, arg_ptr); - va_end(arg_ptr); - - return (fdf->write(fd, buf, mm_strlen(buf))); -} - - -bool -fdmode(int fd, bool block) -{ - bool ok = FALSE; - register int mode, omode; - - if ((mode = fcntl(fd, F_GETFL, NULL)) != -1) { - omode = mode; - - if (block) - mode &= ~(O_NONBLOCK); - else - mode |= O_NONBLOCK; - - if (mode != omode) { - if ((fcntl(fd, F_SETFL, mode)) != -1) - ok = TRUE; - } else - ok = TRUE; - } - - return ok; -} - - -bool -fdbcinit(fdfuncs *fdf, fdbcontext *fdbc, long rrate, long wrate) -{ - bool ok = FALSE; - - if (fdf->mutex_init) { - if ((fdbc->rlock = fdf->mutex_init())) { - if ((fdbc->wlock = fdf->mutex_init())) - ok = TRUE; - else - fdbc->rlock = fdf->mutex_destroy(fdbc->rlock); - } - } else { - fdbc->rlock = fdbc->wlock = NULL; - ok = TRUE; - } - - fdbc->mutex_init = fdf->mutex_init; - fdbc->mutex_destroy = fdf->mutex_destroy; - fdbc->mutex_lock = fdf->mutex_lock; - fdbc->mutex_unlock = fdf->mutex_unlock; - fdbc->thread_yield = fdf->thread_yield; - fdbc->rbytes = fdbc->wbytes = 0; - fdbc->ortime = fdbc->owtime = microtime(); - fdbc->rrate = fdbc->orbytes = rrate; - fdbc->wrate = fdbc->owbytes = wrate; - - return (ok); -} - - -/* ARGSUSED */ -void -fdbcdestroy(fdbcontext *fdbc) -{ - if (fdbc->mutex_destroy) { - if (fdbc->wlock) fdbc->wlock = fdbc->mutex_destroy(fdbc->wlock); - if (fdbc->rlock) fdbc->rlock = fdbc->mutex_destroy(fdbc->rlock); - } -} - - -fdbuf * -fdbopen(fdfuncs *fdf, fdbcontext *fdbc, int fd, size_t rbytes, - size_t wbytes, long rrate, long wrate, int rmstimeout, - int wmstimeout, bool eintr) -{ - fdbuf *fdb; - - if (!rbytes) rbytes = 1; - if (!wbytes) wbytes = 1; - - if ((fdb = (fdf->malloc((sizeof(int) * 4) + sizeof(fdbuf) + rbytes + - wbytes + 2))) != NULL) { - fdbcinit(fdf, &(fdb->context), rrate, wrate); - - fdb->fdf.malloc = fdf->malloc; - fdb->fdf.free = fdf->free; - fdb->fdf.poll = fdf->poll; - fdb->fdf.read = fdf->read; - fdb->fdf.write = fdf->write; - fdb->fdf.sleep = fdf->sleep; - fdb->fdf.usleep = fdf->usleep; - fdb->fdf.mutex_init = fdf->mutex_init; - fdb->fdf.mutex_destroy = fdf->mutex_destroy; - fdb->fdf.mutex_lock = fdf->mutex_lock; - fdb->fdf.mutex_unlock = fdf->mutex_unlock; - fdb->fdf.thread_yield = fdf->thread_yield; - fdb->fdf.eintr = fdf->eintr; - - fdb->gcontext = fdbc; - fdb->fd = fd; - fdb->rsize = rbytes; - fdb->wsize = wbytes; - /* int-align buffers for possible optimizations in mm_memcpy() */ - fdb->rbuf = fdb->wbuf = - (char *)OALIGN_CEIL(((char *)fdb + sizeof(fdbuf)), int); - fdb->wbuf += (int)OALIGN_CEIL(rbytes, int); - fdb->wbuf += sizeof(int); - fdb->rendbuf = fdb->rptr = fdb->rbuf; - fdb->wendbuf = fdb->wptr = fdb->wbuf; - fdb->wendbuf += (int)OALIGN_CEIL(wbytes, int); - fdb->rmstimeout = rmstimeout; - fdb->wmstimeout = wmstimeout; - fdb->eintr = eintr; - fdb->fdberrno = FDB_OK; - return (fdb); - } - - return (NULL); -} - - -fdbuf * -fdbclose(fdbuf *fdb) -{ - void (*freefunc)(void *) = fdb->fdf.free; - - /* Flush write buffer if any */ - if (fdb->rsize) - fdbflushw(fdb); - - fdbcdestroy(&fdb->context); - freefunc(fdb); - - return (NULL); -} - - -bool -fdbcparam_get(fdbcontext *fdbc, int *val, int param) -{ - bool ok = TRUE; - - MUTEX_LOCK(fdbc, rlock); - MUTEX_LOCK(fdbc, wlock); - - switch (param) { - case FDBP_RRATE: - *val = fdbc->rrate; - break; - case FDBP_WRATE: - *val = fdbc->wrate; - break; - default: - ok = FALSE; - } - - MUTEX_UNLOCK(fdbc, wlock); - MUTEX_UNLOCK(fdbc, rlock); - - return (ok); -} - - -bool -fdbcparam_set(fdbcontext *fdbc, int val, int param) -{ - bool ok = TRUE; - - MUTEX_LOCK(fdbc, rlock); - MUTEX_LOCK(fdbc, wlock); - - switch (param) { - case FDBP_RRATE: - fdbc->rrate = fdbc->orbytes = val; - break; - case FDBP_WRATE: - fdbc->wrate = fdbc->owbytes = val; - break; - default: - ok = FALSE; - } - - MUTEX_UNLOCK(fdbc, wlock); - MUTEX_UNLOCK(fdbc, rlock); - - return (ok); -} - - -bool -fdbparam_get(fdbuf *fdb, int *val, int param) -{ - bool ok = TRUE; - - MUTEX_LOCK(&fdb->context, rlock); - MUTEX_LOCK(&fdb->context, wlock); - - fdb->fdberrno = FDB_OK; - - switch (param) { - case FDBP_FD: - *val = fdb->fd; - break; - case FDBP_RTIMEOUT: - *val = fdb->rmstimeout; - break; - case FDBP_WTIMEOUT: - *val = fdb->wmstimeout; - break; - case FDBP_RRATE: - *val = fdb->context.rrate; - break; - case FDBP_WRATE: - *val = fdb->context.wrate; - break; - case FDBP_EINTR: - *val = (int)fdb->eintr; - break; - default: - ok = FALSE; - fdb->fdberrno = FDB_PARAM; - } - - MUTEX_UNLOCK(&fdb->context, wlock); - MUTEX_UNLOCK(&fdb->context, rlock); - - return (ok); -} - - -bool -fdbparam_set(fdbuf *fdb, int val, int param) -{ - bool ok = TRUE; - - MUTEX_LOCK(&fdb->context, rlock); - MUTEX_LOCK(&fdb->context, wlock); - - fdb->fdberrno = FDB_OK; - - switch (param) { - case FDBP_FD: - fdb->fd = val; - break; - case FDBP_RTIMEOUT: - fdb->rmstimeout = val; - break; - case FDBP_WTIMEOUT: - fdb->wmstimeout = val; - break; - case FDBP_RRATE: - fdb->context.rrate = fdb->context.orbytes = val; - break; - case FDBP_WRATE: - fdb->context.wrate = fdb->context.owbytes = val; - break; - case FDBP_EINTR: - fdb->eintr = val != 0 ? TRUE : FALSE; - break; - default: - ok = FALSE; - fdb->fdberrno = FDB_PARAM; - } - - MUTEX_UNLOCK(&fdb->context, wlock); - MUTEX_UNLOCK(&fdb->context, rlock); - - return (ok); -} - - -bool -fdbrdataready(fdbuf *fdb) -{ - bool ok = FALSE; - - MUTEX_LOCK(&fdb->context, rlock); - - if ((fdb->fdberrno = fddataready(&fdb->fdf, fdb->fd, fdb->rmstimeout, - fdb->eintr)) == FDB_OK) - ok = TRUE; - - MUTEX_UNLOCK(&fdb->context, rlock); - - return (ok); -} - - -bool -fdbrcleartosend(fdbuf *fdb) -{ - bool ok = FALSE; - - MUTEX_LOCK(&fdb->context, wlock); - - if ((fdb->fdberrno = fdcleartosend(&fdb->fdf, fdb->fd, fdb->wmstimeout, - fdb->eintr)) == FDB_OK) - ok = TRUE; - - MUTEX_UNLOCK(&fdb->context, wlock); - - return (ok); -} - - -ssize_t -fdbrread(fdbuf *fdb, void *buf, size_t size) -{ - ssize_t err = FDB_OK; - size_t csize; - ssize_t cur, ocur; - u_int64_t ms; - char *ptr = buf; - - MUTEX_LOCK(&fdb->context, rlock); - - csize = size; - while (csize) { - /* Adjust buffer size if required */ - cur = csize; - if (fdb->context.rrate && cur > fdb->context.orbytes) - cur = fdb->context.orbytes; - if (fdb->gcontext) { - MUTEX_LOCK(fdb->gcontext, rlock); - if (fdb->gcontext->rrate && cur > fdb->gcontext->orbytes) - cur = fdb->gcontext->orbytes; - MUTEX_UNLOCK(fdb->gcontext, rlock); - } - if ((err = fddataready(&fdb->fdf, fdb->fd, fdb->rmstimeout, - fdb->eintr)) == FDB_OK) { - ocur = cur; - if (!fdb->eintr && fdb->fdf.eintr != NULL) - while ((cur = fdb->fdf.read(fdb->fd, ptr, (size_t)cur)) == -1 - && fdb->fdf.eintr()) ; - else - cur = fdb->fdf.read(fdb->fd, ptr, (size_t)cur); - if (cur != -1) { - ptr += cur; - csize -= cur; - /* Update local context */ - fdb->context.rbytes += cur; - if (fdb->context.rrate) { - fdb->context.orbytes -= cur; - if (fdb->context.orbytes < 1) { - /* We read a second's worth of data */ - ms = microtime() - fdb->context.ortime; - /* Sleep if required */ - if (ms < MSRES) - fdb->fdf.usleep((unsigned int)(MSRES - ms)); - /* Reset context */ - fdb->context.orbytes = fdb->context.rrate; - fdb->context.ortime = microtime(); - } - } - /* Update global context if any */ - if (fdb->gcontext) { - fdb->gcontext->rbytes += cur; /* Safe out of mutex */ - if (fdb->gcontext->rrate) { - MUTEX_LOCK(fdb->gcontext, rlock); - fdb->gcontext->orbytes -= cur; - if (fdb->gcontext->orbytes < 1) { - /* We read a second's worth of data. Unfortunately - * as we don't know how much time elapsed to obtain - * access to the mutex we need to obtain time again - */ - /* Sleep if required */ - ms = microtime() - fdb->gcontext->ortime; - if (ms < MSRES) - fdb->fdf.usleep((unsigned int)(MSRES - ms)); - /* Reset context */ - fdb->gcontext->orbytes = fdb->gcontext->rrate; - fdb->gcontext->ortime = microtime(); - } - MUTEX_UNLOCK(fdb->gcontext, rlock); - if (fdb->gcontext->thread_yield) - fdb->gcontext->thread_yield(); - } - } - /* Exit read() loop if required */ - if (cur == 0) - err = FDB_ERROR; /* EOF */ - if (cur < ocur) - break; - } else { - if (fdb->fdf.eintr != NULL) { - if (fdb->fdf.eintr()) { - err = FDB_EINTR; - break; - } - } - if (errno != EAGAIN) { - err = FDB_ERROR; - break; - } - } - } else - break; - } - - MUTEX_UNLOCK(&fdb->context, rlock); - - err = (ssize_t)(err != FDB_OK ? err : size - csize); - return (err); -} - - -ssize_t -fdbrwrite(fdbuf *fdb, const void *buf, size_t size) -{ - ssize_t err = FDB_OK; - size_t csize; - ssize_t cur; - u_int64_t ms; - const char *ptr = buf; - - MUTEX_LOCK(&fdb->context, wlock); - - csize = size; - while (csize) { - /* Adjust buffer size if required */ - cur = csize; - if (fdb->context.wrate && cur > fdb->context.owbytes) - cur = fdb->context.owbytes; - if (fdb->gcontext) { - MUTEX_LOCK(fdb->gcontext, wlock); - if (fdb->gcontext->wrate && cur > fdb->gcontext->owbytes) - cur = fdb->gcontext->owbytes; - MUTEX_UNLOCK(fdb->gcontext, wlock); - } - if ((err = fdcleartosend(&fdb->fdf, fdb->fd, fdb->wmstimeout, - fdb->eintr)) == FDB_OK) { - if ((cur = fdb->fdf.write(fdb->fd, ptr, (size_t)cur)) != -1) { - ptr += cur; - csize -= cur; - /* Update local context */ - fdb->context.wbytes += cur; - if (fdb->context.wrate) { - fdb->context.owbytes -= cur; - if (fdb->context.owbytes < 1) { - /* We wrote a second's worth of data */ - /* Sleep if required */ - ms = microtime() - fdb->context.owtime; - if (ms < MSRES) - fdb->fdf.usleep((unsigned int)(MSRES - ms)); - /* Reset context */ - fdb->context.owbytes = fdb->context.wrate; - fdb->context.owtime = microtime(); - } - } - /* Update global context if any */ - if (fdb->gcontext) { - fdb->gcontext->wbytes += cur; /* Safe out of mutex */ - if (fdb->gcontext->wrate) { - MUTEX_LOCK(fdb->gcontext, wlock); - fdb->gcontext->owbytes -= cur; - if (fdb->gcontext->owbytes < 1) { - /* We wrote a second's worth of data. Unfortunately - * as we don't know how much time elapsed to obtain - * access to the mutex we need to obtain time again - */ - /* Sleep if required */ - ms = microtime() - fdb->gcontext->owtime; - if (ms < MSRES) - fdb->fdf.usleep((unsigned int)(MSRES - ms)); - /* Reset context */ - fdb->gcontext->owbytes = fdb->gcontext->wrate; - fdb->gcontext->owtime = microtime(); - } - MUTEX_UNLOCK(fdb->gcontext, wlock); - if (fdb->gcontext->thread_yield) - fdb->gcontext->thread_yield(); - } - } - if (cur == 0) { - err = FDB_ERROR; /* EOF */ - break; - } - } else { - if (errno != EAGAIN) { - err = FDB_ERROR; - break; - } - } - } else - break; - } - - MUTEX_UNLOCK(&fdb->context, wlock); - - err = (ssize_t)(err != FDB_OK ? err : size - csize); - return (err); -} - - -bool -fdbdataready(fdbuf *fdb) -{ - bool ok = FALSE; - - MUTEX_LOCK(&fdb->context, rlock); - - /* Any already available bytes in buffer? */ - if (fdb->rptr < fdb->rendbuf) { - fdb->fdberrno = FDB_OK; - ok = TRUE; - } else { - /* No, query real file descriptor */ - if ((fdb->fdberrno = fddataready(&fdb->fdf, fdb->fd, fdb->rmstimeout, - fdb->eintr)) == FDB_OK) - ok = TRUE; - } - - MUTEX_UNLOCK(&fdb->context, rlock); - - return (ok); -} - - -bool -fdbcleartosend(fdbuf *fdb) -{ - bool ok = FALSE; - - MUTEX_LOCK(&fdb->context, wlock); - - /* Any room in write buffer? */ - if (fdb->wptr < fdb->wendbuf) { - fdb->fdberrno = FDB_OK; - ok = TRUE; - } else { - /* No, query real file descriptor */ - if ((fdb->fdberrno = fdcleartosend(&fdb->fdf, fdb->fd, - fdb->wmstimeout, fdb->eintr)) == FDB_OK) - ok = TRUE; - } - - MUTEX_UNLOCK(&fdb->context, wlock); - - return (ok); -} - - -bool -fdbflushr(fdbuf *fdb) -{ - MUTEX_LOCK(&fdb->context, rlock); - - /* Just discard the read buffer contents if any */ - fdb->rendbuf = fdb->rptr = fdb->rbuf; - fdb->fdberrno = FDB_OK; - - MUTEX_UNLOCK(&fdb->context, rlock); - - return (TRUE); -} - - -bool -fdbflushw(fdbuf *fdb) -{ - size_t len; - ssize_t tmp; - bool ok = TRUE; - - MUTEX_LOCK(&fdb->context, wlock); - - fdb->fdberrno = FDB_OK; - - /* If any bytes pending in write buffer, write it out */ - if ((len = fdb->wptr - fdb->wbuf) > 0) { - if ((tmp = fdbrwrite(fdb, fdb->wbuf, len)) < 0) { - fdb->fdberrno = tmp; - ok = FALSE; - } else - fdb->wptr = fdb->wbuf; - } - - MUTEX_UNLOCK(&fdb->context, wlock); - - return (ok); -} - - -ssize_t -fdbread(fdbuf *fdb, void *buf, size_t size) -{ - ssize_t len = 0; - int cur; - - MUTEX_LOCK(&fdb->context, rlock); - - fdb->fdberrno = FDB_OK; - - /* Any currently available bytes? */ - while (len < 1) { - if ((cur = fdb->rendbuf - fdb->rptr) > 0) { - if (cur > size) - cur = size; - mm_memcpy(buf, fdb->rptr, (size_t)cur); - fdb->rptr += cur; - len += cur; - } else { - /* No, read more bytes in buffer */ - fdb->rendbuf = fdb->rptr = fdb->rbuf; - if ((cur = fdbrread(fdb, fdb->rbuf, fdb->rsize)) < 0) { - fdb->fdberrno = cur; - len = -1; - break; - } else { - if (!cur) break; - fdb->rendbuf += cur; - } - } - } - - MUTEX_UNLOCK(&fdb->context, rlock); - - return (len); -} - - -ssize_t -fdbwrite(fdbuf *fdb, const void *buf, size_t size) -{ - register const void *pos = buf; - register size_t remaining = size; - - MUTEX_LOCK(&fdb->context, wlock); - - fdb->fdberrno = FDB_OK; - - /* As long as there exists space in the buffer, fill it, so that when - * it really gets full, flush it, then continue until all bytes were - * processed. This way, we ensure to respect the specified buffer size - * and use it at it's best. - */ - while (remaining > 0) { - register size_t space, cur; - - if ((space = fdb->wendbuf - fdb->wptr) == 0) { - /* Absolutely no room left, buffer needs flushing. */ - if ((fdb->fdberrno = fdbrwrite(fdb, fdb->wbuf, - (size_t)(fdb->wptr - fdb->wbuf))) < 0) - break; - else { - fdb->wptr = fdb->wbuf; - space = fdb->wendbuf - fdb->wptr; - } - } - /* We know that there is some room, fill what we can */ - cur = (remaining > space) ? space : remaining; - mm_memcpy(fdb->wptr, pos, cur); - remaining -= cur; - pos += cur; - fdb->wptr += cur; - } - - MUTEX_UNLOCK(&fdb->context, wlock); - - if (fdb->fdberrno != FDB_OK) - return -1; - return (ssize_t)(size); -} - - -ssize_t -fdbgets(fdbuf *fdb, char *buf, size_t max, bool sevenbit) -{ - ssize_t len = 0; - int cur; - char *rendbuf, *rptr, *rbuf; - - *buf = 0; - - MUTEX_LOCK(&fdb->context, rlock); - - fdb->fdberrno = FDB_OK; - /* Save current buffer status to restore it in case of read timeout */ - rendbuf = fdb->rendbuf; - rptr = fdb->rptr; - rbuf = fdb->rbuf; - - while (len < max) { - register unsigned char c = 0; - register int cnt = 0; - - /* Are there any available buffered bytes already? If so, - * process them first. - */ - while ((fdb->rptr < fdb->rendbuf) && (len < max)) { - c = *fdb->rptr++; - if (c == '\n') - goto endfdbgets; - if ((c < 32 && c != '\t' && c != 27 && c != 1) || - (c > 126 && sevenbit)) { - /* This character is considered invalid and should be - * ignored; But avoid causing this thread to prevent others - * from working if user is flooding us with binary data. - */ - if (fdb->context.thread_yield) { - cnt++; - if (cnt > 64) { - cnt = 0; - fdb->context.thread_yield(); - } - } - } else - buf[len++] = c; - } - - /* We need to refill our buffer with any currently available - * bytes to read, or wait for new bytes to be available for - * reading. - */ - fdb->rendbuf = fdb->rptr = fdb->rbuf; - if ((cur = fdbrread(fdb, fdb->rbuf, fdb->rsize)) < 0) { - fdb->fdberrno = cur; - /* In case of input timeout, retore the original buffer state - * so that it be safe to call fdbgets() again after a successful - * fdbdataready(), for instance. - */ - if (cur == FDB_TIMEOUT) { - fdb->rendbuf = rendbuf; - fdb->rptr = rptr; - fdb->rbuf = rbuf; - } - goto endfdbgets; - } else { - if (!cur) { - /* EOF */ - fdb->fdberrno = FDB_ERROR; - goto endfdbgets; - } - fdb->rendbuf += cur; - } - - } - -endfdbgets: - buf[len] = 0; - if (fdb->fdberrno != FDB_OK) - len = fdb->fdberrno; - - MUTEX_UNLOCK(&fdb->context, rlock); - - return (len); -} - - -bool -fdbputs(fdbuf *fdb, const char *buf) -{ - return (fdbwrite(fdb, buf, mm_strlen(buf))); -} - - -bool -fdbprintf(fdbuf *fdb, const char *fmt, ...) -{ - char buf[1024]; - va_list arg_ptr; - - *buf = 0; - va_start(arg_ptr, fmt); - vsnprintf(buf, 1023, fmt, arg_ptr); - va_end(arg_ptr); - - return (fdbwrite(fdb, buf, mm_strlen(buf))); -} - - -/* - * XXX Has not been tested yet - */ -int -fdbpoll(struct pollfdb *pfdb, int num, int timeout) -{ - struct pollfd *pfd; - register int i; - register fdbuf *tfdb; - int res, pres; - void * (*mallocfunc)(size_t); - void (*freefunc)(void *); - int (*pollfunc)(struct pollfd *, nfds_t, int); - bool (*polleintrfunc)(void); - bool eintr; - - tfdb = NULL; - res = 0; - if (num < 1) - return (-1); - - /* Process events which could return immediately depending on current - * buffer state of each fdb - */ - for (i = 0; i < num; i++) { - tfdb = pfdb[i].fdb; - pfdb[i].revents = 0; - if (pfdb[i].events & POLLIN) { - MUTEX_LOCK(&tfdb->context, rlock); - if (tfdb->rptr < tfdb->rendbuf) { - pfdb[i].revents |= POLLIN; - res++; - } - MUTEX_UNLOCK(&tfdb->context, rlock); - } - if (pfdb[i].events & POLLOUT) { - MUTEX_LOCK(&tfdb->context, wlock); - if (tfdb->wptr < tfdb->wendbuf) { - pfdb[i].revents |= POLLOUT; - res++; - } - MUTEX_UNLOCK(&tfdb->context, wlock); - } - } - if (res) - return (res); - - /* If we got here, we need to perform actual poll(2) syscall. */ - tfdb = pfdb[0].fdb; - mallocfunc = tfdb->fdf.malloc; - freefunc = tfdb->fdf.free; - pollfunc = tfdb->fdf.poll; - polleintrfunc = tfdb->fdf.eintr; - eintr = tfdb->eintr; - - /* Setup our pollfd array */ - if ((pfd = mallocfunc(sizeof(struct pollfd) * num))) { - for (i = 0; i < num; i++) { - tfdb = pfdb[i].fdb; - if (pfdb[i].events & POLLIN) - MUTEX_LOCK(&tfdb->context, rlock); - if (pfdb[i].events & POLLOUT) - MUTEX_LOCK(&tfdb->context, wlock); - pfd[i].fd = tfdb->fd; - pfd[i].events = pfdb[i].events | POLLHUP | POLLERR | POLLNVAL; - pfd[i].revents = 0; - } - /* Finally call poll function and update res */ - if (!eintr && polleintrfunc != NULL) - while ((pres = pollfunc(pfd, (unsigned int)num, timeout)) == -1 && - polleintrfunc()) ; - else - pres = pollfunc(pfd, (unsigned int)num, timeout); - if (pres == -1) { - pfdb[0].fdb->fdberrno = FDB_ERROR; - if (polleintrfunc != NULL) - if (polleintrfunc()) - pfdb[0].fdb->fdberrno = FDB_EINTR; - res = -1; - } else if (!pres) { - pfdb[0].fdb->fdberrno = FDB_TIMEOUT; - res = 0; - } else { - /* At least one event was received */ - res = 0; - for (i = 0; pres > 0 && i < num; i++) { - if (pfd[i].revents & pfd[i].events) { - pfdb[i].revents = pfd[i].revents; - pres--; - res++; - } - } - } - /* Cleanup */ - for (i = 0; i < num; i++) { - tfdb = pfdb[i].fdb; - if (pfdb[i].events & POLLIN) - MUTEX_UNLOCK(&tfdb->context, rlock); - if (pfdb[i].events & POLLOUT) - MUTEX_UNLOCK(&tfdb->context, wlock); - } - freefunc(pfd); - } else { - pfdb[0].fdb->fdberrno = FDB_ERROR; - res = -1; - } - - return (res); -} - - -int -fdbreadbuf(struct fdbrb_buffer **ubuf, fdbuf *fdb, size_t initial, - size_t linesize, size_t maxsize, long maxlines, - int (*validate)(char *, ssize_t *, int *, void *), void *udata, - bool sevenbit) -{ - struct fdbrb_buffer *buf; - void *tbuf; - ssize_t len; - int res = FDBRB_OK; - - if ((buf = malloc(sizeof(struct fdbrb_buffer)))) { - buf->total = buf->current = 0; - buf->lines = 0; - buf->udata = udata; - *ubuf = buf; - if ((buf->array = malloc(initial))) { - buf->total = initial; - buf->pos = buf->end = buf->array; - buf->end += buf->total; - for (;;) { - if ((!maxlines || buf->lines < maxlines) && - (!maxsize || buf->current < maxsize)) { - if (buf->end - buf->pos < linesize + 2) { - /* Grow buffer */ - if ((tbuf = realloc(buf->array, buf->total * 2))) { - /* The returned pointer is not guaranteed to - * remain the same. - */ - len = buf->pos - buf->array; - buf->total *= 2; - buf->array = buf->pos = buf->end = tbuf; - buf->pos += len; - buf->end += buf->total; - } else { - res = FDBRB_MEM; - break; - } - } - if ((len = fdbgets(fdb, buf->pos, linesize - 2, - sevenbit)) > -1) { - register int r = FDBRB_OK; - - if (validate) { - r = validate(buf->pos, &len, &res, udata); - if (r == FDBRB_REJECT) - continue; - if (r == FDBRB_STOP) - break; - } - buf->pos += len; - *buf->pos++ = '\r'; - *buf->pos++ = '\n'; - buf->lines++; - buf->current += (len + 2); - /* Special support to allow adding one last line and - * to then stop reading. - */ - if (validate && r == FDBRB_ADDSTOP) - break; - } else { - switch (len) { - case FDB_TIMEOUT: - res = FDBRB_TIMEOUT; - break; - case FDB_EINTR: - res = FDBRB_EINTR; - break; - default: - res = FDBRB_EOF; - break; - } - break; - } - } else { - res = FDBRB_OVERFLOW; - break; - } - } - *buf->pos++ = 0; - /* Free some unused remaining space */ - if ((tbuf = realloc(buf->array, buf->current + 1))) { - buf->array = tbuf; - buf->total = buf->current + 1; - } - } else - res = FDBRB_MEM; - } else - res = FDBRB_MEM; - - return (res); -} - - -void -fdbfreebuf(struct fdbrb_buffer **buf) -{ - if (*buf) { - if ((*buf)->array) - free((*buf)->array); - free(*buf); - } - - *buf = NULL; -} diff --git a/mmsoftware/mmlib/mmfd.h b/mmsoftware/mmlib/mmfd.h deleted file mode 100644 index 9e51716..0000000 --- a/mmsoftware/mmlib/mmfd.h +++ /dev/null @@ -1,236 +0,0 @@ -/* $Id: mmfd.h,v 1.16 2004/11/12 16:26:02 mmondor Exp $ */ - -/* - * Copyright (C) 2001-2004, Matthew Mondor - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Matthew Mondor. - * 4. The name of Matthew Mondor may not be used to endorse or promote - * products derived from this software without specific prior written - * permission. - * 5. Redistribution of source code may not be released under the terms of - * any GNU Public License derivate. - * - * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - - - - -#ifndef MMFD_H -#define MMFD_H - - - - -/* HEADERS */ - -#include -#include -#include -#include -#include -#include - -#include - - -/* You may or may not need this. Works fine on BSD and Linux Debian, but - * reportedly fails on RedHat? - * - * #ifdef __GLIBC__ - * typedef unsigned int nfds_t; - * #endif - */ - - - - -/* DEFINITIONS */ - -#define FDB_OK 0 -#define FDB_ERROR -1 -#define FDB_TIMEOUT -2 -#define FDB_PARAM -3 -#define FDB_EINTR -4 - -#define FDBERRNO(a) (a->fdberrno) -#define FDBERRSTR(a) (fdberrstrings[-(a)]) -#define FDBBYTESR(a) (a->context.rbytes) -#define FDBBYTESW(a) (a->context.wbytes) -#define FDBCBYTESR(a) (a->rbytes) -#define FDBCBYTESW(a) (a->wbytes) - -/* Parameter for fdbparam_get/set() functions */ -#define FDBP_FD 0 -#define FDBP_RTIMEOUT 1 -#define FDBP_WTIMEOUT 2 -#define FDBP_RRATE 3 -#define FDBP_WRATE 4 -#define FDBP_EINTR 5 - -/* These are validate() results for fdbreadbuf() */ -#define FDBRB_OK 0 /* Accept line and continue */ -#define FDBRB_REJECT 1 /* Line should be ignored */ -#define FDBRB_STOP 2 /* We should stop reading */ -#define FDBRB_ADDSTOP 3 /* We should add this line and stop reading */ -/* These are standard result codes from fdbreadbuf(), custom ones should be - * negative to not conflict. - */ -#define FDBRB_OVERFLOW 4 /* Return code when buffer/lines full */ -#define FDBRB_MEM 5 /* Out of memory */ -#define FDBRB_TIMEOUT 6 /* Input timeout occured */ -#define FDBRB_EOF 7 /* EOF or lost connection */ -#define FDBRB_EINTR 8 /* Interrupt/signal occured */ - -/* Resolution of microtime() in ticks per second */ -#define MSRES 1000000llu - -/* Internal macros */ - -/* MUTEX_LOCK(fdbcontext *, rlock|wlock); */ -#define MUTEX_LOCK(fdbc, a) do { \ - if ((fdbc)->mutex_lock) \ - (fdbc)->mutex_lock((fdbc)->a); \ -} while (FALSE) - -/* MUTEX_UNLOCK(fdbcontext *, rlock|wlock); */ -#define MUTEX_UNLOCK(fdbc, a) do { \ - if ((fdbc)->mutex_unlock) \ - (fdbc)->mutex_unlock((fdbc)->a); \ -} while (FALSE) - - - - -/* STRUCTURES */ - -/* Structure which permits user to provide custom functions */ -typedef struct fdfuncs { - void * (*malloc)(size_t); - void (*free)(void *); - int (*poll)(struct pollfd *, nfds_t, int); - ssize_t (*read)(int, void *, size_t); - ssize_t (*write)(int, const void *, size_t); - unsigned int (*sleep)(unsigned int); - int (*usleep)(unsigned int); - void * (*mutex_init)(void); - void * (*mutex_destroy)(void *); - void (*mutex_lock)(void *); - void (*mutex_unlock)(void *); - void (*thread_yield)(void); - bool (*eintr)(void); -} fdfuncs; - -/* Bandwidth shaping context, used for an fdbuf, and optionally as a global - * context for several fdbufs as well. - */ -typedef struct fdbcontext { - void * (*mutex_init)(void); - void * (*mutex_destroy)(void *); - void (*mutex_lock)(void *); - void (*mutex_unlock)(void *); - void (*thread_yield)(void); - void *rlock, *wlock; - u_int64_t ortime, owtime; - int64_t rbytes, wbytes; - size_t rrate, wrate, orbytes, owbytes; -} fdbcontext; - -/* This is used for fdb functions, buffering read/write and bandwidth shaping - * over an fd layer - */ -typedef struct fdbuf { - struct fdfuncs fdf; - struct fdbcontext context; - struct fdbcontext *gcontext; - char *rbuf, *rendbuf, *rptr, *wbuf, *wendbuf, *wptr; - size_t rsize, wsize; - int fd, rmstimeout, wmstimeout, fdberrno; - bool eintr; -} fdbuf; - -struct pollfdb { - fdbuf *fdb; - short events, revents; -}; - -struct fdbrb_buffer { - char *array, *pos, *end; - void *udata; - size_t total, current; - long lines; -}; - - - - -/* EXTERNS */ - -extern const char *fdberrstrings[]; - - - - -/* PROTOTYPES */ - -extern u_int64_t microtime(void); -extern int fddataready(fdfuncs *, int, int, bool); -extern int fdcleartosend(fdfuncs *, int, int, bool); -extern int fdputs(fdfuncs *, int, const char *); -extern int fdprintf(fdfuncs *, int, const char *, ...); -extern bool fdmode(int, bool); - -extern bool fdbcinit(fdfuncs *, fdbcontext *, long, long); -extern void fdbcdestroy(fdbcontext *); -extern fdbuf * fdbopen(fdfuncs *, fdbcontext *, int, size_t, size_t, - long, long, int, int, bool); -extern fdbuf * fdbclose(fdbuf *); -extern bool fdbcparam_get(fdbcontext *, int *, int); -extern bool fdbcparam_set(fdbcontext *, int, int); -extern bool fdbparam_get(fdbuf *, int *, int); -extern bool fdbparam_set(fdbuf *, int, int); -extern bool fdbrdataready(fdbuf *); -extern bool fdbrcleartosend(fdbuf *); -extern ssize_t fdbrread(fdbuf *, void *, size_t); -extern ssize_t fdbrwrite(fdbuf *, const void *, size_t); -extern bool fdbdataready(fdbuf *); -extern bool fdbcleartosend(fdbuf *); -extern bool fdbflushr(fdbuf *); -extern bool fdbflushw(fdbuf *); -extern ssize_t fdbread(fdbuf *, void *, size_t); -extern ssize_t fdbwrite(fdbuf *, const void *, size_t); -extern ssize_t fdbgets(fdbuf *, char *, size_t, bool); - -extern bool fdbputs(fdbuf *, const char *); -extern bool fdbprintf(fdbuf *, const char *, ...); -extern int fdbpoll(struct pollfdb *, int, int); -extern int fdbreadbuf(struct fdbrb_buffer **, fdbuf *, size_t, - size_t, size_t, long, - int (*)(char *, ssize_t *, int *, void *), - void *, bool); -extern void fdbfreebuf(struct fdbrb_buffer **); - - - - -#endif diff --git a/mmsoftware/mmlib/mmfifo.3 b/mmsoftware/mmlib/mmfifo.3 deleted file mode 100644 index ffb6c21..0000000 --- a/mmsoftware/mmlib/mmfifo.3 +++ /dev/null @@ -1,278 +0,0 @@ -.\" $Id: mmfifo.3,v 1.8 2004/05/05 23:59:56 mmondor Exp $ -.\" -.\" Copyright (C) 2000-2004, Matthew Mondor -.\" All rights reserved. -.\" -.\" Redistribution and use in source and binary forms, with or without -.\" modification, are permitted provided that the following conditions -.\" are met: -.\" 1. Redistributions of source code must retain the above copyright -.\" notice, this list of conditions and the following disclaimer. -.\" 2. Redistributions in binary form must reproduce the above copyright -.\" notice, this list of conditions and the following disclaimer in the -.\" documentation and/or other materials provided with the distribution. -.\" 3. All advertising materials mentioning features or use of this software -.\" must display the following acknowledgement: -.\" This product includes software developed by Matthew Mondor. -.\" 4. The name of Matthew Mondor may not be used to endorse or promote -.\" products derived from this software without specific prior written -.\" permission. -.\" 5. Redistribution of source code may not be released under the terms of -.\" any GNU Public License derivate. -.\" -.\" THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR -.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES -.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. -.\" IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, -.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -.\" BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF -.\" USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF -.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -.\" -.Dd Feb 11, 2003 -.Os mmsoftware -.Dt MMFIFO 3 -.Sh NAME -.Nm mmfifo -.Nd General purpose portable and versatile FIFO buffers library. -.Sh SYNOPSIS -.Fd #include -.Fd #include -.Fd #include -.Ft void -.Fn FIFO_DEFINE "fifotype_t" "objecttype" -.Ft void -.Fn FIFO_INIT "fifotype_t *fifo" "void *buffer" "u_int32_t size" -.Ft bool -.Fn FIFO_FULL "fifotype_t *fifo" -.Ft bool -.Fn FIFO_EMPTY "fifotype_t *fifo" -.Ft u_int32_t -.Fn FIFO_STAT "fifotype_t *fifo" -.Ft u_int32_t -.Fn FIFO_AVAIL "fifotype_t *fifo" -.Ft void -.Fn FIFO_FLUSH "fifotype_t *fifo" -.Ft void -.Fn FIFO_PUT "fifotype_t *fifo" "objecttype *element" -.Ft void -.Fn FIFO_GET "fifotype_t *fifo" "objecttype *element" -.Ft void -.Fn FIFO_FIND "fifotype_t *fifo" "objecttype **pointer" "objecttype *element" -.Ft void -.Fn FIFO_ALLOC "fifotype_t *fifo" "objecttype **pointer" "u_int32_t *actual" \ -"u_int32_t size" -.Ft void -.Fn FIFO_FREE "fifotype_t *fifo" "objecttype **pointer" "u_int32_t *actual" \ -"u_int32_t size" -.Sh DESCRIPTION -FIFO buffers (First In First Out) are especially useful when -implementing asynchronous systems. For instance in the implementation of -serial RS-232 read/write functions, a trap or interrupt occurs when a byte -is available to read or when the device is ready to be written more data to. -The user API has to be able to be used at anytime however so that the -application may read and write bytes at any time (in fact queueing them for -the trap handler). So two FIFO buffers are required, user API and the trap -handler, each share one side of each queue. Unlike a simple buffer, each side -do not know when the other side will insert or unqueue elements at the other -end. It therefore is extremely useful to synchronize two asynchonous -applications. -.Pp -These almost entirely only consist of macros, which allow to work with a -variety of FIFO buffer types, including FIFOs to hold user supplied type -objects. These are not implemented as linked lists but as fixed buffers -which can hold a maximum number of fixed-sized elements. They also leave -the user application the responsibility to allocate and free the buffers -(explained in the -.Fn FIFO_INIT -section). -.Ss FUNCTIONS -.Bl -tag -width indent -offset indent -.It void Fn FIFO_DEFINE "fifotype_t" "objecttype" -Permits to create a new type of FIFO buffer to hold custom objects. This -in fact only is a macro to create a new fifo*_t structure and typedef it. -.Fa fifotype_t -defines the new FIFO type which should be created. -.Fa objecttype -specifies the type of object that this fifo structure should allow to buffer. -For instance, to create a new fifo type to hold file descriptors, one could -use: -.Bd -literal -offset indent -FIFO_DEFINE(fdfifo_t, int); -.Ed -.Pp -It then would be possible to use the -.Fa fdfifo_t -type, initialize it, and use it. -.Bd -literal -offset indent -fdfifo_t f; -int buf[1024]; -int fd; - -FIFO_INIT(&f, buf, 1024); -FIFO_PUT(&f, &fd); -FIFO_GET(&f, &fd); -.Ed -.Pp -Predefined FIFO buffer types are -.Nm fifo8_t , -.Nm fifo16_t, -.Nm fifo32_t -and -.Nm fifo64_t , -to hold objects of type -.Nm u_int8_t , -.Nm u_int16_t , -.Nm u_int32_t -and -.Nm u_int64_t , -respectively. -.It void Fn FIFO_INIT "fifotype_t *fifo" "void *buffer" "u_int32_t size" -Allows to initialize a FIFO buffer. -.Fa fifo -consists of a pointer to the FIFO buffer to initialize, -.Fa buffer -is the user supplied buffer which should be able to hold the wanted number -of elements, minus one. -.Fa size -specifies the maximum number of elements that the buffer can hold. This is -not expressed in bytes, but in number of elements. -As a general rule, the buffer should hold sizeof(object) * (max + 1) bytes. -When a buffer is done with, it is the responsibility of the user application -to free the supplied -.Fa buffer . -.It bool Fn FIFO_FULL "fifotype_t *fifo" -A useful macro to determine if a FIFO buffer is full and cannot take -new elements. -.Fa fifo -is a pointer to the FIFO buffer. This is useful if for instance one does not -want the FIFO buffer to loose the oldest element if it is full and -.Fn FIFO_PUT -is used. -.It bool Fn FIFO_EMPTY "fifotype_t *fifo" -allows to determine if a FIFO buffer is currently empty, that is, does not hold -any elements. -.It u_int32_t Fn FIFO_STAT "fifotype_t *fifo" -permits to know how many elements are currently buffered into the specified -.Fa fifo -buffer, which can be read using -.Fn FIFO_GET . -.It u_int32_t Fn FIFO_AVAIL "fifotype_t *fifo" -evaluates the number of free elements in the buffer and returns it. -.It void Fn FIFO_FLUSH "fifotype_t *fifo" -allows to get rid of all the current contents, if any, of the specified -.Fa fifo -buffer. The buffer as a result becomes empty, instantly. -.It void Fn FIFO_PUT "fifotype_t *fifo" "objecttype *element" -inserts the element to which supplied -.Fa element -points, into the supplied FIFO buffer -.Fa fifo . -If the buffer is full, the oldest element is automatically discarded, allowing -an automatic rotation within the buffer, but loosing bytes. If this behavior -is not wanted, the -.Fn FIFO_FULL -macro should be used first to detect the buffer full condition. -.It void Fn FIFO_GET "fifotype_t *fifo" "objecttype *element" -attempts to read and obtain an element from the supplied FIFO buffer -.Fa fifo , -and place it into -.Fa element -supplied pointer. This can transparently fail; If it is unsure that the -FIFO buffer holds any data, the -.Fn FIFO_EMPTY -macro may be used before calling this macro. The obtained element, if it -could be read, always consists of the oldest element that was inserted using -.Fn FIFO_PUT , -that is, the First element In is the First element Out. It is also immediately -forgotten afterwards. -.It void Fn FIFO_FIND "fifotype_t *fifo" "objecttype **pointer" \ -"objecttype *element" -attempts to locate -.Fa element -within the current -.Fa fifo -buffer contents, and fills -.Fa pointer -with the element pointer if found, or NULL if the element could not be found. -.It void Fn FIFO_ALLOC "fifotype_t *fifo" "objecttype **pointer" \ -"u_int32_t *actual" "u_int32_t size" -macro is useful for operations which need to insert several elements at once, -as it allows them to allocate the required room and use -.Xr memcpy 3 -or other user-specific method to write in the data. Attempts to allocate room -for -.Fa size -elements in -.Fa fifo . -The pointer to the buffer where elements can be written is placed into -.Fa pointer , -and the number of elements which could be allocated in -.Fa actual , -which may be smaller than the requested size, in which case the caller may -perform other calls to this function. This can return smaller results for two -reasons: 1) The internal head pointer reached the end of the buffer, and -as such a single contiguous buffer cannot be provided, in which case two -calls will be required to this macro to complete the operation. 2) The buffer -did not contain enough room for the requested number of elements. The caller -should not attempt to use the returned pointer if the returned size is zero. -.It void Fn FIFO_FREE "fifotype_t *fifo" "objecttype **pointer" \ -"u_int32_t *actual" "u_int32_t size" -contrary to -.Fn FIFO_ALLOC , -permits to free back a certain number of elements at once from the buffer. -.Fa pointer -is set to the location where the elements can be found, and -.Fa actual -to the number of elements which could be freed. Like it's companion, it can -return a smaller number of elements than that requested, and multiple calls -may then be performed to complete the operation. -.El -.Sh RETURN VALUES -.Bl -tag -width indent -offset indent -.It Xo -.Fn FIFO_DEFINE , -.Fn FIFO_INIT , -.Fn FIFO_FLUSH , -.Fn FIFO_PUT , -.Fn FIFO_GET -.Xc -return nothing. -.It Xo -.Fn FIFO_FULL , -.Fn FIFO_EMPTY -.Xc -return TRUE or FALSE. -.It Xo -.Fn FIFO_STAT , -.Fn FIFO_AVAIL -.Xc -return a number of elements. -.It Fn FIFO_FIND -returns a pointer to an element into the provided pointer. -.It Xo -.Fn FIFO_ALLOC , -.Fn FIFO_FREE -.Xc -return their result pointers and size into the provided pointers. -.El -.Sh SEE ALSO -.Xr malloc 3 , -.Xr free 3 , -.Xr memcpy 3 -.Sh STANDARDS -None -.Sh HISTORY -.Nm -was originally developped on NetBSD 1.5 for the Xisop portable kernel -project from Matthew Mondor. It was modified alot in Febuary 2003 to be more -versatile and generate more optimized C code. -.Sh AUTHORS -The -.Nm -library was written by Matthew Mondor and is -Copyright (c) 2000-2004 Matthew Mondor, All rights reserved. -.Sh BUGS -Please report any bug to mmondor@accela.net diff --git a/mmsoftware/mmlib/mmfifo.c b/mmsoftware/mmlib/mmfifo.c deleted file mode 100644 index d7d9b65..0000000 --- a/mmsoftware/mmlib/mmfifo.c +++ /dev/null @@ -1,146 +0,0 @@ -/* $Id: mmfifo.c,v 1.4 2004/03/22 06:59:34 mmondor Exp $ */ - -/* - * Copyright (C) 2000-2004, Matthew Mondor - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Matthew Mondor. - * 4. The name of Matthew Mondor may not be used to endorse or promote - * products derived from this software without specific prior written - * permission. - * 5. Redistribution of source code may not be released under the terms of - * any GNU Public License derivate. - * - * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - - - - -#include - -#include -#include -#include - - - - -MMCOPYRIGHT("@(#) Copyright (c) 2000-2004\n\ -\tMatthew Mondor. All rights reserved.\n"); -MMRCSID("$Id: mmfifo.c,v 1.4 2004/03/22 06:59:34 mmondor Exp $"); - - - - -/* This function reads all available bytes from the specified FIFO and - * copies them in the supplied buffer until there are none to read, or - * until max bytes have been transfered. Returns the number of bytes that - * were successfully read. - */ -size_t -getnfifo8(u_int8_t *buffer, fifo8_t *fifo, size_t max) -{ - register u_int8_t *pos; - register size_t len, len2, done; - - if (max == 1) { - if (STATFIFO(fifo) > 0) { - GETFIFO(fifo, buffer); - return (1); - } - return (0); - } - - len = fifo->elements; - if (len > max) - len = max; - - if (len) { - pos = fifo->tail; - len2 = fifo->endbuffer - pos; - if (len2 >= len) - done = len; - else - done = len2; - mm_memcpy(buffer, pos, done); - buffer += done; - pos += done; - if (pos == fifo->endbuffer) - pos = fifo->buffer; - len2 -= done; - if (len2) { - mm_memcpy(buffer, pos, len2); - pos += len2; - } - fifo->tail = pos; - fifo->elements -= len; - } - - return (len); -} - - -/* This function inserts size elements into fifo, reading them from buffer. - * It returns the number of elements that could be inserted. - */ -size_t -putnfifo8(fifo8_t *fifo, u_int8_t *buffer, size_t size) -{ - u_int8_t *pos; - size_t len, len2, done; - - if (size == 1) { - if (FULLFIFO(fifo)) - return (0); - PUTFIFO(fifo, buffer); - return (1); - } - - len = size; - len2 = fifo->size - fifo->elements; - if (len2 > len) - len = len2; - - if (len) { - pos = fifo->head; - len2 = fifo->endbuffer - pos; - if (len2 > len) - done = len; - else - done = len2; - mm_memcpy(pos, buffer, done); - buffer += done; - pos += done; - if (pos == fifo->endbuffer) - pos = fifo->buffer; - len2 -= done; - if (len2) { - mm_memcpy(pos, buffer, len2); - pos += len2; - } - fifo->head = pos; - fifo->elements += len; - } - - return (len); -} diff --git a/mmsoftware/mmlib/mmfifo.h b/mmsoftware/mmlib/mmfifo.h deleted file mode 100644 index efe8c3e..0000000 --- a/mmsoftware/mmlib/mmfifo.h +++ /dev/null @@ -1,163 +0,0 @@ -/* $Id: mmfifo.h,v 1.7 2004/06/04 19:13:24 mmondor Exp $ */ - -/* - * Copyright (C) 2000-2004, Matthew Mondor - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Matthew Mondor. - * 4. The name of Matthew Mondor may not be used to endorse or promote - * products derived from this software without specific prior written - * permission. - * 5. Redistribution of source code may not be released under the terms of - * any GNU Public License derivate. - * - * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - - - - -#ifndef MMFIFO_H -#define MMFIFO_H - - - - -#include - - - - -/* Allows to create a new fifo_t type structure, to fit any data type */ -#define FIFO_DEFINE(n, o) typedef struct n { \ - o *top, *bottom, *head, *tail; \ - u_int32_t size; \ -} n - -FIFO_DEFINE(fifo8_t, u_int8_t); -FIFO_DEFINE(fifo16_t, u_int16_t); -FIFO_DEFINE(fifo32_t, u_int32_t); -FIFO_DEFINE(fifo64_t, u_int64_t); - - - -/* Initializes a FIFO */ -#define FIFO_INIT(f, b, s) do { \ - (f)->top = (f)->head = (f)->tail = (b); \ - (f)->bottom = &((b)[(s)]); \ - (f)->size = (s) - 1; \ -} while (/* CONSTCOND */0) - -/* Used to compute the next location of a tail or head pointer, accounting - * for the necessary occasional rotation. - */ -#define FIFO_NEXT(f, p) (&((p)[1]) == (f)->bottom ? (f)->top : &((p)[1])) - -/* Returns TRUE if the FIFO is full, that is, cannot hold more elements */ -#define FIFO_FULL(f) (FIFO_NEXT((f), (f)->head) == (f)->tail) - -/* Returns TRUE if the FIFO is empty */ -#define FIFO_EMPTY(f) ((f)->head == (f)->tail) - -/* Returns the number of currently held elements into a FIFO */ -#define FIFO_STAT(f) (FIFO_EMPTY(f) ? 0 : \ - (f)->head - (f)->tail > 0 ? \ - ((f)->head - (f)->tail) / sizeof(*(f)->head): \ - ((f)->size - ((f)->tail - (f)->head)) / sizeof(*(f)->head)) - -#define FIFO_AVAIL(f) ((f)->size - FIFO_STAT(f)) - -#define FIFO_FLUSH(f) ((f)->tail = (f)->head) - -/* If no available room the oldest element is lost. The caller may verify - * with FIFO_FULL() first if needed. - */ -#define FIFO_PUT(f, e) do { \ - *(f)->head = *(e); \ - (f)->head = FIFO_NEXT((f), (f)->head); \ - if (FIFO_EMPTY(f)) \ - (f)->tail = FIFO_NEXT((f), (f)->tail); \ -} while (/* CONSTCOND */0) - -/* Has no action if the buffer has no more elements, but does not return any - * result to say so. The caller may use FIFO_EMPTY() to check if needed. - */ -#define FIFO_GET(f, e) do { \ - if (!FIFO_EMPTY(f)) { \ - *(e) = *(f)->tail; \ - (f)->tail = FIFO_NEXT((f), (f)->tail); \ - } \ -} while (/* CONSTCOND */0) - -#define FIFO_FIND(f, p, e) do { \ - register typeof(*(e)) *r; \ - \ - *(p) = NULL; \ - for (r = (f)->tail; r != (f)->head; r = FIFO_NEXT(f, r)) \ - if (*r == *(e)) { \ - *(p) = r; \ - break; \ - } \ -} while (/* CONSTCOND */0) - -/* XXX Those are bugged for now */ -#define FIFO_ALLOC(f, p, a, s) do { \ - register int r; \ - \ - if ((r = (f)->tail - (f)->head) != 0) { \ - if (r < 1) \ - r = (f)->bottom - (f)->head; \ - if ((r /= sizeof(*(f)->head)) > (int)(s)) \ - r = (int)(s); \ - *(p) = (f)->head; \ - (f)->head = (&((f)->head[r]) == (f)->bottom ? (f)->top : \ - &((f)->head[r])); \ - } \ - *(a) = (size_t)r; \ -} while (/* CONSTCOND */0) - -#define FIFO_FREE(f, p, a, s) do { \ - register int r; \ - \ - if ((r = (f)->head - (f)->tail) != 0) { \ - if (r < 1) \ - r = (f)->bottom - (f)->tail; \ - if ((r /= sizeof(*(f)->tail)) > (int)(s)) \ - r = (int)(s); \ - *(p) = (f)->tail; \ - (f)->tail = (&((f)->tail[r]) == (f)->bottom ? (f)->top : \ - &((f)->tail[r])); \ - } \ - *(a) = (size_t)r; \ -} while (/* CONSTCOND */0) - -#define FIFO_WRITE(f, p, a, s) do { \ - /* XXX */ \ -} while (/* CONSTCOND */0) - -#define FIFO_READ(f, p, a, s) do { \ - /* XXX */ \ -} while (/* CONSTCOND */0) - - - -#endif diff --git a/mmsoftware/mmlib/mmhash.3 b/mmsoftware/mmlib/mmhash.3 deleted file mode 100644 index 75f3fe1..0000000 --- a/mmsoftware/mmlib/mmhash.3 +++ /dev/null @@ -1,523 +0,0 @@ -.\" $Id: mmhash.3,v 1.12 2004/05/23 01:50:06 mmondor Exp $ -.\" -.\" Copyright (C) 2001-2004, Matthew Mondor -.\" All rights reserved. -.\" -.\" Redistribution and use in source and binary forms, with or without -.\" modification, are permitted provided that the following conditions -.\" are met: -.\" 1. Redistributions of source code must retain the above copyright -.\" notice, this list of conditions and the following disclaimer. -.\" 2. Redistributions in binary form must reproduce the above copyright -.\" notice, this list of conditions and the following disclaimer in the -.\" documentation and/or other materials provided with the distribution. -.\" 3. All advertising materials mentioning features or use of this software -.\" must display the following acknowledgement: -.\" This product includes software developed by Matthew Mondor. -.\" 4. The name of Matthew Mondor may not be used to endorse or promote -.\" products derived from this software without specific prior written -.\" permission. -.\" 5. Redistribution of source code may not be released under the terms of -.\" any GNU Public License derivate. -.\" -.\" THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR -.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES -.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. -.\" IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, -.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -.\" BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF -.\" USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF -.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -.\" -.Dd June 17, 2003 -.Dt MMHASH 3 -.Os mmsoftware -.Sh NAME -.Nm mmhash -.Nd General purpose hash table support -.Sh SYNOPSIS -.Fd #include -.Fd #include -.Fd #include -.Ft bool -.Fo hashtable_init -.Fa "hashtable_t *table" "const char *label" "unsigned int initialcapacity" -.Fa "float loadfactor" "void *(*malloc)(size_t)" "void (*free)(void *)" -.Fa "int (*keycmp)(const void *, const void *, size_t)" -.Fa "int (*keyhash)(const void *, size_t)" "bool dynamic" -.Fc -.Ft void -.Fn hashtable_init2 "hashtable_t *table" "const char *label" "list_t *array" \ -"unsigned int capacity" "int (*keycmp)(const void *, const void *, size_t)" \ -"u_int32_t (*keyhash)(const void *, size_t)" -.Ft void -.Fn hashtable_destroy "hashtable_t *table" "bool freeall" -.Ft hashnode_t * -.Fn hashtable_lookup "hashtable_t *table" "const void *key" "size_t keysize" -.Ft bool -.Fn hashtable_link "hashtable_t *table" "hashnode_t *node" "const void *key" \ -"size_t keysize" "bool check" -.Ft void -.Fn hashtable_unlink "hashtable_t *table" "hashnode_t *node" -.Ft void -.Fn hashtable_empty "hashtable_t *table" "bool freeall" -.Ft void -.Fn hashtable_iterate "hashtable_t *table" \ -"bool (*func)(hashnode_t *, void *)" "void *udata" -.Ft u_int32_t -.Fn hashtable_hash "const void *data" "size_t size" -.Ft unsigned int -.Fn HASHTABLE_NODES "hashtable_t *table" -.Ft bool -.Fn HASHTABLE_VALID "hashtable_t *table" -.Sh DESCRIPTION -The -.Nm -library provides a useful set of functions to perform fast hash tables -operations. A hash table is used to hold key/data pair records, and does not -allow storage of duplicate key elements. The unique key can be used to very -efficiently retreive the corresponding data record after it had been archived. -Iteration through all the records is also allowed, but may be slightly slower -than simply running through a linked list. Note that the storage method is -not sequencial, which means that the natural order in which key/data pairs -are stored internally is not sorted, and automatically changes over time. -.Pp -Internally, a 32-bit hash function is used to obtain an integer for the key -element. Because it is possible for that function to generate duplicates under -certain circumstances with very similar key data, buckets are maintained, which -can hold hash duplicates, without allowing for duplicate user key data, -however. This means that when used properly, it is guaranteed that this library -does not loose entries due to duplicate 32-bit hash collisions. -.Pp -The records tend to be as distributed as possible within the number of buckets, -that is, the table capacity. The buckets consist of an array which is accessed -efficiently providing indexing. The number of buckets will automatically -increase as necessary, by two, when the specified bucket fill factor is met. -However, the system maintains statistics on the average usage of buckets in -time, which allows it to also automatically decrease the number of buckets, -making sure to not decrease it for nothing if it knows that the buckets may -still be necessary soon, for efficiency. The same method that -.Xr mmpool 3 -uses is employed for this. -.Pp -Note that this system is compatible with -.Xr mmpool 3 -and -.Xr mmlist 3 -libraries in that it is safe to allocate -.Dv hashtable_t -and -.Dv hashnode_t -objects using the -.Xr mmpool 3 -library, and that those nodes can then safely be linked into lists using the -.Xr mmlist 3 -library. Note however that when an -.Dv hashnode_t -is linked within a hash table, it should no longer be linked directly into -custom -.Dv list_t -lists until it be unlinked from the hash table. However, the user can always -provide a -.Dv node_t -as part of the custom node for user linking while the element is still linked -into the -.Dv hashtable_t . -.Pp -This library does not provide any kind of special synchronization for multiple -processes or threads to safely access the hash tables or their records. The -caller is responsible for providing this functionality where needed. The -hash table is expected to be in memory, as such this library is not intended -to work on large disk-based databases, but will work with large tables as long -as they can fit into memory. The Berkeley DB3/4 library may be what you want -to use if you need automatic disk storage where memory is only used for -synchronization, cacheing and performance. -.Ss HEADER FILE AND DATA STRUCTURES -.Pp -To use -.Nm -the user should create a custom record structure, prefixing it with an -.Dv hashnode_t -object, and holding the necessary record elements such as the key and data, -although these latter are application-specific. It then can initialize an -.Dv hashtable_t -object and use it. Those primitive objects are defined in -.Pa . -Example: -.Bd -literal -offset indent -struct myrecord { - hashnode_t node; - /* Custom fields follow */ - char key[128]; - int data; -}; -.Ed -.Pp -.Ss FUNCTIONS -.Bl -tag -width indent -offset indent -.It Xo bool -.Fo hashtable_init Fa -.Fa "hashtable_t *table" -.Fa "const char *label" -.Fa "unsigned int initialcapacity" -.Fa "float loadfactor" -.Fa "void *(*malloc)(size_t)" -.Fa "void (*free)(void *)" -.Fa "int (*keycmp)(const void *, const void *, size_t)" -.Fa "int (*keyhash)(const void *, size_t)" -.Fa "bool dynamic" -.Fc -.Xc -is necessary to use first to initialize an -.Dv hashtable_t -object before it can be used to store records. -.Pp -.Fa table -consists of the table object to initialize. -.Pp -.Fa label -only consists of a string to be used for diagnostics logging, to help to -easily identify the hashtable_t on which error occurs. Note that to enable -most diagnostics, -DDEBUG should be passed to the compiler. Otherwise, these -will only serve for out of resources error logging. -.Pp -.Fa initialcapacity -specifies the number of initial buckets that this table should start with. -.Dv HT_DEFAULT_CAPACITY -may be used, but if it is known that the table will immediately be filled with -alot of records, specifying a larger value may be useful to increase insertion -speed if the table is dynamic. -.Pp -.Fa loadfactor -specifies the load factor (based on 1, not 100), meaning how many buckets must -be in use before the table capacity is doubled. -.Dv HT_DEFAULT_FACTOR -is generally used, which equals 0.75 but it may be useful to specify an other -value at times. This parameter is ignored if the table is not dynamic. -.Pp -.Fa malloc -specifies the -.Xr malloc 3 -style allocation function to use to allocate the buckets index array, and -.Fa free -simiarily specifies the -.Xr free 3 -like corresponding counterpart. Specifying custom functions may be useful to -use a table in shared memory, for instance. -.Pp -.Fa keycmp -specifies a custom application-specific function which should be employed -internally to perform key comparision. If it is known that the key data in the -record node structure is always 32-bit aligned, for instance, specifying a -custom function for key comparision can improove performance over using the -standard -.Xr memcmp 3 -function. It is important for the custom function to semantically behave -the same as -.Xr memcmp 3 , -and it is allowed to provide a case-insensitive variant. Of course, the size -argument may or may not be useful for a custom function where the size is -known and can be assumed. -.Pp -.Fa keyhash -similarily specifies the function which should be used to generate 32-bit -hashes on arbitrary data. Generally, -.Fn hashtable_hash -is used, which is general-purpose and case-sensitive, but using a custom -function may be useful to improve performance assuming bit alignment, or using -a case-insensitive algorithm to derive the hash value. -.Pp -.Fa dynamic -specifies if -.Fa loadfactor -should be taken in consideration and hash table capacity (number of buckets) -increased as judged necessary. The table will also dynamically be able to -shrink it's buckets capacity when statistics show that memory is uselessly -wasted for some time. Note that it is safe to insert much more -.Dt hashnode_t -records than the actual number of buckets in a non-dynamic table, but that -doing so will slow down linking (insert) and find (lookup) operations. -.It void Fn hashtable_init2 "hashtable_t *table" "const char *label" \ -"list_t *array" "unsigned int capacity" \ -"int (*keycmp)(const void *, const void *, size_t)" \ -"u_int32_t (*keyhash)(const void *, size_t)" -Initializes an -.Dv hashtable_t -like -.Fn hashtable_init , -but does not allocate anything and creates a static (as opposed to dynamic) -table with a fixed number of buckets. The array of buckets must be supplied -as well as it's number of elements. This is useful when hashtables are to be -allocated and freed efficiently through a -.Dv pool_t -using -.Xr mmpool 3 . -It is important never to call -.Fn hashtable_free -on such a table. -.Fn hashtable_empty -can however be used if wanted before discarding this table. -.It void Fn hashtable_destroy "hashtable_t *table" "bool freeall" -Allows to destroy a previously initialized -.Dv hashtable_t -object. -.Pp -.Fa table -specifies the table to affect and -.Fa freeall -allows to optionally automatically call the -.Xr mmpool 3 -.Fn pool_free -function on all elements if set to -.Dv TRUE , -or to leave them alone if -.Dv FALSE . -.Pp -Note that if all entries are known to have originated from the same -.Dv pool_t -and that the entire pool could be destroyed, it is faster to specify -.Dv FALSE -and to use -.Fn pool_destroy , -since when setting -.Fa autofree -to -.Dv TRUE -the nodes will be individually freed, when -.Fn pool_destroy -will just free all the pages. -.It hashnode_t * Fn hashtable_lookup "hashtable_t *table" "const void *key" \ -"size_t keysize" -allows to perform a fast lookup into specified -.Fa table , -for a record which unique key matches the specified -.Fa key -and -.Fa keysize . -Note that the returned pointer will correspond to the user-supplied one which -was supplied at -.Fn hashtable_link , -that is, to the actual -.Dv hashnode_t -prefixed record in memory, not to a copy. -.It bool Fn hashtable_link "hashtable_t *table" "hashnode_t *node" \ -"const void *key" "size_t keysize" "bool check" -attempts to link the specified -.Fa node -record object into the -.Fa table -.Dv hashtable_t -object. -.Pp -The record will not be copied but will rather be linked. This means -that the user is responsible for holding the memory for the supplied -.Fa node -for as long as it is required. The -.Xr mmpool 3 -library is generally used to allocate records before linking them using this -function. -.Pp -The -.Fa key -and -.Fa keysize -arguments specify the location and size of the actual unique key in the -supplied record. Those will internally be required by the library when calling -the -.Fa keycmp -and -.Fa keyhash -functions provided at -.Fn hashtable_init , -because the library has no other means to know where the key data is stored -within the custom application-specific -.Dv hashnode_t -based structure used to hold the records. -.Pp -If -.Fa check -is TRUE, this function will refuse to insert a record if the key is identical -to an already existing one, which would cause a duplicate. Although occasional -duplicates are allowed from the supplied -.Fa keyhash -function to -.Fn hashtable_init , -duplicate record keys are not allowed. It is generally recommended to specify -TRUE here, however for performance reasons this can be FALSE in cases where -an -.Fn hashtable_lookup -was first called already. -.Pp -If the table is dynamic, it's -number of buckets will automatically be increased if necessary for performance -considerations, when the fill/load factor is reached. -.It void Fn hashtable_unlink "hashtable_t *table" "hashnode_t *node" -permits to unlink the specified -.Fa node -record from the -.Fa table -hash table, which obviously must previously have been inserted into it. The -.Fa node -is usually obtained from a previous -.Fn hashtable_lookup -function call. -.Pp -Note that although the record is unlinked from the table, it is -not freed from memory. The caller is responsible for maintaining the memory -associated with the record nodes (usually using the -.Xr mmpool 3 -library). -.Pp -It is safe to call this function within an iterator function used with -.Fn hashtable_iterate . -If the table is dynamic and that this function is not called from within -an iterator function, the capacity (number of buckets) will automatically -be reduced if statistics suggest that it has been too wide for too long. -Those statistics are maintained even when called within an iterator function -so that the next non-iterated call could have an opportunity to shrink the -capacity. -.It void Fn hashtable_empty "hashtable_t *table" "bool freeall" -unlinks all records from the supplied -.Fa table . -.Pp -The hashtable capacity is automatically restored to the initial one specified -at -.Fn hashtable_init . -If -.Fa freeall -is -.Dv TRUE , -all nodes linked in the table are also automatically freed using -the -.Xr mmpool 3 -library -.Fn pool_free -function. -.Pp -Note that similarily to with -.Fn hashtable_destroy , -it is faster to specify -.Dv FALSE -for -.Fa freeall -and to call -.Xr mmpool 3 -.Fn pool_destroy -if it is known that all nodes originated from the same -.Dv pool_t . -.It void Fn hashtable_iterate "hashtable_t *table" \ -"bool (*func)(hashnode_t *, void *)" "void *udata" -permits to run through the existing records of the specified -.Fa table . -.Pp -The specified -.Fa func -function will be called for each record, sequencially, supplied with the -pointer to a record and the -.Fa udata -custom user data pointer (which can be NULL, but may be useful to share a -common context among iterations). As long as this function returns TRUE, the -iteration will continue. If it returns FALSE, iteration will immediately stop. -.Pp -The order in which the records will flow is unsorted and -undetermined. As long as the supplied function returns -.Dv TRUE , -it will be fed new records until they all have been processed. If it returns -.Dv FALSE , -.Fn hashtable_iterate -immediately stops iteration and returns. -.Pp -It is safe to call -.Fn hashtable_unlink -from within an iterator function. However, -.Fn hashtable_link , -on the other hand, could cause an entry to be located in a next-to-iterate -location, and should therefore only be called with care if on the same table -when iterating, because the same entry could possibly be processed again -during the same iteration. -.It u_int32_t Fn hashtable_hash "const void *data" "size_t size" -is provided as a generic case-sensitive and bit alignment independant -hashing function to be specified at -.Fn hashtable_init . -.Pp -It is known to work well in most circumstances, but it is still recommended -to use a custom function where appropriate when optimizations or functionality -enhancements can be achieved for a particular key data set type. For instance, -a function taking advantage of known 32-bit alignment will be faster. Also, -a system which wants to work with case-insensitive alphanumeric keys -may want to use a different function than -.Fn hashtable_hash . -.It unsigned int Fn HASHTABLE_NODES "hashtable_t *table" -This macro is useful to return the total number of current nodes linked into -the specified -.Fa table -.Dv hashtable_t . -.It bool Fn HASHTABLE_VALID "hashtable_t *table" -This macro returns -.Dv TRUE -if the supplied -.Fa table -is valid, that is, was properly initialized with success, and was not yet -destroyed, or -.Dv FALSE -otherwise. -.El -.Sh RETURN VALUES -.Bl -tag -width indent -offset indent -.It Xo -.Fn hashtable_destroy , -.Fn hashtable_unlink , -.Fn hashtable_empty , -.Fn hashtable_iterate -.Xc -return nothing. -.It Fn hashtable_init -returns -.Dv TRUE -on success, or -.Dv FALSE -on error (out of memory). -.It Fn hashtable_lookup -returns the pointer to the matching -.Dv hashnode_t -if found, or -.Dv NULL -if no entry matched the supplied key. -.It Fn hashtable_link -returns -.Dv TRUE -if the record could be inserted, or -.Dv FALSE -if it refused to insert it because it would consist of a duplicate key entry. -.It Fn hashtable_hash -returns a 32-bit hash code/value corresponding to the specified memory area. -.It Fn HASHTABLE_NODES -returns the total number of currently existing records into the specified -table. -.It Fn HASHTABLE_VALID -returns -.Dv TRUE -or -.Dv FALSE . -.El -.Sh SEE ALSO -.Xr malloc 3 , -.Xr free 3 , -.Xr mmlist 3 , -.Xr mmpool 3 , -.Xr memcmp 3 . -.Sh STANDARDS -None -.Sh HISTORY -.Xr mmhash 3 -was originally developped for the Xisop portable microkernel project -from Matthew Mondor. It later on was adapted to be used by -.Xr mmstatd 8 . -It now is used by other mmsoftware components as well. -.Sh AUTHORS -The mmhash library was written by Matthew Mondor and is -Copyright (c) 2001-2004, Matthew Mondor, All rights reserved. -.Sh BUGS -Please report any bug to mmondor@accela.net diff --git a/mmsoftware/mmlib/mmhash.c b/mmsoftware/mmlib/mmhash.c deleted file mode 100644 index a80b1be..0000000 --- a/mmsoftware/mmlib/mmhash.c +++ /dev/null @@ -1,436 +0,0 @@ -/* $Id: mmhash.c,v 1.25 2004/06/04 01:02:32 mmondor Exp $ */ - -/* - * Copyright (C) 2001-2004, Matthew Mondor - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Matthew Mondor. - * 4. The name of Matthew Mondor may not be used to endorse or promote - * products derived from this software without specific prior written - * permission. - * 5. Redistribution of source code may not be released under the terms of - * any GNU Public License derivate. - * - * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - - - -#include -#include - -#include -#include - - - -MMCOPYRIGHT("@(#) Copyright (c) 2001-2004\n\ -\tMatthew Mondor. All rights reserved.\n"); -MMRCSID("$Id: mmhash.c,v 1.25 2004/06/04 01:02:32 mmondor Exp $"); - - - -/* This system is safe to use 32-bit hashes internally, despite the possibility - * for collisions. We maintain an array or buckets, within which the entries - * are distributed. A 32-bit hash collision entry will end up on the same - * bucket. We however also make sure to not allow to store exact duplicate - * keys. The number if buckets will increase and decrease whenever the system - * detects that it becomes necessary for efficiency. The larger the number of - * buckets, the less nodes are likely to coexist in each bucket. The bucket - * index to use is evaluated using a modulo to convert the 32-bit hash to - * fit into the current number of buckets in the table. Of course, when - * the number of buckets is to be updated (which ideally happens rarely), - * the entries are rehashed to be properly indexed within the new capacity. - * - * The number of buckets is automatically doubled when the table fills up - * to the specified factor (usually 0.75/1), and of course the table rehashed. - * The hash table capacity can therefore also be reduced over time when it is - * known that memory wastage occurs for some while (statistics are maintained - * for this, similarily to how the mmpool library works). This means that if - * the number of entries is constantly growing high and falling back low, - * the table capacity should not change for better performance. - * - * Commonly used values for the initial hash table bucket capacity and fill - * factor are 128, 0.75, respectively, to which are set HT_DEFAULT_CAPACITY - * and HT_DEFAULT_FACTOR. - * - * Searching for a key by pattern, which requires iterating through the - * nodes, rather than through it's absolute key can actually be a little - * slower than running through a simple linked list of absolute hash values, - * since all buckets must be scanned. However, we ensure to stop running - * through buckets when the total number of mappings have been scanned already. - * Lookups using the absolute key of the node will be much faster in the case - * where the hash table grows considerably, however. - */ - - - -#define HASH_INDEX(h, s) ((h) % (s)) - - - -static void hashtable_rehash(hashtable_t *, unsigned int); - - - -bool hashtable_init(hashtable_t *t, const char *label, - unsigned int initialcapacity, float loadfactor, - void *(*allocfunc)(size_t), void (*freefunc)(void *), - int (*keycomp)(const void *, const void *, size_t), - u_int32_t (*keyhash)(const void *, size_t), bool dynamic) -{ - if (DEBUG_FALSE(HASHTABLE_VALID(t))) { - DEBUG_PRINTF("hashtable_init", "Table (%p = %s) already initialized", - t, t->label); - return FALSE; - } - - if ((t->array = allocfunc(sizeof(list_t) * initialcapacity)) != NULL) { - unsigned int i; - - t->magic = MAGIC_HASHTABLE; - t->label = label; - t->malloc = allocfunc; - t->free = freefunc; - t->keycomp = keycomp; - t->keyhash = keyhash; - t->nodes = 0; - t->initial = t->capacity = initialcapacity; - t->loadfactor = loadfactor; - t->threshold = (unsigned int)(((float)initialcapacity) * - loadfactor); - t->avgtotal = t->avgcnt = initialcapacity; - t->dynamic = dynamic; - t->iterating = FALSE; - for (i = 0; i < initialcapacity; i++) - DLIST_INIT(&(t->array[i])); - - return TRUE; - } else - syslog(LOG_NOTICE, "hashtable_init() - Table (%p = %s) - malloc(%d)", - t, label, (int)sizeof(list_t) * initialcapacity); - - return FALSE; -} - - -void hashtable_init2(hashtable_t *t, const char *label, list_t *array, - unsigned int capacity, - int (*keycomp)(const void *, const void *, size_t), - u_int32_t (*keyhash)(const void *, size_t)) -{ - unsigned int i; - - if (DEBUG_FALSE(HASHTABLE_VALID(t))) { - DEBUG_PRINTF("hashtable_init2", "Table (%p = %s) already initialized", - t, t->label); - return; - } - - t->magic = MAGIC_HASHTABLE; - t->label = label; - t->array = array; - t->malloc = NULL; - t->free = NULL; - t->keycomp = keycomp; - t->keyhash = keyhash; - t->nodes = 0; - t->initial = t->capacity = capacity; - t->loadfactor = 0; - t->threshold = 0; - t->avgtotal = t->avgcnt = capacity; - t->dynamic = FALSE; - t->iterating = FALSE; - for (i = 0; i < capacity; i++) - DLIST_INIT(&(t->array[i])); -} - - -void hashtable_destroy(hashtable_t *t, bool freeall) -{ - if (DEBUG_TRUE(HASHTABLE_VALID(t))) { - if (t->array != NULL) { - if (freeall) { - register unsigned int i, done; - register list_t *l; - register hashnode_t *k, *kt; - - for (i = done = 0; done < t->nodes && i < t->capacity; i++) { - l = &(t->array[i]); - if (DLIST_NODES(l) > 0) { - for (k = DLIST_TOP(l); k != NULL; k = kt) { - kt = DLIST_NEXT(k); - pool_free((pnode_t *)k); - done++; - } - } - } - } - t->free(t->array); - } - t->magic = 0; - } else - DEBUG_PRINTF("hashtable_destroy", - "Invalid hashtable_t pointer (%p)", t); -} - - -hashnode_t *hashtable_lookup(hashtable_t *t, const void *key, size_t keysize) -{ - register u_int32_t hash; - register unsigned int i; - register list_t *l; - register hashnode_t *k = NULL; - - if (DEBUG_TRUE(HASHTABLE_VALID(t))) { - hash = t->keyhash(key, keysize); - i = HASH_INDEX(hash, t->capacity); - l = &(t->array[i]); - if (DLIST_NODES(l) > 0) { - DLIST_FOREACH(l, k) { - if (k->hash == hash && k->keysize == keysize && - t->keycomp(k->key, key, keysize) == 0) - break; - } - } - } else - DEBUG_PRINTF("hashtable_lookup", - "Invalid hashtable_t pointer (%p)", t); - - return k; -} - - -bool hashtable_link(hashtable_t *t, hashnode_t *k, const void *key, - size_t keysize, bool check) -{ - register u_int32_t hash; - register unsigned int i; - register list_t *l; - bool ok = TRUE; - - if (DEBUG_FALSE(!(HASHTABLE_VALID(t)))) { - DEBUG_PRINTF("hashtable_link", - "Invalid hashtable_t pointer (%p)", t); - return FALSE; - } - if (DEBUG_FALSE(k == NULL)) { - DEBUG_PRINTF("hashtable_link", "Invalid hashnode_t pointer (NULL)"); - return FALSE; - } - - hash = t->keyhash(key, keysize); - i = HASH_INDEX(hash, t->capacity); - l = &(t->array[i]); - if (check) { - /* We do not allow exact duplicates, so verify first. Duplicate - * hashes are fine, however, as long as the key data is not identical. - */ - if (DLIST_NODES(l) > 0) { - register hashnode_t *tk; - - DLIST_FOREACH(l, tk) { - if (tk == k || (tk->hash == hash && tk->keysize == keysize && - t->keycomp(tk->key, key, keysize) == 0)) { - ok = FALSE; - break; - } - } - } - } - if (ok) { - k->magic = MAGIC_HASHNODE; - k->hash = hash; - k->list = l; - k->key = key; - k->keysize = keysize; - DLIST_INSERT(l, (node_t *)k); - /* Grow capacity if necessary */ - t->nodes++; - if (t->dynamic && !t->iterating) { - if (t->nodes > t->threshold) - hashtable_rehash(t, t->capacity * 2); - } - } - - return ok; -} - - -void hashtable_unlink(hashtable_t *t, hashnode_t *k) -{ - if (DEBUG_TRUE(HASHTABLE_VALID(t))) { - if (DEBUG_TRUE(k != NULL && k->magic == MAGIC_HASHNODE)) { - int exceeding; - - k->magic = 0; - DLIST_UNLINK(k->list, (node_t *)k); - k->list = NULL; - t->nodes--; - - /* Verify if the capacity should be reduced, using statistics */ - t->avgtotal += t->capacity; - t->avgcnt++; - if (t->avgcnt > t->capacity / (t->initial * 3)) { - t->avgcnt = 1; - t->avgtotal = t->capacity; - } - /* Rehash with a smaller capacity if necessary */ - if (t->dynamic && !t->iterating) { - if ((exceeding = t->capacity - (t->avgtotal / t->avgcnt)) > 0) - hashtable_rehash(t, t->capacity - exceeding); - } - } else - DEBUG_PRINTF("hashtable_unlink", - "Invalid hashtnode_t pointer (%p)", k); - } else - DEBUG_PRINTF("hashtable_unlink", - "Invalid hashtable_t pointer (%p)", t); -} - - -/* Note that as the user generally has a pool_t dedicated to the hashnode_t - * elements for a particular table, it may be more efficient to not use - * the option and to pool_free() which does not need to iterate - * through nodes. This function has to however, because it obviously cannot - * assume that the caller wishes to free all nodes of the origin pool_t, or - * that all entries origin from the same pool_t. - */ -void hashtable_empty(hashtable_t *t, bool freeall) -{ - register unsigned int i; - register list_t *l; - register hashnode_t *k, *kt; - - if (DEBUG_TRUE(HASHTABLE_VALID(t))) { - if (freeall) { - for (i = 0; i < t->capacity; i++) { - l = &(t->array[i]); - if (DLIST_NODES(l) > 0) { - for (k = DLIST_TOP(l); k != NULL; k = kt) { - kt = DLIST_NEXT(k); - pool_free((pnode_t *)k); - } - } - } - } else { - for (i = 0; i < t->capacity; i++) - DLIST_INIT(&(t->array[i])); - } - if (t->dynamic && !t->iterating) - hashtable_rehash(t, t->initial); - } else - DEBUG_PRINTF("hashtable_empty", - "Invalid hashtable_t pointer (%p)", t); -} - - -void hashtable_iterate(hashtable_t *t, - bool (*func)(hashnode_t *, void *), void *udata) -{ - register unsigned int i; - register list_t *l; - register hashnode_t *k, *kt; - - if (DEBUG_TRUE(HASHTABLE_VALID(t))) { - if (t->nodes > 0) { - t->iterating = TRUE; - for (i = 0; i < t->capacity; i++) { - l = &(t->array[i]); - if (DLIST_NODES(l) > 0) { - /* Note that we use a temporary variable to hold the next - * key, in case the user function alters the key node - * (i.e. unlinks it) - */ - for (k = DLIST_TOP(l); k != NULL; k = kt) { - kt = DLIST_NEXT(k); - if (!func(k, udata)) { - t->iterating = FALSE; - return; - } - } - } - } - t->iterating = FALSE; - } - } else - DEBUG_PRINTF("hashtable_iterate", - "Invalid hashtable_t pointer (%p)", t); -} - - -/* Rehashes the whole hashtable so that the capacity may be changed to the - * specified one. The memory area is also automatically changed. Ideally, - * this only occurs rarely. If it fails because of a lack of memory, the - * hash table will simply not be affected, but lookups will become slower. - */ -static void hashtable_rehash(hashtable_t *t, unsigned int newcapacity) -{ - list_t *newarray; - - if ((newarray = t->malloc(sizeof(list_t) * newcapacity)) != NULL) { - register unsigned int i, done; - - for (i = 0; i < newcapacity; i++) - DLIST_INIT(&newarray[i]); - - for (i = done = 0; done < t->nodes && i < t->capacity; i++) { - register hashnode_t *k, *kt; - register list_t *l, *newl; - - l = &(t->array[i]); - if (DLIST_NODES(l) > 0) { - for (k = DLIST_TOP(l); k != NULL; k = kt) { - kt = DLIST_NEXT(k); - newl = &newarray[HASH_INDEX(k->hash, newcapacity)]; - DLIST_SWAP(newl, l, (node_t *)k, TRUE); - k->list = newl; - done++; - } - } - } - - t->capacity = newcapacity; - t->threshold = (unsigned int)(((float)newcapacity) * t->loadfactor); - t->free(t->array); - t->array = newarray; - } else - syslog(LOG_NOTICE, "hashtable_rehash() - Table (%p = %s) - " - "Out of memory, not rehashing from %u to %u buckets", - t, t->label, t->capacity, newcapacity); -} - - -u_int32_t hashtable_hash(const void *mem, size_t size) -{ - register u_int32_t hash; - register const unsigned char *curmem, *tomem; - - hash = 0; - curmem = tomem = mem; - tomem += size; - - for (; curmem < tomem; curmem++) - hash = *curmem + (31 * hash); - - return hash; -} diff --git a/mmsoftware/mmlib/mmhash.h b/mmsoftware/mmlib/mmhash.h deleted file mode 100644 index 3d4bcdc..0000000 --- a/mmsoftware/mmlib/mmhash.h +++ /dev/null @@ -1,116 +0,0 @@ -/* $Id: mmhash.h,v 1.9 2004/05/23 02:17:27 mmondor Exp $ */ - -/* - * Copyright (C) 2001-2004, Matthew Mondor - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Matthew Mondor. - * 4. The name of Matthew Mondor may not be used to endorse or promote - * products derived from this software without specific prior written - * permission. - * 5. Redistribution of source code may not be released under the terms of - * any GNU Public License derivate. - * - * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - - - -#ifndef MMHASH_H -#define MMHASH_H - - - -#include - -#include -#include -#include - - - -typedef struct hashnode hashnode_t; -typedef struct hashtable hashtable_t; - - - -struct hashnode { - pnode_t node; - u_int32_t magic; - list_t *list; - u_int32_t hash; - const void *key; - size_t keysize; - /* Custom user data will follow, uncluding the key element to which the - * previous key pointer is expected to point. - */ -}; - -struct hashtable { - pnode_t node; /* In case we want a pool_t of hashtable_t */ - u_int32_t magic; - const char *label; - unsigned int initial, capacity, threshold, nodes; - float loadfactor; - list_t *array; - void *(*malloc)(size_t); - void (*free)(void *); - int (*keycomp)(const void *, const void *, size_t); - u_int32_t (*keyhash)(const void *, size_t); - unsigned int avgtotal, avgcnt; - bool dynamic, iterating; -}; - - - -#define MAGIC_HASHTABLE 0x4854424c /* HTBL */ -#define MAGIC_HASHNODE 0x484e4f44 /* HNOD */ -#define HT_DEFAULT_CAPACITY 128 -#define HT_DEFAULT_FACTOR 0.75f - -#define HASHTABLE_NODES(t) ((t)->nodes) -#define HASHTABLE_VALID(t) ((t) != NULL && (t)->magic == MAGIC_HASHTABLE) - - - -extern bool hashtable_init(hashtable_t *, const char *, - unsigned int, float, void *(*)(size_t), - void (*)(void *), - int (*)(const void *, const void *, size_t), - u_int32_t (*)(const void *, size_t), bool); -extern void hashtable_init2(hashtable_t *, const char *, - list_t *, unsigned int, - int (*)(const void *, const void *, size_t), - u_int32_t (*)(const void *, size_t)); -extern void hashtable_destroy(hashtable_t *, bool); -extern hashnode_t * hashtable_lookup(hashtable_t *, const void *, size_t); -extern bool hashtable_link(hashtable_t *, hashnode_t *, - const void *, size_t, bool); -extern void hashtable_unlink(hashtable_t *, hashnode_t *); -extern void hashtable_empty(hashtable_t *, bool); -extern void hashtable_iterate(hashtable_t *, - bool (*)(hashnode_t *, void *), void *); -extern u_int32_t hashtable_hash(const void *, size_t); - - - -#endif diff --git a/mmsoftware/mmlib/mmheap.3 b/mmsoftware/mmlib/mmheap.3 deleted file mode 100644 index c4dd094..0000000 --- a/mmsoftware/mmlib/mmheap.3 +++ /dev/null @@ -1,485 +0,0 @@ -.\" $Id: mmheap.3,v 1.13 2004/05/05 23:59:56 mmondor Exp $ -.\" -.\" Copyright (C) 2003-2004, Matthew Mondor -.\" All rights reserved. -.\" -.\" Redistribution and use in source and binary forms, with or without -.\" modification, are permitted provided that the following conditions -.\" are met: -.\" 1. Redistributions of source code must retain the above copyright -.\" notice, this list of conditions and the following disclaimer. -.\" 2. Redistributions in binary form must reproduce the above copyright -.\" notice, this list of conditions and the following disclaimer in the -.\" documentation and/or other materials provided with the distribution. -.\" 3. All advertising materials mentioning features or use of this software -.\" must display the following acknowledgement: -.\" This product includes software developed by Matthew Mondor. -.\" 4. The name of Matthew Mondor may not be used to endorse or promote -.\" products derived from this software without specific prior written -.\" permission. -.\" 5. Redistribution of source code may not be released under the terms of -.\" any GNU Public License derivate. -.\" -.\" THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR -.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES -.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. -.\" IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, -.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -.\" BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF -.\" USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF -.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -.\" -.Dd September 3, 2003 -.Dt MMHEAP 3 -.Os mmsoftware -.Sh NAME -.Nm mmheap -.Nd Functions to allocate and manage shared or secure memory -.Sh SYNOPSIS -.Fd #include -.Fd #include -.Fd #include -.Ft int -.Fn shlocks_init "const char *path" "int *locks" "int many" "uid_t uid" \ -"gid_t gid" "mode_t mode" -.Ft int -.Fn shlocks_reopen "const char *path" "int *locks" "int many" -.Ft int -.Fn shlocks_destroy "const char *path" "int *locks" "int many" "bool unlink" -.Ft void * -.Fn shmem_malloc "size_t size" -.Ft void -.Fn shmem_free "void *mem" -.Ft bool -.Fn shmem_getregion "void **raddr" "size_t *rsize" "void *mem" -.Ft void * -.Fn shmem_realloc "void *mem" "size_t size" -.Ft void -.Fn shmem_freeall "void" -.Ft void * -.Fn secure_malloc "size_t size" -.Ft void -.Fn secure_free "void *mem" -.Ft void * -.Fn secure_realloc "void *mem" "size_t size" -.Ft void -.Fn secure_freeall "void" -.Sh DESCRIPTION -The -.Nm -library provides simple functions which permit to use 4.2-4.4BSD style shared -and secure memory, using -.Xr mmap 2 -with -.Sy MAP_ANON -to allocate memory, -.Xr flock 3 -for inter-process synchronization to shared resources using temporary files -as locks, and -.Xr mlock 2 -to secure (wire) critical memory regions into physical memory, preventing them -from being swaped out to the swap device (which also usually means on disk). -This system does not require the use of -.Xr msync 2 -after modifying shared memory. -.Pp -Other alternatives would be SVR4-style using -.Xr mmap 2 -with "/dev/zero" file for shared memory and -.Xr fcntl 2 -for locking/synchronization, POSIX.1-style using -.Xr shm_open 3 -for shared memory, traditional -.Xr mmap 2 -using a temporary file on the filesystem with -.Xr msync 2 -requirement, and SysV -.Xr shmget 2 -and -.Xr semget 2 -which could leak resources at server shutdown, but would allow to more easily -resize the shared memory for all processes, and to be reused if necessary by -several independant invokations of an application. -.Pp -Using this system, shared memory is usually allocated by a parent process, -as well as the necessary locks for synchronization, which then can -.Xr fork 2 -which causes the children to inhrerit the shared memory and locks. It also -has the disadvantage that the shared memory areas cannot easily be resized, -since it would require all processes using the shared memory to modify their -mappings. For this reason, shared memory is usually allocated using a decent -size for the application requirements, and a system like -.Xr mmpool 3 -is then used to allocate and free nodes of the shared region. The application -of course is expected to use -.Xr flock 3 -with shared or exclusive locking, for synchronization as required, using -previously created shared locks. -.Pp -The secure allocation function counterparts do not provide shared memory, -that is, can be inherited to children as exact independant copies but not -using the same memory regions. These are most useful to allocate critical -data which should never leak to disk or swap, such as cryptographic keys. -.Pp -These functions use an API which is compatible with ANSI-C memory management -functions, so that it can easily be used by libraries which allow the caller -to specify ANSI-C compatible custom equivalents like -.Xr mmpool 3 , -.Xr mmfd 3 -or -.Xr mmhash 3 . -.Ss FUNCTIONS -.Bl -tag -width indent -offset left -.It int Fn shlocks_init "const char *path" "int *locks" "int many" \ -"uid_t uid" "gid_t gid" "mode_t mode" -Initializes an array of locks, implemented as temporary files on the -filesystem, for subsequent use of -.Xr flock 3 . -.Pp -.Fa path -specifies the absolute path to a file which will be used as a template to -create the locks. An example would be "/var/run/myapplication.lock". For each -element in the array, the filename will be appended a ".nn" suffix where -"nn" consists of a number. -.Pp -.Fa locks -consists of a pointer to an integer array which will be initialized to index -to the actual filedescriptors corresponding to each lock file. -.Pp -.Fa many -specifies the number of locks that should be initialized, corresponding to the -number of elements into the supplied integer array. -.Pp -.Fa uid -specifies the user ID which should be assigned as the file owner for the locks. --1 may be specified to use the defaults (uid of process creating the files). -.Pp -.Fa gid -specifies the group ID to be assigned as the file group for the licks. --1 may be specified to use the defaults. -.Pp -.Fa mode -specifies the file mode to be applied to created lock files. Frequently used -modes for this consist of 0600 or 0660. -.Pp -These will be inherited to the children processes after -.Xr fork 2 -calls, but a call to -.Fn shlocks_reopen -will become necessary to reopen the lock files in read-only mode by each -children processes, because of the nature of -.Xr flock 2 . -.Pp -It becomes easy to protect the wanted resources by using -.Xr flock 3 -on the filedescriptor index corresponding to a lock: -.Pp -.Bd -literal -offset left -enum shlocks { - SHLOCK_RESOURCE1 = 0, - SHLOCK_RESOURCE2, - SHLOCK_RESOURCE3, - SHLOCK_MAX -}; -static int locks[SHLOCK_MAX]; -#define LOCKSPATH "/var/run/app.lock" - -int err; - -/* In parent process */ -(void) shlocks_init(LOCKSPATH, locks, SHLOCK_MAX, -1, -1, - 0600); - -/* ... */ - -/* In children processes */ -(void) shlocks_reopen(LOCKSPATH, locks, SHLOCK_MAX); - -/* ... */ - -/* Gain exclusive access to resource2 */ -while ((err = flock(locks[SHLOCK_RESOURCE2], LOCK_EX)) == -1 - && errno == EINTR) ; -if (err == 0) { - /* Access it, then unlock resource2 */ - (void) flock(locks[SHLOCK_RESOURCE2], LOCK_UN); -} -.Ed -.It int Fn shlocks_reopen "const char *path" "int *locks" "int many" -Allows to reopen previously created lock files using -.Fn shlocks_init , -for use in children processes as part of their initialization. -Because of the nature of -.Xr flock 2 , -which requires lock files to be reopened in each process after -.Xr fork 2 , -this function is most useful. -.It int Fn shlocks_destroy "const char *path" "int *locks" "int many" \ -"bool unlink" -Permits to destroy a previously created lock array by -.Fn shlocks_init . -The filedescriptors are closed, and the files optionally deleted using -.Xr unlink 2 -if -.Fa unlink is TRUE. -It is possible for the files to linger since -.Xr unlink 2 -would most likely fail if the appication dropped it's privileges using -.Xr setuid 2 , -or changed root using -.Xr chroot 2 -after initialization. However, those lock files will be re-used the next time -the application will run, if such is the case. -.Pp -.Fa path -specifies the same absolute pathname which was supplied at -.Fn shlocks_init . -.Pp -.Fa locks -also points to the same integer array which was provided at -.Fn shlocks_init . -.Pp -.Fa many -specifies the size of the integer array, that is, the number of locks -that were previously initialized with -.Fn shlocks_init . -.Pp -.Fa unlink -tells to attempt to unlink the lock files if TRUE. The files will voluntarily -be left on the filesystem if FALSE. -.It void * Fn shmem_malloc "size_t size" -Permits to allocate a shared memory block of -.Fa size -bytes which will be inherited to the children processes at -.Xr fork 2 . -4.2BSD-style -.Xr mmap 2 -with -.Sy MAP_ANON -is used to allocate the memory area. -.Pp -The memory regions which are shared by multiple processes should be protected -using synchronization locks as required, with -.Xr flock 2 . It however is not necessary to call -.Xr msync 2 -on the memory area after modifications are made to propagate changes to -other processes. -.Pp -It is recommended to allocate few large blocks of memory rather than many -small blocks if memory using this function. For this reason it may be very -useful to use this function in conjonction with the -.Xr mmpool 3 -library. -.It void Fn shmem_free "void *mem" -Frees the shared memory block -.Fa mem -which should have previously been allocated using -.Fn shmem_malloc . -Note that this only invalidates the region for the current process, because -of the nature of -.Xr mmap 2 . -It may be necessary for more than one process shareing the area to need to -call this function as a result. -.It bool Fn shmem_getregion "void **raddr" "size_t *rsize" "void *mem" -Permits to obtain the address and the size of the memory region allocated using -.Xr mmap 2 -so that the caller may optionally use 4.4BSD -.Xr madvise 2 -or -.Xr minherit 2 -easily after calling -.Fn shmem_malloc . -If the supplied -.Fa mem -address was a valid address obtained from calling -.Fn shmem_malloc , -TRUE is returned, with the region address in -.Fa raddr -and it's size in -.Fa rsize . -Otherwise, FALSE is returned. -.It void * Fn shmem_realloc "void *mem" "size_t size" -Behaves like ANSI-C -.Xr realloc 3 -but on a block of memory which previously must have been allocated using -.Fn shmem_malloc . -Note that this has the same limitations as -.Fn shmem_free -because of the nature of -.Xr mmap 2 . -.Pp -The returned pointer may point to a new location, in which case the previous -unaffected memory area will have been copied over. Note that the size of -the area will not be shrinked for performance considerations but that it may -be enlarged as necessary. -.Pp -Specifying NULL for -.Fa mem -will cause this function to behave like -.Fn shmem_malloc . -Specifying 0 for -.Fa size -will cause it to operate like -.Fn shmem_free . -.It void Fn shmem_freeall "void" -This utility function allows to immediately free all allocated blocks which -were obtained by -.Fn shmem_malloc -for the current process. This may be particularily useful to call after using -.Xr fork 2 -on systems on which -.Xr minherit 2 -does not exist and that the memory regions should not be inherited to the -children process. Using this function allows to remove all mappings which -were obtained by the parent. -.It void * Fn secure_malloc "size_t size" -Permits to allocate secure memory for the current process, which will be wired -(locked in physical memory) using 4.4BSD-style -.Xr mlock 2 -so that it may not be written to disk or swapped out. This is mostly useful -to allocate memory areas which are to hold cryptographic information such as -states and keys. -.Pp -.Fa size -specifies the size of the memory area to allocate. -.Pp -Note that it is recommended to allocate fewer large blocks than many small ones -using this function. For this reason, it may be useful to use the -.Xr mmpool 3 -library with this system. At -.Xr fork 2 , -this memory will be inherited to children processes, but as exact independant -copies, rather than shared memory. It may be useful to call -.Fn secure_freeall -after -.Xr fork 2 -to free all secure memory which the new process should not access. -.It void Fn secure_free "void *mem" -Permits to free a memory area -.Fa mem -which was previously allocated using -.Fn secure_malloc . -The memory area is always zeroed prior to unmapping to protect the sensitive -data. -.It void * Fn secure_realloc "void *mem" "size_t size" -Behaves like ANSI-C -.Xr realloc 3 , -but made to use on secure buffers allocated by -.Fn secure_malloc . -.Pp -.Fa mem -specifies the memory area to resize, and -.Fa size -the new size which should become available. -.Pp -The returned pointer may point to a new location, in which case the previous -unaffected memory area will have been copied over. Note that the size of -the area will not be shrinked for performance considerations but that it may -be enlarged as necessary. However, it will always make sure to invalidate -the portion of the area that is expected to be freed by zeroing it, to -protect the critical data. -.Pp -Specifying NULL for -.Fa mem -will cause this function to behave like -.Fn secure_malloc . -Specifying 0 for -.Fa size -will cause it to operate like -.Fn secure_free . -.It void Fn secure_freeall "void" -Similarily to -.Fn shmem_freeall , -permits to free all memory which was allocated using -.Fn secure_malloc -for the current process. However, unlike for shared memory, it is useful to -note that after -.Xr fork 2 -the inherited secure memory actually consists of an independant exact copy, -not of a shared memory area with the parent. -.El -.Sh RETURN VALUES -.Bl -tag -width indent -offset indent -.It Xo -.Fn shlocks_init , -.Fn shlocks_reopen , -.Fn shlocks_destroy -.Xc -return 0 on success, or -1 on error (in which case errno is set). -.It Xo -.Fn shmem_getregion -.Xc -returns TRUE on success, or FALSE on error. -.It Xo -.Fn shmem_malloc , -.Fn secure_malloc -.Xc -return a pointer to the newly allocated memory area, or NULL if no resources -were available or if an invalid parameter was passed. -.It Xo -.Fn shmem_realloc , -.Fn secure_realloc -.Xc -return a pointer to the new allocated memory area if the area moved, or -a pointer to the previous memory area. On failure, returns NULL (unavailable -resources). -.It Xo -.Fn shmem_free , -.Fn secure_free , -.Fn shmem_freeall , -.Fn secure_freeall -.Xc -return nothing. -.El -.Sh ERRORS -The functions which can set errno can use the following codes: -.Bl -tag -width Er -.It Bq Er EINVAL -Invalid parameters were supplied to the function, or the supplied -filedescriptor array was not properly initialized. -.It Bq Er ENOMEM -A memory resource allocation error occurred. -.El -.Pp -Other codes may be set by internal functions such as -.Xr open 2 -and -.Xr mmap 2 . -.Sh SEE ALSO -.Xr malloc 3 , -.Xr free 3 , -.Xr realloc 3 , -.Xr mmap 2 , -.Xr munmap 2 , -.Xr mlock 2 , -.Xr minherit 2 , -.Xr madvise 2 , -.Xr flock 3 , -.Xr fork 2 , -.Xr chroot 2 , -.Xr setuid 2 , -.Xr mmpool 3 , -.Xr mmhash 3 . -.Pp -Alternatives: -.Xr fcntl 2 , -.Xr shm_open 3 , -.Xr msync 2 , -.Xr shmget 2 , -.Xr semget 2 , -.Xr semctl 2 . -.Sh STANDARDS -None, but these functions attempt to comply as much as possible to ANSI-C -semantics as for the API. -.Sh HISTORY -.Nm -was written at first for the closed source mmSNGId server project from -Matthew Mondor, but eventually was exported publically. -.Sh AUTHORS -.Nm -was written by Matthew Mondor and is -Copyright (c) 2003-2004, Matthew Mondor, All rights reserved. -.Sh BUGS -Please report any bug to mmondor@accela.net diff --git a/mmsoftware/mmlib/mmheap.c b/mmsoftware/mmlib/mmheap.c deleted file mode 100644 index 09b3031..0000000 --- a/mmsoftware/mmlib/mmheap.c +++ /dev/null @@ -1,603 +0,0 @@ -/* $Id: mmheap.c,v 1.22 2004/06/04 01:02:32 mmondor Exp $ */ - -/* - * Copyright (C) 2003-2004, Matthew Mondor - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Matthew Mondor. - * 4. The name of Matthew Mondor may not be used to endorse or promote - * products derived from this software without specific prior written - * permission. - * 5. Redistribution of source code may not be released under the terms of - * any GNU Public License derivate. - * - * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - - - -#include -#include -#ifdef __GLIBC__ -#include -#endif -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - - - -MMCOPYRIGHT("@(#) Copyright (c) 2003-2004\n\ -\tMatthew Mondor. All rights reserved.\n"); -MMRCSID("$Id: mmheap.c,v 1.22 2004/06/04 01:02:32 mmondor Exp $"); - - - -/* Some 4.2BSD-style shared locking and memory allocation primitives, using - * mmap(2) with MAP_ANON and flock(3) using temporary files for - * synchronization. msync(2) is not required to use with this mode. - * - * Other alternatives would be SVR4-style using mmap(2) with "/dev/zero" file - * for shared memory and fcntl(2) for locking/synchronization, POSIX.1-style - * using shm_open(3) shared memory, traditional mmap(2) using a temporary - * file on the filesystem with msync(2) requirement, and SysV shmget(2) and - * semget(2) which could leak resources at server shutdown, but would allow - * to more easily resize the shared memory for all processes, and to be - * reused if necessary by several independant invokations of this server. - */ - - - -/* This structure allows to track allocated memory for the freeall functions. - * It also allows better debugging, where it is easy to know when a block to - * be freed was actually allocated using the expected system or not. - */ -struct memhdr { - hashnode_t node; - void *addr; /* Key */ - size_t size; -}; - - - -static bool init(void); -static u_int32_t key_hash32(const void *, size_t); -static int key_cmp32(const void *, const void *, size_t); -static bool freeall_iterator(hashnode_t *, void *); - - - -static size_t pagesize = 0; -/* The tables are process-specific. This means that no locking is necessary - * to access them. The main gloal is to be able to free all shared and/or - * secure memory when wanted for the current process. This is useful to use - * after fork(2), notably. These tables are inherited as exact copies to the - * children processes, because we only use malloc(3) for them. - */ -static hashtable_t shmem_table, secure_table; - - - -/* Hash tables */ - -static bool init(void) -{ - if (pagesize == 0) { - if ((pagesize = (size_t)sysconf(_SC_PAGESIZE)) != -1) { - if (hashtable_init(&shmem_table, "shmem_table", - HT_DEFAULT_CAPACITY, HT_DEFAULT_FACTOR, - malloc, free, key_cmp32, key_hash32, TRUE)) { - if (hashtable_init(&secure_table, "secmem_table", - HT_DEFAULT_CAPACITY, HT_DEFAULT_FACTOR, - malloc, free, key_cmp32, key_hash32, TRUE)) - return TRUE; - hashtable_destroy(&shmem_table, FALSE); - } - } - } else - return TRUE; - - return FALSE; -} - -/* ARGSUSED */ -static u_int32_t key_hash32(const void *data, size_t len) -{ - return *((u_int32_t *)data); -} - -/* ARGSUSED */ -static int key_cmp32(const void *src, const void *dst, size_t len) -{ - return *((u_int32_t *)src) - *((u_int32_t *)dst); -} - -static bool freeall_iterator(hashnode_t *hnod, void *udata) -{ - void (*freefunc)(void *) = udata; - struct memhdr *hdr = (struct memhdr *)hnod; - - freefunc(hdr->addr); - - return TRUE; -} - - -/* Synchronization locks management */ - -int shlocks_init(const char *path, int *locks, int many, - uid_t uid, gid_t gid, mode_t mode) -{ - char *file = NULL; - uid_t my_uid = getuid(); - int i, ret; - - if (path == NULL || *path == '\0' || locks == NULL || many < 1) { - DEBUG_PRINTF("shlocks_init", "Invalid arguments"); - errno = EINVAL; - return -1; - } - - for (i = 0; i < many; i++) - locks[i] = -1; - - if ((file = malloc(mm_strlen(path) + 16))) { - for (i = 0; i < many; i++) { - (void) sprintf(file, "%s.%02d", path, i); - if ((locks[i] = open(file, O_CREAT | O_TRUNC | O_WRONLY, mode)) - == -1) { - DEBUG_PRINTF("shlocks_init", "open(%s) - %s", - file, strerror(errno)); - goto err; - } - if (uid == my_uid) - uid = -1; - if (uid != -1 || gid != -1) { - if (fchown(locks[i], uid, gid) == -1) { - DEBUG_PRINTF("shlocks_init", "chown(%s, %d, %d) - %s", - file, uid, gid, strerror(errno)); - goto err; - } - /* Since mode parameter may depend on umask */ - if (fchmod(locks[i], mode) == -1) { - DEBUG_PRINTF("shlocks_init", "chmod(%s, 0%03o) - %s", - file, mode, strerror(errno)); - goto err; - } - } - while ((ret = flock(locks[i], LOCK_EX)) == -1 && errno == EINTR) ; - if (ret == 0) - (void) flock(locks[i], LOCK_UN); - else { - DEBUG_PRINTF("shlocks_init", "flock(%s) - %s", - file, strerror(errno)); - goto err; - } - } - free(file); - } else { - errno = ENOMEM; - goto err; - } - - return 0; - -err: - if (file != NULL) - free(file); - for (i = 0; i < many; i++) { - if (locks[i] != -1) { - (void) close(locks[i]); - locks[i] = -1; - } - } - - return -1; -} - -int shlocks_reopen(const char *path, int *locks, int many) -{ - char *file = NULL; - int i, cnt; - - if (path == NULL || *path == '\0' || locks == NULL || many < 1) { - DEBUG_PRINTF("shlocks_reopen", "Invalid arguments"); - errno = EINVAL; - return -1; - } - - for (i = 0, cnt = 0; i < many; i++) { - if (locks[i] != -1) { - (void) close(locks[i]); - locks[i] = -1; - cnt++; - } - } - if (cnt != many) { - DEBUG_PRINTF("shlocks_reopen", "Supplied invalid locks array"); - errno = EINVAL; - return -1; - } - - if ((file = malloc(mm_strlen(path) + 16)) != NULL) { - for (i = 0; i < many; i++) { - (void) sprintf(file, "%s.%02d", path, i); - if ((locks[i] = open(file, O_RDONLY)) == -1) { - DEBUG_PRINTF("shlocks_reopen", "open(%s) - %s", - file, strerror(errno)); - goto err; - } - } - free(file); - } else { - errno = ENOMEM; - goto err; - } - - return 0; - -err: - if (file != NULL) - free(file); - for (i = 0; i < many; i++) { - if (locks[i] != -1) { - (void) close(locks[i]); - locks[i] = -1; - } - } - - return -1; -} - -int shlocks_destroy(const char *path, int *locks, int many, bool delete) -{ - char *file; - - if (path == NULL || *path == '\0' || locks == NULL || many < 1) { - DEBUG_PRINTF("shlocks_destroy", "Invalid arguments"); - errno = EINVAL; - return -1; - } - - if ((file = malloc(mm_strlen(path) + 16)) != NULL) { - int i; - - for (i = 0; i < many; i++) { - if (locks[i] != -1) { - (void) close(locks[i]); - /* funlink(2) would have been nice here... */ - if (delete) { - (void) sprintf(file, "%s.%02d", path, i); - (void) unlink(file); - } - locks[i] = -1; - } - } - free(file); - } else { - errno = ENOMEM; - return -1; - } - - return 0; -} - - -/* Memory management */ - -/* Function to allocate shared memory. Note that a pool which only requires - * rare allocations of larger blocks is recommended to use this system, - * (such as mmpool(3) rather than allocating many small blocks of memory - * at a high frequency. Moreover, if shmem_free() is called on the block - * it has to be similarily called for each process which shared this memory. - * Otherwise, only the current process would unmap the memory. When allocating - * shared memory it does not magically appear in all processes. It must rather - * be allocated before fork(2)ing children which will then inherit the shared - * memory areas. Of course, locking has to be used to ensure synchronization - * of the shared resources. flock(3) then comes in handy. This method using - * 4.2BSD-style mmap(2) with MAP_ANON does not require to use msync(2) to - * cause the shared memory to be updated for the other processes. - */ -void *shmem_malloc(size_t size) -{ - void *mem = NULL; - size_t rsize; - - if (!init()) - return NULL; - - rsize = BALIGN_CEIL(sizeof(struct memhdr) + size, pagesize); - if ((mem = mmap(NULL, rsize, PROT_READ | PROT_WRITE, - MAP_ANON | MAP_SHARED, -1, 0)) == MAP_FAILED) { - DEBUG_PRINTF("shmem_malloc", "mmap(%d)", (int)rsize); - mem = NULL; - } else { - struct memhdr *hdr, *rhdr; - - hdr = rhdr = mem; - mem = rhdr->addr = ++hdr; - rhdr->size = rsize; - if (!hashtable_link(&shmem_table, (hashnode_t *)rhdr, &rhdr->addr, - sizeof(void *), TRUE)) { - DEBUG_PRINTF("shmem_malloc", "hashtable_link()"); - munmap(rhdr, rsize); - mem = NULL; - } - } - - return mem; -} - - -/* Note: as this only unmaps the pages of the current process using munmap(2), - * should normally be performed in each process if needed. For normal operation - * in the current case, this function will never be called, because we use - * mmpool(3) pools and mmhash(3) tables in a mode which cannot shrink, and that - * allocated shared memory using this technique automatically gets freed at - * process termination. - */ -void shmem_free(void *mem) -{ - if (mem != NULL) { - struct memhdr *hdr; - - if ((hdr = (struct memhdr *)hashtable_lookup(&shmem_table, &mem, - sizeof(void *))) != NULL) { - hashtable_unlink(&shmem_table, (hashnode_t *)hdr); - if ((munmap(hdr, hdr->size)) == -1) - DEBUG_PRINTF("shmem_free", "munmap(%p, %d)", - hdr, (int)hdr->size); - } else - DEBUG_PRINTF("shmem_free", - "%p not allocated using shmem_malloc()!", - mem); - } -} - - -/* Permits to retreive the mapped area so that the caller may optionally - * use 4.4BSD madvise(2), minherit(2), etc on the memory block. - */ -bool shmem_getregion(void **raddr, size_t *rsize, void *mem) -{ - struct memhdr *hdr; - - if ((hdr = (struct memhdr *)hashtable_lookup(&shmem_table, &mem, - sizeof(void *))) != NULL) { - *raddr = hdr; - *rsize = hdr->size; - return TRUE; - } else - DEBUG_PRINTF("shmem_getregion", - "%p not allocated using shmem_malloc()!", mem); - return FALSE; -} - - -/* Note: Just like ANSI-C realloc(3), the returned pointer could point to - * another area, but the previous buffer contents will be copied into the - * new area if so. Note that this function, just like shmem_free() would need - * to be called by all processes for the remapping to work properly, and that - * they all should account for the new memory pointer if it changes. - */ -void *shmem_realloc(void *mem, size_t size) -{ - if (mem != NULL) { - if (size != 0) { - struct memhdr *hdr; - - if ((hdr = (struct memhdr *)hashtable_lookup(&shmem_table, &mem, - sizeof(void *))) != NULL) { - size_t rsize = hdr->size - sizeof(struct memhdr); - - if (size > rsize) { - void *nmem; - - if ((nmem = shmem_malloc(size)) != NULL) { - mm_memcpy(nmem, mem, rsize); - shmem_free(mem); - - return nmem; - } else - return NULL; - } else - return mem; - } else { - DEBUG_PRINTF("shmem_realloc", - "%p not allocated using shmem_malloc()!", mem); - return NULL; - } - } else { - shmem_free(mem); - return NULL; - } - } else - return shmem_malloc(size); -} - - -/* Permits to free all memory which has been allocated by shmem_alloc() in - * one single step. Useful to use after fork(2) for instance, without the need - * to use BSD minherit(2). - */ -void shmem_freeall(void) -{ - hashtable_iterate(&shmem_table, freeall_iterator, shmem_free); -} - - - -/* These malloc/free alternative functions ensure to lock down in physical - * memory the allocated regions using 4.4BSD-style mlock(2). This is especially - * useful when a memory area is required to hold sensitive information which - * should not leak into the swap device (should never be swapped out). Such - * memory is needed for storage of cryptographic keys, for instance. Note that - * this memory is not shared with children at fork(2), but that it would be - * possible for a child process to inherit an exact independant copy of these - * memory regions. Similarily to with shmem_malloc(), it is recommended to - * allocate few larger blocks than many smaller ones when using this function, - * and mmpool(3) may be useful for this when many small blocks of the same size - * are required. - */ -void *secure_malloc(size_t size) -{ - void *mem = NULL; - size_t rsize; - - if (!init()) - return NULL; - - rsize = BALIGN_CEIL(sizeof(struct memhdr) + size, pagesize); - if ((mem = mmap(NULL, rsize, PROT_READ | PROT_WRITE, MAP_ANON, -1, 0)) - == MAP_FAILED) { - DEBUG_PRINTF("secure_malloc", "mmap(%d)", (int)rsize); - mem = NULL; - } else { - /* We lock down the pages in physical memory so that cryptographic - * keys stored in memory should not be written to disk - * (swapped out). - */ - if (mlock(mem, rsize) != 0) { - /* This important step failed, return an error. */ - DEBUG_PRINTF("secure_malloc", "mlock(%p, %d)", mem, (int)rsize); - if (munmap(mem, rsize) == -1) - DEBUG_PRINTF("secure_malloc", - "munmap(%p, %d)", mem, (int)rsize); - mem = NULL; - } else { - struct memhdr *hdr, *rhdr; - - hdr = rhdr = mem; - mem = rhdr->addr = ++hdr; - rhdr->size = rsize; - if (!hashtable_link(&secure_table, (hashnode_t *)rhdr, &rhdr->addr, - sizeof(void *), TRUE)) { - DEBUG_PRINTF("secure_malloc", "hashtable_link()"); - munlock(rhdr, rsize); - munmap(rhdr, rsize); - mem = NULL; - } - } - } - - return mem; -} - - -/* This is the counterpart to secure_malloc() which allows to unlock and free - * a specified memory region. Note that we also ensure to zero the memory - * before unlocking it to protect any sensitive information which might have - * been stored by the region. Because this memory is only valid within the - * current process, it is safe to call secure_free() anytime just as if it - * was free(3), provided that it be used on regions allocated using the - * secure_malloc() function. - */ -void secure_free(void *mem) -{ - if (mem != NULL) { - struct memhdr *hdr; - - if ((hdr = (struct memhdr *)hashtable_lookup(&secure_table, &mem, - sizeof(void *))) != NULL) { - size_t rsize = hdr->size - sizeof(struct memhdr); - - hashtable_unlink(&secure_table, (hashnode_t *)hdr); - /* Make sure that memory area is zeroed before it be unlocked from - * physical memory. - */ - mm_memclr(mem, rsize); - if ((munlock(hdr, hdr->size)) != 0) - DEBUG_PRINTF("secure_free", - "munlock(%p, %d)", hdr, (int)hdr->size); - if ((munmap(hdr, hdr->size)) == -1) - DEBUG_PRINTF("secure_free", - "munmap(%p, %d)", hdr, (int)hdr->size); - } else - DEBUG_PRINTF("secure_free", - "%p not allocated using secure_malloc()!", mem); - } -} - - -/* Very similarily to shmem_realloc(), the mapped memory area is never - * shrinked calling this function. However, special care is taken to ensure - * to invalidate the extraneous data regions which are supposed to be freed. - */ -void *secure_realloc(void *mem, size_t size) -{ - if (mem != NULL) { - if (size != 0) { - struct memhdr *hdr; - - if ((hdr = (struct memhdr *)hashtable_lookup(&secure_table, &mem, - sizeof(void *))) != NULL) { - size_t rsize = hdr->size - sizeof(struct memhdr); - - if (size > rsize) { - void *nmem; - - if ((nmem = secure_malloc(size)) != NULL) { - mm_memcpy(nmem, mem, rsize); - secure_free(mem); - - return nmem; - } else - return NULL; - } else { - /* We won't remap just to reduce the buffer, but we make - * sure to invalidate any data which may have been critical - * in the supposedly freed region. - */ - if (size < rsize) - mm_memclr(mem + size, rsize - size); - return mem; - } - } else { - DEBUG_PRINTF("secure_realloc", - "%p not allocated using secure_malloc()!", mem); - return NULL; - } - } else { - secure_free(mem); - return NULL; - } - } else - return secure_malloc(size); -} - - -/* Permits to free all memory which has been allocated by secure_alloc() in - * one single step. Useful to use after fork(2) for instance, without the need - * to use BSD minherit(2). - */ -void secure_freeall(void) -{ - hashtable_iterate(&secure_table, freeall_iterator, shmem_free); -} diff --git a/mmsoftware/mmlib/mmheap.h b/mmsoftware/mmlib/mmheap.h deleted file mode 100644 index 233fd16..0000000 --- a/mmsoftware/mmlib/mmheap.h +++ /dev/null @@ -1,67 +0,0 @@ -/* $Id: mmheap.h,v 1.9 2004/06/01 19:11:57 mmondor Exp $ */ - -/* - * Copyright (C) 2003-2004, Matthew Mondor - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Matthew Mondor. - * 4. The name of Matthew Mondor may not be used to endorse or promote - * products derived from this software without specific prior written - * permission. - * 5. Redistribution of source code may not be released under the terms of - * any GNU Public License derivate. - * - * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef MM_HEAP_H -#define MM_HEAP_H - - - -#include -#include -#include -#include - -#include - - - -extern int shlocks_init(const char *, int *, int, uid_t, gid_t, mode_t); -extern int shlocks_reopen(const char *, int *, int); -extern int shlocks_destroy(const char *, int *, int, bool); - -extern void * shmem_malloc(size_t); -extern void shmem_free(void *); -extern bool shmem_getregion(void **, size_t *, void *); -extern void * shmem_realloc(void *, size_t); -extern void shmem_freeall(void); - -extern void * secure_malloc(size_t); -extern void secure_free(void *); -extern void * secure_realloc(void *, size_t); -extern void secure_freeall(void); - - - -#endif diff --git a/mmsoftware/mmlib/mmlifo.3 b/mmsoftware/mmlib/mmlifo.3 deleted file mode 100644 index 12613a4..0000000 --- a/mmsoftware/mmlib/mmlifo.3 +++ /dev/null @@ -1,218 +0,0 @@ -.\" $Id: mmlifo.3,v 1.6 2004/05/05 23:59:56 mmondor Exp $ -.\" -.\" Copyright (C) 2000-2004, Matthew Mondor -.\" All rights reserved. -.\" -.\" Redistribution and use in source and binary forms, with or without -.\" modification, are permitted provided that the following conditions -.\" are met: -.\" 1. Redistributions of source code must retain the above copyright -.\" notice, this list of conditions and the following disclaimer. -.\" 2. Redistributions in binary form must reproduce the above copyright -.\" notice, this list of conditions and the following disclaimer in the -.\" documentation and/or other materials provided with the distribution. -.\" 3. All advertising materials mentioning features or use of this software -.\" must display the following acknowledgement: -.\" This product includes software developed by Matthew Mondor. -.\" 4. The name of Matthew Mondor may not be used to endorse or promote -.\" products derived from this software without specific prior written -.\" permission. -.\" 5. Redistribution of source code may not be released under the terms of -.\" any GNU Public License derivate. -.\" -.\" THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR -.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES -.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. -.\" IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, -.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -.\" BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF -.\" USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF -.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -.\" -.Dd Feb 12, 2003 -.Os mmsoftware -.Dt MMLIFO 3 -.Sh NAME -.Nm mmlifo -.Nd General purpose portable LIFO buffers library. -.Sh SYNOPSIS -.Fd #include -.Fd #include -.Fd #include -.Ft void -.Fn LIFO_DEFINE "lifotype_t" "objecttype" -.Ft void -.Fn LIFO_INIT "lifotype_t *lifo" "void *buffer" "u_int32_t size" -.Ft bool -.Fn LIFO_FULL "lifotype_t *lifo" -.Ft bool -.Fn LIFO_EMPTY "lifotype_t *lifo" -.Ft u_int32_t -.Fn LIFO_STAT "lifotype_t *lifo" -.Ft u_int32_t -.Fn LIFO_AVAIL "lifotype_t *lifo" -.Ft void -.Fn LIFO_FLUSH "lifotype_t *lifo" -.Ft void -.Fn LIFO_PUT "lifotype_t *lifo" "objecttype *element" -.Ft void -.Fn LIFO_GET "lifotype_t *lifo" "objecttype *element" -.Ft void -.Fn LIFO_FIND "lifotype_t *lifo" "objecttype **pointer" "objecttype *element" -.Ft void -.Fn LIFO_ALLOC "lifotype_t *lifo" "objecttype **pointer" "u_int32_t *actual" \ -"u_int32_t size" -.Ft void -.Fn LIFO_FREE "lifotype_t *lifo" "objecttype **pointer" "u_int32_t *actual" \ -"u_int32_t size" -.Sh DESCRIPTION -LIFO buffers (Last In First Out) are usually useful to implement stacks -when the processor stack is not desired to use, for temporary storage, or to -save the state of a few objects. It also is useful in the implementation of -virtual frames and local state variables. These are not using linked lists but -a fixed memory buffer, with entries corresponding to the stack type. The -library is entirely implemented as macros, which also allows to create new -user LIFO buffer types to hold user supplied objects. -.Ss FUNCTIONS -.Bl -tag -width indent -offset indent -.It void Fn LIFO_DEFINE "lifotype_t" "objecttype" -permits to create a new type of LIFO buffer to hold custom objects. This -in fact only is a macro to create a new lifo*_t structure and typedef it. -.Fa lifotype_t -defines the new LIFO buffer type which should be created. -.Fa objecttype -specifies the type of object that this lifo structure should allow to buffer. -For instance, to create a new lifo type to hold file descriptors, one could -use: -.Bd -literal -offset indent -LIFO_DEFINE(fdlifo_t, int); -.Ed -.Pp -It then would be possible to use the -.Fa fdlifo_t -type, initialize it, and use it. -.Bd -literal -offset indent -fdlifo_t f; -int buf[1024]; -int fd; - -LIFO_INIT(&f, buf, 1024); -LIFO_PUT(&f, &fd); -LIFO_GET(&f, &fd); -.Ed -.Pp -Predefined LIFO buffer types are -.Nm lifo8_t , -.Nm lifo16_t, -.Nm lifo32_t -and -.Nm lifo64_t , -to hold objects of type -.Nm u_int8_t , -.Nm u_int16_t , -.Nm u_int32_t -and -.Nm u_int64_t , -respectively. -.It void Fn LIFO_INIT "lifotype_t *lifo" "void *buffer" "u_int32_t size" -allows to initialize a LIFO buffer. -.Fa lifo -consists of a pointer to the LIFO buffer to initialize, -.Fa buffer -is the user supplied buffer which should be able to hold the wanted number -of elements. -.Fa size -specifies the maximum number of elements that the buffer can hold. This is -not expressed in bytes, but in number of elements. -As a general rule, the buffer should hold sizeof(object) * (max + 1) bytes. -When a buffer is done with, it is the responsibility of the user application -to free the supplied -.Fa buffer . -.It bool Fn LIFO_FULL "lifotype_t *lifo" -is a useful macro to determine if a LIFO buffer is full and cannot take -new elements. -.Fa lifo -is a pointer to the LIFO buffer. When full, -.Fn LIFO_PUT -will have no effect; -.Fn LIFO_GET -or -.Fn LIFO_FLUSH -should be used first. -.It bool Fn LIFO_EMPTY "lifotype_t *lifo" -returns true if the supplied -.Ft lifo -buffer currently holds no elements. -.It u_int32_t Fn LIFO_STAT "lifotype_t *lifo" -permits to know how many elements are currently buffered into the specified -.Fa lifo -buffer, and which can be read using -.Fn LIFO_GET . -.It u_int32_t Fn LIFO_AVAIL "lifotype_t *lifo" -tells the number of additional entries which the supplied buffer can hold. -.It void Fn LIFO_FLUSH "lifotype_t *lifo" -allows to get rid of all the current contents, if any, of the specified -.Fa lifo -buffer. The buffer as a result becomes empty instantly. -.It void Fn LIFO_PUT "lifotype_t *lifo" "objecttype *element" -inserts the element to which supplied -.Fa element -points, into the supplied LIFO buffer -.Fa lifo . -This can transparently fail if the buffer is full, if it is uncertain that -room exists in the buffer, -.Fn LIFO_FULL -should be used first, unless element loss is desired. -.It void Fn LIFO_GET "lifotype_t *lifo" "objecttype *element" -attempts to read and obtain an element from the supplied LIFO buffer -.Fa lifo , -and place it into -.Fa element -supplied pointer. This can transparently fail; If it is unsure that the -LIFO buffer holds any data, -.Fn LIFO_EMPTY -should be used first. The obtained element, if it could be read, always -consists of the most recent element that was inserted using -.Fn LIFO_PUT , -that is, the Last element In is the First element Out. -.El -.Sh RETURN VALUES -.Bl -tag -width indent -offset indent -.It Xo -.Fn LIFO_DEFINE , -.Fn LIFO_INIT , -.Fn LIFO_FLUSH , -.Fn LIFO_GET , -.Fn LIFO_PUT -.Xc -return nothing. -.It Xo -.Fn LIFO_FULL , -.Fn LIFO_EMPTY -.Xc -return TRUE or FALSE. -.It Xo -.Fn LIFO_STAT , -.Fn LIFO_AVAIL -.Xc -return a number of elements. -.El -.Sh SEE ALSO -.Xr malloc 3 , -.Xr free 3 , -.Xr memcpy 3 -.Sh STANDARDS -None -.Sh HISTORY -.Nm -was originally developped for the portable Xisop kernel project -project from Matthew Mondor. -.Sh AUTHORS -The -.Nm -library was written by Matthew Mondor and is -Copyright (c) 2000-2004 Matthew Mondor, All rights reserved. -.Sh BUGS -Please report any bug to mmondor@accela.net diff --git a/mmsoftware/mmlib/mmlifo.h b/mmsoftware/mmlib/mmlifo.h deleted file mode 100644 index 2096708..0000000 --- a/mmsoftware/mmlib/mmlifo.h +++ /dev/null @@ -1,107 +0,0 @@ -/* $Id: mmlifo.h,v 1.5 2004/06/04 19:13:24 mmondor Exp $ */ - -/* - * Copyright (C) 2000-2004, Matthew Mondor - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Matthew Mondor. - * 4. The name of Matthew Mondor may not be used to endorse or promote - * products derived from this software without specific prior written - * permission. - * 5. Redistribution of source code may not be released under the terms of - * any GNU Public License derivate. - * - * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - - - -#ifndef MMLIFO_H -#define MMLIFO_H - - - -#include - - - -/* LIFO_DEFINE(lifotypename, objecttype); */ -#define LIFO_DEFINE(n, o) typedef struct n { \ - u_int32_t size, elements; \ - o *buffer, *endbuffer, *head; \ -} n - -/* Because of the way this is implemented using macros, it would also be - * possible to provide lifo types to hold structures. - */ -LIFO_DEFINE(lifo8_t, u_int8_t); -LIFO_DEFINE(lifo16_t, u_int16_t); -LIFO_DEFINE(lifo32_t, u_int32_t); -LIFO_DEFINE(lifo64_t, u_int64_t); - - - -/* XXX Although it's great to use macros for these operations, it also - * prevents assembly functions to be provided to replace them. - */ -/* void LIFO_INIT(lifo*_t *, u_int*_t *, u_int32_t); */ -#define LIFO_INIT(f, b, s) do { \ - (f)->size = (s); \ - (f)->elements = 0; \ - (f)->buffer = (f)->endbuffer = (f)->head = (b); \ -} while (/* CONSTCOND */0) - -/* bool LIFO_FULL(lifo*_t *); */ -#define LIFO_FULL(f) ((f)->elements == (f)->size) - -/* u_int32_t LIFO_STAT(lifo*_t *); */ -#define LIFO_STAT(f) ((f)->elements) - -/* void LIFO_FLUSH(lifo*_t *); */ -#define LIFO_FLUSH(f) do { \ - (f)->head = (f)->buffer; \ - (f)->elements = 0; \ -} while (/* CONSTCOND */0) - -/* void LIFO_PUT(lifo*_t *, u_int*_t *); */ -#define LIFO_PUT(s, e) do { \ - if ((s)->elements < (s)->size) { \ - *((s)->head++) = *(e); \ - (s)->elements++; \ - } \ -} while (/* CONSTCOND */0) - -/* void LIFO_GET(lifo*_t *, u_int*_t *); */ -#define LIFO_GET(s, e) do { \ - if ((s)->elements > 0) { \ - *(e) = *(--(s)->head); \ - (s)->elements--; \ - } \ -} while (/* CONSTCOND */0) - -/* LIFO_ALLOC(lifo*_t *, u_int*_t **, size_t *); */ - -/* LIFO_FREE(lifo*_t, u_int*_t **, size_t *); */ - - - -#endif diff --git a/mmsoftware/mmlib/mmlimitrate.3 b/mmsoftware/mmlib/mmlimitrate.3 deleted file mode 100644 index 2207b2a..0000000 --- a/mmsoftware/mmlib/mmlimitrate.3 +++ /dev/null @@ -1,179 +0,0 @@ -.\" $Id: mmlimitrate.3,v 1.4 2004/05/05 23:59:56 mmondor Exp $ -.\" -.\" Copyright (C) 2001-2004, Matthew Mondor -.\" All rights reserved. -.\" -.\" Redistribution and use in source and binary forms, with or without -.\" modification, are permitted provided that the following conditions -.\" are met: -.\" 1. Redistributions of source code must retain the above copyright -.\" notice, this list of conditions and the following disclaimer. -.\" 2. Redistributions in binary form must reproduce the above copyright -.\" notice, this list of conditions and the following disclaimer in the -.\" documentation and/or other materials provided with the distribution. -.\" 3. All advertising materials mentioning features or use of this software -.\" must display the following acknowledgement: -.\" This product includes software developed by Matthew Mondor. -.\" 4. The name of Matthew Mondor may not be used to endorse or promote -.\" products derived from this software without specific prior written -.\" permission. -.\" 5. Redistribution of source code may not be released under the terms of -.\" any GNU Public License derivate. -.\" -.\" THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR -.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES -.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. -.\" IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, -.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -.\" BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF -.\" USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF -.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -.\" -.Dd October 19, 2003 -.Dt MMLIMITRATE 3 -.Os mmsoftware -.Sh NAME -.Nm mmlimitrate -.Nd Useful library for rate limiting in various situations -.Sh SYNOPSIS -.Fd #include -.Fd #include -.Fd #include -.Ft void -.Fn LR_INIT "struct limitrate *lr" "long max" "time_t secs" "time_t now" -.Ft void -.Fn LR_EXPIRE "struct limitrate *lr" "time_t now" -.Ft time_t -.Fn LR_REMAINS "struct limitrate *lr" "time_t now" -.Ft long -.Fn LR_POSTS "struct limitrate *lr" -.Ft long -.Fn LR_MAXPOSTS "struct limitrate *lr" -.Ft time_t -.Fn LR_PERIOD "struct limitrate *lr" -.Ft bool -.Fn lr_allow "struct limitrate *lr" "long many" "time_t now" "bool expire" -.Sh DESCRIPTION -.Nm -was written to get rid of code duplication which started to occur in the -various mmsoftware daemons which usually need various forms of rate limiting. -Most of the system was implemented as macros for speed. -.Ss FUNCTIONS -.Bl -tag -width indent -offset indent -.It void Fn LR_INIT "struct limitrate *lr" "long max" "time_t secs"\ - "time_t now" -Permits to initialize the -.Fa lr -element of type -.Dv struct limitrate , -to allow a maximum of -.Fa max -posts per period of -.Fa secs -seconds. -The -.Fn lr_allow -function will then be used for posting. -.Fa now -provides the current time as obtained from the -.Xr time 3 -function. -.It void Fn LR_EXPIRE "struct limitrate *lr" "time_t now" -Verifies if the period for the specified -.Fa lr -expired, and if so, resets it's number of posts to zero and restarts the -period. -.Fa now -provides the current time as obtained from -.Xr time 3 . -.It void Fn LR_REMAINS "struct limitrate *lr" "time_t now" -Returns the remaining number of seconds for the period of -.Fa lr -to expire, or 0 if expiration already occurred. -.Fa now -provides the current time as returned by -.Xr time 3 . -.It long Fn LR_POSTS "struct limitrate *lr" -Returns the current number of posts which occurred on -.Fa lr -within the current period (which may or may not have expired, but was not -reset yet). Extraneous posts which were still refused by -.Fn lr_allow -are included. -.It long Fn LR_MAXPOSTS "struct limitrate *lr" -Returns the maximum allowed number of posts which can occur within a second -for the specified -.Fa lr , -as initialized in -.Fn LR_INIT . -.It time_t Fn LR_PERIOD "struct limitrate *lr" -Returns the length of a period for -.Fa lr -in seconds, as initialized in -.Fn LR_INIT . -.It bool Fn lr_allow "struct limitrate *lr" "long many" "time_t now"\ - "bool expire" -This function registers the specified number of posts -.Fa ( many ) -in the specified -.Fa lr -within the current period for that structure. -TRUE is returned on success (the posts were allowed), or FALSE if the maximum -allowed number of posts within a period is exceeded. Note that unless this -period is specifically reset, subsequent requests will also be refused with -FALSE in this case. -Note that the -.Fa now -parameter is ignored if -.Fa expire -is FALSE. Otherwise, -.Fa now -specifies the current time as returned by -.Xr time 3 . -.Fa expire -tells -.Fn lr_allow -to automatically reset the entry if it's period expired, that is, calling -.Fn LR_EXPIRE -internally. -.El -.Sh RETURN VALUES -.Bl -tag -width indent -offset indent -.It Xo -.Fn LR_INIT , -.Fn LR_EXPIRE -.Xc -return nothing. -.It Xo -.Fn LR_REMAINS , -.Fn LR_PERIOD -.Xc -return a number of seconds in the form of a -.Dv time_t . -.It Xo -.Fn LR_POSTS , -.Fn LR_MAXPOSTS -.Xc -return a positive integer or 0. -.It Fn lr_allow -returns a boolean -.Dv TRUE -or -.Dv FALSE . -.El -.Sh SEE ALSO -.Xr time 3 -.Sh STANDARDS -None -.Sh HISTORY -.Xr mmlimitrate 3 -was written for the mmsoftware project in October 2003 -.Sh AUTHORS -The -.Nm -library was written by Matthew Mondor and is Copyright (c) 2001-2004, -Matthew Mondor, All rights reserved. -.Sh BUGS -Please report any bug to mmondor@accela.net diff --git a/mmsoftware/mmlib/mmlimitrate.c b/mmsoftware/mmlib/mmlimitrate.c deleted file mode 100644 index b483321..0000000 --- a/mmsoftware/mmlib/mmlimitrate.c +++ /dev/null @@ -1,73 +0,0 @@ -/* $Id: mmlimitrate.c,v 1.4 2004/03/22 06:59:35 mmondor Exp $ */ - -/* - * Copyright (C) 2001-2004, Matthew Mondor - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Matthew Mondor. - * 4. The name of Matthew Mondor may not be used to endorse or promote - * products derived from this software without specific prior written - * permission. - * 5. Redistribution of source code may not be released under the terms of - * any GNU Public License derivate. - * - * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - - - -#include -#include - -#include -#include - - - -MMCOPYRIGHT("@(#) Copyright (c) 2001-2004\n\ -\tMatthew Mondor. All rights reserved.\n"); -MMRCSID("$Id: mmlimitrate.c,v 1.4 2004/03/22 06:59:35 mmondor Exp $"); - - - -/* Informs that new posts should happen for the specified . If - * is 0, the current time will be used. Otherwise, it can be used to specify - * the known current time as an optimization. If is TRUE, automatic - * expiration will be verified for the period and number of posts reset. - * Otherwise, the lr_expire() function should be used. - * Returns TRUE if those posts should be allowed, or FALSE to reject them, - * in which case it is considered excessive flood. - */ -bool lr_allow(struct limitrate *f, long many, time_t now, bool exp) -{ - if (exp) { - if (now == 0) - now = time(NULL); - LR_EXPIRE(f, now); - } - - f->posts += many; - if (f->posts > f->max) - return FALSE; - - return TRUE; -} diff --git a/mmsoftware/mmlib/mmlimitrate.h b/mmsoftware/mmlib/mmlimitrate.h deleted file mode 100644 index 274737b..0000000 --- a/mmsoftware/mmlib/mmlimitrate.h +++ /dev/null @@ -1,101 +0,0 @@ -/* $Id: mmlimitrate.h,v 1.7 2004/06/04 19:20:32 mmondor Exp $ */ - -/* - * Copyright (C) 2001-2004, Matthew Mondor - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Matthew Mondor. - * 4. The name of Matthew Mondor may not be used to endorse or promote - * products derived from this software without specific prior written - * permission. - * 5. Redistribution of source code may not be released under the terms of - * any GNU Public License derivate. - * - * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - - - -#ifndef MMLIMITRATE_H -#define MMLIMITRATE_H - - - -#include - -#include - - - -/* Generic system to prevent flood */ -struct limitrate { - time_t expires, seconds; - long posts, max; -}; - - - -/* Permits to initialize a limitrate structure */ -#define LR_INIT(f, rmax, secs, now) do { \ - (f)->posts = 0; \ - (f)->max = (rmax); \ - (f)->seconds = (secs); \ - (f)->expires = (now) + (secs); \ -} while (/* CONSTCOND */0); - -/* If the period elapsed for the specified limitrate structure, reset it */ -#define LR_EXPIRE(f, now) do { \ - if ((f)->expires < (now)) { \ - (f)->expires = (now) + (f)->seconds; \ - (f)->posts = 0; \ - } \ -} while (/* CONSTCOND */0); - -/* Utility macro */ -#define LR_LARGER(a, b) ((a) > (b) ? (a) : (b)) - -/* If the period did not yet elapse for the specified limitrate structure, - * returns the number of seconds remaining before it elapses, or 0 otherwise. - */ -#define LR_REMAINS(f, now) (LR_LARGER((f)->expires - (now), 0)) - -/* Returns the current number of posts which occurred for the specified - * limitrate structure. This value is reset to 0 when the structure is - * expired using LR_EXPIRE(). - */ -#define LR_POSTS(f) ((f)->posts) - -/* Returns the maximum allowed number of posts which can occur within - * the period for the specified limitrate structure. - */ -#define LR_MAXPOSTS(f) ((f)->max) - -/* Returns the time period for the specified limitrate structure, in seconds */ -#define LR_PERIOD(f) ((f)->seconds) - - - -extern bool lr_allow(struct limitrate *, long, time_t, bool); - - - -#endif diff --git a/mmsoftware/mmlib/mmlist.3 b/mmsoftware/mmlib/mmlist.3 deleted file mode 100644 index eb0073f..0000000 --- a/mmsoftware/mmlib/mmlist.3 +++ /dev/null @@ -1,205 +0,0 @@ -.\" $Id: mmlist.3,v 1.12 2004/05/05 23:59:56 mmondor Exp $ -.\" -.\" Copyright (C) 2001-2004, Matthew Mondor -.\" All rights reserved. -.\" -.\" Redistribution and use in source and binary forms, with or without -.\" modification, are permitted provided that the following conditions -.\" are met: -.\" 1. Redistributions of source code must retain the above copyright -.\" notice, this list of conditions and the following disclaimer. -.\" 2. Redistributions in binary form must reproduce the above copyright -.\" notice, this list of conditions and the following disclaimer in the -.\" documentation and/or other materials provided with the distribution. -.\" 3. All advertising materials mentioning features or use of this software -.\" must display the following acknowledgement: -.\" This product includes software developed by Matthew Mondor. -.\" 4. The name of Matthew Mondor may not be used to endorse or promote -.\" products derived from this software without specific prior written -.\" permission. -.\" 5. Redistribution of source code may not be released under the terms of -.\" any GNU Public License derivate. -.\" -.\" THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR -.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES -.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. -.\" IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, -.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -.\" BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF -.\" USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF -.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -.\" -.Dd October 8, 2003 -.Os mmsoftware -.Dt MMLIST 3 -.Sh NAME -.Nm mmlist -.Nd General purpose doubly linked list library of macros. -.Sh SYNOPSIS -.Fd #include -.Fd #include -.Fd #include -.Ft void -.Fn DLIST_INIT "list_t *list" -.Ft void -.Fn DLIST_APPEND "list_t *list" "node_t *node" -.Ft void -.Fn DLIST_INSERT "list_t *list" "node_t *node" -.Ft void -.Fn DLIST_INSERTAT "list_t *list" "node_t *atnode" "node_t *node" -.Ft void -.Fn DLIST_SWAP "list_t *dest" "list_t *source" "node_t *node" "bool insert" -.Ft void -.Fn DLIST_UNLINK "list_t *list" "node_t *node" -.Ft u_int32_t -.Fn DLIST_NODES "list_t *list" -.Ft node_t * -.Fn DLIST_TOP "list_t *list" -.Ft node_t * -.Fn DLIST_BOTTOM "list_t *list" -.Ft node_t * -.Fn DLIST_NEXT "node_t *node" -.Ft node_t * -.Fn DLIST_PREV "node_t *node" -.Ft void -.Fn DLIST_FOREACH "list_t *list" "node_t *iterator" -.Sh DESCRIPTION -This library provides a useful set of functions to manipulate doubly linked -lists. -.Ss HEADER FILE AND DATA STRUCTURES -The following structures and fields of interest which are left for the user -to reference are described below (only fields of user interest are shown), -declared in -.Aq Pa mmlist.h -.Bd -literal -offset indent -typedef struct node node_t; -typedef struct list list_t; - -struct node { - node_t *prev, *next; -}; - -struct list { - node_t *top, *bottom; - u_int32_t nodes; -}; -.Ed -.Pp -A custom node has to be declared as a structure using the node structure as -it's first field, as in the following: -.Bd -literal -offset indent -struct mynode { - node_t node; - /* custom fields... */ -}; -.Ed -.Ss FUNCTIONS -.Bl -tag -width indent -offset indent -.It void Fn DLIST_INIT "list_t *list" -initializes a -.Nm list -object in such a way that it can be used to store nodes. This should be called -before starting to append nodes into it. One may alternatively initialize the -object explicitely at creation time by assigning -.Sy DLIST_INITIALIZER -to it, as in the following example: -.Bd -literal -offset indent -list_t list = DLIST_INITIALIZER; -.Ed -.It void Fn DLIST_APPEND "list_t *list" "node_t *node" -links node pointed to by -.Fa node -after all elements in list specified by -.Fa list -.It void Fn DLIST_INSERT "list_t *list" "node_t *node" -links node -.Fa node -before all elements of list -.Fa list -.It void Fn DLIST_INSERTAT "list_t *list" "node_t *atnode" "node_t *node" -links node -.Fa node -before node -.Fa atnode -into -.Fa list -.It void Fn DLIST_SWAP "list_t *dest" "list_t *source" "node_t *node" \ -"bool insert" -unlinks node -.Fa node -from -.Fa source , -and appends or inserts it into list -.Fa dest -depending on weither -.Fa insert -is TRUE or FALSE. -.It void Fn DLIST_UNLINK "list_t *list" "node_t *node" -unlinks node -.Fa node -from -.Fa list . -.It u_int32_t Fn DLIST_NODES "list_t *list" -returns the number of currently linked nodes into the -.Fa list -list_t. -.It node_t * Fn DLIST_TOP "list_t *list" -returns the pointer to the first linked node of -.Fa list , -or NULL if there are none. -.It node_t * Fn DLIST_BOTTOM "list_t *list" -returns the pointer to the last linked node of -.Fa list , -or NULL if there are none. -.It node_t * Fn DLIST_NEXT "node_t *node" -returns the pointer to the next linked node after -.Fa node , -or NULL if -.Fa node -consists of the last node in that -.Fa list . -.It node_t * Fn DLIST_PREV "node_t *node" -returns the pointer to the previous linked node before -.Fa node , -or NULL if -.Fa node -consists of the top node in that -.Fa list . -.It Fn DLIST_FOREACH "list_t *list" "node_t *iterator" -macro consists of a useful clean shortcut to using a -.Fn for -to run through all nodes of -.Fa list , -from top to bottom. -.Fa iterator -specifies the variable (node_t *) which should be pointed to each node within -the following { and } braces pair. Example: -.Bd -literal -offset indent -list_t *list; -node_t *node; - -DLIST_FOREACH(list, node) { - /* - * node now points to a different node each time - * this is executed, and the loop exits when the - * full list has been processed. - */ -} -.Ed -.El -.Sh SEE ALSO -.Xr mmpool 3 , -.Xr mmhash 3 . -.Sh STANDARDS -None -.Sh HISTORY -mmlist was originally developped on NetBSD 1.5 for the Xisop portable kernel -project from Matthew Mondor, then imported to be used by mmserver, mmftpd and -mmmail daemons. -.Sh AUTHORS -The mmlist library was written by Matthew Mondor and is -Copyright (c) 2001-2004, Matthew Mondor, All rights reserved. -.Sh BUGS -Please report any bug to mmondor@accela.net diff --git a/mmsoftware/mmlib/mmlist.h b/mmsoftware/mmlib/mmlist.h deleted file mode 100644 index ce464fd..0000000 --- a/mmsoftware/mmlib/mmlist.h +++ /dev/null @@ -1,179 +0,0 @@ -/* $Id: mmlist.h,v 1.11 2004/06/04 19:13:24 mmondor Exp $ */ - -/* - * Copyright (C) 2001-2004, Matthew Mondor - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Matthew Mondor. - * 4. The name of Matthew Mondor may not be used to endorse or promote - * products derived from this software without specific prior written - * permission. - * 5. Redistribution of source code may not be released under the terms of - * any GNU Public License derivate. - * - * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - - - -#ifndef MM_LIST_H -#define MM_LIST_H - - - -#include -#include - - - -typedef struct list list_t; -typedef struct node node_t; - - - -struct node { - node_t *prev, *next; -}; - -struct list { - node_t *top, *bottom; - u_int32_t nodes; -}; - - - -/* Some macros to optimize operations on doubly linked lists */ -#define DLIST_INITIALIZER {NULL, NULL, 0} - -#define DLIST_INIT(lst) do { \ - (lst)->top = (lst)->bottom = NULL; \ - (lst)->nodes = 0; \ -} while (/* CONSTCOND */0) - -#define DLIST_UNLINK(lst, nod) do { \ - register node_t *prev = (nod)->prev, *next = (nod)->next; \ - \ - if (prev != NULL) \ - prev->next = next; \ - else \ - (lst)->top = next; \ - if (next != NULL) \ - next->prev = prev; \ - else \ - (lst)->bottom = prev; \ - (lst)->nodes--; \ -} while (/* CONSTCOND */0) - -#define DLIST_APPEND(lst, nod) do { \ - register node_t *tmp = (lst)->bottom; \ - \ - if (tmp != NULL) { \ - tmp->next = (nod); \ - (nod)->prev = tmp; \ - (nod)->next = NULL; \ - (lst)->bottom = (nod); \ - } else { \ - (lst)->bottom = (lst)->top = (nod); \ - (nod)->next = (nod)->prev = NULL; \ - } \ - (lst)->nodes++; \ -} while (/* CONSTCOND */0) - -#define DLIST_INSERT(lst, nod) do { \ - register node_t *tmp = (lst)->top; \ - \ - if (tmp != NULL) { \ - tmp->prev = (nod); \ - (nod)->prev = NULL; \ - (nod)->next = tmp; \ - (lst)->top = (nod); \ - } else { \ - (lst)->top = (lst)->bottom = (nod); \ - (nod)->next = (nod)->prev = NULL; \ - } \ - (lst)->nodes++; \ -} while (/* CONSTCOND */0) - -#define DLIST_INSERTAT(lst, atnode, nod) do { \ - register node_t *prev = (atnode)->prev, *next = (atnode); \ - \ - (nod)->next = next; \ - next->prev = (nod); \ - if (prev != NULL) { \ - prev->next = (nod); \ - (nod)->prev = prev; \ - } else { \ - (lst)->top = (nod); \ - (nod)->prev = NULL; \ - } \ - (lst)->nodes++; \ -} while (/* CONSTCOND */0) - -#define DLIST_SWAP(dst, src, nod, ins) do { \ - register node_t *prev = (nod)->prev, *next = (nod)->next; \ - \ - if (prev != NULL) \ - prev->next = next; \ - else \ - (src)->top = next; \ - if (next != NULL) \ - next->prev = prev; \ - else \ - (src)->bottom = prev; \ - (src)->nodes--; \ - if ((ins)) { \ - if ((prev = (dst)->top) != NULL) { \ - prev->prev = (nod); \ - (nod)->prev = NULL; \ - (nod)->next = prev; \ - (dst)->top = (nod); \ - } else { \ - (dst)->top = (dst)->bottom = (nod); \ - (nod)->next = (nod)->prev = NULL; \ - } \ - } else { \ - if ((prev = (dst)->bottom) != NULL) { \ - prev->next = (nod); \ - (nod)->prev = prev; \ - (nod)->next = NULL; \ - (dst)->bottom = (nod); \ - } else { \ - (dst)->bottom = (dst)->top = (nod); \ - (nod)->next = (nod)->prev = NULL; \ - } \ - } \ - (dst)->nodes++; \ -} while (/* CONSTCOND */0) - -#define DLIST_TOP(lst) ((void *)((list_t *)(lst))->top) -#define DLIST_BOTTOM(lst) ((void *)((list_t *)(lst))->bottom) -#define DLIST_NEXT(var) ((void *)((node_t *)(var))->next) -#define DLIST_PREV(var) ((void *)((node_t *)(var))->prev) - -#define DLIST_FOREACH(lst, var) \ - for ((var) = DLIST_TOP((lst)); (var) != NULL; (var) = DLIST_NEXT((var))) - -#define DLIST_NODES(lst) (((list_t *)(lst))->nodes) - - - -#endif diff --git a/mmsoftware/mmlib/mmloadarray.c b/mmsoftware/mmlib/mmloadarray.c deleted file mode 100644 index a9e8b67..0000000 --- a/mmsoftware/mmlib/mmloadarray.c +++ /dev/null @@ -1,115 +0,0 @@ -/* $Id: mmloadarray.c,v 1.4 2004/03/22 06:59:35 mmondor Exp $ */ - -/* - * Copyright (C) 2001-2004, Matthew Mondor - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Matthew Mondor. - * 4. The name of Matthew Mondor may not be used to endorse or promote - * products derived from this software without specific prior written - * permission. - * 5. Redistribution of source code may not be released under the terms of - * any GNU Public License derivate. - * - * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - - - - -/* HEADERS */ - -#include -#include -#include -#include -#include -#include - -#include -#include -#include - - - - -MMCOPYRIGHT("@(#) Copyright (c) 2001-2004\n\ -\tMatthew Mondor. All rights reserved.\n"); -MMRCSID("$Id: mmloadarray.c,v 1.4 2004/03/22 06:59:35 mmondor Exp $"); - - - - -/* FUNCTIONS */ - -/* XXX Should be fixed to not use mm_strspl() so that it could grow the - * required pointers buffer dynamically, as needed. Possibly that using - * fdbreadbuf() would be suitable. Then we could actually still all - * mm_strspl(). - */ -struct carray * -load_char_array(char *file, int max) -{ - char *map, **ptrs; - struct carray *ca; - int fd, size, elems; - struct stat info; - - if ((fd = open(file, O_RDONLY))) { - if ((ca = malloc(sizeof(struct carray)))) { - if ((ptrs = malloc(sizeof(char *) * (max + 1)))) { - if (!fstat(fd, &info)) { - size = info.st_size; - if ((map = mmap(NULL, size + 1, PROT_READ | PROT_WRITE, - MAP_PRIVATE, fd, 0))) { - close(fd); - map[size] = 0; - elems = mm_strspl(ptrs, map, max, '\n'); - ptrs[elems] = NULL; - ca->map = map; - ca->array = ptrs; - ca->size = size; - ca->elements = elems; - mmap(map, size + 1, PROT_READ, MAP_PRIVATE, fd, 0); - return (ca); - } - } - free(ptrs); - } - free(ca); - } - close(fd); - } - - return (NULL); -} - - -struct carray * -free_char_array(struct carray *ca) -{ - munmap(ca->map, ca->size); - free(ca->array); - free(ca); - - return (NULL); -} diff --git a/mmsoftware/mmlib/mmloadarray.h b/mmsoftware/mmlib/mmloadarray.h deleted file mode 100644 index eccc889..0000000 --- a/mmsoftware/mmlib/mmloadarray.h +++ /dev/null @@ -1,70 +0,0 @@ -/* $Id: mmloadarray.h,v 1.4 2004/06/01 19:11:57 mmondor Exp $ */ - -/* - * Copyright (C) 2001-2004, Matthew Mondor - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Matthew Mondor. - * 4. The name of Matthew Mondor may not be used to endorse or promote - * products derived from this software without specific prior written - * permission. - * 5. Redistribution of source code may not be released under the terms of - * any GNU Public License derivate. - * - * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - - - - -#ifndef MM_LOADARRAY_H - - - - -/* HEADERS */ - -#include - - - - -/* DEFINITIONS */ - -struct carray { - void *map; - char **array; - int size, elements; -}; - - - - -/* PROTOTYPES */ - -extern struct carray * load_char_array(char *, int); -extern struct carray * free_char_array(struct carray *); - - - - -#endif diff --git a/mmsoftware/mmlib/mmlog.c b/mmsoftware/mmlib/mmlog.c deleted file mode 100644 index 1cf2ebe..0000000 --- a/mmsoftware/mmlib/mmlog.c +++ /dev/null @@ -1,99 +0,0 @@ -/* $Id: mmlog.c,v 1.6 2005/09/21 10:33:59 mmondor Exp $ */ - -/* - * Copyright (C) 2000-2004, Matthew Mondor - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Matthew Mondor. - * 4. The name of Matthew Mondor may not be used to endorse or promote - * products derived from this software without specific prior written - * permission. - * 5. Redistribution of source code may not be released under the terms of - * any GNU Public License derivate. - * - * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - - - - -#include -#include -#include -#include -#include - -#include -#include - - - - -MMCOPYRIGHT("@(#) Copyright (c) 2000-2004\n\ -\tMatthew Mondor. All rights reserved.\n"); -MMRCSID("$Id: mmlog.c,v 1.6 2005/09/21 10:33:59 mmondor Exp $"); - - - - -void -mmsyslog(int level, int loglevel, const char *fmt, ...) -{ - va_list lst; - char buf[1024]; - - if (!level || level <= loglevel) { - va_start(lst, fmt); - vsnprintf(buf, 1023, fmt, lst); - va_end(lst); - syslog(LOG_NOTICE, "%s", buf); - } -} - - -#ifdef DEBUG -void -mmdebug(const char *file, const char *func, int line, const char *fmt, ...) -{ - va_list lst; - char buf[1024]; - - va_start(lst, fmt); - vsnprintf(buf, 1023, fmt, lst); - va_end(lst); - syslog(LOG_NOTICE, "%s:%s():%d - %s", file, func, line, buf); -} - -void -mmdebug2(const char *file, const char *func, int line, const char *fmt, ...) -{ - va_list lst; - char buf[1024]; - - va_start(lst, fmt); - vsnprintf(buf, 1023, fmt, lst); - va_end(lst); - syslog(LOG_NOTICE, "%s:%s():%d - %s", file, func, line, buf); - - abort(); -} -#endif diff --git a/mmsoftware/mmlib/mmlog.h b/mmsoftware/mmlib/mmlog.h deleted file mode 100644 index 250359d..0000000 --- a/mmsoftware/mmlib/mmlog.h +++ /dev/null @@ -1,102 +0,0 @@ -/* $Id: mmlog.h,v 1.16 2005/09/21 10:33:59 mmondor Exp $ */ - -/* - * Copyright (C) 2000-2004, Matthew Mondor - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Matthew Mondor. - * 4. The name of Matthew Mondor may not be used to endorse or promote - * products derived from this software without specific prior written - * permission. - * 5. Redistribution of source code may not be released under the terms of - * any GNU Public License derivate. - * - * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - - - - -#ifndef MMLOG_H -#define MMLOG_H - - - - -#include - -#include - - - - -/* So that debugging messages can use DEBUG_PRINTF() and not be included when - * we don't need them. Note: Use of __func__ seems specific to the latest - * GCC releases, it is thus discouraged. - */ -#ifdef DEBUG - -/* Allows to print a debugging message */ -#define DEBUG_PRINTF(f, s...) mmdebug(__FILE__, f, __LINE__, s) - -/* Similar to assert, but won't exit the application, just evaluates to TRUE - * if condition is TRUE, or always to TRUE if debugging is disabled. - * I.E. if (DEBUG_TRUE(condition)) ... - */ -#define DEBUG_TRUE(c) (c) - -/* Similar to DEBUG_TRUE(), but evaluates to TRUE if condition is TRUE, or - * always to FALSE if debugging is disabled. - * I.E. if (DEBUG_FALSE(condition)) ... - */ -#define DEBUG_FALSE(c) (c) - -/* Macro similar to assert(3) but which does not exit the application. Will - * instead log the condition. - */ -#ifdef DEBUG_ASSERT_ABORT -#define DEBUG_ASSERT(c) if (!(c)) \ - mmdebug2(__FILE__, __func__, __LINE__, "DEBUG_ASSERT(" #c ") == %d", c); -#else -#define DEBUG_ASSERT(c) if (!(c)) \ - mmdebug(__FILE__, __func__, __LINE__, "DEBUG_ASSERT(" #c ") == %d", c); -#endif - -#else - -#define DEBUG_PRINTF(f, s...) ; -#define DEBUG_TRUE(c) /* CONSTCOND */1 -#define DEBUG_FALSE(c) /* CONSTCOND */0 -#define DEBUG_ASSERT(c) ; - -#endif - - - - -extern void mmsyslog(int, int, const char *, ...); -extern void mmdebug(const char *, const char *, int, const char *, ...); - - - - -#endif diff --git a/mmsoftware/mmlib/mmpath.3 b/mmsoftware/mmlib/mmpath.3 deleted file mode 100644 index ce578c4..0000000 --- a/mmsoftware/mmlib/mmpath.3 +++ /dev/null @@ -1,351 +0,0 @@ -.\" $Id: mmpath.3,v 1.8 2004/05/05 23:59:56 mmondor Exp $ -.\" -.\" Copyright (C) 2001-2004, Matthew Mondor -.\" All rights reserved. -.\" -.\" Redistribution and use in source and binary forms, with or without -.\" modification, are permitted provided that the following conditions -.\" are met: -.\" 1. Redistributions of source code must retain the above copyright -.\" notice, this list of conditions and the following disclaimer. -.\" 2. Redistributions in binary form must reproduce the above copyright -.\" notice, this list of conditions and the following disclaimer in the -.\" documentation and/or other materials provided with the distribution. -.\" 3. All advertising materials mentioning features or use of this software -.\" must display the following acknowledgement: -.\" This product includes software developed by Matthew Mondor. -.\" 4. The name of Matthew Mondor may not be used to endorse or promote -.\" products derived from this software without specific prior written -.\" permission. -.\" 5. Redistribution of source code may not be released under the terms of -.\" any GNU Public License derivate. -.\" -.\" THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR -.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES -.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. -.\" IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, -.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -.\" BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF -.\" USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF -.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -.\" -.Dd January 16, 2003 -.Os mmsoftware -.Dt MMPATH 3 -.Sh NAME -.Nm mmpath -.Nd Virtual chroot sanity checking system for public services -.Sh SYNOPSIS -.Fd #include -.Fd #include -.Fd #include -.Fd #include -.Ft bool -.Fn path_copy "char *dst" "const char *src" "size_t max" "bool glob" -.Ft bool -.Fn path_valid "char *to" "char *clean" "const char *root" "const char *cwd" \ - "char *path" "bool glob" "bool chdir" "bool empty" -.Ft bool -.Fn path_parent "char *cwd" -.Ft int -.Fn path_exists "const char *file" "long *size" "char *time" "bool checkowner" -.Ft bool -.Fn path_ls "fdbuf *fdb" "const char *path" "const char *user" \ -"const char *group" "int flags" -.Sh DESCRIPTION -This library provides functions especially useful to be used by public -services needing to run under a non-privileged user, only supporting -virtual users. A root jail is simulated on a specified root directory -and sanity checking is performed on the user-supplied paths. -.Ss FUNCTIONS -.Bl -tag -width indent -offset indent -.It bool Fn path_copy "char *dst" "const char *src" "size_t max" "bool glob" -copies -.Fa src -to -.Fa dst -respecting -.Fa max -like -.Xr strncpy 3 -would, but also performs alot of sanity checking, and substitutes multiple -consecutive '/' by a single one. If -.Fa glob -is TRUE, pattern matching wildcards are accepted in the last path -element (after the last '/'). The -.Xr fnmatch 3 -function will internally be used to test matching. If FALSE, globbing -characters are refused. The first character of the -.Fa dst -will always start with '/' and will always end without trailing '/' on success. -It's sanity checking will not accept '.' at beginning of any path element, -and unprintable characters, as well as '~', '\\' and '%' are rejected anywhere. -The resulting -.Fa dst -string is guarenteed to be NIL terminated. -.It Xo bool -.Fo path_valid -.Fa "char *to" -.Fa "char *clean" -.Fa "const char *root" -.Fa "const char *cwd" -.Fa "char *path" -.Fa "bool glob" -.Fa "bool chdir" -.Fa "bool empty" -.Fc -.Xc -is a very important function which should be called whenever a user-supplied -path is provided, before any use of that untrusted pathname be made. It can -serve three main functions: -.Bl -tag -width indent -.It 1) -Provide the application an absolute path to a specific file or directory as -viewed by the operating system (not relative to the virtual user root -directory). -.It 2) -Provide the application a virtual root relative absolute path to a specific -file or directory. -.It 3) -Provide the new CWD (current working directory) relative to the virtual root -directory, useful for changedir operations using user-supplied paths. -.El -.Pp -Alot of sanity checking is performed on the user-supplied path. The -.Fa to -and -.Fa clean -supplied result buffers should be at least MMPATH_MAX bytes long. As a general -rule of thumb, -.Fa to -will be used to obtain the new system-relative absolute pathname to be -used by the application, and -.Fa clean -to obtain the virtual /root/home (actually /home/path) relative absolute -pathname to display to the virtual user unless NULL was specified. The -.Fa root -parameter points to a string containing the system-relative absolute path -to the virtual user's root or home directory. -.Fa cwd -points to a string containing the actual user's current working directory -relative to it's virtual fake root/home directory. -.Fa path -consists of the user-supplied pathname to be sanity evaluated. This can consist -of both virtual home/root relative absolute pathnames or CWD relative ones. -.Fa glob -may be TRUE globbing characters are to be allowed in the last path -element of the user-supplied -.Fa path -parameter ('*', '?', '|', '(', ')', '{', '}', '[', ']'), or FALSE otherwise. -.Fa chdir -should be TRUE if the caller wants to obtain a new CWD to be used for future -.Fn path_valid -calls, useful to implement virtual user -.Fn chdir -like function, in which case -.Fa to -will be set to the new CWD. -.Pp -If -.Fa chdir -is TRUE, it will be initialized to contain the new faked root relative path -to the directory supplied, in fact the new CWD to use for future -.Fn path_valid -calls. Of course the programmer should perform sanity checking on weither -directory exists first, usually using -.Fn path_exists -function on the user's system relative absolute root and returned path -concanated together. -.Pp -If -.Fa empty -is TRUE, an empty supplied path will be accepted as meaning the current -directory. This may be useful for commands such as directory listing which -by default should operate on the current directory, but also optionally -be supplied a pathname. -.Pp -Here is a little code excerpt demonstrating this: -(extracted from mmftpd.c) -.Bd -literal -static int -main_cwd(clientenv *clenv) -{ - char path[MMPATH_MAX], path2[MMPATH_MAX]; - int t = MMPATH_DENIED; - - if (path_valid(path, NULL, clenv->home, clenv->cwd, - &clenv->buffer[3], FALSE, TRUE, FALSE)) { - /* Make sure directory exists and can be accessed - * safely - */ - snprintf(path2, MMPATH_MAX - 1, "%s%s", clenv->home, - path); - if ((t = path_exists(path2, NULL, NULL, - clenv->checkowner)) != MMPATH_DENIED) { - if (t == MMPATH_DIR) { - mm_strncpy(clenv->cwd, path, - MMPATH_MAX - 1); - if (!directory_message(clenv, 250)) - return (STATE_ERROR); - reply(clenv->fdb, 250, FALSE, - "Command successful"); - - return (STATE_CURRENT); - } else - reply(clenv->fdb, 550, FALSE, - "No such file or directory"); - } else - reply(clenv->fdb, 502, FALSE, - "Permission denied"); - } else - reply(clenv->fdb, 550, FALSE, - "Invalid pathname"); - - REGISTER_ERROR(clenv); - - return (STATE_CURRENT); -} -.Ed -.Pp -Of course the application should only safely use information provided through -.Fa to -supplied buffer to maintain the virtual chroot jail security and prevent -bad directory or filenames to be used. -.It bool Fn path_parent "char *cwd" -function permits to modify a supplied CWD (starting with '/', not ending -with '/'), in a way to strip the last element if possible. Useful to implement -functions such as FTP PWD command. -.Fn path_valid -can however be used for "cd .." and the like, using the -.Fa chdir -boolean parameter. -.It int Fn path_exists "const char *file" "long *size" "char *time" \ -"bool checkowner" -consists of a very useful function to obtain information pertaining to the -existance, type, size, and time of a particular file. -.Fa file -should be a fullpath obtained from -.Fn path_valid -function of course. If the file exists, -.Fa size -pointer, if not NULL, will be used to store the size of the file, -in the wanted long variable. -.Fa time -byte buffer pointer, if not NULL, will be used to store the date and time -of last modification time of the file, in the very old YYYYMMDDHHMMSS format -which eventually became ISO 3307 standard. Obviously the buffer should at -least be 16 bytes in size. Some sanity checking is also performed to make -sure that symbolic links and special files don't be allowed access to, -that directories are at least readable and executable and files at least -readable, and if -.Fa checkowner -is TRUE, file ownership is also evaluated. (See the RETURN VALUES section -for more information). -.It bool Fn path_ls "fdbuf *fdb" "const char *path" "const char *user" \ -"const char *group" "int flags" -is used to write to supplied -.Fa fdb -handler (see SEE ALSO section about mmfd) a list of files. -.Fa path -should as usual be obtained using -.Fn path_valid -and should point to a system-relative absolute path, to the directory or -file to be listed. Some sanity checking is performed to ensure to not display -special files (if any). The -.Fa flags -parameter allows to specify options on the listing behavior. The following -can be XORed: -.Fa MMLS_GLOB -allows a wildcard to be used in the last pathname element which will be -processed using -.Xr fnmatch 3 -for matching. Only matching files will be displayed. Wildcards are not accepted -by default. -.Fa MMLS_LONG -specifies that the long listing format is desired. When this is used, the -.Fa user -and -.Fa group -supplied strings will be the owner and group displayed for each file, -respectively. Otherwise only filenames are listed. -.Fa MMLS_OWNERONLY -tells -.Fn path_ls -to only display files which belong to the current effective user ID -(as reported by -.Xr geteuid 2 ) . -Other files will be logged via the syslog facility and hidden from the list. -When -.Fa MMLS_READONLY -is set along with -.Fa MMLS_LONG , -directories and files will appear read-only even if they aren't, and -executable files with the 'x' bit set will appear like normal read-only files. -.Pp -.El -.Sh RETURN VALUES -.Bl -tag -width indent -offset indent -.It Fn path_copy -returns TRUE on success, or FALSE if the supplied -.Fa src -path is illegal. -.It Fn path_valid -returns TRUE if the user supplied path was valid and operation successful, -or FALSE otherwise (invalid path). -.It Fn path_parent -returns TRUE on success, or FALSE if there is no parent directory (root -was reached). -.It Fn path_exists -returns -.Bq Er MMPATH_NONE -if file does not exist or consists of a special file, -.Bq Er MMPATH_FILE -if file exists and consists of a regular file, or -.Bq Er MMPATH_DIR -if file exists and is a directory. If file consists of a special file or does -not belong to the current effective UID (and -.Fa checkowner -is TRUE), syslog facility is used to warn about the security problem and -.Bq Er MMPATH_DENIED -is returned. It would consist of a mistake to confuse -.Bq Er MMPATH_NONE -and -.Bq Er MMPATH_DENIED -in an application, especially if it needs to verify that a file does not exist -before creating one. -.It Fn path_ls -returns TRUE on success, or FALSE if it could not write to supplied -.Fa fdb -handler. It also logs via syslog security issues. -.El -.Sh SEE ALSO -.Xr mmfd 3 , -.Xr chdir 2 , -.Xr geteuid 2 , -.Xr strncpy 3 , -.Xr directory 3 , -.Xr fnmatch 3 . -.Sh STANDARDS -None, this library is however intended to run on a system conforming to -IEEE Std 1003.2-1992 (``POSIX.2''). -.Fn path_copy -and -.Fn path_valid -behavior according to multiple consecutive '/' follow the IEEE 1003.1 standard. -.Sh HISTORY -mmpath was originally developped on NetBSD 1.5.1 for the mmftpd project -from Matthew Mondor. -.Fn path_copy -was developped later on using NetBSD 1.5.3 and -.Fn path_valid -was totally rewritten around a cleaner design, depending on the -.Fn path_copy -function. (These changes were done for mmftpd 0.0.16). It now should be very -solid. -.Sh AUTHORS -The mmpath library was written by Matthew Mondor and is -Copyright (c) 2001-2004 Matthew Mondor, All rights reserved. -.Sh BUGS -Please report any bug to mmondor@accela.net diff --git a/mmsoftware/mmlib/mmpath.c b/mmsoftware/mmlib/mmpath.c deleted file mode 100644 index 7e75d9a..0000000 --- a/mmsoftware/mmlib/mmpath.c +++ /dev/null @@ -1,501 +0,0 @@ -/* $Id: mmpath.c,v 1.19 2006/06/13 17:17:02 mmondor Exp $ */ - -/* - * Copyright (C) 2001-2004, Matthew Mondor - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Matthew Mondor. - * 4. The name of Matthew Mondor may not be used to endorse or promote - * products derived from this software without specific prior written - * permission. - * 5. Redistribution of source code may not be released under the terms of - * any GNU Public License derivate. - * - * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - - - - -/* HEADERS */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - - - - -static bool path_ls2(fdbuf *, const char *, const char *, const char *, - const char *, int, uid_t, int); - - - - -MMCOPYRIGHT("@(#) Copyright (c) 2001-2004\n\ -\tMatthew Mondor. All rights reserved.\n"); -MMRCSID("$Id: mmpath.c,v 1.19 2006/06/13 17:17:02 mmondor Exp $"); - - - - -/* GLOBALS */ - -static const char *months[] = { - "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", - "Oct", "Nov", "Dec", NULL -}; - -static const char *perms[] = { - "---", "--x", "-w-", "-wx", "r--", "r-x", "rw-", "rwx", NULL -}; - - - - -/* FUNCTIONS */ - -/* Copies src to dst performing path sanity checking and replacing multiple - * consecutive '/' by a single one. dst will start with '/' and end without - * trailing '/'. Returns TRUE on success, or FALSE if invalid path was supplied - * as src. - */ -bool -path_copy(char *dst, const char *src, size_t max, bool glob) -{ - const char *srcptr; - char *dstptr, *todstptr, l; - int i1, i2; - bool ok = TRUE; - - srcptr = src; - dstptr = todstptr = dst; - todstptr += (max - 1); - *dstptr++ = l = '/'; - i1 = i2 = 0; - while (*srcptr != '\0' && dstptr < todstptr) { - if (l == '/') { - /* Only allow globbing characters in the last path element */ - if (i1 || i2) { - ok = FALSE; - break; - } - /* Replace multiple consecutive instances of '/' by one */ - while (*srcptr == '/') - srcptr++; - /* Prohibit '.' at start of new path element */ - if (*srcptr == '.') { - ok = FALSE; - break; - } - } - if (*srcptr == '\0') - break; - /* Only allow '*' if globbing is allowed, and a maximum of three */ - if (*srcptr == '*') { - if (glob && i1 < 3) - i1++; - else { - ok = FALSE; - break; - } - /* Only allow these if globbing is allowed */ - } else if (ISGLOBCHAR(*srcptr)) { - if (glob) - i2++; - else { - ok = FALSE; - break; - } - } - /* Refuse illegal characters for paths */ - if (!((isprint((int)*srcptr) || *srcptr == ' ') && *srcptr != '%' && - *srcptr != '\\' && *srcptr != '~')) { - ok = FALSE; - break; - } - *dstptr++ = l = *srcptr++; - } - if (dstptr > (dst + 1) && dstptr[-1] == '/') - dstptr[-1] = '\0'; - else - *dstptr = '\0'; - - return ok; -} - - -/* Useful function to process user supplied paths securely, into a fake - * chroot jail environment. See mmpath(3) man page for more information. - */ -bool -path_valid(char *to, char *clean, const char *root, const char *cwd, - char *path, bool glob, bool chd, bool empty) -{ - char tcwd[MMPATH_MAX + 1], tmpstr[MMPATH_MAX + 1], *start, *tmp; - bool t1 = FALSE; - - *to = '\0'; - if (chd) - glob = FALSE; - mm_strncpy(tcwd, cwd, MMPATH_MAX); - - /* First strip starting and trailing spaces from supplied path, - * as well as trailing '/' characters. Also get rid of any starting '.', - * '~' or '/', in which two last cases causes tcwd to be cleared. - * If no path was supplied, refuse the entry. - */ - for (tmp = path; *tmp != '\0' && *tmp == ' '; tmp++) ; - if (*tmp == '\0' && !empty) - return FALSE; - if (*tmp == '~') { - *tcwd = '\0'; - tmp++; - } else if (*tmp == '/') { - while (*tmp == '/') - tmp++; - *tcwd = '\0'; - } else if (tmp[0] == '.' && ((t1 = (tmp[1] == '/')) || tmp[1] == '\0')) { - tmp++; - if (t1) - tmp++; - } - for (start = tmp; *tmp != '\0'; tmp++) ; - for (tmp--; *tmp == ' '; tmp--) - *tmp = '\0'; - for (; *tmp == '/'; tmp--) - *tmp = '\0'; - if (tmp == start && *tmp == '\0' && !empty) - return FALSE; - - /* Now process all starting ".." or "../", modifying tcwd */ - tmp = start; - while (tmp[0] == '.' && tmp[1] == '.' && - (tmp[2] == '\0' || (t1 = (tmp[2] == '/')))) { - if (!path_parent(tcwd)) - return FALSE; - tmp += 2; - if (t1) { - while (*tmp == '/') - tmp++; - } - } - start = tmp; - if (tcwd[0] == '/' && tcwd[1] == '\0') - tcwd[0] = '\0'; - - if (chd) { - /* We were called to supply new safe cwd */ - snprintf(tmpstr, MMPATH_MAX - 1, "/%s/%s", tcwd, start); - if (path_copy(to, tmpstr, MMPATH_MAX, FALSE)) - return TRUE; - return FALSE; - } - - /* We should supply caller with both absolute path (system wide) and - * clean path having the appearance of an absolute path, into the - * fake chrooted environment, suitable to report to the user. - */ - snprintf(tmpstr, MMPATH_MAX - 1, "/%s/%s/%s", root, tcwd, start); - if (path_copy(to, tmpstr, MMPATH_MAX, glob)) { - if (clean) { - snprintf(tmpstr, MMPATH_MAX - 1, "/%s/%s", tcwd, start); - if (path_copy(clean, tmpstr, MMPATH_MAX, glob)) - return TRUE; - } else - return TRUE; - } - - return FALSE; -} - - -/* Returns TRUE if it could go to parent directory, and fixes the string, - * or FALSE if it couldn't (reached '/'). cwd is expected to start with '/' - */ -bool -path_parent(char *cwd) -{ - register size_t i; - - i = mm_strlen(cwd); - if (i < 2 || *cwd != '/') - return FALSE; - - /* Strip possible trailing '/' */ - for (i--; cwd[i] == '/'; i--) - cwd[i] = '\0'; - - /* Locate next '/' */ - while (i != 0 && cwd[i] != '/') - i--; - if (i != 0) - cwd[i] = '\0'; - else - cwd[i + 1] = '\0'; - - return TRUE; -} - - -/* Returns MMPATH_DIR if file exists and is a directory, MMPATH_FILE if a - * normal data file, and returns MMPATH_NONE if file does not exist, - * if file is a symbolic link, or is not owned by us (if own is TRUE), - * for security considerations. - * Of course the filename should have been returned by path_valid() - * If size or time pointers are supplied, we fill them. Supplied time buffer - * should at least be 16 bytes. The timestamp is returned in the very old - * YYYYMMDDHHMMSS format which eventually became ISO 3307 standard. - */ -int -path_exists(const char *file, long *size, char *time, bool own) -{ - struct stat st; - struct tm tm; - register int ret; - - if ((lstat(file, &st)) != -1) { - /* Evaluate if file/directory is allowed access to */ - if (!own || st.st_uid == geteuid()) { - if (S_ISDIR(st.st_mode)) { - if (access(file, R_OK | X_OK) == 0) - ret = MMPATH_DIR; - else - ret = MMPATH_DENIED; - } else if (S_ISREG(st.st_mode)) { - if (access(file, R_OK) == 0) { - ret = MMPATH_FILE; - if (size != NULL) - *size = st.st_size; - if (time != NULL) { - if ((gmtime_r(&st.st_mtime, &tm)) != NULL) { - snprintf(time, 15, "%04d%02d%02d%02d%02d%02d", - 1900 + tm.tm_year, tm.tm_mon + 1, - tm.tm_mday, tm.tm_hour, tm.tm_min, - tm.tm_sec); - time[15] = '\0'; - } - } - } else - ret = MMPATH_DENIED; - } else { - ret = MMPATH_DENIED; - syslog(LOG_NOTICE, - "path_exists() - Special file: '%s'", file); - } - } else { - ret = MMPATH_DENIED; - syslog(LOG_NOTICE, - "path_exists() - Wrong owner: '%s'", file); - } - } else - ret = MMPATH_NONE; - - return ret; -} - - -/* This function lists the contents of a directory or information on a file, - * or of files matching a pattern, outputting the list to fdb. - * See mmpath(3) man page for more information. - * NOTE: fts_open(3) and companions could have been used; They unfortunately - * need to perform lstat(2) calls for each entry anyways if we are to obtain - * owner information. Also, lstat(2) will solve the glibc problem about - * opendir(3) interface which was not fully implemented, as we can use all - * required information from the stat structure. - * - * Possibly a cache could be maintained of lstat(2) results; This however - * would imply that all functions performed on files also update this cache, - * using provided wrappers for rename(2), unlink(2), chmod(2) and chown(2)... - * And the cache would need to be hashed-based, with absolute paths to not - * confuse itself between various filesystems. A similar cache could be - * maintained for uid->name conversion if we were to show the real owner - * of a file when own is FALSE. - */ -bool -path_ls(fdbuf *fdb, const char *path, const char *user, const char *group, - int flags) -{ - bool ret = TRUE; - int i; - char d[MMPATH_MAX], p[MMPATH_MAX], t[MMPATH_MAX], t2[MMPATH_MAX]; - const char *tmp; - DIR *dir; - struct dirent *dire; - struct tm tm; - time_t tim; - uid_t uid = 0; - size_t len; - - /* First determine directory to opendir() */ - if ((i = path_exists(path, NULL, NULL, (flags & MMLS_OWNERONLY))) - != MMPATH_DENIED) { - /* Cache current time to compare year */ - tim = time(NULL); - gmtime_r(&tim, &tm); - /* And current uid to compare owner */ - uid = geteuid(); - if (i == MMPATH_FILE) { - /* Requesting listing for a single file, first isolate - * filename from path, then display it - */ - len = mm_strlen(path); - for (tmp = &path[(len > 0 ? len - 1 : 0)]; - *tmp != '\0' && *tmp != '/'; tmp--) ; - if (*tmp == '/') - tmp++; - return (path_ls2(fdb, path, tmp, user, group, tm.tm_year, uid, - flags)); - } else if (i == MMPATH_DIR) { - /* Requesting listing of a whole directory */ - i = mm_strncpy(d, path, MMPATH_MAX - 2); - i--; - if (i && d[i] == '/') - d[i] = '\0'; - flags &= ~MMLS_GLOB; - } else if ((flags & MMLS_GLOB) != 0) { /* MMPATH_NONE */ - /* Requesting list of files matching a pattern in a directory, - * first isolate pattern and directory, make sure we only allow - * patterns on files (last path component) - */ - len = mm_strlen(path); - for (tmp = &path[(len > 0 ? len - 1 : 0)]; - *tmp != '\0' && *tmp != '/'; tmp--) ; - if (*tmp == '/') - tmp++; - mm_strncpy(p, tmp, MMPATH_MAX - 1); - i = mm_strncpy(d, path, tmp - path); - i--; - if (i && d[i] == '/') - d[i] = '\0'; - } else - ret = FALSE; - } else - ret = FALSE; - - if (ret) { - /* Scan directory listing files */ - if ((dir = opendir(d)) != NULL) { - while ((dire = readdir(dir)) != NULL) { - tmp = dire->d_name; - /* Skip any "hidden" or curr/prev files */ - if (*tmp != '.') { - if ((flags & MMLS_GLOB) != 0) - if (fnmatch(p, tmp, FNM_NOESCAPE | FNM_PATHNAME | - FNM_PERIOD) != 0) - tmp = NULL; - if (tmp) { - snprintf(t2, MMPATH_MAX - 1, "%s/%s", d, tmp); - if (path_copy(t, t2, MMPATH_MAX - 1, FALSE)) { - if (!(ret = path_ls2(fdb, t, tmp, user, group, - tm.tm_year, uid, flags))) - break; - } - } - } - } - closedir(dir); - } else - syslog(LOG_NOTICE, "* path_ls() - opendir(%s)", d); - } - - return ret; -} - - -/* I agree that security cannot be obtained by obfuscation alone; I however - * still consider hiding data which could have a misuse potential as part of - * a good security plan. We will not show any special files, or potentially - * dangerous permission bits (eg: setuid bit). We also cause special files - * to be ignored. - */ -static bool -path_ls2(fdbuf *fdb, const char *realfile, const char *filetext, - const char *user, const char *group, int year, uid_t uid, int flags) -{ - bool ret = TRUE; - struct stat st; - struct tm tm; - mode_t mode; - const char *usr, *grp, *oth; - char type, hy[8]; - - if ((lstat(realfile, &st)) != -1) { - mode = st.st_mode; - /* Evaluate if we should display file, or hide and log it */ - if ((((flags & MMLS_OWNERONLY) == 0) || st.st_uid == uid) && - (S_ISREG(mode) || S_ISDIR(mode))) { - if ((flags & MMLS_LONG) != 0) { - if ((gmtime_r(&st.st_mtime, &tm)) != NULL) { - if (tm.tm_year == year) - snprintf(hy, 6, "%02d:%02d", tm.tm_hour, tm.tm_min); - else - snprintf(hy, 6, " %04d", 1900 + tm.tm_year); - /* Obfuscation alone obviously doesn't guarantee security; - * Good administration and obfuscation are however welcome. - */ - mode &= ~(S_ISUID | S_ISGID | S_ISVTX); - if (S_ISDIR(mode)) { - type = 'd'; - mode &= ~S_IFMT; - if ((flags & MMLS_READONLY) != 0) - mode &= ~0222; - } else { - type = '-'; - mode &= ~S_IFMT; - if ((flags & MMLS_READONLY) != 0) - mode &= ~0333; - } - usr = perms[(mode & 0700) >> 6]; - grp = perms[(mode & 0070) >> 3]; - oth = perms[mode & 0007]; - ret = fdbprintf(fdb, - "%c%s%s%s %3d %s %s %9ld %s %2d %s %s\r\n", - type, usr, grp, oth, st.st_nlink, user, group, - (long)st.st_size, months[tm.tm_mon], - tm.tm_mday, hy, filetext); - } else - syslog(LOG_NOTICE, "path_ls2() - gmtime_r()"); - } else - ret = fdbprintf(fdb, "%s\r\n", filetext); - } else - syslog(LOG_NOTICE, - "path_ls2() - Special file or wrong owner: '%s'", - realfile); - } else - syslog(LOG_NOTICE, "path_ls2 - lstat(%s)", realfile); - - return (ret); -} diff --git a/mmsoftware/mmlib/mmpath.h b/mmsoftware/mmlib/mmpath.h deleted file mode 100644 index da1683f..0000000 --- a/mmsoftware/mmlib/mmpath.h +++ /dev/null @@ -1,92 +0,0 @@ -/* $Id: mmpath.h,v 1.8 2004/06/01 19:11:57 mmondor Exp $ */ - -/* - * Copyright (C) 2001-2004, Matthew Mondor - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Matthew Mondor. - * 4. The name of Matthew Mondor may not be used to endorse or promote - * products derived from this software without specific prior written - * permission. - * 5. Redistribution of source code may not be released under the terms of - * any GNU Public License derivate. - * - * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - - - - -#ifndef MMPATH_H -#define MMPATH_H - - - - -/* HEADERS */ - -#include - -#include -#include - - - - -/* DEFINITIONS */ - -#define MMPATH_MAX 256 - -/* For exists() result */ -#define MMPATH_NONE 0 -#define MMPATH_DIR 1 -#define MMPATH_FILE 2 -#define MMPATH_DENIED 3 - -/* ls() and ls2() flags */ -#define MMLS_LONG (1<<0) -#define MMLS_GLOB (1<<1) -#define MMLS_READONLY (1<<2) -#define MMLS_OWNERONLY (1<<3) - -/* Useful macro */ -#define ISGLOBCHAR(c) ((c) == '*' || (c) == '?' || (c) == '|' || \ - (c) == '(' || (c) == ')' || (c) == '[' || (c) == ']' || \ - (c) == '{' || (c) == '}') - - - - -/* PROTOTYPES */ - -extern bool path_copy(char *, const char *, size_t, bool); -extern bool path_valid(char *, char *, const char *, const char *, char *, - bool, bool, bool); -extern bool path_parent(char *); -extern int path_exists(const char *, long *, char *, bool); -extern bool path_ls(fdbuf *, const char *, const char *, const char *, - int); - - - - -#endif diff --git a/mmsoftware/mmlib/mmpool.3 b/mmsoftware/mmlib/mmpool.3 deleted file mode 100644 index c56bc5f..0000000 --- a/mmsoftware/mmlib/mmpool.3 +++ /dev/null @@ -1,320 +0,0 @@ -.\" $Id: mmpool.3,v 1.7 2004/05/22 17:43:59 mmondor Exp $ -.\" -.\" Copyright (C) 2001-2004, Matthew Mondor -.\" All rights reserved. -.\" -.\" Redistribution and use in source and binary forms, with or without -.\" modification, are permitted provided that the following conditions -.\" are met: -.\" 1. Redistributions of source code must retain the above copyright -.\" notice, this list of conditions and the following disclaimer. -.\" 2. Redistributions in binary form must reproduce the above copyright -.\" notice, this list of conditions and the following disclaimer in the -.\" documentation and/or other materials provided with the distribution. -.\" 3. All advertising materials mentioning features or use of this software -.\" must display the following acknowledgement: -.\" This product includes software developed by Matthew Mondor. -.\" 4. The name of Matthew Mondor may not be used to endorse or promote -.\" products derived from this software without specific prior written -.\" permission. -.\" 5. Redistribution of source code may not be released under the terms of -.\" any GNU Public License derivate. -.\" -.\" THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR -.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES -.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. -.\" IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, -.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -.\" BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF -.\" USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF -.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -.\" -.Dd Mars 26, 2003 -.Os mmsoftware -.Dt MMPOOL 3 -.Sh NAME -.Nm mmpool -.Nd General purpose efficient memory pools -.Sh SYNOPSIS -.Fd #include -.Fd #include -.Fd #include -.Ft bool -.Fo pool_init -.Fa "pool_t *pool" "const char *label" "void *(*malloc)(void)" -.Fa "void (*free)(void *)" "bool (*create)(pnode_t *)" -.Fa "void (*destroy)(pnode_t *)" "size_t nodesize" -.Fa "u_int32_t nodesperpage" "u_int32_t minpages" "u_int32_t maxpages" -.Fc -.Ft bool -.Fn pool_destroy "pool_t *pool" -.Ft pnode_t * -.Fn pool_alloc "pool_t *pool" "bool zero" -.Ft pnode_t * -.Fn pool_free "pnode_t *pnode" -.Ft bool -.Fn POOL_VALID "pool_t *pool" -.Ft bool -.Fn PNODE_VALID "pnode_t *pnode" -.Sh DESCRIPTION -This library provides a useful set of functions to minimize the frequency of -calls to -.Xr malloc 3 -and -.Xr free 3 -functions. These pools internally use buffers, and evaluate when they should -shrink the buffers, using statistics on the current general memory usage. -Using it can significantly speed up programs which need to often allocate and -free objects, especially on systems on which the general purpose allocation -functions are unefficienty implemented. -.Pp -Optional support is also provided for constructor and destructor functions to -automatically be called by the allocator, minimizing the frequency of -initializing and destroying those objects. Although this will generally -require more memory resources in the case of objects which need to allocate -other resources at creation time, it will significantly enhance performance, -since objects will be destroyed only when memory pages are destroyed and -freed, following the previously mentionned heuristics. -.Ss HEADER FILE AND DATA STRUCTURES -This library is compatible with -.Xr mmlist 3 -in that the -.Nm pnode_t -object header internally begins with an -.Xr mmlist 3 -.Nm node_t -header. It is thus safe to use the -.Xr mmlist 3 -doubly linked list manipulation macros directly on the allocated -.Xr mmpool 3 -objects. -.Pp -.Nm pool_t , -for simple efficient fixed-sized objects allocation: A custom user-specified -object type has to be defined as a structure, which needs to begin using a -field of -.Nm pnode_t -type, as in the following: -.Bd -literal -offset indent -struct myobject { - pnode_t node; - /* custom fields... */ -}; -.Ed -.Ss FUNCTIONS -.Bl -tag -width indent -offset indent -.It Xo bool -.Fo pool_init -.Fa "pool_t *pool" -.Fa "const char *label" -.Fa "void *(*malloc)(void)" -.Fa "void (*free)(void *)" -.Fa "bool (*create)(pnode_t *)" -.Fa "void (*destroy)(pnode_t *)" -.Fa "size_t nodesize" -.Fa "u_int32_t nodesperpage" -.Fa "u_int32_t minpages" -.Fa "u_int32_t maxpages" -.Fc -.Xc -is necessary to use before a -.Nm pool_t -can be used. -.Pp -.Fa pool -specifies the pointer to the -.Nm pool_t -structure which is to be initialized to later serve to allocate objects. -.Pp -.Fa label -serves for verbose diagnostics. Should generally be set to the pool_t variable -name used by the application for this pool. Note that diagnostics code will -not be enabled by default. Both -DDEBUG and -DMMPOOLDEBUG must be passed to -the compiler CFLAGS to enable them. These are _very_ verbose and are only -meant for debugging. -.Pp -.Fa malloc -and -.Fa free -specify the functions which are to be called when new buffers need to be -allocated by the pool, or when it needs to shrink the buffers, and can consist -of -.Xr malloc 3 -and -.Xr free 3 , -respectively, or to custom functions which are expected to behave the same -API-wise. -.Pp -.Fa create -and -.Fa destroy -specify the object constructor and destrutor functions which are to be called -for every object of created or destroyed pages. Both can be NULL if no such -support is wanted. Using this can however yield in considerable performance -improvements. This however has to be used with care; You then assume that -objects are pre-created at -.Fn pool_alloc , -and that you also must return them in their created state at -.Fn pool_free , -leaving the pool allocator to automatically create and destroy them as needed -internally. Note that the zero-fill feature of -.Fn pool_alloc -cannot be used with this feature. The supplied -.Fa create -function should return TRUE on success, or FALSE on failure. -.Pp -.Fa nodesize -tells the individual size of the fixed-sized objects which are to be allocated -and freed by this pool. -.Pp -.Fa nodesperpage -consists of the number of wanted elements per internal buffer. New buffers -of -.Fa nodesperpage -* -.Fa nodesize -will be dynamically allocated if no more objects are prebuffered at a future -node allocation request by calling the user-supplied -.Fa malloc . -.Pp -Although the 'page' termination is used, it does not correspond to an actual -physical or virtual memory page, but to a buffer. -When the system evaluates that it should shrink because the current buffers -largely exceed the current useage for some time, those will be freed back as -necessary calling the user-supplied -.Fa free -function. -.Pp -.Fa minpages -is useful to make sure that a pool never frees back totally it's buffers. -This may be wanted for instance when critical code will be used which needs -to allocate and/or free objects from the pool, but which cannot call the -higher-level -.Xr malloc 3 -or -.Xr free 3 -functions. This number of buffers are immediately allocated and setup as -available pre-buffered nodes at initialization. -If -.Fa minpages -is 0, it will be able to shrink completely as the system notices that no -buffers are required. -.Pp -.Fa maxpages , -similarily to -.Fa minpages -can set a boundary on the maximum number of pages (buffers) which are to be -allowed into the pool. Using 0 here allows the pool to always dynamically grow -as required. Using for instance a value of 2 for both -.Fa minpages -and -.Fa maxpages -causes the pool to be fixed to -.Fa nodesperpage -* -.Fa minpages -available elements, and the higher-level -.Xr malloc 3 -and -.Xr free 3 -functions will never be called, except at -.Fn pool_init -and -.Fn pool_destroy . -.It bool Fn pool_destroy "pool_t *pool" -can be called on a pre-initialized -.Nm pool_t -to totally free any remaining buffers and elements it may hold, and mark it as -invalid. -.Fa pool -will no longer be able to serve allocation or freeing requests until it is -re-initialized again using -.Fn pool_init -again (if so). Note that any currently allocated objects from this pool which -have not yet been freed back are also immediately freed, and become invalid. -.It pnode_t * Fn pool_alloc "pool_t *pool" "bool zero" -attempts to efficiently allocate a -.Nm pnode_t -prefixed object from the specified -.Fa pool , -and optionally clears it with zeros if -.Fa zero -is TRUE. Note that this zeroing feature can only be used on objects not part -of a pool using constructors/destructors for it's objects. The size of the -object is always fixed to the -.Fa nodesize -argument which was specified at -.Fn pool_init -for this pool. The returned object is guaranteed to be long-word aligned. -.It pnode_t * Fn pool_free "pnode_t *pnode" -attempts to efficently free a -.Nm pnode_t -prefixed object back to the -.Nm pool_t -it belongs to, which is internally remembered. -.It bool Fn POOL_VALID "pool_t *pool" -can be useful to determine if the supplied -.Fa pool -pointer was previously successfully initialized and was not destroyed since. -.It bool Fn PNODE_VALID "pnode_t *pnode" -can also be useful when it is necessary to determine if an -.Nm pnode_t -expected prefixed object to which -.Fa pnode -points is valid, that is, was successfully allocated, and originates from a -.Nm pool_t -which is also still valid. -.El -.Sh RETURN VALUES -.Bl -tag -width indent -offset indent -.It Fn pool_init -returns TRUE on success, or FALSE on failure (our of memory or invalid -parameters). -.It Fn pool_destroy -returns TRUE on success, or FALSE if the supplied -.Fa pool -pointer points to an invalid -.Nm pool_t , -which either was previously destroyed already, or was never initialized. -.It Fn pool_alloc -returns a pointer of -.Nm pnode_t -type corresponding to the header of the newly allocated object, or NULL -(invalid -.Fn pool , -out of memory, or out of pre-buffered elements if -.Fa maxpages -was set at -.Fn pool_init . -.It Fn pool_free -always returns NULL, and internally makes sure to not attempt to free -already freed or invalid objects. -.It Xo -.Fn POOL_VALID , -.Fn PNODE_VALID -.Xc -macros return TRUE or FALSE. -.El -.Sh SEE ALSO -.Xr malloc 3 , -.Xr free 3 , -.Xr mmlist 3 , -.Xr mmhash 3 . -.Sh STANDARDS -None -.Sh HISTORY -This -.Nm pool_t -system was originally developped on NetBSD 1.5 for the Xisop -portable microkernel project from Matthew Mondor, which also served behind -the more general -.Nm mpool_t -system. It later on was adapted so part of it could be used by mmserver, -mmftpd and mmmail daemons. -.Sh AUTHORS -The mmpool library was written by Matthew Mondor and is -Copyright (c) 2001-2004, Matthew Mondor, All rights reserved. -.Sh BUGS -Please report any bug to mmondor@accela.net diff --git a/mmsoftware/mmlib/mmpool.c b/mmsoftware/mmlib/mmpool.c deleted file mode 100644 index 5b1d22d..0000000 --- a/mmsoftware/mmlib/mmpool.c +++ /dev/null @@ -1,493 +0,0 @@ -/* $Id: mmpool.c,v 1.29 2004/09/22 06:23:54 mmondor Exp $ */ - -/* - * Copyright (C) 2001-2004, Matthew Mondor - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Matthew Mondor. - * 4. The name of Matthew Mondor may not be used to endorse or promote - * products derived from this software without specific prior written - * permission. - * 5. Redistribution of source code may not be released under the terms of - * any GNU Public License derivate. - * - * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - - - -#include -#include -#include - -#include -#include -#include -#include -#include - - - -MMCOPYRIGHT("@(#) Copyright (c) 2001-2004\n\ -\tMatthew Mondor. All rights reserved.\n"); -MMRCSID("$Id: mmpool.c,v 1.29 2004/09/22 06:23:54 mmondor Exp $"); - - - -/* DEFINITIONS */ - -#define BPAGE_SIZE ((size_t)OALIGN_CEIL(sizeof(bpage_t), long)) - -/* Define MMPOOLDEBUG and DEBUG for extremely verbose diagnostics */ -#ifdef MMPOOLDEBUG -#define POOL_DEBUG DEBUG_PRINTF -#else -#define POOL_DEBUG(s...) ; -#endif - - - -/* STATIC FUNCTION PROTOTYPES */ - -static bpage_t * pool_page_create(const char *, pool_t *); -static bpage_t * pool_page_destroy(bpage_t *); - - - -/* STATIC FUNCTIONS */ - -/* Creates a new page of objects, and calls the constructor function for each - * object of the new page if needed. Returns NULL on failure, or the new - * bpage_t pointer on success. - */ -static bpage_t * -pool_page_create(const char *func, pool_t *pool) -{ - register size_t nodesize = pool->nodesize; - register u_int8_t *ptr, *toptr; - register bpage_t *page; - register list_t *list; - - if ((page = pool->malloc(pool->pagesize)) == NULL) { - syslog(LOG_NOTICE, - "%s - Out of memory allocating page for pool_t (%s)", - func, pool->label); - return NULL; - } - - /* Initialize bpage_t */ - page->magic = MAGIC_PAGE; - page->pool = pool; - DLIST_INIT(&page->objects); - - /* Create all objects of that bpage_t, inserting them into it's list_t. - * If any object creation fails (it's optional construtor function can - * fail), destroy the page and return NULL. - */ - if (pool->create != NULL) { - for (ptr = toptr = (u_int8_t *)page, ptr += BPAGE_SIZE, - toptr += pool->pagesize, list = &page->objects; - ptr + nodesize < toptr; ptr += nodesize) { - ((pnode_t *)ptr)->magic = 0; - ((pnode_t *)ptr)->page = page; - if (!pool->create((pnode_t *)ptr)) { - syslog(LOG_NOTICE, "%s - Page object constructor function " - "returned error for pool_t (%s)", - func, pool->label); - return pool_page_destroy(page); - } - DLIST_APPEND(list, (node_t *)ptr); - } - } else { - for (ptr = toptr = (u_int8_t *)page, ptr += BPAGE_SIZE, - toptr += pool->pagesize, list = &page->objects; - ptr + nodesize < toptr; ptr += nodesize) { - ((pnode_t *)ptr)->magic = 0; - ((pnode_t *)ptr)->page = page; - DLIST_APPEND(list, (node_t *)ptr); - } - } - - return page; -} - -/* Destroys a page previously created using pool_page_create(), calling the - * destructor function for each object of the page if necessary. Returns NULL. - */ -static bpage_t * -pool_page_destroy(bpage_t *page) -{ - register pnode_t *pnode; - register void (*destroy)(pnode_t *); - - if ((destroy = (page->pool->destroy)) != NULL) { - /* We need to destroy all objects */ - DLIST_FOREACH(&page->objects, pnode) - destroy(pnode); - } - - page->pool->free(page); - - return NULL; -} - - - -/* PUBLIC FUNCTIONS */ - -/* Initializes a memory pool for efficient management of fixed sized nodes. - * returns TRUE on success or FALSE on failure. - */ -bool -pool_init(pool_t *pool, const char *label, void *(*mallocfunc)(size_t), - void (*freefunc)(void *), bool (*create)(pnode_t *), - void (*destroy)(pnode_t *), size_t nodesize, u_int32_t nodesperpage, - u_int32_t minpages, u_int32_t maxpages) -{ - bool ok = FALSE; - - if (DEBUG_FALSE(POOL_VALID(pool))) { - DEBUG_PRINTF("pool_init", "Pool already initialized (%p = %s)", - pool, pool->label); - return FALSE; - } - - if (DEBUG_TRUE(pool != NULL && mallocfunc != NULL && freefunc != NULL && - ((create != NULL && destroy != NULL) || - (void *)create == (void *)destroy) && - nodesize != 0 && nodesperpage > 0)) { - register size_t ns = (size_t)OALIGN_CEIL(nodesize, long); - register size_t ps = (BPAGE_SIZE + ((nodesperpage + 1) * ns)); - - pool->magic = 0; - pool->label = label; - pool->malloc = mallocfunc; - pool->free = freefunc; - pool->create = create; - pool->destroy = destroy; - pool->nodesize = ns; - pool->minpages = minpages; - pool->maxpages = maxpages; - pool->nodesperpage = nodesperpage; - pool->pagesize = ps; - pool->avgtotal = pool->avgcnt = minpages; - DLIST_INIT(&pool->pages); - DLIST_INIT(&pool->fpages); - DLIST_INIT(&pool->epages); - - /* Allocate minimum number of pages, if needed. We insert them into - * the empty pages list, but minpages will be honored as such to - * never free pages below it. - */ - for (; minpages > 0; minpages--) { - register bpage_t *p; - - if ((p = pool_page_create("pool_init", pool)) == NULL) - break; - DLIST_APPEND(&pool->epages, (node_t *)p); - } - - /* Validate this pool */ - pool->magic = MAGIC_POOL; - - /* Have we failed to allocate any needed pages? If so, destroy pool - * and return FALSE. - */ - if (minpages == 0) - ok = TRUE; - else if (minpages < pool->minpages) - (void) pool_destroy(pool); - - } else - DEBUG_PRINTF("pool_init", "Invalid parameters"); - - if (ok) - POOL_DEBUG("pool_init", "(%p = %s) initialized", pool, pool->label); - - return ok; -} - - -/* Destroys a pool which previously has been created by pool_init(), and frees - * any resources it uses (all the nodes it created become invalid). - * Returns TRUE on success, or FALSE if the pool pointer is invalid. - */ -bool -pool_destroy(pool_t *pool) -{ - bool ok = FALSE; - - /* Make sure pool_t is valid */ - if (DEBUG_TRUE(POOL_VALID(pool))) { - register bpage_t *p, *t; - - POOL_DEBUG("pool_destroy", "(%p = %s) destroying", pool, pool->label); - - /* Destroy all pages of all lists */ - for (p = DLIST_TOP(&pool->pages); p != NULL; p = t) { - t = DLIST_NEXT(p); - (void) pool_page_destroy(p); - } - for (p = DLIST_TOP(&pool->fpages); p != NULL; p = t) { - t = DLIST_NEXT(p); - (void) pool_page_destroy(p); - } - for (p = DLIST_TOP(&pool->epages); p != NULL; p = t) { - t = DLIST_NEXT(p); - (void) pool_page_destroy(p); - } - - /* Invalidate pool_t */ - pool->magic = 0; - - ok = TRUE; - } else - DEBUG_PRINTF("pool_destroy", "Invalid pool_t pointer (%p)", pool); - - return ok; -} - - -/* Allows to very efficiently allocate a single node from the specified pool, - * optionally initializing it's memory to zeros. Returns the newly allocated - * node pointer on success, or NULL on error. - */ -pnode_t * -pool_alloc(pool_t *pool, bool zero) -{ - pnode_t *pnode = NULL; - - if (DEBUG_TRUE(POOL_VALID(pool))) { - if (DEBUG_TRUE(!zero || pool->create == NULL)) { - register bpage_t *page; - - /* Are there any partially used pages? */ - if ((page = DLIST_TOP(&pool->pages)) == NULL) { - /* No, we thus attempt to move a page from our empty pages - * cache back into our usable pages list. The order of the - * usable page list becomes irrelevant at DLIST_SWAP() since - * it will be the only node. - */ - POOL_DEBUG("pool_alloc", "(%p = %s) No usable page", - pool, pool->label); - if ((page = DLIST_TOP(&pool->epages)) == NULL) { - /* No more free pages in our cache neither. If maxpages is - * set and not yet reached, we need to create a new page. - * If we can't, return NULL for error. - */ - POOL_DEBUG("pool_alloc", - "(%p = %s) No empty page", pool, pool->label); - if (pool->maxpages == 0 || - DLIST_NODES(&pool->fpages) < pool->maxpages) { - POOL_DEBUG("pool_alloc", - "(%p = %s) Creating new usable page", - pool, pool->label); - if ((page = pool_page_create("pool_alloc", pool)) - == NULL) - return NULL; - DLIST_APPEND(&pool->pages, (node_t *)page); - } else - return NULL; - } else { - POOL_DEBUG("pool_alloc", "(%p = %s) swapping page " - "(%p) from empty to usable", - pool, pool->label, page); - DLIST_SWAP(&pool->pages, &pool->epages, (node_t *)page, - FALSE); - } - } else { - POOL_DEBUG("pool_alloc", - "(%p = %s) Allocating from usable page", - pool, pool->label); - } - - /* now points to a page we know we can at least get a free - * object node from. Obtain one and unlink it. - */ - pnode = DLIST_TOP(&page->objects); - DLIST_UNLINK(&page->objects, (node_t *)pnode); - - /* Have we obtained the last available object from this page? - * If so, move the page to the full pages list. The order of the - * full pages list nodes is irrelevant, since we don't know which - * of those pages are more likely to be swapped to the usable - * pages list first, and we won't run through that list, except at - * pool_destroy(). - */ - if (DLIST_NODES(&page->objects) == 0) { - POOL_DEBUG("pool_alloc", "(%p = %s) swapping page (%p) " - "from usable to full list", - pool, pool->label, page); - DLIST_SWAP(&pool->fpages, &pool->pages, (node_t *)page, - FALSE); - } - - /* If requested, zero object. This is stupid if a constructor and - * destructor are used, but can be useful otherwise. - */ - if (zero) { - page = pnode->page; - mm_memclr(pnode, pool->nodesize); - pnode->page = page; - } - - /* Mark this node as a valid allocated object */ - pnode->magic = MAGIC_PNODE; - - POOL_DEBUG("pool_alloc", - "(%p = %s) got object (%p) from page (%p)", - pool, pool->label, pnode, page); - - } else - DEBUG_PRINTF("pool_alloc", - "Will not zero a constructed object of pool (%s)", - pool->label); - } else - DEBUG_PRINTF("pool_alloc", "Invalid pool_t pointer (%p)", pool); - - return pnode; -} - - -/* Efficiently frees the specified node back to the pool it was allocated from. - * Returns NULL. Uses heuristics keeping statistics to determine when to - * actually shrink the memory blocks internally used by the pool, so that - * frequently growing and shrinking pools will remain large for scalability. - * This also makes a big difference when constructors and destructors are used - * and need to execute a significant amount of code. - */ -pnode_t * -pool_free(pnode_t *pnode) -{ - - if (DEBUG_TRUE(PNODE_VALID(pnode))) { - register bpage_t *page = pnode->page; - register pool_t *pool = page->pool; - register u_int32_t count; - - POOL_DEBUG("pool_free", - "(%p = %s) freeing object (%p) from page (%p)", - pool, pool->label, pnode, page); - - /* Invalidate object */ - pnode->magic = 0; - - /* Record how many nodes there currently are in our page's object - * list, to be used later on - */ - count = DLIST_NODES(&page->objects); - - /* Add node back to it's page's object list. Insert it so that we - * favor reuse of recently used objects soon. - */ - DLIST_INSERT(&page->objects, (node_t *)pnode); - - /* Was this page full before we inserted our node? If so, page is in - * full pages list. Move it to the usable pages list. Insert it to - * favor reuse of recently used pages soon (this page will soon return - * back to the full pages list). - */ - if (count == 0) { - POOL_DEBUG("pool_free", - "(%p = %s) swapping page (%p) from full to usable list", - pool, pool->label, page); - DLIST_SWAP(&pool->pages, &pool->fpages, (node_t *)page, TRUE); - } else { - /* Did we cause our node insertion to totally free back the page? - * If so, the page is on the usable free list, but move it back to - * the empty pages list. Insert it to favor reuse of recently used - * pages soon (this page will be the first to get used again when - * the usable pages list needs pages). - */ - if (++count == pool->nodesperpage) { - POOL_DEBUG("pool_free", "(%p = %s) swapping page (%p) " - "from usable to empty list", - pool, pool->label, page); - DLIST_SWAP(&pool->epages, &pool->pages, (node_t *)page, TRUE); - } - } - - if ((pool->minpages < pool->maxpages) || - (pool->minpages == 0 && pool->maxpages == 0)) { - register int exceeding; - - /* This pool_t is allowed to shrink. Maintain average pages usage - * to prevent destroying our pages in the empty pages list unless - * we should. - */ - count = DLIST_NODES(&pool->pages) + DLIST_NODES(&pool->fpages); - pool->avgtotal += count; - pool->avgcnt++; - - /* Using * 8 here means that pool_free() needs to at least be - * called to release as much objects as can fit into eight full - * pages in order to execute the following block. But of course, - * this does not mean that eight pages will recently have been - * freed, since allocations probably also occured meanwhile. - * XXX - * It would actually be nice to perhaps use a time delay here - * instead of this. However, since nodes can be freed quite often, - * it would be a burden to use a syscall all the time. What we - * could do, however, is update the current time every second via - * setitimer(2) or such, however this can clobber application - * timers. We perhaps could let the application call a function to - * update the time once in a while when it judges it adequate, - * however... - */ - if (pool->avgcnt > pool->nodesperpage * 8) { - /* Do statistics suggest that we should shrink the pool? - * If so, free pages from our empty pages cache back to the - * system, destroying their objects if necessary. We'll make - * sure to at least leave a one page hysterisis for better - * performance. - */ - if ((exceeding = (count + DLIST_NODES(&pool->epages) - 1) - - (pool->avgtotal / pool->avgcnt)) > 0) { - register list_t *epages = &pool->epages; - - POOL_DEBUG("pool_alloc()", - "(%p = %s) Destroying %d exceeding pages", - pool, pool->label, exceeding); - - /* Preferably free pages which haven't been used - * recently - */ - for (; exceeding > 0 && - (page = DLIST_BOTTOM(epages)) != NULL; - exceeding--) { - POOL_DEBUG("pool_free", - "(%p = %s) destroying page (%p)", - pool, pool->label, page); - DLIST_UNLINK(epages, (node_t *)page); - (void) pool_page_destroy(page); - } - } - /* Reset statistics */ - pool->avgcnt = 1; - pool->avgtotal = count; - } - } - } else - DEBUG_PRINTF("pool_free", "Invalid pnode_t pointer (%p)", pnode); - - return NULL; -} diff --git a/mmsoftware/mmlib/mmpool.h b/mmsoftware/mmlib/mmpool.h deleted file mode 100644 index a5e60f0..0000000 --- a/mmsoftware/mmlib/mmpool.h +++ /dev/null @@ -1,111 +0,0 @@ -/* $Id: mmpool.h,v 1.5 2004/09/07 06:20:41 mmondor Exp $ */ - -/* - * Copyright (C) 2001-2004, Matthew Mondor - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Matthew Mondor. - * 4. The name of Matthew Mondor may not be used to endorse or promote - * products derived from this software without specific prior written - * permission. - * 5. Redistribution of source code may not be released under the terms of - * any GNU Public License derivate. - * - * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - - - - -#ifndef MM_POOL_H -#define MM_POOL_H - - - -#include -#include -#include - - - -typedef struct pnode pnode_t; -typedef struct bpage bpage_t; -typedef struct pool pool_t; - - - -#define MAGIC_POOL 0x504f4f4c /* POOL */ -#define MAGIC_PAGE 0x50414745 /* PAGE */ -#define MAGIC_PNODE 0x504e4f44 /* PNOD */ - -#define POOL_VALID(p) ((p) != NULL && (p)->magic == MAGIC_POOL) -#define PNODE_VALID(p) ((p) != NULL && (p)->magic == MAGIC_PNODE && \ - (p)->page != NULL && (p)->page->magic == MAGIC_PAGE && \ - (p)->page->pool != NULL && (p)->page->pool->magic == MAGIC_POOL) - - - -/* Header structure of internally allocated/prepared page/blocks */ -struct bpage { - node_t node; - u_int32_t magic; - pool_t *pool; - list_t objects; -}; - -/* Header structure of individual pool objects */ -struct pnode { - node_t node; - u_int32_t magic; - bpage_t *page; -}; - -/* Pool control structure */ -struct pool { - pnode_t node; /* Hey, we never know, a pool_t of pool_t */ - u_int32_t magic; - const char *label; - void *(*malloc)(size_t); - void (*free)(void *); - int (*create)(pnode_t *); - void (*destroy)(pnode_t *); - size_t nodesize, pagesize; - u_int32_t minpages, maxpages, nodesperpage; - u_int32_t avgtotal, avgcnt; - /* Usable pages, totally full/busy pages, totally empty/free pages */ - list_t pages, fpages, epages; -}; - - - -/* Public API prototypes */ -extern bool pool_init(pool_t *, const char *, void *(*)(size_t), - void (*)(void *), bool (*)(pnode_t *), - void (*)(pnode_t *), size_t, - u_int32_t, u_int32_t, u_int32_t); -extern bool pool_destroy(pool_t *); -extern pnode_t * pool_alloc(pool_t *, bool); -extern pnode_t * pool_free(pnode_t *); - - - -#endif diff --git a/mmsoftware/mmlib/mmrc4.c b/mmsoftware/mmlib/mmrc4.c deleted file mode 100644 index c8f876c..0000000 --- a/mmsoftware/mmlib/mmrc4.c +++ /dev/null @@ -1,173 +0,0 @@ -/* $Id: mmrc4.c,v 1.11 2006/06/13 16:26:21 mmondor Exp $ */ - -/* This code illustrates a sample implementation - * of the Arcfour algorithm - * Copyright (c) April 29, 1997 Kalle Kaukonen. - * All Rights Reserved. - * - * Redistribution and use in source and binary forms, with or - * without modification, are permitted provided that this copyright - * notice and disclaimer are retained. - * - * THIS SOFTWARE IS PROVIDED BY KALLE KAUKONEN AND CONTRIBUTORS ``AS - * IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS - * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL KALLE - * KAUKONEN OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -/* - * Copyright (C) 2000-2004, Matthew Mondor - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Matthew Mondor. - * 4. The name of Matthew Mondor may not be used to endorse or promote - * products derived from this software without specific prior written - * permission. - * 5. Redistribution of source code may not be released under the terms of - * any GNU Public License derivate. - * - * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -/* Obviously, it is very important that every state use a totally new unique - * key. If two keys are ever the same, holes become available for deciphering. - * - Matt - */ - - - -#include -#include - -#include -#include -#include -#include -#include - - - -MMCOPYRIGHT("@(#) Copyright (c) 2000-2004\n\ -\tMatthew Mondor. All rights reserved.\n"); -MMRCSID("$Id: mmrc4.c,v 1.11 2006/06/13 16:26:21 mmondor Exp $"); - - - -void rc4_init(rc4_t *ctx, const unsigned char *key, size_t len, size_t skip) -{ - register unsigned int i, t, u, ki, si; - register unsigned int *state; - - state = ctx->state; - ctx->x = 0; - ctx->y = 0; - for (i = 0; i < 256; i++) - state[i] = i; - ki = si = 0; - for (i = 0; i < 256; i++) { - t = state[i]; - si = (si + key[ki] + t) & 0xff; - u = state[si]; - state[si] = t; - state[i] = u; - if (++ki >= len) - ki = 0; - } - ctx->magic = MAGIC_RC4; - - /* - * If skip is non-zero, skip the number of bytes from the keystream. - * It is recommended to at least skip 1024 bytes to avoid a known RC4 - * vulnerability. - */ - if (skip != 0) { - x = ctx->x; - y = ctx->y; - for (i = 0; i < skip; i++) { - x = (x + 1) & 0xff; - sx = state[x]; - y = (sx + y) & 0xff; - state[x] = state[y]; - state[y] = sx; - } - } -} - - -void rc4_destroy(rc4_t *ctx) -{ - if (DEBUG_TRUE(RC4_VALID(ctx))) - mm_memclr(ctx, sizeof(rc4_t)); -} - - -void rc4_encrypt(rc4_t *ctx, unsigned char *buf, size_t len) -{ - register unsigned int x, y, sx, sy; - register unsigned int *state; - register unsigned char *endbuf; - - state = ctx->state; - x = ctx->x; - y = ctx->y; - for (endbuf = buf + len; buf < endbuf; buf++) { - x = (x + 1) & 0xff; - sx = state[x]; - y = (sx + y) & 0xff; - state[x] = sy = state[y]; - state[y] = sx; - *buf ^= state[(sx + sy) & 0xff]; - } - ctx->x = x; - ctx->y = y; -} - - -void rc4_encrypt2(rc4_t *ctx, unsigned char *dst, const unsigned char *src, - size_t len) -{ - register unsigned int x, y, sx, sy; - register unsigned int *state; - register const unsigned char *endsrc; - - state = ctx->state; - x = ctx->x; - y = ctx->y; - for (endsrc = src + len; src < endsrc; src++, dst++) { - x = (x + 1) & 0xff; - sx = state[x]; - y = (sx + y) & 0xff; - state[x] = sy = state[y]; - state[y] = sx; - *dst = *src ^ state[(sx + sy) & 0xff]; - } - ctx->x = x; - ctx->y = y; -} diff --git a/mmsoftware/mmlib/mmrc4.h b/mmsoftware/mmlib/mmrc4.h deleted file mode 100644 index f3f2d06..0000000 --- a/mmsoftware/mmlib/mmrc4.h +++ /dev/null @@ -1,77 +0,0 @@ -/* $Id: mmrc4.h,v 1.9 2006/06/13 16:26:21 mmondor Exp $ */ - -/* - * Copyright (C) 2000-2004, Matthew Mondor - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Matthew Mondor. - * 4. The name of Matthew Mondor may not be used to endorse or promote - * products derived from this software without specific prior written - * permission. - * 5. Redistribution of source code may not be released under the terms of - * any GNU Public License derivate. - * - * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - - - - -#ifndef MM_RC4_H -#define MM_RC4_H - - - - -#include - -#include -#include - - - -#define MAGIC_RC4 0x52433400 /* RC4\0 */ - -#define rc4_decrypt rc4_encrypt -#define rc4_decrypt2 rc4_encrypt2 -#define RC4_VALID(r) ((r) != NULL && (r)->magic == MAGIC_RC4) - - - -typedef struct rc4 rc4_t; -struct rc4 { - pnode_t node; - u_int32_t magic; - int x, y, state[256]; -}; - - - -extern void rc4_init(rc4_t *, const unsigned char *, size_t, size_t); -extern void rc4_destroy(rc4_t *); -extern void rc4_encrypt(rc4_t *, unsigned char *, size_t); -extern void rc4_encrypt2(rc4_t *, unsigned char *, const unsigned char *, - size_t); - - - -#endif diff --git a/mmsoftware/mmlib/mmrc4util.3 b/mmsoftware/mmlib/mmrc4util.3 deleted file mode 100644 index f88d92f..0000000 --- a/mmsoftware/mmlib/mmrc4util.3 +++ /dev/null @@ -1,324 +0,0 @@ -.\" $Id: mmrc4util.3,v 1.8 2004/05/05 23:59:57 mmondor Exp $ -.\" -.\" Copyright (C) 2003-2004, Matthew Mondor -.\" All rights reserved. -.\" -.\" Redistribution and use in source and binary forms, with or without -.\" modification, are permitted provided that the following conditions -.\" are met: -.\" 1. Redistributions of source code must retain the above copyright -.\" notice, this list of conditions and the following disclaimer. -.\" 2. Redistributions in binary form must reproduce the above copyright -.\" notice, this list of conditions and the following disclaimer in the -.\" documentation and/or other materials provided with the distribution. -.\" 3. All advertising materials mentioning features or use of this software -.\" must display the following acknowledgement: -.\" This product includes software developed by Matthew Mondor. -.\" 4. The name of Matthew Mondor may not be used to endorse or promote -.\" products derived from this software without specific prior written -.\" permission. -.\" 5. Redistribution of source code may not be released under the terms of -.\" any GNU Public License derivate. -.\" -.\" THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR -.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES -.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. -.\" IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, -.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -.\" BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF -.\" USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF -.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -.\" -.Dd August 10, 2003 -.Dt MMRC4UTIL 3 -.Os mmsoftware -.Sh NAME -.Nm mmrc4util -.Nd Utility functions to use OpenSSL RC4 implementation with -.Xr mmfd 3 -.Sh SYNOPSIS -.Fd #include -.Fd #include -.Fd #include -.Fd #include -.Fd #include -.Ft void -.Fn rc4_load "RC4_KEY **key" "void **data" "const char *file" "size_t size" -.Ft void -.Fn rc4_random "RC4_KEY **key" "void **data" "size_t size" -.Ft void -.Fn rc4_free "RC4_KEY **key" "void **data" -.Ft void -.Fn rc4_create "RC4_KEY **key" "const void *data" "size_t size" -.Ft void -.Fn rc4_copy "RC4_KEY **ctxdest" "RC4_KEY *ctxorig" "void **datadest" \ -"const void *dataorig" "size_t datasize" -.Ft ssize_t -.Fn rc4_read "RC4_KEY *key" "fdbuf *fdb" "void *cryptbuf" "void *buf" \ -"size_t size" -.Ft ssize_t -.Fn rc4_write "RC4_KEY *key" "fdbuf *fdb" "void *cryptbuf" "const void *buf" \ -"size_t size" -.Sh DESCRIPTION -The -.Nm -library was especially designed to take advantage of the -.Xr mmheap 3 -.Fn secure_* -functions, and to help to provide secure transparent RC4 tunnels via an -.Xr mmfd 3 -library wrapper, with additional protection against connection highjacking -using checksums. -.Pp -The keys stored in memory will not be swapped out to disk because they are -stored in memory which is locked/wired in the physical memory using 4.4BSD -.Xr mlock 2 . -.Pp -Note that the RC4 block cipher uses private key cryptography, and should only -be assumed to be secure if the shared secret between the communicating parties -changes for each session. A handshake method like RSA, DSA or Diffie-Hellman, -or equivalent, should be used to agree on the new session key, when using such -private key based ciphers over a network. -.Ss FUNCTIONS -.Bl -tag -width indent -offset indent -.It void Fn rc4_load "RC4_KEY **key" "void **data" "const char *file" \ -"size_t size" -Permits to load and initialize a key from a file. The file is mapped in -locked memory as well while being read. -.Pp -.Fa key -should point to a pointer to an RC4_KEY context, which will be set to the -pointer to the newly created key context in secure memory on success, or -to NULL on failure. -.Pp -If -.Fa data -is non-NULL, the supplied pointer will also be set to a secure memory location -where the file contents will be stored, or will be set to NULL on failure to -allocate the necessary memory. -.Pp -.Fa file -points to an absolute fullpath to the key file to be loaded. -.Pp -.Fa size -specifies the size of the key, in bytes (not in bits, actually bits / 8). -.It void Fn rc4_random "RC4_KEY **key" "void **data" "size_t size" -Allows to create a random key of arbitrary size and store it in secure memory. -.Pp -.Fa key -should point to an RC4_KEY pointer, which will be set to the pointer of the new -memory region where the key context will be stored, or will be set to NULL -on failure. -.Pp -.Fa data -points to a void * which if non-NULL, will be set to the pointer of the new -memory region where the key data will be stored, or will be set to NULL on -failure. -.Pp -.Fa size -specifies the size of the key to be created, in bytes, not in bits. This size -is expected to be a multiple of 16 (128 bits). -.Pp -The "/dev/urandom" device is used to seed -.Xr srandom 3 -at every 128 bits of key generation. -.Xr random 3 -is then used as the pseudo-random number generator to generate each 128 bits -segments. -.It void Fn rc4_free "RC4_KEY **key" "void **data" -Invalidates and destroys the specified key and/or data. -.Pp -.Fa key , -if non-NULL, should point to an RC4_KEY pointer, which if non-NULL will cause -the key it points to, to be destroyed and freed, and the pointer to be reset -to NULL. -.Pp -.Fa data , -if non-NULL, should point to a void *, which if non-NULL will cause the data -to which it points to be destroyed and freed, and the pointer to be reset to -NULL. -.It void Fn rc4_create "RC4_KEY **key" "const void *data" "size_t size" -Attempts to create a new RC4_KEY context in secure memory, using the supplied -key data. This in fact mainly consists of a safer frontend to -.Xr RC4_set_key 3 . -.Pp -.Fa key -should point to an RC4_KEY pointer, which will be set to the new allocated and -initialized context on success, or to NULL on failure. -.Pp -.Fa data -points to a buffer holding the key data from which the context should be -created. -.Pp -.Fa size -specifies the length of the key data in bytes (not in bits). -.It void Fn rc4_copy "RC4_KEY **ctxdest" "RC4_KEY *ctxorig" "void **datadest" \ -"const void *dataorig" "size_t datasize" -Optionally makes an exact copy of the specified -.Fa ctxorig -context, if non-NULL, into a new independant secure memory location, and sets -the supplied -.Fa ctxdest -RC4_KEY pointer to the new context address, or to NULL on failure. -The original key is not affected. -.Pp -Also optionally makes a duplicate of the specified -.Fa dataorig -key data, if non-NULL, into a new independant secure memory location, and sets -the supplied -.Fa datadest -data pointer to the new copy address, or NULL on failure. -.Fa datasize -is then used to determine how many bytes to copy. -.It ssize_t Fn rc4_read "RC4_KEY *key" "fdbuf *fdb" "void *cryptbuf" \ -"void *buf" "size_t size" -Is a helper function to write -.Xr mmfd 3 -custom -.Xr read 2 -wrappers, and has support to use a checksum to detect data which may have been -inserted into the stream by a peer attempting a TCP connection hijacking -attempt. For this, it uses additionnal bytes to implement this data sanity -checking, and as such the fdbuf context is required to update the amount of -transfered bytes. -.Pp -.Fa key , -if non-NULL, specifies the RC4 key context to use to decrypt the incomming -data from the supplied filedescriptor. If NULL, the function behaves -identically to -.Xr read 2 . -.Pp -.Fa fdb -points to the -.Xr mmfd 3 -fdbuf context which is being used. This pointer is used to update the read -bytes counter of the context, because additionnal bytes are required for the -checksum data which will be processed transparently, but we want to account -those bytes, which -.Xr mmfd 3 -would not be able to do itself in this circumstance. The -.Fa fdb -pointer is also obviously used to determine which filedescriptor to use. -.Pp -.Fa cryptbuf -should point to a buffer in secure memory (see -.Xr mmheap 3 -man page), which will be used to store the encrypted part of the data which -is to be read, before it be uncrypted to -.Fa buf . -It should be large enough to accomodate -.Fa size . -Because -.Xr mmfd 3 -allows to set the I/O read/write buffer sizes, the required size should easily -be determined and set to the same buffer size. -.Pp -.Fa buf -points to the buffer where the incomming bytes should be stored. If under an -RC4 tunnel, the data will be unencrypted automatically before being stored -in this buffer. It is recommended that this buffer be in secure memory. -.Pp -.Fa size -tells the maximum number of bytes to read into -.Fa buf . -The additionnal checksum control information will be transparently discarded -and do not need to be taken into account when specifying -.Fa size . -If under an RC4 tunnel, this function will block, ignoring -.Xr poll 2 -EINTR events, until a successful block with a checksum has been read, unless -an error occurs. Note that it is problematic to call this function with a size -which is inferior to an actual block which was written at the other end using -.Fn rc4_write . -This is generally not a problem since the -.Xr mmfd 3 -library performs buffering, and will generally read and write using a maximum -fixed block size. -.It ssize_t Fn rc4_write "RC4_KEY *key" "fdbuf *fdb" "void *cryptbuf" \ -"const void *buf" "size_t size" -Is very similar to -.Fa rc4_read -and serves the same purpose. It transparently adds additionnal checksum -information to the bytes written out on the stream as necessary, for -.Fa rc4_read -to process back at reception to perform the sanity checking against TCP -connection hijacking. -.Pp -.Fa key , -if NULL, causes -.Fa rc4_write -to behave identically to -.Xr write 2 . -If non-NULL, specifies the RC4 key context to use to encrypt the outgoing -bytes. -.Pp -.Fa fdb -is used to update the -.Xd mmfd 3 -context to account for the additionnal bytes being output for sanity control, -and to know which filedescriptor to write to. -.Pp -.Fa cryptbuf -should point to a buffer in secure memory, corresponding to the size of the -I/O buffer size set for this -.Fa fdb . -It will be used so that the supplied -.Fa buf -need not be modified after a write; The buffer will be encrypted to -.Fa cryptbuf -before it actually gets written. -.Pp -.Fa buf -consists of the data which should be output on -.Fa fd , -and transparently encrypted if -.Fa key -is non-NULL . It is recommended that this buffer be in secure memory. -.Pp -.Fa size -specifies the number of bytes to be written out. The function will block, -even ignoring -.Xr poll 2 -EINTR events, until the data has been output, unless of course an error occurs. -.El -.Sh RETURN VALUES -.Bl -tag -width indent -offset indent -.It Xo -.Fn rc4_load , -.Fn rc4_random , -.Fn rc4_free , -.Fn rc4_create , -.Fn rc4_copy -.Xc -return nothing. -.It Xo -.Fn rc4_read , -.Fn rc4_write -.Xc -return the size of the successfully read or written block, or -1 on error. -.Sh SEE ALSO -.Xr mmfd 3 , -.Xr mmheap 3 , -.Xr mlock 2 , -.Xr rc4 3 , -.Xr read 2 , -.Xr write 2 , -.Xr poll 2 , -.Xr srandom 3 , -.Xr random 3 , -.Xr zero 4 . -.El -.Sh STANDARDS -None -.Sh HISTORY -.Nm -was written at first for the closed source PSFDEPd server project from -Matthew Mondor, but eventually was exported publically. -.Sh AUTHORS -.Nm -was written by Matthew Mondor and is -Copyright (c) 2003-2004, Matthew Mondor, All rights reserved. -.Sh BUGS -Please report any bug to mmondor@accela.net diff --git a/mmsoftware/mmlib/mmrc4util.c b/mmsoftware/mmlib/mmrc4util.c deleted file mode 100644 index d08d31c..0000000 --- a/mmsoftware/mmlib/mmrc4util.c +++ /dev/null @@ -1,449 +0,0 @@ -/* $Id: mmrc4util.c,v 1.14 2006/06/13 17:17:02 mmondor Exp $ */ - -/* - * Copyright (C) 2003-2004, Matthew Mondor - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Matthew Mondor. - * 4. The name of Matthew Mondor may not be used to endorse or promote - * products derived from this software without specific prior written - * permission. - * 5. Redistribution of source code may not be released under the terms of - * any GNU Public License derivate. - * - * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - - - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - - - -MMCOPYRIGHT("@(#) Copyright (c) 2003-2004\n\ -\tMatthew Mondor. All rights reserved.\n"); -MMRCSID("$Id: mmrc4util.c,v 1.14 2006/06/13 17:17:02 mmondor Exp $"); - - - -static bool init(void); -static ssize_t rc4_bread(int, void *, size_t); -static ssize_t rc4_bwrite(int, const void *, size_t); - - - -static size_t pagesize = 0; - - - -static bool init(void) -{ - bool ok = FALSE; - - if (pagesize == 0) { - if ((pagesize = (size_t)sysconf(_SC_PAGESIZE)) != -1) - ok = TRUE; - } else - ok = TRUE; - - return ok; -} - - -/* Safely load a private key from the specified file, in a way which will - * prevent the key contents from being swapped out to the swap device. - */ -void rc4_load(RC4_KEY **key, void **data, const char *file, size_t size) -{ - void *mem; - int fd; - size_t rsize; - - if (key == NULL) - return; - - *key = NULL; - if (!init()) - return; - - rsize = (size_t)BALIGN_CEIL(size, pagesize); - - if (DEBUG_FALSE(file == NULL)) { - DEBUG_PRINTF("rc4_load", "file == NULL"); - return; - } - - if ((fd = open(file, O_RDONLY)) != -1) { - if ((mem = mmap(NULL, rsize, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, - 0)) != MAP_FAILED) { - if (mlock(mem, rsize) == 0) { - (void) close(fd); - fd = -1; - if ((*key = secure_malloc(sizeof(RC4_KEY))) != NULL) - RC4_set_key(*key, size, mem); - else - syslog(LOG_NOTICE, "rc4_load() - secure_malloc(%d)", - sizeof(RC4_KEY)); - if (data != NULL) { - if ((*data = secure_malloc(size)) != NULL) - mm_memcpy(*data, mem, size); - else - syslog(LOG_NOTICE, "rc4_load() - secure_malloc(%d)", - (int)size); - } - mm_memclr(mem, rsize); - munlock(mem, rsize); - } else - syslog(LOG_NOTICE, "rc4_load() - mlock(%p, %d)", - mem, (int)rsize); - (void) munmap(mem, rsize); - } else - syslog(LOG_NOTICE, "rc4_load() - mmap(%p, %d)", - mem, (int)rsize); - if (fd != -1) - (void) close(fd); - } else - syslog(LOG_NOTICE, "rc4_load() - open(%s)", file); -} - - -/* Creates a random block of memory, and returns both the block and associated - * rc4 key. This system also uses safe memory. - */ -void rc4_random(RC4_KEY **key, void **data, size_t size) -{ - int fd, i, i2; - int rndsize = size / 16; - unsigned long seed; - long *rnd; - - *key = *data = NULL; - - /* We seed the random generator using an unsigned long value from the - * "/dev/urandom" device at every 128 bits (16 bytes, generally 4 longs). - * We use 4.2BSD random(3) which yields better results than ANSI/POSIX - * rand(3) as the PRNG. Hmm mmap(2) did not seem to work with - * "/dev/urandom" for some reason, so we just call read(2) as needed now. - */ - if ((fd = open("/dev/urandom", O_RDONLY)) != -1) { - if (data != NULL && (*data = secure_malloc(size)) != NULL) { - rnd = *data; - for (i = 0; i < rndsize; i++) { - if (read(fd, &seed, sizeof(unsigned long)) == - sizeof(unsigned long)) - srandom(seed); - else - syslog(LOG_NOTICE, "rc4_random() - read(/dev/urandom)"); - for (i2 = 0; i2 < (16 / sizeof(long)); i2++) - *rnd++ = random(); - } - if (key != NULL) { - if ((*key = secure_malloc(sizeof(RC4_KEY))) != NULL) - RC4_set_key(*key, size, - (const unsigned char *)*data); - else - syslog(LOG_NOTICE, "rc4_random() - secure_malloc(key)"); - } - } else - syslog(LOG_NOTICE, "rc4_random() - secure_malloc(data)"); - (void) close(fd); - } else - syslog(LOG_NOTICE, "rc4_random() - open(/dev/urandom)"); -} - - -/* Frees an rc4 key, and/or the associated random buffer. Actually only - * provided as an elegant API around secure_free(). - */ -void rc4_free(RC4_KEY **key, void **data) -{ - if (key != NULL && *key != NULL) { - secure_free(*key); - *key = NULL; - } - if (data != NULL && *data != NULL) { - secure_free(*data); - *data = NULL; - } -} - - -/* Frontend to RC4_set_key() which allocates the key memory using - * secure_malloc(). - * - * XXX For better performance it would be nice to use mmpool(3) on a block - * of memory allocated using secure_malloc(). However, this requires knowing - * the maximum number of expected concurrent sessions so that the buffer be - * pre-allocated large enough. Of course, keys would need to be zeroed before - * being freed using pool_free() as well... To know how much memory to - * allocate, the application could call rc4_init(), possibly, with required - * number of maximum keys. This would also obsolete our init() function. - */ -void rc4_create(RC4_KEY **key, const void *data, size_t len) -{ - if (key != NULL && *key == NULL) { - if ((*key = secure_malloc(sizeof(RC4_KEY))) != NULL) - RC4_set_key(*key, (int)len, (const unsigned char *)data); - } -} - - -/* Optionally duplicates a key context, automatically allocating safe memory - * for the new one. Optionally duplicates the supplied key data also. - */ -void rc4_copy(RC4_KEY **key, RC4_KEY *fromkey, void **data, - const void *fromdata, size_t size) -{ - if (key != NULL && *key == NULL && fromkey != NULL) { - if ((*key = secure_malloc(sizeof(RC4_KEY))) != NULL) - mm_memcpy(*key, fromkey, sizeof(RC4_KEY)); - } - if (data != NULL && *data == NULL && fromdata != NULL && size > 0) { - if ((*data = secure_malloc(size)) != NULL) - mm_memcpy(*data, fromdata, size); - } -} - - -/* Wrapper functions for the mmfd library, internally using RC4 cipher if any - * is set for the current session. Moreover, because the cipher key is hard - * to guess, but that random data would still normally result in unexpected - * results at successful decryption, we use the advantage of the cipher to also - * obscure the necessary data header which we add, consisting of a checksum - * of the data followed by the length of the block for which the checksum - * accounts. This allows protection against an eavesdropper attack which could - * use TCP sequence prediction or sniffing to input spoofed TCP packets into - * the stream, which would result in unexpected data at input. This way, the - * stream's security does not only rely on the clear text TCP/IP headers to - * prevent connection hijacking. The checksum/length header is of course, - * also encrypted, and thus difficult to forge for an attacker, since the - * checksum is calculated from the data before it was encrypted. - * - * XXX We only use a simple checksum, but we encode it using RC4 as well. - * We could have used harder to crack checksum or hash function but it would - * be considerably slower than the current one. CRC32 or even MD5 or SHA1 could - * also be used. - */ - - -ssize_t rc4_read(RC4_KEY *key, fdbuf *fdb, void *cryptbuf, void *buf, - size_t size) -{ - ssize_t ret = 0; - int fd = fdb->fd; - - if (size > 0) { - if (key != NULL) { - u_int32_t header[2]; - - /* Obtain header which rc4_write() setup for us. This assumes - * that we generally write and read blocks of a similar size at - * once. This is what actually happens with buffering. If we - * attempt to read a larger block than the actual block which was - * written, we only read the data which the header accounted for, - * so that the next read operation would still obtain the header - * of the next written block. - */ - if (rc4_bread(fd, cryptbuf, sizeof(u_int32_t) * 2) == - sizeof(u_int32_t) * 2) { - RC4(key, sizeof(u_int32_t) * 2, cryptbuf, (void *)header); - *header = (u_int32_t)ntohl(*header); - header[1] = (u_int32_t)ntohl(header[1]); - /* Verify size */ - if (header[1] <= size) { - size = header[1]; - if ((ret = rc4_bread(fd, cryptbuf, size)) == size) { - size_t i; - u_int32_t csum; - const unsigned char *b; - - RC4(key, size, cryptbuf, buf); - /* Verify checksum */ - for (b = buf, csum = 0, i = 0; i < size; i++) - csum += b[i]; - if (csum != *header) { - syslog(LOG_NOTICE, "rc4_read() - " - "Invalid hdr checksum (c = %u, hc = %u)", - csum, *header); - ret = -1; - } - } - } else { - syslog(LOG_NOTICE, "rc4_read() - " - "Invalid hdr size (s = %u, hs = %u)", - size, header[1]); - ret = -1; - } - } - if (ret > 0) { - /* Update fdb context for extraneous bytes */ - fdb->context.rbytes += sizeof(u_int32_t) * 2; - if (fdb->gcontext != NULL) - fdb->gcontext->rbytes += sizeof(u_int32_t) * 2; - } - } else - ret = read(fd, buf, size); - } - - return ret; -} - - -ssize_t rc4_write(RC4_KEY *key, fdbuf *fdb, void *cryptbuf, const void *buf, - size_t size) -{ - ssize_t ret = 0; - int fd = fdb->fd; - - if (size > 0) { - if (key != NULL) { - u_int32_t header[2]; - size_t i; - const unsigned char *b; - - /* Calculate a checksum for the block, and write out the serialized - * checksum with block length for rc4_read(). - */ - for (b = buf, *header = 0, i = 0; i < size; i++) - *header += b[i]; - *header = (u_int32_t)htonl(*header); - header[1] = (u_int32_t)htonl(size); - RC4(key, sizeof(u_int32_t) * 2, (void *)header, cryptbuf); - if (rc4_bwrite(fd, cryptbuf, sizeof(u_int32_t) * 2) != - sizeof(u_int32_t) * 2) { - syslog(LOG_NOTICE, "rc4_write() - Error writing header!"); - ret = -1; - } else { - /* Write out actual block */ - RC4(key, size, buf, cryptbuf); - ret = rc4_bwrite(fd, cryptbuf, size); - } - if (ret > 0) { - /* Update fdb context for extraneous bytes */ - fdb->context.wbytes += sizeof(u_int32_t) * 2; - if (fdb->gcontext != NULL) - fdb->gcontext->wbytes += sizeof(u_int32_t) * 2; - } - } else - ret = write(fd, buf, size); - } - - return ret; -} - - -/* These functions are used so that block read and write operations are sure - * to be synchronized. We know that poll(2) first returned with success for - * the following direction, so we can start with the actual operation, and - * then loop as required polling again. Moreover, these ignore the EINTR - * events. Of course, an important timer which needs to break the operation - * will be able to do so by taking direct action such as sending SIGTERM, - * calling exit(3), etc. Other timers should normally set flags for the - * main state swicher loop to verify for. - */ - - -static ssize_t rc4_bread(int fd, void *buf, size_t size) -{ - unsigned char *b; - ssize_t cur; - struct pollfd fds[] = { - {-1, POLLIN | POLLERR | POLLHUP | POLLNVAL, 0} - }; - - fds[0].fd = fd; - for (b = buf, cur = 0; ;) { - ssize_t ret; - int sel = 0; - - while ((ret = read(fd, b, size - cur)) == -1 && errno == EINTR) ; - if (ret > 0) { - cur += ret; - b += ret; - } else { - cur = ret; - break; - } - if (cur < size) { - while (!(sel & POLLIN)) { - while ((sel = poll(fds, 1, -1)) == -1 && errno == EINTR) ; - if (sel != 1) - return -1; - sel = fds[0].revents; - if (sel & POLLERR || sel & POLLHUP || sel & POLLNVAL) - return -1; - } - } else - break; - } - - return cur; -} - - -static ssize_t rc4_bwrite(int fd, const void *buf, size_t size) -{ - const unsigned char *b; - ssize_t cur; - struct pollfd fds[] = { - {-1, POLLOUT | POLLERR | POLLHUP | POLLNVAL, 0} - }; - - fds[0].fd = fd; - for (b = buf, cur = 0; ;) { - ssize_t ret; - int sel = 0; - - if ((ret = write(fd, b, size - cur)) > 0) { - cur += ret; - b += ret; - } else { - cur = ret; - break; - } - if (cur < size) { - while (!(sel & POLLOUT)) { - while ((sel = poll(fds, 1, -1)) == -1 && errno == EINTR) ; - if (sel != 1) - return -1; - sel = fds[0].revents; - if (sel & POLLERR || sel & POLLHUP || sel & POLLNVAL) - return -1; - } - } else - break; - } - - return cur; -} diff --git a/mmsoftware/mmlib/mmrc4util.h b/mmsoftware/mmlib/mmrc4util.h deleted file mode 100644 index e973979..0000000 --- a/mmsoftware/mmlib/mmrc4util.h +++ /dev/null @@ -1,61 +0,0 @@ -/* $Id: mmrc4util.h,v 1.5 2004/06/01 19:11:57 mmondor Exp $ */ - -/* - * Copyright (C) 2003-2004, Matthew Mondor - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Matthew Mondor. - * 4. The name of Matthew Mondor may not be used to endorse or promote - * products derived from this software without specific prior written - * permission. - * 5. Redistribution of source code may not be released under the terms of - * any GNU Public License derivate. - * - * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef MM_RC4UTIL_H -#define MM_RC4UTIL_H - - - -#include - -#include - -#include -#include - - - -extern void rc4_load(RC4_KEY **, void **, const char *, size_t); -extern void rc4_random(RC4_KEY **, void **, size_t); -extern void rc4_free(RC4_KEY **, void **); -extern void rc4_create(RC4_KEY **, const void *, size_t); -extern void rc4_copy(RC4_KEY **, RC4_KEY *, void **, const void *, - size_t); -extern ssize_t rc4_read(RC4_KEY *, fdbuf *, void *, void *, size_t); -extern ssize_t rc4_write(RC4_KEY *, fdbuf *, void *, const void *, size_t); - - - -#endif diff --git a/mmsoftware/mmlib/mmreadcfg.c b/mmsoftware/mmlib/mmreadcfg.c deleted file mode 100644 index 7dd9305..0000000 --- a/mmsoftware/mmlib/mmreadcfg.c +++ /dev/null @@ -1,491 +0,0 @@ -/* $Id: mmreadcfg.c,v 1.20 2006/06/13 17:17:02 mmondor Exp $ */ - -/* - * Copyright (C) 1991-2004, Matthew Mondor - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Matthew Mondor. - * 4. The name of Matthew Mondor may not be used to endorse or promote - * products derived from this software without specific prior written - * permission. - * 5. Redistribution of source code may not be released under the terms of - * any GNU Public License derivate. - * - * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - - - - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - - - - -MMCOPYRIGHT("@(#) Copyright (c) 1991-2004\n\ -\tMatthew Mondor. All rights reserved.\n"); -MMRCSID("$Id: mmreadcfg.c,v 1.20 2006/06/13 17:17:02 mmondor Exp $"); - - - - -const char *const mmreadcfg_strings[CRE_MAX] = { - "Unknown error", - "Config file not found", - "Out of memory", - "Illegal keyword", - "String too long", - "String too short", - "Value too high", - "Value too low", - "Not a boolean value (TRUE/FALSE)", - "Required keyword not found", - "Keyword defined more than once", - "Keyword too long", - "Unmatched quote", - "No keyword data/value", - "Unexpected end of file" -}; - - - -bool -mmreadcfg(cres_t *res, carg_t *arg, const char *cfg) -{ - carg_t *argptr; - pool_t pool; - hashtable_t table; - struct CNode *nod; - int fd, line; - bool ret, quoted, cont; - char *file = NULL, *cptr, *tptr; - struct stat st; - - /* Clear results structure */ - res->CR_Keyword = NULL; - res->CR_Err = 0; - res->CR_Line = -1; - *(res->CR_Data) = '\0'; - /* Default return code */ - ret = FALSE; - - /* First make sure that file exists and is readable, then map it */ - if ((fd = open(cfg, O_RDONLY)) == -1) { - res->CR_Err = CRE_NOT_FOUND; - goto end; - } - if ((fstat(fd, &st)) == -1) { - res->CR_Err = CRE_NOT_FOUND; - goto end; - } - if ((file = mmap(NULL, (size_t)st.st_size, PROT_READ | PROT_WRITE, - MAP_FILE | MAP_PRIVATE, fd, (off_t)0)) == MAP_FAILED) { - res->CR_Err = CRE_OUT_OF_MEM; - goto end; - } - - /* Setup our hash table of keywords for fast lookup */ - if (!pool_init(&pool, "keyword_pool", malloc, free, NULL, NULL, - sizeof(struct CNode), 8192 / sizeof(struct CNode), 0, 0)) { - res->CR_Err = CRE_OUT_OF_MEM; - goto end; - } - if (!hashtable_init(&table, "keyword_table", - HT_DEFAULT_CAPACITY, HT_DEFAULT_FACTOR, malloc, free, - mm_memcmp, mm_memhash32, TRUE)) { - res->CR_Err = CRE_OUT_OF_MEM; - goto end; - } - for (argptr = arg; argptr->CA_Type != CAT_END; argptr++) { - if ((nod = (struct CNode *)pool_alloc(&pool, FALSE)) == NULL) { - res->CR_Err = CRE_OUT_OF_MEM; - goto end; - } - (void) mm_strncpy(nod->keyword, argptr->CA_Keyword, CKW_MAX); - mm_strupper(nod->keyword); - nod->args = argptr; - if (!hashtable_link(&table, (hashnode_t *)nod, nod->keyword, - mm_strlen(nod->keyword), TRUE)) { - /* Programming bug */ - res->CR_Err = CRE_MULT_DEF_KEYWD; - res->CR_Keyword = argptr; - DEBUG_PRINTF("mmreadcfg", - "Duplicate keyword! (%s)", nod->keyword); - goto end; - } - } - - /* Parse file. We expect keyword/data pairs and allow quoted elements - * to wrap lines. We unset the pointer to NULL when parsing for a - * keyword, and otherwise set it when parsing for a data element. - * When encountering an ';' or '#' character outside of a pair of quotes - * the rest of the line is ignored. - */ - for (line = 1, nod = NULL, cptr = file, tptr = file + (size_t)st.st_size; - cptr < tptr;) { - char *wptr, oc; - size_t len; - - /* Parse for an element, consisting of either a blank separated, - * or quote enclosed one. First skip any leading blanks or comments, - * as well as '=' characters. - */ - quoted = FALSE; - for (; cptr < tptr && *cptr != '\n' && - (isspace((int)*cptr) || *cptr == '='); cptr++) ; - if (cptr == tptr) - break; - if (*cptr == '\n') { - /* We want to update line count and continue */ - cptr++; - line++; - continue; - } - if (*cptr == ';' || *cptr == '#') { - /* Comment, skip to end of line and continue */ - for (; cptr < tptr && *cptr != '\n'; cptr++) ; - if (*cptr == '\n') - line++; - cptr++; - continue; - } - /* We are at the beginning of an element. Verify if it is quoted. */ - if (*cptr == '"') { - quoted = TRUE; - cptr++; - } - /* Locate end of element. If it is quoted, we are allowed to wrap - * lines. ';' or '#' are allowed within the element as long as it - * does not terminate by either a blank/space or quote. We also - * allow '"' characters in single-word entries. - */ - do { - cont = FALSE; - for (wptr = cptr; cptr < tptr && - ( (!quoted && !isspace((int)*cptr) && *cptr != '=') || - (quoted && (*cptr != '\n' && *cptr != '"')) ); cptr++) ; - if (quoted && *cptr == '\n') { - cptr++; - line++; - cont = TRUE; - } - } while (cont == TRUE); - /* Note: isspace() includes '\n' */ - if (!( (quoted && *cptr == '"') || - (!quoted && (isspace((int)*cptr) || *cptr == '=')) )) { - /* This element ended abnormally, EOF was reached before it - * was terminated. - */ - res->CR_Err = CRE_UNEXPECTED_EOF; - res->CR_Line = line; - if (nod != NULL) - res->CR_Keyword = nod->args; - goto end; - } - /* We reached the end of this element, wptr points to the start and - * cptr to the character after the end of it. Quotes have been - * stripped if any. - */ - oc = *cptr; - len = cptr - wptr; - *cptr = '\0'; - - if (nod == NULL) { - - /* Parsing for keyword */ - if (len > CKW_MAX - 1) { - /* Exceeds maximum allowed length for a keyword */ - res->CR_Err = CRE_KEYWD_TOO_LONG; - (void) mm_strncpy(res->CR_Data, wptr, CKD_MAX - 1); - res->CR_Line = line; - goto end; - } - /* Verify if keyword exists, and if so, if it was already set */ - mm_strupper(wptr); - if ((nod = (struct CNode *)hashtable_lookup(&table, wptr, len)) - == NULL) { - /* Unknown keyword */ - res->CR_Err = CRE_ILLEGAL_KEYWD; - (void) mm_strncpy(res->CR_Data, wptr, CKD_MAX - 1); - res->CR_Line = line; - goto end; - } - if (nod->args->CA_Flags & CAF_TOUCHED) { - /* Duplicate/redefinition */ - res->CR_Err = CRE_MULT_DEF_KEYWD; - res->CR_Keyword = nod->args; - res->CR_Line = line; - goto end; - } - /* We continue, next pass will be to parse this keyword's data */ - - } else { - - /* Parsing for keyword data */ - switch (nod->args->CA_Type) { - case CAT_STR: - if (len < nod->args->CA_Min) { - res->CR_Err = CRE_STR_TOO_SHORT; - res->CR_Keyword = nod->args; - res->CR_Line = line; - (void) mm_strncpy(res->CR_Data, wptr, CKD_MAX - 1); - goto end; - } else if (len > nod->args->CA_Max) { - res->CR_Err = CRE_STR_TOO_LONG; - res->CR_Keyword = nod->args; - res->CR_Line = line; - (void) mm_strncpy(res->CR_Data, wptr, CKD_MAX - 1); - goto end; - } - /* Length sanity already done, efficiently copy */ - mm_memcpy(nod->args->CA_Data, wptr, len); - ((char *)nod->args->CA_Data)[len] = '\0'; - break; - case CAT_BOOL: - if (mm_strcasecmp(wptr, "TRUE") == 0) - *((bool *)nod->args->CA_Data) = TRUE; - else if (mm_strcasecmp(wptr, "FALSE") == 0) - *((bool *)nod->args->CA_Data) = FALSE; - else { - /* Not a valid boolean */ - res->CR_Err = CRE_NOT_BOOLEAN; - res->CR_Keyword = nod->args; - res->CR_Line = line; - (void) mm_strncpy(res->CR_Data, wptr, CKD_MAX - 1); - goto end; - } - break; - case CAT_VAL: - { - long v; - - v = strtol(wptr, NULL, 0); - if (v < nod->args->CA_Min) { - res->CR_Err = CRE_VAL_TOO_LOW; - res->CR_Keyword = nod->args; - res->CR_Line = line; - (void) mm_strncpy(res->CR_Data, wptr, CKD_MAX - 1); - goto end; - } else if (nod->args->CA_Max != 0 && - v > nod->args->CA_Max) { - res->CR_Err = CRE_VAL_TOO_HIGH; - res->CR_Keyword = nod->args; - res->CR_Line = line; - (void) mm_strncpy(res->CR_Data, wptr, CKD_MAX - 1); - goto end; - } - *((long *)nod->args->CA_Data) = v; - } - break; - default: - DEBUG_PRINTF("mmreadcfg", "Unknown data type for '%s': %d", - nod->keyword, nod->args->CA_Type); - } - - /* Mark keyword as set, then unset */ - nod->args->CA_Flags |= CAF_TOUCHED; - nod = NULL; - } - - /* Restore original character which we replaced with '\0', if we - * only skipped it now we could mess up lines counting, if the last - * element end delimiter was '\n' (an unquoted element). - */ - *cptr = oc; - if (*cptr != '\n') - cptr++; - } - - /* If we reached this code, the whole file was parsed successfully. - * Now verify if all required parameters were fullfilled. - */ - for (argptr = arg; argptr->CA_Type != CAT_END; argptr++) { - if (argptr->CA_Flags & CAF_REQUIRED && - !(argptr->CA_Flags & CAF_TOUCHED)) { - /* This requirement was not met */ - res->CR_Err = CRE_REQUIRED_KEYWD; - res->CR_Keyword = argptr; - goto end; - } - } - - /* Success all the way */ - ret = TRUE; - -end: - /* Free any resources we are still holding */ - if (HASHTABLE_VALID(&table)) - hashtable_destroy(&table, FALSE); - if (POOL_VALID(&pool)) - (void) pool_destroy(&pool); - if (file != NULL) - (void) munmap(file, (size_t)st.st_size); - if (fd != -1) - (void) close(fd); - if (res->CR_Err != 0) { - syslog(LOG_NOTICE, "! Error parsing configuration file '%s'", cfg); - syslog(LOG_NOTICE, "! Error : %s", mmreadcfg_strerr(res->CR_Err)); - if (*(res->CR_Data) != '\0') - syslog(LOG_NOTICE, "! Data : %s\n", res->CR_Data); - if ((argptr = res->CR_Keyword) != NULL) { - syslog(LOG_NOTICE, "! Keyword: %s\n", argptr->CA_Keyword); - syslog(LOG_NOTICE, "! Minimum: %ld\n", argptr->CA_Min); - syslog(LOG_NOTICE, "! Maximum: %ld\n", argptr->CA_Max); - } - if (res->CR_Line != -1) - syslog(LOG_NOTICE, "! Line : %d\n", res->CR_Line); - } - - return ret; -} - - -bool -mmmapstring(const cmap_t *cmap, const char *str, long *res) -{ - while (cmap->CM_Str) { - if (!mm_strcmp(cmap->CM_Str, str)) { - *res = cmap->CM_Val; - return (TRUE); - } - cmap++; - } - - return (FALSE); -} - - -gid_t * -mmgetgidarray(int *n, const char *groups) -{ - gid_t *array; - struct group *grp; - int i; - char *args[NGROUPS_MAX], wgroups[256]; - - (void) mm_strncpy(wgroups, groups, 255); - - if ((*n = mm_strspl(args, wgroups, NGROUPS_MAX, ',')) > 0) { - if ((array = malloc(sizeof(gid_t) * (*n)))) { - for (i = 0; i < *n; i++) { - if ((grp = getgrnam(args[i]))) - array[i] = grp->gr_gid; - else - break; - } - if (i == *n) - return (array); - free(array); - } - } - - return (NULL); -} - - -gid_t * -mmfreegidarray(gid_t *array) -{ - if (array != NULL) - free(array); - - return (NULL); -} - - -uid_t -mmgetuid(const char *user) -{ - struct passwd *pwd; - uid_t id; - - if ((pwd = getpwnam(user))) - id = pwd->pw_uid; - else - id = -1; - - return (id); -} - - -gid_t -mmgetgid(const char *group) -{ - struct group *grp; - gid_t id; - - if ((grp = getgrnam(group))) - id = grp->gr_gid; - else - id = -1; - - return (id); -} - - -/* Intended to be called by the superuser. There is no way to retreive - * privileges back after calling this function. We also check that it - * worked before exiting with TRUE for success. Note all the explicit - * 0 checking indead of boolean conditions for cleanliness. - */ -bool -mmdropprivs(uid_t uid, gid_t *gids, int ngids) -{ -#ifndef NODROPPRIVS - if (ngids) { - /* First set real and effective primary group id */ - if (setgid(gids[0]) != 0 || setegid(gids[0]) != 0) - return (FALSE); - ngids--; - /* Set secondary group ids if any */ - if (ngids > 0) - if (setgroups(ngids, &gids[1]) != 0) - return (FALSE); - } - /* Finally set the real and effective user id */ - if (setuid(uid) != 0 || seteuid(uid) != 0) - return (FALSE); - /* Now make sure that it worked :) */ - if (getuid() != uid || geteuid() != uid) - return (FALSE); -#endif /* !NODROPPRIVS */ - - return (TRUE); -} diff --git a/mmsoftware/mmlib/mmreadcfg.h b/mmsoftware/mmlib/mmreadcfg.h deleted file mode 100644 index 9ea437f..0000000 --- a/mmsoftware/mmlib/mmreadcfg.h +++ /dev/null @@ -1,143 +0,0 @@ -/* $Id: mmreadcfg.h,v 1.9 2005/01/27 09:07:15 mmondor Exp $ */ - -/* - * Copyright (C) 1991-2004, Matthew Mondor - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Matthew Mondor. - * 4. The name of Matthew Mondor may not be used to endorse or promote - * products derived from this software without specific prior written - * permission. - * 5. Redistribution of source code may not be released under the terms of - * any GNU Public License derivate. - * - * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - - - - -#ifndef MMREADCFG_H -#define MMREADCFG_H - - - - -#include -#include -#include - -#include -#include - - - - -/* Definitions for CfgArg CA_Type */ -#define CAT_STR 1 /* One string, quoted if spaces */ -#define CAT_VAL 2 /* A signed number (long) */ -#define CAT_BOOL 3 /* A boolean value (TRUE or FALSE) */ -#define CAT_END 0 /* End of options list */ - -/* Definitions for CfgArg CA_Flags */ -#define CAF_NONE 0 -#define CAF_REQUIRED (1<<0) -#define CAF_TOUCHED (1<<1) - -/* Definitions for CfgResult CR_Err */ -enum cre { - CRE_UNKNOWN = 0, - CRE_NOT_FOUND, - CRE_OUT_OF_MEM, - CRE_ILLEGAL_KEYWD, - CRE_STR_TOO_LONG, - CRE_STR_TOO_SHORT, - CRE_VAL_TOO_HIGH, - CRE_VAL_TOO_LOW, - CRE_NOT_BOOLEAN, - CRE_REQUIRED_KEYWD, - CRE_MULT_DEF_KEYWD, - CRE_KEYWD_TOO_LONG, - CRE_UNMATCHED_QUOTE, - CRE_NO_KEYWD_DATA, - CRE_UNEXPECTED_EOF, - CRE_MAX -}; - -/* Maximum allowed length for a keyword, including NIL */ -#define CKW_MAX 32 -#define CKD_MAX 256 - - - - -/* ReadCfg needed structures */ -typedef struct CfgArg -{ - int CA_Type, CA_Flags; - long CA_Min, CA_Max; - const char *CA_Keyword; - void *CA_Data; -} carg_t; - -typedef struct CfgResult -{ - carg_t *CR_Keyword; - int CR_Err, CR_Line; - char CR_Data[CKD_MAX]; -} cres_t; - -typedef struct CMap -{ - const char *CM_Str; - long CM_Val; -} cmap_t; - -struct CNode -{ - hashnode_t node; - char keyword[CKW_MAX]; - carg_t *args; -}; - - - - -extern const char *const mmreadcfg_strings[]; - -#define mmreadcfg_strerr(a) (mmreadcfg_strings[(enum cre)(a)]) - - - - -extern bool mmreadcfg(cres_t *, carg_t *, const char *); -extern bool mmmapstring(const cmap_t *, const char *, long *); -extern gid_t * mmgetgidarray(int *, const char *); -extern gid_t * mmfreegidarray(gid_t *); -extern uid_t mmgetuid(const char *); -extern gid_t mmgetgid(const char *); -extern bool mmdropprivs(uid_t, gid_t *, int); - - - - -#endif diff --git a/mmsoftware/mmlib/mmserver.c b/mmsoftware/mmlib/mmserver.c deleted file mode 100644 index cc5e251..0000000 --- a/mmsoftware/mmlib/mmserver.c +++ /dev/null @@ -1,1413 +0,0 @@ -/* $Id: mmserver.c,v 1.34 2005/02/20 01:18:01 mmondor Exp $ */ - -/* - * Copyright (C) 2000-2004, Matthew Mondor - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Matthew Mondor. - * 4. The name of Matthew Mondor may not be used to endorse or promote - * products derived from this software without specific prior written - * permission. - * 5. Redistribution of source code may not be released under the terms of - * any GNU Public License derivate. - * - * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - - - - -/* HEADERS */ - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - - - - -MMCOPYRIGHT("@(#) Copyright (c) 2000-2004\n\ -\tMatthew Mondor. All rights reserved.\n"); -MMRCSID("$Id: mmserver.c,v 1.34 2005/02/20 01:18:01 mmondor Exp $"); - - - - -/* DEFINITIONS */ - - - - -/* STATIC FUNCTIONS PROTOTYPES */ - -static void sighandler(int); -static struct iface * parse_ifaces(char *, char *); -static void phandleclient(struct thread_object *, void *); -static void writepidfile(const char *); -static void * async_thread(void *); -static struct async_clenv * async_open_clenv(void); -static struct async_clenv * async_close_clenv(struct async_clenv *); -static bool aclenv_constructor(pnode_t *); -static void aclenv_destructor(pnode_t *); -static bool spawn_async_procs(uid_t, gid_t *, int, - size_t); -static void async_proc_main(int); -static void async_resolvehostname(struct async_msg *); - -static u_int32_t clnode_keyhash(const void *, size_t); -static int clnode_keycmp(const void *, const void *, - size_t); -static void * clnode_expire_thread(void *); -static bool clnode_expire_thread_iterator(hashnode_t *, - void *); - - - - -/* GLOBAL VARIABLES */ - -static int (*handleclientfunc)(unsigned long, int, - clientlistnode *, struct iface *, - struct async_clenv *); -static pth_mutex_t ctable_lock, ppool_lock; -static pool_t cpool, ppool; -static hashtable_t ctable; -static struct async_env *async = NULL; - -/* Useful so that daemons can log standard disconnection status easily */ -const char *const mms_reasons[MMS_MAX] = { - "NORMAL", - "INPUT_TIMEOUT", - "OUTPUT_TIMEOUT", - "INPUT_ERROR", - "OUTPUT_ERROR", - "RESOURCE_ERROR", - "NOT_AVAILABLE", - "MANY_ERRORS", - "CONNECTION_RATE_EXCEEDED", /* These three used internally */ - "NUMBER_OF_ADDRESSES_EXCEEDED", - "CONNECTIONS_PER_ADDRESS_EXCEEDED", - "UNKNOWN" -}; - - - - -/* MAIN DAEMON PROGRAM */ - -/* Before calling this function, the async_init() and async_init_pth() - * functions should have been called. Before any async_*() or tcp_server() - * calls the process is expected to have called make_daemon(). - */ -void -tcp_server(char *message, char *server_names, char *listen_ips, uid_t uid, - gid_t *gids, int ngids, long maxips, long maxperip, long ratemax, - long rateper, long timeout, int port, bool resolve, - int (*handleclient1)(unsigned long, int, clientlistnode *, - struct iface *, struct async_clenv *)) -{ - struct sockaddr addr; - struct sockaddr_in server, *sinaddr; - socklen_t addrl; - clientlistnode *clnode; - int msgsock, ret, msglen, nifaces, nifaces2, i, reason; - bool ok; - struct pollfd *fds; - struct ifacendx *fdsi; - struct iface *ifaces, *tif; - unsigned long id; - pth_t clnode_thread; - pth_attr_t threadattr, clnode_threadattr; - int tcp_proto; - - sinaddr = (struct sockaddr_in *)&addr; - handleclientfunc = handleclient1; - id = (unsigned long)time(NULL); - msgsock = -1; - msglen = mm_strlen(message); - fds = NULL; - fdsi = NULL; - ifaces = NULL; - - /* Pth related */ - threadattr = pth_attr_new(); - pth_attr_set(threadattr, PTH_ATTR_JOINABLE, FALSE); - clnode_threadattr = pth_attr_new(); - pth_attr_set(clnode_threadattr, PTH_ATTR_JOINABLE, TRUE); - pth_mutex_init(&ctable_lock); - pth_mutex_init(&ppool_lock); - - /* Used by resolve_hostname() */ - if (!mmstrinit(malloc, free, 16384)) { - syslog(LOG_NOTICE, "tcp_server() - mmstrinit()"); - closelog(); - exit(-1); - } - - if ((ifaces = parse_ifaces(server_names, listen_ips)) == NULL) { - closelog(); - mmstrexit(); - exit(-1); - } - - if (!pool_init(&cpool, "clients_pool", malloc, free, NULL, NULL, - sizeof(clientlistnode), 65536 / sizeof(clientlistnode), - 0, 0) || - !pool_init(&ppool, "phandles_pool", malloc, free, NULL, NULL, - sizeof(phandleinfo), 16384 / sizeof(phandleinfo), 0, 0) || - !hashtable_init(&ctable, "ctable", maxips, 1, malloc, free, - clnode_keycmp, clnode_keyhash, FALSE) || - ((clnode_thread = pth_spawn(clnode_threadattr, - clnode_expire_thread, &rateper)) == NULL)) { - syslog(LOG_NOTICE, "tcp_server() - Out of memory"); - closelog(); - mmstrexit(); - free(ifaces); - exit(-1); - } - - if (thread_object_init() == -1) { - syslog(LOG_NOTICE, "tcp_server() - thread_object_init()"); - exit(-1); - } - - /* Bind the port we should listen to, with all specified addresses */ - { - struct protoent *pent; - - /* Get protocol number for TCP */ - if ((pent = getprotobyname("TCP")) != NULL) - tcp_proto = pent->p_proto; - else - tcp_proto = -1; - } - for (ret = nifaces = 0, tif = ifaces; *(tif->hostname); tif++) { - if ((tif->sock = socket(AF_INET, SOCK_STREAM, 0)) > -1) { - int opt; - struct linger linger; - - opt = 1; - if ((setsockopt(tif->sock, SOL_SOCKET, SO_REUSEADDR, &opt, - sizeof(int))) == -1) - DEBUG_PRINTF("tcp_server", - "Error on setsockopt(%s:%d, SO_REUSEADDR) - (%s)", - tif->address_str, port, tif->hostname); - if ((setsockopt(tif->sock, SOL_SOCKET, SO_KEEPALIVE, &opt, - sizeof(int))) == -1) - DEBUG_PRINTF("tcp_server", - "Error on setsockopt(%s:%d, SO_KEEPALIVE) - (%s)", - tif->address_str, port, tif->hostname); - - linger.l_onoff = 0; - linger.l_linger = 0; - if ((setsockopt(tif->sock, SOL_SOCKET, SO_LINGER, &linger, - sizeof(struct linger))) == -1) - DEBUG_PRINTF("tcp_server", - "Error on setsockopt(%s:%d, SO_LINGER) - (%s)", - tif->address_str, port, tif->hostname); - - opt = 65536; - if ((setsockopt(tif->sock, SOL_SOCKET, SO_SNDBUF, &opt, - sizeof(int))) == -1) - DEBUG_PRINTF("tcp_server", - "Error on setsockopt(%s:%d, SO_SNDBUF) - (%s)", - tif->address_str, port, tif->hostname); - - if (tcp_proto != -1) { - opt = 1; - if ((setsockopt(tif->sock, tcp_proto, TCP_NODELAY, &opt, - sizeof(int))) == -1) - DEBUG_PRINTF("tcp_server", - "Error on setsockopt(%s:%d, TCP_NODELAY) - (%s)", - tif->address_str, port, tif->hostname); - } - - mm_memclr(&server, sizeof(struct sockaddr_in)); - server.sin_family = AF_INET; - server.sin_addr = tif->address.sin_addr; - server.sin_port = htons(port); - if ((bind(tif->sock, (struct sockaddr *)(void *)&server, - sizeof(struct sockaddr_in))) == 0) - nifaces++; - else { - syslog(LOG_NOTICE, - "tcp_server() - Error on bind(%s:%d) - (%s)", - tif->address_str, port, tif->hostname); - close(tif->sock); - tif->sock = -1; - } - } - } - - /* Drop privileges and start listening to all bound ports */ - if (nifaces > 0) { - if (mmdropprivs(uid, gids, ngids)) { - for (tif = ifaces; *(tif->hostname); tif++) { - if (tif->sock != -1) { - if ((listen(tif->sock, maxips * maxperip)) != 0) { - syslog(LOG_NOTICE, "tcp_server() - listen(%s)", - tif->hostname); - close(tif->sock); - tif->sock = -1; - nifaces--; - } else - syslog(LOG_NOTICE, "Listening on [%s:%d] - (%s)", - tif->address_str, port, tif->hostname); - } - } - } else { - syslog(LOG_NOTICE, - "tcp_server() - Cannot change uid and gid to safe privs"); - ret = -1; - } - } else { - syslog(LOG_NOTICE, - "tcp_server() - no interfaces (left) to listen to"); - ret = -1; - } - - if (!ret) { - /* Setup our poll() array, with corresponding index array */ - if ((fds = malloc((sizeof(struct pollfd) * nifaces) + - (sizeof(struct ifacendx) * nifaces))) != NULL) { - for (fdsi = (struct ifacendx *)&(fds[nifaces]), i = 0, - tif = ifaces; - *(tif->hostname); tif++) { - if (tif->sock != -1) { - fds[i].fd = tif->sock; - fds[i].events = POLLIN; - fds[i].revents = 0; - fdsi[i].iface = tif; - i++; - } - } - } else { - syslog(LOG_NOTICE, "tcp_server() - Out of memory"); - ret = -1; - } - } else - ret = -1; - - if (ret) { - free(ifaces); - mmstrexit(); - if (clnode_thread != NULL) { - pth_abort(clnode_thread); - pth_join(clnode_thread, NULL); - } - if (HASHTABLE_VALID(&ctable)) - hashtable_destroy(&ctable, FALSE); - if (POOL_VALID(&ppool)) - pool_destroy(&ppool); - if (POOL_VALID(&cpool)) - pool_destroy(&cpool); - exit(ret); - } - - /* Start answering and dispatching connections */ - syslog(LOG_NOTICE, "Started for uid: %d", uid); - for (;;) { - if ((nifaces2 = pth_poll(fds, nifaces, -1)) > 0) { - /* Loop but once only, and only for long as nifaces2 times. - * Use our fast index to reference to the interface structure. - */ - for (i = 0; i < nifaces && nifaces2; i++) { - if (fds[i].revents & POLLIN) { - nifaces2--; - addrl = sizeof(struct sockaddr); - if ((msgsock = pth_accept(fds[i].fd, &addr, &addrl)) - != -1) { - /* Make sure that we respect connection and rate - * limits. - */ - ok = FALSE; - reason = MMS_NORMAL; - pth_mutex_acquire(&ctable_lock, FALSE, NULL); - if ((clnode = (clientlistnode *)hashtable_lookup( - &ctable, &sinaddr->sin_addr.s_addr, - sizeof(u_int32_t))) == NULL) { - /* Create new node */ - if (HASHTABLE_NODES(&ctable) < maxips) { - if ((clnode = (clientlistnode *)pool_alloc( - &cpool, FALSE)) != NULL) { - clnode->hostname = NULL; - clnode->connections = 0; - if (ratemax != 0) - LR_INIT(&clnode->lr, ratemax, - (time_t)rateper, time(NULL)); - clnode->timeout = (timeout * 1000); - clnode->client = addr; - clnode->resolve = resolve; - hashtable_link(&ctable, - (hashnode_t *)clnode, - &((struct sockaddr_in *)&clnode-> - client)->sin_addr.s_addr, - sizeof(u_int32_t), FALSE); - } else - syslog(LOG_NOTICE, - "tcp_server() - Out of memory"); - } else - reason = MMS_ADDRESSES_EXCEEDED; - } - if (clnode != NULL) { - /* Either we found an existing node or we created - * it successfully - */ - if (clnode->connections < maxperip) { - if (ratemax != 0) { - if (!(ok = lr_allow(&clnode->lr, 1, 0, - FALSE))) - reason = MMS_RATE_EXCEEDED; - } else - ok = TRUE; - } else - reason = MMS_CONPERADDRESS_EXCEEDED; - } - pth_mutex_release(&ctable_lock); - - if (ok) { - phandleinfo *phi; - - /* Dispatch client to a thread */ - pth_mutex_acquire(&ppool_lock, FALSE, NULL); - phi = (phandleinfo *)pool_alloc(&ppool, FALSE); - pth_mutex_release(&ppool_lock); - if (phi != NULL) { - id++; - phi->id = id; - phi->socket = msgsock; - phi->clnode = clnode; - /* Indexed reference */ - phi->iface = fdsi[i].iface; - if (thread_object_call(NULL, phandleclient, - phi) != -1) - clnode->connections++; - else { - if (phi != NULL) { - pth_mutex_acquire(&ppool_lock, FALSE, - NULL); - pool_free((pnode_t *)phi); - pth_mutex_release(&ppool_lock); - } - syslog(LOG_NOTICE, "tcp_server() - " - "Error launching thread"); - } - } else - syslog(LOG_NOTICE, - "tcp_server() - Out of memory"); - } else { - char ipaddr[20]; - - /* Close down connection with client - * immediately, sending message. - */ - (void) pth_write(msgsock, message, msglen); - shutdown(msgsock, 2); - close(msgsock); - mm_strcpy(ipaddr, "0.0.0.0"); - inet_ntop(AF_INET, &(sinaddr->sin_addr), ipaddr, - 19); - if (reason != MMS_NORMAL) - syslog(LOG_NOTICE, "tcp_server() - [%s] - %s", - ipaddr, MMS_RSTRING(reason)); - } - - pth_mutex_release(&ctable_lock); - - } else - syslog(LOG_NOTICE, "tcp_server() - pth_accept()"); - } - } - } - } - - free(ifaces); - mmstrexit(); - pth_abort(clnode_thread); - pth_join(clnode_thread, NULL); - hashtable_destroy(&ctable, FALSE); - pool_destroy(&ppool); - pool_destroy(&cpool); - /* Unrequired for Pth (non-existing even) - * pth_mutex_destroy(&apool_lock); - * pth_mutex_destroy(&ctable_lock); - * pth_mutex_destroy(&ppool_lock); - */ - - exit(ret); -} - - -/* Writes our process ID number to specified file. To be called before - * chroot(2) or dropping privileges. - */ -static void -writepidfile(const char *file) -{ - char str[16]; - int fd; - - if ((fd = open(file, O_CREAT | O_TRUNC | O_WRONLY, 0600)) != -1) { - snprintf(str, 15, "%d\n", getpid()); - write(fd, str, mm_strlen(str)); - close(fd); - } else - syslog(LOG_NOTICE, "writepidfile() - open(%s)", file); -} - - -/* Uses fork() to become a daemon, and detaches from the current tty. - * Returns either successful or not - */ -bool -make_daemon(const char *pidfile, const char *jail) -{ - int pid; - -#ifdef NODETACH - pid = getpid(); -#else - if ((pid = fork()) != -1) -#endif - { - -#ifndef NODETACH - if (pid > 0) { - /* Parent */ - exit(0); - } else -#endif - { - int fd; - struct sigaction act; - - /* Child */ - setsid(); - chdir("/"); - if ((fd = open("/dev/null", O_RDWR)) != -1) { - dup2(fd, 0); - dup2(fd, 1); - dup2(fd, 2); - if (fd > 2) - close(fd); - } - writepidfile(pidfile); - - if (jail && *jail) { - if ((chroot(jail)) == -1) { - syslog(LOG_NOTICE, - "make_daemon() - Could not chroot(2) to '%s'", - jail); - exit(-1); - } - chdir("/"); - } - - /* Setup our break handler and ignore wanted signals */ - act.sa_handler = sighandler; - act.sa_flags = SA_NOCLDWAIT; - sigemptyset(&act.sa_mask); - sigaction(SIGTERM, &act, NULL); - sigaction(SIGCHLD, &act, NULL); - sigaction(SIGSEGV, &act, NULL); - signal(SIGTTOU, SIG_IGN); - signal(SIGTTIN, SIG_IGN); - signal(SIGTSTP, SIG_IGN); - signal(SIGPIPE, SIG_IGN); - - umask(0); - -#ifndef __GLIBC__ - setproctitle("Main threaded process"); -#endif - - return (TRUE); - } - } - - return (FALSE); -} - - -static void -sighandler(int sig) -{ - switch (sig) { - case SIGSEGV: - syslog(LOG_NOTICE, "Exiting (SIGSEGV)"); - kill(0, SIGTERM); - exit(0); - break; - case SIGTERM: - syslog(LOG_NOTICE, "Exiting (SIGTERM)"); - signal(SIGTERM, SIG_IGN); - kill(0, SIGTERM); - exit(0); - break; - case SIGCHLD: - { - int s = 0; - - while ((wait3(&s, WNOHANG, NULL)) > 0) ; - } - } -} - - -static struct iface * -parse_ifaces(char *hostnames, char *addresses) -{ - struct iface *ifaces; - char *hosts[65], *ips[65]; - struct sockaddr_in saddr; - int n, i; - - ifaces = NULL; - - if (hostnames != NULL && addresses != NULL && *hostnames != '\0' && - *addresses != '\0') { - if ((n = mm_straspl(hosts, hostnames, 64)) == - mm_straspl(ips, addresses, 64)) { - if ((ifaces = malloc(sizeof(struct iface) * (n + 1))) != NULL) { - /* Setup our array */ - for (i = 0; i < n; i++) { - mm_memclr(&saddr, sizeof(struct sockaddr_in)); - if ((inet_aton(ips[i], &(saddr.sin_addr))) == 1) { - mm_strncpy(ifaces[i].hostname, hosts[i], 64); - mm_strncpy(ifaces[i].address_str, ips[i], 15); - ifaces[i].sock = -1; - ifaces[i].address = saddr; - *(ifaces[i + 1].hostname) = '\0'; - } else { - syslog(LOG_NOTICE, - "parse_ifaces() - Invalid address [%s]", - ips[i]); - free(ifaces); - ifaces = NULL; - break; - } - } - } - } else - syslog(LOG_NOTICE, - "parse_ifaces() - Number of hostnames and addresses mismatch"); - } - - return (ifaces); -} - - -/* SERVER (THREAD) */ - -/* This is started by pth_spawn(), it calls the user handler function, which - * only has to care about serving the client. - */ -static void -phandleclient(struct thread_object *obj, void *args) -{ - phandleinfo *phi; - int socket, ret; - clientlistnode *clnode; - char *tmp; - struct iface *iface; - struct async_clenv *aclenv; - unsigned long id; - - /* Get and discard our passed parameters */ - phi = (phandleinfo *)args; - id = phi->id; - socket = phi->socket; - clnode = phi->clnode; - iface = phi->iface; - pth_mutex_acquire(&ppool_lock, FALSE, NULL); - pool_free((pnode_t *)phi); - pth_mutex_release(&ppool_lock); - - ret = 0; - - if ((aclenv = async_open_clenv())) { - if (clnode->resolve) { - /* We want to resolve the client's IP address' hostname, but not - * if it already was done for another client currently logged on - * from that same address. - */ - tmp = NULL; - /* We need to determine if hostname should be resolved. */ - if (clnode->hostname == NULL) { - /* It seems so, so let's do it without clobbering the mutex */ - tmp = resolve_hostname(aclenv, &(clnode->client)); - } - if (tmp) { - /* Lock the mutex for a very short moment */ - pth_mutex_acquire(&ctable_lock, FALSE, NULL); - /* Verify again for NULL to avoid a potential memory leak */ - if(clnode->hostname == NULL) - clnode->hostname = tmp; - else - tmp = mmstrfree(tmp); - pth_mutex_release(&ctable_lock); - } - } - - /* Handle the client */ - ret = handleclientfunc(id, socket, clnode, iface, aclenv); - - async_close_clenv(aclenv); - } else - syslog(LOG_NOTICE, "phandleclient() - async_open_clenv()"); - - /* Cleanup */ - - /* Close connection with client */ - if (socket != -1) { - shutdown(socket, 2); - close(socket); - } - - /* Decrease our connection/refcount counter, and let our ctable thread - * decide if/when to uncache our node. - */ - pth_mutex_acquire(&ctable_lock, FALSE, NULL); - if (clnode->connections > 0) - clnode->connections--; - pth_mutex_release(&ctable_lock); - - /* kthxbye :) */ -} - - - -/* This consists of a general purpose thread which can serve real - * asynchroneous functions via another thread, suitable to use for functions - * which can block the whole process when using non-preemptive threads like - * the Pth library provides. Pth message ports are used to communicate with - * this device in a way that processes waiting for results only block - * the requesting thread. The function internally uses unix datagrams to - * similarly communicate arguments and obtain back results from a free process - * in the asynchroneous processes pool. Another advantage of this technique is - * that on SMP systems the various processors can now be taken advantage of. - * The caller should of course expect data rather than pointers to be used for - * both arguments and return values since pointers are only valid for the - * current process. - * - * Drawbacks exist though; socket send() and recv() calls are internally - * required, as well as memory copy operations. Moreover, two new - * filedescriptor are required in the main process for each asynchroneous - * process in our pool. - * It should be used where necessary, like calculating MD5 hashes, resolving - * hostnames and evaluating directory tree sizes recursively, etc. - * - * It would have been possible to use different datagram sizes to transfer - * arguments and results to/from the other processes, but because of the way - * the Pth AmigaOS-style messages work, a decision was made so that unions are - * used by async functions and the whole structure is transfered back and - * forth. - */ - -/* ARGSUSED */ -static void * -async_thread(void *args) -{ - list_t queue, *freeprocs = &async->freeprocs; - struct async_idx *idx = NULL; - struct async_proc *proc; - struct pollfd *pfds = NULL; - char *ptr; - int i, nfds; - size_t idx_size, pfd_size; - - /* freeprocs is our pool of available async processes, queue is our - * queue of requests waiting to be processed and replied to. - * We use the idx array to fast-index process node via an fd which - * status changed during poll(). - */ - - /* Initialize our queue, fd->process index and poll array */ - i = 0; - DLIST_INIT(&queue); - DLIST_FOREACH(freeprocs, proc) - if (i < proc->sock) - i = proc->sock; - nfds = freeprocs->nodes; - idx_size = (size_t)OALIGN_CEIL(sizeof(struct async_idx) * (i + 1), long); - pfd_size = (size_t)OALIGN_CEIL(sizeof(struct pollfd) * (nfds + 1), long); - if ((ptr = calloc(idx_size + pfd_size, 1)) != NULL) { - idx = (struct async_idx *)ptr; - ptr += idx_size; - pfds = (struct pollfd *)ptr; - } else { - /* Fatal error */ - syslog(LOG_NOTICE, "async_thread() - calloc(%d)", - idx_size + pfd_size); - pth_exit(NULL); - } - i = 0; - DLIST_FOREACH(freeprocs, proc) { - idx[proc->sock].aproc = proc; - pfds[i].fd = proc->sock; - pfds[i].events = POLLIN | POLLERR; - i++; - } - - for (;;) { - char *omsg; - - /* First check for new messages and queue them in our queue list if - * any, but only wait for them if all processes are free (we don't - * expect any results). Special note: Pth will only notify event for - * a port if it received a message while it was empty. - */ - if ((pth_msgport_pending(async->port))) { - while ((omsg = (char *)pth_msgport_get(async->port)) != NULL) - DLIST_APPEND(&queue, (node_t *)(omsg - sizeof(pnode_t))); - } else if (freeprocs->nodes == async->nprocs) { - pth_wait(async->ring); - while ((omsg = (char *)pth_msgport_get(async->port)) != NULL) - DLIST_APPEND(&queue, (node_t *)(omsg - sizeof(pnode_t))); - } - - /* Verify for any available processes to dispatch requests to, and - * if so, dispatch requests from the queue until no more processes - * are available or the queue is empty. - */ - if (DLIST_NODES(&queue) > 0 && DLIST_NODES(freeprocs) > 0) { - struct async_proc *p; - struct async_msg *m; - size_t len; - - while ((p = DLIST_TOP(freeprocs)) != NULL && - (m = DLIST_TOP(&queue)) != NULL) { - if (DEBUG_TRUE(m->func_id < async->nfuncs)) { - len = async->funcs[m->func_id].msg_len; - idx[p->sock].aclenv = m->aclenv; - if ((pth_send(p->sock, m, len, 0)) == len) - DLIST_UNLINK(freeprocs, (node_t *)p); - else - syslog(LOG_NOTICE, "async_thread() - send(%d:%d)", - p->sock, (int)len); - } else - DEBUG_PRINTF("async_thread", - "Unknown function %u", m->func_id); - DLIST_UNLINK(&queue, (node_t *)m); - } - } - - /* Wait for results from our async processes via their socket, - * but be sure to break when new Pth messages are available. - * If new results are obtained, reply back to the caller thread. - */ - for (;;) { - int selr, i; - - if ((selr = pth_poll_ev(pfds, nfds, -1, async->ring)) > 0) { - for (i = 0; selr > 0 && i < nfds; i++) { - if (pfds[i].revents & POLLERR) { - /* If this happens something is really wrong, exit */ - syslog(LOG_NOTICE, "async_thread() - POLLERR(%d)", - pfds[i].fd); - kill(0, SIGTERM); - for (;;) - sleep(30); - } - if (pfds[i].revents & POLLIN) { - int fd = pfds[i].fd; - size_t len; - struct async_proc *p = idx[fd].aproc; - struct async_clenv *e = idx[fd].aclenv; - - /* Results, reply message back to client thread and - * return this process node in the free pool. - */ - if ((len = pth_recv(fd, e->msg, async->msg_len, - MSG_WAITALL)) < - sizeof(struct async_msg)) - syslog(LOG_NOTICE, "async_thread() - recv(%d:%d)", - fd, (int)async->msg_len); - pth_msgport_reply(&(e->msg->msg)); - DLIST_APPEND(freeprocs, (node_t *)p); - selr--; - } - } - } - if ((pth_event_occurred(async->ring))) - /* Other requests we must queue immediately */ - break; - } - } - - /* NOTREACHED */ - pth_exit(NULL); - return (NULL); -} - - -/* This function should be called at early application startup to setup - * the asynchroneous pool of processes. async_init_pth() call may then - * be called later on after pth_init() to initialize the client-side - * asynchroneous context (required before using async_open_clenv()). - * This function is assumed to be called once only, and it's effects to - * only be discarded when the main process exits. - */ -bool -async_init(struct async_func *funcs, int procs, uid_t uid, gid_t *gids, - int ngids) -{ - unsigned int i, nfuncs; - size_t funcs_len, msg_len, env_len; - bool res = FALSE; - - /* Evaluate required space to hold user functions plus our resolve one */ - for (i = 0; funcs[i].func; i++) ; - nfuncs = i + 1; - funcs_len = sizeof(struct async_func) * (nfuncs + 1); - - /* Evaluate maximum message size we should expect for arguments */ - msg_len = sizeof(struct async_resolvehostname_msg); - for (i = 0; i < nfuncs; i++) { - if (msg_len < funcs[i].msg_len) - msg_len = funcs[i].msg_len; - } - - /* Necessary allocations */ - env_len = (size_t)OALIGN_CEIL(sizeof(struct async_env), long); - funcs_len = (size_t)OALIGN_CEIL(funcs_len, long); - msg_len = (size_t)OALIGN_CEIL(msg_len, long); - if ((async = calloc(env_len + funcs_len + msg_len, 1)) != NULL) { - if (pool_init(&async->freeprocs_pool, "freeprocs_pool", - malloc, free, NULL, NULL, sizeof(struct async_proc), - procs, 1, 1)) { - register char *ptr = (char *)async; - - ptr += env_len; - async->funcs = (struct async_func *)ptr; - ptr += funcs_len; - async->msg = (struct async_msg *)ptr; - async->nfuncs = nfuncs + 1; - async->nprocs = procs; - async->msg_len = msg_len; - /* Fill in functions */ - async->funcs[0].func = async_resolvehostname; - async->funcs[0].msg_len = sizeof(struct async_resolvehostname_msg); - for (i = 0; i < nfuncs; i++) { - async->funcs[i + 1].func = funcs[i].func; - async->funcs[i + 1].msg_len = funcs[i].msg_len; - } - DLIST_INIT(&async->freeprocs); - res = TRUE; - } else - syslog(LOG_NOTICE, "async_init() - pool_init(freeprocs_pool)"); - } else - syslog(LOG_NOTICE, "async_init() - calloc(%d)", - env_len + funcs_len + msg_len); - - if (res) { - /* Start async slave processes */ - if (!spawn_async_procs(uid, gids, ngids, msg_len)) { - syslog(LOG_NOTICE, "async_init() - spawn_async_procs()"); - res = FALSE; - } - } - - if (!res) { - if (async) { - pool_destroy(&async->freeprocs_pool); - free(async); - async = NULL; - } - } - - return (res); -} - - -/* This function is used by an application after pth_init() to initialize - * the asynchroneous thread device/server, which will dispatch requests to - * the asynchroneous pool of processes in a thread-safe manner (without - * blocking the whole process. async_init() should have been called first. - */ -bool -async_init_pth(void) -{ - size_t env_len; - bool res = FALSE; - - if (DEBUG_TRUE(async)) { - - res = TRUE; - pth_mutex_init(&async->apool_lock); - /* Setup our fast-allocation pool for client-side async environments */ - env_len = (size_t)OALIGN_CEIL(sizeof(struct async_clenv), long); - if (!pool_init(&async->apool, "asyncenv_pool", malloc, free, - aclenv_constructor, aclenv_destructor, - env_len + async->msg_len, - 16384 / (env_len + async->msg_len), 0, 0)) { - syslog(LOG_NOTICE, "async_init_pth() - pool_init(apool)"); - res = FALSE; - } - - if (res) { - pth_attr_t attrs; - - res = FALSE; - if ((attrs = pth_attr_new()) != NULL) { - /* Start async slave thread device */ - if ((async->port = pth_msgport_create("mms_async_server"))) { - if ((async->ring = pth_event(PTH_EVENT_MSG, - async->port))) { - pth_attr_set(attrs, PTH_ATTR_JOINABLE, FALSE); - if ((pth_spawn(attrs, async_thread, NULL)) != NULL) - res = TRUE; - pth_attr_destroy(attrs); - } else - syslog(LOG_NOTICE, "async_init_pth() - pth_event()"); - } else - syslog(LOG_NOTICE, - "async_init_pth() - pth_msgport_create()"); - pth_attr_destroy(attrs); - } - } - - if (!res) { - if (async->ring) { - pth_event_free(async->ring, PTH_FREE_ALL); - async->ring = NULL; - } - if (async->port) { - pth_msgport_destroy(async->port); - async->port = NULL; - } - pool_destroy(&async->apool); - } - - } else - DEBUG_PRINTF("async_init_pth", "Call async_init() first"); - - return (res); -} - - -/* Used by a client thread to initialize a context suitable to call the - * async_call() function with. Each thread needs one to use the device. - * async_init() and async_init_pth() should have been called first. - */ -static struct async_clenv * -async_open_clenv(void) -{ - struct async_clenv *aclenv = NULL; - - if (DEBUG_TRUE(async != NULL && POOL_VALID(&async->apool))) { - /* Optimized for speed using a pool of pre-allocated nodes */ - pth_mutex_acquire(&async->apool_lock, FALSE, NULL); - aclenv = (struct async_clenv *)pool_alloc(&async->apool, FALSE); - pth_mutex_release(&async->apool_lock); - } else - DEBUG_PRINTF("async_open_clenv", - "Call async_init() and async_init_pth() first"); - - return aclenv; -} - -/* Destroys an async_clenv which was previously initialized using the - * async_open_clenv() function. - */ -static struct async_clenv * -async_close_clenv(struct async_clenv *aclenv) -{ - pth_mutex_acquire(&async->apool_lock, FALSE, NULL); - pool_free((pnode_t *)aclenv); - pth_mutex_release(&async->apool_lock); - - return (NULL); -} - -static bool aclenv_constructor(pnode_t *pnode) -{ - struct async_clenv *aclenv = (struct async_clenv *)pnode; - - if ((aclenv->port = pth_msgport_create("XXX(NULL)")) != NULL) { - if ((aclenv->ring = pth_event(PTH_EVENT_MSG, aclenv->port))) { - register char *ptr = (char *)aclenv; - - ptr += (size_t)OALIGN_CEIL(sizeof(struct async_clenv), long); - aclenv->msg = (struct async_msg *)ptr; - aclenv->msg->msg.m_replyport = aclenv->port; - aclenv->msg->msg.m_size = async->msg_len; - - return TRUE; - } - pth_msgport_destroy(aclenv->port); - } - - return FALSE; -} - -static void aclenv_destructor(pnode_t *pnode) -{ - struct async_clenv *aclenv = (struct async_clenv *)pnode; - - pth_event_free(aclenv->ring, PTH_FREE_ALL); - pth_msgport_destroy(aclenv->port); -} - - - -/* This is the function which is used to call async functions handled by - * async_thread() and our pool of slave processes. It does not check for - * timeout, the async functions set should be sure to always return within a - * reasonable delay if they could block indefinitely. - */ -void -async_call(struct async_clenv *aclenv, unsigned int function) -{ - /* Send request */ - aclenv->msg->func_id = function; - aclenv->msg->aclenv = aclenv; - pth_msgport_put(async->port, &(aclenv->msg->msg)); - - /* Sleep until we get reply back, in a thread-safe manner. - * Note: This will permanently block the current thread if the - * task does not complete. It would be possible to respect a timeout - * here, however this could cause problems. For instance, we may have - * sent a resolve request which takes some time because of a network - * problem, and the client could disconnect (and this thread end). - * The async function reply would then attempt to use a no longer - * existing port. (It could look for the port name in the list, but - * this would have considerable performance drawbacks. Ideally, - * any async operation would be abortable by the thread that initiated - * it. This could be tricky to implement in order to work for everything - * properly, avoiding resource leaks). So blocking the thread until - * we get a reply back seems a good choice here; The async functions - * should ensure to eventually return. - */ - for (;;) { - if ((pth_wait(aclenv->ring))) { - if ((pth_event_occurred(aclenv->ring))) { - /* We know that message pointer will be the same */ - if ((pth_msgport_get(aclenv->port))) - break; - } - } - } -} - - -/* This function is called by async_init() and starts up all slave - * asynchroneous processes. It knows when to stop when the blocklist has no - * more entries. It is expected that make_daemon() was used first, so that - * filedescriptors 0-2 be already redirected to /dev/null. - */ -static bool -spawn_async_procs(uid_t uid, gid_t *gids, int ngids, size_t msg_len) -{ - struct async_proc *aproc; - int s[2], opt; - bool ret = FALSE; - - if (async == NULL) - return ret; - - do { - if ((aproc = (struct async_proc *)pool_alloc(&async->freeprocs_pool, - TRUE))) { - if ((socketpair(PF_UNIX, SOCK_DGRAM, 0, s)) == 0) { - - /* Set buffer sizes to make sure that atomic datagram - * operations work as expected for the maximum packet size - */ - opt = (int)BALIGN_CEIL(msg_len, 1024); - if (setsockopt(s[0], SOL_SOCKET, SO_SNDBUF, &opt, - sizeof(int)) == -1 || - setsockopt(s[0], SOL_SOCKET, SO_RCVBUF, &opt, - sizeof(int)) == -1 || - setsockopt(s[1], SOL_SOCKET, SO_SNDBUF, &opt, - sizeof(int)) == -1 || - setsockopt(s[1], SOL_SOCKET, SO_RCVBUF, &opt, - sizeof(int)) == -1) - DEBUG_PRINTF("spawn_async_procs", "setsockopt(SO_*BUF)"); - - aproc->sock = s[0]; - /* Create new process */ - if (!(aproc->pid = fork())) { - close(s[0]); - - /* Child */ - chdir("/"); - umask(0); - - /* Finally drop privileges and perform our tasks */ - if (mmdropprivs(uid, gids, ngids)) { - close(s[0]); - while ((aproc = DLIST_TOP(&async->freeprocs)) - != NULL) { - close(aproc->sock); - DLIST_UNLINK(&async->freeprocs, (node_t *)aproc); - } - async_proc_main(s[1]); - } else - syslog(LOG_NOTICE, - "spawn_async_procs() - mmdropprivs()"); - - } else if (aproc->pid == -1) { - pool_free((pnode_t *)aproc); - aproc = NULL; - syslog(LOG_NOTICE, "spawn_async_procs() - fork()"); - close(s[0]); - close(s[1]); - } - } else - syslog(LOG_NOTICE, "spawn_async_procs() - socketpair()"); - } - if (aproc) { - close(s[1]); - DLIST_APPEND(&async->freeprocs, (node_t *)aproc); - } - else ret = TRUE; - } while (aproc); - - return (ret); -} - - -/* The main loop of each asynchroneous process in the pool */ -static void -async_proc_main(int sock) -{ - struct pollfd pfd[] = { - {-1, POLLIN | POLLERR, 0} - }; - -#ifndef __GLIBC__ - setproctitle("Asynchroneous server process"); -#endif - - pfd[0].fd = sock; - - for (;;) { - int selr; - unsigned int func_id; - size_t len; - - /* Wait for packets, process them and send result packets */ - if ((selr = poll(pfd, 1, -1))) { - if (selr == 1) { - if (pfd[0].revents & POLLERR) { - /* This should not happen. If it does something really - * went wrong; Exit. - */ - syslog(LOG_NOTICE, - "async_proc_main() - Socket error (%d)", sock); - kill(0, SIGTERM); - for (;;) - sleep(30); - } - if (pfd[0].revents & POLLIN) { - if ((len = recv(sock, async->msg, async->msg_len, - MSG_WAITALL)) - >= sizeof(struct async_msg)) { - func_id = async->msg->func_id; - if (func_id < async->nfuncs && - len == async->funcs[func_id].msg_len) { - async->funcs[func_id].func(async->msg); - if ((send(sock, async->msg, len, 0)) != len) - syslog(LOG_NOTICE, - "async_proc_main() - send(%d:%d)", - sock, (int)len); - } else - syslog(LOG_NOTICE, "async_proc_main() - " - "Invalid function %u on socket %d", - func_id, sock); - } else - syslog(LOG_NOTICE, "async_proc_main() - recv(%d:%d)", - sock, (int)async->msg_len); - } - } - } - } -} - - -/* This function will always be available in the asynchoneous functions set, - * referenced as ASYNC_RESOLVEHOSTNAME when using async_call(). - */ -static void -async_resolvehostname(struct async_msg *msg) -{ - struct async_resolvehostname_msg *amsg = (void *)msg; - struct sockaddr *saddr = &(amsg->un.args.saddr); - - if ((getnameinfo(saddr, sizeof(struct sockaddr_in), amsg->un.res.hostname, - 255, NULL, 0, 0)) != 0) { - DEBUG_PRINTF("async_resolvehostname", "getnameinfo()"); - mm_strcpy(amsg->un.res.hostname, "unknown"); - } -} - - -/* Easy function wrapper for async_resolvehostname(). mmstrfree() should - * be called to release the supplied result string when no longer needed. - * This function is thread-safe, as well as asynchroneous, the current - * process (all threads) will remain free to execute other tasks when - * the current thread waits for the hostname resolution to complete, as the - * request is dispatched to one of the asynchroneous processes in our pool. - */ -char * -resolve_hostname(struct async_clenv *aclenv, struct sockaddr *saddr) -{ - char *tmp; - struct async_resolvehostname_msg *amsg = (void *)aclenv->msg; - - if ((tmp = mmstralloc(255)) != NULL) { - mm_memcpy(&(amsg->un.args.saddr), saddr, sizeof(struct sockaddr)); - async_call(aclenv, ASYNC_RESOLVEHOSTNAME); - mm_strncpy(tmp, amsg->un.res.hostname, 255); - } else - syslog(LOG_NOTICE, "resolve_hostname() - mmstralloc()"); - - return (tmp); -} - - -/* These are related to the clnode ipaddress hash table. */ - - -/* A quick hashtable_hash() replacement which already deals with unique - * 32-bit values - */ -/* ARGSUSED */ -static u_int32_t -clnode_keyhash(const void *data, size_t len) -{ - return *((u_int32_t *)data); -} - - -/* A quick memcmp() replacement which only needs to compare two 32-bit values - */ -/* ARGSUSED */ -static int -clnode_keycmp(const void *src, const void *dst, size_t len) -{ - return *((u_int32_t *)src) - *((u_int32_t *)dst); -} - - -/* This is the actual ctable hash control and expiration thread */ -/* ARGSUSED */ -static void * -clnode_expire_thread(void *args) -{ - long period = *((long *)args); - struct clnode_expire_thread_iterator_udata data; - - /* Set initial timeout to maximum allowed */ - data.soonest = period; - data.cnt = 0; - for (;;) { - /* Sleep until it is known that at least one node expired */ - pth_sleep((unsigned int)data.soonest); - /* Tell our iterator function the current time and the maximum - * allowed time to wait to - */ - data.current = time(NULL); - data.soonest = period; - /* Lock ctable, reset expired nodes, garbage collect, and set - * data.soonest to the time of the soonest next expireing node. - */ - pth_mutex_acquire(&ctable_lock, FALSE, NULL); - if (HASHTABLE_NODES(&ctable) > 0) - hashtable_iterate(&ctable, clnode_expire_thread_iterator, &data); - pth_mutex_release(&ctable_lock); - } - - /* NOTREACHED */ - pth_exit(NULL); - return NULL; -} - - -static bool -clnode_expire_thread_iterator(hashnode_t *hnod, void *udata) -{ - clientlistnode *clnode = (clientlistnode *)hnod; - struct clnode_expire_thread_iterator_udata *data = udata; - time_t rem; - - /* If the node expired, reset it. For nodes which do not, record the - * soonest to expire node. For nodes which expired and for which no - * connections exist anymore, expunge them. - */ - if ((rem = LR_REMAINS(&clnode->lr, data->current)) == 0) { - /* This entry expired */ - if (clnode->connections == 0) { - /* Safe to expunge this node from the cache */ - hashtable_unlink(&ctable, (hashnode_t *)clnode); - pool_free((pnode_t *)clnode); - } else { - /* Reset it */ - LR_EXPIRE(&clnode->lr, data->current); - rem = LR_REMAINS(&clnode->lr, data->current); - } - } - if (rem != 0 && data->soonest > rem) - data->soonest = rem; - - /* If the cache is big, prevent from interfering with other threads */ - if ((data->cnt++) == 64) { - data->cnt = 0; - pth_yield(NULL); - } - - return TRUE; -} diff --git a/mmsoftware/mmlib/mmserver.h b/mmsoftware/mmlib/mmserver.h deleted file mode 100644 index 9bf088a..0000000 --- a/mmsoftware/mmlib/mmserver.h +++ /dev/null @@ -1,238 +0,0 @@ -/* $Id: mmserver.h,v 1.10 2004/06/09 20:58:54 mmondor Exp $ */ - -/* - * Copyright (C) 2000-2004, Matthew Mondor - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Matthew Mondor. - * 4. The name of Matthew Mondor may not be used to endorse or promote - * products derived from this software without specific prior written - * permission. - * 5. Redistribution of source code may not be released under the terms of - * any GNU Public License derivate. - * - * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - - - - -#ifndef MMSERVER_H -#define MMSERVER_H - - - - -#include -#include - -#include -#include -#include -#include -#include - - - - -/* DEFINITIONS */ - -enum mms { - MMS_NORMAL = 0, - MMS_INPUT_TIMEOUT, - MMS_OUTPUT_TIMEOUT, - MMS_INPUT_ERROR, - MMS_OUTPUT_ERROR, - MMS_RESOURCE_ERROR, - MMS_NOT_AVAILABLE, - MMS_MANY_ERRORS, - MMS_RATE_EXCEEDED, - MMS_ADDRESSES_EXCEEDED, - MMS_CONPERADDRESS_EXCEEDED, - MMS_UNKNOWN, - MMS_MAX -}; - -#define MMS_RSTRING(x) (mms_reasons[x]) - -/* Asynchroneous functions */ -#define ASYNC_RESOLVEHOSTNAME 0 - - - - -/* STRUCTURES */ - -/* Each IP address has such a node, which is shared among connections on that - * address. We also cache it as long as is required to keep the connection - * rate statistics for that address. It is freed (returned to the pool) when - * no connections are left and occurred for connection rate period. - */ -typedef struct clientlistnode { - hashnode_t node; - char *hostname; - long connections; - struct limitrate lr; - int timeout; - /* Key is ((struct sockaddr_in *)&clnode->client)->sin_addr.s_addr */ - struct sockaddr client; - bool resolve; -} clientlistnode; - -struct clnode_expire_thread_iterator_udata { - time_t current, soonest; - int cnt; -}; - -/* This is passed to the client threads, which they return to the pool when - * done with (internally, the user thread function does not need to free it). - */ -typedef struct phandleinfo { - pnode_t node; - clientlistnode *clnode; - struct iface *iface; - unsigned long id; - int socket; -} phandleinfo; - -/* XXX most security options and limits should be on a per-interface basis, - * This would however require mmreadcfg library enhancements. - */ -struct iface { - char hostname[72]; /* empty for last array element */ - char address_str[16]; /* Server address string */ - int sock; /* Bound socket (or -1) */ - struct sockaddr_in address; /* Address to bind() to (address_str) */ -}; - -/* Structure used to quickly index poll array offset with iface */ -struct ifacendx { - struct iface *iface; -}; - - - -/* These are for the special asynchroneous functions support for functions Pth - * cannot provide without blocking the whole process. They will be dispatched - * to a specialized process through the Pth message passing system. - */ - -/* A message which is to pass arguments to the async thread for a function, - * and to receive the function results. The user structure is usually - * different for both sides. The msg and func_id fields will be transfered - * back unchanged. Generally the user provides a fixed size structure using - * a union to be transfered both ways (results and arguments parts). As the - * Pth library messages are moved around by simply moving pointers, like on - * AmigaOs, this is ideal. - */ -struct async_msg { - pnode_t node; - pth_message_t msg; - struct async_clenv *aclenv; - unsigned int func_id; - /* User data structure here, user async functions should not touch - * the above internal/private control fields. - */ -}; - -/* Used to register available functions the async process should support */ -struct async_func { - void (*func)(struct async_msg *); - size_t msg_len; -}; - -/* Node used for each process in our async process pool */ -struct async_proc { - pnode_t node; - pid_t pid; - int sock; -}; - -/* Global async server context environment */ -struct async_env { - pth_t slave; - pth_msgport_t port; - pth_event_t ring; - pth_mutex_t apool_lock; - struct async_func *funcs; - unsigned int nfuncs, nprocs; - size_t msg_len; - pool_t freeprocs_pool, apool; - list_t freeprocs; - struct async_msg *msg; -}; - -/* Used to map fd to async_proc and async_clenv pointers in async_thread() */ -struct async_idx { - struct async_proc *aproc; - struct async_clenv *aclenv; -}; - -/* Client/thread specific async context */ -struct async_clenv { - pnode_t node; - pth_msgport_t port; - pth_event_t ring; - struct async_msg *msg; -}; - -/* Used for the ASYNC_RESOLVEHOST function */ -struct async_resolvehostname_msg { - struct async_msg msg; - union { - struct { - char hostname[256]; - } res; - struct { - struct sockaddr saddr; - } args; - } un; -}; - - - - -/* PROTOTYPES */ - -extern void tcp_server(char *, char *, char *, uid_t, gid_t *, int, - long, long, long, long, long, int, bool, - int (*)(unsigned long, int, clientlistnode *, - struct iface *, struct async_clenv *)); -extern bool make_daemon(const char *, const char *); - -extern bool async_init(struct async_func *, int, uid_t, gid_t *, int); -extern bool async_init_pth(void); -extern void async_call(struct async_clenv *, unsigned int); - -extern char * resolve_hostname(struct async_clenv *, struct sockaddr *); - - - - -/* GLOBAL VARIABLES */ - -extern const char *const mms_reasons[]; - - - - -#endif diff --git a/mmsoftware/mmlib/mmserver2.3 b/mmsoftware/mmlib/mmserver2.3 deleted file mode 100644 index 333508c..0000000 --- a/mmsoftware/mmlib/mmserver2.3 +++ /dev/null @@ -1,1190 +0,0 @@ -.\" $Id: mmserver2.3,v 1.16 2004/10/05 15:39:29 mmondor Exp $ -.\" -.\" Copyright (C) 2004, Matthew Mondor -.\" All rights reserved. -.\" -.\" Redistribution and use in source and binary forms, with or without -.\" modification, are permitted provided that the following conditions -.\" are met: -.\" 1. Redistributions of source code must retain the above copyright -.\" notice, this list of conditions and the following disclaimer. -.\" 2. Redistributions in binary form must reproduce the above copyright -.\" notice, this list of conditions and the following disclaimer in the -.\" documentation and/or other materials provided with the distribution. -.\" 3. All advertising materials mentioning features or use of this software -.\" must display the following acknowledgement: -.\" This product includes software developed by Matthew Mondor. -.\" 4. The name of Matthew Mondor may not be used to endorse or promote -.\" products derived from this software without specific prior written -.\" permission. -.\" 5. Redistribution of source code may not be released under the terms of -.\" any GNU Public License derivate. -.\" -.\" THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR -.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES -.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. -.\" IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, -.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -.\" BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF -.\" USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF -.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -.\" -.Dd March 21, 2004 -.Dt MMSERVER2 3 -.Os mmsoftware -.Sh NAME -.Nm mmserver2 -.Nd Library implementing the basis for internet protocol servers -.Sh SYNOPSIS -.Fd #include -.Fd #include -.Fd #include -.Ft void -.Fn server_init "void" -.Ft void * -.Fn server_shmem_malloc "size_t size" -.Ft int -.Fn server_rwlocks_init "const char *path" "int num" "const char *user" \ -"const char *group" "mode_t mode" -.Ft int -.Fn server_rwlock_ctl "int id" "int op" -.Ft int -.Fn server_socket_bind "struct server_socket_config *config" -.Ft int -.Fn server_start "struct server_config *config" -.Ft int -.Fn server_exit "void" -.Ft int -.Fn server_recycle "bool interrupt" -.Ft void -.Fn server_close "void" -.Ft unsigned int -.Fn server_alarm "unsigned int seconds" -.Ft int -.Fn server_execve "const char *path" "char *const argv[]" "char *const envp[]" -.Ft const char * -.Fn SERVER_REASON_STRING "int reason" -.Ft socklen_t -.Fn SERVER_SOCKADDR_SOCKLEN "struct server_sockaddr *addr" -.Ft int * -.Fn SERVER_SOCKADDR_PORT "struct server_sockaddr *addr" -.Ft sa_family_t * -.Fn SERVER_SOCKADDR_FAMILY "struct server_sockaddr *addr" -.Ft void * -.Fn SERVER_SOCKADDR_ADDRESS "struct server_sockaddr *addr" -.Ft struct sockaddr * -.Fn SERVER_SOCKADDR "struct server_sockaddr *addr" -.Sh DESCRIPTION -The -.Nm -library greatly simplifies the task of writing internet protocol server daemons -which can optionally support both IPv4 and IPv6 TCP and UDP, as well as -AF_LOCAL SOCK_STREAM and SOCK_DGRAM sockets. The system internally manages a -pool of children processes which it uses to dispatch client requests to. This -pool will grow and shrink as needed to efficiently serve a high number of high -frequency client requests. This prevents the need from using -.Xr fork 2 -to dispatch every request or to have to start a thread using -.Xr pthread_create 3 -for each of them. Every process in the pool has the ability to -.Xr recvfrom 2 -or -.Xr accept 2 -itself, so that filedescriptor passing over AF_LOCAL sockets is unnecessary -to dispatch requests to children processes. A lock is used to properly -distribute the incomming requests among all children processes evenly, -although this optional feature can be disabled on systems which have no -problems with multiple processes and concurrent usage of -.Xr accept 2 -or -.Xr recvfrom 2 -and where distribution of incomming requests is adequate among them. -A bottleneck can then be avoided. -.Pp -The system provides alot of per-socket configuration options for a high -flexibility. It also internally comports an address to hostname resolution -cache for efficiency which is automatically used whenever resolving is -enabled for wanted sockets. Some care also was taken so that processes may -concurrently call -.Xr getnameinfo 3 -at the same time, preventing hostname resolution from being a bottleneck. -Because a process is considered to be in busy state when this happens, -other processes will be launched as necessary to cope with the concurrency. -The reason for calling this library -.Nm -is that the author already had implemented -.Xr mmserver 3 -which used GNU -.Xr Pth -threads and was used to write -.Xr mmftpd 8 -and -.Xr mmmail 8 . -This early implementation only supported AF_INET family with SOCK_STREAM type, -and required a new thread to be launched to serve every new connection. -.Pp -We also provide means to execute application-specific function hooks at -multiple points such as when launching or killing a children process in -the pool, at the occurance of particular signals, etc. Also provided is -a system for easy inter-process synchronization which can be used to -access either shared memory or shared resources, with a minimal amount of -code lines. -.Pp -Support is also provided for AF_LOCAL/AF_UNIX SOCK_DGRAM and SOCK_STREAM -sockets. It is allowed for the same application to have multiple sockets -of multiple types, of multiple address families. -.Pp -This library is particularly made for BSD systems (the author mostly -runs NetBSD) but should work on Linux without modifications (which implements -alot of BSD APIs including 4.2BSD sockets API, 4.2BSD -.Xr flock 2 -and 4.4BSD -.Xr mmap 2 -with MAP_ANON). It may also well work on a number of different Unix variants -but the author did not specifically try. -.Pp -An effort was made to make this library as efficient as possible while still -providing both a clean API and address-based limits functionality. To achieve -this, memory pools of fixed sized objects are used for various objects which -need to be allocated and freed in critical sections. The hash table lookups -number of buckets is also set high enough as to cause the less nodes to be -shared on a bucket, for faster lookups. -.Xr mmpool 3 -and -.Xr mmhash 3 -are used for this. -Moreover, cache lookup and update operations are avoided for sockets which -do not need per-address limits nor hostname resolution, for maximum -performance. -.Pp -Support is also provided for applications which need to execute the -.Xr execve 2 -system call, causing the process which serves the client to -.Xr exit 3 -or -.Xr _exit 2 . -Of course, the performance of such an application cannot reach that of one -which can serve multiple requests per children process of the pool, since -exiting after serving a single connection, the process must be restarted. -However, the advantage of a pool of processes still allows such applications -to benefit from a higher performance than simply using -.Xr fork 2 -linearily per new client connection. The performance gains will be observed -when sudden bursts of client requests occur frequently, and of course by the -fact that not all clients requests may cause execution of another program -to override the process, for instance clients which fail authentication or -exceed limits can cause the process to serve multiple connections without -restarting. -.Pp -Reasons which were taken into consideration when deciding to use -processes instead of threads for this second implementation were the following: -.Bl -enum -offset left -.It -Although POSIX defined a definite API for threads (pthreads), the author's -experience using them on a variety of Unix-like systems proven that there were -significant implementation-specific issues which often had to be dealt with -depending on the system. Some systems even use non-preemptive implementations. -.It -There is a rather large number of non-reentrant (thread unfriendly) functions -in most C libraries. In fact, several POSIX standard functions are not thread -safe. -.It -The security model of process-based systems is generally higher than that of -thread-based ones. Because threads inherently share memory within the whole -process, it is generally very easy to render a minor discovered vulnerability -of the system into a much more destructive exploit. Moreover, the process -based model allows application authors to implement fancy tricks as privilege -separation, still avoiding most of the application from running as the -superuser even if part of it needs to. A thread model does not permit such -flexibility (of course, we cannot assume to run on an implementation internally -using processes corresponding to each thread as in the 1:1 models when -programming applications using POSIX threads). -.It -The cost of using -.Xr fork 2 -has greatly decreased in modern operating systems, using COW (Copy On Write) -pages implementations. And because we are managing a pool of processes, -this problem is solved. -.It -The popular -.Xr OpenSSL 3 -library distributed with BSD systems, and available as a package on most other -systems, generally performs much better when using a process per client. This -library is especially useful in the implementation of HTTPS servers for -instance, but also useful for many other network protocols. -.It -Systems on which the C library is bogus and comports memory leaks can benefit -from a system which causes client serving processes to recycle themselves after -a set number of client requests have been served. -.It -The popular free -.Xr MySQL 8 -database server also has issues when some of it's features are used on -thread-based servers, such as table locking. Having one persistent connection -to the MySQL server per process in the pool resolves any such problems easily. -A client being blocked in a -.Xr libmysqlclient 3 -function call will only affect it's own process without interefering with other -clients. This is only one of many examples when working with popular third -party applications and libraries. -.It -A thread cannot reliably use -.Xr fork 2 , -.Xr flock 2 , -.Xr system 3 , -.Xr popen 3 -or -.Xr execve 2 . -A process has no problem if the need to use such functions arise. -.It -Even the standard stdio library cannot be used properly within a thread without -worrying about thread concurrency issues. They were made with the assumption -of a nonthreaded process in mind. This is also true of most I/O related -unix functions. Weither a particular system causes a thread-safe version -of these functions to be linked in with the application which uses threads -or not is an assumption one cannot make reliably. With -.Xr mmserver 3 , -.Xr mmftpd 8 -and -.Xr mmmail 8 -needed to use the -.Xr mmfd 3 -library for buffered streams (which also provides bandwidth shaping). -.It -Finally, a system using multiple processes is always able to take advantage of -the Operating System and hardware Symetric Multi Processor features where -available. This is not always the case with threads, as it greatly depends on -the implementation internals. -.El -.Pp -.Ss STRUCTURES -These structures are defined in -.Aq Pa mmserver2.h -and are part of the API. The internal functions used by the library code -are not included and hidden for convenience -(no user serviceable parts inside :) -We here describe the structures -along with each of their fields definitions: -.Bl -tag -width indent -offset left -.It Nm struct server_socket_config -It is recommended to use -.Xr memset 3 -to first zero this structure before setting it's parameters. It is then passed -to the -.Fn server_socket_bind -function, which internally makes it's own copy so that it does not matter if -it resides on the stack. It is best to set all of the fields to valid values, -explicitely. -.Bl -tag -width indent -offset left -.It int family; -The address family type of this socket, AF_INET, AF_INET6 or AF_LOCAL, -respectively. -.It int type; -The socket type, SOCK_STREAM or SOCK_DGRAM, respectively. For AF_INET and -AF_INET6 family sockets, SOCK_STREAM corresponds to TCP and SOCK_DGRAM to UDP. -.It int port; -The port number this socket should listen on, in host byte order. Should be -0 for AF_LOCAL sockets. -.It int backlog; -For SOCK_STREAM sockets only, this specifies the backlog parameters passed -to the -.Xr listen 2 -syscall. This is closely related to the -.Nm children_maximum -field of the -.Nm struct server_config , -as the connections which cannot immediately be dispatched to -processes will be queued by the backlog. Should be 0 for SOCK_DGRAM sockets. -.It bool create_stream; -For SOCK_STREAM sockets only, tells the system that a stdio FILE * stream -should be provided to the client request handler function, for convenience. -An advantage of this is that buffering can easily be used without additionnal -functions, but a known disadvantage is that stdio does not handle input -timeouts. Although SIGPIPE will internally be handled by the system, it -is advised to specify a SIGALRM handler in -.Nm struct server_config -and to use -.Fn server_alarm -to implement input timeout functionality. -.It bool address_resolve; -This should be FALSE for AF_LOCAL sockets. If TRUE, specifies that the system -should attempt to resolve client addresses to hostnames. This hostname will -be passed along to the handler functions. Note that for efficiency, an -internal cache is used to resolve hostnames which recently have been resolved -using the DNS system. This means that this functionality can even be used with -SOCK_DGRAM UDP sockets without too much slow down. Of course, it is always -more efficient to not resolve addresses, but it can be necessary for some -applications like for SMTP mail servers. -.It bool address_rate_limits; -Specifies if client IP address based request rate limits should be -enabled for this server socket (TRUE or FALSE). Should always be FALSE for -AF_LOCAL sockets. -.Nm address_cache_rate_limit -should be 0 if FALSE is specified. However, -.Nm address_cache_rate_period -will still be used for hostname cacheing. -.It bool address_concurrency_limits; -If client IP address based concurrency limits should be enabled for -this server socket (TRUE or FALSE). Should always be FALSE for AF_LOCAL -sockets. -.Nm address_cache_concurrency_limit -should be 0 if FALSE is specified. -.It u_int32_t address_cache_size; -Specifies the maximum number of unique addresses which can be stored into -the rate limit sanity checking cache. This means that this socket can serve -at most this number of concurrently different addresses at once. It therefore -should generally be high enough. Should be 0 if -.Nm address_resolve , -.Nm address_rate_limits -and -.Nm address_concurrency_limits -are all FALSE. Will be used otherwise. -.It u_int32_t address_cache_rate_limit; -The maximum number of requests which each IP address can perform within -.Nm address_cache_rate_period -seconds. Exceeding requests are delegated to the reject handler (if any) -rather than to the request handler. Should be 0 if -.Nm address_rate_limits -is FALSE. -.It u_int32_t address_cache_rate_period; -The number of seconds in which a maximum of -.Nm address_cache_rate_limit -requests are allowed from each IP address. Setting a low value here will -be less efficient but will also permit the address cache size to be smaller. -These values should be chosen with care depending on the particular -application. Note that even if rate limits are disabled specifying FALSE to -.Nm address_cache_rate_limit , -this period is used to control the automatic expiration of hostname cache -entries, if resolving is enabled. What happens is that the system makes sure -that no more current requests being served exist for an address when this -number of seconds elapses. If there are, the system again waits this number -of seconds and so on. If there are no requests this address cache node can -be freed, and the reference counter of the corresponding hostname cache node -decreased. If the reference count becomes zero for the hostname node, it -can be freed. This value should be 0 if -.Nm address_cache_size -is 0. -.It u_int32_t address_cache_concurrency_limit; -The maximum number of concurrent requests which each IP address is allowed -to perform at once. Exceeding requests are sent to the reject handler rather -than to the request handler. Should be 0 if -.Nm address_cache_concurrency_limits -is FALSE. -.It size_t packet_size; -For SOCK_DGRAM sokets only, specifies the maximum expected size of the -intitial client request packet. When the client request is received -this first packet is buffered and passed to the request handler function. -Although it would have been possible to internally use the MSG_PEEK flag -to -.Xr recvfrom 2 -and let the request handlers unqueue the first packet themselves from the -socket, this would have resulted in concurrency issues among the multiple -processes of the pool. -.It char bind_address[100]; -The address to -.Xr bind 2 -this listening socket to. -This can be "0.0.0.0" in the case of AF_INET to allow requests on all -interfaces from all IP addresses, "::" for AF_INET6 to also allow requests -on all interfaces from all IPv6 addresses. For AF_LOCAL, this should be a -fullpath to a file socket. Of course, the format of this address -should correspond to the chosen -.Nm family -field. -.It char socket_user[32]; -For AF_LOCAL family sockets only, if non-empty, this defines the unix user -which should be set as the AF_LOCAL socket file owner using -.Xr lchown 2 . -Has to be empty for other socket families. -.It char socket_group[32]; -For AF_LOCAL family sockets only, if non-empty, this defines the unix group -which should be set as the AF_LOCAL socket file group using -.Xr lchown 2 . -Has to be empty for other socket families. -.It mode_t socket_mode; -For AF_LOCAL family sockets only, specifies the mode of the file to be set -using -.Xr lchmod 2 -on the AF_LOCAL socket file. Note that any executable or suid/sgid bits will -automatically be stripped from this mask if present. Is ignored for other -socket families. -.It int (*setsockopts)(int); -After creating this socket using -.Xr socket 2 , -but before calling -.Xr bind 2 , -this optional function, if not NULL, is allowed to be called to set wanted -application-specific socket options, for instance using -.Xr setsockopt 2 . -This function should return 0 on success, or -1 on failure. The -.Fa int -parameter passed to it consists of the filedescriptor of the socket to affect. -.It void (*request_handler)(struct server_request *); -This function pointer is required and tells which application-specific function -to call to serve an incomming client request on this socket. A pointer -to a -.Nm struct server_request -is provided which allows the handler function to obtain needed information -about the client as well as application-specific user data associated with -this socket. Because SOCK_DGRAM sockets are by definition connectionless, -note that the initial request packet is then buffered and stored for the -request and reject handlers, as well as the original server socket. It -becomes the responsibility of the application to use -.Xr bind 2 -and/or -.Xr connect 2 -if it needs to use -.Xr recv 2 , -.Xr send 2 , -.Xr read 2 -and -.Xr write 2 -for communication with the client rather than -.Xr recvfrom 2 -and -.Xr sendto 2 . -The initial request packet which is buffered for the handler should first -be processed before attempting to receive again. Although it would have -been possible for the underlaying system to use the MSG_PEEK flag -to -.Xr recvfrom 2 , -if the request handler would not have unqueued the packet fast enough -there could be concurrency issues where other processes would also be -dispatched the same request. This is avoided by simply unqueing the request -packet and providing it to the request handlers. Be sure to read in detail -the informations provided on the -.Nm struct server_request -structure later on. -.It void (*reject_handler)(struct server_request *, int); -Called instead of the request handler function when the client address exceeds -it's request limits. Apart from the usual structure passed to this function, -the -.Fa int -parameter will represent the rejection reason: -.Bl -tag -width indent -offset left -.It REASON_CACHE_SIZE -The address cache size for this socket was exceeded; This address will be -allowed to perform requests whenever room exists to do it. -.It REASON_CONCURRENCY -The address exceeded the concurrent allowed number of simultaneous requests -it can perform. It currently has other existing requests which are ongoing -fine. -.It REASON_RATE -The address exceeded the allowed rate at which it can perform independent -requests. It will be allowed again whenever the limit counter expires for -a new period. -.El -.Pp -Because AF_LOCAL sockets have no address-based limits, this function will -never be called for them, and therefore should be NULL. -.It void (*request_interrupt_hook)(struct server_request *); -Called whenever a currently being served request is interrupted via a call -to -.Fn server_recycle -with the -.Fa interrupt -flag turned on, causing current requests to be interrupted. This handler -should perform any wanted application or protocol-specific task like sending -a protocol-friendly close message. The normal client closing procedure will -then be performed. Note that this also will be called if set, in the case where -.Nm exit_interrupt_requests -of -.Nm struct server_config -is TRUE when SIGTERM is received, causing the process to exit. -.It void (*request_close_hook)(struct server_request *); -Allows the application to set a custom function to be called whenever the -server disconnects a client (or terminates serving one). Because it is -possible for this event to happen as the result of an internal SIGPIPE -signal resulting from a client disconnection or socket error, it may be -important to release some resources which have been allocated by the -request handler function and must not remain dangling. -.It void *user_data; -Optional user data, that is, application-specific data associated with this -particular socket which is to be passed to the request handlers. This can -be useful for instance for an HTTP server to distinguish HTTP sockets from -HTTPS ones, or any other wanted functionality. Set to NULL if no such data -is wanted. -.El -.It Nm struct server_config -It is recommended to use -.Xr memset 3 -to zero out this structure before filling it with the wanted parameters. -This structure can then be passed to the -.Fn server_start -function. An internal copy of it will be made so that it does not matter -if the supplied structure resides on the stack. It is best to set all of the -fields to valid values, explicitely. -.Bl -tag -width indent -offset left -.It char proc_title[32]; -Especially useful with BSD systems -.Xr setproctitle 3 -fonctionality. This allows to append a custom prefix to the titles set for -the processes. This is useful if for instance multiple configurations exist -for a single program and thus several copies are running and should easily -be distinguished. The -.Xr mmspawnd 8 -program uses this feature. Can be set to an empty string if no prefix is -needed. -.It char locks_path[256]; -Should be set to the full path, including a filename, which will be used -to create all internal lock files to be used internally for shared resources -synchronization. The lock files will be created with this name plus a unique -suffix. -.It char locks_user[32]; -Specifies the user which should be set as the owner of the created lock -files, or an empty string to leave the default owner (usually the current user -ID of the process). -.It char locks_group[32]; -Specifies the group to be set as the group of the created lock files, or -an empty string to leave the default creation group. -.It mode_t locks_mode; -The file creation mode to be applied to the newly created lock files. -These permissions should be set with care so that the files generally cannot -be accessed by other applications or users, but that they be accessed at -least for read access by children processes of the application, so that -the lock files may be reopened by each process (a requirement for -.Xr flock 2 ) . -A popular value for this is 0600. -.It char null_path[256]; -Specifies the full path to the -.Xr null 4 -device, to redirect stdin, stdout and stderr at initialization when detaching -from the tty. -.It char pid_path[256]; -The full path to the filename used to store the process ID (pid_t) of the -parent process. This will be used by other software to know which pid to -signal using SIGTERM or SIGHUP afterwards. -.It u_int32_t children_initial; -The number of children processes to initially startup at application launch. -This number will usually consist of -.Nm children_minspare -but it can be set higher if it is known that high load is immediately expected. -.It u_int32_t children_minspare; -Specifies the minimum number of spare processes (ready, unclobbered processes) -which should be available to answer extraneous imminent client requests. -Every second, the system verifies if this number is met and if not, new -children processes are started in the pool to meet the requirement. -In the case of an HTTP server, this for instance allows to make sure that -there be enough processes to answer requests from browsers which perform -multiple simultaneous requests to load images of a page (like Netscape). -.It u_int32_t children_maxspare; -The maximum unused children processes in the pool that may exist. This permits -to shrink the pool of processes when it is known that we are wasting resources -with too many of them. The system maintains an average of unused processes -number so that it may only shrink the pool when it establishes that the current -load allows it. It would be unefficient to shrink this pool too fast in the -case of bursts of many client requests followed by short moments of inactivity -when other bursts are known to occur soon. The system avoids this. -.It u_int32_t children_maximum; -The maximum number of total processes (busy or not) in the pool. Note that -this should be high enough to handle the maximum expected loads. Because -each process can only handle one particular request at a time, this represents -the total maximum request concurrency. Although this does not mean that clients -are inherently blocked out if this number is reached and all the processes -are busy (it also depends on the backlog set for each listening socket), it -is a possible event. In the case of AF_INET or AF_INET6 SOCK_DGRAM sockets, -because of the unreliable nature of UDP, packets may be lost if this value is -too low (I.E. there are many more clients than number of processes). -.It u_int32_t children_average_seconds; -We previously described the way in which we shrink the size of the pool as -needed maintaining an average count of ready processes in -.Nm children_maxspare . -This value specifies the number of seconds over which this average is -calculated before deciding to shrink the pool. A number of 300 seconds -will cause the pool to only be shrinked if it is known that too many processes -were ideling for nothing during 5 minutes. -.It u_int32_t children_maxrequests; -Specifies the maximum number of consecutive client requests which each children -process may serve before recycling. Recycling causes the process to exit and -to be immediately replaced by another new process. This can be especially -useful in the cases where memory leaks are observed into the system libraries -and the processes tend to grow overtime. This limit should be rather high -for performance considerations. 1000 or more is generally a good value. -Although this feature cannot be disabled, it is allowed to set a very high -value (u_int32_t)-1 if wanted. -.It bool serialization_lock; -If TRUE, a synchronization lock will be used to more evenly distribute requests -among the chilren processes in the pool. This also may be required on some -systems for proper function. If FALSE, a bottleneck can be avoided, allowing -requests to simultaneously be dispatched among all processes of the pool, which -will be sleeping in -.Xr poll 2 -rather than -.Xr flock 2 -or -.Xr poll 2 . -At least on NetBSD, disabling the lock does not seem to generally cause -problems, but it does in the case where the processes are swapped out. It then -seems that some processes will wait on the "netconn" channel (in -.Xr accept 2 ) -rather than at -.Xr poll 2 -on the "select" channel. Using a serialization lock fixes this issue. -.It bool exit_interrupt_requests; -Tells weither or not requests currently being processed should be interrupted -whenever the parent process is to exit (generally as the result of reception -of SIGTERM signal). Note that the request interrupt handler hook ( -.Nm request_interrupt_hook -of -.Nm struct server_socket_config ) -will be called if set whenever an active request is to be cancelled before -the process exits. This usually can be set to TRUE in most circumstances. -However, some services may require this to be FALSE (I.E. a remote -administration service where the link must remain up even when ordering a -full server restart, where SIGHUP is not enough). If FALSE, the children -processes will die whenever they finish serving the current request, if any. -.It bool children_setsid; -If TRUE, specifies that each children process in the pool should have their own -process group ID using -.Xr setsid 2 , -instead of sharing the master parent's process group. This is generally set to -FALSE for most setups. -.It bool using_execve; -If the application needs to use the -.Xr execve 2 -system call to serve a client, this should be set to TRUE. This enables a -special hash table to map children process IDs with their status structure -in shared memory, so that when the client serving process exits the parent -be notified that it may be recycled immediately. Note that such an application -must always call -.Xr _exit 2 -if -.Xr execve 2 -ever returns (it normally will not return, however). For other applications, -should be set to FALSE to avoid the need to maintain such a lookup table for -better efficiency. The -.Fn server_execve -function should be used instead of -.Xr execve 2 , -which will internally -.Xr _exit 2 -if necessary. Note that enabling this option also causes the close-on-exec flag -to be applied to the filedescriptors which should not be available during the -execution of the program used to serve the client, using -.Xr fcntl 2 . -.It int (*parent_init_hook)(void); -Optional application-specific function to be called at parent master process -initialization after it detaches from the tty and becomes a daemon, before -launching any children processes. Can be NULL if no such function is wanted. -Such a function may alter privileges of the process or perform other wanted -initialization. The function should return 0 on sucess or -1 on error, -optionally setting errno. If this function fails when the parent process calls -it, the server will exit after logging the error via -.Xr syslog 3 . -This function is called after binding all listening sockets. -.It void (*parent_exit_hook)(void); -Optional application-specific function to be called before the parent master -process exits, or NULL. This may be useful to clean up resources allocated -by the -.Nm parent_init_hook -custom function. -.It void (*parent_sighup_hook)(void); -Can be NULL if no such function is wanted. Optional application-specific -function to be called in the parent process context upon reception of the -SIGHUP signal. This function can potentially perform some configuration files -reload and such. It is also responsible for calling the -.Fn server_recycle -function as needed. -.It void (*parent_timer_hook)(void); -If not NULL, specifies a function which is to be called every -.Nm parent_timer_seconds -number of seconds. This function should return rather fast. This may be useful -when another process than the children ones need to perform some work at -regular intervals. A timer of at least 1 seconds must be used if a pointer is -set here. If the application requires more fancy things, and cannot be -satisfied by such a function hook which needs to return quite fast, it is -recommended to launch an application-specific process which can serve better -the tasks at hand. This can be done through -.Nm parent_init_hook . -.It u_int32_t parent_timer_seconds; -If -.Nm parent_timer_hook -is set a pointer, this must hold a value larger than 0, specifying the interval -timer in seconds for the hook function to be called. -.It int (*child_init_hook)(void); -Optional application-specific function to be called by each new child process -started in the pool, or NULL. This function can perfom tasks such as alter -the privileges of the process, attaching to any process-specific persistent -devices, pre-creation of process-specific client sockets, etc. Note that in the -case of an application using -.Nm using_execve -support and setting up persistent sockets it may be wanted to also use -.Xr fcntl 2 -on the filedescriptors to set the close-on-exec flag. -.It void (*child_exit_hook)(void); -Optional application-specific function to be called by each child process -before they exit, or NULL. Can be used to detach from process-specific -persistent devices for instance. -.It void (*child_sigalrm_hook)(void); -Optional application-specific function to be called upon reception of the -SIGALRM signal by the child processes. This is mostly useful to use in -conjunction with the -.Fn server_alarm -function while serving a client request in the handler function. This system -can also be used with -.Xr mmalarm 3 -if needed. A system using the FILE * stdio stream to serve a SOCK_STREAM client -connection will generally at least need to use this system to observe -input timeouts. -.El -.It Nm struct server_request -A pointer to such a structure is passed to client request handler functions. -Be sure to read the details of each field, as well as the corresponding -address manipulation macros described later on, plus the documentation of -the handler functions. -.Bl -tag -width indent -offset left -.It int client_socket; -For SOCK_STREAM sockets, the socket of the connected client. For -SOCK_DGRAM sockets, will be set to the same descriptor as for the -.Nm server_socket -field. -Do not close this filedescriptor yourself, the library always takes care of it. -.It int server_socket; -The filedescriptor of the listening server. -Note that this filedescriptor should not be modified (I.E.) do never use -.Xr close 2 , -.Xr connect 2 -or -.Xr bind 2 -on this filedescriptor. It is however possible to use -.Xr dup 2 . -This is provided because for some SOCK_DGRAM applications it is convenient. -Use this descriptor with care, as it is possible to mess up the server if it -is modified. -.It int server_socket_type; -SOCK_STREAM or SOCK_DGRAM, respectively. -.It int server_socket_family; -AF_INET, AF_INET6 or AF_LOCAL, respectively. -.It int client_port; -In the case of AF_LOCAL sockets, will always be 0. Otherwise, will be set to -the originator client request port, in host byte order. -.It int server_socket_port; -For AF_LOCAL sockets, always 0. Otherwise, the port number corresponding to -the socket on which this request was received, in host byte order. -.It FILE *client_stream; -In the case of SOCK_STREAM sockets with the -.Nm create_stream -parameter set to TRUE, this consists of the stdio line-buffered FILE stream -which can be used to communicate with the connected client, taking some care -about input timeouts. -.It const struct server_sockaddr *server_socket_address; -The address on which this server socket was bound to. See the macros provided -to work with this address later on. -.It const struct server_sockaddr *client_address; -The address of the client issueing the request. See the macros provided -to work with this address later on. -.It const char *server_socket_address_name; -The ASCII C string representation of the address on which this server socket -was bound to. -.It const char *client_address_name; -The ASCII C string representation of the address of the client isseuing the -request. In the case where no address limits nor hostname resolution -is used (this is always the case for AF_LOCAL sockets), will be NULL. -This is because the string of the address is stored into a cache node. -.It const char *client_address_hostname; -If address to hostname resolution was enabled for this socket, consists of the -ASCII hostname C string. NULL otherwise. -.It void *socket_user_data; -The pointer supplied at the -.Nm user_data -parameter for this server socket. May be used for any application-specific -data. -.It u_int32_t client_address_concurrency; -The number of concurrent simultaneous requests from this client address. -Always 0 in the case of AF_LOCAL sockets or for ones which do not have any -address based limits enabled nor hostname resolution. -.It const void *packet_data; -For SOCK_DGRAM sockets only, a pointer to the first received packet from -the client. This packet should be processed by the request handler first -before reading other packets from the client or replying to the request. -.It size_t packet_size; -For SOCK_DGRAM sockets only, the size of the above received -.Nm packet_data -in bytes. -.El -.It Nm struct server_sockaddr -This structure is generally not needed by SOCK_STREAM using this library, -unless address specific limits and hostname resolution are disabled, in which -case it may be useful, the address ASCII C string not being automatically -supplied to the request handlers in this case. It also is useful to -SOCK_DGRAM sockets. It is provided as an address-family independent -alternative to the sockaddr structure, including the family type, and a -pointer to this structure is provided to the client request handler functions -for convenience. An application may optionally use it. See later on the -utility macros which are provided to manipulate this structure easily. -.El -.Ss FUNCTIONS -.Bl -tag -width indent -offset left -.It void Fn server_init "void" -Must be called before any other function of this library can be called. -Otherwise other functions will error, setting errno to EPERM. -.It void * Fn server_shmem_malloc "size_t size" -Allows the application to optionally allocate blocks of shared memory which -shall be available to all processes. -.Fa size -specifies the size of the block of memory to allocate. -Returned is the address of the new shared memory block, or NULL. -Can be called multiple times, but must be called before -.Fn server_start . -The application will need to ensure proper synchronization among the -concurrent processes itself, using -.Fn server_rwlock_ctl -as needed to access these shared resources. There are no provided functions -to free or resize these memory blocks until the application terminates. -If dynamic allocation of shared memory objects is needed, it is recommended -to use the -.Xr mmpool 3 -library in conjunction with this function, and to ensure to create the -pool to not dynamically be resized, with a fixed number of maximum objects. -.It int Fn server_rwlocks_init "const char *path" "int num" \ -"const char *user" "const char *group" "mode_t mode" -Must be called before -.Fn server_start . -Allows the application to allocate -.Fa num -number of read-write locks which may be used for synchronization of shared -resources among concurrent processes. -.Fa path -specifies the full path of a filename to be used to serve as a template to -create the internal lock files. -.Fa user -consists of the user which should be set as the lock files owner, or an empty -string to keep the default creation owner. -.Fa group -similarily consists of the group which should be set for the files, or an empty -string for the default group. -.Fa mode -specifies the permissions mode which should be set to the lock files. -Note that these permissions should be set properly in order for children -processes to be able to reopen the files in read mode (a requirement for -.Xr flock 2 ) . -Returns 0 on success or -1 on error. Can only be called once, the required -number of locks should be specified for the whole application. -.It int Fn server_rwlock_ctl "int id" "int op" -Can only be called after -.Fn server_start . -Allows to obtain or release an advisory lock on one of the previously -created locks by -.Fn server_rwlocks_init . -When in blocking mode, this function internally ensures to restart -upon signal events. It is possible to upgrade a shared read-access lock -to an exclusive write-access lock or to downgrade an exclusive lock -to a shared one by calling this function again with the new wanted lock -type. However, it is not guarenteed that another process will not be able -to obtain the lock in between the status change. -.Fa id -specifies the lock ID number to operate on, 0 being the first lock. -.Fa op -tells which type of operation should be performed on the lock, using -bitmasks which can be ORed: -.Bl -tag -width indent -offset left -.It SHL_READ -Obtain a shared read access lock on the object. -.It SHL_WRITE -Obtain an exclusive read-write access lock on the object. -.It SHL_FREE -Release any currently held lock by this process from the object. -.It SHL_TRY -In the case of SHL_READ or SHL_WRITE, specifies that the process should -not block if the lock cannot immediately be obtained, but that the function -should instead return -1 with errno set to EAGAIN. -.El -.Pp -This function returns 0 on success, or -1 on error. Unlike -.Xr flock 2 , -will internally restart upon EINTR events when SHL_TRY is not used. -This means that -1 will never be returned with EINTR from this function. -.It int Fn server_socket_bind "struct server_socket_config *config" -Allows to register a server socket to listen on. Can only be called -before -.Fn server_start . -.Fa config -supplies a pointer to the socket-specific configuration. The description of -the parameters are described in the -.Nm struct server_socket_config -section. Note that the supplied configuration structure will be copied -internally such that it does not matter if it resides on the stack. -This functon returns -1 on error (in which case it releases any resources -it internally allocated meanwhile and can be called again), or 0 on success. -Can be called as many times as necessary to register all wanted sockets. -Sockets of multiple address famillies and types may be registered. -.It int Fn server_start "struct server_config *config" -Actually launches the whole system. The current process will exit if this -function succeeds, with the server detached from any tty and transformed into -a daemon. The children process pool will be created and the system becomes -ready to accept client requests. -.Fa config -supplies the configuration structure, which is described in -the -.Nm struct server_config -section. -Returns -1 on error, in which case an attempt is made to release the -internally allocated resources while not unregistering the sockets. -In normal operation, however, the application generally exits if this fails. -.It int Fn server_exit "void" -Can only be called after -.Fn server_start . -Allows the application to properly exit. Can be called either by the parent -process (via a handler or hook) or by any child process. Note that it is -an error for an application to use the -.Xr exit 3 -or -.Xr _exit 2 -function calls after -.Fn server_start -success. In case of applications using -.Xr execve 2 , -the -.Fn server_execve -wrapper function should be used instead, which will internally -.Xr _exit 2 -if necessary. -.It int Fn server_recycle "bool interrupt" -Can only be called after -.Fn server_start . -Provided so that the application can register a SIGHUP handler function -.Nm parent_sighup_hook -in -.Nm struct server_config -and then call this function whenever it finishes it's wanted tasks, such as -re-reading a configuration file. Because this is done into the parent process, -and that the new changes can only be seen by newly started children processes, -they must be recycled. The way in which this is done can be specified via the -.Fa interrupt -parameter. TRUE means that it should interrupt any currently being served -requests (which can cause the -.Nm request_interrupt_hook -function, if any, to be called to allow the application to send an -application-specific protocol-friendly message to the clients upon closing). -FALSE means that current clients should not be dropped but that each process -should recycle whenever it can (as soon as it finishes serving the current -request). -.It void Fn server_close "void" -Can only be called by the children processes after -.Fn server_start . -Although the client connection (for SOCK_STREAM sockets) is automatically -closed as needed when the request handler function returns, it is possible to -explicitely end the request calling this function. This function never -returns, the process immediately becomes ready to serve another request. -If any -.Nm request_close_hook -function was provided, it also will automatically be called, either after -normal request termination or forced termination triggered by this function. -Note that it is an error to call the -.Xr exit 3 -or -.Xr _exit 2 -functions after -.Fn server_start -success. In the case where an application uses -.Xr execve 2 , -with -.Nm using_execve -set to TRUE, the -.Fn server_execve -function should be used. -.It unsigned int Fn server_alarm "unsigned int seconds" -An -.Xr alarm 3 -clone which internally uses -.Xr setitimer 2 . -Can only be called after -.Fn server_start . -Utility function which can be used to implement input timeouts into -child processes request handlers. Can also of course be used in conjunction -with the -.Xr mmalarm 3 -timers multiplexer if wanted. Unlike -.Xr alarm 3 , -always returns 0. -.It int Fn server_execve "const char *path" "char *const argv[]" \ -"char *const envp[]" -consists of an -.Xr execve 2 -replacement which application programs should call instead, in conjunction -with the -.Nm using_execve -flag of -.Nm struct server_config -set to TRUE. This function can only return if -.Fn server_start -was not yet successfully called or if -.Nm using_execve -is FALSE, or if not called by a child process currently serving a SOCK_STREAM -connection (-1 with EPERM). Otherwise, it will internally call -.Xr _exit 2 -itself in case of a failure when invoking -.Xr execve 2 . -It however will log an error if returning from -.Xr execve 2 , -since it is expected to be an application bug. See the -.Xr execve 2 -manual page for more details about calling semantics. -This function also automatically links the client socket filedescriptor -to the stdin and stdout streams of the process, as well as restoring -signal behavior to the default action. Moreover, shared memory will be -freed before launching the program, and unwanted filedescriptors closed -because of the close-on-exec flag set with -.Xr fcntl 2 -when creating the sockets. An application using -.Nm -should most definitely use this function instead of -.Xr execve 2 . -.El -.Ss MACROS -.Bl -tag -width indent -offset left -.It const char * Fn SERVER_REASON_STRING "int reason" -Utility macro to map a rejected reason number to a printable string. Useful -to use within the -.Nm reject_handler -function. -.El -.Pp -The following macros are especially useful to work with the family independent -.Nm struct server_sockaddr -structure, for AF_INET, AF_INET6 and AF_LOCAL: -.Bl -tag -width indent -offset left -.It socklen_t Fn SERVER_SOCKADDR_SOCKLEN "struct server_sockaddr *addr" -Useful to determine the length of the family-specific internal sockaddr_* -structure for the family-independent -.Nm struct server_sockaddr -.Fa addr -pointer. -.It int * Fn SERVER_SOCKADDR_PORT "struct server_sockaddr *addr" -Returns a pointer to the port associated with the address of the specified -.Nm struct server_sockaddr -.Fa addr -pointer. This port is always in network byte order, so the -.Xr htons 3 -function should be used to set it, and the -.Xr ntohs 3 -function to query it. -Returns NULL if the address corresponds to the AF_LOCAL family. -.It sa_family_t * Fn SERVER_SOCKADDR_FAMILY "struct server_socaddr *addr" -Returns the sa_family_t pointer to the address family identifyer (AF_INET, -AF_INET6, AF_LOCAL) for the specified -.Nm struct server_sockaddr -.Fa addr -pointer. -.It void * Fn SERVER_SOCKADDR_ADDRESS "struct server_sockaddr *addr" -Returns the family-specific IP address of the specified -.Fa addr . -This is especially useful when using the -.Xr inet_ntop 3 -and -.Xr inet_pton 3 -functions, for instance, which can be provided the addr->family field for -their address family argument, and a pointer provided by this macro for -the address argument. -.It struct sockaddr * Fn SERVER_SOCKADDR "struct server_sockaddr *addr" -Utility macro to return the family-independent -.Nm struct sockaddr -pointer of the specified -.Nm struct server_sockaddr -.Fa addr -pointer. To avoid having to use &addr->u.sockaddr. -.El -.Sh RETURN VALUES -.Bl -tag -width indent -offset indent -.It Xo -.Fn server_init , -.Fn server_close -.Xc -return nothing. -.It Fn server_shmem_malloc -returns a pointer on success, or NULL on error. -.It Xo -.Fn server_rwlocks_init , -.Fn server_rwlock_ctl , -.Fn server_socket_bind , -.Fn server_start , -.Fn server_exit , -.Fn server_recycle , -.Xc -return 0 on success, or -1 on error, in which case errno is set. -.It Fn server_alarm -returns 0 on success, or (unsigned int)-1 on error. -.It Fn server_execve -returns -1 on error, but normally never returns. -.El -.Sh ERRORS -The errno global variable can be set to the following when a function -returns -1 for error: -.Bl -tag -width Er -.It Bq Er EPERM -The library does not allow this function to be called in this order. -This may be caused by a failure to call -.Fn server_init -or in a function which should be called before or after -.Fn server_start . -Another possibility is -.Fn server_execve -being called when -.Nm using_execve -is FALSE and/or the child process is not currently serving a SOCK_STREAM -connected client. -.It Bq Er EINVAL -An invalid parameter was supplied, either in a configuration structure -or as parameters to the function. -.It Bq Er EAGAIN -(only for -.Fn server_rwlock_ctl ) , -SHL_TRY was used and the lock could not immediately be obtained. -.It Bq Er ENOMEM -There was a failure to allocate the necessary memory. -.El -.Pp -Other errno codes may be set and then are the result of internal errors -when calling the C library functions or kernel syscalls, such as -.Xr open 2 , -.Xr mmap 2 , -etc. -.Sh SEE ALSO -.Xr inet 4 , -.Xr inet6 4 , -.Xr ip 4 , -.Xr ip6 4 , -.Xr unix 4 , -.Xr mmap 2 , -.Xr flock 2 , -.Xr socket 2 , -.Xr bind 2 , -.Xr connect 2 , -.Xr recvfrom 2 , -.Xr sendto 2 , -.Xr recv 2 , -.Xr send 2 , -.Xr read 2 , -.Xr write 2 , -.Xr lchown 2 , -.Xr lchmod 2 , -.Xr setsid 2 , -.Xr fcntl 2 , -.Xr execve 2 , -.Xr _exit 2 , -.Xr getnameinfo 3 , -.Xr inet_ntop 3 , -.Xr inet_pton 3 , -.Xr htons 3 , -.Xr ntohs 3 , -.Xr exit 3 , -.Xr mmserver 3 , -.Xr mmalarm 3 , -.Xr mmpool 3 , -.Xr mmhash 3 , -.Xr mmlist 3 , -.Xr mmheap 3 , -.Xr mmlimitrate 3 . -.Sh HISTORY -.Nm -was written by Matthew Mondor, to use on NetBSD 1.6.2 in March 2004 and -was released under a BSD-style license. -.Sh AUTHORS -.Nm -as well as this documentation were -written by Matthew Mondor and are -Copyright (c) 2004, Matthew Mondor, All rights reserved. -.Sh BUGS -Please report any bug to mmondor@accela.net diff --git a/mmsoftware/mmlib/mmserver2.c b/mmsoftware/mmlib/mmserver2.c deleted file mode 100644 index a0cdb7a..0000000 --- a/mmsoftware/mmlib/mmserver2.c +++ /dev/null @@ -1,2976 +0,0 @@ -/* $Id: mmserver2.c,v 1.51 2005/12/09 21:08:33 mmondor Exp $ */ - -/* - * Copyright (C) 2004, Matthew Mondor - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Matthew Mondor. - * 4. The name of Matthew Mondor may not be used to endorse or promote - * products derived from this software without specific prior written - * permission. - * 5. Redistribution of source code may not be released under the terms of - * any GNU Public License derivate. - * - * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -/* XXX TODO XXX - * - Perhaps allow support for stderr appended to a file for debugging if - * application wants to, in the case of using_execve == TRUE. - * - Add some support for request maximum timeout (at least for using_execve - * applications, since there is no other way to cause execve(2) requests - * to interrupt than to kill the process from another one (the parent could). - * This however is tricky. Applications which don't use execve(2) don't need - * such a timer as they can set one up themselves. However, if they do use - * execve(2), the children processes cannot rely on SIGALRM anymore. They - * then need another process to interrupt them using SIGTERM if they need - * to be interrupted after a timeout. The parent could do it with it's - * timer_ctx via mmalarm(3) easily, but because the children are answering - * and handling the incomming requests, how can they tell the parent to - * start such a timer for them, unless I used a special IPC mechanism. - * If they had to launch their own control process using fork(2) it would - * slow down the system too much... I could perhaps use some kind of - * AF_LOCAL SOCK_DGRAM trick. A child process would simply send a packet - * with it's pid_t, causing a new timeout to be created in the parent to - * kill that pid_t with SIGTERM if it expires. However, we also need to - * get rid of any existing entry at the SIGCHLD handler... server_execve() - * would be responsible for starting the timer before calling execve(2), - * noticing the parent to do so. The SIGCHLD handler in the parent would - * be responsible for stopping the timer if it did not expire already. - */ - - - -/* HEADERFILES */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#ifdef __GLIBC__ -#include -#include -#endif /* __GLIBC__ */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - - - -MMCOPYRIGHT("@(#) Copyright (c) 2004\n\ -\tMatthew Mondor. All rights reserved.\n"); -MMRCSID("$Id: mmserver2.c,v 1.51 2005/12/09 21:08:33 mmondor Exp $"); - - - -/* DEFINITIONS */ - -/* - * We store a node for each child process with it's current status. Each of - * them have the address of their node, so that they efficiently may change - * their status. We do not need a synchronization lock since every child - * process only accesses it's own node, to which it has a pointer, and because - * only the master parent process adds or removes these nodes. - */ -enum process_status { - PSTAT_READY = 0, - PSTAT_BUSY, - PSTAT_DEAD, - PSTAT_RECYCLE -}; -struct child_process_node { - pnode_t node; - pid_t pid; - volatile enum process_status status; - u_int32_t served; - /* - * The following are only used for using_execve == TRUE applications, - * for use by the SIGCHLD signal handler of the parent process. - */ - struct socket_node *snode; - struct address_node *anode; -}; - -/* - * The following nodes are also only used for applications using execve(2), so - * that the SIGCHLD handler of the parent may efficently link the pid_t of the - * dieing children processes to their status node. - */ -struct child_pid_status_node { - hashnode_t node; - pid_t pid; /* Key */ - struct child_process_node *status; -}; - -/* Permissions for a file */ -struct perms { - uid_t uid; - gid_t gid; - mode_t mode; -}; - -/* Internal synchronization locks */ -enum shlocks { - SHLOCK_ACCEPT, - SHLOCK_HOSTNAMES, - SHLOCK_MAX -}; - -/* - * Useful macros to clean the code up; Also easy to modify to add - * diagnostic/debugging info as needed. - */ -#define LOCK(fd) do { \ - int err; \ - while ((err = flock((fd), LOCK_EX)) == -1 && errno == EINTR) ; \ - if (DEBUG_FALSE(err == -1)) \ - DEBUG_PRINTF("FLOCK", "flock(%d, LOCK_EX) == -1", (fd));\ -} while (/* CONSTCOND */0) - -#define UNLOCK(fd) \ - (void) flock((fd), LOCK_UN) - - - -/* Normal memory which is only used by the parent process */ -struct parent { - /* List of children processes */ - list_t processes_list; - /* - * Table to map pid to children_process structure for fast status - * lookup in our SIGCHLD handler, with corresponding fast allocation - * pool_t. - */ - hashtable_t processes_pid_table; - pool_t processes_pid_pool; - /* List of listening lockets */ - list_t sockets_list; - /* Timers */ - timerctx_t timer_ctx; - /* PSTAT_READY average */ - u_int32_t ready_avg_cnt, ready_avg; - /* - * Flag we set if we already warned because reaching the maximum - * number of processes. - */ - bool maximum_processes_reached; - /* Number of children processes lanched so far */ - u_int32_t launched; -}; - -/* - * Normal memory which is used by all processes and not modified after - * launching children processes. - */ -struct global { - /* TRUE once server_start() was called successfully */ - bool started; - /* PID of parent process */ - pid_t parent_pid; - /* Server configuration */ - struct server_config config; - char ulocks_path[256], slocks_path[256]; - /* Number of user locks and of listening sockets */ - int ulocks_num, sockets_total; - /* Map of pollfd indexes to socket_nodes */ - struct socket_node **sockets_node_index; -}; - -/* Shared memory among all processes, MAP_SHARED */ -struct shared { - /* Memory pool of child_process structures */ - pool_t processes_pool; - /* Memory pool of hostname_node structures */ - pool_t hostnames_pool; - /* Address->hostname cache */ - hashtable_t hostnames_table; -}; - -/* Normal process-specific memory which changes for each process */ -struct local { - /* PID of this current child process */ - pid_t child_pid; - /* - * Link to our child_process node so that we can change our status and - * update our current served requests number. - */ - struct child_process_node *status; - /* Arrays of int/filedescriptors to our open lock files */ - int locks[SHLOCK_MAX], *ulocks, - *sockets_locks; - /* Information concerning the current client being served */ - int socket; - FILE *stream; - struct socket_node *socket_node; - struct address_node *address_node; - struct server_request request; - /* Used so that we may jump back to the main child loop */ - jmp_buf jmpbuf; - /* The pollfd array corresponding to the listening sockets */ - struct pollfd *sockets_pollfd; - /* Used to make server_alarm() more efficient */ - unsigned int alarm_seconds; -}; - - - -/* - * Internally stored for each socket created with server_socket_bind(). - * Everytime server_socket_bind() is called successfully, we store such a new - * node in a list_t (parent.sockets_list). When server_start() is called, - * an array of index with corresponding pollfd and int lock filedescriptors - * is created for efficient internal use in the main server loop. - */ -struct socket_node { - node_t node; - struct server_socket_config config; - struct server_sockaddr address; - int socket, *lock; - pool_t addresses_pool; - hashtable_t addresses_table; - void *user_data; - void *packet_data; - size_t packet_size; -}; - -/* - * We store one such node in the socket-specific address cache, used to - * implement optional rate and/or concurrent number of connections on an - * address basis. Each socket has an independent cache with it's own - * corresponding synchronization lock. - */ -struct address_node { - hashnode_t node; - struct server_sockaddr address; /* Key */ - struct limitrate lr; - u_int32_t concurrent; - /* - * The following is used to speed up hostname cache lookups when - * frequent connections occur from the same address, where the lookup - * only has to be done when this entry is added. We store a reference - * count in the hostname_node structure so that we know when it can - * safely be freed. - */ - struct hostname_node *hostname_node; - char addressname[64]; -}; - -/* - * And we store such nodes in the global address to hostname resolution - * cache, used to speed up hostname lookups of frequently connecting addresses. - * These expire when the reference count reaches 0, in which case all socket- - * specific address_node caches dropped their internal pointer link to us. - * This is a global cache shared by all sockets with a single synchronization - * lock. - */ -struct hostname_node { - hashnode_t node; - struct server_sockaddr address; /* Key */ - u_int32_t refcount; - char hostname[256]; -}; - -/* Used to share information with the iterator */ -struct expire_iterator_udata { - time_t current; - u_int32_t soonest; - hashtable_t *table; -}; - - - -/* STATIC FUNCTIONS PROTOTYPES */ - -static void * i_shmem_malloc(size_t); -static u_int32_t server_sockaddr_hash(const void *, size_t); -static int server_sockaddr_cmp(const void *, const void *, - size_t); -static u_int32_t server_pid_hash(const void *, size_t); -static int server_pid_cmp(const void *, const void *, size_t); -static void pidfile_write(const char *); -static int perms(struct perms *, const char *, const char *, - const char *, mode_t); -static void shlocks_closeonexec(int *, int); - -static pid_t parent_launch(void); -static void parent_main(void); -static bool parent_main_address_iterator(hashnode_t *, void *); -static void parent_main_expire(timerid_t, void *); -static void parent_main_pool_manager(timerid_t, void *); -static void parent_main_timer_hook(timerid_t, void *); -static void parent_sighandler(int); -static void parent_exit(int); - -static pid_t child_launch(void); -static void child_main(void); -static struct address_node *address_open(struct socket_node *, - struct server_sockaddr *, int *); -static void address_close(struct socket_node *, - struct address_node *); -static void child_sighandler(int); -static void child_close(bool); -static void child_exit(int); - - - -/* GLOBALS */ - -/* To map REASON_* to strings */ -const char *const server_reasons_strings[REASON_MAX] = { - "Success", - "Address cache size limit exceeded", - "Concurrency limit reached for this address", - "Request rate limit reached for this address" -}; - -struct server_af_info *server_afi[1]; - -/* - * Only used in the parent process, MAP_COPY. - * It would be possible to use secure wired memory and free it in the - * children processes if it was necessary. - */ -static struct parent parent; - -/* Shared among all processes, MAP_COPY */ -static bool initialized = FALSE; -static struct global global; -static struct server_af_info *i_server_afi = NULL; - -/* Shared memory, MAP_SHARED */ -static struct shared *shared; - -/* Process-specific, MAP_COPY */ -static struct local local; - - - -/* LIBRARY EXPORTED FUNCTIONS */ - -/* Must be called before any other function of this library can be called. */ -void -server_init(void) -{ - - if (!initialized) { - int i, limit; - struct sockaddr_in sin; - struct sockaddr_in6 sin6; - struct sockaddr_un sun; - - /* - * Initialize address family info lookup table. - * This is used to optimize SERVER_SOCKADDR_*() macros. - */ - limit = 0; - if (limit < AF_INET) - limit = AF_INET; - if (limit < AF_INET6) - limit = AF_INET6; - if (limit < AF_LOCAL) - limit = AF_LOCAL; - - if ((i_server_afi = malloc(sizeof(struct server_af_info) * - 3)) == NULL || - (*server_afi = malloc(sizeof(struct server_af_info *) * - limit)) == NULL) { - syslog(LOG_NOTICE, - "server_init() - malloc() - %s", - strerror(errno)); - abort(); - } - - /* AF_INET */ - i_server_afi[0].sock_length = sizeof(struct sockaddr_in); - i_server_afi[0].port_offset = (long)&sin.sin_port - (long)&sin; - i_server_afi[0].addr_offset = (long)&sin.sin_addr - (long)&sin; - /* AF_INET6 */ - i_server_afi[1].sock_length = sizeof(struct sockaddr_in6); - i_server_afi[1].port_offset = (long)&sin6.sin6_port - - (long)&sin6; - i_server_afi[1].addr_offset = (long)&sin6.sin6_addr - - (long)&sin6; - /* AF_LOCAL */ - i_server_afi[2].sock_length = sizeof(struct sockaddr_un); - i_server_afi[2].port_offset = 0; /* Invalid */ - i_server_afi[2].addr_offset = (long)&sun.sun_path - (long)&sun; - - for (i = 0; i < limit; i++) - server_afi[i] = NULL; - server_afi[AF_INET] = &i_server_afi[0]; - server_afi[AF_INET6] = &i_server_afi[1]; - server_afi[AF_LOCAL] = &i_server_afi[2]; - - /* - * Initialize other requirements - */ - parent.maximum_processes_reached = FALSE; - shared = NULL; - *global.ulocks_path = '\0'; - DLIST_INIT(&parent.sockets_list); - /* server_alarm() requires this */ - local.alarm_seconds = 1; - (void) server_alarm(0); - initialized = TRUE; - } -} - -/* - * Allows the caller to initialize shared memory blocks before starting up the - * server. The caller is responsible for remembering the pointers to these - * addresses, as well as to use any necessary synchronization when accessing - * the shared memory concurrently. This must be done at system initialization, - * before launching the server processes, or we return NULL with EPERM. - */ -void * -server_shmem_malloc(size_t size) -{ - - if (!initialized || global.started) { - errno = EPERM; - return NULL; - } - - return shmem_malloc(size); -} - -/* - * Permits the caller to initialize a pool of shared resources synchronization - * read-write locks. This must be done before launching the server processes, - * or we return -1 with EPERM. It can only be called once, make sure to create - * the necessary number of locks at once. - */ -int -server_rwlocks_init(const char *path, int num, const char *user, - const char *group, mode_t mode) -{ - struct perms creds; - - if (!initialized || local.ulocks != NULL) { - errno = EPERM; - return -1; - } - if (path == NULL || *path == '\0' || num < 1 || user == NULL || - group == NULL) { - errno = EINVAL; - return -1; - } - if (perms(&creds, "server_rwlocks_init()", user, group, mode) == -1) - return -1; - - /* - * We don't need to use shared memory for this, we need an independent - * copy in each process even, so that these locks may automatically be - * reopened by new processes (which could cause the filedescriptor to - * change). - */ - (void) mm_strncpy(global.ulocks_path, path, 255); - if ((local.ulocks = malloc(sizeof(int) * num)) == NULL) { - errno = ENOMEM; - return -1; - } - if (shlocks_init(global.ulocks_path, local.ulocks, num, creds.uid, - creds.gid, creds.mode) == -1) { - free(local.ulocks); - local.ulocks = NULL; - syslog(LOG_NOTICE, - "server_rwlocks_init() - shlocks_init() - %s", - strerror(errno)); - /* errno already set to internal error */ - return -1; - } - global.ulocks_num = num; - - return 0; -} - -/* - * Allows to synchronize wanted shared memory resources, locking or unlocking - * read-write locks in shared or exclusive mode, with optional nonblocking - * flag. Locks the process until the lock can be obtained in blocking mode. - * The id corresponds to a lock number index. Can only be used after starting - * the server, and of course only if locks were already setup using - * server_rwlocks_init(), or we return -1 with EPERM. - * We return -1 with EINVAL if the supplied lock id is invalid. - * An attempt was made to make this as abstracted as possible so that it be - * easy to internally implement these synchronization locks differently. - * For now, we use 4.2BSD flock(2). - */ -int -server_rwlock_ctl(int id, int op) -{ - int flockop, err; - - if (!initialized || !global.started || local.ulocks == NULL) { - errno = EPERM; - return -1; - } - if (id < 0 || id >= global.ulocks_num) { - errno = EINVAL; - return -1; - } - - if ((op & SHL_WRITE) != 0) - flockop = LOCK_EX; - else if ((op & SHL_FREE) != 0) - flockop = LOCK_UN; - else if ((op & SHL_READ) != 0) - flockop = LOCK_SH; - else { - errno = EINVAL; - return -1; - } - if ((op & SHL_TRY) != 0) - flockop |= LOCK_NB; - - if ((flockop & LOCK_NB) == 0 && (flockop & LOCK_UN) == 0) { - /* - * We need to loop until operation is done in case we are - * interrupted by a signal (in which case errno == EINTR) - */ - while ((err = flock(local.ulocks[id], flockop)) == -1 && - errno == EINTR) ; - } else - err = flock(local.ulocks[id], flockop); - - return err; -} - -/* - * Allows to bind to an AF_INET(6) or AF_LOCAL SOCK_DGRAM or SOCK_STREAM - * listening socket. Can only be called before starting the server or -1 is - * returned with errno set to EPERM. - */ -int -server_socket_bind(struct server_socket_config *config) -{ - struct socket_node *snode; - const char *error = NULL; - struct perms creds; - - if (!initialized || global.started) { - errno = EPERM; - return -1; - } - - /* If any of the following conditions is true, will return EINVAL */ - errno = EINVAL; - - /* - * Perform some sanity checking on supplied configuration. - * Very ugly - */ - if (config->family != AF_INET && config->family != AF_INET6 && - config->family != AF_LOCAL) { - DEBUG_PRINTF("server_socket_bind", - "Invalid address family %d", config->family); - return -1; - } - if (config->type != SOCK_STREAM && config->type != SOCK_DGRAM) { - DEBUG_PRINTF("server_socket_bind", "Invalid socket type %d", - config->type); - return -1; - } - if (config->family == AF_LOCAL) { - if (config->port != 0) { - DEBUG_PRINTF("server_socket_bind", - "Port number not allowed for AF_LOCAL"); - return -1; - } - if (config->address_resolve) { - DEBUG_PRINTF("server_socket_bind", - "Hostname resolution not allowed for AF_LOCAL"); - return -1; - } - if (config->address_rate_limits) { - DEBUG_PRINTF("server_socket_bind", - "Address limits not allowed for AF_LOCAL"); - return -1; - } - if (config->address_concurrency_limits) { - DEBUG_PRINTF("server_socket_bind", - "Address concurrency limits not allowed for " - "AF_LOCAL"); - return -1; - } - if (config->address_cache_size != 0) { - DEBUG_PRINTF("server_socket_bind", - "Address cache size should be 0 for AF_LOCAL"); - return -1; - } - if (config->reject_handler != NULL) { - DEBUG_PRINTF("server_socket_bind", - "Reject handler should be NULL for AF_LOCAL " - "sockets"); - return -1; - } - if (perms(&creds, "server_socket_bind()", config->socket_user, - config->socket_group, config->socket_mode) == -1) { - return -1; - } - } else { - if (config->port < 1 || config->port > 65535) { - DEBUG_PRINTF("server_socket_bind", - "Invalid port number %d", config->port); - return -1; - } - } - if (*(config->bind_address) == '\0') { - DEBUG_PRINTF("server_socket_bind", "No address supplied"); - return -1; - } - if (config->request_handler == NULL) { - DEBUG_PRINTF("server_socket_bind", - "No request_handler() supplied"); - return -1; - } - if (config->address_rate_limits || config->address_resolve || - config->address_concurrency_limits) { - if (config->address_cache_size == 0) { - DEBUG_PRINTF("server_socket_bind", - "Address cache size should be specified if limits" - " or hostname resolution is required"); - return -1; - } - } else { - if (config->address_cache_size != 0) { - DEBUG_PRINTF("server_socket_bind", - "Address cache size should be 0 when no limits " - "nor hostname resolution is needed"); - return -1; - } - } - if (config->address_rate_limits) { - if (config->address_cache_rate_limit == 0) { - DEBUG_PRINTF("server_socket_bind", - "Address rate limits enabled but no limit " - "specified"); - return -1; - } - } else { - if (config->address_cache_rate_limit != 0) { - DEBUG_PRINTF("server_socket_bind", - "Address rate limits disabled but a limit was " - "specified"); - return -1; - } - } - if (config->address_cache_size != 0 && - config->address_cache_rate_period < 1) { - DEBUG_PRINTF("server_socket_bind", - "Address cache period should be specified with cache " - "size"); - return -1; - } - if (config->address_concurrency_limits) { - if (config->address_cache_concurrency_limit == 0) { - DEBUG_PRINTF("server_socket_bind", - "Address concurrency limits " - "enabled without specified limit"); - return -1; - } - } else { - if (config->address_cache_concurrency_limit != 0) { - DEBUG_PRINTF("server_socket_bind", - "Address concurrency limits " - "disabled but a limit was specified"); - return -1; - } - } - if (config->type == SOCK_DGRAM && config->packet_size == 0) { - DEBUG_PRINTF("server_socket_bind", - "Packet size must be specified for SOCKET_DGRAM sockets"); - return -1; - } - if (config->type == SOCK_STREAM && config->packet_size != 0) { - DEBUG_PRINTF("server_socket_bind", - "Packet size should be 0 for SOCKET_STREAM sockets"); - return -1; - } - - /* - * Allocate socket_node and initialize it. We allocate it as shared - * memory since the hashtable_t and pool_t structures for the address - * cache need to be shared. - */ - if ((snode = i_shmem_malloc(sizeof(struct socket_node))) == NULL) { - errno = ENOMEM; - return -1; - } - (void) mm_memcpy(&snode->config, config, - sizeof(struct server_socket_config)); - mm_memclr(&snode->address, sizeof(struct server_sockaddr)); - snode->address.ss_family = snode->config.family; - snode->packet_data = NULL; - - /* Create socket */ - if ((snode->socket = socket(snode->address.ss_family, - snode->config.type, 0)) == -1) { - if (snode->address.ss_family == AF_INET) - error = "socket(AF_INET)"; - else if (snode->address.ss_family == AF_INET6) - error = "socket(AF_INET6)"; - else if (snode->address.ss_family == AF_LOCAL) - error = "socket(AF_LOCAL)"; - else - error = "socket(AF_UNKNOWN)"; - goto err; - } - /* Allow to set application-specific socket options */ - if (snode->config.setsockopts != NULL) { - if (snode->config.setsockopts(snode->socket) != 0) { - error = "setsockopts()"; - goto err; - } - } - - /* bind(2) it */ - if (snode->address.ss_family != AF_LOCAL) { - *(SERVER_SOCKADDR_PORT(&snode->address)) = - htons(snode->config.port); - if (inet_pton(snode->address.ss_family, - snode->config.bind_address, - SERVER_SOCKADDR_ADDRESS(&snode->address)) != 1) { - syslog(LOG_NOTICE, - "server_socket_bind() - Invalid %s address '%s'", - (snode->address.ss_family == AF_INET ? - "IPv4" : "IPv6"), snode->config.bind_address); - goto err; - } - } else - (void) mm_strncpy(SERVER_SOCKADDR_ADDRESS(&snode->address), - snode->config.bind_address, 100); - if (bind(snode->socket, SERVER_SOCKADDR(&snode->address), - SERVER_SOCKADDR_SOCKLEN(&snode->address)) == -1) { - error = "bind()"; - goto err; - } - - if (config->family == AF_LOCAL) { - /* Apply specified permissions on filesystem file */ - if (lchown(snode->config.bind_address, creds.uid, creds.gid) - == -1) { - error = "lchown()"; - goto err; - } -#ifdef __GLIBC__ - if (chmod(snode->config.bind_address, creds.mode) == -1) { -#else /* __GLIBC__ */ - if (lchmod(snode->config.bind_address, creds.mode) == -1) { -#endif /* __GLIBC__ */ - error = "lchmod()"; - goto err; - } - } - - /* listen(2) if necessary */ - if (snode->config.type == SOCK_STREAM) { - if (listen(snode->socket, snode->config.backlog) == -1) { - error = "listen()"; - goto err; - } - } - - /* - * If we must respect concurrency and/or rate limits we need to - * initialize our socket-specific address cache and associated memory - * pool. The socket-specific synchronization locks used to access - * this cache are crated later on at server_start(). - */ - if (snode->config.address_cache_size != 0) { - if (!pool_init(&snode->addresses_pool, "addresses_pool", - i_shmem_malloc, shmem_free, NULL, NULL, - sizeof(struct address_node), - snode->config.address_cache_size, 1, 1)) { - error = "pool_init()"; - errno = ENOMEM; - goto err; - } - if (!hashtable_init(&snode->addresses_table, "addresses_table", - snode->config.address_cache_size, 1, i_shmem_malloc, - shmem_free, server_sockaddr_cmp, server_sockaddr_hash, - FALSE)) { - error = "hashtable_init()"; - errno = ENOMEM; - goto err; - } - } - - /* Success, remember socket and return */ - DLIST_APPEND(&parent.sockets_list, (node_t *)snode); - return 0; -err: - /* Failure, free any dangling resources */ - if (snode != NULL) { - if (POOL_VALID(&snode->addresses_pool)) - (void) pool_destroy(&snode->addresses_pool); - if (HASHTABLE_VALID(&snode->addresses_table)) - hashtable_destroy(&snode->addresses_table, FALSE); - if (snode->socket != -1) - (void) close(snode->socket); - shmem_free(snode); - } - if (error != NULL) - syslog(LOG_NOTICE, "server_socket_bind() - %s - %s", - error, strerror(errno)); - return -1; -} - -/* - * Finally launches the whole thing and start to serve connections. At least - * sockets must have been setup using server_socket_bind() before calling this - * function, or -1 is returned with EPERM. Otherwise this function never - * returns on success. If this function returns, the application is expected - * to exit(3). If it doesn't, an attempt is still made to not leak resources. - */ -int -server_start(struct server_config *config) -{ - int i, *slocks = NULL; - struct pollfd *fds = NULL; - struct socket_node **socks = NULL, *snode; - u_int32_t resolve_cache_size; - struct perms creds; - - if (!initialized || global.started || - DLIST_NODES(&parent.sockets_list) == 0) { - errno = EPERM; - return -1; - } - - /* Default errno value */ - errno = EINVAL; - - /* - * Perform some sanity checking on supplied configuration. - * Ugly a bit - */ - if (config->locks_path == '\0') { - syslog(LOG_NOTICE, - "server_start() - config->locks_path not set"); - return -1; - } - if (perms(&creds, "server_start()", config->locks_user, - config->locks_group, config->locks_mode) == -1) - return -1; - if (config->null_path == '\0') { - syslog(LOG_NOTICE, - "server_start() - config->null_path not set"); - return -1; - } - if (config->pid_path == '\0') { - syslog(LOG_NOTICE, - "server_start() - config->pid_path not set"); - return -1; - } - if (config->children_initial < 1) { - syslog(LOG_NOTICE, - "server_start() - config->children_initial < 1"); - return -1; - } - if (config->children_maxspare < config->children_minspare) { - syslog(LOG_NOTICE, - "server_start() - config->children_maxspare < " - "config->children_minspare"); - return -1; - } - if (config->children_maximum < - config->children_initial + config->children_maxspare) { - syslog(LOG_NOTICE, - "server_start() - config->children_maximum < " - "config->children_initial + config->children_maxspare"); - return -1; - } - if (config->children_average_seconds < 60) { - syslog(LOG_NOTICE, - "server_start() - config->children_average_seconds < 60"); - return -1; - } - if (config->children_maxrequests < 100) { - syslog(LOG_NOTICE, - "server_start() - config->children_maxrequests < 100"); - return -1; - } - if (config->parent_timer_hook != NULL) { - if (config->parent_timer_seconds == 0) { - syslog(LOG_NOTICE, - "server_start() - " - "config->parent_timer_hook != NULL && " - "config->parent_timer_seconds == 0"); - return -1; - } - } else { - if (config->parent_timer_seconds != 0) { - syslog(LOG_NOTICE, - "server_start() - " - "config->parent_timer_hook == NULL && " - "config->parent_timer_seconds != 0"); - return -1; - } - } - - /* Remember configuration globally */ - mm_memcpy(&global.config, config, sizeof(struct server_config)); - - /* - * Initialize necessary structures for our sockets; Our pollfd array - * and corresponding socket_node index array. Also create our - * socket-specific locks and hashtable_t if we must observe any - * limits. Note that we still create these locks if a socket needs - * no limits, but that they will not be used for such sockets. - */ - i = DLIST_NODES(&parent.sockets_list); - if ((socks = malloc(sizeof(struct socket_node *) * i)) == NULL || - (fds = malloc(sizeof(struct pollfd) * i)) == NULL || - (slocks = malloc(sizeof(int) * i)) == NULL) { - errno = ENOMEM; - goto err; - } - i = 0; - resolve_cache_size = 0; - DLIST_FOREACH(&parent.sockets_list, snode) { - socks[i] = snode; - fds[i].fd = snode->socket; - fds[i].events = POLLIN; - fds[i].revents = 0; - if (snode->config.address_resolve) - resolve_cache_size += snode->config.address_cache_size; - snode->lock = &slocks[i]; - /* - * Here we allocate memory on the normal heap for the packet - * buffer which needs to be process-specific. We however tie - * this buffer pointer to the shared structure, which still - * shouldn't hurt since the vm space of children processes - * should cause the pointer to still point to process-specific - * heap data, fork(2) duplicating the vm space. - */ - if (snode->config.packet_size != 0 && - snode->packet_data == NULL) - snode->packet_data = malloc(snode->config.packet_size); - i++; - } - /* - * Account for the extra entry which might be used when multiple - * processes concurrently call getnameinfo(3) for the same address - * node - */ - if (resolve_cache_size != 0) - resolve_cache_size += config->children_maximum; - local.sockets_pollfd = fds; - global.sockets_node_index = socks; - global.sockets_total = i; - local.sockets_locks = slocks; - - /* Allocate required shared memory */ - if ((shared = shmem_malloc(sizeof(struct shared))) == NULL) { - syslog(LOG_NOTICE, "server_start() - shmem_malloc(shared)"); - errno = ENOMEM; - goto err; - } - - /* - * Initialize hostname cache. We ensure that this cache be large - * enough to be shared among sockets and to accomodate the total of - * all their allowed addresses. Of course, we only take in - * consideration sockets with hostname resolution enabled. - * Although this cache may be rather large, mmpool(3) ensures to - * reuse recently accessed pages, so that BSD should be able to page - * out unnecessary pages of the cache (especially that we use - * madvise(2) with MADV_RANDOM). - */ - if (resolve_cache_size != 0) { - if (!pool_init(&shared->hostnames_pool, "hostnames_pool", - i_shmem_malloc, shmem_free, NULL, NULL, - sizeof(struct hostname_node), resolve_cache_size, 1, 1)) { - syslog(LOG_NOTICE, - "server_start() - pool_init(hostnames_pool)"); - errno = ENOMEM; - goto err; - } - if (!hashtable_init(&shared->hostnames_table, - "hostnames_table", resolve_cache_size, 1, - i_shmem_malloc, shmem_free, - server_sockaddr_cmp, server_sockaddr_hash, FALSE)) { - syslog(LOG_NOTICE, - "server_start() - " - "hashtable_init(hostnames_table)"); - errno = ENOMEM; - goto err; - } - } - - /* - * Initialize our shared synchronization locks (internal ones and - * socket specific ones). - */ - (void) snprintf(global.slocks_path, 255, "%s-S", - global.config.locks_path); - if (shlocks_init(global.config.locks_path, local.locks, SHLOCK_MAX, - creds.uid, creds.gid, creds.mode) == -1 || - shlocks_init(global.slocks_path, local.sockets_locks, - global.sockets_total, creds.uid, creds.gid, creds.mode) == -1) { - syslog(LOG_NOTICE, "server_start() - shlocks_init() - %s", - strerror(errno)); - goto err; - } - - /* - * Initialize global process status stuff. Although the allocation - * pool_t for child_process structures is stored in shared memory, - * the child_pid_status_node one, processes_list and - * processes_pid_table can all use the standard heap since only the - * parent accesses them. We use a list_t for the list which needs to - * frequently be iterated through for speed, and a hashtable_t if - * needed for pid->status lookup table if using_execve is enabled. - */ - if (!pool_init(&shared->processes_pool, "processes_pool", - i_shmem_malloc, shmem_free, NULL, NULL, - sizeof(struct child_process_node), - global.config.children_maximum, 1, 1)) { - syslog(LOG_NOTICE, - "server_start() - pool_init(shared->processes_pool)"); - errno = ENOMEM; - goto err; - } - DLIST_INIT(&parent.processes_list); - if (global.config.using_execve) { - if (!pool_init(&parent.processes_pid_pool, - "processes_pid_pool", - malloc, free, NULL, NULL, - sizeof(struct child_pid_status_node), - global.config.children_maximum, 1, 1)) { - syslog(LOG_NOTICE, - "server_start() - " - "pool_init(parent.processes_pid_pool)"); - errno = ENOMEM; - goto err; - } - if (!hashtable_init(&parent.processes_pid_table, - "processes_pid_table", global.config.children_maximum, 1, - malloc, free, server_pid_cmp, server_pid_hash, FALSE)) { - syslog(LOG_NOTICE, - "server_start() - " - "hashtable_init(parent.processes_pid_table)"); - errno = ENOMEM; - goto err; - } - } - - /* - * Launch the whole thing. parent_launch() also will call the - * registered parent_init_hook() function if any. It also detaches - * the process from any tty, and creates its own process group. - */ - if (parent_launch() == -1) { - syslog(LOG_NOTICE, "server_start() - parent_launch() - %s", - strerror(errno)); - goto err; - } - /* NOTREACHED */ - exit(EXIT_SUCCESS); - -err: - /* - * Free any allocated resources to avoid leaking memory or - * filedescriptors in the case where the application does not exit(3) - * if we fail. - */ - if(global.config.using_execve) { - if (HASHTABLE_VALID(&parent.processes_pid_table)) - hashtable_destroy(&parent.processes_pid_table, FALSE); - if (POOL_VALID(&parent.processes_pid_pool)) - (void) pool_destroy(&parent.processes_pid_pool); - } - if (POOL_VALID(&shared->processes_pool)) - (void) pool_destroy(&shared->processes_pool); - (void) shlocks_destroy(global.config.locks_path, local.locks, - SHLOCK_MAX, TRUE); - if (slocks != NULL) - (void) shlocks_destroy(global.slocks_path, local.sockets_locks, - global.sockets_total, TRUE); - if (HASHTABLE_VALID(&shared->hostnames_table)) - hashtable_destroy(&shared->hostnames_table, FALSE); - if (POOL_VALID(&shared->hostnames_pool)) - (void) pool_destroy(&shared->hostnames_pool); - if (shared != NULL) { - shmem_free(shared); - shared = NULL; - } - if (slocks != NULL) - free(slocks); - if (fds != NULL) - free(fds); - if (socks != NULL) - free(socks); - - return -1; -} - -/* - * Can be used in the case of fatal errors or to cause the server to exit. Can - * be called by the parent or the children processes. Can only be called after - * server_start(), or -1 is returned with EPERM. - */ -int -server_exit(void) -{ - sigset_t set; - - if (!initialized || !global.started) { - errno = EPERM; - return -1; - } - - /* Kill ourself with SIGTERM */ - kill(global.parent_pid, SIGTERM); - /* Just idle until SIGTERM is processed by our signal handler */ - (void) sigemptyset(&set); - for (;;) - (void) sigsuspend(&set); - - /* NOTREACHED */ - return 0; -} - -/* - * Can only be called by the parent process (typically in the SIGHUP handler - * function). This causes all current children processes to recycle whenever - * they complete their current handler. If interrupt is TRUE, causes them to - * also immediately abort their current handler and then recycle. Returns -1 - * with EPERM if not called by the parent process. - */ -int -server_recycle(bool interrupt) -{ - sigset_t set, oset; - struct child_process_node *node; - - if (!initialized || !global.started || getpid() != global.parent_pid) { - errno = EPERM; - return -1; - } - - /* - * Block SIGALRM so that we may not be bothered by the process manager - * freeing nodes, and SIGHUP to not recurse. We also block SIGUSR1 so - * that the children processes do not cause the process manager to be - * called during this time. - */ - (void) sigemptyset(&set); - (void) sigaddset(&set, SIGALRM); - (void) sigaddset(&set, SIGUSR1); - (void) sigaddset(&set, SIGHUP); - oset = set; - (void) sigprocmask(SIG_BLOCK, &set, NULL); - - if (interrupt) { - /* - * We must abort any currently active connections and recycle - * all processes. To do so, we send a SIGHUP to each of our - * children. We do not send the signal to the whole process - * group since the parent must not receive it. - */ - DLIST_FOREACH(&parent.processes_list, node) { - if (node->status == PSTAT_BUSY || - node->status == PSTAT_READY) - (void) kill(node->pid, SIGHUP); - } - } else { - /* - * We must cause each process to recycle when they finish - * handling their current connection. The way we do this is - * causing the currently served number of connections for - * processes in PSTAT_BUSY state to be higher than the limit - * so that they recycle when finished but sending the SIGHUP - * signal to all PSTAT_READY ones. - */ - DLIST_FOREACH(&parent.processes_list, node) { - if (node->status == PSTAT_BUSY) - node->served = - global.config.children_maxrequests + 1; - else if (node->status == PSTAT_READY) - (void) kill(node->pid, SIGHUP); - } - } - - /* Restore normal signal mask */ - (void) sigprocmask(SIG_UNBLOCK, &oset, NULL); - - /* - * Now ensure to cause the process manager to perform a round - * immediately, in case the SIGUSR1 signals sent by the children to - * the parent process were not queued during the time we were - * blocking the signal (they are queued on NetBSD, but let's not - * assume they always are). - */ - (void) kill(global.parent_pid, SIGUSR1); - - return 0; -} - -/* - * Although when exiting the handler() function the connection is automatically - * closed if need be (I.E. a SOCK_STREAM socket), it is possible to call this - * function to perform the same functionality. This also handles the address - * cache limits update. - */ -void -server_close(void) -{ - - child_close(TRUE); - /* NOTREACHED */ -} - -/* - * A somewhat alarm(3) compatible function using setitimer(2). Suitable to be - * used standalone instead of the deprecated alarm(3), or to be internally used - * with mmalarm(3). Makes sure to not call setitimer(2) when called with 0 - * seconds to disable any timer if no timer is currently running, as an - * optimization. - */ -unsigned int -server_alarm(unsigned int seconds) -{ - struct itimerval itv; - - timerclear(&itv.it_interval); - timerclear(&itv.it_value); - if (seconds != 0) - itv.it_value.tv_sec = local.alarm_seconds = seconds; - if (local.alarm_seconds != 0) { - local.alarm_seconds = seconds; - if (setitimer(ITIMER_REAL, &itv, NULL) == -1) { - syslog(LOG_NOTICE, - "server_alarm(%d) - setitimer() - %s", - seconds, strerror(errno)); - return (unsigned int)-1; - } - } - - return 0; -} - -/* - * execve(2) replacement performing some library-specific sanity checking - * and never returning if calling execve(2). We also make sure to tie the - * current client connection to our stdin/stdout filedescriptors, and to - * leave stderr tied to null(4). We return -1 with EPERM if a SOCK_STREAM - * socket is not being served by a children process. - * XXX It might be good to append stderr to a file if the application requests - * it, like in mmspawnd(8). - */ -int -server_execve(const char *path, char * const *argv, char * const *envp) -{ - - /* Some sanity checking */ - if (!initialized || !global.started || !global.config.using_execve || - local.socket == -1 || local.socket_node == NULL || - local.socket_node->config.type != SOCK_STREAM) { - errno = EPERM; - syslog(LOG_NOTICE, "server_execve() - %s", strerror(errno)); - return -1; - } - - /* - * Setup stdin/stdout tied to local.socket and close local.socket. - * stderr is already redirected to null(4). - */ - (void) dup2(local.socket, STDIN_FILENO); - (void) dup2(local.socket, STDOUT_FILENO); - /* local.socket always > 2 in this case */ - (void) close(local.socket); - - /* - * Restore default signals actions. If we do not do this and the - * current process ignored any signal, they will be ignored after - * execve(2). Ones we handle will be automaticaly restored to the - * default action. - */ - { - struct sigaction act; - - act.sa_handler = SIG_DFL; - act.sa_flags = SA_NOCLDSTOP; - (void) sigemptyset(&act.sa_mask); - (void) sigaction(SIGUSR1, &act, NULL); - (void) sigaction(SIGTTOU, &act, NULL); - (void) sigaction(SIGTTIN, &act, NULL); - (void) sigaction(SIGTSTP, &act, NULL); - } - - /* - * And finally call execve(2). If it ever returns, log error and - * _exit(2). - */ - (void) execve(path, argv, envp); - syslog(LOG_NOTICE, "server_execve() - execve(2) returned! - %s", - strerror(errno)); - _exit(EXIT_FAILURE); - /* NOTREACHED */ -} - - - -/* LIBRARY INTERNAL SUPPORT FUNCTIONS */ - -/* - * Utility function to allocate shared memory and advise the virtual memory - * kernel manager on BSD systems, about the fact that access to pages will be - * random in general. - */ -static void * -i_shmem_malloc(size_t size) -{ - void *mem; - - if ((mem = shmem_malloc(size)) != NULL) { -#ifndef __GLIBC__ - /* - * Use 4.4BSD madvise(2) to specify that we will access these - * pages randomly for better vm efficiency. - */ - { - void *raddr; - size_t rsize; - - if (shmem_getregion(&raddr, &rsize, mem)) { - if (madvise(raddr, rsize, MADV_RANDOM) == -1) - DEBUG_PRINTF("i_shmem_malloc", - "madvise() - %s", strerror(errno)); - } - } -#endif - } - - return mem; -} - -/* Used to efficiently hash to 32-bit the supplied server_sockaddr keys */ -/* ARGSUSED */ -static u_int32_t -server_sockaddr_hash(const void *d, size_t len) -{ - const struct server_sockaddr *sa = d; - u_int32_t hash = 0, *words; - - switch (sa->ss_family) { - case AF_INET: - /* Very fast, already stored internally as a 32-bit value */ - hash = (u_int32_t)sa->u.sockaddr_in.sin_addr.s_addr; - break; - case AF_INET6: - /* Requires hashing 4 32-bit words, rather fast */ - words = (u_int32_t *)&sa->u.sockaddr_in6.sin6_addr; - /* hash = words[0] ^ words[1] ^ words[2] ^ words[3]; */ - hash = words[0] + (67306411U * hash); - hash = words[1] + (67306411U * hash); - hash = words[2] + (67306411U * hash); - hash = words[3] + (67306411U * hash); - break; - default: - DEBUG_PRINTF("server_sockaddr_hash", - "Invalid family %d!", sa->ss_family); - } - - return hash; -} - -/* And to efficiently compare the supplied server_sockaddr keys */ -/* ARGSUSED */ -static int -server_sockaddr_cmp(const void *s, const void *d, size_t len) -{ - const struct server_sockaddr *sa = s, *da = d; - u_int32_t *sawords, *dawords; - - if (sa->ss_family != da->ss_family) - return -1; - - switch(sa->ss_family) { - case AF_INET: - /* Single 32-bit word */ - if (sa->u.sockaddr_in.sin_addr.s_addr != - da->u.sockaddr_in.sin_addr.s_addr) - return -1; - break; - case AF_INET6: - /* 4 32-bit words */ - sawords = (u_int32_t *)&sa->u.sockaddr_in6.sin6_addr; - dawords = (u_int32_t *)&da->u.sockaddr_in6.sin6_addr; - if (sawords[0] != dawords[0] || sawords[1] != dawords[1] || - sawords[2] != dawords[2] || sawords[3] != dawords[3]) - return -1; - break; - default: - DEBUG_PRINTF("server_sockaddr_cmp", - "Invalid family %d!", sa->ss_family); - return -1; - } - - return 0; -} - -/* To efficiently hash pid_t */ -/* ARGSUSED */ -static u_int32_t -server_pid_hash(const void *d, size_t len) -{ - - return (u_int32_t)(*(pid_t *)d); -} - -/* And efficiently compare pid_t */ -/* ARGSUSED */ -static int -server_pid_cmp(const void *s, const void *d, size_t len) -{ - - return (int)((*(pid_t *)s) - (*(pid_t *)d)); -} - -/* - * Writes our process ID number to specified file. To be called before - * chroot(2) or dropping privileges. - */ -static void -pidfile_write(const char *file) -{ - char str[16]; - int fd; - - if ((fd = open(file, O_CREAT | O_TRUNC | O_WRONLY, 0600)) != -1) { - (void) snprintf(str, 15, "%d\n", getpid()); - (void) write(fd, str, mm_strlen(str)); - (void) close(fd); - } else - syslog(LOG_NOTICE, "pidfile_write() - open(%s) - %s", - file, strerror(errno)); -} - -/* Useful to perform sanity checking on permissions and resolve them to IDs */ -static int -perms(struct perms *p, const char *func, const char *user, const char *group, - mode_t mode) -{ - struct passwd *pwd; - struct group *grp; - - if (p == NULL || user == NULL || group == NULL) { - errno = EINVAL; - return -1; - } - - if (*user != '\0') { - if ((pwd = getpwnam(user)) != NULL) - p->uid = pwd->pw_uid; - else { - errno = EINVAL; - syslog(LOG_NOTICE, "%s - Invalid user '%s'", - func, user); - return -1; - } - } else - p->uid = -1; - - if (*group != '\0') { - if ((grp = getgrnam(group)) != NULL) - p->gid = grp->gr_gid; - else { - errno = EINVAL; - syslog(LOG_NOTICE, "%s - Invalid group '%s'", - func, group); - return -1; - } - } else - p->gid = -1; - - mode &= ~(S_IXUSR | S_IXGRP | S_IXOTH | S_ISUID | S_ISGID | S_ISVTX); - if (mode == 0) { - errno = EINVAL; - syslog(LOG_NOTICE, "%s - Invalid mode '000'", func); - return -1; - } - p->mode = mode; - - return 0; -} - -/* - * Useful after shlocks_reopen() in children processes if using_execve is - * TRUE - */ -static void -shlocks_closeonexec(int *fds, int num) -{ - int i; - - for (i = 0; i < num; i++) { - if (fds[i] != -1) { - if (fcntl(fds[i], F_SETFD, 1) == -1) - syslog(LOG_NOTICE, - "shlocks_closeonexec() - fcntl()"); - } - } -} - - -/* Process control related */ - -/* - * Launches the main parent master process, detaching from any tty. - * The real work starts in parent_main(), which is called after we succeed. - */ -static pid_t -parent_launch(void) -{ - pid_t pid; - int fd; - - /* Create new process */ - if ((pid = fork()) == -1) - return -1; - if (pid != 0) - exit(EXIT_SUCCESS); - - pidfile_write(global.config.pid_path); - - /* Create new process group and detach */ - (void) setsid(); - (void) chdir("/"); - if ((fd = open(global.config.null_path, O_RDWR)) != -1) { - (void) dup2(fd, STDIN_FILENO); - (void) dup2(fd, STDOUT_FILENO); - (void) dup2(fd, STDERR_FILENO); - if (fd > STDERR_FILENO) - (void) close(fd); - } else - syslog(LOG_NOTICE, "parent_launch() - open(%s) - %s", - global.config.null_path, strerror(errno)); - - if (global.config.using_execve) { - /* - * Set line buffering mode for stdio streams, to be inherited - * by children processes. - */ - (void) setvbuf(stdin, NULL, _IOLBF, 0); - (void) setvbuf(stdout, NULL, _IOLBF, 0); - (void) setvbuf(stderr, NULL, _IOLBF, 0); - } - - /* Setup our signal handler and ignore unwanted signals */ - { - struct sigaction act; - - act.sa_handler = parent_sighandler; - act.sa_flags = SA_NOCLDSTOP; - (void) sigemptyset(&act.sa_mask); - (void) sigaction(SIGSEGV, &act, NULL); - (void) sigaction(SIGTERM, &act, NULL); - (void) sigaction(SIGCHLD, &act, NULL); - (void) sigaction(SIGUSR1, &act, NULL); - (void) sigaction(SIGHUP, &act, NULL); - (void) sigaction(SIGALRM, &act, NULL); - - act.sa_handler = SIG_IGN; - (void) sigaction(SIGTTOU, &act, NULL); - (void) sigaction(SIGTTIN, &act, NULL); - (void) sigaction(SIGTSTP, &act, NULL); - } - - /* Reopen lock files */ - { - bool ok; - - ok = TRUE; - /* Internal locks */ - if (shlocks_reopen(global.config.locks_path, local.locks, - SHLOCK_MAX) == -1) - ok = FALSE; - /* Socket locks */ - if (shlocks_reopen(global.slocks_path, local.sockets_locks, - global.sockets_total) == -1) - ok = FALSE; - /* User locks */ - if (local.ulocks != NULL) { - if (shlocks_reopen(global.ulocks_path, local.ulocks, - global.ulocks_num) == -1) - ok = FALSE; - } - if (!ok) { - syslog(LOG_NOTICE, - "parent_launch() - shlocks_reopen() - %s", - strerror(errno)); - parent_exit(EXIT_FAILURE); - } - } - - /* Initialize our timer_ctx */ - if (!timer_ctx_init(&parent.timer_ctx, malloc, free, time, - server_alarm)) { - syslog(LOG_NOTICE, "parent_launch() - timer_ctx_init() - %s", - strerror(errno)); - parent_exit(EXIT_FAILURE); - } - - /* Register our process ID for all processes to use */ - global.parent_pid = getpid(); - - parent.launched = 0; - - /* Finally run */ - parent_main(); - - /* NOTREACHED */ - return 0; -} - -/* - * Main parent process loop. We ensure to manage the children processes pool, - * as well as flush expired client addresses entries from each of the socket's - * caches. We also then flush no longer referenced hostname cached entries - * from the global hostname resolution cache. - */ -static void -parent_main(void) -{ - sigset_t set; - struct expire_iterator_udata data; - struct socket_node *snode; - u_int32_t maxperiod, old_launched; - volatile bool node_expired; - time_t tim; - -#ifndef __GLIBC__ - (void) setproctitle("%s: (Parent) launched 0 processes", - global.config.proc_title); -#endif /* ! __GLIBC__ */ - - global.started = TRUE; - old_launched = parent.launched; - - if (global.config.parent_init_hook != NULL) { - if (global.config.parent_init_hook() != 0) { - syslog(LOG_NOTICE, - "parent_main() - parent_init_hook() != 0"); - parent_exit(EXIT_FAILURE); - } - } - (void) sigemptyset(&set); - - /* - * Setup our process pool management timer. This timer restarts and - * causes parent_main_pool_manager() to be called every second. - */ - tim = time(NULL); - parent.ready_avg_cnt = parent.ready_avg = 0; - (void) timer_init(&parent.timer_ctx, tim, 1, parent_main_pool_manager, - NULL, TRUE); - - /* If wanted, setup our user parent timer hook */ - if (global.config.parent_timer_hook != NULL) - (void) timer_init(&parent.timer_ctx, tim, - global.config.parent_timer_seconds, - parent_main_timer_hook, NULL, TRUE); - - /* - * Start by forking a few children processes. child_launch() - * automatically adds the new child_process_node for the new process - * status. - */ - { - u_int32_t i; - - for (i = 0; i < global.config.children_initial; i++) { - if (child_launch() == -1) { - syslog(LOG_NOTICE, "child_launch()"); - parent_exit(EXIT_FAILURE); - } - } - } - - /* If we get here we definitely can say that we successfully started */ - syslog(LOG_NOTICE, "Started"); - - /* - * Set initial timeout to maximum allowed but for the soonest - * expireing socket timer. then becomes the maximum - * amount of time we should ever sleep for. Using 300 seconds - * (five minutes) is reasonable here. - */ - maxperiod = 300; - DLIST_FOREACH(&parent.sockets_list, snode) { - if (snode->config.address_cache_size != 0 && - snode->config.address_cache_rate_period < maxperiod) - maxperiod = snode->config.address_cache_rate_period; - } - data.soonest = maxperiod; - for (;;) { - /* - * First setup a timer so that we be notified whenever the - * first to expire node is to be flushed. - */ - node_expired = FALSE; - (void) timer_init(&parent.timer_ctx, tim, data.soonest, - parent_main_expire, (void *)&node_expired, FALSE); - - /* - * The following causes this process to sleep until the - * occurance of the next signal (typically SIGALRM or SIGUSR1 - * in this case). This way we may still manage our pool of - * processes every second or immediately when obtaining a - * SIGUSR1, while also verifying for our expiration timer to - * complete. - */ - while (!node_expired) { -#ifndef __GLIBC__ - if (old_launched != parent.launched) { - old_launched = parent.launched; - (void) setproctitle( - "%s: (Parent) launched %u processes", - global.config.proc_title, old_launched); - } -#endif /* ! __GLIBC__ */ - (void) sigsuspend(&set); - } - - /* - * Update current time, allows us to only call time(2) once - * per loop only. Although we may use locks in the following - * block and perform some operations, the time will be rather - * close, if not accurate. - */ - tim = time(NULL); - - if (node_expired) { - /* - * Acquire the global hostname cache lock since the - * following code will need to modify it. - */ - LOCK(local.locks[SHLOCK_HOSTNAMES]); - /* - * For each socket-specific address cache, reset - * expired nodes, garbage collect, and set - * data.soonest to the soonest expireing node so that - * we know how many seconds to sleep next time. - * This also takes care of the hostname cache. - */ - data.current = tim; - data.soonest = maxperiod; - DLIST_FOREACH(&parent.sockets_list, snode) { - if (HASHTABLE_VALID(&snode->addresses_table)) { - LOCK(*snode->lock); - data.table = &snode->addresses_table; - hashtable_iterate( - &snode->addresses_table, - parent_main_address_iterator, - &data); - UNLOCK(*snode->lock); - } - } - UNLOCK(local.locks[SHLOCK_HOSTNAMES]); - } - } - /* NOTREACHED */ - - exit(EXIT_SUCCESS); -} - -/* - * Iterate through all nodes of a hashtable_t to reset or expire address - * and hostname cache nodes. It is called by the above function whenever - * the soonest to expire node timer expires. This function also leaves - * the caller with the next to expire time so that a timer may be restarted. - */ -static bool -parent_main_address_iterator(hashnode_t *hnode, void *udata) -{ - struct address_node *anode = (struct address_node *)hnode; - struct expire_iterator_udata *data = udata; - time_t rem; - - /* - * If the node expired, reset it. For those which do not, record the - * soonest to expire one. For nodes which expired and for which no - * connections exist anymore, expunge them, and update the - * hostname_node reference count entry, freeing the hostname cache - * entry if it has no more references. - */ - - if ((rem = LR_REMAINS(&anode->lr, data->current)) != 0) { - /* Reset this entry */ - LR_EXPIRE(&anode->lr, data->current); - rem = LR_REMAINS(&anode->lr, data->current); - goto out; - } - - /* This entry expired, check for concurrency */ - if (anode->concurrent != 0) - goto out; - - /* - * Safe to expunge this node from the cache. - * Also take care of the hostname_node if needed. - */ - if (anode->hostname_node != NULL) { - if ((--anode->hostname_node->refcount) == 0) { - hashtable_unlink(&shared->hostnames_table, - (hashnode_t *)anode->hostname_node); - (void) pool_free((pnode_t *)anode->hostname_node); - } - } - hashtable_unlink(data->table, (hashnode_t *)anode); - (void) pool_free((pnode_t *)anode); - -out: - if (rem != 0 && data->soonest > rem) - data->soonest = rem; - - return TRUE; -} - -/* - * Useful function to switch a flag variable whenever a particular timer - * associated with it expires, to be used with mmalarm(3) library. - */ -/* ARGSUSED */ -static void -parent_main_expire(timerid_t tid, void *udata) -{ - volatile bool *expired = udata; - - *expired = TRUE; -} - -/* - * This function permits to manage the pool of children processes. It is - * called every second. - */ -/* ARGSUSED */ -static void -parent_main_pool_manager(timerid_t tid, void *udata) -{ - sigset_t set, oset; - u_int32_t ready, busy, dead, recycle, total; - int i, to; - struct child_process_node *node, *tmp; - - /* - * We do not actually need to account all the states, but do so - * anyways in case we need a fancier algorithm later on. - */ - - /* - * First block SIGALRM and SIGUSR1 signals for the duration of our - * loop, making us uninterruptible by our own events. Also SIGCHLD - * in case we have using_execve set. - */ - (void) sigemptyset(&set); - (void) sigaddset(&set, SIGALRM); - (void) sigaddset(&set, SIGUSR1); - (void) sigaddset(&set, SIGCHLD); - oset = set; - (void) sigprocmask(SIG_BLOCK, &set, NULL); - - /* - * Run through list of processes, accounting the total of each - * status type, and flushing PSTAT_DEAD and PSTAT_RECYCLE entries. - */ - for (ready = busy = dead = recycle = 0, - total = DLIST_NODES(&parent.processes_list), - node = DLIST_TOP(&parent.processes_list); node != NULL; - node = tmp) { - tmp = DLIST_NEXT(node); - switch (node->status) { - case PSTAT_READY: - ready++; - break; - case PSTAT_BUSY: - busy++; - break; - case PSTAT_DEAD: - dead++; - DLIST_UNLINK(&parent.processes_list, (node_t *)node); - (void) pool_free((pnode_t *)node); - break; - case PSTAT_RECYCLE: - recycle++; - DLIST_UNLINK(&parent.processes_list, (node_t *)node); - (void) pool_free((pnode_t *)node); - break; - } - } - - /* First restart any processes which need recycling immediately */ - if (recycle > 0) { - for (i = 0; i < recycle; i++) - (void) child_launch(); - ready += recycle; - recycle = 0; - } - - /* - * Determine if more processes are needed, in which case we may - * launch more immediately, unless CHILDREN_MAXIMUM is reached. - * We need to make sure that there always at least are - * CHILDREN_MINSPARE PSTAT_READY processes. - */ - if (ready < global.config.children_minspare) { - for (i = 0, to = global.config.children_minspare - ready; - total < global.config.children_maximum && i < to; i++) { - (void) child_launch(); - ready++; - total++; - } - } - if (!parent.maximum_processes_reached && - total == global.config.children_maximum) { - /* - * Warn that the maximum number of processes was reached, - * only once - */ - syslog(LOG_NOTICE, - "Warning: maximum number of processes reached (%u)", - global.config.children_maximum); - parent.maximum_processes_reached = TRUE; - } - - /* - * Now determine if we can safely kill processes which are not - * in use since some time. To do this we maintain average - * statistics, so that we may not unnecessarily kill and create - * new processes often if the load is distributed in bursts rather - * than evenly. We spread that average calculation on - * CHILDREN_AVERAGE_SECONDS (specifying the number of samples). - * We kill the offending processes but let them go to PSTAT_DEAD state - * themselves, nodes which are freed at the next run. - */ - if (parent.ready_avg_cnt == global.config.children_average_seconds) { - parent.ready_avg /= parent.ready_avg_cnt; - if ((to = parent.ready_avg - global.config.children_maxspare) - > 0) { - for (i = 0, node = DLIST_TOP(&parent.processes_list); - i < to && node != NULL; - node = DLIST_NEXT(node)) { - if (node->status == PSTAT_READY) { - (void) kill(node->pid, SIGTERM); - i++; - } - } - } - parent.ready_avg_cnt = parent.ready_avg = 0; - } else { - parent.ready_avg += ready; - parent.ready_avg_cnt++; - } - - /* We can now unblock signals to resume normal operation. */ - (void) sigprocmask(SIG_UNBLOCK, &oset, NULL); -} - -/* - * Only called if a user parent timer was specified which should execute at - * fixed intervals. - */ -/* ARGSUSED */ -static void -parent_main_timer_hook(timerid_t tid, void *udata) -{ - sigset_t set, oset; - - /* - * First block SIGALRM and SIGUSR1 signals for the duration of our - * loop, making us uninterruptible by our own events. - */ - (void) sigemptyset(&set); - (void) sigaddset(&set, SIGALRM); - (void) sigaddset(&set, SIGUSR1); - oset = set; - (void) sigprocmask(SIG_BLOCK, &set, NULL); - - global.config.parent_timer_hook(); - - /* - * We can now unblock SIGALRM and SIGUSR1 signals to resume normal - * operation. - */ - (void) sigprocmask(SIG_UNBLOCK, &oset, NULL); -} - -/* - * This function handles all signals we are interested about in the parent - * master process. - */ -static void -parent_sighandler(int sig) -{ - - switch (sig) { - case SIGSEGV: - syslog(LOG_NOTICE, "Received SIGSEGV! Exiting"); - parent_exit(EXIT_FAILURE); - /* NOTREACHED */ - break; - case SIGTERM: - syslog(LOG_NOTICE, "Received SIGTERM, exiting"); - parent_exit(EXIT_SUCCESS); - /* NOTREACHED */ - break; - case SIGCHLD: - { - int status = 0; - pid_t pid; - sigset_t set, oset; - struct child_pid_status_node *snode; - int cnt; - - /* Reap our dieing children processes. */ - if (!global.config.using_execve) { - /* - * Fast path, nothing else to do since child - * called child_exit() and parent will know - * status. - */ - for (;;) { - while ((pid = wait3(&status, WNOHANG, - NULL)) == -1 && errno == EINTR) ; - if (pid < 1) - break; - } - break; - } - - /* - * Application uses execve(2) to serve clients. - * Block needed signals for atomic operations, use our - * pid to status lookup table and see if status is - * PSTAT_BUSY, in which case set status to - * PSTAT_RECYCLE, and perform any necessary cleanup - * related to address and hostname caches. Do this - * for each process we receive exit status from using - * wait3(2). Afterwards, if we did so for any pid, - * execute the pool manager which will restart the - * needed processes. We also discard the pid->status - * lookup nodes here, but let the pool manager free - * actual status entries. - */ - (void) sigemptyset(&set); - (void) sigaddset(&set, SIGCHLD); - (void) sigaddset(&set, SIGUSR1); - (void) sigaddset(&set, SIGHUP); - (void) sigaddset(&set, SIGALRM); - oset = set; - (void) sigprocmask(SIG_BLOCK, &set, NULL); - - for (cnt = 0; ; ) { - while ((pid = wait3(&status, WNOHANG, - NULL)) == -1 && errno == EINTR) ; - if (pid < 1) - break; - if (!WIFEXITED(status)) - continue; - - if (WEXITSTATUS(status) != 0) - syslog(LOG_NOTICE, - "Child %d terminated with code %d", - (int)pid, WEXITSTATUS(status)); - - /* is pid_t of child which just exited */ - if ((snode = (struct child_pid_status_node *) - hashtable_lookup( - &parent.processes_pid_table, &pid, - sizeof(pid_t))) == NULL) - continue; - - if (snode->status->status == PSTAT_BUSY) { - /* - * Must cleanup. address_close() is - * safe to call here since it will - * lock the filedescriptor at an - * integer array index rather than a - * process-specific fd, so we are sure - * that it will use our own lock fd at - * same index offset. - */ - snode->status->status = PSTAT_RECYCLE; - address_close(snode->status->snode, - snode->status->anode); - cnt++; - } - - hashtable_unlink(&parent.processes_pid_table, - (hashnode_t *)snode); - (void) pool_free((pnode_t *)snode); - } - - (void) sigprocmask(SIG_UNBLOCK, &oset, NULL); - - if (cnt != 0) - /* - * At least one process cleaned up, recycle - * it/them. - */ - parent_main_pool_manager(-1, NULL); - } - break; - case SIGUSR1: - /* Force an immediate processes pool management round */ - parent_main_pool_manager(-1, NULL); - break; - case SIGHUP: - /* The custom sighup handler hook will call server_recycle() */ - if (global.config.parent_sighup_hook != NULL) - global.config.parent_sighup_hook(); - break; - case SIGALRM: - timer_ctx_execute(&parent.timer_ctx, time(NULL)); - break; - default: - syslog(LOG_NOTICE, - "parent_sighandler() - Unexpected signal %d", sig); - } -} - -/* Main exit point of the parent process */ -static void -parent_exit(int code) -{ - sigset_t set; - struct socket_node *snode; - struct child_process_node *pnode; - - /* - * Block SIGALRM, SIGHUP and SIGUSR1 then send SIGTERM to all children - * processes which we are allowed to kill. Then make sure that they - * exit. We however won't kill processes which are currently serving - * requests if exit_interrupt_children is FALSE. We instead will cause - * them to exit after they terminate serving their request. - */ - (void) sigemptyset(&set); - (void) sigaddset(&set, SIGALRM); - (void) sigaddset(&set, SIGHUP); - (void) sigaddset(&set, SIGUSR1); - (void) sigprocmask(SIG_BLOCK, &set, NULL); - - DLIST_FOREACH(&parent.processes_list, pnode) { - if (pnode->status == PSTAT_BUSY) { - if (global.config.exit_interrupt_requests) - (void) kill(pnode->pid, SIGTERM); - else - pnode->served = - global.config.children_maxrequests + 1; - } else if (pnode->status == PSTAT_READY) - (void) kill(pnode->pid, SIGTERM); - } - - /* Call user exit hook if any */ - if (global.config.parent_exit_hook != NULL) - global.config.parent_exit_hook(); - - /* - * unlink(2) our files... However, in case where we cannot interrupt - * ongoing requests, we must leave lock files dangling since the - * remaining requests may still need to use flock(2) on them. - * And we cannot rely on a hack assuming that on some systems - * unlinking the file before closing it will cause the file to only - * be deleted once the last instance is closed. It becomes the - * responsibility of the application to use a creation mode of 0600 - * instead of 0400 then if the application is to reuse existing lock - * files once restarted. - */ - - /* AF_LOCAL dangling sockets if any */ - DLIST_FOREACH(&parent.sockets_list, snode) { - if (snode->config.family == AF_LOCAL) { - (void) close(snode->socket); - (void) unlink(snode->config.bind_address); - } - } - - /* Lock files */ - if (global.config.exit_interrupt_requests) { - if (local.locks != NULL && global.config.locks_path != '\0') - (void) shlocks_destroy(global.config.locks_path, - local.locks, SHLOCK_MAX, TRUE); - if (local.sockets_locks != NULL && global.slocks_path != '\0') - (void) shlocks_destroy(global.slocks_path, - local.sockets_locks, global.sockets_total, TRUE); - if (local.ulocks != NULL && *global.ulocks_path != '\0') - (void) shlocks_destroy(global.ulocks_path, - local.ulocks, global.ulocks_num, TRUE); - } - - /* And finally parent pid file. */ - (void) unlink(global.config.pid_path); - - exit(code); -} - - -/* - * Starts up a new child process in our pool of processes, which will - * immediately start to be ready to process client requests in PSTAT_READY - * state. - */ -static pid_t -child_launch(void) -{ - pid_t pid; - struct child_process_node *cnode; - struct child_pid_status_node *snode = NULL; - - /* - * Allocate new process status node and link it. This memory consists - * of shared memory. We don't need to hold any locks here since only - * the parent process ever accesses the linking points. - */ - if ((cnode = (struct child_process_node *)pool_alloc( - &shared->processes_pool, FALSE)) == NULL) { - syslog(LOG_NOTICE, - "child_launch() - pool_alloc(shared->processes_pool)"); - return -1; - } - DLIST_APPEND(&parent.processes_list, (node_t *)cnode); - - if (global.config.using_execve) { - /* - * Allocate corresponding pid->status lookup node, which we - * however can only link if process creation succeeds, since - * we are linking using the pid_t as a key in the hashtable_t. - */ - if ((snode = (struct child_pid_status_node *)pool_alloc( - &parent.processes_pid_pool, FALSE)) == NULL) { - syslog(LOG_NOTICE, - "child_launch() - " - "pool_alloc(parent.processes_pid_pool)"); - DLIST_UNLINK(&parent.processes_list, (node_t *)cnode); - (void) pool_free((pnode_t *)cnode); - return -1; - } - } - - /* Start new process */ - if ((pid = fork()) == -1) { - if (snode != NULL) - (void) pool_free((pnode_t *)snode); - DLIST_UNLINK(&parent.processes_list, (node_t *)cnode); - (void) pool_free((pnode_t *)cnode); - syslog(LOG_NOTICE, "child_launch() - fork() - %s", - strerror(errno)); - return -1; - } - if (pid != 0) { - cnode->pid = pid; - cnode->status = PSTAT_READY; - cnode->served = 0; - if (snode != NULL) { - /* - * Link fast pid_t->status lookup node now that we - * have pid_t. - */ - cnode->snode = NULL; - cnode->anode = NULL; - snode->pid = pid; - snode->status = cnode; - (void) hashtable_link(&parent.processes_pid_table, - (hashnode_t *)snode, &snode->pid, sizeof(pid_t), - FALSE); - } - parent.launched++; - return pid; - } - - /* Create own process group if requested */ - if (global.config.children_setsid) - (void) setsid(); - - (void) chdir("/"); - - /* Setup signal handler */ - { - struct sigaction act; - sigset_t set; - - act.sa_handler = child_sighandler; - act.sa_flags = SA_NOCLDSTOP; - (void) sigemptyset(&act.sa_mask); - (void) sigaction(SIGSEGV, &act, NULL); - (void) sigaction(SIGTERM, &act, NULL); - (void) sigaction(SIGPIPE, &act, NULL); - (void) sigaction(SIGALRM, &act, NULL); - (void) sigaction(SIGHUP, &act, NULL); - - act.sa_handler = SIG_IGN; - (void) sigaction(SIGUSR1, &act, NULL); - - /* - * Because the signal mask is inherited from the parent and - * that it blocks these when starting us with fork(2), let's - * unblock these. - */ - (void) sigemptyset(&set); - (void) sigaddset(&set, SIGALRM); - (void) sigaddset(&set, SIGUSR1); - (void) sigaddset(&set, SIGCHLD); - (void) sigprocmask(SIG_UNBLOCK, &set, NULL); - } - - /* Reopen lock files */ - { - bool ok; - - ok = TRUE; - /* Internal locks */ - if (shlocks_reopen(global.config.locks_path, local.locks, - SHLOCK_MAX) == -1) - ok = FALSE; - /* Socket locks */ - if (shlocks_reopen(global.slocks_path, local.sockets_locks, - global.sockets_total) == -1) - ok = FALSE; - /* User locks */ - if (local.ulocks != NULL) { - if (!shlocks_reopen(global.ulocks_path, local.ulocks, - global.ulocks_num) == -1) - ok = FALSE; - } - if (!ok) { - syslog(LOG_NOTICE, - "child_launch() - shlocks_reopen() - %s", - strerror(errno)); - exit(EXIT_FAILURE); - } - } - if (global.config.using_execve) { - int i; - - /* - * Set close on exec flag for lock filedescriptors, they - * should be automatically closed whenever the process uses - * execve(2). - */ - shlocks_closeonexec(local.locks, SHLOCK_MAX); - shlocks_closeonexec(local.sockets_locks, global.sockets_total); - if (local.ulocks != NULL) - shlocks_closeonexec(local.ulocks, global.ulocks_num); - - /* Do the same for listening socket filedescriptors */ - for (i = 0; i < global.sockets_total; i++) - (void) fcntl(local.sockets_pollfd[i].fd, F_SETFD, 1); - } - - /* Initialize global variables within this process */ - local.status = cnode; - local.socket = -1; - local.stream = NULL; - local.child_pid = getpid(); - /* Disable any pending timer */ - (void) server_alarm(0); - - /* Finally run */ - child_main(); - - /* NOTREACHED */ - return 0; -} - -/* - * The main children process loop. Listen for a client request and process it, - * and then do the same again until we reached our maximum number of requests - * to process, in which case we exit to be replaced immediately. - */ -static void -child_main(void) -{ - int err, i, reason; - struct server_sockaddr addr; - socklen_t addrl; - struct socket_node *snode; - - /* Call user child initialization hook if any */ - if (global.config.child_init_hook != NULL) { - if (global.config.child_init_hook() != 0) { - syslog(LOG_NOTICE, - "child_main() - child_init_hook() != 0"); - child_exit(PSTAT_DEAD); - } - } - - /* - * Main loop starting point; We use setjmp(3) here so that we can - * at any time resume here (including at SIGPIPE signal handler) - * without the need for a finite state machine. - */ - (void) sigsetjmp(local.jmpbuf, 1); - /* Advertize to parent that we're a ready process (not PSTAT_BUSY) */ - local.status->status = PSTAT_READY; - - for (;;) { -#ifndef __GLIBC__ - /* BSD owns */ - (void) setproctitle("%s: (Child) served %u requests (READY)", - global.config.proc_title, local.status->served); -#endif /* ! __GLIBC__ */ - - if (local.status->served >= - global.config.children_maxrequests) { - /* - * This process served its maximum allowed number of - * connections, set our status so that a replacement - * process be launched soon and exit. - */ - child_exit(PSTAT_RECYCLE); - } - - /* - * We must obtain the accept lock, sleeping until it is - * acquired. This is used to properly distribute the clients - * among the ready processes. - */ - if (global.config.serialization_lock) - LOCK(local.locks[SHLOCK_ACCEPT]); - - /* - * Now poll(2) to know which socket we should take care of. - * Our main idle loop either sleeps at flock(2) or poll(2). - */ - while ((err = poll(local.sockets_pollfd, - global.sockets_total, -1)) == -1 && errno == EINTR) ; - if (err > 0) { - for (i = 0; i < global.sockets_total; i++) { - if ((local.sockets_pollfd[i].revents & POLLIN) - != 0) - break; - } - } else - i = global.sockets_total; - - if (global.sockets_total <= i) { - if (err == -1) - syslog(LOG_NOTICE, - "child_main() - accept()/connect() - %s", - strerror(errno)); - goto err; - } - - /* consists of the socket number to take care of */ - snode = global.sockets_node_index[i]; - - /* - * Before releasing the accept lock we must either accept(2) - * on the TCP port or recvfrom(2) for UDP. This should - * normally not block the process as we just used poll(2). - * First set addrl to the proper length for the address family. - */ - local.socket = -1; - addr.ss_family = snode->config.family; - addrl = SERVER_SOCKADDR_SOCKLEN(&addr); - if (snode->config.type == SOCK_STREAM) { - /* TCP, accept(2) */ - local.socket = accept(snode->socket, - SERVER_SOCKADDR(&addr), &addrl); - } else { - /* UDP, recvfrom(2) */ - if ((snode->packet_size = recvfrom(snode->socket, - snode->packet_data, snode->config.packet_size, 0, - SERVER_SOCKADDR(&addr), &addrl)) != -1) - local.socket = snode->socket; - } - - if (local.socket == -1) { - syslog(LOG_NOTICE, - "child_main() - accept()/connect() - %s", - strerror(errno)); - goto err; - } - - /* We can already release the accept lock */ - if (global.config.serialization_lock) - UNLOCK(local.locks[SHLOCK_ACCEPT]); - - /* - * Make sure to observe limits, and resolve the client address - * to a hostname if needed. If this returns NULL we know that - * the problem consists of a cache size one. In any case, - * if the returned reason is not REASON_OK the request handler - * should not be called; The reject handler can. - * For AF_LOCAL sockets, the cache size is always 0. - */ - local.socket_node = snode; - if (snode->config.address_cache_size != 0) - local.address_node = address_open(snode, &addr, - &reason); - else { - reason = REASON_OK; - local.address_node = NULL; - } - - /* Consider that a request is being served */ - local.status->served++; - local.status->status = PSTAT_BUSY; -#ifndef __GLIBC__ - (void) setproctitle("%s: (Child) served %u requests (BUSY)", - global.config.proc_title, local.status->served); -#endif /* ! __GLIBC__ */ - - /* Create stdio FILE stream if needed */ - if (snode->config.create_stream) { - if ((local.stream = fdopen(local.socket, "rw+")) - == NULL) - syslog(LOG_NOTICE, - "child_main() - fdopen() - %s", - strerror(errno)); - else - (void) setvbuf(local.stream, NULL, _IOLBF, 0); - } - - /* - * Initialize local.request for passing. It's a bit lame - * for efficiency, but allows to define a clean user API which - * does not disclose or depend on internal library structures. - * We at least are not holding any locks at this time. - * We also allow situations where local.address_node == NULL - * in which case no resolving or limits are wanted. - */ - local.request.client_socket = local.socket; - local.request.server_socket_type = snode->config.type; - local.request.server_socket_family = snode->config.family; - local.request.server_socket_port = snode->config.port; - local.request.client_stream = local.stream; - local.request.server_socket_address = &snode->address; - local.request.server_socket_address_name = - snode->config.bind_address; - local.request.server_socket = snode->socket; - if (snode->config.family != AF_LOCAL) - local.request.client_port = - ntohs(*(SERVER_SOCKADDR_PORT(&addr))); - else - local.request.client_port = 0; - local.request.client_address = &addr; - if (local.address_node != NULL) { - local.request.client_address_name = - local.address_node->addressname; - if (local.address_node->hostname_node != NULL) - local.request.client_address_hostname = - local.address_node->hostname_node-> - hostname; - else - local.request.client_address_hostname = NULL; - local.request.client_address_concurrency = - local.address_node->concurrent; - } else { - local.request.client_address_name = NULL; - local.request.client_address_hostname = NULL; - local.request.client_address_concurrency = 0; - } - local.request.socket_user_data = snode->user_data; - if (snode->config.type == SOCK_DGRAM) { - local.request.packet_data = snode->packet_data; - local.request.packet_size = snode->packet_size; - } else { - local.request.packet_data = NULL; - local.request.packet_size = 0; - } - - /* Call proper handler function */ - if (reason == REASON_OK) - snode->config.request_handler(&local.request); - else if (snode->config.reject_handler != NULL) - snode->config.reject_handler(&local.request, reason); - - /* - * The following automatically sets state to PSTAT_READY - * and returns in the main loop at setjmp(). - */ - child_close(TRUE); - /* NOTREACHED */ - -err: - /* - * An error occurred, let another a chance to obtain the - * accept(2) lock and continue looping. - */ - if (global.config.serialization_lock) - UNLOCK(local.locks[SHLOCK_ACCEPT]); - } - /* NOTREACHED */ -} - -/* - * Register or update limits for the specified client address into the current - * socket's address cache, while also resolving the hostname, using the global - * hostname cache as necessary. address_close() must be called for this - * address in order to balance this effect. This also will automatically - * resolve an address to a hostname if necessary (using the hostname cache for - * speed). - * If there is an address cache size issue and that a new address cannot be - * added into it, NULL is returned. We always return the address_node pointer - * otherwise. If returned reason is not REASON_OK, an error occured and the - * rejection handler should be called instead of the request handler. - */ -static struct address_node * -address_open(struct socket_node *snode, struct server_sockaddr *addr, - int *reason) -{ - struct address_node *anode = NULL; - struct hostname_node *hnode = NULL; - bool created = FALSE; - time_t now = time(NULL); - - *reason = REASON_OK; - LOCK(*snode->lock); - /* Does entry already exist? */ - if ((anode = (struct address_node *)hashtable_lookup( - &snode->addresses_table, addr, sizeof(struct server_sockaddr))) - == NULL) { - /* Create new entry */ - if ((anode = (struct address_node *)pool_alloc( - &snode->addresses_pool, FALSE)) != NULL) { - /* - * Rate limiting and connections concurrency - * initialization - */ - LR_INIT(&anode->lr, - snode->config.address_cache_rate_limit, - snode->config.address_cache_rate_period, now); - anode->concurrent = 0; - /* Cache node initialization */ - (void) mm_memcpy(&anode->address, addr, - sizeof(struct server_sockaddr)); - anode->hostname_node = NULL; - /* - * Convert address to text form in anode->addressname - */ - if (inet_ntop(snode->config.family, - SERVER_SOCKADDR_ADDRESS(addr), anode->addressname, - 63) == NULL) - (void) mm_strcpy(anode->addressname, - ""); - (void) hashtable_link(&snode->addresses_table, - (hashnode_t *)anode, &anode->address, - sizeof(struct server_sockaddr), FALSE); - created = TRUE; - } else { - syslog(LOG_NOTICE, - "Maximum number of addresses reached for socket " - "cache on port %d (%s, %s) - (%u)", - snode->config.port, - (snode->config.family == AF_INET ? - "AF_INET" : "AF_INET6"), - (snode->config.type == SOCK_STREAM ? - "SOCK_STREAM" : "SOCK_DGRAM"), - snode->config.address_cache_size); - *reason = REASON_CACHE_SIZE; - } - } - if (anode != NULL) { - /* Node was found or successfully created, now check limits. */ - if (!snode->config.address_concurrency_limits || - anode->concurrent < - snode->config.address_cache_concurrency_limit) { - if (snode->config.address_rate_limits && - !lr_allow(&anode->lr, 1, now, FALSE)) { - syslog(LOG_NOTICE, - "Refusing %s for exceeding request rate" - "(%ld requests in %u seconds, " - "%ld seconds left to clear)", - anode->addressname, LR_POSTS(&anode->lr), - snode->config.address_cache_rate_period, - LR_REMAINS(&anode->lr, now)); - *reason = REASON_RATE; - } - } else { - syslog(LOG_NOTICE, - "Refusing %s for exceeding number of concurrent " - "requests per address (%u)", - anode->addressname, - snode->config.address_cache_concurrency_limit); - *reason = REASON_CONCURRENCY; - } - /* - * We increase concurrency here, to guarantee that this anode - * cannot be freed by the parent even if we release the lock - * now. We also record the hnode link (if any) before lock - * release. - */ - anode->concurrent++; - hnode = anode->hostname_node; - } - UNLOCK(*snode->lock); - - /* - * If hnode != NULL the hostname was already resolved and linked to - * this anode and we do not need to even perform any cache lookup. - */ - if (anode != NULL && snode->config.address_resolve && hnode == NULL) { - struct hostname_node *hnode2; - - /* - * Hostname resolution needed. We ensure to be able for - * multiple children processes to concurrently perform - * hostname resolution, including possibly on the same - * address since we cannot perform address node granularity - * interprocess locking. To do this we need to use our - * address cache and hostname cache locks wisely. - * We ensure to not hold any locks when calling getnameinfo(3), - * and we need to verify if the node was meanwhile resolved by - * another concurrent process when returning from - * getnameinfo(3), in which case we must free our new hnode - * and associate our anode to the cached one. Otherwise, we - * insert our hnode and link our anode to it. - */ - LOCK(local.locks[SHLOCK_HOSTNAMES]); - /* First attempt to resolve from the cache */ - if ((hnode = (struct hostname_node *)hashtable_lookup( - &shared->hostnames_table, addr, - sizeof(struct server_sockaddr))) == NULL) { - /* - * Can't resolve from cache, so create a new hnode - * and resolve the hostname here. Make sure that no - * locks are held first. - */ - if ((hnode = (struct hostname_node *)pool_alloc( - &shared->hostnames_pool, FALSE)) == NULL) { - syslog(LOG_NOTICE, - "hostname cache too small!"); - goto hcache_err; - } - UNLOCK(local.locks[SHLOCK_HOSTNAMES]); - (void) mm_memcpy(&hnode->address, addr, - sizeof(struct server_sockaddr)); - hnode->refcount = 0; - if ((getnameinfo(SERVER_SOCKADDR(addr), - SERVER_SOCKADDR_SOCKLEN(addr), hnode->hostname, - 255, NULL, 0, 0)) != 0) - (void) mm_strcpy(hnode->hostname, ""); - /* - * Now verify in cache again if our address already - * was resolved by a concurrent process while we were - * waiting for getnameinfo(3). - */ - LOCK(local.locks[SHLOCK_HOSTNAMES]); - if ((hnode2 = (struct hostname_node *)hashtable_lookup( - &shared->hostnames_table, addr, - sizeof(struct server_sockaddr))) == NULL) - /* No, so add our hnode. */ - (void) hashtable_link(&shared->hostnames_table, - (hashnode_t *)hnode, &hnode->address, - sizeof(struct server_sockaddr), FALSE); - else { - /* - * Already resolved, so free our hnode and - * revert to the cached one we found. - */ - (void) pool_free((pnode_t *)hnode); - hnode = hnode2; - } - } - /* Obtain anode lock since we need to test-and-set */ - LOCK(*snode->lock); - /* - * If an hnode wasn't already assigned to our anode, - * link hnode to our anode with the lock held. - */ - if (anode->hostname_node == NULL) { - anode->hostname_node = hnode; - /* - * We only want to register a reference count for a - * new anode, since the hnode would never get freed - * from the cache otherwise (the reference count is - * only decreased by one when freeing an anode entry). - */ - if (created) - hnode->refcount++; - } - UNLOCK(*snode->lock); - -hcache_err: - UNLOCK(local.locks[SHLOCK_HOSTNAMES]); - } - - if (global.config.using_execve) { - /* For parent SIGCHLD handler */ - local.status->anode = anode; - local.status->snode = snode; - } - - return anode; -} - -/* - * Update the address concurency counter so that the parent process may - * garbage collect this address node whenever it expires. We do not care - * about the hostname_node reference counter here and let the parent process - * deal with that. - */ -static void -address_close(struct socket_node *snode, struct address_node *anode) -{ - - if (snode != NULL && anode != NULL) { - LOCK(*snode->lock); - /* - * Leave the parent process garbage collect our node if - * needed. - */ - anode->concurrent--; - UNLOCK(*snode->lock); - } -} - -/* - * This function receives all signals we are interested on in the children - * processes. - */ -static void -child_sighandler(int sig) -{ - - switch (sig) { - case SIGSEGV: - syslog(LOG_NOTICE, "SIGSEGV! Recycling hoping to recover"); - child_exit(PSTAT_RECYCLE); - /* NOTREACHED */ - break; - case SIGTERM: - if (local.status->status == PSTAT_BUSY && - local.socket_node->config.request_interrupt_hook != NULL) - /* Call interrupt handler hook */ - local.socket_node->config.request_interrupt_hook( - &local.request); - child_exit(PSTAT_DEAD); - /* NOTREACHED */ - break; - case SIGPIPE: - /* Connection loss. Interrupt the current connection. */ - child_close(TRUE); - /* NOTREACHED */ - break; - case SIGALRM: - /* Call sigalrm handler hook if any */ - if (global.config.child_sigalrm_hook != NULL) - global.config.child_sigalrm_hook(); - break; - case SIGHUP: - /* Interrupt current connections and recycle */ - if (local.status->status == PSTAT_BUSY && - local.socket_node->config.request_interrupt_hook != NULL) - /* Call interrupt handler hook */ - local.socket_node->config.request_interrupt_hook( - &local.request); - child_exit(PSTAT_RECYCLE); - /* NOTREACHED */ - break; - default: - syslog(LOG_NOTICE, - "child_sighandler() - Unexpected signal %d", sig); - } -} - -/* - * Closes connection with client as necessary. If resume is TRUE, uses a - * non-local jump to resume to accepting other client requests. Otherwise, - * this function returns. - */ -static void -child_close(bool resume) -{ - - /* Disable any pending timer */ - (void) server_alarm(0); - - if (local.socket_node != NULL) { - /* Call user close hook if any */ - if (local.socket_node->config.request_close_hook != NULL) - local.socket_node->config.request_close_hook( - &local.request); - if (local.socket_node->config.type == SOCK_STREAM) { - if (local.stream != NULL) { - (void) fflush(local.stream); - (void) fclose(local.stream); - local.stream = NULL; - } - if (local.socket != -1) { - (void) close(local.socket); - local.socket = -1; - } - } else - local.socket = -1; - if (local.address_node != NULL) { - address_close(local.socket_node, local.address_node); - local.address_node = NULL; - } - local.socket_node = NULL; - } - if (resume) - /* This will automatically set process to PSTAT_READY */ - (void) siglongjmp(local.jmpbuf, 0); -} - -/* - * Child process exit point, Depending on state, this process will be - * restarted immediately by the parent process manager (PSTAT_RECYCLE), - * or will simply exit, leaving the parent to decice when to replace it, - * if needed (PSTAT_DEAD). - */ -static void -child_exit(int state) -{ - - child_close(FALSE); - - /* Call user children exit hook if any */ - if (global.config.child_exit_hook != NULL) - global.config.child_exit_hook(); - - if ((local.status->status = state) == PSTAT_RECYCLE) - /* Cause master loop to immediately reiterate */ - (void) kill(global.parent_pid, SIGUSR1); - - exit(EXIT_SUCCESS); -} diff --git a/mmsoftware/mmlib/mmserver2.h b/mmsoftware/mmlib/mmserver2.h deleted file mode 100644 index 81a4046..0000000 --- a/mmsoftware/mmlib/mmserver2.h +++ /dev/null @@ -1,222 +0,0 @@ -/* $Id: mmserver2.h,v 1.14 2005/12/09 21:08:33 mmondor Exp $ */ - -/* - * Copyright (C) 2004, Matthew Mondor - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Matthew Mondor. - * 4. The name of Matthew Mondor may not be used to endorse or promote - * products derived from this software without specific prior written - * permission. - * 5. Redistribution of source code may not be released under the terms of - * any GNU Public License derivate. - * - * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -/* - * Public API provided to applications - */ - - - -#ifndef MMLIB_MMSERVER2_H -#define MMLIB_MMSERVER2_H - - - -/* HEADERFILES */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - - - -/* DEFINITIONS */ - -/* Used to map REASON_* to strings */ -extern const char *const server_reasons_strings[]; -#define SERVER_REASON_STRING(n) (server_reasons_strings[(n)]) - -/* Used for server_rwlock_ctl() */ -#define SHL_READ (1 << 0) -#define SHL_WRITE (1 << 1) -#define SHL_FREE (1 << 2) -#define SHL_TRY (1 << 3) - -/* Reason passed to socket_node->reject_handler() */ -enum server_reject_reasons { - REASON_OK = 0, - REASON_CACHE_SIZE, - REASON_CONCURRENCY, - REASON_RATE, - REASON_MAX -}; - - - -/* STRUCTURES */ - -/* Configuration structure for server_start() */ -struct server_config { - /* - * Using arrays here instead of string pointers makes sure that the - * data remains valid, that it is not part of the stack of the caller - * function. - */ - char proc_title[32]; - char locks_path[256], locks_user[32], locks_group[32]; - mode_t locks_mode; - char null_path[256]; - char pid_path[256]; - u_int32_t children_initial, children_minspare, children_maxspare, - children_maximum, children_average_seconds, - children_maxrequests; - bool serialization_lock, exit_interrupt_requests, - children_setsid, using_execve; - int (*parent_init_hook)(void); - void (*parent_exit_hook)(void); - void (*parent_sighup_hook)(void); - void (*parent_timer_hook)(void); - u_int32_t parent_timer_seconds; - int (*child_init_hook)(void); - void (*child_exit_hook)(void); - void (*child_sigalrm_hook)(void); -}; - -/* So we can store a universal sockaddr variant to support multiple families */ -struct server_sockaddr { - union { - struct sockaddr sockaddr; - struct sockaddr_in sockaddr_in; - struct sockaddr_in6 sockaddr_in6; - struct sockaddr_un sockaddr_un; - } u; -}; -#define ss_family u.sockaddr.sa_family - -struct server_af_info { - size_t sock_length; - size_t port_offset; - size_t addr_offset; -}; - -extern struct server_af_info *server_afi[1]; - -/* - * Note that the following macros should only be used after calling - * server_init(), since they require the server_afi array to previously have - * been initialized. - */ - -/* Returns pointer to family of address */ -#define SERVER_SOCKADDR_FAMILY(s) \ - ((sa_family_t *)(&(s)->ss_family)) -/* Returns length of address */ -#define SERVER_SOCKADDR_SOCKLEN(s) \ - (server_afi[(int)(s)->ss_family]->sock_length) -/* Returns pointer to port of address */ -#define SERVER_SOCKADDR_PORT(s) \ - ((in_port_t *)(((char *)s) + \ - server_afi[(int)(s)->ss_family]->port_offset)) -/* Returns pointer to family-specific address */ -#define SERVER_SOCKADDR_ADDRESS(s) \ - ((void *)(((char *)s) + \ - server_afi[(int)(s)->ss_family]->addr_offset)) -/* Returns struct sockaddr family-independent pointer */ -#define SERVER_SOCKADDR(s) \ - (&((s)->u.sockaddr)) - -/* - * Passed to the application-specific connection/rejection/interrupt handlers. - */ -struct server_request { - int client_socket, client_port, - server_socket_type, - server_socket_family, - server_socket, - server_socket_port; - FILE *client_stream; - const struct server_sockaddr *server_socket_address, - *client_address; - const char *server_socket_address_name, - *client_address_name, - *client_address_hostname; - void *socket_user_data; - u_int32_t client_address_concurrency; - const void *packet_data; - size_t packet_size; -}; - -/* Used to pass parameters to server_socket_bind() */ -struct server_socket_config { - int family, type, port, backlog; - bool create_stream, address_resolve, address_rate_limits, - address_concurrency_limits; - u_int32_t address_cache_size, address_cache_rate_limit, - address_cache_rate_period, - address_cache_concurrency_limit; - size_t packet_size; - char bind_address[100]; - char socket_user[32], socket_group[32]; - mode_t socket_mode; - int (*setsockopts)(int); - void (*request_handler)(struct server_request *); - void (*reject_handler)(struct server_request *, int); - void (*request_interrupt_hook)(struct server_request *); - void (*request_close_hook)(struct server_request *); - void *user_data; -}; - - - -/* PROTOTYPES */ - -extern void server_init(void); -extern void * server_shmem_malloc(size_t); -extern int server_rwlocks_init(const char *, int, const char *, - const char *, mode_t); -extern int server_rwlock_ctl(int, int); -extern int server_socket_bind(struct server_socket_config *); -extern int server_start(struct server_config *); -extern int server_exit(void); -extern int server_recycle(bool); -extern void server_close(void); -extern unsigned int server_alarm(unsigned int); -extern int server_execve(const char *, char * const *, - char * const *); - - - -#endif diff --git a/mmsoftware/mmlib/mmsql.c b/mmsoftware/mmlib/mmsql.c deleted file mode 100644 index f702e72..0000000 --- a/mmsoftware/mmlib/mmsql.c +++ /dev/null @@ -1,336 +0,0 @@ -/* $Id: mmsql.c,v 1.13 2004/12/03 17:29:50 mmondor Exp $ */ - -/* - * Copyright (C) 2000-2004, Matthew Mondor - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Matthew Mondor. - * 4. The name of Matthew Mondor may not be used to endorse or promote - * products derived from this software without specific prior written - * permission. - * 5. Redistribution of source code may not be released under the terms of - * any GNU Public License derivate. - * - * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -/* - * This requires linking with libmysqlclient. We lock a mutex before - * performing an operation to ensure that if multiple concurrent - * threads were ever calling an operation at the same time the lock would - * at least be thread-safe, they would wait their turn safely. To be - * able to work with multiple thread libraries, we use supplied thread - * functions. - */ - - - - -/* HEADERS */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include -#include -#include -#include - - - - -MMCOPYRIGHT("@(#) Copyright (c) 2000-2004\n\ -\tMatthew Mondor. All rights reserved.\n"); -MMRCSID("$Id: mmsql.c,v 1.13 2004/12/03 17:29:50 mmondor Exp $"); - - - - -/* GLOBAL VARIABLES */ - -static MYSQL *mysql = NULL; -/* Locking between threads */ -static struct mmsql_threadsupport thrfuncs; -static void *mmsql_lock; - - - - -/* FUNCTIONS */ - -bool -mmsql_init(struct mmsql_threadsupport *funcs) -{ - thrfuncs.mutex_init = funcs->mutex_init; - thrfuncs.mutex_destroy = funcs->mutex_destroy; - thrfuncs.mutex_lock = funcs->mutex_lock; - thrfuncs.mutex_unlock = funcs->mutex_unlock; - thrfuncs.thread_yield = funcs->thread_yield; - thrfuncs.thread_sleep = funcs->thread_sleep; - - if ((mmsql_lock = thrfuncs.mutex_init()) != NULL) - return (TRUE); - - return (FALSE); -} - - -void -mmsql_exit(void) -{ - if (mmsql_lock != NULL) - thrfuncs.mutex_destroy(mmsql_lock); -} - - -bool -mmsql_open(const char *host, const char *user, const char *pw, const char *db) -{ - if (DEBUG_FALSE(mysql != NULL)) { - mmsql_close(); - DEBUG_PRINTF("mmsql_open", "Called again before mmsql_close()"); - } - - if ((mysql = mysql_init(NULL)) != NULL) { - if ((mysql_real_connect(mysql, host, user, pw, db, 0, NULL, 0)) - != NULL) - return (TRUE); - else - syslog(LOG_NOTICE, "mmsql_open() - mysql_real_connect()"); - mysql_close(mysql); - } else - syslog(LOG_NOTICE, "mmsql_open() - mysql_init()"); - - return (FALSE); -} - - -void -mmsql_close(void) -{ - if (DEBUG_TRUE(mysql != NULL)) { - mysql_close(mysql); - mysql = NULL; - } else - DEBUG_PRINTF("mmsql_close", "Called before mmsql_open()"); -} - - -/* This permits threads and processes to have exclusive access to the mmsql - * functions until it calls mmsql_unlock() - */ -void -mmsql_glock(const char *name) -{ - MYSQL_RES *mysqlres; - MYSQL_ROW *row; - unsigned long *lengths; - unsigned char line[16]; - char query[1024]; - int res = 0, count = 0; - - while (count < 10) { - snprintf(query, 1023, "SELECT GET_LOCK('%s',60)", name); - if ((mysqlres = mmsql_query(query, mm_strlen(query))) != NULL) { - - if ((mysql_num_rows(mysqlres)) == 1 && - (row = (MYSQL_ROW *)mysql_fetch_row(mysqlres)) != NULL) { - if ((mysql_num_fields(mysqlres)) == 1) { - lengths = mysql_fetch_lengths(mysqlres); - if (row[0]) { - mm_memcpy(line, row[0], lengths[0]); - line[lengths[0]] = 0; - res = atoi(line); - } - } else - syslog(LOG_NOTICE, "mmsql_glock() - mysql_num_fields()"); - } else - syslog(LOG_NOTICE, - "mmsql_glock() - mysql_num_rows()/mysql_fetch_rows()"); - - mysqlres = mmsql_free_result(mysqlres); - } else - syslog(LOG_NOTICE, "mmsql_glock() - mmsql_query(%s)", query); - - if (res == 1) break; - DEBUG_PRINTF("mmsql_glock", "GET_LOCK(%s)", name); - - /* Make sure to not loop taking 100% CPU time, and to - * eventually exit loop, the SQL server seens down so - * other operations should fail safely. - */ - thrfuncs.thread_sleep(1); - count++; - } -} - - -/* After a thread or process called mmsql_glock(), it may permit others to use - * the mmsql functions again, using this function. - */ -void -mmsql_gunlock(const char *name) -{ - MYSQL_RES *mysqlres; - MYSQL_ROW *row; - unsigned long *lengths; - unsigned char line[16]; - char query[1024]; - - snprintf(query, 1023, "SELECT RELEASE_LOCK('%s')", name); - if ((mysqlres = mmsql_query(query, mm_strlen(query))) != NULL) { - - if ((mysql_num_rows(mysqlres)) == 1 && - (row = (MYSQL_ROW *)mysql_fetch_row(mysqlres)) != NULL) { - if ((mysql_num_fields(mysqlres)) == 1) { - lengths = mysql_fetch_lengths(mysqlres); - if (row[0]) { - mm_memcpy(line, row[0], lengths[0]); - line[lengths[0]] = 0; - if ((atoi(line)) != 1) - DEBUG_PRINTF("mmsql_gunlock", - "RELEASE_LOCK(%s)", name); - } - } else - syslog(LOG_NOTICE, "mmsql_gunlock() - mysql_num_fields()"); - } else - syslog(LOG_NOTICE, - "mmsql_gunlock() - mysql_num_row()/mysql_fetch_rows()"); - - mysqlres = mmsql_free_result(mysqlres); - } else - syslog(LOG_NOTICE, "mmsql_gunlock() - mmsql_query(%s)", query); -} - - -/* This function reconnects to the mysql server if connection was lost */ -void -mmsql_ping(void) -{ - int res; - - if ((res = mysql_ping(mysql)) != 0) - DEBUG_PRINTF("mmsql_ping", "mysql_ping() - (%s)", mysql_error(mysql)); -} - - -/* This function is used to perform queries to which results are expected, - * mysql_free_results() should be called on the pointer it returns. - */ -MYSQL_RES * -mmsql_query(const char *query, long len) -{ - MYSQL_RES *mysqlres = NULL; - int res; - - if (len == -1) - len = mm_strlen(query); - - /* We lock the mutex of our shared mysql handle, perform the query - * and return the results which the caller might use for as long as - * required. - */ - if (mmsql_lock != NULL) - thrfuncs.mutex_lock(mmsql_lock); - - /* Ensure that connection is still active to the server */ - mmsql_ping(); - - if ((res = mysql_real_query(mysql, query, len)) == 0) { - if ((mysqlres = mysql_store_result(mysql)) == NULL) - syslog(LOG_NOTICE, "mmsql_query() - mysql_store_result()"); - } else - syslog(LOG_NOTICE, "mmsql_query() - mysql_real_query(%s) - (%s)", - query, mysql_error(mysql)); - - if (mmsql_lock != NULL) - thrfuncs.mutex_unlock(mmsql_lock); - - return (mysqlres); -} - - -MYSQL_RES * -mmsql_free_result(MYSQL_RES *mysqlres) -{ - if (mysqlres != NULL) - mysql_free_result(mysqlres); - - return (NULL); -} - - -/* This function also performs an sql query, but one for which no results - * are expected except error condition. - */ -bool -mmsql_command(const char *query, long len) -{ - bool res = TRUE; - int res2; - - if (len == -1) - len = mm_strlen(query); - - if (mmsql_lock != NULL) - thrfuncs.mutex_lock(mmsql_lock); - - /* Ensure that connection to the server is still active */ - mmsql_ping(); - - if ((res2 = mysql_real_query(mysql, query, len)) != 0) { - syslog(LOG_NOTICE, "mmsql_command() - mysql_real_query() - (%s)", - mysql_error(mysql)); - res = FALSE; - } - - if (mmsql_lock != NULL) - thrfuncs.mutex_unlock(mmsql_lock); - - return (res); -} - - -u_int64_t -mmsql_last_auto_id(void) -{ - u_int64_t id; - - if (mmsql_lock != NULL) - thrfuncs.mutex_lock(mmsql_lock); - - id = mysql_insert_id(mysql); - - if (mmsql_lock != NULL) - thrfuncs.mutex_unlock(mmsql_lock); - - return id; -} diff --git a/mmsoftware/mmlib/mmsql.h b/mmsoftware/mmlib/mmsql.h deleted file mode 100644 index 0202e94..0000000 --- a/mmsoftware/mmlib/mmsql.h +++ /dev/null @@ -1,87 +0,0 @@ -/* $Id: mmsql.h,v 1.8 2004/08/21 10:00:12 mmondor Exp $ */ - -/* - * Copyright (C) 2000-2004, Matthew Mondor - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Matthew Mondor. - * 4. The name of Matthew Mondor may not be used to endorse or promote - * products derived from this software without specific prior written - * permission. - * 5. Redistribution of source code may not be released under the terms of - * any GNU Public License derivate. - * - * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - - - - -#ifndef MMSQL_H -#define MMSQL_H - - - - -/* HEADERS */ - -#include - -#include - -#include - - - - -/* STRUCTURES */ - -struct mmsql_threadsupport { - void * (*mutex_init)(void); - void * (*mutex_destroy)(void *); - void (*mutex_lock)(void *); - void (*mutex_unlock)(void *); - void (*thread_yield)(void); - void (*thread_sleep)(int); -}; - - - - -/* PROTOTYPES */ - -extern bool mmsql_init(struct mmsql_threadsupport *); -extern void mmsql_exit(void); -extern bool mmsql_open(const char *, const char *, const char *, - const char *); -extern void mmsql_close(void); -extern void mmsql_glock(const char *); -extern void mmsql_gunlock(const char *); -extern void mmsql_ping(void); -extern MYSQL_RES * mmsql_query(const char *, long); -extern MYSQL_RES * mmsql_free_result(MYSQL_RES *); -extern bool mmsql_command(const char *, long); -extern u_int64_t mmsql_last_auto_id(void); - - - -#endif diff --git a/mmsoftware/mmlib/mmstat.3 b/mmsoftware/mmlib/mmstat.3 deleted file mode 100644 index 0f6c11d..0000000 --- a/mmsoftware/mmlib/mmstat.3 +++ /dev/null @@ -1,242 +0,0 @@ -.\" $Id: mmstat.3,v 1.9 2004/05/05 23:59:57 mmondor Exp $ -.\" -.\" Copyright (C) 2002-2004, Matthew Mondor -.\" All rights reserved. -.\" -.\" Redistribution and use in source and binary forms, with or without -.\" modification, are permitted provided that the following conditions -.\" are met: -.\" 1. Redistributions of source code must retain the above copyright -.\" notice, this list of conditions and the following disclaimer. -.\" 2. Redistributions in binary form must reproduce the above copyright -.\" notice, this list of conditions and the following disclaimer in the -.\" documentation and/or other materials provided with the distribution. -.\" 3. All advertising materials mentioning features or use of this software -.\" must display the following acknowledgement: -.\" This product includes software developed by Matthew Mondor. -.\" 4. The name of Matthew Mondor may not be used to endorse or promote -.\" products derived from this software without specific prior written -.\" permission. -.\" 5. Redistribution of source code may not be released under the terms of -.\" any GNU Public License derivate. -.\" -.\" THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR -.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES -.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. -.\" IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, -.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -.\" BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF -.\" USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF -.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -.\" -.Dd January 1, 2003 -.Os mmsoftware -.Dt mmstat_t 3 -.Sh NAME -.Nm mmstat -.Nd Statistical book-keeper with transaction isolation and recovery support -.Sh SYNOPSIS -.Fd #include -.Fd #include -.Fd #include -.Fd #include -.Ft bool -.Fn mmstat_initialize "void" -.Ft bool -.Fn mmstat_init "mmstat_t *mms" "bool persistent" "bool autoflush" -.Ft bool -.Fn mmstat "mmstat_t *mms" "enum stat_type type" "int64_t value" \ -"const char *fmt" "..." -.Ft bool -.Fn mmstat_transact "mmstat_t *mms" "bool lock" -.Ft mmstatres_t * -.Fn mmstat_report "const char *fmt" "..." -.Ft mmstatres_t * -.Fn mmstat_freeres "mmstatres_t *res" -.Ft mmstatent_t * -.Fn mmstat_nextres "mmstatres_t *res" -.Ft bool -.Fn mmstat_rotate "const char *pattern" "const char *prefix" -.Sh DESCRIPTION -mmstat consists of a daemon (mmstatd) and library API (mmstat). mmstatd -handles a statistical database on a key/value pair basis and stores for -each the creation and modification timestamps as well. These entries may -be volatile or persistent. Through the mmstat library applications may -create, update, and delete statistical keys. The mmstatd design was made -around two asynchroneous processes, the logger and the librarian. The logger -dispatches update requests writing recovery logs and syncing to disk, while -the librarian processes those logs, affecting the memory database, and syncs -that database to disk at fixed intervals. -.Ss FUNCTIONS -.Bl -tag -width indent -offset indent -.It bool Fn mmstat_initialize "void" -is optional and can be called to initialize the library. Other library -functions will automatically do this if need be; It however may be necessary -to force this initialization to occur using this function before using -.Xr chroot 2 -since the socket and configuration files may not longer be available from -the new root jail. Note that -.Fn mmstat_report -and -.Fn mmstat_rotate -cannot be pre-initialized and that those calls will fail if the socket file -is not available. Note that internally the -.Nm MMSTATCONF -environment variable will be checked to determine an alternative configuration -file to read. Otherwise the default consists of -.Nm /usr/local/etc/mmstatd.conf . -.It bool Fn mmstat_init "mmstat_t *mms" "bool persistent" "bool autoflush" -initializes supplied mmstat_t -.Fa mms -object. This is required before calling other mmstat functions using this -handle. It in fact transforms the object into a handle suitable for other -calls. There is no need to perform any special cleanup on the handle once -done with it. It can be reused multiple times, arbritrarily, since it first -was initialized. The handle is initialized for persistent storage if -.Fa persistent -is TRUE, or for volatile storage if FALSE. Volatile keys are not stored -into the disk database, they also will not be recovered in case of a crash. -If -.Ar autoflush -is TRUE, updated or reset entries will automatically be deleted when they -reach zero, atomically. This can be useful with volatile keys for a -"who" service for instance. -Multiple handles can be obtained by the same process. -.It bool Fn mmstat "mmstat_t *mms" "enum stat_type type" "int64_t value" \ -"const char *fmt" "..." -permits to create, modify or delete a statistical key-based entry. -.Fa mms -consists of the handle to perform operation through, -.Fa type -should be STAT_UPDATE, STAT_RESET or STAT_DELETE. -.Fa value -consists of the value to add to the specified key, or value to reset it -to when STAT_RESET is used. -.Fa fmt -specifies the key to perform operation on. When a key does not exist, it is -automatically created on the fly, unless a '*' and '?' wildcard matching -pattern is provided to only operate on existing matching keys. Only -applications which belong to the LOG_GROUP can affect statistic changes. See -.Xr mmstatd.conf 5 -for more information. -.It bool Fn mmstat_transact "mmstat_t *mms" "bool lock" -allows to transaction-protect a set of -.Fn mmstat -function calls. These operations will be isolated from other requests and -are therefore guarenteed to be atomic. -.Fa mms -is the handle to affect, and -.Fa lock -should be TRUE (lock) or FALSE (unlock). -.It mmstatres_t * Fn mmstat_report "const char *fmt" "..." -permits to retreive statistics from the database. -.Fa fmt -should be NULL if a full report is wanted, a pattern with * and ? wildcard -matching, or an absolute key name. It should be noted that only applications -which belong to the STAT_GROUP are allowed to obtain statistic reports. See -.Xr mmstatd.conf 5 -for details. Some care should be taken when requesting reports, since load -on the librarian daemon is much higher when processing reports than updates. -It is important that the applications process the report as fast as possible -with -.Fn mmstat_nextres -and then call -.Fn mmstat_freeres -to allow the librarian to perform it's vital tasks. See -.Nm mmstatd/src/mmstat.c -for an example on how to do this. -.It mmstatres_t * Fn mmstat_freeres "mmstatres_t *res" -closes the internal IPC resources established with the librarian process. It -is important to call this function after -.Fn mmstat_report -is called, and as soon as possible to free the librarian. -.Fa res -consists of the report results that were obtained from it. -.It mmstatent_t * Fn mmstat_nextres "mmstatres_t *res" -allows to obtain results one by one from the -.Fa res -handle obtained from -.Fn mmstat_report -sequencially. The results are obtained into an mmstatent_t structure, as defined -in -.Aq Pa mmstat.h -(described below). -.Bd -literal -offset indent -typedef mmstat_entry { - int64_t value; - time_t created, modified; - uid_t uid; - char key[KEY_SIZE]; - bool persistent; -} mmstatent_t; -.Ed -.It bool Fn mmstat_rotate "const char *pattern" "const char *prefix" -permits to atomically rename all persistent keys matching -.Fa pattern -(with optional * and ? wildcard) by inserting -.Fa prefix -string before each. This is very useful for weekly/monthly/yearly rotation -of statistics by cron events. This function also forces an immediate db sync -to disk. -.El -.Sh RETURN VALUES -.Bl -tag -width indent -offset indent -.It Fn mmstat_initialize -returns TRUE on success, or FALSE if the configuration file or logging socket -could not be accessed. -.It Fn mmstat_init -returns TRUE on success, or FALSE on error (socket could not be created). -.It Fn mmstat -returns TRUE on success, or FALSE on error. It should be noted that in the case -of a transaction protected operation, the results of the closing -.It Fn mmstat_transact -should be evaluated instead. -.It Fn mmstat_transact -returns TRUE when a lock is acquired, but FALSE if a lock is already active -on the specified handle. When a lock is released, -.Fn mmstat_transact -returns TRUE if the set of operations could be processed, or FALSE otherwise. -.It Fn mmstat_report -returns an -.Fa mmstatres_t -results handle pointer on success, or NULL on error. -.It Fn mmstat_freeres -always returns NULL. -.It Fn mmstat_nextres -returns the next entry of results as an -.Fa mmstatent_t -pointer in the specified -.Fa mmstatres_t -set, but returns NULL when reaching the end of results. -.It Fn mmstat_rotate -returns FALSE if it could not establish connection with the mmstat daemon, -or TRUE otherwise. -.El -.Sh ENVIRONMENT -.Bl -tag -width XXXXXXXXXX -.It Pa Nm MMSTATCONF -If set, specifies the absolute location of the configuration file to load -instead of the default -.Nm /usr/local/etc/mmstatd.conf . -.El -.Sh SEE ALSO -.Xr mmstatd.conf 5 , -.Xr mmstatd 8 , -.Xr mmstat 8 , -.Xr syslog 3 , -.Xr chroot 2 . -.Sh STANDARDS -None whatsoever, it was custom-designed from scratch. It however was written -to comply with most ANSI-C programming norms, and is expected to run on a -system conforming to IEEE Std 1003.1-1990 (``POSIX''). -.Sh HISTORY -mmstatd and mmstat library were primarily written on NetBSD 1.5.3 for the -mmftpd and mmmail suite of daemons. -.Sh AUTHOR -The mmstat protocol is Copyright (c) 2002-2004, Matthew Mondor, All Rights -Reserved. -The mmstat library and the mmstatd daemon were written by Matthew Mondor. -.Sh BUGS -Please report any bug to mmondor@accela.net diff --git a/mmsoftware/mmlib/mmstat.c b/mmsoftware/mmlib/mmstat.c deleted file mode 100644 index af0e0a4..0000000 --- a/mmsoftware/mmlib/mmstat.c +++ /dev/null @@ -1,430 +0,0 @@ -/* $Id: mmstat.c,v 1.22 2005/01/31 15:58:10 mmondor Exp $ */ - -/* - * Copyright (C) 2002-2004, Matthew Mondor - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Matthew Mondor. - * 4. The name of Matthew Mondor may not be used to endorse or promote - * products derived from this software without specific prior written - * permission. - * 5. Redistribution of source code may not be released under the terms of - * any GNU Public License derivate. - * - * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - - - - -/* HEADERS */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - - - - -MMCOPYRIGHT("@(#) Copyright (c) 2002-2004\n\ -\tMatthew Mondor. All rights reserved.\n"); -MMRCSID("$Id: mmstat.c,v 1.22 2005/01/31 15:58:10 mmondor Exp $"); - - - - -/* PRIVATE PROTOTYPES */ - -static bool i_mmstat_send(struct log_entry *, int); - - - - -/* GLOBALS */ - -static struct mmstat_config CONF; -static bool mmstat_initialized = FALSE, connect_log = TRUE; -static int lsock = -1; -static struct sockaddr_un laddr; - - - -/* FUNCTIONS */ - -/* Public API mmstat functions */ - -bool -mmstat_init(mmstat_t *mms, bool persistent, bool autoflush) -{ - bool ret = FALSE; - - if (mmstat_initialized || mmstat_initialize()) { - mms->transaction = FALSE; - mms->persistent = persistent; - mms->autoflush = autoflush; - mms->transact_pos = 0; - ret = TRUE; - } - - return (ret); -} - - -bool -mmstat(mmstat_t *mms, enum stat_type type, int64_t value, const char *fmt, ...) -{ - bool ok = FALSE; - struct log_entry *entry; - va_list arg_ptr; - - if (DEBUG_TRUE(type == STAT_RESET || type == STAT_UPDATE || - type == STAT_DELETE)) { - if (DEBUG_TRUE(mms->transact_pos < MAX_TRANSACT)) { - /* In case transaction mode is active, make sure to not overflow - * buffer, and that we write next entry at the right offset. - * The mmstatd logger process only distinguishes transaction - * protected requests in that they are all sent in a single packet. - */ - entry = &(mms->entries[mms->transact_pos]); - mms->transact_pos++; - /* Now simply fill new log entry */ - mm_memclr(entry, sizeof(struct log_entry)); - entry->type = (int32_t)type; - entry->persistent = (u_int16_t)mms->persistent; - entry->autoflush = (u_int16_t)mms->autoflush; - if (fmt != NULL) { - va_start(arg_ptr, fmt); - vsnprintf(entry->key, KEY_SIZE - 1, fmt, arg_ptr); - va_end(arg_ptr); - } - if (type == STAT_UPDATE) - entry->un.update.modifier = value; - else if (type == STAT_RESET) - entry->un.reset.value = value; - ok = TRUE; - } else - DEBUG_PRINTF("mmstat", - "Exceeded number of entries in transaction"); - } else - DEBUG_PRINTF("mmstat", - "Type not one of STAT_RESET | STAT_UPDATE | STAT_DELETE"); - if (ok && !mms->transaction) { - /* Not in transaction mode, flush packet to mmstatd immediately */ - ok = i_mmstat_send(mms->entries, 1); - mms->transact_pos = 0; - } - - return (ok); -} - - -bool -mmstat_transact(mmstat_t *mms, bool lock) -{ - bool ok = FALSE; - - if (lock) { - if (DEBUG_TRUE(!mms->transaction)) { - /* Start transaction mode */ - mms->transaction = TRUE; - mms->transact_pos = 0; - ok = TRUE; - } else - DEBUG_PRINTF("mmstat_transact", "Transaction already started"); - } else { - if (DEBUG_TRUE(mms->transaction)) { - /* End transaction mode, flush transaction queue to mmstatd */ - if (i_mmstat_send(mms->entries, mms->transact_pos)) - ok = TRUE; - mms->transaction = FALSE; - mms->transact_pos = 0; - } else - DEBUG_PRINTF("mmstat_transact", "Transaction already ended"); - } - - return (ok); -} - - -mmstatres_t * -mmstat_report(const char *fmt, ...) -{ - mmstatres_t *res = NULL; - struct sockaddr_un sun; - va_list arg_ptr; - char key[KEY_SIZE], c; - - if (fmt != NULL) { - mm_memclr(key, KEY_SIZE); - va_start(arg_ptr, fmt); - vsnprintf(key, KEY_SIZE - 1, fmt, arg_ptr); - va_end(arg_ptr); - connect_log = FALSE; - if (mmstat_initialized || mmstat_initialize()) { - if ((res = malloc(sizeof(mmstatres_t)))) { - if ((res->fd = socket(AF_LOCAL, SOCK_STREAM, 0)) != -1) { - mm_memclr(&sun, sizeof(struct sockaddr_un)); - sun.sun_family = AF_UNIX; - mm_strcpy(sun.sun_path, CONF.STAT_SOCKET); - if (connect(res->fd, (struct sockaddr *)&sun, - sizeof(struct sockaddr_un)) != -1) { - if ((read(res->fd, &c, 1) == 1) && (c == '+')) { - if (write(res->fd, "s", 1) == 1 && - write(res->fd, key, KEY_SIZE) == KEY_SIZE) - return (res); - } - } - close(res->fd); - } - free(res); - } else - syslog(LOG_NOTICE, "mmstat_report() - Out of memory"); - } - } - - return (NULL); -} - - -mmstatent_t * -mmstat_nextres(mmstatres_t *res) -{ - struct pollfd fds[] = { - {res->fd, POLLIN, 0} - }; - - for (;;) { - if (poll(fds, 1, -1) == 1) { - if (fds[0].revents & POLLIN) { - if (read(res->fd, &res->entry, sizeof(mmstatent_t)) == - sizeof(mmstatent_t)) - return (&res->entry); - else break; - } - } - } - - return (NULL); -} - - -mmstatres_t * -mmstat_freeres(mmstatres_t *res) -{ - if (DEBUG_FALSE(res == NULL || res->fd == -1)) { - DEBUG_PRINTF("mmstat_freeres", "NULL prameter or fd already closed!"); - return NULL; - } - - close(res->fd); - free(res); - - return (NULL); -} - - -bool -mmstat_rotate(const char *pattern, const char *prefix) -{ - struct sockaddr_un sun; - int fd; - char pat[KEY_SIZE], pre[KEY_SIZE], c; - bool ok = FALSE; - - if (pattern != NULL && prefix != NULL) { - mm_memclr(pat, KEY_SIZE); - mm_strncpy(pat, pattern, KEY_SIZE - 1); - mm_memclr(pre, KEY_SIZE); - mm_strncpy(pre, prefix, KEY_SIZE - 1); - connect_log = FALSE; - if (mmstat_initialized || mmstat_initialize()) { - if ((fd = socket(AF_LOCAL, SOCK_STREAM, 0)) != -1) { - mm_memclr(&sun, sizeof(struct sockaddr_un)); - sun.sun_family = AF_UNIX; - mm_strcpy(sun.sun_path, CONF.STAT_SOCKET); - if (connect(fd, (struct sockaddr *)&sun, - sizeof(struct sockaddr_un)) != -1) { - if ((read(fd, &c, 1) == 1) && (c == '+')) { - if (write(fd, "r", 1) == 1 && - write(fd, pat, KEY_SIZE) == KEY_SIZE && - write(fd, pre, KEY_SIZE) == KEY_SIZE) - ok = TRUE; - } - } - close(fd); - } - } - } - - return (ok); -} - - -/* Internal functions not available to the public mmstat API */ - -static bool -i_mmstat_send(struct log_entry *entries, int len) -{ - int l; - bool ok = FALSE; - - l = sizeof(struct log_entry) * len; - while (!ok) { - if (lsock != -1) { - if ((send(lsock, entries, l, 0)) == l) - ok = TRUE; - else { - if (DEBUG_FALSE(errno == EMSGSIZE)) - DEBUG_PRINTF("i_mmstat_send", - "Your kernel has a broken setsockopt(2)"); - close(lsock); - lsock = -1; - } - } - if (!ok) { - /* Attempt to re-establish connection to mmstatd(8), but stop - * trying if it fails. Stats update will be lost! - */ - DEBUG_PRINTF("i_mmstat_send", - "Re-establishing lost connection to mmstatd(8)"); - if (!mmstat_initialize()) - break; - } - } - - return (ok); -} - - -/* - * XXX Note that to be thread-safe, the system should normally be initialized - * by the main process before threads are launched. This is because no mutex - * is used to lock on the filedescriptor, and it would probably be possible - * for several sockets to concurrently be opened at the same time in case of - * mmstatd(8) shutdown (threads invoking mmstat() will then require to - * reconnect, and this function will be invoked. This probably means that I - * should provide some optional thread support for this locking to - * mmstat_initialize. In fact, connect(), open(), close() should also be - * provided by the caller so that thread-safe functions could be supplied, - * like is done for mmfd and mmsqld. - */ - /* XXX If I allocate a mutex, it's not really ideal, since I only need - * one in the whole process! I rather need something like thread_once or - * such. If I don't allocate the mutex though, I have to use pthread API - * rather than abstract threading API. So I should still allocate it - * probably. I guess that user also has to pass thread_once function... - * along with some abstract boolean var to use internally for this. - */ - -bool -mmstat_initialize(void) -{ - char *conf_file = "/usr/local/etc/mmstatd.conf", *tmp; - bool ok = FALSE; - cres_t cres; - carg_t cargs[] = { - {CAT_STR, CAF_NONE, 1, 31, "USER", CONF.USER}, - {CAT_STR, CAF_NONE, 1, 255, "GROUPS", CONF.GROUPS}, - {CAT_STR, CAF_NONE, 1, 31, "LOG_FACILITY", CONF.LOG_FACILITY}, - {CAT_STR, CAF_NONE, 1, 255, "PID_FILE", CONF.PID_FILE}, - {CAT_STR, CAF_NONE, 1, 255, "LOCK_FILE", CONF.LOCK_FILE}, - {CAT_STR, CAF_NONE, 1, 255, "LOG_SOCKET", CONF.LOG_SOCKET}, - {CAT_STR, CAF_NONE, 1, 255, "STAT_SOCKET", CONF.STAT_SOCKET}, - {CAT_STR, CAF_NONE, 1, 127, "ENV_DIR", CONF.ENV_DIR}, - {CAT_STR, CAF_NONE, 1, 31, "LOG_GROUP", CONF.LOG_GROUP}, - {CAT_STR, CAF_NONE, 1, 31, "STAT_GROUP", CONF.STAT_GROUP}, - {CAT_VAL, CAF_NONE, 1, 99999999, "SYNC_INTERVAL", - &CONF.SYNC_INTERVAL}, - {CAT_VAL, CAF_NONE, 0, 99999999, "SYNC_BYTES", &CONF.SYNC_BYTES}, - {CAT_VAL, CAF_NONE, 1, 99999999, "MAX_LOGSIZE", &CONF.MAX_LOGSIZE}, - {CAT_VAL, CAF_NONE, 0, 9999, "STATS_RATE", &CONF.STATS_RATE}, - {CAT_VAL, CAF_NONE, 1, 9999, "STATS_TIME", &CONF.STATS_TIME}, - {CAT_END, CAF_NONE, 0, 0, NULL, NULL} - }; - - /* Set defaults */ - mm_strcpy(CONF.USER, "mmstatd"); - mm_strcpy(CONF.GROUPS, "mmstat staff"); - mm_strcpy(CONF.LOG_FACILITY, "LOG_AUTHPRIV"); - mm_strcpy(CONF.PID_FILE, "/var/mmstatd/mmstatd.pid"); - mm_strcpy(CONF.LOG_SOCKET, "/var/mmstatd/mmstatd_log.sock"); - mm_strcpy(CONF.STAT_SOCKET, "/var/mmstatd/mmstatd_stat.sock"); - mm_strcpy(CONF.ENV_DIR, "/var/mmstatd"); - mm_strcpy(CONF.LOG_GROUP, "mmstat"); - mm_strcpy(CONF.STAT_GROUP, "staff"); - CONF.SYNC_INTERVAL = 1800; - CONF.SYNC_BYTES = 4096; - CONF.MAX_LOGSIZE = 1048576; - CONF.STATS_RATE = 5; - CONF.STATS_TIME = 10; - - if ((tmp = getenv("MMSTATCONF"))) - conf_file = tmp; - if (mmreadcfg(&cres, cargs, conf_file)) { - if (connect_log) { - if ((lsock = socket(AF_LOCAL, SOCK_DGRAM, 0)) != -1) { - int opt; - - /* Set output buffer size */ - opt = (int)BALIGN_CEIL(sizeof(struct log_entry) * MAX_TRANSACT, - 1024); - if (setsockopt(lsock, SOL_SOCKET, SO_SNDBUF, &opt, sizeof(int)) - == -1) - DEBUG_PRINTF("mmstat_initialize", - "setsockopt(SO_SNDBUF)"); - - mm_memclr(&laddr, sizeof(struct sockaddr_un)); - mm_strncpy(laddr.sun_path, CONF.LOG_SOCKET, 100); - laddr.sun_family = AF_UNIX; - if ((connect(lsock, (struct sockaddr *)&laddr, - sizeof(struct sockaddr_un))) == -1) { - close(lsock); - lsock = -1; - syslog(LOG_NOTICE, "mmstat_initialize() - connect(%s)", - CONF.LOG_SOCKET); - } - } else - syslog(LOG_NOTICE, "mmstat_initialize() - socket()"); - if (lsock != -1) - ok = TRUE; - } else - ok = TRUE; - } else - syslog(LOG_NOTICE, "mmstat_initialize() - mmreadcfg(%s)", conf_file); - - mmstat_initialized = ok; - return (ok); -} diff --git a/mmsoftware/mmlib/mmstat.h b/mmsoftware/mmlib/mmstat.h deleted file mode 100644 index f08805b..0000000 --- a/mmsoftware/mmlib/mmstat.h +++ /dev/null @@ -1,148 +0,0 @@ -/* $Id: mmstat.h,v 1.11 2004/06/01 23:52:43 mmondor Exp $ */ - -/* - * Copyright (C) 2002-2004, Matthew Mondor - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Matthew Mondor. - * 4. The name of Matthew Mondor may not be used to endorse or promote - * products derived from this software without specific prior written - * permission. - * 5. Redistribution of source code may not be released under the terms of - * any GNU Public License derivate. - * - * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - - - - -#ifndef MMSTAT_H -#define MMSTAT_H - - - - -#include - -#include -#include -#include -#include - - - - -#define MAX_TRANSACT 32 -#define KEY_SIZE 256 - -typedef struct mmstat_entry mmstatent_t; -typedef struct mmstat_handle mmstat_t; -typedef struct mmstat_report mmstatres_t; - - - - -enum stat_type { - STAT_TRANSACT = 1, - STAT_NEWFILE = 2, - STAT_UPDATE = 3, - STAT_RESET = 4, - STAT_DELETE = 5 -}; - -/* This structure is always in host byte order for now, since remote operation - * is not currently allowed. - */ -struct mmstat_entry { - int64_t value; - time_t created, modified; - uid_t uid; - char key[KEY_SIZE]; - bool persistent; -}; - -/* Note: elements of the following structure have been explicitely set to - * bit-fixed types, so that it can be architecture and platform independent. - * BYTEORDER_*() macros from mmarch(3) are used to achieve this. - */ -struct log_entry { - int32_t type; /* enum stat_type */ - u_int32_t uid; /* uid_t */ - u_int32_t time; /* time_t */ - u_int16_t persistent, autoflush; /* booleans */ - char key[KEY_SIZE]; - union { - struct { - u_int32_t begin; /* boolean */ - } transact; - struct { - u_int32_t lognum; - } newfile; - struct { - int64_t modifier; - } update; - struct { - int64_t value; - } reset; - struct { - } del; - } un; -}; - -struct mmstat_handle { - bool persistent, transaction, autoflush; - int transact_pos; - struct log_entry entries[MAX_TRANSACT]; -}; - -struct mmstat_report { - int fd; - mmstatent_t entry; -}; - -struct mmstat_config { - char USER[32], GROUPS[256], LOG_FACILITY[32], PID_FILE[256], - LOCK_FILE[256], LOG_SOCKET[256], STAT_SOCKET[256], ENV_DIR[128], - LOG_GROUP[32], STAT_GROUP[32]; - long SYNC_INTERVAL, SYNC_BYTES, MAX_LOGSIZE, STATS_RATE, STATS_TIME; - gid_t log_group, stat_group; - off_t max_logsize; -}; - - - - -extern bool mmstat_initialize(void); -extern bool mmstat_init(mmstat_t *, bool, bool); -extern bool mmstat(mmstat_t *, enum stat_type, int64_t, - const char *, ...); -extern bool mmstat_transact(mmstat_t *, bool); -extern mmstatres_t * mmstat_report(const char *, ...); -extern mmstatent_t * mmstat_nextres(mmstatres_t *); -extern mmstatres_t * mmstat_freeres(mmstatres_t *); -extern bool mmstat_rotate(const char *, const char *); - - - - -#endif diff --git a/mmsoftware/mmlib/mmstr.c b/mmsoftware/mmlib/mmstr.c deleted file mode 100644 index e5f916c..0000000 --- a/mmsoftware/mmlib/mmstr.c +++ /dev/null @@ -1,163 +0,0 @@ -/* $Id: mmstr.c,v 1.8 2004/05/22 17:44:00 mmondor Exp $ */ - -/* - * Copyright (C) 2000-2004, Matthew Mondor - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Matthew Mondor. - * 4. The name of Matthew Mondor may not be used to endorse or promote - * products derived from this software without specific prior written - * permission. - * 5. Redistribution of source code may not be released under the terms of - * any GNU Public License derivate. - * - * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - - - - -/* INCLUDES */ - -#include -#include - -#include -#include -#include -#include - - - - -MMCOPYRIGHT("@(#) Copyright (c) 2000-2004\n\ -\tMatthew Mondor. All rights reserved.\n"); -MMRCSID("$Id: mmstr.c,v 1.8 2004/05/22 17:44:00 mmondor Exp $"); - - - - -/* GLOBALS */ - -static pool_t mmstrp; -static pth_mutex_t mmstrp_lock = PTH_MUTEX_INIT; - - - - -/* Has to be called before mmstrdup() and mmstrfree() can be used, mmstrexit() - * should be called to free the resources we allocate. The buffer consists of - * the minimum amount of bytes for pre-buffered 256 bytes strings, and max - * specifies the maximum of buffers that should be dynamically allocated, 0 - * for no limit - */ -bool -mmstrinit(void *(*allocfunc)(size_t), void (*freefunc)(void *), - size_t buffer) -{ - bool ok = FALSE; - - pth_mutex_acquire(&mmstrp_lock, FALSE, NULL); - if (!POOL_VALID(&mmstrp)) - ok = pool_init(&mmstrp, "mmstr_pool", allocfunc, freefunc, NULL, NULL, - sizeof(mmstrnode), buffer / sizeof(mmstrnode), 0, 0); - else - ok = TRUE; - pth_mutex_release(&mmstrp_lock); - - return ok; -} - - -/* Frees all resources that were allocated by mmstrinit() and mmstrdup() */ -void -mmstrexit(void) -{ - pth_mutex_acquire(&mmstrp_lock, FALSE, NULL); - pool_destroy(&mmstrp); - pth_mutex_release(&mmstrp_lock); -} - - -/* This consists of a fast strdup() replacement, but strings must be freed using - * mmstrfree() and strings are fixed 256 bytes in length - */ -char * -mmstrdup(const char *str) -{ - register const char *ptr; - register size_t len; - register mmstrnode *nod; - - /* First evaluate string length, then make sure it's <255. We then - * allocate and copy it, then return the new string pointer. - */ - for (ptr = str; *ptr != '\0'; ptr++) ; - if ((len = (size_t)(ptr - str) + 1) < 256) { - pth_mutex_acquire(&mmstrp_lock, FALSE, NULL); - nod = (mmstrnode *)pool_alloc(&mmstrp, FALSE); - pth_mutex_release(&mmstrp_lock); - if (nod != NULL) { - mm_memcpy(nod->string, str, len); - return nod->string; - } - } - - return NULL; -} - - -/* This function allocates a string of specified bytes size and returns a - * pointer to it, or NULL on error. String will be an empty one. - */ -char * -mmstralloc(size_t size) -{ - register mmstrnode *nod = NULL; - - if (size < 256) { - pth_mutex_acquire(&mmstrp_lock, FALSE, NULL); - nod = (mmstrnode *)pool_alloc(&mmstrp, FALSE); - pth_mutex_release(&mmstrp_lock); - if (nod != NULL) { - *nod->string = '\0'; - return nod->string; - } - } - - return NULL; -} - - -/* Frees a string which must have been allocated using mmstrdup */ -char * -mmstrfree(char *str) -{ - /* Evaluate actual node pointer from supplied string pointer and free it */ - str -= sizeof(pnode_t); - - pth_mutex_acquire(&mmstrp_lock, FALSE, NULL); - pool_free((pnode_t *)str); - pth_mutex_release(&mmstrp_lock); - - return NULL; -} diff --git a/mmsoftware/mmlib/mmstr.h b/mmsoftware/mmlib/mmstr.h deleted file mode 100644 index 69fac14..0000000 --- a/mmsoftware/mmlib/mmstr.h +++ /dev/null @@ -1,76 +0,0 @@ -/* $Id: mmstr.h,v 1.6 2004/06/01 19:11:57 mmondor Exp $ */ - -/* - * Copyright (C) 2000-2004, Matthew Mondor - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Matthew Mondor. - * 4. The name of Matthew Mondor may not be used to endorse or promote - * products derived from this software without specific prior written - * permission. - * 5. Redistribution of source code may not be released under the terms of - * any GNU Public License derivate. - * - * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - - - - -#ifndef MMSTR_H -#define MMSTR_H - - - - -/* INCLUDES */ - -#include - -#include -#include - - - - -/* STRUCTURES */ - -typedef struct mmstrnode { - pnode_t node; - char string[256]; -} mmstrnode; - - - - -/* PROTOTYPES */ - -extern bool mmstrinit(void * (*)(size_t), void(*)(void *), size_t); -extern void mmstrexit(void); -extern char * mmstrdup(const char *); -extern char * mmstralloc(size_t); -extern char * mmstrfree(char *); - - - - -#endif diff --git a/mmsoftware/mmlib/mmstring.c b/mmsoftware/mmlib/mmstring.c deleted file mode 100644 index ab7a60a..0000000 --- a/mmsoftware/mmlib/mmstring.c +++ /dev/null @@ -1,1405 +0,0 @@ -/* $Id: mmstring.c,v 1.31 2005/01/31 18:11:57 mmondor Exp $ */ - -/* - * Copyright (C) 1989-2004, Matthew Mondor - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Matthew Mondor. - * 4. The name of Matthew Mondor may not be used to endorse or promote - * products derived from this software without specific prior written - * permission. - * 5. Redistribution of source code may not be released under the terms of - * any GNU Public License derivate. - * - * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -/* XXX Implement mm_strstr() */ - -/* A main reason for providing my string library is that as most mmsoftware - * is security-aware, and some implementations vary, it is safer. A popular - * example is strn*() functions which are not always implemented properly - * on some libc's. I even once stumbled on some bogus strlen() which used - * some 4-bytes hackery in a wrong way. Moreover, some memmov()/memcpy() - * implementations behavior is not right. So let's not worry too much about - * each CPU cycle, but rather about preventing buffer overflow opportunities, - * and producing consistant code. But let's still code these functions in a - * way to remain reasonably fast. - */ - - - - -#include -#include - -#include -#include - - - - -MMCOPYRIGHT("@(#) Copyright (c) 1989-2004\n\ -\tMatthew Mondor. All rights reserved.\n"); -MMRCSID("$Id: mmstring.c,v 1.31 2005/01/31 18:11:57 mmondor Exp $"); - -static const unsigned char toupper_table[] = { - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, - 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, - 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, - 0x1E, 0x1F, ' ', '!', '"', '#', '$', '%', '&', 0x27, - '(', ')', '*', '+', ',', '-', '.', '/', '0', '1', - '2', '3', '4', '5', '6', '7', '8', '9', ':', ';', - '<', '=', '>', '?', '@', 'A', 'B', 'C', 'D', 'E', - 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', - 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', - 'Z', '[', 0x5C, ']', '^', '_', '`', 'A', 'B', 'C', - 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', - 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', - 'X', 'Y', 'Z', '{', '|', '}', '~', 0x7F, 0x80, 0x81, - 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, - 0x8C, 0x8D, 0x8E, 0x8F, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, - 0x96, 0x97, 0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9D, 0x9E, 0x9F, - 0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, - 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF, 0xB0, 0xB1, 0xB2, 0xB3, - 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD, - 0xBE, 0xBF, 0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, - 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF, 0xD0, 0xD1, - 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xDB, - 0xDC, 0xDD, 0xDE, 0xDF, 0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, - 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF, - 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, - 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF -}; - -static const unsigned char tolower_table[] = { - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, - 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, - 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, - 0x1E, 0x1F, ' ', '!', '"', '#', '$', '%', '&', 0x27, - '(', ')', '*', '+', ',', '-', '.', '/', '0', '1', - '2', '3', '4', '5', '6', '7', '8', '9', ':', ';', - '<', '=', '>', '?', '@', 'a', 'b', 'c', 'd', 'e', - 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', - 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', - 'z', '[', 0x5C, ']', '^', '_', '`', 'a', 'b', 'c', - 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', - 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', - 'x', 'y', 'z', '{', '|', '}', '~', 0x7F, 0x80, 0x81, - 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, - 0x8C, 0x8D, 0x8E, 0x8F, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, - 0x96, 0x97, 0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9D, 0x9E, 0x9F, - 0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, - 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF, 0xB0, 0xB1, 0xB2, 0xB3, - 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD, - 0xBE, 0xBF, 0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, - 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF, 0xD0, 0xD1, - 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xDB, - 0xDC, 0xDD, 0xDE, 0xDF, 0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, - 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF, - 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, - 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF -}; - - - - -/* Splits columns of a string delimited by spaces and/or tabs, and fills - * char **argv with pointers to each column. Returns the number of columns - * that could be filled in. The supplied string IS modified. - */ -int -mm_straspl(char **argv, char *str, int maxcols) -{ - register char *ptr, *ptr2; - int col; - - for (ptr = str, col = 0; *ptr != '\0' && col < maxcols; ) { - for (; *ptr == ' ' || *ptr == '\t'; ptr++) ; - if (*ptr != '\0') { - for (ptr2 = ptr; *ptr != '\0' && *ptr != ' ' && *ptr != '\t'; - ptr++) ; - if (ptr != ptr2) { - if (*ptr != '\0') - *ptr++ = '\0'; - argv[col++] = ptr2; - } else - break; - } else - break; - } - - return col; -} - - -/* Similar to mm_straspl() but supports single quote-enclosed multiword - * strings. We also consider '\r' and '\n' as an EOL indicator, and return -1 - * on error (unclosed quote). This function is most useful to parse command - * lines. - */ -int -mm_strasplq(char **argv, char *str, int maxcols) -{ - register char *ptr, *sptr; - int col; - bool quoted; - - for (ptr = str, col = 0; - *ptr != '\0' && *ptr != '\n' && *ptr != '\r' && col < maxcols; ) { - for (; *ptr == ' ' || *ptr == '\t'; ptr++) ; - if (*ptr != '\0' && *ptr != '\n' && *ptr != '\r') { - if (*ptr == '\'') { - quoted = TRUE; - ptr++; - } else - quoted = FALSE; - for (sptr = ptr; *ptr != '\0' && *ptr != '\n' && *ptr != '\r' && - ((!quoted && *ptr != ' ' && *ptr != '\t') || - (quoted && *ptr != '\'')); ptr++) ; - if (quoted && *ptr != '\'') - return -1; - if (ptr != sptr) { - if (*ptr != '\0') { - if (*ptr != '\n' && *ptr != '\r') - *ptr++ = '\0'; - else - *ptr = '\0'; - } - argv[col++] = sptr; - } else - break; - } - } - - return col; -} - - -/* Parses the supplied command line , of which the first word/element - * is expected to contain the absolute fullpath of an executable. Following - * may be optional arguments. On success, TRUE is returned, and the executable - * fullpath is set in , the arguments in , as well as the argument - * count in (which includes the first argument, argv[0] which consists - * of the executable name (without the directory elements)). - * Very useful prior to using execve(2). - * FALSE is returned if a parsing error occurs (mismatched quote, - * first element not an absolute fullpath, etc) in which case the supplied - * pointers and should be considered in an undefined state. - * is modified, and the pointers will point to locations within it. - * It is thus advised to duplicate the command string as necessary. - * A maximum of entries in argv will be allowed (including argv[0] and - * terminating NULL), which means that it at least should be 2. - */ -bool -mm_cmdparse(char **path, int *argc, char **argv, char *cmd, int max) -{ - int i; - register char *ptr; - - /* Not enough space in argv? */ - if (max < 2) - return FALSE; - /* Failed parsing (unmatched quote?) */ - if ((i = mm_strasplq(argv, cmd, max - 1)) == -1) - return FALSE; - /* Command not an absolute fullpath? */ - if (*argv[0] != '/') - return FALSE; - - *argc = i; - argv[i] = NULL; - *path = argv[0]; - - /* Find end of command */ - for (ptr = *path; *ptr != '\0'; ptr++) ; - /* Find last '/' in command */ - for (; ptr > *path && *ptr != '/'; ptr--) ; - /* If only '/' is at first column, no command specified, return FALSE. - * Otherwise, split command at last '/' and set argv[0] to command. - */ - if (ptr == *path || *(++ptr) == '\0') - return FALSE; - argv[0] = ptr; - - return TRUE; -} - - -/* XXX Unlike ANSI, returns pointer at end of destination rather to beginning - * to allow special optimizations in loops. - */ -char * -mm_strcat(char *dest, const char *src) -{ - for (; *dest != '\0'; dest++) ; - for (; (*dest = *src++) != '\0'; dest++) ; - - return (dest); -} - - -char * -mm_strchr(const char *str, int c) -{ - for (; *str != '\0' && *str != c; str++) ; - - return (*str != '\0' ? (char *)str : NULL); -} - - -int -mm_strcmp(const char *s1, const char *s2) -{ - for (; *s1 != '\0' && *s2 != '\0' && *s1 == *s2; s1++, s2++) ; - - return ((int)((const unsigned char)*s1 - (const unsigned char)*s2)); -} - - -/* XXX Unlike standard strcpy(), returns pointer to end of copied string in - * destination, rather than to beginning, more useful to optimize some loops. - * This variant should never be called strcpy() (i.e., could be called - * _strcpy() however). - */ -char * -mm_strcpy(char *dest, const char *src) -{ - for (; (*dest = *src++) != '\0'; dest++) ; - - return (dest); -} - - -/* XXX Should be mm_strdup() but then conflicts with libmm! */ -char * -_mm_strdup(const char *str) -{ - char *new; - register const char *ptr; - register size_t len; - - for (new = NULL, ptr = str; *ptr != '\0'; ptr++) ; - - len = (size_t)(ptr - str) + 1; - if ((new = malloc(len)) != NULL) - (void) mm_memcpy(new, str, len); - - return new; -} - - -int -mm_strcasecmp(const char *s1, const char *s2) -{ - register const unsigned char *us1, *us2; - register unsigned char cs1, cs2; - - for (us1 = (const unsigned char *)s1, us2 = (const unsigned char *)s2, - cs1 = tolower_table[(int)*us1], cs2 = tolower_table[(int)*us2]; - cs1 != '\0' && cs2 != '\0' && - (cs1 = tolower_table[(int)*us1]) == - (cs2 = tolower_table[(int)*us2]); - us1++, us2++) ; - - return ((int)(cs1 - cs2)); -} - - -size_t mm_strlen(const char *str) -{ - register const char *ptr = str; - - /* Although this causes ptr to be increased even when 0 is reached, - * compilers generally generate better code. - * i.e. GCC2-m68k: tstb %a0@+ vs tstb %a0@ and addql #1, %a0 - * Matt - */ - while (*ptr++ != '\0') ; - - /* Don't forget to substract 1 */ - return ((size_t)((ptr - 1) - str)); -} - - -/* XXX Unlike ANSI, returns pointer at end of destination rather to beginning - * to allow special optimizations in loops. - */ -char * -mm_strncat(char *dest, const char *src, size_t max) -{ - if (max != 0) { - register const char *toptr; - - for (toptr = dest, toptr += max; dest < toptr && *dest != '\0'; - dest++) ; - for (; dest < toptr && (*dest = *src++) != '\0'; dest++) ; - if (dest < toptr) - *dest = '\0'; - } - - return dest; -} - - -char * -mm_strnchr(const char *str, int c, size_t max) -{ - if (max > 0) { - register const char *toptr; - - for (toptr = str, toptr += max; - str < toptr && *str != '\0' && (int)*str != c; str++) ; - - if (str == toptr || *str == '\0') - return NULL; - - return (char *)str; - } - - return NULL; -} - - -int -mm_strncmp(const char *s1, const char *s2, size_t max) -{ - if (max > 0) { - register const char *toptr; - - for (toptr = s1, toptr += max; s1 < toptr && *s1 != '\0' && - *s2 != '\0' && *s1 == *s2; s1++, s2++) ; - - return (s1 < toptr ? - ((int)((const unsigned char)*s1 - (const unsigned char)*s2)) : - 0); - } - - return 0; -} - - -/* XXX This function, unlike the useless return code of the standard ANSI one, - * returns the number of characters successfully copied (without the NUL). - * Moreover, this implementation ALWAYS NUL-terminates the resulting string, - * even if the output exceeded the buffer in which case the results are - * truncated. This is much better for security-critical applications. - */ -size_t -mm_strncpy(char *dest, const char *src, size_t max) -{ - if (max > 0) { - register const char *sptr; - register char *toptr; - - for (sptr = src, toptr = dest, toptr += max; - dest < toptr && (*dest = *sptr) != '\0'; sptr++, dest++) ; - if (dest == toptr) - *dest = '\0'; - - return ((size_t)(sptr - src)); - } - - *dest = '\0'; - return 0; -} - - -char * -mm_strndup(const char *str, size_t max) -{ - char *new; - register const char *ptr, *toptr; - size_t len; - - for (toptr = ptr = str, toptr += max, new = NULL; - ptr < toptr && *ptr != '\0'; ptr++) ; - len = (size_t)(ptr - str); - if ((new = malloc(len + 1)) != NULL) { - (void) mm_memcpy(new, str, len); - new[len] = '\0'; - } - - return new; -} - - -int -mm_strncasecmp(const char *s1, const char *s2, size_t max) -{ - register const unsigned char *us1, *us2, *toptr; - register unsigned char cs1, cs2; - - if (max == 0) - return 0; - - for (us1 = (const unsigned char *)s1, us2 = (const unsigned char *)s2, - cs1 = tolower_table[(int)*us1], cs2 = tolower_table[(int)*us2], - toptr = us1, toptr += max; - us1 < toptr && cs1 != '\0' && cs2 != '\0' && - (cs1 = tolower_table[(int)*us1]) == - (cs2 = tolower_table[(int)*us2]); - us1++, us2++) ; - - return (us1 < toptr ? ((int)(cs1 - cs2)) : 0); -} - - -size_t -mm_strnlen(const char *str, size_t max) -{ - register const char *fptr, *tptr; - - for (fptr = tptr = str, tptr += max; fptr < tptr && *fptr != '\0'; fptr++) - ; - - return ((size_t)(fptr - str)); -} - - -char * -mm_strnrchr(const char *str, int c, size_t max) -{ - register const char *toptr, *found; - - for (found = NULL, toptr = str, toptr += max; - str < toptr && *str != '\0'; str++) { - if (*str == c) - found = str; - } - - return (char *)found; -} - - -char * -mm_strrchr(const char *str, int c) -{ - register const char *found; - - for (found = NULL; *str != '\0'; str++) { - if (*str == c) - found = str; - } - - return (char *)found; -} - - -/* Splits columns of a string delimited by sep, and fills - * char **argv with pointers to each column. Returns the number of columns - * that could be filled in. The supplied string IS modified. Note that two - * contiguous separators cause empty entries to be filled in. An exception - * consists of the last separator, which is ignored if nothing is found after - * it. - */ -int -mm_strspl(char **argv, char *str, int maxcols, char sep) -{ - register char *ptr, *ptr2; - int col; - - for (col = 0, ptr = str; *ptr != '\0' && col < maxcols; ) { - for (ptr2 = ptr; *ptr != '\0' && *ptr != sep; ptr++) ; - if (*ptr != '\0') - *ptr++ = '\0'; - argv[col++] = ptr2; - } - - return col; -} - - -void -mm_strlower(char *str) -{ - register unsigned char *ustr; - - for (ustr = (unsigned char *)str; *ustr != '\0'; ustr++) - *ustr = tolower_table[(int)*ustr]; -} - - -void -mm_strupper(char *str) -{ - register unsigned char *ustr; - - for (ustr = (unsigned char *)str; *ustr != '\0'; ustr++) - *ustr = toupper_table[(int)*ustr]; -} - - -/* This function generates a 32-bit hash using the supplied string which - * is suitable for fast lookup for command comparision. It simply converts - * characters to uppercase and stores them in the value. It of course can - * only perform this for 4 bytes. It will stop at either end of string '\0' - * or space ' '. If the string has more than 4 characters 0 is returned. - */ -u_int32_t -mm_strpack32(const char *str, size_t min) -{ - register unsigned const char *ustr; - register u_int32_t hash = 0; - size_t i; - - for (ustr = (unsigned const char *)str, i = 0; *ustr > 32 && i < 5; i++) { - hash <<= 8; - hash |= toupper_table[(int)*ustr++]; - } - if (i < min || i > 4) - hash = 0; - - return hash; -} - - -unsigned long -mm_htol(const char *s) -{ - register unsigned long v; - - for (v = 0; *s != '\0'; ) { - if (*s >= '0' && *s <= '9') { - if (v <= (unsigned short)-1) - v = (unsigned short)v * 16; - else - v = v * 16; - v += *s++ - '0'; - } else if (*s >= 'a' && *s <= 'f') { - if (v <= (unsigned short)-1) - v = (unsigned short)v * 16; - else - v = v * 16; - v += *s++ - 87; - } else if (*s >= 'A' && *s <= 'F') { - if (v <= (unsigned short)-1) - v = (unsigned short)v * 16; - else - v = v * 16; - v += *s++ - 55; - } else - break; - } - - return v; -} - - -void -mm_strrev(char *str) -{ - register char *p1, *p2, t; - - for (p1 = p2 = str; *p2 != '\0'; p2++) ; - if (p2 > p1) - p2--; - - for (;p1 < p2; p1++, p2--) { - t = *p1; - *p1 = *p2; - *p2 = t; - } -} - -void -mm_strrevto(char *dst, const char *src) -{ - register const char *p; - - for (p = src; *p != '\0'; p++) ; - - while (p >= src) - *dst++ = *--p; - - *dst = '\0'; -} - - -/* This hash function should be pretty safe as it was tested to not cause - * duplicates on: - * locate '*' - * cat /usr/share/dict/words - * XXX Could be unrolled a little - */ -u_int64_t -mm_strhash64(const char *str) -{ - u_int64_t hash; - register const unsigned char *ptr; - - for (hash = 0, ptr = (const unsigned char *)str; *ptr != '\0'; ptr++) - hash = *ptr + (2818937311llu * hash); - - return hash; -} - - -/* Variant of hash64 on memory buffer */ -u_int64_t -mm_memhash64(const void *mem, size_t size) -{ - u_int64_t hash; - register const unsigned char *curmem, *tomem; - - for (hash = 0, curmem = tomem = (const unsigned char *)mem, tomem += size; - curmem < tomem; curmem++) - hash = *curmem + (2818937311llu * hash); - - return hash; -} - - -u_int32_t -mm_memhash32(const void *mem, size_t size) -{ - register u_int32_t hash; - register const unsigned char *curmem, *tomem; - - hash = 0; - curmem = tomem = mem; - tomem += size; - -#if !defined(ARCH_LOWCACHE) - while (curmem < tomem - 4) { -#if !defined(ARCH_USEINDEXING) - hash = *curmem++ + (31 * hash); - hash = *curmem++ + (31 * hash); - hash = *curmem++ + (31 * hash); - hash = *curmem++ + (31 * hash); -#else /* !defined(ARCH_USEINDEXING) */ - hash = curmem[0] + (31 * hash); - hash = curmem[1] + (31 * hash); - hash = curmem[2] + (31 * hash); - hash = curmem[3] + (31 * hash); - curmem += 4; -#endif /* !defined(ARCH_USEINDEXING) */ - } -#endif /* !defined(ARCH_LOWCACHE) */ - while (curmem < tomem) - hash = *curmem++ + (31 * hash); - - return hash; -} - - -/* These functions are useful to use in conjunction with mmhash(3) if - * case-insensitive processing of data is required while case-sensitivity of - * records storage must be preserved. - */ - -u_int32_t -mm_memcasehash32(const void *mem, size_t size) -{ - register u_int32_t hash; - register const unsigned char *curmem, *tomem; - - hash = 0; - curmem = tomem = mem; - tomem += size; - -#if !defined(ARCH_LOWCACHE) - while (curmem < tomem - 4) { -#if !defined(ARCH_USEINDEXING) - hash = toupper_table[(int)*curmem++] + (31 * hash); - hash = toupper_table[(int)*curmem++] + (31 * hash); - hash = toupper_table[(int)*curmem++] + (31 * hash); - hash = toupper_table[(int)*curmem++] + (31 * hash); -#else /* !defined(ARCH_USEINDEXING) */ - hash = toupper_table[(int)curmem[0]] + (31 * hash); - hash = toupper_table[(int)curmem[1]] + (31 * hash); - hash = toupper_table[(int)curmem[2]] + (31 * hash); - hash = toupper_table[(int)curmem[3]] + (31 * hash); - curmem += 4; -#endif /* !defined(ARCH_USEINDEXING) */ - } -#endif /* !defined(ARCH_LOWCACHE) */ - while (curmem < tomem) - hash = toupper_table[(int)*curmem++] + (31 * hash); - - return hash; -} - -int -mm_memcasecmp(const void *s1, const void *s2, size_t size) -{ - register const unsigned char *ptr1, *ptr2, *toptr; - -#define CMP() toupper_table[(int)*ptr1++] != toupper_table[(int)*ptr2++] -#define RET() return (int)(toupper_table[(int)*(--ptr1)] - \ - toupper_table[(int)*(--ptr2)]) - - ptr1 = toptr = s1; - toptr += size; - ptr2 = s2; - -#if !defined(ARCH_LOWCACHE) - while (ptr1 < toptr - 4) - if (CMP() || CMP() || CMP() || CMP()) - RET(); -#endif - while (ptr1 < toptr) - if (CMP()) - RET(); - -#undef CMP -#undef RET - - return 0; -} - - -/* Some may argue about the usefulness of such a thing. Some C libraries will - * provide more efficient architecture-dependent functions using unrolling - * when necessary, word copy methods, or even hardware data manipulation - * functions. But, some systems may have broken implementations, and others - * will provide unefficient C or assembly ones. So I borrow this code from - * my Xisop project which is portable and known to work decently. - * These functions are probably as fast as C ones could be. - * The ARCH_LONG_BITS, ARCH_LOWCACHE and ARCH_USEINDEXING definitions are - * found in mmlib/mmtypes.h and should be tweaked for maximum performance on - * a particular architecture. Note that compiler will also affect results. - * The reason for using "int" as the general word size for the system is that - * "long" type may be implemented as 32-bit on a 16-bit or pseudo-32bit - * system, and would be less efficient than moving words of the native size - * for the CPU. - * - * These functions seem to optimize especially well on m68k using GCC2. - * The i386/i586/i686 code is decent although it could be improoved. - * - * XXX It might be nice to use bit operators instead of modulo into the byte - * alignment macros. - * - * Matt - */ - - -int -mm_memcmp(const void *dest, const void *src, size_t len) -{ - register const unsigned char *ptr, *toptr, *dptr; - - ptr = toptr = src; - toptr += len; - dptr = dest; - -#define RET(a, b) return (int)(*(--(a)) - *(--(b))) - -#if ARCH_LONG_BITS == 8 - -#if !defined(ARCH_LOWCACHE) - while (ptr < toptr - 8) - if (*ptr++ != *dptr++ || *ptr++ != *dptr++ || *ptr++ != *dptr++ || - *ptr++ != *dptr++ || *ptr++ != *dptr++ || *ptr++ != *dptr++ || - *ptr++ != *dptr++ || *ptr++ != *dptr++) - RET(dptr, ptr); -#endif /* !defined(ARCH_LOWCACHE) */ - while (ptr < toptr) - if (*ptr++ != *dptr++) - RET(dptr, ptr); - -#else /* ARCH_LONG_BITS != 8 */ - - if (len < 32 || ((long)ptr % sizeof(long)) != - ((long)dptr % sizeof(long))) { -#if !defined(ARCH_LOWCACHE) - while (ptr < toptr - 8) - if (*ptr++ != *dptr++ || *ptr++ != *dptr++ || *ptr++ != *dptr++ || - *ptr++ != *dptr++ || *ptr++ != *dptr++ || - *ptr++ != *dptr++ || *ptr++ != *dptr++ || - *ptr++ != *dptr++) - RET(dptr, ptr); -#endif /* !defined(ARCH_LOWCACHE) */ - while (ptr < toptr) - if (*ptr++ != *dptr++) - RET(dptr, ptr); - } else { - register const unsigned long *lptr, *ltoptr, *ldptr, *ldtoptr; - register const unsigned char *dtoptr; - - dtoptr = dptr; - dtoptr += len; - lptr = (const unsigned long *)OALIGN_CEIL(ptr, long); - ltoptr = (const unsigned long *)OALIGN_FLOOR(toptr, long); - ldptr = (const unsigned long *)OALIGN_CEIL(dptr, long); - ldtoptr = (const unsigned long *)OALIGN_FLOOR(dtoptr, long); - if (ldtoptr - ldptr < ltoptr - lptr) - ltoptr--; - - while (ptr < (const unsigned char *)lptr) - if (*ptr++ != *dptr++) - RET(dptr, ptr); -#if !defined(ARCH_LOWCACHE) - while (lptr < ltoptr - (sizeof(long) * 8)) { - if (*lptr++ != *ldptr++ || *lptr++ != *ldptr++ || - *lptr++ != *ldptr++ || *lptr++ != *ldptr++ || - *lptr++ != *ldptr++ || *lptr++ != *ldptr++ || - *lptr++ != *ldptr++ || *lptr++ != *ldptr++) - RET(ldptr, lptr); - } -#endif /* !defined(ARCH_LOWCACHE) */ - while (lptr < ltoptr) - if (*lptr++ != *ldptr++) - RET(ldptr, lptr); - ptr = (const unsigned char *)lptr; - dptr = (const unsigned char *)ldptr; - while (ptr < toptr) - if (*ptr++ != *dptr++) - RET(dptr, ptr); - } - -#endif /* ARCH_LONG_BITS == 8 */ - -#undef RET - - return 0; -} - - -void * -mm_memcpy(void *dest, const void *src, size_t len) -{ - register const unsigned char *ptr, *toptr; - register unsigned char *dptr; - - ptr = toptr = src; - toptr += len; - dptr = dest; - -#if ARCH_LONG_BITS == 8 - -#if !defined(ARCH_LOWCACHE) - while (ptr < toptr - 8) { -#if !defined(ARCH_USEINDEXING) - *dptr++ = *ptr++; - *dptr++ = *ptr++; - *dptr++ = *ptr++; - *dptr++ = *ptr++; - *dptr++ = *ptr++; - *dptr++ = *ptr++; - *dptr++ = *ptr++; - *dptr++ = *ptr++; -#else /* !defined(ARCH_USEINDEXING) */ - dptr[0] = ptr[0]; - dptr[1] = ptr[1]; - dptr[2] = ptr[2]; - dptr[3] = ptr[3]; - dptr[4] = ptr[4]; - dptr[5] = ptr[5]; - dptr[6] = ptr[6]; - dptr[7] = ptr[7]; - dptr += 8; - ptr += 8; -#endif /* !defined(ARCH_USEINDEXING) */ - } -#endif /* !defined(ARCH_LOWCACHE) */ - while (ptr < toptr) - *dptr++ = *ptr++; - -#else /* ARCH_LONG_BITS != 8 */ - - if (len < 32 || ((long)ptr % sizeof(long)) != - ((long)dptr % sizeof(long))) { -#if !defined(ARCH_LOWCACHE) - while (ptr < toptr - 8) { -#if !defined(ARCH_USEINDEXING) - *dptr++ = *ptr++; - *dptr++ = *ptr++; - *dptr++ = *ptr++; - *dptr++ = *ptr++; - *dptr++ = *ptr++; - *dptr++ = *ptr++; - *dptr++ = *ptr++; - *dptr++ = *ptr++; -#else /* !defined(ARCH_USEINDEXING) */ - dptr[0] = ptr[0]; - dptr[1] = ptr[1]; - dptr[2] = ptr[2]; - dptr[3] = ptr[3]; - dptr[4] = ptr[4]; - dptr[5] = ptr[5]; - dptr[6] = ptr[6]; - dptr[7] = ptr[7]; - dptr += 8; - ptr += 8; -#endif /* !defined(ARCH_USEINDEXING) */ - } -#endif /* !defined(ARCH_LOWCACHE) */ - while (ptr < toptr) - *dptr++ = *ptr++; - } else { - register const unsigned long *lptr, *ltoptr; - register unsigned long *ldptr, *ldtoptr; - register unsigned char *dtoptr; - - dtoptr = dptr; - dtoptr += len; - lptr = (const unsigned long *)OALIGN_CEIL(ptr, long); - ltoptr = (const unsigned long *)OALIGN_FLOOR(toptr, long); - ldptr = (unsigned long *)OALIGN_CEIL(dptr, long); - ldtoptr = (unsigned long *)OALIGN_FLOOR(dtoptr, long); - if (ldtoptr - ldptr < ltoptr - lptr) - ltoptr--; - - while (ptr < (const unsigned char *)lptr) - *dptr++ = *ptr++; -#if !defined(ARCH_LOWCACHE) - while (lptr < ltoptr - (sizeof(long) * 8)) { -#if !defined(ARCH_USEINDEXING) - *ldptr++ = *lptr++; - *ldptr++ = *lptr++; - *ldptr++ = *lptr++; - *ldptr++ = *lptr++; - *ldptr++ = *lptr++; - *ldptr++ = *lptr++; - *ldptr++ = *lptr++; - *ldptr++ = *lptr++; -#else /* !defined(ARCH_USEINDEXING) */ - ldptr[0] = lptr[0]; - ldptr[1] = lptr[1]; - ldptr[2] = lptr[2]; - ldptr[3] = lptr[3]; - ldptr[4] = lptr[4]; - ldptr[5] = lptr[5]; - ldptr[6] = lptr[6]; - ldptr[7] = lptr[7]; - ldptr = &ldptr[8]; - lptr = &lptr[8]; -#endif /* !defined(ARCH_USEINDEXING) */ - } -#endif /* !defined(ARCH_LOWCACHE) */ - while (lptr < ltoptr) - *ldptr++ = *lptr++; - ptr = (unsigned const char *)lptr; - dptr = (unsigned char *)ldptr; - while (ptr < toptr) - *dptr++ = *ptr++; - } - -#endif /* ARCH_LONG_BITS == 8 */ - - return dest; -} - - -/* Can work with overlapping areas */ -void * -mm_memmove(void *dest, const void *src, size_t len) -{ - register const unsigned char *ptr = NULL, *toptr = NULL; - register unsigned char *dptr = NULL; - size_t d; - - if (dest < src) - d = (const unsigned char *)src - (unsigned char *)dest; - else - d = (unsigned char *)dest - (const unsigned char *)src; - -#if ARCH_LONG_BITS == 8 - - if (dest < src) { - /* Copy in increasing order */ - ptr = toptr = (const unsigned char *)src; - toptr += len; - dptr = dest; -#if !defined(ARCH_LOWCACHE) - while (ptr < toptr - 8) { -#if !defined(ARCH_USEINDEXING) - *dptr++ = *ptr++; - *dptr++ = *ptr++; - *dptr++ = *ptr++; - *dptr++ = *ptr++; - *dptr++ = *ptr++; - *dptr++ = *ptr++; - *dptr++ = *ptr++; - *dptr++ = *ptr++; -#else /* !defined(ARCH_USEINDEXING) */ - dptr[0] = ptr[0]; - dptr[1] = ptr[1]; - dptr[2] = ptr[2]; - dptr[3] = ptr[3]; - dptr[4] = ptr[4]; - dptr[5] = ptr[5]; - dptr[6] = ptr[6]; - dptr[7] = ptr[7]; - dptr += 8; - ptr += 8; -#endif /* !defined(ARCH_USEINDEXING) */ - } -#endif /* !defined(ARCH_LOWCACHE) */ - while (ptr < toptr) - *dptr++ = *ptr++; - } else if (dest > src) { - /* Copy in reverse order */ - ptr = toptr = (const unsigned char *)src; - ptr += len; - dptr = dest; - dptr += len; -#if !defined(ARCH_LOWCACHE) - while (ptr >= toptr + 8) { -#if !defined(ARCH_USEINDEXING) - *dptr-- = *ptr--; - *dptr-- = *ptr--; - *dptr-- = *ptr--; - *dptr-- = *ptr--; - *dptr-- = *ptr--; - *dptr-- = *ptr--; - *dptr-- = *ptr--; - *dptr-- = *ptr--; -#else /* !defined(ARCH_USEINDEXING) */ - dptr[0] = ptr[0]; - dptr[-1] = ptr[-1]; - dptr[-2] = ptr[-2]; - dptr[-3] = ptr[-3]; - dptr[-4] = ptr[-4]; - dptr[-5] = ptr[-5]; - dptr[-6] = ptr[-6]; - dptr[-7] = ptr[-7]; - dptr -= 8; - ptr -= 8; -#endif /* !defined(ARCH_USEINDEXING) */ - } -#endif /* !defined(ARCH_LOWCACHE) */ - while (ptr >= toptr) - *dptr-- = *ptr--; - } - -#else /* ARCH_LONG_BITS != 8 */ - - if (len < 32 || d < sizeof(long) || - ((long)ptr % sizeof(long)) != ((long)dptr % sizeof(long))) { - if (dest < src) { - /* Copy in increasing order */ - ptr = toptr = (const unsigned char *)src; - toptr += len; - dptr = dest; -#if !defined(ARCH_LOWCACHE) - while (ptr < toptr - 8) { -#if !defined(ARCH_USEINDEXING) - *dptr++ = *ptr++; - *dptr++ = *ptr++; - *dptr++ = *ptr++; - *dptr++ = *ptr++; - *dptr++ = *ptr++; - *dptr++ = *ptr++; - *dptr++ = *ptr++; - *dptr++ = *ptr++; -#else /* !defined(ARCH_USEINDEXING) */ - dptr[0] = ptr[0]; - dptr[1] = ptr[1]; - dptr[2] = ptr[2]; - dptr[3] = ptr[3]; - dptr[4] = ptr[4]; - dptr[5] = ptr[5]; - dptr[6] = ptr[6]; - dptr[7] = ptr[7]; - dptr += 8; - ptr += 8; -#endif /* !defined(ARCH_USEINDEXING) */ - } -#endif /* !defined(ARCH_LOWCACHE) */ - while (ptr < toptr) - *dptr++ = *ptr++; - } else if (dest > src) { - /* Copy in reverse order */ - ptr = toptr = (const unsigned char *)src; - ptr += len; - dptr = dest; - dptr += len; -#if !defined(ARCH_LOWCACHE) - while (ptr >= toptr + 8) { -#if !defined(ARCH_USEINDEXING) - *dptr-- = *ptr--; - *dptr-- = *ptr--; - *dptr-- = *ptr--; - *dptr-- = *ptr--; - *dptr-- = *ptr--; - *dptr-- = *ptr--; - *dptr-- = *ptr--; - *dptr-- = *ptr--; -#else /* !defined(ARCH_USEINDEXING) */ - dptr[0] = ptr[0]; - dptr[-1] = ptr[-1]; - dptr[-2] = ptr[-2]; - dptr[-3] = ptr[-3]; - dptr[-4] = ptr[-4]; - dptr[-5] = ptr[-5]; - dptr[-6] = ptr[-6]; - dptr[-7] = ptr[-7]; - dptr -= 8; - ptr -= 8; -#endif /* !defined(ARCH_USEINDEXING) */ - } -#endif /* !defined(ARCH_LOWCACHE) */ - while (ptr >= toptr) - *dptr-- = *ptr--; - } - } else { - if (dest < src) { - /* Increasing order */ - register const unsigned long *lptr, *ltoptr; - register unsigned long *ldptr, *ldtoptr; - register unsigned char *dtoptr; - - ptr = toptr = (const unsigned char *)src; - toptr += len; - dptr = dest; - - dtoptr = dptr; - dtoptr += len; - lptr = (const unsigned long *)OALIGN_CEIL(ptr, long); - ltoptr = (const unsigned long *)OALIGN_FLOOR(toptr, long); - ldptr = (unsigned long *)OALIGN_CEIL(dptr, long); - ldtoptr = (unsigned long *)OALIGN_FLOOR(dtoptr, long); - if (ldtoptr - ldptr < ltoptr - lptr) - ltoptr--; - - while (ptr < (const unsigned char *)lptr) - *dptr++ = *ptr++; -#if !defined(ARCH_LOWCACHE) - while (lptr < ltoptr - (sizeof(long) * 8)) { -#if !defined(ARCH_USEINDEXING) - *ldptr++ = *lptr++; - *ldptr++ = *lptr++; - *ldptr++ = *lptr++; - *ldptr++ = *lptr++; - *ldptr++ = *lptr++; - *ldptr++ = *lptr++; - *ldptr++ = *lptr++; - *ldptr++ = *lptr++; -#else /* !defined(ARCH_USEINDEXING) */ - ldptr[0] = lptr[0]; - ldptr[1] = lptr[1]; - ldptr[2] = lptr[2]; - ldptr[3] = lptr[3]; - ldptr[4] = lptr[4]; - ldptr[5] = lptr[5]; - ldptr[6] = lptr[6]; - ldptr[7] = lptr[7]; - ldptr = &ldptr[8]; - lptr = &lptr[8]; -#endif /* !defined(ARCH_USEINDEXING) */ - } -#endif /* !defined(ARCH_LOWCACHE) */ - while (lptr < ltoptr) - *ldptr++ = *lptr++; - ptr = (const unsigned char *)lptr; - dptr = (unsigned char *)ldptr; - while (ptr < toptr) - *dptr++ = *ptr++; - } else if (dest > src) { - /* Reverse order */ - register const long *lptr, *ltoptr; - register long *ldptr, *ldtoptr; - register char *dtoptr; - - ptr = toptr = (const unsigned char *)src; - ptr += len; - dptr = dest; - dptr += len; - - dtoptr = dest; - lptr = (const unsigned long *)OALIGN_FLOOR(ptr, long); - ldptr = (unsigned long *)OALIGN_FLOOR(dptr, long); - ltoptr = (const unsigned long *)OALIGN_CEIL(toptr, long); - ldtoptr = (unsigned long *)OALIGN_CEIL(dtoptr, long); - if (ldptr - ldtoptr < lptr - ltoptr) - ltoptr++; - - while (ptr >= (const unsigned char *)lptr) - *dptr-- = *ptr--; -#if !defined(ARCH_LOWCACHE) - while (lptr >= ltoptr + (sizeof(long) * 8)) { -#if !defined(ARCH_USEINDEXING) - *ldptr-- = *lptr--; - *ldptr-- = *lptr--; - *ldptr-- = *lptr--; - *ldptr-- = *lptr--; - *ldptr-- = *lptr--; - *ldptr-- = *lptr--; - *ldptr-- = *lptr--; - *ldptr-- = *lptr--; -#else /* !defined(ARCH_USEINDEXING) */ - ldptr[0] = lptr[0]; - ldptr[-1] = lptr[-1]; - ldptr[-2] = lptr[-2]; - ldptr[-3] = lptr[-3]; - ldptr[-4] = lptr[-4]; - ldptr[-5] = lptr[-5]; - ldptr[-6] = lptr[-6]; - ldptr[-7] = lptr[-7]; - ldptr = &ldptr[-8]; - lptr = &lptr[-8]; -#endif /* !defined(ARCH_USEINDEXING) */ - } -#endif /* !defined(ARCH_LOWCACHE) */ - while (lptr >= ltoptr) - *ldptr-- = *lptr--; - ptr = (const unsigned char *)lptr; - dptr = (unsigned char *)ldptr; - while (ptr >= toptr) - *dptr-- = *ptr--; - } - } - -#endif /* ARCH_LONG_BITS == 8 */ - - return dest; -} - - -void * -mm_memset(void *mem, int fill, size_t len) -{ - register unsigned char *ptr, *toptr; - u_int8_t byte = (u_int8_t)fill; - - ptr = toptr = mem; - toptr += len; - -#if ARCH_LONG_BITS == 8 - -#if !defined(ARCH_LOWCACHE) - while (ptr < toptr - 8) { -#if !defined(ARCH_USEINDEXING) - *ptr++ = byte; - *ptr++ = byte; - *ptr++ = byte; - *ptr++ = byte; - *ptr++ = byte; - *ptr++ = byte; - *ptr++ = byte; - *ptr++ = byte; -#else /* !defined(ARCH_USEINDEXING) */ - ptr[0] = byte; - ptr[1] = byte; - ptr[2] = byte; - ptr[3] = byte; - ptr[4] = byte; - ptr[5] = byte; - ptr[6] = byte; - ptr[7] = byte; - ptr += 8; -#endif /* !defined(ARCH_USEINDEXING) */ - } -#endif /* !defined(ARCH_LOWCACHE) */ - while (ptr < toptr) - *ptr++ = byte; - -#else /* ARCH_LONG_BITS != 8 */ - - if (len < 32) { -#if !defined(ARCH_LOWCACHE) - while (ptr < toptr - 8) { -#if !defined(ARCH_USEINDEXING) - *ptr++ = byte; - *ptr++ = byte; - *ptr++ = byte; - *ptr++ = byte; - *ptr++ = byte; - *ptr++ = byte; - *ptr++ = byte; - *ptr++ = byte; -#else /* !defined(ARCH_USEDARRAYS) */ - ptr[0] = byte; - ptr[1] = byte; - ptr[2] = byte; - ptr[3] = byte; - ptr[4] = byte; - ptr[5] = byte; - ptr[6] = byte; - ptr[7] = byte; - ptr += 8; -#endif /* !defined(ARCH_USEINDEXING) */ - } -#endif /* !defined(ARCH_LOWCACHE) */ - while (ptr < toptr) - *ptr++ = byte; - } else { - register unsigned long *lptr, *ltoptr, lword = 0; - - if (byte != 0) { -#if ARCH_LONG_BITS == 16 - lword = ((byte << 8) & 0xff00) | (byte & 0x00ff); -#elif ARCH_LONG_BITS == 32 - /* Creating a 16-bit word from the 8-bit one, then the 32-bit word - * from the 16-bit one require less instructions. - */ - register u_int16_t tmp; - - tmp = ((byte << 8) & 0xff00) | (byte & 0x00ff); - lword = ((tmp << 16) & 0xffff0000) | (tmp & 0x0000ffff); -#elif ARCH_LONG_BITS == 64 - /* Creating a 16-bit word from the 8-bit one, a 32-bit word from - * the 16-bit one and a 64-bit word from the 32-bit one require - * less instructions. - */ - register u_int32_t tmp2; - register u_int16_t tmp; - - tmp = ((byte << 8) & 0xff00) | (byte & 0x00ff); - tmp2 = ((tmp << 16) & 0xffff0000) | (tmp & 0x0000ffff); - lword = ((tmp2 << 32) & 0xffffffff00000000ULL) | - (tmp2 & 0x00000000ffffffffULL); -#endif - } - - lptr = (unsigned long *)OALIGN_CEIL(ptr, long); - ltoptr = (unsigned long *)OALIGN_FLOOR(toptr, long); - - while (ptr < (unsigned char *)lptr) - *ptr++ = byte; -#if !defined(ARCH_LOWCACHE) - while (lptr < ltoptr - (sizeof(long) * 8)) { -#if !defined(ARCH_USEINDEXING) - *lptr++ = lword; - *lptr++ = lword; - *lptr++ = lword; - *lptr++ = lword; - *lptr++ = lword; - *lptr++ = lword; - *lptr++ = lword; - *lptr++ = lword; -#else /* !defined(ARCH_USEINDEXING) */ - lptr[0] = lword; - lptr[1] = lword; - lptr[2] = lword; - lptr[3] = lword; - lptr[4] = lword; - lptr[5] = lword; - lptr[6] = lword; - lptr[7] = lword; - lptr = &lptr[8]; -#endif /* !defined(ARCH_USEINDEXING) */ - } -#endif /* !defined(ARCH_LOWCACHE) */ - while (lptr < ltoptr) - *lptr++ = lword; - ptr = (unsigned char *)lptr; - while (ptr < toptr) - *ptr++ = byte; - } - -#endif /* ARCH_LONG_BITS == 8 */ - - return mem; -} diff --git a/mmsoftware/mmlib/mmstring.h b/mmsoftware/mmlib/mmstring.h deleted file mode 100644 index 430edbb..0000000 --- a/mmsoftware/mmlib/mmstring.h +++ /dev/null @@ -1,106 +0,0 @@ -/* $Id: mmstring.h,v 1.17 2005/01/27 09:07:15 mmondor Exp $ */ - -/* - * Copyright (C) 1989-2004, Matthew Mondor - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Matthew Mondor. - * 4. The name of Matthew Mondor may not be used to endorse or promote - * products derived from this software without specific prior written - * permission. - * 5. Redistribution of source code may not be released under the terms of - * any GNU Public License derivate. - * - * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - - - - -#ifndef MMSTRING_H -#define MMSTRING_H - - - - -#include -#include - - - - -#define mm_memclr(a, l) mm_memset((a), 0, (l)) - - - - -extern size_t mm_strlen(const char *); -extern size_t mm_strnlen(const char *, size_t); - -extern char * mm_strcpy(char *, const char *); -extern size_t mm_strncpy(char *, const char *, size_t); - -extern char * mm_strcat(char *, const char *); -extern char * mm_strncat(char *, const char *, size_t); - -extern int mm_strcmp(const char *, const char *); -extern int mm_strncmp(const char *, const char *, size_t); -extern int mm_strcasecmp(const char *, const char *); -extern int mm_strncasecmp(const char *, const char *, size_t); - -extern char * mm_strchr(const char *, int); -extern char * mm_strnchr(const char *, int, size_t); -extern char * mm_strrchr(const char *, int); -extern char * mm_strnrchr(const char *, int, size_t); - -/* To not conflict with libmm! */ -extern char * _mm_strdup(const char *); -extern char * mm_strndup(const char *, size_t); - -extern int mm_straspl(char **, char *, int); -extern int mm_strasplq(char **, char *, int); -extern bool mm_cmdparse(char **, int *, char **, char *, int); -extern int mm_strspl(char **, char *, int, char); - -extern void mm_strlower(char *); -extern void mm_strupper(char *); -extern u_int32_t mm_strpack32(const char *, size_t); -extern unsigned long mm_htol(const char *); -extern void mm_strrev(char *); -extern void mm_strrevto(char *, const char *); -extern u_int64_t mm_strhash64(const char *); -extern u_int64_t mm_memhash64(const void *, size_t); - -extern u_int32_t mm_memhash32(const void *, size_t); -extern u_int32_t mm_memcasehash32(const void *, size_t); -extern int mm_memcasecmp(const void *, const void *, size_t); - -extern int mm_memcmp(const void *, const void *, size_t); -extern void * mm_memcpy(void *, const void *, size_t); -extern void * mm_memmove(void *, const void *, size_t); -extern void * mm_memset(void *, int, size_t); -/*extern void mm_memclr(void *, size_t);*/ - - - - -#endif diff --git a/mmsoftware/mmlib/mmtypes.h b/mmsoftware/mmlib/mmtypes.h deleted file mode 100644 index 0059e1f..0000000 --- a/mmsoftware/mmlib/mmtypes.h +++ /dev/null @@ -1,97 +0,0 @@ -/* $Id: mmtypes.h,v 1.7 2004/06/01 19:11:43 mmondor Exp $ */ - -/* - * Copyright (C) 2000-2004, Matthew Mondor - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Matthew Mondor. - * 4. The name of Matthew Mondor may not be used to endorse or promote - * products derived from this software without specific prior written - * permission. - * 5. Redistribution of source code may not be released under the terms of - * any GNU Public License derivate. - * - * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - - - -#ifndef MM_TYPES_H -#define MM_TYPES_H - - - -/* Load arch-specific configuration */ -#include "mmarch.h" - - - -/* This eventually has to go away or to be changed to another name, since it - * conflicts with curses, which also defines it as a 8-bit type. - * XXX - */ -typedef int bool; - - - -#ifndef NULL -#define NULL ((void *)0) -#endif - -#ifndef TRUE -#define TRUE /* CONSTCOND */(1) -#endif - -#ifndef FALSE -#define FALSE /* CONSTCOND */(0) -#endif - - - -#ifndef _SYS_TYPES_H_ -#ifndef __GLIBC__ -typedef unsigned long size_t; -#endif -#endif -#ifdef __GLIBC__ -typedef unsigned long useconds_t; -#endif - - - -#define MMCOPYRIGHT(x) static const char mmcopyright[] = x -#define MMRCSID(x) static const char mmrcsid[] = x - - - -/* Useful to o-align a value relative to v (sizes and pointers) */ -#define OALIGN_CEIL(v, o) \ - ((((size_t)(v)) + (sizeof(o)) - 1) / (sizeof(o)) * (sizeof(o))) -#define OALIGN_FLOOR(v, o) ((size_t)(v) - (((size_t)(v) % sizeof(o)))) - -/* Useful to byte align a value with supplied size */ -#define BALIGN_CEIL(v, s) ((((size_t)(v)) + (s) - 1) / (s) * (s)) -#define BALIGN_FLOOR(v, s) ((size_t)(v) - (((size_t)(v) % (s)))) - - - -#endif diff --git a/mmsoftware/mmmail/ChangeLog b/mmsoftware/mmmail/ChangeLog deleted file mode 100644 index 6beaffd..0000000 --- a/mmsoftware/mmmail/ChangeLog +++ /dev/null @@ -1,887 +0,0 @@ -$Id: ChangeLog,v 1.52 2005/11/17 07:38:07 mmondor Exp $ - - - -Release: mmmail 0.0.24 devl -Date : November 17, 2005 -By : Matthew Mondor - -* New features - - mailbox-specific statistic counters maintained with mmstat(3) are now - unified under mmmail|* rather than separated into mmsmtpd|* and mmpop3d|*. - - domain-specific statistic counters are now also maintained which total - the number of in/out messages and bytes for all mailboxes of the domain. - This is very useful to know how much one can charge for a domain, and/or - to generate pie graphics of distribution of bandwidth among the domains. - - Added STATFAIL_LOGIN and STATFAIL_PASSWORD configuration file options to - mmpop3d(8) which allow to easily detect failed logins. - - Added mmpop3d|total|logins mmstat(3) key - - mmmail can now store actual message bodies into a maildir similar format - (although not compatible with maildir). The old MySQL glob storage format - prooved to be quite slow on heavily loaded systems (MySQL being good at - small fields but very slow with large glob fields). If MMMAIL_MYSQL is - defined at compilation time, old glob MySQL storage will be used in the - mail table. On the other hand, if MMMAIL_FILE is defined at compilation - time, file storage will be used. - For file storage, a directory is automatically created per mailbox if - needed into the MMMAIL_FILE_DIR configuration file specified directory. - Within each mailbox directory, each message will be stored using a unique - filename. MySQL is still used to store an entry for each message, except - the body, and for each entry will hold the unique filename of the message. - This means that no scanning of the mail directory is needed. The MySQL - thread-friendly locking is still used when adding, accessing or deleting - messages, using a per-mailbox lock, so that it is safe to assume that - operations will be properly serialized, even over NFS. The administrator - should only add and delete message through mmsmtpd and mmpop3d, using the - SMTP and POP3 protocols, avoiding to modify the directories and files, - with the exception that a directory and all its files can safely be - deleted after a mailbox was deleted and all its mail entries destroyed in - the MySQL database. (Note that this is automatically done by mmsmtpd(8) - when a mailbox (or user with all it's mailboxes) are deleted via the HTTP - administration frontend). - There is an upgrade-0.0.24.sql script which was added to modify the - existing SQL tables to the newly expected format without loss of data. - tables.sql also was updated to reflect the new format. - - Added support for per-mailbox incoming mail filters so that private - mailboxes may only accept mail from allowed MAIL FROM:<> addresses. - Patterns may be used with '*' and '?' wildcards. This is optional; - mailboxes need to set box_filter=1 to enable this, and to add their - filters to the filter table. New STATFAIL_FILTER added to record - statistics on addresses attempting to send to mailboxes filtering them - out. The box_filter_type field specifies the type of filtering; 'D' to - deny messages by default except those for which a pattern exists or 'A' - to accept by default except for those having a matching pattern. - - Implemented basic relaying support, which is always optional, of course. - RELAYING must be TRUE in the mmsmtpd configuration file, the sender IP - address or hostname must be listed in the relayfrom table of patterns, - and destination domain must not be listed in the relaylocal patterns table - for a post to be accepted for relaying. mmsmtpd then queues the message - into the relayqueue table along with the message in a queue directory, - and the mmrelayd daemon must be running in order to process the queue and - actually perform mail relaying. The relaying daemon uses multiple threads - so that it may process multiple messages simultaneously. Relaying is only - possible if compiled with MMMAIL_FILE defined (file storage mode rather - than MySQL storage for message body text). RESOLVE_MX_RCPT was added as - well to configuration file to prevent invalid destinators addresses mail - to even get queued for relay. - - Sanity checking is now done on incomming message headers. Incomplete - messages with only a header (or part of a header) are converted to valid - messages. Messages which are missing the headers 'Message-Id:', 'Date:', - 'From:' or 'To:' are modified to include these. This was done in - preparation for relaying support. We want the relay queue to only contain - valid messages which other SMTP daemons are likely to accept. - - Added a user administration HTTP frontend for them to manage their mailbox - filters, implemented in PHP. - - Added an administration HTTP frontend to easily manage users and mailboxes - implemented in PHP. -* Performance enhancements - - The configuration file parser was rewritten to be more efficient and - to hold cleaner code than the previous one which was migrated from - one of my first C programs many years ago :) The configuration files - format is now more flexible as well, although the current files will - parse properly without modifications still. - - mmhash(3) is now used by the command parser for more speed. Any such - speed improvement is always welcome when using userspace threads. - - mmpool(3) was reimplemented for better efficiency. Also was added support - for object constructor/destrutor functions. - - In situations where the system has to scale up and down often, performance - improvements should be noticed because of the implementation of - mm_pth_pool(3), taking advantage of the new mmpool(3) facilities. This - causes a pool of threads to already be available, to which client - connections can be dispatched, rather than having to launch a new thread - per client. We also use this support in the allocation of client-specific - nodes which can recycle the objects. - - The new file storage method for message bodies is much more efficient - when used rather than the old MySQL glob storage method. - - mmserver(3) was enhanced as to use proper 4.2BSD setsockopt(2) to enhance - performance of sends and eliminate unnecessary TCP delays. -* Bug fixes - - mmstatd(8) and the mmstat(3) library had a bugfix. - - The message rate sanity checking host-based cache nodes expireing thread - would be awakened unnecessarily too fast, considering FLOOD_EXPIRES as a - number of seconds rather than as a number of minutes. - - A bug was found in the hops counting support for an incomming message. - - A free() bug was found in mmpool(3)'s pool_free(). - - The old string library which had functions copied as-is from my old code - of the 80's was througly revised. Many functions were either rewritten or - modified for better style and performance. Moreover, all library functions - have been throughly tested using a program especially made to test them. - A bug in mm_memcmp() and one in mm_memmove() were fixed along the way. - - CONNECTION_PERIOD was allowed to be 0, which caused the server to take - alot of CPU time. It can now only start at 1 (larger values recommended). - - A minor bug was fixed relative to single dot processing into messages. - We used to strip the first dot of all lines starting with dot, when - receiving, when all we needed to do was to replace ".." lines by "." ones. -* Other - - A minor code cleanup was done - - BSD-style mdoc manual pages were enhanced. - - The mmlimitrate(3) library was written to restrict the amount of code - duplication among my various software which require rate limiting in - various situations. The software now uses this API for rate throttling - (except the bandwidth shaping which is handled by mmfd(3) already). - - We dropped the user_admin column from the user table, as well as the - alias_user column from the alias table. The user table's user_id, as well - as box table's box_user columns now are varchar(32) and can serve for user - login ID via an HTTP frontend, instead of having to use numeric ID which - would be annoying for login. - - Added support for LOCK_PATH, allowing configuration-specific lock file to - be used to determine if the daemon is already running, avoiding to spawn - multiple copies (which used to be possible because of the port reusing - flag used at bind(2) to allow the server to be launched immediately - despite possibly pending state in the TCP stack. - - - -Release: mmmail 0.0.23 devl -Date : July 1, 2003 -By : Matthew Mondor - -* Bug fixes - - The IP-address based connection rate and DNS cache expire thread used to - sometimes waste CPU cycles looping more often that it should, sleeping for - 0 seconds between loops. Fixed. - - The same was happening to the message rate host-based cache expire thread, - which also was fixed. - - mmstatd(8) had a bugfix and was upgraded to 0.0.8 -* Important - - The mmstat(3) key names were modified to be '|' separated rather than - '.' separated. Although I have been trying to avoid such a change which - obviously requires fixing alot of scripts I am using, it was proven with - time that the '.' character was too widely used and that '|' was ideal - as a replacement. It is not hard to use a script to use the mmstat(8) - reset command and convert all old entries to the new type if necessary. - As a result, filenames which comport '.' characters for which counters - are maintained are much better to handle and to isolate. The same applies - to IP addresses. - - The maximum key name length now was bumped from 128 to 256 bytes. As - a result, a special log file synchronization will be required as was - the case for mmstatd 0.0.7 upgrade (and mmmail 0.0.22 upgrade). This also - requires mmstat clients using the mmstat(3) API to be recompiled. -* New features - - Per-mailbox message/bytes statistics are now being maintained via mmstat(3) - in mmsmtpd for incomming messages. - - Per-user/box logins/message/bytes/delete statistics are now being - maintained via mmstat(3) by mmpop3d. - - - -Release: mmmail 0.0.22 devl -Date : June 19, 2003 -By : Matthew Mondor - -* Important - - Important changes to mmstat(3) and mmstatd(8) were made which require a - database synchronization procedure to be performed before upgrading. - See mmstatd/ChangeLog for details. - - The GROUPS directive in mmsmtpd.conf(5), mmpop3d.conf(5) and - mmstatd.conf(5) now expect groups to be comma-separated instead of - space-separated. The quotes then of course also become optional if - multiple groups are specified. This was made because mmreadcfg(3) library - is also used by other of my projects for which comma-separated groups - were also required. -* Various - - Fixed ALIGN_CEIL alignment macros which used to always add bytes even - if the value was already aligned properly. As a result a few bytes of - memory were often wasted. - - Interface of various lower layer functions and macros were improved, and - mmftpd was migrated to use the new ones. - - When an alias points to an invalid address, an error is now logged about - it for the administrator to notice and delete the alias. - - mmstatd will now perform much better when used with a large number of - keys, and will now retain the real time of the operation if it had to - be killed before it can incorporate the recovery logs and sync. - - mmsmtpd now uses mmhash(3) for it's FLOOD_PROTECTION cache which enhances - performance when many hosts are in the cache, and yields cleaner code. - As the number of RCPTs allowed per post is generally small, it still uses - linked lists for those with the old 64-bit hash for faster comparition, - but sequencial searching is still performed when checking against - duplicates. This will change if I ever obtain a request about someone who - needs to use enough RCPTs in a post to use a hash table for performance. - - mmhash(3) is now used by mmserver(3) as well, which should enhance - connection validity and DNS hostname cache performance. -* Bug fixes - - The SQL SELECT statement in mmpop3d's auth_pass() function was erroneous - and often caused POP3 authentication problems. Fixed. - - - -Release: mmmail 0.0.21 devl -Date : January 9, 2003 -By : Matthew Mondor - -* Bug fixes (thanks to Jeroen Oostendorp for reporting) - - HELO would not accept IP addresses. It now will. - - The first matching entry in the aliases list would immediately map - an address, even if there existed closer matches. The new algorithm - will only map the address to the best match. (See important changes - section for more details). - - The RESOLVE_MX_HELO configuration file keyword was changed to RESOLVE_HELO - and it's behavior changed accordingly, to resolve hostname's A record - rather than the MX one. It was erroneous to expect SMTP clients to have - valid MX records. - - The check_nofrom() function, used to check for a match within the - nofrom database table, would only match on a hostname basis if resolving - client hostnames was enabled and the address could resolve successfully. - It now will also always check if the IP address also matches no matter - what. -* Important changes - - When desired, it is important for frontend scripts to be able to generate - required password hashes. This has not been easy with the previous method, - which did not even use standard base64 encoding but a custom one on the - MD5 result. Calling external binaries such as mmpasswd is not ideal. - Moreover, I wanted to get rid of the mhash library dependancy. - The solution was simple: password hashes now consist of standard - crypt(3) generated strings. Both MD5 and DES modes are supported. - However, mmpasswd will only generate MD5 ones with crypt(3). - This also allows administrators to easily migrate from real system - users, as this is the way system user password hashes are stored. - An attempt was made to provide a utility to easily convert old hashes to - new ones; This however is impossible due to a bug in the previous method - which caused the last few bytes of the MD5 results to be lost during the - base64 conversion. - A recommendation is to generate random passwords for the users, notify - them via email and then activate the new version using the new passwords - say, a week afterwards. This password format will then remain unchanged - for next versions. - It is important that people upgrading from 0.0.20 or older execute the - provided MySQL script upgrade-0.0.21.sql. Also, the user_passwd field of - the user table will need to be updated using the new mmpasswd results for - each user password. - - The alias table was modified because of the new best matching algorithm - which could be slower with large tables. To cope against this, an alias - column was added (the upgrade-0.0.21.sql script will also add it). This - means that you will have to add the new domains after upgrading, and alter - the patterns to no longer hold the domain. -* Real asynchroneous functions support - - The Pth library has limits in that only one process is used for all - threads. The various pth_*() functions, and special care has to be taken - to prevent locking the whole process when a single thread needs to - perform operations, because of the non-preemptive nature of Pth. - Another side effect of using Pth is that SMP systems gain no performance - over single CPU systems. Moreover, some functions which can take a while - can lock the whole process when no pth_*() wrapper function exists to - do it. An obvious example is hostname resolving, which can lock the whole - process for long periods on slow networks. - - A solution was worked out to allow threads to execute real asynchroneous - functions without blocking the main process (and therefore allowing other - threads to remain responsive), and to even take advantage of - multi-processor systems where available. - - The technique uses an AmigaOS-like device task/thread which works over the - Pth thread-safe message passing mechanism, to serve the various - asynchroneous functions like a daemon would. This device internally uses - a pool of real asynchroneous processes to which it dispatches the requests - in a distributed manner via unix datagram sockets. - - The new ASYNC_PROCESSES configuration file option was added as a result, - which allows to specify the number of slave asynchroneous processes to run. - - This facility is currently used by mmmail to generate password hashes and - to resolve hostnames and MX records (when enabled). -* Miscelaneous new features - - If the daemons are compiled with -DNODETACH, the main process will not - fork(). Useful for debugging. - - If compiled with -DNODROPPRIVS, daemons will make sure they are not - started by the superuser to accept running, but will then not attempt - to perform any modifications on the current permissions. Useful for - non-privileged users. - - mmstatd can now be specified configuration file to use at startup - via command-line argument. - - mmstat library will now first check for MMSTATCONF environment variable - for the configuration file to use instead of the default - "/etc/mmstatd.conf". This can be useful to non-privileged users who want - to run mmstatd. - The default is to only accept to be started by the superuser and then - drop privileges. - - mmstat user client utility now supports hreport for more human readable - results (although less verbose). - - Configuration file option DELAY_ON_ERROR was added to allow to pause - at every user command error. Thanks to Jeroen Oostendorp for the idea. - - mmstat facility now allows wildcard pattern matching for UPDATE, RESET - and DELETE operations which operates atomically on all matching keys - for that uid. - - STATFAIL_EOF was added to mmsmtpd.conf which allows to maintain statistics - via the mmstat service about clients which disconnect improperly. -* Other - - Optimized the command matching loop by using fast packed hashes - - Other optimizations were performed by moving some variables in - closer scope, usually resulting in compilers using registers for - appropriate variables. This is especially useful for GCC which - ignores register directives. - - Significant code cleanups - - Several environment variables can now be set to modify the behavior - of the install.sh script (eg: change installation prefix, default - group/user, etc). - - The memory pool allocation system was optimized even more, now keeping - statistics on the average number of pages in use, so that it scales - better. Previously used pages are now privileged as well, which causes - unix kernels to allow processes using it to perform more efficiently, - as well as the underlaying allocator. - - Better synchronization with mmstat service, now using persistant keys - and has possibility to delete old temporary keys at startup using - wildcard matching. - - - -Release: mmmail 0.0.20 devl -Date : November 8, 2002 -By : Matthew Mondor - -* New features - - The new STATFAIL_ADDRESS, STATFAIL_FLOOD, STATFAIL_FULL and - STATFAIL_TIMEOUT boolean confguration file options were added, which - allow the administrator to view statistics using the mmstat(8) utility, - optionally taking action on abnormal functional issues. - See mmsmtpd.conf(5) man page for more information. - - Added new CHROOT_DIR configuration parameter to optionally allow the - server to run enclosed into a chroot(2) jail. - - When starting the server it is now possible to specify the configuration - file to read. This allows to start several copies using different - configurations. -* Bug fixes - - When multiple groups feature was introduced in 0.0.16 devl, mmmail stopped - setting the initial group using setgid(2), assuming that setgroups(2) - would. This was not the case, and mmmail would then remain part of the - wheel group. It will now set the real and effective primary group, the - secondary groups, and then the real and effective user. The first group - of the GROUPS configuration file parameter is used for the primary group. - Although mmmail does not access files, this bug did not hurt it, but - let's not leave anything out considering security. - - The pop3d needed to replace any "\n.\n" to "\n..\n" while sending out - to of course avoid the client to consider the message completed when it - is not the case. This is now done. As a result it cannot send the - whole message using a single fdbrwrite() call, it has to perform several - ones as needed. - - Removed the annoying LOOP: debugging which used to flood syslog. -* Other - - mmsmtpd now has less overhead when creating new client contexts - - Now uses getnameinfo(3) instead of gethostbyaddr(3) to resolve hostnames - if RESOLVE_HOSTS is TRUE. This is more suitable with threads, since - gethostbyaddr(3) uses static data and required a mutex to be thread-safe. - - - -Release: mmmail 0.0.19 devl -Date : October 28, 2002 -By : Matthew Mondor - -* Important bug fix - - omission of an unlinknode() in the new rate limit feature was fixed. - The daemons could lock in a loop taking 100% CPU time. Fixed. - - - -Release: mmmail 0.0.18 devl -Date : October 26, 2002 -By : Matthew Mondor - -* New features - - Anti-DoS connection rate limit feature was added in mmserver library, - consequently new CONNECTION_RATE and CONNECTION_PERIOD configuration - file options were added. These are on a per-IP address basis. - - The bytes statistic counters were bumped to 64-bit. -* Bug fixes - - Yet again a build/install script bug; Permissions would not be applied - properly to newly installed files, the problem started to occur when - automatic directory creation was added. This was now fixed. Users should - consider upgrading as fast as possible, or to make sure that their - configuration files in /etc/ have safe permissions. - See the following man pages: mmsmtpd.conf(5), mmpop3d.conf(5). - The install script will force safe permissions on the configuration files - while preserving them if they already exist. - - The staff group would not be created by install.sh if it did not exist. - Thanks to Jeroen Oostendorp for reporting this. - - The bandwidth shaping system was not accurate enough, as it used to - throttle by 4096 bytes blocks, requireing too much CPU time in syscalls - for high limits. It was rewritten using a new design which now only sleeps - if required after the maximum number of bytes allowed into a second were - transfered. So it now uses a second's worth of data resolution rather than - a 4096 bytes resolution. Some parts of mmfd library internals and API - were modified as a result (See mmfd(3) man page for details). Another - side effect is that it takes far less CPU time with high limits. - Thanks to Darren Price for the extensive testing and reports, - to Eric Weisenhaus and agrajag for reporting these earlier. - - There existed a potential race condition when hostname resolving was - enabled, which could cause a memory leak. This was fixed. - Thanks to Jeroen Oostendorp for reporting it. -* Other - - When mmmail needs to write out blocks of data it no longer uses mmfd(3) - library fdbwrite() buffering call but fdbrwrite() instead to save CPU - cycles and avoid buffer size splits. - - Some source code auditing and cleaning was made, several optimizations - were performed, some buffers were aligned to favorize word-copy/move - operations as opposed to single-byte moves, in mmstring library). - - I thank everyone who are testing this software, and am again requesting - that it be evaluated, as I intend to eventually release mmmail v1 stable. - Support for more features will mostly be added into future mmmail devl - software, for an eventual mmmail v2. The current design drafts for it - can be found at http://mmondor.gobot.ca/software.html under the mmmail - TODO entry. For mmmail v1 release to happen, it has to be free of any - issues, it is therefore important to report any bug, to mmondor@gobot.ca. - It also would be nice if I could release with it a list of systems it is - known to work on, so if betatesters find some time to describe their setup - to me via email it would be appreciated. I yet have to test it again on - ultrasparc with Linux and Solaris for instance. - - - -Release: mmmail 0.0.17 devl -Date : October 11, 2002 -By : Matthew Mondor - -* Bug fixes - - The build scripts did not behave as expected on systems which are using - bash for /bin/sh. Some script sections needed modifications to work - on both. Now seems to build fine on linux systems. - - Various new GCC versions warning issues were fixed. - - When installing, some directories were assumed to exist, they will - now be created when missing. - - A bug in mm_memmov() was fixed. - - Some systems, including linux, required libresolv to be linked with - the daemons. Fixed. - - - -Release: mmmail 0.0.16 devl -Date : October 6, 2002 -By : Matthew Mondor - -* New features - - Now uses mmstatd(8) for persistant statistics storage and volatile who - database. This includes mmstat(8) administration facility. - This daemon also comports crash recovery using internal logging. - See mmstatd(8) man page for more information. - - Processes may now be part of several groups. - - It is now possible to set global maximum download and upload limits for - the whole server. - - Improved installation/upgrade make.sh and install.sh scripts, and use - of /bin/sh rather than make because of GNU/BSD make inconsistencies. - - mmmail(8), mmsmtpd(8), mmpop3d(8), mmsmtpd.conf(5), mmpop3d.conf(5), - mmpasswd(8), mmstatd(8), mmstat(8), mmstat(3), mmstatd.conf(5) - man pages were written. -* Bug Fixes - - Hostname sections starting with alpha characters only used to be accepted, - it now will accept alpha-numeric instead for the starting hostname section - character. - - The "Received:" headers were wrong about the "for" part. The previous - way do_data() worked prevented modifying the message header for each - RCPT, which was required. do_data() was now rewritten around mmfd(3) - fdbreadbuf() and "for" should be adequate as it's a re-implementation. - Thanks to Jeroen Oostendorp for reminding me of this bug. - - On various systems, including OpenBSD, the NetBSD-style __RCSID and - __COPYRIGHT, although useful, caused problems. They were now replaced - by custom macros, which should fix compiling issues. - Thanks to Dinos Costanti for reporting the issue. -* Other - - The code used to be compatible with older C compilers, it now was converted - to ANSI C. Use of __P() macro is no longer made. - - mmsmtpd now reads in messages faster than it used to, do_data() was - re-written in a way to avoid two previous memory copying passes on the - whole message. It now uses mmfd(3)'s new fdbreadbuf() function. Previously - it would call fdbgets() on a buffer, copy the string into a linked list, - using strdup(3), and after message reception another buffer would be - created by copying all lines from the list sequencially again. - These two passes can now be avoided, as fdbreadbuf() causes fdbgets() to - write it's lines directly into a single buffer. Of course we still can't - avoid the buffer copy/translation pass which occurs to produce the mysql - query, however. - - Some mmlist node manipulation functions were replaced by macros. - mmlist(3) now privileges pre-allocated nodes when inserting them back - in the free nodes queue, making a better useage of memory. - - - -Release: mmmail 0.0.15 devl -Date : July 27, 2002 -By : Matthew Mondor - -* Bug fixes - - Fixed bandwidth shaping library, recent versions would totally ignore - bandwidth rate specified, because of erroneous 0 initialization. - Also fixed a bug where bandwidth would always be lower than requested. - Thanks to Eric Weisenhaus, Darren Price and agrajag for reporting these. - - Configuration option LOG_LEVEL would not accept values below 1 - - Fixed mm_strnicmp() which would sometimes cause various problems - - Sometimes fdbgets() status would not be logged in case of error, - occuring during mail receipt, not disclosing why 520 Error was sent. - - The 520 Error sent when an error occurs receiving mail data was changed - to a 452 code error. - - Added LOG_LEVEL 4 for extreme verbosity (message DATA lines are logged). - - - -Release: mmmail 0.0.14 devl -Date : June 4, 2002 -By : Matthew Mondor - -* Bug fixes - - The FROM:<> string was compared case-sensitively (thanks to Jeroen - Oostendorp) - - Various optimizations - - A pretty serious bug was fixed (which only affected glibc-based systems), - where syslog() would potentially be called with user supplied parts, - including fmt sequences. Thanks to Benoît Roussel for providing me with - a very detailed security advisory and to Guillaume Pelat for discovering - the problem. - - - -Release: mmmail 0.0.13 devl -Date : May 19, 2002 -By : Matthew Mondor - -* Config file support - - Servers now reads /etc/mmsmtpd.conf and /etc/mmpop3d.conf which obsolete - previous mmversion.h pre-compilation issues. This also permits to leave - the daemon executables to be readable by other users, as passwords get - read from the config files. Of course configuration files should remain - only accessible by root. - - Defaults are set, configuration file is parsed, then command line - arguments parsed which may override some of the configuration file - settings. - - Syslog facility can now be specified. -* Bug fixes - - When the mysql server was down, and mmsql lib's mmsql_glock() was called - by the daemons, 100% CPU time would be used in an endless loop. The loop - boundaries and rate are now controlled properly. - - When a mail box is full, mmsmtpd now issues a 402 message instead of the - previous 502 code, which would prevent some MTAs from retrying later on. - - When too many DATA lines were sent through mmsmtpd for a message, it - would previously not output the proper error message properly, instead - two messages were being sent, the error one, and the successful one, - confusing most smtp clients. - - Thanks to Jeroen Oostendorp for beta testing throughly mmmail 0.0.12 - and noticing the three above bugs which were fixed in 0.0.13. He also - made the MAIL FROM:<> and hops checking support suggestions. - - The process ID file was not created. - - Detected and fixed misuse of snprintf(), of which the returned value - should not be trusted. -* Support for MAIL FROM:<> added - - These used to be considered illegal, it now is possible for the admin - to allow them (crond and some intranet MTAs seem to use it). - - Only clients connecting from allowed list of hosts/addresses can use it, - the new nofrom MySQL table has been added for this purpose. - - * and ? pattern matching is allowed, and addresses or hosts can be used. -* Implemented maximum hops checking support - - New configuration file option can be set to limit the number of maximum - hops in a message in order to accept it. This helps filtering invalid - emails and spam. -* Install/Upgrade shortcut - - A make.sh script was now written which allows to compile and then install - or upgrade mmmail faster. - - Written scripts/upgrade.sql to allow easy transition from mmmail 0.0.12 - or older to mmmail 0.0.13, without deleting the current database info, - since new tables and fields were introduced. -* Useful debug logging - - When using level 3 verbosity logging, replies will also be logged via - syslog to specified facility. This is especially useful for beta-testers - and debugging. -* (finally) Implemented aliasing - - Thanks to Ryan Werber, Daniel DeMaggio and Jeroen Oostendorp for - suggestions and ideas. - - Allows to setup aliases based on * and ? pattern matching, the new - MySQL table alias has been created for this purpose. -* Implemented message flood (mail bombing) protection - - Thanks to Daniel DeMaggio and Jeroen Oostendorp for the idea. - - FLOOD_PROTECTION boolean, FLOOD_CACHE size, FLOOD_MESSAGES and - FLOOD_EXPIRES configuration file options added. - - This is based on a per client host basis, addresses/hostnames are - cached with statistics in a hash table. -* Added various fields along the SQL tables - - Allows automatic scripts to identify/inactivate/delete unused boxes - or users using new datetime fields. - - Statistics on the number of logins for a user are now stored. - - Added an admin field to the user box, so that it becomes possible for - a frontend to allow an administrator to create users and mailboxes - for them. This however in the future should be restructured to be on - a per-host basis instead. Thanks to Christoph Dworzak and Daniel DeMaggio - for providing the idea. - - Added a user active boolean field to the user SQL table to permit easily - disabling access temporarily to a user without having to take out his - mail boxes or user entry from the database. -* Introduced a 64-bit hash function - - Internally used to improve various table lookups (already supplied - RCPTs and the FLOOD_PROTECTION cache for instance) - - Hash function was tested against duplicates with /usr/share/dict/words - and find / inputs with success -* Server too busy messages introduced - - When the maximum number of IP addresses have been reached, or when too - many connections from the same address occurs, a protocol-friendly message - is now sent before closing the connections. Some clients have been - confused with previous behavior. -* Multiple interfaces support - - Adminstrator may now specify several IP addresses to bind() to, as well - as a server hostname for each, using LISTEN_IPS and SERVER_NAMES config - file parameters. -* Now using getpwnam() and getgrnam() for more configurability - - Used to read /etc/passwd and /etc/group directly. - - - -Release: mmmail 0.0.12 devl -Date : April 22, 2002 -By : Matthew Mondor - -* Fixed a nasty bug - - As the mmfd library was lately fully redesigned some daemon code would - not properly use the status results when a connection was unexpectedly - lost. - - This rendered easy to crash the daemon flooding with alot of connections - not exiting using standard QUIT command. -* Better disconnection logging - - The the general status/reason of the disconnection is now also logged - with the statistics via syslog. -* Compilation issues resolved - - Fixed a conflict which occurred between the BSD and linux usleep() - function not using the same argument type (unsigned long vs useconds_t) - - mmpasswd would not compile properly on linux - - - -Release: mmmail 0.0.11 devl -Date : April 17, 2002 -By : Matthew Mondor - -* Implemented bandwidth management support - - mmfd library finally supports bandwidth shaping as well as buffering, - mmmail daemons now also take adventage of this, administrator may - optionally decide the speeds limits in KB/s for both reading and writing - data from with each client. -* Implemented transfer statistics - - Detailed statistics on the number of bytes transfered in both directions, - with number of RCPT/messages received/sent and DELE commands are now - logged via syslog at logout. -* More descriptive logging on DoS attempts - - When a connection is rejected because the maximum number of IP addresses - is reached, or that maximum number of connections for an IP address - is exceeded, mmserver library now reports the offending IP address and - reason via syslog. - - - -Release: mmmail 0.0.10 devl -Date : March 24, 2002 -By : Matthew Mondor - -* Moved to CVS with unified libraries - - Less prone to some bugs caused by inconsistencies among code modules -* More efficient memory management - - mmlist's buffering capabilities now used by mmmail suite daemons -* More efficient I/O - - New mmfd library provides fast buffering around fds increasing execution - speed considerably, mmsmtpd used to read in messages slowly especially -* Bugfixes - - Some SMTP clients sending mixed-case hostnames at HELO would be refused - access, fixed. - - Under some circumstances it would theoretically be possible to crash - mmpop3d (a loop could possibly read outside of it's heap) although - I could not observe the effects, auditing the code after CVS transition - made me discover it. - - Some memory would not be freed immediately after mmsmtpd processed a - message. - - French accents and the like would be stripped out of messages by mmsmtpd, - fixed (mmfd.c). - - Will no longer refuse to start if lingering sockets with TIME_WAIT exist - - - -Release: mmmail 0.0.8a devl -Date : January 14, 2002 -By : Matthew Mondor - -* Backported - - This release consist of an update to mmmail 0.0.8, with only part of - the changes of 0.0.9 (mmsmtpd on 0.0.9 seems to segfault, and I couldn't - find time to debug it, and needed to update my production servers to work - as soon as possible). - - The changes fixes the wrong connection IDs in logging, and the fdgets() - modification was applied as well as the LIST Out of range problem patch. - - - -Release: mmmail 0.0.9 devl -Date : January 6, 2002 -By : Matthew Mondor - -* Fixed wrong connection IDs - - If more than one connection at once were originating from the same IP - address, the connection ID would remain the same. Fixed. This is essencial - to properly associates commands executed by each connection together. -* Modified mmfd.c's fdgets() function - - The line input function would cause telnet control sequences to be - inserted in the command string, as non-printable characters. They are now - ignored. This would cause some commands issued by rare clients to not be - recognized. -* Debugged LIST - - The LIST POP3 command when called with a message number parameter would - often fail with an out of range error. Fixed. -* Performance enhancements - - mmmail now uses the new mmlist and mmstr libraries just like mmftpd for - internal buffering preventing malloc() and free() from being called too - often. Moreover, general performance of linked lists related code should - be enhanced. - - The administrator can set the new BUFFERS variable in the mmversion.h - files to higher values for servers under more load for more buffering. - - - -Release: mmmail 0.0.8 devl -Date : November 26, 2001 -By : Matthew Mondor - -* Fixed some of the error limits code, thanks to Ryan Werber for reporting - - Some commands such as RECV used to increase the errors number variable - which caused a POP3 client to fail finishing fetching all messages from - a box with many messages. Fixed. - - - -Release: mmmail 0.0.7 devl -Date : November 24, 2001 -By : Matthew Mondor - -* Fixed broken TOP POP3 command, thanks to Tyler Mitchell for reporting it - - When rewriting many parts of the POP3 daemon for mmmail 0.0.6 version, - a bug was introduced which caused TOP command to always return invalid - range errors. Fixed. - - Moreover, TOP since the first mmmail version never worked as expected by - most users, where TOP 0 would display the message header lines, and - any higher number would be lines counted after that header. Fixed. - - As an anti-dos feature using TOP on the same message twice was not - permitted unless RSET was used. Now behaves normally. - - As POP3 protocol does not provide a help system, TOP command now mentions - that PAGE could also be used on it's result line. - - - -Release: mmmail 0.0.6 devl -Date : November 21, 2001 -By : Matthew Mondor - -* mmmail and mmmail-pth were fusionned - - As alot of work as been performed on the pth daemon lately, and a single - edition is preferable to maintain, mmmail officially uses pth library - as of this edition. I mostly run it on netbsd personally and the pth - editions run awesome on it. I also run a linux server with it in a - production environment, so I consider it stable and efficient enough. -* Not using regex(7) anymore - - In order to make the command interpreter much faster, unvulnerable to - possible regex related bugs, and to make it more portable (yes, I have - noticed that besides POSIX pthreads, POSIX regex have problems on some - systems and required different regex pattern strings), I am now parsing - the commands myself. SMTP and POP3 commands are very simple to parse. - Moreover, jump tables are now used instead of switch/case. This permits - general better performance when using libpth especially, since it only - provides non-preemptive scheduling, among all threads, using a single - process's CPU time. -* UID and GID now internally evaluated from names - - The UID and GID to run as are now specified to the daemons as names as - found in /etc/passwd and /etc/group. The daemons now perform necessary - convertions to numerical values. -* Better permissions sanity checking when run from inetd - - Will now refuse to run if it can't make sure it runs as required user - and group -* HELO may optionally be required - - Will prevent the use of MAIL before HELO was used, if the admin wants -* Less strict on MAIL and RCPT format - - Some SMTP clients and servers used to not fully respect the strict RFC - MAIL FROM: and RCPT TO: forms, adding spaces - or omitting < >. mmsmtpd will now work with those. -* Customizable logging level - - The admin may now setup wanted logging level for each daemon. -* mmtcpfwd-like connection ID logging - - When connection/disconnection logging is wanted, now uses connection IDs - which permit to effectively link the events together. -* Implemented unstandard POP3 PAGE command - - As TOP often is not powerful enough to properly allow reading messages - via a telnet client for instance, PAGE was - implemented, where consists of the message to be read, of - the page number to be displayed, and the number of lines per page - of the terminal (with a maximum limit of 60). -* README was updated - - As many changes were performed in both daemons, although I personally - tested them a bit, next release should be a stable one, once this one - has been more tested on production systems. - - - -Release: mmmail pth-0.0.5 devl -Date : October 2, 2001 -By : Matthew Mondor - -* mmsql_glock() and mmsql_gunlock() used to be implemented around flock() - - This required mmsmtpd/mmpop3d daemons to share a common filesystem - as /tmp/mmail-mmsql.lock file was used. - - Now uses MySQL's special GET_LOCK() and RELEASE_LOCK() functions. - - The daemons should now be able to run on two different hosts -* Fixed a small memory leak in mmpop3d - - mhash allocates data for the hash, which has to be freed although - it's documentation never mentionned it, when I noticed the leak and - re-read the auth code I found logic that mhash_end() couldn't free - the hash which we need to use afterwards, adding a free() fixed it. -* Note: The pth library version with which mmmail-pth was thoughly tested - consists of libpth14 (v1.4). I noticed that on linux, pth seems tricky. - These daemons run fine on NetBSD with libpth12-14, although it only seemed - to work fine on linux with libpth14. It used to segfault with libpth13 on - linux. - - - -Release: mmmail pth-0.0.4 devl -Date : September 28, 2001 -By : Matthew Mondor - -* Fixed a bug which occured at times around strncpy() which implementation - varies from clib to clib. - - Unexpected mail box full issues were fixed - - - -Release: mmmail pth-0.0.3 devl -Date : Aug 12, 2001 -By : Matthew Mondor - -* Portability issues - - As using POSIX threads cause portability issues among implementations, - this one was fixed to use the GNU portable pth thread library. - It is not really made to fully replace the other implementation using - POSIX threads, I also may merge them together eventually but are only - two concurrent versions at present time. - - - -Release: mmmail 0.0.3 devl -Date : July 9, 2001 -By : Matthew Mondor - -* Data integrity enhancement - - mmsql_glock() and mmsql_gunlock() implemented in between mmsmtpd - and mmpop3d to prevent mailbox corruption which could happen if - they were both writing and deleting messages at once. This never - happened but as two SQL commands are required for mail insertion - or deletion, it is better to make sure that nothing can interfere - between them. -* Revision - - Removed all // comments and replaced them by /* */ ones to follow - standard C conventions - - Updated the README file -* Security enhancement - - The daemons no longer return to the client the strings they send - when they are unrecognized or invalid commands. - - - -Release: mmmail 0.0.2 devl -Date : June 3, 2001 -By : Matthew Mondor - -* Bugfix - - Now uses mysql_ping() regularly to re-establish connection - with the MySQL server if it was lost, very useful for remote - server connections though TCP/IP. - - - -Release: mmmail 0.0.1 devl -Date : May 29, 2001 -By : Matthew Mondor - -* Initial development release - diff --git a/mmsoftware/mmmail/GNUmakefile b/mmsoftware/mmmail/GNUmakefile deleted file mode 100644 index 9ad6850..0000000 --- a/mmsoftware/mmmail/GNUmakefile +++ /dev/null @@ -1,50 +0,0 @@ -# $Id: GNUmakefile,v 1.7 2005/01/23 14:47:34 mmondor Exp $ - -MMLIBS := $(addprefix ../mmlib/,mmfd.o mmhash.o mmlimitrate.o mmsql.o \ -mmlog.o mmpool.o mmreadcfg.o mmserver.o mm_pth_pool.o mmstat.o mmstr.o \ -mmstring.o) - -PTH_CFLAGS := $(shell pth-config --cflags) -PTH_LDFLAGS := $(shell pth-config --ldflags --libs) -MYSQL_CFLAGS := $(shell mysql_config --cflags) -MYSQL_LDFLAGS := $(shell mysql_config --libs) - -CFLAGS += $(PTH_CFLAGS) $(MYSQL_CFLAGS) -I. -I../mmlib -LDFLAGS += $(PTH_LDFLAGS) $(MYSQL_LDFLAGS) -lc -lcrypt - -OBJS := src/mmsmtpd/mmsmtpd.o src/mmpop3d/mmpop3d.o src/mmrelayd/mmrelayd.o -BINS := src/mmsmtpd/mmsmtpd src/mmpop3d/mmpop3d src/mmrelayd/mmrelayd - -CFLAGS += -Wall -#CFLAGS += -DMMMAIL_MYSQL -CFLAGS += -DMMMAIL_FILE - - -all: $(BINS) - -# To compile all C files to objects -%.o: %.c - cc -c ${CFLAGS} -o $@ $< - -# To link all executables (binaries) -$(BINS): $(MMLIBS) $(OBJS) - cc -o $@ ${LDFLAGS} ${MMLIBS} $@.o - - -install: - install -cs -o 0 -g 0 -m 500 src/mmsmtpd/mmsmtpd /usr/local/sbin - install -cs -o 0 -g 0 -m 500 src/mmpop3d/mmpop3d /usr/local/sbin - install -cs -o 0 -g 0 -m 500 src/mmrelayd/mmrelayd /usr/local/sbin - install -c -o 0 -g 0 -m 444 src/mmmail.8 /usr/local/man/man8 - install -c -o 0 -g 0 -m 444 src/mmsmtpd/mmsmtpd.8 /usr/local/man/man8 - install -c -o 0 -g 0 -m 444 src/mmpop3d/mmpop3d.8 /usr/local/man/man8 - install -c -o 0 -g 0 -m 444 src/mmrelayd/mmrelayd.8 /usr/local/man/man8 - install -c -o 0 -g 0 -m 444 src/mmsmtpd/mmsmtpd.conf.5 \ - /usr/local/man/man5 - install -c -o 0 -g 0 -m 444 src/mmpop3d/mmpop3d.conf.5 \ - /usr/local/man/man5 - install -c -o 0 -g 0 -m 444 src/mmrelayd/mmrelayd.conf.5 \ - /usr/local/man/man5 - -clean: - rm -f $(BINS) $(OBJS) $(MMLIBS) diff --git a/mmsoftware/mmmail/README b/mmsoftware/mmmail/README deleted file mode 100644 index f208b7b..0000000 --- a/mmsoftware/mmmail/README +++ /dev/null @@ -1,322 +0,0 @@ -$Id: README,v 1.5 2003/06/19 15:03:29 mmondor Exp $ - -MMMAIL(8) NetBSD System Manager's Manual MMMAIL(8) - -NAME - mmmail - SMTP and POP3 suite of daemons - -SYNOPSIS - mmsmtpd [config_file] - mmpop3d [config_file] - -DESCRIPTION - mmmail consists of a suite of SMTP and POP3 daemons, mmsmtpd(8) and - mmpop3d(8) which support bandwidth shaping and a MySQL backend for mes- - sage storage. These servers run as an unprivileged user and should - therefore be more secure than most MTAs. Additionally, some effort was - put in providing various protection methods against Denial of Service at- - tacks, and mail bombing. It is also possible to run the daemons into a - chroot(2) jail. - - Currently, relaying is not supported by mmmail but work is in progress to - eventually have mmfilterd and mmrelayd written as part of mmmail. There - are no official HTTP administration frontends for mmmail yet neither, al- - though there exist third party perl and PHP scripts for apache which can - be used, there is no guarentee whatsoever on their security. Eventually - a small secure ssl-httpd will be written which should serve remote admin- - istration needs for the various mm daemons. Some work is also in progress - to redesign some parts of the system so that various message storage and - authentication methods could be chosen from (MySQL, pgsql, flatfile, db4, - etc), and to allow to delegate administration of particular hosts to - their administrator, who could manage the users and mailboxes for that - host. There also have been some notes written about a possible eventual - mailing list manager system. - - The current design allows to securely receive mail through SMTP for many - users on several hosts, and serve back that mail securely via POP3. It is - efficient at doing so. It supports multiple mailboxes per user, aliasing, - and virtual hosts. Good statistics are kept using the mmstat(3) interface - via mmstatd(8) and the SMTP and POP3 daemons may run on different servers - via a persistant TCP link to the MySQL server. There also can be several - of each on various servers using replicated MySQL servers. - - Each mail box can have different limits on the maximum number of messages - they may hold, as well as the maximum size their mailbox may grow to, in - bytes. - - mmsmtpd and mmpop3d establish one main connection with the MySQL server - each, through which it processes all queries. This normally would be an - advantage to keep the file descriptors amount smaller, and for remote - connections using TCP/IP to the mysql server, which should cause a speed - improvement. It also will re-establish that connection whenever it is - lost, for whatever reason, which permits to run the SQL server anywhere - on the internet (although running the server on a LAN is recommended, un- - less under kerberos, ssh or ipsec tunneling). - - Special note: As the suite supports vitualhosts, and that some mail - clients cannot handle '@' in usernames, it by convention replaces the - first '.' in the username by '@' internally, to match it with a mailbox. - This prevents using '.' in username part of addresses, _ will be used - without problem though: eg: for the mailbox mmondor@myhost.net, the cor- - responding username would be mmondor.myhost.net. The password will remain - the one of the user who owns that mail box. - -INSTALLATION - This document assumes that you know how to manage mysql tables using the - mysql client, as you will need to create a new mysql user (typically - called mmmail), and a new database for it. Only mmmail user should have - access to the mmmail database and only root and the dedicated user under - which mysqld runs should be able to access the database files (typically - /home/databases/mysql/ directory). These are common security administra- - tion procedures that mysqld users should know about. The mmmail suite is - highly dependant on the mysql database server. This also means that you - are responsible for database integrity, that you must provide it using - efficient backup systems, UPS, etc, whatever required for general data - safety using MySQL. I also assume that you know how to manage unix file - permissions, groups, and how to make sure that mysqld does not run as - root, that only mysql user can access the mysql data directory, that you - also know how to only allow wanted hosts and users to be able to connect - to mysqld, etc. Another important thing to note is that as mmmail source - tarball does not use autoconf, you may need to edit the Makefile files, - if some libraries or include headerfiles cannot be found using the de- - fault ones. (LIBDIR and INCDIR variables may need to be modified espe- - cially). - - mmmail requires mmpasswd(8) and mmstatd(8) which are provided with it. - Normally, executing the make.sh script should compile the distribution, - and install.sh should install or upgrade all of the necessary components. - Note that there are several configurable options which can be set prior - to calling install.sh script; Use the './install.sh help' command to see - a description of the environment variables which you may want to set. - The mmlib/makedefs.sh file contains defaults for building commands, li- - brary and include paths, and may be modified if compilation issues occur. - The building process is no longer dependant on BSD or GNU make, only - /bin/sh is required. You may want to look at the mmstatd(8) and - mmstatd.conf(5) man pages on how to configure mmstatd. You first should - make sure that you have the following libraries: - - libpth version 1.4.1 or later (GNU Pth) - libmysqlclient (usually part of mysql-client package) - - Upgrading from 0.0.12 or older to 0.0.13 or later: Note that if you pre- - viously were using mmmail 0.0.12 or older, and are upgrading to mmmail - 0.0.13 or later, two new MySQL tables were introduced for aliasing and - FROM:<> support (see scripts/tables.sql for more information). If you - wish to ease the transition you should execute the - scripts/upgrade-0.0.13.sql MySQL script, so that you don't have to re- - create the tables. This should also preserve your current data. - - Upgrading from 0.0.20 or older to 0.0.21 or later: If you are upgrading - from mmmail 0.0.20 to 0.0.21 or later, an important change has been per- - formed on the way password hashes are stored. Main reasons for this are - that frontend scripts would not easily be able to generate these, and - that they were not compatible with system ones, making migration process - from real users to virtual ones harder. The size of the user_passwd col- - umn of the user MySQL table in the mmmail database will need to be modi- - fied. Moreover, the password hashes will need to be changed for all - users. Unfortunately, it was impossible to provide a utility to convert - old password hashes to new ones because of a bug in the previous base64 - conversion which would loose some of the last bytes of the MD5 hash. - Please see mmpasswd(8) man page for more details on the password hash - format. To perform the transition you should execute the - scripts/upgrade-0.0.21.sql MySQL script. Your current data will be pre- - served. - - To install, first extract the source code tree - - # tar xzvf mmmail-0.0.21.tgz - # cd mmmail-0.0.21/ - - A script has been provided which permits to build and install/upgrade all - binaries, preserving old file permissions and old configuration files in - /etc/ if those previously existed. Those will install the binaries into - /usr/local/sbin/ and the configuration files to /etc/. Moreover, if the - daemons are already running, it makes sure to kill them before writing - over the files again, and to restart them afterwards. It is very useful - to quickly upgrade mmmail. To execute it, simply execute make.sh : - - # ./make.sh - # ./install.sh - - The man pages and required binaries should now have been installed in - /usr/local, but any old existing configuration files in /etc/ will be - preserved in the case of an upgrade. It therefore generally consists of a - good idea to consult the configuration files man pages after upgrading, - in this case mmsmtpd.conf(5) and mmpop3d.conf(5). - - Let's now configure mmmail - - As this does not consist of a MySQL manual, I will restrict the examples - to the minimum, as if we were to run the mysql server on the same machine - the mail daemons are running on (under which case connections are made - using UNIX domain sockets rather than TCP/IP also). The MySQL user name - is mmmail, it's password is set to mmmailpassword in this example, and - the mmmail database named mmmail. We then make sure that only MySQL root - and mmmail users can access the mmmail database (using mysql grant per- - mission tables, this does not solve the other issues about database secu- - rity which were explaned above). - - There also is a supplied password program which can be used to generate - the hash that mmmail requires for a user password, it has to be case-sen- - sitive in the user table. It consists of mmpasswd (See the mmpasswd(8) - man page for details). There currently is no software provided to manage - users and mailboxes, so the administrator has to edit the tables manually - using a MySQL client. - - An important thing to additionally remember consists of the MySQL - max_allowed_packet variable which should be set high enough for the - /etc/mmsmtpd.conf MAX_DATA_SIZE configuration option. A common way to - perform this is passing the command line argument -O - max_allowed_packet=5M for instance when starting the MySQL server daemon. - - Let's create the mmmail database, mmmail user, and permit the user to ac- - cess it's database, with proper permissions for mmmail suite: - - $ mysql -u root -p - password: *********** - > create database mmmail; - > use mysql; - > insert into user (Host,User,Password) - values('localhost','mmmail',password('mmmailpassword')); - > insert into db values('localhost','mmmail','mmmail', - 'Y','Y','Y','Y','N','N','N','N','Y','N'); - > flush privileges; - - Now we need to create the mmmail tables from the .sql file. The reason we - are doing this as the mysql root user is that mmmail user does not have - rights to alter the mmmail database, for obvious security reasons. - - $ mysql -u root -p mmmail use mmmail; - > insert into user (user_name,user_passwd,user_created) - values('some user','$1$UrxLMU$f5Hb/pVGURl7sZ.4sEiYD1',NOW()); - > insert into box values('mmondor@myhost.net',1,5242880, - 0,500,0,NOW(),NOW(),NOW()); - > insert into box values('mm@anotherhost.net',1,5242880, - 0,500,0,NOW(),NOW(),NOW()); - - This sets for both boxes the following limits: 5242880 maximum size in - bytes for the whole mailbox contents, and 500 messages maximum capacity. - - Now let's say we want to have all mail sent to the domain redirected to - that mailbox we created, we will create an alias for it. Pattern matching - is allowed in the alias_pattern field, using '*' and '?'. - - > insert into alias values('*','myhost.net' - 'mmondor@myhost.net',1,NOW()); - - This will redirect any mail addressed to anyone @myhost.net to mmon- - dor@myhost.net mailbox. The way aliasing works is as follows: The desti- - nation address (RCPT) is first looked up. If it exists it is immediately - chosen. If it doesn't, the alias table is scanned for the closest match- - ing pattern. If a pattern matches, the supplied address is internally - substituted by the address set for the alias pattern, and the new address - is again looked up. If either no aliases can map the address, or it - mapped it to an unexisting box, the address is rejected. This means that - if mmondor and lgodbout existed @myhost.net domain, they of course would - take precedence. An alias would be looked for afterwards only. - - Some systems use an empty MAIL FROM:<> address. If you need some hosts to - allow it, you can add patterns to accept into the nofrom table. If - RESOLVE_HOSTS is TRUE in /etc/mmsmtpd.conf, matches will be checked - against both hostnames and addresses in this table. As such, an entry as - "127.0.0.1" will still match for "localhost", although a "localhost" en- - try by itself would obviously only match if resolving worked for the - 127.0.0.1 address. Just like for aliases patterns, '*' and '?' pattern - matching is performed. - - > insert into nofrom values('127.0.0.1'); - > insert into nofrom values('*.gobot.ca'); - - This would allow clients connecting from localhost (weither resolving is - turned on or off) to use empty from address while posting mail. Moreover, - it would only allowed properly resolved gobot.ca hosts to also do so. - - Verify that the configuration files in /etc are right, then you should be - able to start the daemons, as the superuser (they will drop privileges): - - /usr/local/sbin/mmstatd - /usr/local/sbin/mmsmtpd - /usr/local/sbin/mmpop3d - - If any problem occur, carefully check the syslog logs in /var/log. - -SECURITY CONSIDERATIONS - The /etc/mmsmtpd.conf and /etc/mmpop3d.conf files should only be readable - by the superuser. The cause consists in the MySQL access password being - held in them. - - -rw------- 1 root wheel 2510 Sep 8 2002 /etc/mmpop3d.conf - -rw------- 1 root wheel 4504 Sep 8 2002 /etc/mmsmtpd.conf - - It also is important that the MySQL server runs as an unprivileged user, - and has decent root and mmmail users passwords set. It is a good idea - where possible to have it not listen on the TCP port for more security. - Additionally, The MySQL data directory should only be accessible by root - and the MySQL server. It is a good idea to restrict root and mmmail users - from localhost as well, in the mysql database, and to restrict the mmmail - database to the mmmail user, in the db database. Please read the MySQL - manual for more information on using it securely. Here are a few example - ideas: - - $ ps axwwwo user,command | grep mysql | grep -v grep - mysql /usr/pkg/libexec/mysqld --basedir=/usr/pkg --datadir=/home/mysql --user=mysql --pid-file=/home/mysql/gobot.pid -O max_allowed_packet=8M --skip-networking - - $ grep database /etc/group - database:*:1000: - - $ grep mysql /etc/passwd - mysql:*:1001:1000::/home/mysql:/sbin/nologin - - $ ls -ld /home/mysql - drwx------ 9 mysql database 512 Dec 31 1997 /home/mysql - - $ mysql -u root - ERROR 1045: Access denied for user: 'root@localhost' (Using password: NO) - $ mysql -u mmmail - ERROR 1045: Access denied for user: 'mmmail@localhost' (Using password: NO) - -FILES - /usr/local/sbin/mmstatd The mmstatd(8) daemon, required by mmmail - - /usr/local/sbin/mmsmtpd The SMTP server, see mmsmtpd(8) - - /usr/local/sbin/mmpop3d The POP3 server, see mmpop3d(8) - - /etc/mmsmtpd.conf The configuration file for mmsmtpd (See - mmsmtpd.conf(5) for details) - - /etc/mmpop3d.conf The configuration file for mmpop3d (See - mmpop3d.conf(5) for details) - -AUTHOR - mmsmtpd and mmpop3d were designed and written by Matthew Mondor and are - Copyright (c) 2001-2003, Matthew Mondor, All rights reserved. They were - primarily developped on NetBSD but were also mildly tested on Linux prior - to release. - -SEE ALSO - mmsmtpd(8), mmpop3d(8), mmsmtpd.conf(5), mmpop3d.conf(5), mmstatd(8), - mmpasswd(8), setgroups(2), setuid(2), chroot(2), mysqld(1), mysql(1), - mmfd(3). - -BUGS - mmmail does not support relaying, IMAP and other popular features. It is - currently under a totally new design for v2, and will most likely be - written from scratch to support alot of features, including mailing list - management, administration delegation for domains, multiple storage meth- - ods, etc. If you would like to see the current design drafts and offer - suggestions, they can be found at http://mmondor.gobot.ca/software.html - under the mmmail section. - - Please report any bug to mmondor@gobot.ca - -NetBSD 1.6_STABLE 22 December, 2002 5 diff --git a/mmsoftware/mmmail/clean.sh b/mmsoftware/mmmail/clean.sh deleted file mode 100755 index 9d8b4e0..0000000 --- a/mmsoftware/mmmail/clean.sh +++ /dev/null @@ -1,22 +0,0 @@ -#!/bin/sh -# $Id: clean.sh,v 1.1 2002/12/11 10:14:54 mmondor Exp $ - -. ../mmlib/makefuncs.sh - -cd ../mmlib/ -clean mmlib -cd ../ - -cd mmpasswd/ -clean mmpasswd -cd ../ - -cd mmstatd/src/ -clean mmstatd -cd ../../ - -cd mmmail/src/mmsmtpd/ -clean mmsmtpd -cd ../mmpop3d -clean mmpop3d -cd ../../../ diff --git a/mmsoftware/mmmail/etc/mmpop3d.conf b/mmsoftware/mmmail/etc/mmpop3d.conf deleted file mode 100644 index 2039090..0000000 --- a/mmsoftware/mmmail/etc/mmpop3d.conf +++ /dev/null @@ -1,115 +0,0 @@ -; $Id: mmpop3d.conf,v 1.4 2003/07/12 02:21:25 mmondor Exp $ -; -; mmpop3d mmmail component configuration file (/etc/mmpop3d.conf) -; and # are considered comments, and can occur at start or end of line. -; Read mmpop3d.conf(5) man page for details. - - -; Daemon administration options -; ----------------------------- -; -; Number of asynchroneous slave processes that should be launched and used -; to perform tasks such as hostname resolving and MD5 hashing. -ASYNC_PROCESSES 3 -; -; Optional location of directory we should chroot(2) to. Note that special -; configuration is requried for this, /etc/hosts and /etc/resolv.conf -; files will be required in the new root for instance. -;CHROOT_DIR "" -; -; Location of the path where to store our process ID -PID_PATH "/var/run/mmpop3d.pid" -; -; User mmpop3d should run as -USER "mmmail" -; Groups process should be part of -GROUPS mmmail,mmstat -; -; Increase this to higher values if high server load is expected -ALLOC_BUFFERS 1 -; -; Logging verbosity level for syslog -; 0 = critical and important messages only -; 1 = connections are logged -; 2 = informational logging (RETR and DELE are logged) -; 3 = verbose logging (all commands and replies, but PASS) -; 4 = even PASS is logged, with the user passwords -LOG_FACILITY LOG_AUTHPRIV LOG_LEVEL 2 - - -; TCP service general and security options -; ---------------------------------------- -; -; IP addresses we should bind() to, separated by spaces -LISTEN_IPS "127.0.0.1" -; -; Advertized server hostnames, separated by spaces, there should be the same -; number of entries than for LISTEN_IPS -SERVER_NAMES "pop3.localhost" -; -; Port we listen for connections on -LISTEN_PORT 110 -; -; Resolving hostnames for logging can make the system slow depending on -; DNS server reliability and service load -RESOLVE_HOSTS FALSE -; -; If TRUE, will force a delay when the client performs an error. -DELAY_ON_ERROR FALSE -; -; Maximum number of bad commands user may issue per session -MAX_ERRORS 16 -; -; Total maximum number of simultanious different IP addresses we allow -MAX_IPS 64 -; Maximum simultanious connections per single IP address we accept -MAX_PER_IP 1 -; -; Maximum number of connections per IP address to accept within -; CONNECTION_PERIOD seconds (0 to disable connection rate limit) -CONNECTION_RATE 10 -; Period in seconds in which a maximum of CONNECTION_RATE connections are -; allowed -CONNECTION_PERIOD 30 -; -; Inactivity input timeout in seconds -INPUT_TIMEOUT 120 -; -; Maximum bandwidth allowed per connection, specified in kilobytes/second. -; 0 will disable bandwidth shaping. IN is how fast we accept data from the -; client, and OUT for how fast we may send data to the client. This is on -; a connection basis, which means that MAXIPS and MAXPERIP should be taken -; in consideration if bandwidth is to be controlled for all traffic (eg: to -; limit the daemon's total bandwidth limit to 380k/sec for instance) -BANDWIDTH_IN 4 BANDWIDTH_OUT 12 -; -; The global maximum bandwidth speed limit, in kilobytes per second, for the -; whole server, at which it is allowed to read and write from/to clients. -; 0 for no limit. -GBANDWIDTH_IN 0 GBANDWIDTH_OUT 0 - - -; POP3 security options -; --------------------- -; -; Keep stats on addresses failing logins via mmstat(3) -STATFAIL_LOGIN FALSE -; -; Keep stats on addresses failing passwords for existing logins via mmstat(3) -STATFAIL_PASSWORD FALSE - - -; MySQL options -; ------------- -; -; Host name of MySQL server we should connect to (localhost for UNIX socket) -DB_HOST "localhost" -; -; MySQL user which has all access on DB_DATABASE -DB_USER "mmmail" -; -; MySQL authentication password for DB_USER -DB_PASSWORD "mmmailpassword" -; -; Name of mmmail MySQL database DB_USER owns, typically "mmmail" -DB_DATABASE "mmmail" diff --git a/mmsoftware/mmmail/etc/mmsmtpd.conf b/mmsoftware/mmmail/etc/mmsmtpd.conf deleted file mode 100644 index c2227e2..0000000 --- a/mmsoftware/mmmail/etc/mmsmtpd.conf +++ /dev/null @@ -1,175 +0,0 @@ -; $Id: mmsmtpd.conf,v 1.6 2005/03/05 15:33:33 mmondor Exp $ -; -; mmsmtpd configuration file (/etc/mmsmtpd.conf) -; and # are considered comments, and can happen at start or end of line. -; Read the mmsmtpd.conf(5) man page for details. - - -; Daemon administration options -; ----------------------------- -; -; Number of asynchroneous slave processes that should be launched and used -; to perform tasks such as hostname resolving. -ASYNC_PROCESSES 3 -; -; Optional location of directory we should chroot(2) to. Note that special -; configuration is requried for this, /etc/hosts and /etc/resolv.conf -; files will be required in the new root for instance. -;CHROOT_DIR "" -; -; Location of lock file used to determine if daemon already runs -LOCK_PATH "/var/run/mmsmtpd.lock" -; -; Location of the path where to store our process ID -PID_PATH "/var/run/mmsmtpd.pid" -; -; If using MMMAIL_FILE for file storage of message bodies rather than -; MMMAIL_MYSQL, this specifies where mail box directories should be created -; and messages stored in them. This should also work fine using NFS. -; -MAIL_DIR "/var/mmmail-dir" -; -; User mmsmtpd should run as -USER "mmmail" -; Groups process should be part of -GROUPS mmmail,mmstat -; -; Increase this to higher values if high server load is expected -ALLOC_BUFFERS 1 -; -; Logging verbosity level for syslog -; 0 = critical and important messages only -; 1 = connections are logged -; 2 = informational logging (HELO, MAIL and RCPT are logged) -; 3 = verbose logging (all commands and replies) -; 4 = debugging (even message DATA lines are logged) -LOG_FACILITY LOG_AUTHPRIV LOG_LEVEL 2 - - -; TCP service general and security options -; ---------------------------------------- -; -; IP addresses we should bind() to, separated by spaces -LISTEN_IPS "127.0.0.1" -; -; Advertized server hostnames, separated by spaces, there should be the same -; number of entries than for LISTEN_IPS -SERVER_NAMES "smtp.localhost" -; -; Port we listen for connections on -LISTEN_PORT 25 -; -; Resolving hostnames for logging can make the system slow depending on -; DNS server reliability and service load -RESOLVE_HOSTS FALSE -; -; If TRUE, will force a delay when the client performs an error. -DELAY_ON_ERROR FALSE -; -; Maximum number of bad commands user may issue per session -MAX_ERRORS 16 -; -; Total maximum number of simultanious different IP addresses we allow -MAX_IPS 64 -; Maximum simultanious connections per single IP address we accept -MAX_PER_IP 1 -; -; Maximum number of connections per IP address to accept within -; CONNECTION_PERIOD seconds (0 to disable connection rate limit) -CONNECTION_RATE 10 -; Period in seconds in which a maximum of CONNECTION_RATE connections are -; allowed -CONNECTION_PERIOD 30 -; -; Inactivity input timeout in seconds -INPUT_TIMEOUT 900 -; -; Maximum bandwidth allowed per connection, specified in kilobytes/second. -; 0 will disable bandwidth shaping. IN is how fast we accept data from the -; client, and OUT for how fast we may send data to the client. This is on -; a connection basis, which means that MAXIPS and MAXPERIP should be taken -; in consideration if bandwidth is to be controlled for all traffic (eg: to -; limit the daemon's total bandwidth limit to 380k/sec for instance) -BANDWIDTH_IN 16 BANDWIDTH_OUT 4 -; -; The global maximum bandwidth speed limit, in kilobytes per second, for the -; whole server, at which it is allowed to read and write from/to clients. -; 0 for no limit. -GBANDWIDTH_IN 0 GBANDWIDTH_OUT 0 - - -; SMTP security options -; --------------------- -; -; Should statistics be kept using the mmstat(3) facility about hosts issueing -; wrong destination addresses? Then how about hosts that flood with messages? -STATFAIL_ADDRESS TRUE -STATFAIL_FLOOD TRUE -; -; Should statistics also be kept on the number of times a mailbox was full -; when an attempt to insert a new message was made? -STATFAIL_FULL TRUE -; -; Statistics about addresses timeing out while sending a message -STATFAIL_TIMEOUT TRUE -; -; Should we only accept MAIL command from valid addresses with MX records? -RESOLVE_MX_MAIL FALSE -; -; Is client required to HELO before being allowed to use MAIL? -REQUIRE_HELO FALSE -; -; Maximum number of allowed destination recipients per message -MAX_RCPTS 16 -; -; Maximum DATA lines to accept for a message, make sure that it is enough for -; general messages of MAX_DATA_SIZE bytes large. -MAX_DATA_LINES 16000 -; -; Maximum allowed DATA message size in bytes. Note that MySQL must also have -; been setup to accept BLOB fields of MAX_DATA_SIZE, via the -; max_allowed_packet and optionally query_buffer_size MySQL control variables. -; This can be as simple as passing -O max_allowed_packet=10M as command-line -; argument to mysqld. -MAX_DATA_SIZE 1048576 -; -; Maximum number of Received: lines allowed in a message -MAX_HOPS 30 -; -; For use against mail bombing, these optionally can be set. -; Turns on or off mail flood protection. These are on a per-client -; address/hostname basis. -FLOOD_PROTECTION TRUE -; -; The following only are taken into account if FLOOD_PROTECTION is TRUE. -; Make sure that this is high enough, depending on how you set FLOOD_EXPIRES -; and FLOOD_MESSAGES. It specifies the maximum number of cache entries which -; can be remembered for hosts from which mail was recently received. -; This typically can be set to the same value as MAX_IPS, or larger. -FLOOD_CACHE 100 -; -; This specifies how many messages maximum will be accepted within -; FLOOD_EXPIRES delay. Higher than this will be considered flood, and a -; try again reply will be sent to the SMTP client. -FLOOD_MESSAGES 20 -; -; This is the number of minutes for which the cache entry for a host will -; be remembered. It thus consists of the number of minutes within which a -; maximum of FLOOD_MESSAGES will be accepted, on a per-host basis. -FLOOD_EXPIRES 30 - - -; MySQL options -; ------------- -; -; Host name of MySQL server we should connect to (localhost for UNIX socket) -DB_HOST "localhost" -; -; MySQL user which has all access on DB_DATABASE -DB_USER "mmmail" -; -; MySQL authentication password for DB_USER -DB_PASSWORD "mmmailpassword" -; -; Name of mmmail MySQL database DB_USER owns, typically "mmmail" -DB_DATABASE "mmmail" diff --git a/mmsoftware/mmmail/install.sh b/mmsoftware/mmmail/install.sh deleted file mode 100755 index 0cc2b08..0000000 --- a/mmsoftware/mmmail/install.sh +++ /dev/null @@ -1,158 +0,0 @@ -#!/bin/sh -# $Id: install.sh,v 1.6 2003/10/23 01:01:27 mmondor Exp $ - -if [ "$1" = "help" ]; then - echo - echo 'You can optionally set the following environment variables' - echo 'to customize the installation process. For each is shown an' - echo 'example using the default value followed by a breif description.' - echo - echo 'export MMLAUNCH="TRUE"' - echo ' Tells the install process to immediately launch the daemon(s)' - echo ' They are automatically killed before the binaries are copied' - echo ' over the old ones as is necessary with some systems. The' - echo ' default is to not start them back automatically.' - echo - echo 'export MMPREFIX="/usr/local"' - echo ' Allows to set the installation base directory. All files but' - echo ' configuration ones will be installed in directories relative' - echo ' to this one.' - echo - echo 'export MMCONFDIR="/etc"' - echo ' Directory in which configuration files should be stored.' - echo - echo 'export MMDEFAULTUSER="0"' - echo ' User new files and directories should be owned by, using' - echo ' user id or name.' - echo - echo 'export MMDEFAULTGROUP="0"' - echo ' Group new files and directories should be under, using id' - echo ' or name.' - echo - echo 'export MMADMINGROUP="staff"' - echo ' The administrators group, these can for instance view and the' - echo ' rotate mmstat statistics and execute mmpasswd. Will be' - echo ' created automatically if necessary.' - echo - echo 'export MMSTATDIR="/var/mmstatd"' - echo ' The directory in which mmstatd will store the stats database' - echo ' and log files.' - echo - echo 'export MMSTATDUSER="mmstatd"' - echo ' The user mmstatd will run under, to be automatically created.' - echo - echo 'export MMSTATDGROUP="mmstat"' - echo ' Group the mmstatd user should be part of, automatically' - echo ' created.' - echo - echo 'export MMMAILUSER="mmmail"' - echo ' The user mmmail daemons should run under, will be created' - echo ' automatically if nonexisting.' - echo - echo 'export MMMAILGROUP="mmmail"' - echo ' Group the mmmail user should be part of. Created if needed.' - echo - exit -fi - -# Set defaults if not set -if [ -z "$MMLAUNCH" ]; then - export MMLAUNCH='FALSE' -fi -if [ -z "$MMPREFIX" ]; then - export MMPREFIX='/usr/local' -fi -if [ -z "$MMCONFDIR" ]; then - export MMCONFDIR='/etc' -fi -if [ -z "$MMDEFAULTUSER" ]; then - export MMDEFAULTUSER='0' -fi -if [ -z "$MMDEFAULTGROUP" ]; then - export MMDEFAULTGROUP='0' -fi -if [ -z "$MMADMINGROUP" ]; then - export MMADMINGROUP='staff' -fi -if [ -z "$MMSTATDIR" ]; then - export MMSTATDIR='/var/mmstatd' -fi -if [ -z "$MMSTATDUSER" ]; then - export MMSTATDUSER='mmstatd' -fi -if [ -z "$MMSTATDGROUP" ]; then - export MMSTATDGROUP='mmstat' -fi -if [ -z "$MMMAILUSER" ]; then - export MMMAILUSER='mmmail' -fi -if [ -z "$MMMAILGROUP" ]; then - export MMMAILGROUP='mmmail' -fi - -. ../mmlib/makefuncs.sh - -instgroup $MMADMINGROUP - -cd ../mmlib/ -instman mmfd.3 3 -instman mmlist.3 3 -instman mmpool.3 3 -instman mmstat.3 3 -instman mmhash.3 3 -instman mmlimitrate.3 3 -cd ../ - -cd mmpasswd/ -instbin mmpasswd 750 $MMADMINGROUP -instman mmpasswd.8 8 -cd ../ - -cd mmstatd/src/ -instuser $MMSTATDUSER $MMSTATDGROUP -killbin mmstatd -instbin mmstatd 700 -instbin mmstat 750 $MMADMINGROUP -instman mmstat.8 8 -instman mmstatd.8 8 -instman mmstatd.conf.5 5 -cd ../etc/ -instconf mmstatd.conf 640 $MMSTATDGROUP -instdir $MMSTATDIR 750 $MMSTATDUSER $MMSTATDGROUP -if [ "$MMLAUNCH" = "TRUE" ]; then - startbin mmstatd $MMCONFDIR/mmstatd.conf -fi -cd ../../ - -cd mmmail/src/mmsmtpd/ -instuser $MMMAILUSER $MMMAILGROUP -killbin mmsmtpd -instbin mmsmtpd 700 -instman mmsmtpd.8 8 -instman mmsmtpd.conf.5 5 -cd ../mmpop3d -killbin mmpop3d -instbin mmpop3d 700 -instman mmpop3d.8 8 -instman mmpop3d.conf.5 5 -cd ../../etc -instconf mmsmtpd.conf 600 -instconf mmpop3d.conf 600 -if [ "$MMLAUNCH" = "TRUE" ]; then - startbin mmsmtpd $MMCONFDIR/mmsmtpd.conf - startbin mmpop3d $MMCONFDIR/mmpop3d.conf -fi -cd ../src -instman mmmail.8 8 -cd ../../ - -echo -echo "*** Please read the following man pages ***" -echo -echo "mmstat(8), mmstatd(8), mmstatd.conf(5) mmpasswd(8)" -echo "mmmail(8), mmsmtpd(8), mmsmtpd.conf(5), mmpop3d(8) mmpop3d.conf(5)" -echo "source auditors: mmstat(3), mmfd(3), mmlist(3), mmpool(3), mmhash(3)," -echo " mmlimitrate(3)" -echo -echo "Thank you for using mmsoftware." -echo diff --git a/mmsoftware/mmmail/make.sh b/mmsoftware/mmmail/make.sh deleted file mode 100755 index 9bb6844..0000000 --- a/mmsoftware/mmmail/make.sh +++ /dev/null @@ -1,31 +0,0 @@ -#!/bin/sh -# $Id: make.sh,v 1.2 2003/01/01 14:54:11 mmondor Exp $ - -. ../mmlib/makefuncs.sh - -cd ../mmlib/ -clean mmlib -makebin mmlib -cd ../ - -cd mmpasswd/ -clean mmpasswd -makebin mmpasswd -cd ../ - -cd mmstatd/src/ -clean mmstatd -makebin mmstatd -cd ../../ - -cd mmmail/src/mmsmtpd/ -clean mmsmtpd -makebin mmsmtpd -cd ../mmpop3d -clean mmpop3d -makebin mmpop3d -cd ../../ - -echo -echo 'You may now ./install.sh help or ./install.sh to install/upgrade' -echo diff --git a/mmsoftware/mmmail/scripts/mmpop3d.sh b/mmsoftware/mmmail/scripts/mmpop3d.sh deleted file mode 100755 index b54b01f..0000000 --- a/mmsoftware/mmmail/scripts/mmpop3d.sh +++ /dev/null @@ -1,32 +0,0 @@ -#!/bin/sh -# -# /etc/init.d/mmpop3d: start or stop daemon (Mondor) -# -# $Id: mmpop3d.sh,v 1.1 2002/12/11 10:16:17 mmondor Exp $ - -PATH=/bin:/sbin:/usr/bin:/usr/sbin - -case "$1" in - start) - echo -n "Starting up mmpop3d daemon" - /usr/local/sbin/mmpop3d - echo "." - ;; - stop) - echo -n "Shutting down mmpop3d daemon" - /bin/kill `cat /var/run/mmpop3d.pid` - echo "." - ;; - restart|force-reload) - /etc/init.d/mmpop3d.sh stop - /bin/sleep 10s - /etc/init.d/mmpop3d.sh start - ;; - *) - echo "Usage: /etc/init.d/mmpop3d.sh {start|stop|restart|force-reload}" - exit 1 - ;; -esac - -exit 0 - diff --git a/mmsoftware/mmmail/scripts/mmsmtpd.sh b/mmsoftware/mmmail/scripts/mmsmtpd.sh deleted file mode 100755 index 2b9a75a..0000000 --- a/mmsoftware/mmmail/scripts/mmsmtpd.sh +++ /dev/null @@ -1,32 +0,0 @@ -#!/bin/sh -# -# /etc/init.d/mmsmtpd: start or stop daemon (Mondor) -# -# $Id: mmsmtpd.sh,v 1.1 2002/12/11 10:16:18 mmondor Exp $ - -PATH=/bin:/sbin:/usr/bin:/usr/sbin - -case "$1" in - start) - echo -n "Starting up mmsmtpd daemon" - /usr/local/sbin/mmsmtpd - echo "." - ;; - stop) - echo -n "Shutting down mmsmtpd daemon" - /bin/kill `cat /var/run/mmsmtpd.pid` - echo "." - ;; - restart|force-reload) - /etc/init.d/mmsmtpd.sh stop - /bin/sleep 10s - /etc/init.d/mmsmtpd.sh start - ;; - *) - echo "Usage: /etc/init.d/mmsmtpd.sh {start|stop|restart|force-reload}" - exit 1 - ;; -esac - -exit 0 - diff --git a/mmsoftware/mmmail/scripts/tables.sql b/mmsoftware/mmmail/scripts/tables.sql deleted file mode 100644 index 79faf56..0000000 --- a/mmsoftware/mmmail/scripts/tables.sql +++ /dev/null @@ -1,148 +0,0 @@ -# $Id: tables.sql,v 1.20 2005/03/26 11:45:44 mmondor Exp $ -# -# MySQL dump 8.13 -# -# Host: localhost Database: mmmail -#-------------------------------------------------------- -# Server version 3.23.35 - -# -# Table structure for table 'alias' -# - -CREATE TABLE alias ( - alias_domain varchar(64) NOT NULL default '', - alias_pattern varchar(64) NOT NULL default '', - alias_box varchar(64) NOT NULL default '', - alias_created datetime NOT NULL default '0000-00-00 00:00:00', - PRIMARY KEY (alias_domain,alias_pattern,alias_box) -) TYPE=MyISAM; - -# -# Table structure for table 'box' -# - -CREATE TABLE box ( - box_address varchar(64) NOT NULL default '', - box_user varchar(32) NOT NULL, - box_max_size int(11) NOT NULL default '0', - box_size int(11) NOT NULL default '0', - box_max_msgs int(11) NOT NULL default '0', - box_msgs int(11) NOT NULL default '0', - box_created datetime NOT NULL default '0000-00-00 00:00:00', - box_in datetime NOT NULL default '0000-00-00 00:00:00', - box_out datetime NOT NULL default '0000-00-00 00:00:00', - box_filter tinyint(1) NOT NULL default '0', - box_description varchar(64) NOT NULL default '', - PRIMARY KEY (box_address) -) TYPE=MyISAM; - -# -# Table structure for table 'filter' -# - -CREATE TABLE filter ( - filter_address varchar(64) NOT NULL default '', - filter_pattern varchar(128) NOT NULL default '', - filter_created datetime NOT NULL default '0000-00-00 00:00:00', - filter_description varchar(64) NOT NULL default '', - PRIMARY KEY (filter_address,filter_allow) -) TYPE=MyISAM; - -# -# Table structure for table 'mail' -# - -CREATE TABLE mail ( - mail_id bigint(20) NOT NULL auto_increment, - mail_box varchar(64) NOT NULL default '', - mail_file varchar(255), - mail_created datetime NOT NULL default '0000-00-00 00:00:00', - mail_size int(11) NOT NULL default '0', - mail_data longtext, - PRIMARY KEY (mail_id), - KEY mail_box (mail_box) -) TYPE=MyISAM; - -# -# Table structure for table 'nofrom' -# - -CREATE TABLE nofrom ( - nofrom_pattern varchar(64) NOT NULL default '', - PRIMARY KEY (nofrom_pattern) -) TYPE=MyISAM; - -# -# Table structure for table 'user' -# - -CREATE TABLE user ( - user_id varchar(32) NOT NULL, - user_name varchar(64) NOT NULL default '', - user_passwd char(34) binary NOT NULL default '', - user_created datetime NOT NULL default '0000-00-00 00:00:00', - user_activity datetime NOT NULL default '0000-00-00 00:00:00', - user_logins bigint(20) NOT NULL default '0', - user_active tinyint(1) NOT NULL default '1', - user_admin tinyint(1) NOT NULL default '0', - PRIMARY KEY (user_id) -) TYPE=MyISAM; - -# -# Table structure for table 'relayqueue' -# - -CREATE TABLE relayqueue ( - relayqueue_id bigint(20) NOT NULL auto_increment, - relayqueue_from varchar(64) NOT NULL default '', - relayqueue_ipaddr varchar(16) NOT NULL default '0.0.0.0', - relayqueue_todomain varchar(64) NOT NULL default '', - relayqueue_touser varchar(64) NOT NULL default '', - relayqueue_size int(11) NOT NULL default '0', - relayqueue_file varchar(255) NOT NULL default '', - relayqueue_queued datetime NOT NULL default '0000-00-00 00:00:00', - relayqueue_lasttry datetime NOT NULL default '0000-00-00 00:00:00', - relayqueue_expire datetime NOT NULL default '0000-00-00 00:00:00', - relayqueue_tries int(11) NOT NULL default '0', - relayqueue_lasterror int(11) NOT NULL default '0', - PRIMARY KEY (relayqueue_id) -) TYPE=MyISAM; - -# -# Table structure for table 'relaylocal' -# - -CREATE TABLE relaylocal ( - relaylocal_pattern varchar(64) NOT NULL default '', - PRIMARY KEY (relaylocal_pattern) -) TYPE=MyISAM; - -# -# Table structure for table 'relayfrom' -# - -CREATE TABLE relayfrom ( - relayfrom_pattern varchar(64) NOT NULL default '', - PRIMARY KEY (relayfrom_pattern) -) TYPE=MyISAM; - -# -# Table structure for table `session` -# - -CREATE TABLE session ( - session_id varchar(64) binary NOT NULL default '', - session_expires int(11) NOT NULL default '0', - session_data text NOT NULL, - PRIMARY KEY (session_id) -) TYPE=MyISAM; - -# -# Table structure for table `boxdelete` -# - -CREATE TABLE boxdelete ( - boxdelete_address varchar(64) NOT NULL default '', - PRIMARY KEY (boxdelete_address) -) TYPE=MyISAM; diff --git a/mmsoftware/mmmail/scripts/upgrade-0.0.13.sql b/mmsoftware/mmmail/scripts/upgrade-0.0.13.sql deleted file mode 100644 index c90aacf..0000000 --- a/mmsoftware/mmmail/scripts/upgrade-0.0.13.sql +++ /dev/null @@ -1,33 +0,0 @@ -# $Id: upgrade-0.0.13.sql,v 1.1 2002/12/22 12:55:45 mmondor Exp $ -# -# You should execute this script if you are upgrading mmmail from 0.0.12 or -# older to 0.0.13 or later, in order to not have to rebuild your whole MySQL -# data. -# - -ALTER TABLE box ADD COLUMN box_created datetime NOT NULL; -ALTER TABLE box ADD COLUMN box_in datetime NOT NULL; -ALTER TABLE box ADD COLUMN box_out datetime NOT NULL; -UPDATE box SET box_created=NOW(), box_in=NOW(), box_out=NOW(); - -ALTER TABLE user ADD COLUMN user_created datetime NOT NULL; -ALTER TABLE user ADD COLUMN user_activity datetime NOT NULL; -ALTER TABLE user ADD COLUMN user_logins bigint(20) NOT NULL DEFAULT 0; -ALTER TABLE user ADD COLUMN user_admin bigint(20) NOT NULL; -ALTER TABLE user ADD COLUMN user_active bool NOT NULL DEFAULT 1; -UPDATE user SET user_created=NOW(), user_activity=NOW(); - -ALTER TABLE mail ADD COLUMN mail_created datetime NOT NULL AFTER mail_box; - -CREATE TABLE alias ( - alias_pattern varchar(64) NOT NULL default '', - alias_box varchar(64) NOT NULL default '', - alias_user bigint(20) NOT NULL default '0', - alias_created datetime NOT NULL default '0000-00-00 00:00:00', - PRIMARY KEY (alias_pattern) -) TYPE=MyISAM; - -CREATE TABLE nofrom ( - nofrom_pattern varchar(64) NOT NULL default '', - PRIMARY KEY (nofrom_pattern) -) TYPE=MyISAM; diff --git a/mmsoftware/mmmail/scripts/upgrade-0.0.21.sql b/mmsoftware/mmmail/scripts/upgrade-0.0.21.sql deleted file mode 100644 index eaeaafc..0000000 --- a/mmsoftware/mmmail/scripts/upgrade-0.0.21.sql +++ /dev/null @@ -1,11 +0,0 @@ -# $Id: upgrade-0.0.21.sql,v 1.2 2003/01/04 01:51:08 mmondor Exp $ -# -# You should execute this script if you are upgrading mmmail from 0.0.20 or -# older to 0.0.21 or later, to change the size of the password field -# which needs to be larger. -# - -ALTER TABLE user MODIFY COLUMN user_passwd char(34) BINARY NOT NULL DEFAULT ''; - -ALTER TABLE alias DROP PRIMARY KEY; -ALTER TABLE alias ADD COLUMN alias_domain char(64) NOT NULL DEFAULT ''; diff --git a/mmsoftware/mmmail/scripts/upgrade-0.0.24.sql b/mmsoftware/mmmail/scripts/upgrade-0.0.24.sql deleted file mode 100644 index 0281b7c..0000000 --- a/mmsoftware/mmmail/scripts/upgrade-0.0.24.sql +++ /dev/null @@ -1,87 +0,0 @@ -# $Id: upgrade-0.0.24.sql,v 1.15 2005/03/26 11:45:44 mmondor Exp $ -# -# You should execute this script if you are upgrading mmmail from 0.0.21 or -# later to 0.0.24 or later. This adds the new mail_file field for those using -# file message body storage, and modifies the glob field to allow NULL values. -# It also adds the filter table for optional per-box allow filters, and -# introduces support for optional relaying and related security. -# - -ALTER TABLE mail MODIFY COLUMN mail_data longtext; -ALTER TABLE mail ADD COLUMN mail_file varchar(255); -ALTER TABLE box ADD COLUMN box_filter tinyint(1) NOT NULL default '0'; -ALTER TABLE box ADD COLUMN box_filter_type char(1) NOT NULL default 'D'; -ALTER TABLE box ADD COLUMN box_description varchar(64) NOT NULL default ''; - -ALTER TABLE user MODIFY user_id varchar(32) NOT NULL; -ALTER TABLE box MODIFY box_user varchar(32) NOT NULL; -ALTER TABLE user DROP COLUMN user_admin; -ALTER TABLE user ADD COLUMN user_admin tinyint(1) NOT NULL default '0'; -ALTER TABLE alias DROP COLUMN alias_user; -ALTER TABLE alias ADD PRIMARY KEY (alias_domain,alias_pattern,alias_box); - -CREATE TABLE filter ( - filter_address varchar(64) NOT NULL default '', - filter_pattern varchar(128) NOT NULL default '', - filter_created datetime NOT NULL default '0000-00-00 00:00:00', - filter_description varchar(64) NOT NULL default '', - PRIMARY KEY (filter_address,filter_allow) -) TYPE=MyISAM; - -# -# Table structure for table 'relayqueue' -# - -CREATE TABLE relayqueue ( - relayqueue_id bigint(20) NOT NULL auto_increment, - relayqueue_from varchar(64) NOT NULL default '', - relayqueue_ipaddr varchar(16) NOT NULL default '0.0.0.0', - relayqueue_todomain varchar(64) NOT NULL default '', - relayqueue_touser varchar(64) NOT NULL default '', - relayqueue_size int(11) NOT NULL default '0', - relayqueue_file varchar(255) NOT NULL default '', - relayqueue_queued datetime NOT NULL default '0000-00-00 00:00:00', - relayqueue_lasttry datetime NOT NULL default '0000-00-00 00:00:00', - relayqueue_expire datetime NOT NULL default '0000-00-00 00:00:00', - relayqueue_tries int(11) NOT NULL default '0', - relayqueue_lasterror int(11) NOT NULL default '0', - PRIMARY KEY (relayqueue_id) -) TYPE=MyISAM; - -# -# Table structure for table 'relaylocal' -# - -CREATE TABLE relaylocal ( - relaylocal_pattern varchar(64) NOT NULL default '', - PRIMARY KEY (relaylocal_pattern) -) TYPE=MyISAM; - -# -# Table structure for table 'relayfrom' -# - -CREATE TABLE relayfrom ( - relayfrom_pattern varchar(64) NOT NULL default '', - PRIMARY KEY (relayfrom_pattern) -) TYPE=MyISAM; - -# -# Table structure for table 'session' -# - -CREATE TABLE session ( - session_id varchar(64) binary NOT NULL default '', - session_expires int(11) NOT NULL default '0', - session_data text NOT NULL, - PRIMARY KEY (session_id) -) TYPE=MyISAM; - -# -# Table structure for table `boxdelete` -# - -CREATE TABLE boxdelete ( - boxdelete_address varchar(64) NOT NULL default '', - PRIMARY KEY (boxdelete_address) -) TYPE=MyISAM; diff --git a/mmsoftware/mmmail/src/mmmail.8 b/mmsoftware/mmmail/src/mmmail.8 deleted file mode 100644 index df5873a..0000000 --- a/mmsoftware/mmmail/src/mmmail.8 +++ /dev/null @@ -1,477 +0,0 @@ -.\" $Id: mmmail.8,v 1.8 2004/11/09 05:31:14 mmondor Exp $ -.\" -.\" Copyright (C) 2001-2004, Matthew Mondor -.\" All rights reserved. -.\" -.\" Redistribution and use in source and binary forms, with or without -.\" modification, are permitted provided that the following conditions -.\" are met: -.\" 1. Redistributions of source code must retain the above copyright -.\" notice, this list of conditions and the following disclaimer. -.\" 2. Redistributions in binary form must reproduce the above copyright -.\" notice, this list of conditions and the following disclaimer in the -.\" documentation and/or other materials provided with the distribution. -.\" 3. All advertising materials mentioning features or use of this software -.\" must display the following acknowledgement: -.\" This product includes software written by Matthew Mondor. -.\" 4. The name of Matthew Mondor may not be used to endorse or promote -.\" products derived from this software without specific prior written -.\" permission. -.\" -.\" THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR -.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES -.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. -.\" IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, -.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -.\" BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF -.\" USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF -.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -.\" -.Dd 22 December, 2002 -.Dt MMMAIL 8 -.Os -.Sh NAME -.Nm mmmail -.Nd SMTP and POP3 suite of daemons -.Sh SYNOPSIS -.Nm mmsmtpd Op Ar config_file -.Nm mmpop3d Op Ar config_file -.Sh DESCRIPTION -.Nm mmmail -consists of a suite of SMTP and POP3 daemons, -.Xr mmsmtpd 8 -and -.Xr mmpop3d 8 -which support bandwidth shaping and a MySQL backend for message storage. -These servers run as an unprivileged user and should therefore be more secure -than most MTAs. Additionally, some effort was put in providing various -protection methods against Denial of Service attacks, and mail bombing. -It is also possible to run the daemons into a -.Xr chroot 2 -jail. Basic relaying is also supported, when properly enabled and configured. -.Pp -There are no official HTTP administration frontends for -.Nm mmmail -yet, although there exist third party perl and PHP scripts for apache -which can be used, there is no guarentee whatsoever on their security. -Eventually a small secure ssl-httpd will be written which should serve remote -administration needs for the various mm daemons. Some work is also in progress -to redesign some parts of the system so that various message storage and -authentication methods could be chosen from (MySQL, pgsql, flatfile, db4, etc), -and to allow to delegate administration of particular hosts to their -administrator, who could manage the users and mailboxes for that host. -There also have been some notes written about a possible eventual mailing -list manager system. -.Pp -The current design allows to securely receive mail through SMTP for many -users on several hosts, and serve back that mail securely via POP3. It is -efficient at doing so. It supports multiple mailboxes per user, aliasing, -and virtual hosts. Good statistics are kept using the -.Xr mmstat 3 -interface via -.Xr mmstatd 8 -and the SMTP and POP3 daemons may run on different servers via a persistant -TCP link to the MySQL server. There also can be several of each on various -servers using replicated MySQL servers. -.Pp -Each mail box can have different limits on the maximum number of messages -they may hold, as well as the maximum size their mailbox may grow to, in -bytes. -.Pp -.Nm mmsmtpd -and -.Nm mmpop3d -establish one main connection with the MySQL server each, -through which it processes all queries. This normally would be an advantage to -keep the file descriptors amount smaller, and for remote connections using -TCP/IP to the mysql server, which should cause a speed improvement. It also -will re-establish that connection whenever it is lost, for whatever reason, -which permits to run the SQL server anywhere on the internet (although running -the server on a LAN is recommended, unless under kerberos, ssh or ipsec -tunneling). -.Pp -Special note: As the suite supports vitualhosts, and that some mail clients -cannot handle '@' in usernames, it by convention replaces the first '.' in the -username by '@' internally, to match it with a mailbox. This prevents using '.' -in username part of addresses, _ will be used without problem though: -eg: for the mailbox mmondor@myhost.net, the corresponding username would be -mmondor.myhost.net. The password will remain the one of the user who owns that -mail box. -.Sh INSTALLATION -This document assumes that you know how to manage mysql tables using the mysql -client, as you will need to create a new mysql user (typically called mmmail), -and a new database for it. Only mmmail user should have access to the mmmail -database and only root and the dedicated user under which mysqld runs should -be able to access the database files (typically /home/databases/mysql/ -directory). These are common security administration procedures that mysqld -users should know about. The mmmail suite is highly dependant on the mysql -database server. This also means that you are responsible for database -integrity, that you must provide it using efficient backup systems, UPS, etc, -whatever required for general data safety using MySQL. I also assume that you -know how to manage unix file permissions, groups, and how to make sure that -mysqld does not run as root, that only mysql user can access the mysql data -directory, that you also know how to only allow wanted hosts and users to be -able to connect to mysqld, etc. Another important thing to note is that as -mmmail source tarball does not use autoconf, you may need to edit the Makefile -files, if some libraries or include headerfiles cannot be found using the -default ones. -.Ar ( LIBDIR -and -.Ar INCDIR -variables may need to be modified especially). -.Pp -.Nm mmmail -requires -.Xr mmpasswd 8 -and -.Xr mmstatd 8 -which are provided with it. Normally, executing the -.Nm make.sh -script should compile the distribution, and -.Nm install.sh -should install or upgrade all of the necessary components. -Note that there are several configurable options which can be set prior to -calling -.Nm install.sh -script; Use the -.Nm './install.sh help' -command to see a description of the environment variables which you may want -to set. -The -.Nm mmlib/makedefs.sh -file contains defaults for building commands, library and include paths, and -may be modified if compilation issues occur. The building process is no longer -dependant on BSD or GNU make, only /bin/sh is required. -You may want to -look at the -.Xr mmstatd 8 -and -.Xr mmstatd.conf 5 -man pages on how to configure -.Nm mmstatd. -You first should make sure that you have the following libraries: -.Bd -literal -offset indent -libpth version 1.4.1 or later (GNU Pth) - http://www.gnu.org/software/pth/pth.html - -libmysqlclient (usually part of mysql-client package) - http://www.mysql.org -.Ed -.Pp -.Nm "Upgrading from 0.0.12 or older to 0.0.13 or later:" -Note that if you previously were using mmmail 0.0.12 or older, and are -upgrading to mmmail 0.0.13 or later, two new MySQL tables were introduced -for aliasing and -.Sy FROM:<> -support (see -.Nm scripts/tables.sql -for more information). -If you wish to ease the transition you should execute the -.Nm scripts/upgrade-0.0.13.sql -MySQL script, so that you don't have to re-create the tables. This should -also preserve your current data. -.Pp -.Nm "Upgrading from 0.0.20 or older to 0.0.21 or later:" -If you are upgrading from mmmail 0.0.20 to 0.0.21 or later, an important change -has been performed on the way password hashes are stored. Main reasons for -this are that frontend scripts would not easily be able to generate these, -and that they were not compatible with system ones, making migration process -from real users to virtual ones harder. The size of the -.Fa user_passwd -column of the -.Fa user -MySQL table in the -.Fa mmmail -database will need to be modified. -Moreover, the password hashes will need to be changed for all users. -Unfortunately, it was impossible to provide a utility to convert old password -hashes to new ones because of a bug in the previous base64 conversion which -would loose some of the last bytes of the MD5 hash. -Please see -.Xr mmpasswd 8 -man page for more details on the password hash format. To perform the -transition you should execute the -.Nm scripts/upgrade-0.0.21.sql -MySQL script. Your current data will be preserved. -.Pp -.Nm "Upgrading from 0.0.21 or later to 0.0.24 or later:" -Similarily to with other upgrade procedures where modifications were -performed to the database layout, you should execute the -.Nm scripts/upgrade-0.0.24.sql -MySQL script. Your current data will be preserved. -.Pp -To install, first extract the source code tree -.Bd -literal -offset indent -# tar xzvf mmmail-0.0.21.tgz -# cd mmmail-0.0.21/ -.Ed -.Pp -A script has been provided which permits to build and install/upgrade all -binaries, preserving old file permissions and old configuration files in -/etc/ if those previously existed. Those will install the binaries into -/usr/local/sbin/ and the configuration files to /etc/. Moreover, if the -daemons are already running, it makes sure to kill them before writing over -the files again, and to restart them afterwards. It is very useful to quickly -upgrade mmmail. To execute it, simply execute -.Nm make.sh -: -.Bd -literal -offset indent -# ./make.sh -# ./install.sh -.Ed -.Pp -The man pages and required binaries should now have been installed in -/usr/local, but any old existing configuration files in /etc/ will be -preserved in the case of an upgrade. It therefore generally consists of -a good idea to consult the configuration files man pages after upgrading, -in this case -.Xr mmsmtpd.conf 5 -and -.Xr mmpop3d.conf 5 . -.Pp -Let's now configure -.Nm mmmail -.Pp -As this does not consist of a MySQL manual, I will restrict the examples to -the minimum, as if we were to run the mysql server on the same machine the -mail daemons are running on (under which case connections are made using UNIX -domain sockets rather than TCP/IP also). The MySQL user name is mmmail, it's -password is set to mmmailpassword in this example, and the mmmail database named -mmmail. We then make sure that only MySQL root and mmmail users can access the -mmmail database (using mysql grant permission tables, this does not solve the -other issues about database security which were explaned above). -.Pp -There also is a supplied password program which can be used to generate the -hash that mmmail requires for a user password, it has to be case-sensitive in -the user table. It consists of -.Nm mmpasswd -(See the -.Xr mmpasswd 8 -man page for details). There currently is no software -provided to manage users and mailboxes, so the administrator has to edit the -tables manually using a MySQL client. -.Pp -An important thing to additionally remember consists of the MySQL -.Ar max_allowed_packet -variable which should be set high enough for the -.Nm /etc/mmsmtpd.conf -.Ar MAX_DATA_SIZE -configuration option. A common way to perform -this is passing the command line argument -.Nm -O max_allowed_packet=5M -for instance when starting the MySQL server daemon. -.Pp -Let's create the mmmail database, mmmail user, and permit the user to access -it's database, with proper permissions for mmmail suite: -.Bd -literal -offset indent -$ mysql -u root -p -password: *********** -> create database mmmail; -> use mysql; -> insert into user (Host,User,Password) - values('localhost','mmmail',password('mmmailpassword')); -> insert into db values('localhost','mmmail','mmmail', - 'Y','Y','Y','Y','N','N','N','N','Y','N'); -> flush privileges; -.Ed -.Pp -Now we need to create the mmmail tables from the .sql file. The reason we are -doing this as the mysql root user is that mmmail user does not have rights -to alter the mmmail database, for obvious security reasons. -.Bd -literal -offset indent -$ mysql -u root -p mmmail use mmmail; -> insert into user (user_name,user_passwd,user_created) - values('some user','$1$UrxLMU$f5Hb/pVGURl7sZ.4sEiYD1',NOW()); -> insert into box (box_address,box_user,box_max_size, - box_max_msgs,box_msgs,box_filter,box_created) - values('mmondor@myhost.net',1,5242880, 0,500,0,0,NOW()); -.Ed -.Pp -This sets for both boxes the following limits: 5242880 maximum size in bytes -for the whole mailbox contents, and 500 messages maximum capacity. -.Pp -Now let's say we want to have all mail sent to the domain redirected to -that mailbox we created, we will create an alias for it. Pattern matching -is allowed in the alias_pattern field, using '*' and '?'. -.Bd -literal -offset indent -> insert into alias (alias_domain,alias_pattern,alias_box, - alias_user,alias_created) values('*','myhost.net', - 'mmondor@myhost.net',1,NOW()); -.Ed -.Pp -This will redirect any mail addressed to anyone @myhost.net to -mmondor@myhost.net mailbox. The way aliasing works is as follows: The -destination address -.Sy ( RCPT ) -is first looked up. If it exists it is immediately -chosen. If it doesn't, the alias table is scanned for the closest matching -pattern. If a pattern matches, the supplied address is internally substituted -by the address set for the alias pattern, and the new address is again looked -up. If either no aliases can map the address, or it mapped it to an unexisting -box, the address is rejected. This means that if mmondor and lgodbout existed -@myhost.net domain, they of course would take precedence. An alias would be -looked for afterwards only. -.Pp -Some systems use an empty -.Sy MAIL FROM:<> -address. If you need some hosts to -allow it, you can add patterns to accept into the nofrom table. If -.Ar RESOLVE_HOSTS -is -.Nm TRUE -in -.Nm /etc/mmsmtpd.conf , -matches will be checked against both hostnames and addresses in -this table. As such, an entry as "127.0.0.1" will still match for "localhost", -although a "localhost" entry by itself would obviously only match if resolving -worked for the 127.0.0.1 address. -Just like for aliases patterns, '*' and '?' pattern matching is performed. -.Bd -literal -offset indent -> insert into nofrom (nofrom_pattern) values('127.0.0.1'); -> insert into nofrom (nofrom_pattern) values('*.gobot.ca'); -.Ed -.Pp -This would allow clients connecting from localhost (weither resolving is -turned on or off) to use empty from address while posting mail. Moreover, -it would only allowed properly resolved gobot.ca hosts to also do so. -.Pp -There is an optional box_filter field which can be set to 1 (TRUE), which -can be useful for private addresses for which the owner wants to set -permissions for allowed mail addresses to post to. Patterns can be specified -in the filter table, with optional '*' and '?' pattern matching. -.Bd -literal -offset indent -> insert into filter (filter_address,filter_allow) - values('mmondor@gobot.ca','*@gobot.ca'); -.Ed -.Pp -This would allow mail from all @gobot.ca addresses to the mmondor@gobot.ca -mailbox. -.Pp -Verify that the configuration files in /etc are right, then you should be -able to start the daemons, as the superuser (they will drop privileges): -.Bd -literal -offset indent -/usr/local/sbin/mmstatd -/usr/local/sbin/mmsmtpd -/usr/local/sbin/mmpop3d -.Ed -.Pp -If any problem occur, carefully check the syslog logs in /var/log. -.Sh SECURITY CONSIDERATIONS -The -.Nm /etc/mmsmtpd.conf -and -.Nm /etc/mmpop3d.conf -files should only be readable by the superuser. The cause consists in the -MySQL access password being held in them. -.Bd -literal -offset indent --rw------- 1 root wheel 2510 Sep 8 2002 /etc/mmpop3d.conf --rw------- 1 root wheel 4504 Sep 8 2002 /etc/mmsmtpd.conf -.Ed -.Pp -It also is important that the MySQL server runs as an unprivileged user, -and has decent root and mmmail users passwords set. It is a good idea where -possible to have it not listen on the TCP port for more security. Additionally, -The MySQL data directory should only be accessible by root and the MySQL -server. It is a good idea to restrict root and mmmail users from localhost -as well, in the mysql database, and to restrict the mmmail database to the -mmmail user, in the db database. Please read the MySQL manual for more -information on using it securely. Here are a few example ideas: -.Bd -literal -$ ps axwwwo user,command | grep mysql | grep -v grep -mysql /usr/pkg/libexec/mysqld --basedir=/usr/pkg --datadir=/home/mysql --user=mysql --pid-file=/home/mysql/gobot.pid -O max_allowed_packet=8M --skip-networking - -$ grep database /etc/group -database:*:1000: - -$ grep mysql /etc/passwd -mysql:*:1001:1000::/home/mysql:/sbin/nologin - -$ ls -ld /home/mysql -drwx------ 9 mysql database 512 Dec 31 1997 /home/mysql - -$ mysql -u root -ERROR 1045: Access denied for user: 'root@localhost' (Using password: NO) -$ mysql -u mmmail -ERROR 1045: Access denied for user: 'mmmail@localhost' (Using password: NO) -.Ed -.Pp -Note that because the -.Xr mmpasswd 8 -command requires the password to be supplied on the command line, it could be -a problem if the command history was readable by other users. This may change -in a future release. -.Sh FILES -.Bl -tag -width XXXXXXXXXXXXXXXXXXXXXXXXX -compact -.It Pa /usr/local/sbin/mmstatd -The -.Xr mmstatd 8 -daemon, required by -.Nm mmmail -.Pp -.It Pa /usr/local/sbin/mmsmtpd -The SMTP server, see -.Xr mmsmtpd 8 -.Pp -.It Pa /usr/local/sbin/mmpop3d -The POP3 server, see -.Xr mmpop3d 8 -.Pp -.It Pa /etc/mmsmtpd.conf -The configuration file for -.Nm mmsmtpd -(See -.Xr mmsmtpd.conf 5 -for details) -.Pp -.It Pa /etc/mmpop3d.conf -The configuration file for -.Nm mmpop3d -(See -.Xr mmpop3d.conf 5 -for details) -.El -.Sh AUTHOR -.Nm mmsmtpd -and -.Nm mmpop3d -were designed and written by Matthew Mondor and are Copyright (c) 2001-2004, -Matthew Mondor, All rights reserved. They were primarily developped on -NetBSD but were also mildly tested on Linux prior to release. -.Sh SEE ALSO -.Xr mmsmtpd 8 , -.Xr mmrelayd 8 , -.Xr mmpop3d 8 , -.Xr mmsmtpd.conf 5 , -.Xr mmrelayd.conf 5 , -.Xr mmpop3d.conf 5 , -.Xr mmstatd 8 , -.Xr mmpasswd 8 , -.Xr setgroups 2 , -.Xr setuid 2 , -.Xr chroot 2 , -.Xr mysqld 1 , -.Xr mysql 1 , -.Xr mmfd 3 . -.Sh BUGS -.Nm mmmail -does not support IMAP or mail filtering. It is currently -under a totally new design for v2, and will most likely be written from -scratch to support alot of features, including mailing list management, -administration delegation for domains, multiple storage methods, etc. -If you would like to see the current design drafts and offer suggestions, -they can be found at http://gobot.accela.net/software.html under the mmmail -section. -.Pp -Please report any bug to mmondor@accela.net diff --git a/mmsoftware/mmmail/src/mmpop3d/Makefile b/mmsoftware/mmmail/src/mmpop3d/Makefile deleted file mode 100644 index 8d2b58c..0000000 --- a/mmsoftware/mmmail/src/mmpop3d/Makefile +++ /dev/null @@ -1,36 +0,0 @@ -# $Id: Makefile,v 1.4 2003/07/17 22:26:51 mmondor Exp $ - -CC = gcc -MAKE = make -ECHO = echo - -CFLAGS += -D_REENTRANT -DDEBUG -Wall - -PTHINCDIR != $(ECHO) `pth-config --cflags` -PTHLIBDIR != $(ECHO) -L`pth-config --libdir` -MYSQLINCDIR != $(ECHO) `mysql_config --cflags` -MYSQLLIBDIR != $(ECHO) `mysql_config --libs` -INCDIR = -I/usr/include -I/usr/local/include -I. -I../../../mmlib $(PTHINCDIR) $(MYSQLINCDIR) -LIBDIR = -L/usr/local/lib -L/usr/lib -L/lib -L/usr/pkg/lib $(PTHLIBDIR) $(MYSQLLIBDIR) -LIBS = ../../../mmlib/libmmondor.a -lc -lcrypt -lpth - -OBJS = mmpop3d.o - -CCOBJ = $(CC) $(CFLAGS) -c $(INCDIR) - - - - -all: $(OBJS) - $(CC) $(CFLAGS) -o mmpop3d $(INCDIR) $(LIBDIR) $(OBJS) $(LIBS) - - - -clean: - -rm -f $(OBJS) mmpop3d - - - -mmpop3d.o: - $(CCOBJ) mmpop3d.c - diff --git a/mmsoftware/mmmail/src/mmpop3d/makepart.sh b/mmsoftware/mmmail/src/mmpop3d/makepart.sh deleted file mode 100755 index 6591518..0000000 --- a/mmsoftware/mmmail/src/mmpop3d/makepart.sh +++ /dev/null @@ -1,29 +0,0 @@ -#!/bin/sh -# $Id: makepart.sh,v 1.3 2003/01/01 14:54:11 mmondor Exp $ - -. ../../../mmlib/makedefs.sh - -OBJS='mmpop3d.o' -BIN1='mmpop3d' - -if [ "$1" = "clean" ]; then - show $RM $OBJS $BIN1 - exit 0 -fi - -PTHINCDIR="`pth-config --cflags`" -PTHLIBDIR="-L`pth-config --libdir`" -MYSQLINCDIR="`mysql_config --cflags | $SED s/\'//g`" -MYSQLLIBDIR="`mysql_config --libs | $SED s/\'//g`" -INCDIR="-I../../../mmlib $STDINC $PTHINCDIR $MYSQLINCDIR" -LIBDIR="$STDLIB $PTHLIBDIR $MYSQLLIBDIR" -LIBS='../../../mmlib/libmmondor.a -lc -lcrypt -lpth' - -for obj in $OBJS; do - if [ ! -f $obj ]; then - src=`$ECHO $obj | $SED 's/\.o$/.c/'` - show $CC $CFLAGS -c $INCDIR $src - fi -done - -show $CC $CFLAGS -o $BIN1 $INCDIR $LIBDIR $BIN1.o $LIBS diff --git a/mmsoftware/mmmail/src/mmpop3d/mmpop3d.8 b/mmsoftware/mmmail/src/mmpop3d/mmpop3d.8 deleted file mode 100644 index 092bf93..0000000 --- a/mmsoftware/mmmail/src/mmpop3d/mmpop3d.8 +++ /dev/null @@ -1,86 +0,0 @@ -.\" $Id: mmpop3d.8,v 1.3 2004/05/05 23:59:57 mmondor Exp $ -.\" -.\" Copyright (C) 2001-2004, Matthew Mondor -.\" All rights reserved. -.\" -.\" Redistribution and use in source and binary forms, with or without -.\" modification, are permitted provided that the following conditions -.\" are met: -.\" 1. Redistributions of source code must retain the above copyright -.\" notice, this list of conditions and the following disclaimer. -.\" 2. Redistributions in binary form must reproduce the above copyright -.\" notice, this list of conditions and the following disclaimer in the -.\" documentation and/or other materials provided with the distribution. -.\" 3. All advertising materials mentioning features or use of this software -.\" must display the following acknowledgement: -.\" This product includes software written by Matthew Mondor. -.\" 4. The name of Matthew Mondor may not be used to endorse or promote -.\" products derived from this software without specific prior written -.\" permission. -.\" -.\" THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR -.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES -.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. -.\" IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, -.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -.\" BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF -.\" USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF -.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -.\" -.Dd 11 Dec, 2002 -.Dt MMPOP3D 8 -.Os -.Sh NAME -.Nm mmpop3d -.Nd POP3 server of the -.Xr mmmail 8 -suite -.Sh SYNOPSIS -.Nm mmpop3d Op Ar config_file -.Sh DESCRIPTION -.Nm mmpop3d -consists of the POP3 server of the -.Nm mmmail -suite (See -.Xr mmmail 8 -man page for more information). It's configuration file consists of -.Nm /etc/mmpop3d.conf -(See -.Xr mmpop3d.conf 5 -man page for details). This server requires the -.Nm mmstatd -daemon to be running to use it's statistical and who features (See -.Xr mmstatd 8 , -.Xr mmstat 8 -and -.Xr mmstat 3 -man pages for details). -.Sh AUTHOR -.Nm mmmail -and -.Nm mmpop3d -were written by Matthew Mondor and are Copyright (c) 2001-2004, Matthew Mondor, -All rights reserved. -.Sh FILES -.Bl -tag -width XXXXXXXXXXXXXXXXXXXXXXXXX -compact -.It Pa /usr/local/sbin/mmpop3d -The actual POP3 server binary -.Pp -.It Pa /usr/local/sbin/mmstatd -The statistics librarian daemon binary -.Pp -.It Pa /etc/mmpop3d.conf -The configuration file for -.Nm mmpop3d -.Sh SEE ALSO -.Xr mmmail 8 , -.Xr mmpop3d.conf 5 , -.Xr mmsmtpd 8 , -.Xr mmsmtpd.conf 5 , -.Xr mmstatd 8 , -.Xr mmstat 8 , -.Xr mmstat 3 . -.Sh BUGS -Please report any bug to mmondor@accela.net diff --git a/mmsoftware/mmmail/src/mmpop3d/mmpop3d.c b/mmsoftware/mmmail/src/mmpop3d/mmpop3d.c deleted file mode 100644 index ab1de52..0000000 --- a/mmsoftware/mmmail/src/mmpop3d/mmpop3d.c +++ /dev/null @@ -1,1860 +0,0 @@ -/* $Id: mmpop3d.c,v 1.40 2005/03/05 15:33:33 mmondor Exp $ */ - -/* - * Copyright (C) 2001-2004, Matthew Mondor - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software written by Matthew Mondor. - * 4. The name of Matthew Mondor may not be used to endorse or promote - * products derived from this software without specific prior written - * permission. - * - * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - - - - -/* HEADERS */ - -#include -#include -#include -#include -#include -#include -#include -#ifdef __GLIBC__ -#include -#endif -#include -#include /* strerror(3) */ - -#include -#include -#include - -#include - -#include -#include - -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "mmpop3d.h" - - - - -MMCOPYRIGHT("@(#) Copyright (c) 2001-2004\n\ -\tMatthew Mondor. All rights reserved.\n"); -MMRCSID("$Id: mmpop3d.c,v 1.40 2005/03/05 15:33:33 mmondor Exp $"); - - - - -/* GLOBAL VARIABLES */ -static CONFIG CONF; - -/* Here consists of the commands we support */ -static command commands[] = { - /* LogLevel, Name */ - {3, "NOOP"}, - {3, "RSET"}, - {3, "QUIT"}, - {2, "USER"}, - {4, "PASS"}, - {3, "STAT"}, - {3, "LIST"}, - {3, "RETR"}, - {3, "DELE"}, - {3, "LAST"}, - {3, "TOP"}, - {3, "PAGE"}, - {5, "BEER"}, - {0, NULL} -}; - -/* Now here consists of our auth state functions */ -static int (*state_auth[])(clientenv *) = { - all_noop, /* NOOP */ - NULL, /* RSET */ - all_quit, /* QUIT */ - auth_user, /* USER */ - auth_pass, /* PASS */ - NULL, /* STAT */ - NULL, /* LIST */ - NULL, /* RETR */ - NULL, /* DELE */ - NULL, /* LAST */ - NULL, /* TOP */ - NULL, /* PAGE */ - all_beer /* BEER */ -}; - -/* And our main state function */ -static int (*state_main[])(clientenv *) = { - all_noop, /* NOOP */ - main_rset, /* RSET */ - all_quit, /* QUIT */ - NULL, /* USER */ - NULL, /* PASS */ - main_stat, /* STAT */ - main_list, /* LIST */ - main_retr, /* RETR */ - main_dele, /* DELE */ - main_last, /* LAST */ - main_top, /* TOP */ - main_page, /* PAGE */ - all_beer /* BEER */ -}; - -/* And here are our states */ -static const struct state states[] = { - {state_auth, FALSE, "Authenticate first"}, - {state_main, FALSE, "Already authenticated"} -}; - -/* Used for mmsyslog() */ -static int LOGLEVEL; - -/* Pool used to allocate clientenv nodes */ -static pth_mutex_t clenv_lock; -static pool_t clenv_pool; - -/* Pool used to optimize creating/destroying mmfd mutexes */ -static pth_mutex_t mutexes_lock; -static pool_t mutexes_pool; - -/* Used for fast command lookup */ -static pool_t command_pool; -static hashtable_t command_table; - -/* Global bandwidth shaping fdb context */ -static fdbcontext fdbc; - -/* Pth support for mmfd library (that library rocks my world :) */ -static fdfuncs gfdf = { - malloc, - free, - pth_poll, - pth_read, - pth_write, - pth_sleep, - pth_usleep, - _pth_mutex_create, - _pth_mutex_destroy, - _pth_mutex_lock, - _pth_mutex_unlock, - _pth_thread_yield, - _pth_eintr -}; - - - - -/* MAIN */ - -int -main(int argc, char **argv) -{ - uid_t uid; - gid_t *gids; - char *conf_file = "/etc/mmpop3d.conf"; - int ret = -1, ngids; - long facility; - char *db_host; - bool strlist; - cres_t cres; - carg_t *cargp; - carg_t cargs[] = { - {CAT_STR, CAF_NONE, 1, 255, "LOCK_PATH", CONF.LOCK_PATH}, - {CAT_STR, CAF_NONE, 1, 255, "CHROOT_DIR", CONF.CHROOT_DIR}, - {CAT_STR, CAF_NONE, 1, 255, "PID_PATH", CONF.PID_PATH}, - {CAT_STR, CAF_NONE, 1, 31, "USER", CONF.USER}, - {CAT_STR, CAF_NONE, 1, 255, "GROUPS", CONF.GROUPS}, - {CAT_STR, CAF_NONE, 1, 31, "LOG_FACILITY", CONF.LOG_FACILITY}, - {CAT_STR, CAF_NONE, 1, 1023, "LISTEN_IPS", CONF.LISTEN_IPS}, - {CAT_STR, CAF_NONE, 1, 1023, "SERVER_NAMES", CONF.SERVER_NAMES}, - {CAT_STR, CAF_NONE, 1, 63, "DB_HOST", CONF.DB_HOST}, - {CAT_STR, CAF_NONE, 1, 31, "DB_USER", CONF.DB_USER}, - {CAT_STR, CAF_NONE, 1, 31, "DB_PASSWORD", CONF.DB_PASSWORD}, - {CAT_STR, CAF_NONE, 1, 31, "DB_DATABASE", CONF.DB_DATABASE}, - {CAT_VAL, CAF_NONE, 1, 32, "ASYNC_PROCESSES", &CONF.ASYNC_PROCESSES}, - {CAT_VAL, CAF_NONE, 1, 9999, "ALLOC_BUFFERS", &CONF.ALLOC_BUFFERS}, - {CAT_VAL, CAF_NONE, 0, 4, "LOG_LEVEL", &CONF.LOG_LEVEL}, - {CAT_VAL, CAF_NONE, 1, 65535, "LISTEN_PORT", &CONF.LISTEN_PORT}, - {CAT_VAL, CAF_NONE, 1, 1000, "MAX_ERRORS", &CONF.MAX_ERRORS}, - {CAT_VAL, CAF_NONE, 1, 99999, "MAX_IPS", &CONF.MAX_IPS}, - {CAT_VAL, CAF_NONE, 1, 99999, "MAX_PER_IP", &CONF.MAX_PER_IP}, - {CAT_VAL, CAF_NONE, 0, 99999, "CONNECTION_RATE", - &CONF.CONNECTION_RATE}, - {CAT_VAL, CAF_NONE, 1, 99999, "CONNECTION_PERIOD", - &CONF.CONNECTION_PERIOD}, - {CAT_VAL, CAF_NONE, 1, 99999, "INPUT_TIMEOUT", &CONF.INPUT_TIMEOUT}, - {CAT_VAL, CAF_NONE, 0, 99999, "BANDWIDTH_IN", &CONF.BANDWIDTH_IN}, - {CAT_VAL, CAF_NONE, 0, 99999, "BANDWIDTH_OUT", &CONF.BANDWIDTH_OUT}, - {CAT_VAL, CAF_NONE, 0, 99999, "GBANDWIDTH_IN", &CONF.GBANDWIDTH_IN}, - {CAT_VAL, CAF_NONE, 0, 99999, "GBANDWIDTH_OUT", &CONF.GBANDWIDTH_OUT}, - {CAT_BOOL, CAF_NONE, 0, 0, "RESOLVE_HOSTS", &CONF.RESOLVE_HOSTS}, - {CAT_BOOL, CAF_NONE, 0, 0, "DELAY_ON_ERROR", &CONF.DELAY_ON_ERROR}, - {CAT_BOOL, CAF_NONE, 0, 0, "STATFAIL_LOGIN", &CONF.STATFAIL_LOGIN}, - {CAT_BOOL, CAF_NONE, 0, 0, "STATFAIL_PASSWORD", - &CONF.STATFAIL_PASSWORD}, - {CAT_END, CAF_NONE, 0, 0, NULL, NULL} - }; - cmap_t cmap[] = { - {"LOG_AUTH", LOG_AUTH}, - {"LOG_AUTHPRIV", LOG_AUTHPRIV}, - {"LOG_CRON", LOG_CRON}, - {"LOG_DAEMON", LOG_DAEMON}, - {"LOG_FTP", LOG_FTP}, - {"LOG_LPR", LOG_LPR}, - {"LOG_MAIL", LOG_MAIL}, - {"LOG_NEWS", LOG_NEWS}, - {"LOG_SYSLOG", LOG_SYSLOG}, - {"LOG_USER", LOG_USER}, - {"LOG_UUCP", LOG_UUCP}, - {NULL, 0} - }; - struct async_func afuncs[] = { - {async_checkpw, sizeof(struct async_checkpw_msg)}, - {NULL, 0} - }; - struct mmsql_threadsupport mmsqlfuncs = { - _pth_mutex_create, - _pth_mutex_destroy, - _pth_mutex_lock, - _pth_mutex_unlock, - _pth_thread_yield, - _pth_thread_sleep - }; - mmstat_t vstat; - - /* Set defaults */ - *CONF.CHROOT_DIR = 0; - mm_strcpy(CONF.LOCK_PATH, "/var/run/mmpop3d.lock"); - mm_strcpy(CONF.PID_PATH, "/var/run/mmpop3d.pid"); - mm_strcpy(CONF.USER, "mmmail"); - mm_strcpy(CONF.GROUPS, "mmmail,mmstat"); - mm_strcpy(CONF.LOG_FACILITY, "LOG_AUTHPRIV"); - mm_strcpy(CONF.LISTEN_IPS, "127.0.0.1"); - mm_strcpy(CONF.SERVER_NAMES, "pop3.localhost"); - mm_strcpy(CONF.DB_HOST, "localhost"); - mm_strcpy(CONF.DB_USER, "mmmail"); - mm_strcpy(CONF.DB_PASSWORD, "mmmailpassword"); - mm_strcpy(CONF.DB_DATABASE, "mmmail"); - CONF.ASYNC_PROCESSES = 3; - CONF.ALLOC_BUFFERS = 1; - CONF.LOG_LEVEL = 3; - CONF.LISTEN_PORT = 110; - CONF.MAX_ERRORS = 16; - CONF.MAX_IPS = 64; - CONF.MAX_PER_IP = 1; - CONF.CONNECTION_RATE = 10; - CONF.CONNECTION_PERIOD = 30; - CONF.INPUT_TIMEOUT = 120; - CONF.BANDWIDTH_IN = 4; - CONF.BANDWIDTH_OUT = 12; - CONF.GBANDWIDTH_IN = 0; - CONF.GBANDWIDTH_OUT = 0; - CONF.RESOLVE_HOSTS = FALSE; - CONF.DELAY_ON_ERROR = FALSE; - CONF.STATFAIL_LOGIN = FALSE; - CONF.STATFAIL_PASSWORD = FALSE; - - /* Advertize */ - printf("\r\n+++ %s (%s)\r\n\r\n", DAEMON_NAME, DAEMON_VERSION); - - /* Read config file */ - if (argc == 2) - conf_file = argv[1]; - if (!mmreadcfg(&cres, cargs, conf_file)) { - /* Error parsing configuration file, report which */ - printf("\nError parsing '%s'\n", conf_file); - printf("Error : %s\n", mmreadcfg_strerr(cres.CR_Err)); - if (*(cres.CR_Data)) printf("Data : %s\n", cres.CR_Data); - if ((cargp = cres.CR_Keyword) != NULL) { - printf("Keyword: %s\n", cargp->CA_Keyword); - printf("Minimum: %ld\n", cargp->CA_Min); - printf("Maximum: %ld\n", cargp->CA_Max); - } - if (cres.CR_Line != -1) - printf("Line : %d\n", cres.CR_Line); - printf("\n"); - exit(-1); - } - - /* Ensure that only a single instance with same configuration runs */ - if (lock_check(CONF.LOCK_PATH) == -1) { - printf("\nCouldn't lock file '%s' (already running?)\n\n", - CONF.LOCK_PATH); - exit(-1); - } - - /* Post parsing */ - if (!mmmapstring(cmap, CONF.LOG_FACILITY, &facility)) { - printf("\nUnknown syslog facility %s\n\n", CONF.LOG_FACILITY); - exit(-1); - } - LOGLEVEL = CONF.LOG_LEVEL; - /* Translate to numbers the user and group we were told to run as */ - if ((uid = mmgetuid(CONF.USER)) == -1) { - printf("\nUnknown user %s\n\n", CONF.USER); - exit(-1); - } - if (!(gids = mmgetgidarray(&ngids, CONF.GROUPS))) { - printf("\nOne of following groups unknown: '%s'\n\n", CONF.GROUPS); - exit(-1); - } - if (!(mm_strcmp(CONF.DB_HOST, "localhost"))) db_host = NULL; - else db_host = CONF.DB_HOST; - - /* Finally init everything */ - openlog(DAEMON_NAME, LOG_PID | LOG_NDELAY, facility); - -#ifndef NODROPPRIVS - if ((getuid())) { - printf("\nOnly the super user may start this daemon\n\n"); - syslog(LOG_NOTICE, "* Only superuser can start me"); - exit(-1); - } -#else /* NODROPPRIVS */ - if ((getuid()) == 0) { - printf("\nCompiled with NODROPPRIVS, refusing to run as uid 0\n\n"); - syslog(LOG_NOTICE, "* NODROPPRIVS, refusing to run as uid 0"); - exit(-1); - } -#endif /* !NODROPPRIVS */ - - /* In case we chroot(2), the following is a good idea to execute first */ - mmstat_initialize(); - mmstat_init(&vstat, TRUE, TRUE); - mmstat_transact(&vstat, TRUE); - mmstat(&vstat, STAT_DELETE, 0, "mmpop3d|current|connections"); - mmstat(&vstat, STAT_DELETE, 0, "mmpop3d|who|*"); - mmstat_transact(&vstat, FALSE); - if (!(mmsql_open(db_host, CONF.DB_USER, CONF.DB_PASSWORD, - CONF.DB_DATABASE))) { - printf("\nCould not connect to MySQLd\n\n"); - syslog(LOG_NOTICE, "* Could not connect to MySQLd"); - exit(-1); - } - - make_daemon(CONF.PID_PATH, CONF.CHROOT_DIR); - - /* After possible chroot(2) */ - async_init(afuncs, CONF.ASYNC_PROCESSES, uid, gids, ngids); - - /* Things which shouldn't be part of the async pool processes */ - pth_init(); - async_init_pth(); - pth_mutex_init(&clenv_lock); - pth_mutex_init(&mutexes_lock); - - /* Allocate necessary pools */ - /* Client nodes */ - pool_init(&clenv_pool, "clenv_pool", malloc, free, NULL, NULL, - sizeof(clientenv), - (65536 * CONF.ALLOC_BUFFERS) / sizeof(clientenv), 0, 0); - /* Mutexes pool for mmfd */ - pool_init(&mutexes_pool, "mutexes_pool", malloc, free, NULL, NULL, - sizeof(struct mutexnode), - (16384 * CONF.ALLOC_BUFFERS) / sizeof(struct mutexnode), 0, 0); - /* mmstr nodes */ - strlist = mmstrinit(malloc, free, 16384 * CONF.ALLOC_BUFFERS); - - if (hash_commands(commands, 3) && POOL_VALID(&clenv_pool) && - POOL_VALID(&mutexes_pool) && strlist) { - fdbcinit(&gfdf, &fdbc, CONF.GBANDWIDTH_IN * 1024, - CONF.GBANDWIDTH_OUT * 1024); - mmsql_init(&mmsqlfuncs); - - tcp_server("-ERR Server too busy, try again\r\n", - CONF.SERVER_NAMES, CONF.LISTEN_IPS, uid, gids, ngids, - CONF.MAX_IPS, CONF.MAX_PER_IP, CONF.CONNECTION_RATE, - CONF.CONNECTION_PERIOD, CONF.INPUT_TIMEOUT, - CONF.LISTEN_PORT, CONF.RESOLVE_HOSTS, handleclient); - - mmfreegidarray(gids); - ret = 0; - mmsql_close(); - mmsql_exit(); - } else { - printf("\nOut of memory\n\n"); - syslog(LOG_NOTICE, "* Out of memory"); - } - - if (strlist) - mmstrexit(); - if (HASHTABLE_VALID(&command_table)) - hashtable_destroy(&command_table, FALSE); - if (POOL_VALID(&command_pool)) - pool_destroy(&command_pool); - if (POOL_VALID(&mutexes_pool)) - pool_destroy(&mutexes_pool); - if (POOL_VALID(&clenv_pool)) - pool_destroy(&clenv_pool); - - fdbcdestroy(&fdbc); - kill(0, SIGTERM); - exit(ret); -} - - -/* Here consists of our command functions */ - - -static int -all_noop(clientenv *clenv) -{ - reply(clenv->fdb, TRUE, "Nothing performed"); - - return (STATE_CURRENT); -} - - -static int -all_quit(clientenv *clenv) -{ - int nextstate = STATE_END; - fdbuf *fdb = clenv->fdb; - - if (clenv->login) { - if (!do_update(clenv)) { - reply(fdb, FALSE, "Error applying changes"); - nextstate = STATE_ERROR; - } else - reply(fdb, TRUE, "%s closing connection", clenv->iface->hostname); - } else - reply(fdb, TRUE, "%s closing connection", clenv->iface->hostname); - - return (nextstate); -} - - -static int -all_beer(clientenv *clenv) -{ - reply(clenv->fdb, TRUE, "Here, enjoy!"); - - return (STATE_CURRENT); -} - - -static int -auth_user(clientenv *clenv) -{ - int nextstate = STATE_CURRENT; - fdbuf *fdb = clenv->fdb; - char *cmdline = clenv->buffer, *args[3], addr[64], *tmp; - - if (clenv->mailbox != NULL) - reply(fdb, FALSE, "Username already specified"); - else { - if ((mm_straspl(args, cmdline, 2)) == 2) { - /* Convert first . to @ in username */ - for (tmp = args[1]; *tmp != '\0'; tmp++) { - if (*tmp == '.') { - *tmp = '@'; - break; - } - } - if (valid_address(addr, args[1])) { - if ((clenv->mailbox = mmstrdup(addr)) == NULL) - nextstate = STATE_ERROR; - reply(fdb, TRUE, "Username accepted"); - } else - reply(fdb, FALSE, "Invalid username"); - } else - reply(fdb, FALSE, "Command syntax error"); - } - - return (nextstate); -} - - -static int -auth_pass(clientenv *clenv) -{ - int nextstate = STATE_CURRENT; - fdbuf *fdb = clenv->fdb; - char *cmdline = clenv->buffer, *args[3], line[1024], pwhash[36], id[33]; - MYSQL_RES *mysqlres; - MYSQL_ROW *row; - unsigned long *lengths; - - if (clenv->mailbox != NULL) { - if ((mm_straspl(args, cmdline, 2)) == 2) { - /* Check if user and password are ok, - * switch state if so, or drop client if not. - */ - snprintf(line, 1023, - "SELECT user_id,user_passwd FROM box LEFT JOIN user " - "ON box_user=user_id WHERE box_address='%s' AND " - "user_active=1", - clenv->mailbox); - *id = 0; - *pwhash = 0; - if ((mysqlres = mmsql_query(line, mm_strlen(line))) != NULL) { - if ((mysql_num_rows(mysqlres)) == 1) { - if ((row = (MYSQL_ROW *)mysql_fetch_row(mysqlres)) - != NULL) { - if ((mysql_num_fields(mysqlres)) == 2) { - lengths = mysql_fetch_lengths(mysqlres); - if (row[0] != NULL) { - mm_memcpy(id, row[0], lengths[0]); - id[lengths[0]] = '\0'; - } - if (row[1] != NULL) { - mm_memcpy(pwhash, row[1], lengths[1]); - pwhash[lengths[1]] = '\0'; - } - } - } - } - mysqlres = mmsql_free_result(mysqlres); - } else - DEBUG_PRINTF("auth_pass", "mmsql_query(%s)", line); - if (*pwhash == '\0') { - /* User does not exist */ - mmsyslog(0, LOGLEVEL, "%08X Failed login for %s (unexisting)", - clenv->id, clenv->mailbox); - if (CONF.STATFAIL_LOGIN) - mmstat(&clenv->pstat, STAT_UPDATE, 1, - "mmpop3d|failed|login|%s", clenv->c_ipaddr); - clenv->mailbox = mmstrfree(clenv->mailbox); - nextstate = STATE_END; - reply(fdb, FALSE, "Authentication failiure"); - } else if(checkpw(clenv, args[1], pwhash)) { - char *domptr; - - /* Authentication successful, update box and user tables - * and build messages index - */ - snprintf(line, 1023, - "UPDATE box SET box_out=NOW() WHERE box_address='%s'", - clenv->mailbox); - if (!mmsql_command(line, mm_strlen(line))) - DEBUG_PRINTF("auth_pass", "mmsql_command(%s)", line); - snprintf(line, 1023, - "UPDATE user SET user_activity=NOW()," - "user_logins=user_logins+1 WHERE user_id='%s'", id); - if (!mmsql_command(line, mm_strlen(line))) - DEBUG_PRINTF("auth_pass", "mmsql_command(%s)", line); - clenv->login = TRUE; - if (!do_buildindex(clenv)) { - reply(fdb, FALSE, "Error"); - nextstate = STATE_ERROR; - } - mmsyslog(1, LOGLEVEL, - "%08X Successful login for %s", clenv->id, - clenv->mailbox); - mmstat_transact(&clenv->pstat, TRUE); - mmstat(&clenv->pstat, STAT_UPDATE, 1, "mmpop3d|total|logins"); - mmstat(&clenv->pstat, STAT_UPDATE, 1, "mmmail|box|%s|logins", - clenv->mailbox); - domptr = mm_strchr(clenv->mailbox, '@'); - domptr++; - mmstat(&clenv->pstat, STAT_UPDATE, 1, - "mmmail|domain|%s|logins", domptr); - mmstat_transact(&clenv->pstat, FALSE); - mmstat(&clenv->vstat, STAT_UPDATE, 1, "mmpop3d|who|%s", - clenv->mailbox); - reply(fdb, TRUE, "Authentication successful"); - nextstate = STATE_MAIN; - } else { - /* User exists but the password was wrong */ - mmsyslog(0, LOGLEVEL, "%08X Failed login for %s (existing)", - clenv->id, clenv->mailbox); - mmstat_transact(&clenv->pstat, TRUE); - if (CONF.STATFAIL_LOGIN) - mmstat(&clenv->pstat, STAT_UPDATE, 1, - "mmpop3d|failed|login|%s", clenv->c_ipaddr); - if (CONF.STATFAIL_PASSWORD) - mmstat(&clenv->pstat, STAT_UPDATE, 1, - "mmpop3d|failed|password|%s|%s", - clenv->mailbox, clenv->c_ipaddr); - mmstat_transact(&clenv->pstat, FALSE); - clenv->mailbox = mmstrfree(clenv->mailbox); - nextstate = STATE_END; - reply(fdb, FALSE, "Authentication failiure"); - } - } else - reply(fdb, FALSE, "Command syntax error"); - } else - reply(fdb, FALSE, "Specify username first"); - - return (nextstate); -} - - -static int -main_rset(clientenv *clenv) -{ - int nextstate = STATE_CURRENT; - - if (!init_clientenv(clenv)) - nextstate = STATE_ERROR; - else - reply(clenv->fdb, TRUE, "Reset state"); - - return (nextstate); -} - - -static int -main_stat(clientenv *clenv) -{ - reply(clenv->fdb, TRUE, "%ld %ld", clenv->newmessages, clenv->newsize); - - return (STATE_CURRENT); -} - - -static int -main_list(clientenv *clenv) -{ - int nextstate = STATE_CURRENT; - long i; - fdbuf *fdb = clenv->fdb; - char *args[3]; - - if ((mm_straspl(args, clenv->buffer, 2)) == 2) { - /* Single message specified */ - i = atol(args[1]); - i--; /* Array starts at 0, not 1 */ - if (i < 0 || i > clenv->messages - 1 || clenv->index == NULL || - clenv->index[i].deleted) { - reply(fdb, FALSE, "Out of range"); - REGISTER_ERROR(clenv); - } else - reply(fdb, TRUE, "%ld %ld", i + 1, clenv->index[i].size); - } else { - /* All messages */ - reply(fdb, TRUE, "%ld message(s) (%ld octets)", clenv->newmessages, - clenv->newsize); - if (clenv->index != NULL) { - for (i = 0; i < clenv->messages; i++) { - if (!clenv->index[i].deleted) { - if (!fdbprintf(fdb, "%ld %ld\r\n", i + 1, - clenv->index[i].size)) { - nextstate = STATE_ERROR; - break; - } - } - } - } - if (nextstate != STATE_ERROR) { - if ((fdbwrite(fdb, ".\r\n", 3)) != 3) - nextstate = STATE_ERROR; - } - } - - return (nextstate); -} - - -static int -main_retr(clientenv *clenv) -{ - int nextstate = STATE_CURRENT; - long i; - fdbuf *fdb = clenv->fdb; - char *args[3]; - - if ((mm_straspl(args, clenv->buffer, 2)) == 2) { - i = atol(args[1]); - i--; /* Array starts at 0, not 1 */ - if (i < 0 || !(i < clenv->messages) || clenv->index == NULL || - clenv->index[i].deleted) { - reply(fdb, FALSE, "Out of range"); - REGISTER_ERROR(clenv); - } else { - /* Valid message number to retreive */ - if (clenv->index[i].retreived) { - reply(fdb, FALSE, "Already retreived"); - REGISTER_ERROR(clenv); - } else { - clenv->last = i + 1; - clenv->retreived++; - if (!do_retreive(fdb, &clenv->index[i])) - nextstate = STATE_ERROR; - } - } - } else { - reply(fdb, FALSE, "Command syntax error"); - REGISTER_ERROR(clenv); - } - - return (nextstate); -} - - -static int -main_dele(clientenv *clenv) -{ - long i; - fdbuf *fdb = clenv->fdb; - char *args[3]; - - if ((mm_straspl(args, clenv->buffer, 2)) == 2) { - if (clenv->index != NULL) { - i = atol(args[1]); - i--; /* Array starts at 0, not 1 */ - if (i < 0 || !(i < clenv->messages)) { - reply(fdb, FALSE, "Out of range"); - REGISTER_ERROR(clenv); - } else { - if (!clenv->index[i].deleted) { - clenv->index[i].deleted = TRUE; - clenv->newmessages--; - clenv->newsize -= clenv->index[i].size; - reply(fdb, TRUE, "Maked to delete"); - clenv->deleted++; - } else { - reply(fdb, FALSE, "Already marked to delete"); - REGISTER_ERROR(clenv); - } - } - } else { - reply(fdb, FALSE, "No messages"); - REGISTER_ERROR(clenv); - } - } else { - reply(fdb, FALSE, "Command syntax error"); - REGISTER_ERROR(clenv); - } - - return (STATE_CURRENT); -} - - -static int -main_last(clientenv *clenv) -{ - reply(clenv->fdb, TRUE, "%ld", clenv->last); - - return (STATE_CURRENT); -} - - -static int -main_top(clientenv *clenv) -{ - int nextstate = STATE_CURRENT; - long i1, i2; - fdbuf *fdb = clenv->fdb; - char *args[4]; - - if ((mm_straspl(args, clenv->buffer, 3)) == 3) { - i1 = atol(args[1]); - i2 = atol(args[2]); - i1--; /* Array starts at 0, not 1 */ - if ((i1 < 0) || !(i1 < clenv->messages) || clenv->index == NULL || - clenv->index[i1].deleted) { - reply(fdb, FALSE, "Out of range"); - REGISTER_ERROR(clenv); - } else { - /* Valid message number to retreive */ - if (clenv->index[i1].retreived) { - reply(fdb, FALSE, "Already retreived"); - REGISTER_ERROR(clenv); - } else { - clenv->last = i1 + 1; - if (!do_top(fdb, &clenv->index[i1], i2)) - nextstate = STATE_ERROR; - } - } - } else { - reply(fdb, FALSE, "Command syntax error"); - REGISTER_ERROR(clenv); - } - - return (nextstate); -} - - -static int -main_page(clientenv *clenv) -{ - int nextstate = STATE_CURRENT; - long i1, i2, i3; - fdbuf *fdb = clenv->fdb; - char *args[5]; - - if ((mm_straspl(args, clenv->buffer, 4)) == 4) { - i1 = atol(args[1]); - i2 = atol(args[2]); - i3 = atol(args[3]); - i1--; /* Array starts at 0, not 1 */ - if (i1 < 0 || i2 < 1 || i3 < 1 || i3 > 60 || - !(i1 < clenv->messages) || clenv->index == NULL || - clenv->index[i1].deleted) { - reply(fdb, FALSE, "Out of range"); - REGISTER_ERROR(clenv); - } else { - /* Valid message number to retreive */ - if (clenv->index[i1].retreived) { - reply(fdb, FALSE, "Already retreived"); - REGISTER_ERROR(clenv); - } else { - clenv->last = i1 + 1; - if (!do_page(fdb, &clenv->index[i1], i2, i3)) - nextstate = STATE_ERROR; - } - } - } else { - reply(fdb, FALSE, "Command syntax error"); - REGISTER_ERROR(clenv); - } - - return (nextstate); -} - - - - -/* Function used to return standard RFC result strings to the client, - * and returns FALSE on error. - * NOTE: As we are no longer calling write() directly, but fdbprintf() - * buffering function instead, it is no longer necessary to check the reply() - * return value each time it is called. We made sure to ignore SIGPIPE, - * and we let the main state switcher loop check connection state via - * fdbgets(). - */ -static bool -reply(fdbuf *fdb, bool result, const char *fmt, ...) -{ - char buf[1024], *ok = "+OK", *err = "-ERR", *res = ok; - va_list arg_ptr; - bool ret = FALSE; - - if (!result) - res = err; - - *buf = 0; - va_start(arg_ptr, fmt); - vsnprintf(buf, 1023, fmt, arg_ptr); - va_end(arg_ptr); - - if (fdbprintf(fdb, "%s %s\r\n", res, buf)) ret = TRUE; - - /* XXX should be able to show the clenv->id */ - mmsyslog(3, LOGLEVEL, "> %s (%s)", res, buf); - - return (ret); -} - - -/* Allocate and prepare a clenv. Returns NULL on error */ -static clientenv * -alloc_clientenv(void) -{ - clientenv *clenv; - - pth_mutex_acquire(&clenv_lock, FALSE, NULL); - clenv = (clientenv *)pool_alloc(&clenv_pool, TRUE); - pth_mutex_release(&clenv_lock); - - if (clenv != NULL) { - mmstat_init(&clenv->pstat, TRUE, FALSE); - mmstat_init(&clenv->vstat, TRUE, TRUE); - } - - return (clenv); -} - - -/* Useful on RSET to reset initial clenv state, - * returns TRUE on success or FALSE on error - */ -static bool -init_clientenv(clientenv *clenv) -{ - msgnode *mnode; - long i, t; - - if (clenv != NULL) { - - if ((mnode = clenv->index) != NULL) { - /* Init message index flags to unread/undeleted */ - for (i = 0, t = clenv->messages; i < t; i++) { - mnode[i].deleted = FALSE; - mnode[i].retreived = FALSE; - } - clenv->last = 1; - } else - clenv->last = 0; - - clenv->newmessages = clenv->messages; - clenv->newsize = clenv->size; - - return (TRUE); - } - - return (FALSE); -} - - -/* Frees all ressources allocated by a clenv */ -static clientenv * -free_clientenv(clientenv *clenv) -{ - if (clenv->mailbox != NULL) - mmstrfree(clenv->mailbox); - if (clenv->index != NULL) - free(clenv->index); - - pth_mutex_acquire(&clenv_lock, FALSE, NULL); - pool_free((pnode_t *)clenv); - pth_mutex_release(&clenv_lock); - - return (NULL); -} - - -/* - * Used to ensure that only a single server instance with the same - * configuration runs at the same time. - */ -static int -lock_check(const char *path) -{ - int fd; - - if ((fd = open(path, O_CREAT | O_TRUNC | O_WRONLY, 0600)) != -1) { - if (flock(fd, LOCK_EX | LOCK_NB) == 0) - return 0; - (void) close(fd); - } - - return -1; -} - -/* Used to initialize command hash table */ -static bool -hash_commands(struct command *cmd, size_t min) -{ - int i; - - /* We do not care for any unfreed resources, the program will free them - * and exit if we return FALSE. - */ - if (!pool_init(&command_pool, "command_pool", malloc, free, NULL, NULL, - sizeof(struct commandnode), 64, 1, 0) || - !hashtable_init(&command_table, "commands_table", 64, 1, malloc, - free, commandnode_keycmp, commandnode_keyhash, TRUE)) - return FALSE; - - for (i = 0; cmd->name != NULL; cmd++, i++) { - struct commandnode *nod; - - if ((nod = (struct commandnode *)pool_alloc(&command_pool, FALSE)) - == NULL) - return FALSE; - if ((nod->hash = mm_strpack32(cmd->name, min)) == 0) - return FALSE; - nod->command = cmd; - nod->index = i; - if (!hashtable_link(&command_table, (hashnode_t *)nod, &nod->hash, - sizeof(u_int32_t), TRUE)) { - DEBUG_PRINTF("hash_commands", "hashtable_link(%s)", cmd->name); - return FALSE; - } - } - - return TRUE; -} - - -/* A quick hashtable_hash() replacement which already deals with unique - * 32-bit values - */ -/* ARGSUSED */ -static u_int32_t -commandnode_keyhash(const void *data, size_t len) -{ - return *((u_int32_t *)data); -} - - -/* A quick memcmp() replacement which only needs to compare two 32-bit values - */ -/* ARGSUSED */ -static int -commandnode_keycmp(const void *src, const void *dst, size_t len) -{ - return *((u_int32_t *)src) - *((u_int32_t *)dst); -} - - -/* Returns whether or not supplied address is valid, and if it is return the - * parsed address in the supplied string. String should be at least 64 bytes. - */ -static bool -valid_address(char *to, char *addr) -{ - char *ptr, *ptr2; - - mm_strlower(addr); - - /* First locate required @ */ - for (ptr = addr; *ptr != '\0' && *ptr != '@'; ptr++) ; - if (*ptr == '\0') - return (FALSE); - - /* Then scan to the left */ - for (ptr2 = (ptr - 1); ptr2 >= addr && valid_char(*ptr2); ptr2--) ; - if (ptr2 == (ptr - 1)) - return (FALSE); - ptr2++; - - /* Now validate hostname part */ - ptr++; - if (valid_host(ptr)) { - mm_strncpy(to, ptr2, 63); - return (TRUE); - } - - return (FALSE); -} - - -static bool -valid_host(char *host) -{ - char *ptr; - - mm_strlower(host); - - /* First make sure all characters are valid */ - for (ptr = host; *ptr != '\0'; ptr++) { - if (!valid_char(*ptr)) { - *ptr = '\0'; - break; - } - } - if (ptr == host) - return (FALSE); - - /* Now verify that all parts of the hostname are starting with - * an alphanumeric char - */ - ptr = host; - while (*ptr != '\0') { - if (!isalnum(*ptr) || *ptr == ' ') - return (FALSE); - else { - /* Find next host part */ - while (*ptr != '\0' && *ptr != '.') - ptr++; - if (*ptr == '.') { - ptr++; - continue; - } - if (*ptr == '\0') - break; - } - ptr++; - } - - return (TRUE); -} - - -/* A useful function which returns wether or not a character is valid - * in an email address or hostname - */ -inline static bool -valid_char(char c) -{ - return (isalnum(c) || c == '.' || c == '-' || c == '_'); -} - - -/* Builds the index array of messages for the current mailbox */ -static bool -do_buildindex(clientenv *clenv) -{ - char line[1024]; - bool ok = FALSE; - msgnode *mnode; - int rows, i, numfields; - long size; - MYSQL_RES *mysqlres; - MYSQL_ROW *row; - unsigned long *lengths; - - /* Query mysql server */ -#if defined(MMMAIL_MYSQL) - snprintf(line, 1023, - "SELECT mail_id,mail_size FROM mail WHERE mail_box='%s'", - clenv->mailbox); - numfields = 2; -#elif defined(MMMAIL_FILE) - snprintf(line, 1023, - "SELECT mail_id,mail_size,mail_file FROM mail WHERE " - "mail_box='%s'", clenv->mailbox); - numfields = 3; -#else -#error "One of MMMAIL_FILE or MMMAIL_MYSQL must be #defined!" -#endif - - if ((mysqlres = mmsql_query(line, mm_strlen(line))) != NULL) { - - if ((rows = mysql_num_rows(mysqlres)) != 0) { - /* Allocate array and fill it */ - if ((mnode = (msgnode *)malloc(rows * sizeof(msgnode))) != NULL) { - - ok = TRUE; - clenv->size = 0; - for (i = 0; i < rows; i++) { - if ((row = (MYSQL_ROW *)mysql_fetch_row(mysqlres)) - != NULL) { - if ((mysql_num_fields(mysqlres)) == numfields) { - lengths = mysql_fetch_lengths(mysqlres); - if (row[0] != NULL) { - /* Note: this assumes that the field is 20 - * bytes - */ - mm_memcpy(mnode[i].id, row[0], lengths[0]); - mnode[i].id[lengths[0]] = '\0'; - mnode[i].retreived = mnode[i].deleted = FALSE; - if (row[1] != NULL) { - mm_memcpy(line, row[1], lengths[1]); - line[lengths[1]] = '\0'; - size = atol(line); - mnode[i].size = size; - clenv->size += size; - } else { - ok = FALSE; - break; - } -#if defined(MMMAIL_FILE) - if (row[2] != NULL) { - mm_memcpy(mnode[i].file, row[2], - lengths[2]); - mnode[i].file[lengths[2]] = '\0'; - } else { - ok = FALSE; - break; - } -#endif - } else { - ok = FALSE; - break; - } - } else { - DEBUG_PRINTF("do_buildindex", - "mysql_num_fields() != %d", numfields); - ok = FALSE; - break; - } - } else { - DEBUG_PRINTF("do_buildindex", "mysql_fetch_rows()"); - ok = FALSE; - break; - } - } - - if (ok) { - clenv->index = mnode; - clenv->messages = rows; - clenv->newmessages = clenv->messages; - clenv->newsize = clenv->size; - } else - free(mnode); - - } else - DEBUG_PRINTF("do_buildindex", - "malloc(%d)", rows * sizeof(msgnode)); - } else - ok = TRUE; - - mmsql_free_result(mysqlres); - } else - DEBUG_PRINTF("do_buildindex", "mmsql_query(%s)", line); - - return (ok); -} - - -/* Loads a message body, either using mmap(2) for file storage mode, or a - * MySQL query. This function along with do_message_free() are called by - * do_retreive(), do_top() and do_page(). - */ -static bool -do_message_load(msgdata *mdata, msgnode *mnode) -{ -#if defined(MMMAIL_MYSQL) - - char line[1024]; - MYSQL_RES *mysqlres; - MYSQL_ROW *row; - unsigned long *lengths; - - snprintf(line, 1024, "SELECT mail_data FROM mail WHERE mail_id=%s", - mnode->id); - if ((mysqlres = mmsql_query(line, mm_strlen(line))) != NULL) { - if ((mysql_num_rows(mysqlres)) == 1) { - if ((row = (MYSQL_ROW *)mysql_fetch_row(mysqlres)) != NULL) { - if ((mysql_num_fields(mysqlres)) == 1) { - lengths = mysql_fetch_lengths(mysqlres); - if (row[0] != NULL) { - mdata->internal = mysqlres; - mdata->body = (char *)row[0]; - mdata->size = lengths[0]; - - return TRUE; - } else - DEBUG_PRINTF("do_message_load", "row[0] == NULL"); - } else - DEBUG_PRINTF("do_message_load", "mysql_num_fields != 1"); - } else - DEBUG_PRINTF("do_message_load", "mysql_fetch_row()"); - } else - DEBUG_PRINTF("do_message_load", "mysql_num_rows != 1"); - mmsql_free_result(mysqlres); - } else - DEBUG_PRINTF("do_message_load", "mmsql_query(%s)", line); - -#elif defined(MMMAIL_FILE) - - int fd; - - if ((fd = open(mnode->file, O_RDONLY)) != -1) { - struct stat st; - - if (fstat(fd, &st) == 0) { - mdata->size = (size_t)st.st_size; - if ((mdata->internal = mmap(NULL, mdata->size, PROT_READ, - MAP_FILE | MAP_PRIVATE, fd, 0)) != MAP_FAILED) { - mdata->body = mdata->internal; - (void) close(fd); - - return TRUE; - } else - DEBUG_PRINTF("do_message_load", "mmap() == %s", - strerror(errno)); - } else - DEBUG_PRINTF("do_message_load", "fstat() == %s", strerror(errno)); - (void) close(fd); - } else - DEBUG_PRINTF("do_message_load", "open(%s) == %s", mnode->file, - strerror(errno)); - -#endif - - mdata->internal = NULL; - mdata->body = NULL; - mdata->size = 0; - - return FALSE; -} - -/* Frees a previously loaded message body. */ -static void -do_message_free(msgdata *mdata) -{ -#if defined(MMMAIL_MYSQL) - mmsql_free_result(mdata->internal); -#elif defined(MMMAIL_FILE) - (void) munmap(mdata->internal, mdata->size); -#endif - - mdata->internal = NULL; - mdata->body = NULL; - mdata->size = 0; -} - - -/* Query database and output the message contents to fdb, with . */ -static bool -do_retreive(fdbuf *fdb, msgnode *mnode) -{ - bool ok = FALSE; - msgdata mdata; - - if (do_message_load(&mdata, mnode)) { - if (reply(fdb, TRUE, "%u octets", (unsigned int)mdata.size)) { - fdbflushw(fdb); - if (msgwrite(fdb, mdata.body, mdata.size)) { - if ((fdbwrite(fdb, "\r\n.\r\n", 5)) == 5) { - mnode->retreived = TRUE; - ok = TRUE; - } - } - if (!ok) { - if ((fdbwrite(fdb, "\r\n.\r\n", 5)) == 5) - ok = TRUE; - } - } - do_message_free(&mdata); - } - - return (ok); -} - - -/* Query database and output the message contents to fdb, with . */ -static bool -do_top(fdbuf *fdb, msgnode *mnode, long lines) -{ - register char *tmp, *to; - bool ok = FALSE; - long i; - msgdata mdata; - - if (do_message_load(&mdata, mnode)) { - if (reply(fdb, TRUE, - "%lu octets, also try PAGE " - "", mdata.size)) { - /* First display all message headers */ - to = tmp = mdata.body; - to += mdata.size; - /* Find end of headers */ - i = 0; - while (tmp < to && *tmp != '\0' && i < 2) { - if (*tmp == '\r') - i++; - else if (*tmp != '\n') - i = 0; - tmp++; - } - /* Find out where ends the specified line */ - while (tmp < to && *tmp != '\0' && lines > 0) { - if (*tmp == '\r') - lines--; - tmp++; - } - /* Finally print out result */ - fdbflushw(fdb); - if (msgwrite(fdb, mdata.body, tmp - mdata.body)) { - if ((fdbwrite(fdb, "\r\n.\r\n", 5)) == 5) { - /* mnode->retreived = TRUE; */ - ok = TRUE; - } - } - if (!ok) { - if ((fdbwrite(fdb, "\r\n.\r\n", 5)) == 5) - ok = TRUE; - } - } - do_message_free(&mdata); - } - - return (ok); -} - - -/* This function is like do_top but more powerful */ -static bool -do_page(fdbuf *fdb, msgnode *mnode, long page, long lines) -{ - register char *tmp, *from, *to; - bool ok = FALSE; - long lfrom, i; - msgdata mdata; - - /* Evaluate what the range should be in lines */ - if (page == 1) - lfrom = 0; - else - lfrom = (page - 1) * lines; - - if (do_message_load(&mdata, mnode)) { - if (reply(fdb, TRUE, "%lu octets", mdata.size)) { - /* Find out where starts and ends wanted range */ - i = lfrom; - to = tmp = mdata.body; - to += mdata.size; - while (tmp < to && *tmp != '\0' && i > 0) { - if (*tmp == '\r') - i--; - tmp++; - } - if (tmp < to) { - from = tmp; - i = lines; - while (tmp < to && *tmp != '\0' && i > 0) { - if (*tmp == '\r') - i--; - tmp++; - } - if (tmp < to) - to = tmp; - if (from != to) { - fdbflushw(fdb); - if (msgwrite(fdb, from, to - from)) { - if ((fdbwrite(fdb, "\r\n.\r\n", 5)) == 5) - ok = TRUE; - } - } - } - if (!ok) { - if ((fdbwrite(fdb, "\r\n.\r\n", 5)) == 5) - ok = TRUE; - } - } - do_message_free(&mdata); - } - - return (ok); -} - - - -/* Updates the database to delete messages that were marked as deleted, - * and uses a hack to make sure to update the mailbox's current sizes - * for messages that were successfully deleted only - */ -static bool -do_update(clientenv *clenv) -{ - char line[1024]; - bool ok = TRUE; - long i, t, messages, size; - msgnode *mnode; - - if ((t = clenv->messages) > 0) { - - /* Deleted messages and bytes */ - messages = 0; - size = 0; - - mnode = clenv->index; - /* Delete messages that were marked, use a lock between our two calls - * which prevents mmsmtpd or other threads to interfere in-between - */ - mmsql_glock("mmmail_boxmail"); - for (i = 0; i < t; i++) { - if (mnode[i].deleted) { - snprintf(line, 1023, "DELETE FROM mail WHERE mail_id=%s", - mnode[i].id); - if (!mmsql_command(line, mm_strlen(line))) { - DEBUG_PRINTF("do_update", "mmsql_command(%s)", line); - ok = FALSE; - break; - } else { -#if defined(MMMAIL_FILE) - /* Also unlink associated file */ - if (unlink(mnode[i].file) == -1) - mmsyslog(0, LOGLEVEL, "unlink(%s) == %s", - mnode[i].file, strerror(errno)); -#endif - messages++; - size += mnode[i].size; - } - } - } - if (messages > 0 && size > 0) { - /* Update mailbox size */ - snprintf(line, 1023, - "UPDATE box SET box_size=box_size-%ld," - "box_msgs=box_msgs-%ld WHERE box_address='%s'", - size, messages, clenv->mailbox); - if (!mmsql_command(line, mm_strlen(line))) - DEBUG_PRINTF("do_update", "mmsql_command(%s)", line); - } - mmsql_gunlock("mmmail_boxmail"); - } - - return (ok); -} - - -/* Performs adequate message lines "." to ".." reformatting while writing, - * uses fdbrwrite() as those events are quite rare, to be more efficient. - * This assumes that any pending buffered bytes were first flushed. - */ -static bool -msgwrite(fdbuf *fdb, const void *buf, size_t size) -{ - const char *from, *from2, *to, *to2; - ssize_t l = 0; - bool ok = TRUE; - - from = from2 = buf; - to = to2 = buf + size; - - to -= 3; - while (from < to && from2 < to) { - if (from[0] == '\n' && from[1] == '.' && from[2] == '\r') { - if ((l = fdbrwrite(fdb, from2, from - from2)) < 0) - break; - if ((l = fdbrwrite(fdb, "\n..\r", 4)) < 0) - break; - from2 = from + 3; - } - from++; - } - if (from2 < to2) - l = fdbrwrite(fdb, from2, to2 - from2); - if (l < 1) - ok = FALSE; - - return (ok); -} - - - -/* This is the main function that is called to serve a client. - * It comports the main loop and state switcher. - */ -static int -handleclient(unsigned long id, int fd, clientlistnode *clientlnode, - struct iface *iface, struct async_clenv *aclenv) -{ - char buffer[1024], ipaddr[20], *tmp; - int len, state, nstate, timeout, dstatus; - clientenv *clenv; - struct sockaddr_in *sinaddr; - fdbuf *fdb; - int64_t data_in, data_out; - unsigned long time_total, messages_out, deleted; - time_t time_start, time_end; - - data_in = data_out = messages_out = deleted = 0; - dstatus = MMS_RESOURCE_ERROR; - timeout = clientlnode->timeout; - clenv = NULL; - - /* Obtain IP address of client */ - sinaddr = (struct sockaddr_in *)&clientlnode->client; - if ((tmp = inet_ntoa(sinaddr->sin_addr))) mm_strncpy(ipaddr, tmp, 19); - else mm_strncpy(ipaddr, "0.0.0.0", 8); - - if (clientlnode->hostname) - /* Log user's IP and hostname */ - mmsyslog(1, LOGLEVEL, "%08X Connect: [%s] - (%s)", id, ipaddr, - clientlnode->hostname); - else - /* Log user's IP */ - mmsyslog(1, LOGLEVEL, "%08X Connect: [%s]", id, ipaddr); - - time_start = time(NULL); - - - if ((fdb = fdbopen(&gfdf, &fdbc, fd, 8192, 8192, CONF.BANDWIDTH_IN * 1024, - CONF.BANDWIDTH_OUT * 1024, timeout, timeout, FALSE)) - != NULL) { - - /* Allocate our clientenv to share with state functions */ - if ((clenv = alloc_clientenv()) != NULL) { - - /* Query some configuration options from the MySQL server, - * such as max_rcpts, max_mesg_lines, max_mesg_size, hostname - */ - clenv->fdb = fdb; - clenv->buffer = buffer; - clenv->errors = 0; - clenv->timeout = timeout; - clenv->c_hostname = clientlnode->hostname; - clenv->c_ipaddr = ipaddr; - clenv->id = id; - clenv->iface = iface; - clenv->aclenv = aclenv; - - reply(fdb, TRUE, "%s (%s (%s)) Service ready", iface->hostname, - DAEMON_NAME, DAEMON_VERSION); - state = STATE_AUTH; - dstatus = MMS_NORMAL; - - mmstat(&clenv->vstat, STAT_UPDATE, 1, - "mmpop3d|current|connections"); - mmstat(&clenv->pstat, STAT_UPDATE, 1, - "mmpop3d|total|connections"); - - /* Main state switcher loop */ - for (;;) { - register struct commandnode *nod; - u_int32_t chash; - - fdbflushw(fdb); - if ((len = fdbgets(fdb, buffer, 1023, FALSE)) > -1) { - - /* If there were too many errors, exit accordingly */ - if (clenv->errors > CONF.MAX_ERRORS) { - reply(fdb, FALSE, "Too many errors"); - dstatus = MMS_MANY_ERRORS; - break; - } - /* Verify if command matches an existing one */ - nod = NULL; - if ((chash = mm_strpack32(buffer, 3)) != 0) - nod = (struct commandnode *)hashtable_lookup( - &command_table, &chash, sizeof(u_int32_t)); - if (nod != NULL) { - register int (*func)(clientenv *); - - mmsyslog(nod->command->loglevel, LOGLEVEL, - "%08X < %s", id, buffer); - - if ((func = states[state].functions[nod->index]) - != NULL) { - - /* Valid command, process it in current state */ - nstate = func(clenv); - if (nstate == STATE_END || nstate == STATE_ERROR) - break; - if (nstate != STATE_CURRENT) - state = nstate; - - } else { - /* Unimplemented command for this state */ - REGISTER_ERROR(clenv); - if (!reply(fdb, states[state].errcode, - states[state].errtext)) - break; - } - - } else { - mmsyslog(3, LOGLEVEL, "%08X < %s", id, buffer); - if (!reply(fdb, FALSE, - "Syntax error or unknown command")) - break; - REGISTER_ERROR(clenv); - } - - } else { - /* Input error */ - if (len == FDB_TIMEOUT) { - dstatus = MMS_INPUT_TIMEOUT; - break; - } else if (len == FDB_ERROR) { - dstatus = MMS_INPUT_ERROR; - break; - } else { - dstatus = MMS_UNKNOWN; - break; - } - } - - } - - messages_out = clenv->retreived; - deleted = clenv->deleted; - data_in = FDBBYTESR(fdb); - data_out = FDBBYTESW(fdb); - - mmstat_transact(&clenv->vstat, TRUE); - if (clenv->mailbox != NULL) - mmstat(&clenv->vstat, STAT_UPDATE, -1, "mmpop3d|who|%s", - clenv->mailbox); - mmstat(&clenv->vstat, STAT_UPDATE, -1, - "mmpop3d|current|connections"); - mmstat_transact(&clenv->vstat, FALSE); - - mmstat_transact(&clenv->pstat, TRUE); - if (clenv->mailbox != NULL) { - char *domptr; - - /* Record per-mailbox statistics */ - mmstat(&clenv->pstat, STAT_UPDATE, messages_out, - "mmmail|box|%s|messages-out", clenv->mailbox); - mmstat(&clenv->pstat, STAT_UPDATE, deleted, - "mmmail|box|%s|messages-deleted", clenv->mailbox); - mmstat(&clenv->pstat, STAT_UPDATE, data_in, - "mmmail|box|%s|bytes-in", clenv->mailbox); - mmstat(&clenv->pstat, STAT_UPDATE, data_out, - "mmmail|box|%s|bytes-out", clenv->mailbox); - /* And per-domain statistics. The '@' character is guaranteed - * to be there because we forged the address ourselves from the - * user login string, previously. - */ - domptr = mm_strchr(clenv->mailbox, '@'); - domptr++; - mmstat(&clenv->pstat, STAT_UPDATE, messages_out, - "mmmail|domain|%s|messages-out", domptr); - mmstat(&clenv->pstat, STAT_UPDATE, data_out, - "mmmail|domain|%s|bytes-out", domptr); - /* We also count for the domain the extra input bytes for - * their mailboxes in the POP3 protocol. We do not do this - * for SMTP however where a session may serve to deliver - * messages to several domains, in which case we only record - * the actual message input size. - */ - mmstat(&clenv->pstat, STAT_UPDATE, data_in, - "mmmail|domain|%s|bytes-in", domptr); - } - mmstat(&clenv->pstat, STAT_UPDATE, messages_out, - "mmpop3d|total|messages-out"); - mmstat(&clenv->pstat, STAT_UPDATE, deleted, - "mmpop3d|total|deletions"); - mmstat(&clenv->pstat, STAT_UPDATE, data_in, - "mmpop3d|total|bytes-in"); - mmstat(&clenv->pstat, STAT_UPDATE, data_out, - "mmpop3d|total|bytes-out"); - mmstat_transact(&clenv->pstat, FALSE); - - /* Free our state-shared clenv */ - clenv = free_clientenv(clenv); - } else - DEBUG_PRINTF("handleclient", "alloc_clientenv()"); - - fdbclose(fdb); - } else - DEBUG_PRINTF("handleclient", "fdbopen(%d)", fd); - - - /* Log results */ - time_end = time(NULL); - time_total = time_end - time_start; - mmsyslog(1, LOGLEVEL, - "%08X Closed: [%s] - (Seconds: %lu, Data in: %lld out: %lld, " - "Retreived: %lu, Deleted: %lu, Status: %s)", - id, ipaddr, time_total, data_in, data_out, messages_out, - deleted, MMS_RSTRING(dstatus)); - - return (0); -} - - -/* mmfd library thread support functions */ - - -static void * -_pth_mutex_create(void) -{ - struct mutexnode *mnod; - - pth_mutex_acquire(&mutexes_lock, FALSE, NULL); - mnod = (struct mutexnode *)pool_alloc(&mutexes_pool, FALSE); - pth_mutex_release(&mutexes_lock); - - if (mnod != NULL) - pth_mutex_init(&mnod->mutex); - - return ((void *)mnod); -} - - -static void * -_pth_mutex_destroy(void *mtx) -{ - /* struct mutexnode *mnod = mtx; */ - - /* pth_mutex_destroy(&mnod->mutex); */ - pth_mutex_acquire(&mutexes_lock, FALSE, NULL); - pool_free(mtx); - pth_mutex_release(&mutexes_lock); - - return (NULL); -} - - -static void -_pth_mutex_lock(void *mtx) -{ - struct mutexnode *mnod = mtx; - - pth_mutex_acquire(&mnod->mutex, FALSE, NULL); -} - - -static void -_pth_mutex_unlock(void *mtx) -{ - struct mutexnode *mnod = mtx; - - pth_mutex_release(&mnod->mutex); -} - - -static void -_pth_thread_yield(void) -{ - pth_yield(NULL); -} - - -static void -_pth_thread_sleep(int secs) -{ - pth_sleep(secs); -} - - -static bool -_pth_eintr(void) -{ - if (errno == EINTR) - return TRUE; - - return FALSE; -} - - -/* Here are our real asynchroneous functions, called by the slave processes */ - - -/* crypt(3) is not thread-safe, it uses lame static buffer. - * Let's change this, using another process with our thread-safe - * synchronization provided by mmserver(3)'s async subsystem. - * This also delegates DES, MD5 or SHA1 hashing this making the threaded - * process more responsive. - */ -static void -async_checkpw(struct async_msg *msg) -{ - struct async_checkpw_msg *amsg = (void *)msg; - char *hash; - - if ((hash = crypt(amsg->un.args.passwd, amsg->un.args.hash)) != NULL) { - if ((mm_strcmp(hash, amsg->un.args.hash)) == 0) - amsg->un.res.matching = TRUE; - else - amsg->un.res.matching = FALSE; - } else - amsg->un.res.matching = FALSE; -} - - -/* And our wrapper functions calling the asynchroneous device */ - - -static bool -checkpw(clientenv *clenv, const char *passwd, const char *hash) -{ - struct async_checkpw_msg *amsg = (void *)clenv->aclenv->msg; - - mm_strncpy(amsg->un.args.passwd, passwd, 255); - mm_strncpy(amsg->un.args.hash, hash, 47); - async_call(clenv->aclenv, ASYNC_CHECKPW); - - return (amsg->un.res.matching); -} diff --git a/mmsoftware/mmmail/src/mmpop3d/mmpop3d.conf.5 b/mmsoftware/mmmail/src/mmpop3d/mmpop3d.conf.5 deleted file mode 100644 index 91829d6..0000000 --- a/mmsoftware/mmmail/src/mmpop3d/mmpop3d.conf.5 +++ /dev/null @@ -1,349 +0,0 @@ -.\" $Id: mmpop3d.conf.5,v 1.8 2005/03/05 15:33:33 mmondor Exp $ -.\" -.\" Copyright (C) 2001-2004, Matthew Mondor -.\" All rights reserved. -.\" -.\" Redistribution and use in source and binary forms, with or without -.\" modification, are permitted provided that the following conditions -.\" are met: -.\" 1. Redistributions of source code must retain the above copyright -.\" notice, this list of conditions and the following disclaimer. -.\" 2. Redistributions in binary form must reproduce the above copyright -.\" notice, this list of conditions and the following disclaimer in the -.\" documentation and/or other materials provided with the distribution. -.\" 3. All advertising materials mentioning features or use of this software -.\" must display the following acknowledgement: -.\" This product includes software written by Matthew Mondor. -.\" 4. The name of Matthew Mondor may not be used to endorse or promote -.\" products derived from this software without specific prior written -.\" permission. -.\" -.\" THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR -.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES -.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. -.\" IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, -.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -.\" BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF -.\" USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF -.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -.\" -.Dd 11 July, 2003 -.Dt MMPOP3D.CONF 5 -.Os mmsoftware -.Sh NAME -.Nm mmpop3d.conf -.Nd -.Xr mmpop3d.conf 5 -configuration file for -.Xr mmpop3d 8 -of -.Xr mmmail 8 -suite -.Sh SECURITY CONSIDERATIONS -This -.Nm /etc/mmpop3d.conf -configuration file should only be readable and writeable by the superuser, -as the MySQL access password is stored in it. -.Bd -literal -offset indent --rw------- 1 root wheel 2510 Sep 8 00:22 /etc/mmpop3d.conf -.Ed -.Pp -Please read the -.Xr mmmail 8 -man page for more information on other security consideration issues. -.Sh DESCRIPTION -The -.Nm /etc/mmpop3d.conf -file may contain one or more keyword/value pairs per line, empty lines or -comments. A ';' or '#' character causes all other characters to be considered -as a comment, and to be ignored, until the end of the current line. It is -very important to enclose the value for a keyword in double quotes ('"' -characters) if the following characters are found in it: ';', '#', space and -tab. Here are documented the various possible keywords and their -allowed values, as well as their defaults. -.Pp -.Nm Daemon administration parameters -.Pp -.Bl -tag -width XXXXXXXX -offset indent -compact -.It Nm ASYNC_PROCESSES Ar "number" -The server internally launches a pool of asynchroneous processes to serve -functions which could block the main process for too long (this in fact -also means all threads with userspace threads such as provided by the Pth -library). This includes resolving hostnames and calculating MD5 hashes. -This also allows multiprocessor systems to have an advantage when executing -these functions. It should be noted that two additional file descriptors are -required in the main process for each async process of the pool. The -administrator will hence generally configure -.Fa MAX_IPS , -.Fa MAX_PER_IP -and/or the maximum allowed number of file descriptors allowed for a process by -the kernel, accordingly. -.Pp -.It Nm CHROOT_DIR Ar "directory" -Location of the directory where the daemon optionally should -.Xr chroot 2 -in, or "" if it should not. Note that special configuration is generally -required for this. All files and directories the server will need access to -should also reside in that directory, notably in -.Nm /etc/ , -such as -.Nm /etc/resolv.conf , /etc/hosts -and -.Nm /etc/group , -depending on the system's libc. The -.Xr mmstat 3 -and -.Xr syslog 3 -facilities will be initialized before -.Xr chroot 2 -is called to ensure that they continue working properly. Similarly, -connection to the MySQL server is also established first. Note that -if the connection is lost and has to be re-established to the server, -to a local socket which is outside of the new root, it will not be able -to re-establish it, although normally local UNIX socket connections are -reliable. This is not a problem with remote server connections, where -re-establishing may happen more often, and the jail won't matter. -.Pp -.It Nm LOCK_PATH Ar "file" -Location of lock file which should be used to determine if the daemon is -already running. Note that this path resides outside of the -.Xr chroot 2 -if enabled using -.Fa CHROOT_DIR . -.Pp -.It Nm PID_PATH Ar "file" -Location where -.Nm mmpop3d -writes it's pid file. This consists of a small file holding the process ID of -the main process, which can be used by scripts or the administrator to kill -the daemon properly. This file will be written before -.Xr chroot 2 -is called, if -.Fa CHROOT_DIR -was enabled. -.Pp -.It Nm USER Ar "user" -Unprivileged user server process should run as. -.Pp -.It Nm GROUPS Ar "group,..." -Groups process should be part of, separated by commas, without spaces. It is -important to also include here the 'mmstat' group. -.Pp -.It Nm ALLOC_BUFFERS Ar "number" -On systems which are expected to operate under high load, serving a large -number of connections simultaneously, it may be ideal to increase this -variable. This pre-allocates buffers using the -.Xr mmpool 3 -library. For a system serving 64 connections, a suitable value may be 4. This -feature will mostly cause noticeable speed increase on systems which do not -provide very efficient memory allocators, such as glibc on Linux. The best -is to perform various tests and evaluate the required number to use here. -.Pp -Note that this currently has little significance with the current -implementation since scaling was enhanced. However, if it is known that a high -number of connections are immediately expected at system launch time, it may -be useful to immediately scale up the server using this parameter. -.Pp -.It Nm LOG_FACILITY Ar "facility" -Syslog facility which should be used for error logging. Should normally be -one of -.Sy LOG_AUTH , LOG_AUTHPRIV , LOG_CRON , LOG_DAEMON , -.Sy LOG_FTP , LOG_KERN , LOG_LPR , LOG_MAIL , -.Sy LOG_NEWS , LOG_SYSLOG , LOG_USER , -or -.Sy LOG_UUCP . -See -.Xr syslog 3 -man page for more information. -.Pp -.It Nm LOG_LEVEL Ar "number" -This consists of the logging verbosity level, controlling which gravity of -messages should be logged through the -.Xr syslog 3 -.Ar LOG_FACILITY -facility. Here consists of the allowable levels and their meaning: -.Bd -literal -offset indent -0 = critical and important messages only -1 = connections are logged -2 = informational logging (RETR and DELE also) -3 = verbose logging (all commands and replies but - PASS) -4 = debugging (even PASS with password) -.Ed -.El -.Pp -.Nm TCP server parameters -.Pp -.Bl -tag -width XXXXXXXX -offset indent -compact -.It Nm LISTEN_IPS Ar "n.n.n.n ..." -IPv4 addresses we should -.Xr bind 2 -to, separated by spaces. These typically consist of the addresses bound to -a wanted interface to accept connections from. 0.0.0.0 may be used if -connections from all interfaces should be allowed. Obviously, if more than -a single address is specified, they should be enclosed in double quotes. -.Pp -.It Nm SERVER_NAMES Ar "name ..." -Advertized server hostnames, separated by spaces, there should be the same -number of entries than for -.Ar LISTEN_IPS , -and of course also should be enclosed in double quotes if more than one -hostname is specified. These can be any wanted hostnames. -.Pp -.It Nm LISTEN_PORT Ar "number" -inet TCP port we should be listening for connections on. -.Pp -.It Nm RESOLVE_HOSTS Ar "boolean" -Resolving clients hostnames for logging can make the system slow depending on -DNS server reliability and service load. boolean should be TRUE or FALSE, -respectively. -.Pp -.It Nm MAX_ERRORS Ar "number" -Maximum number of bad commands user may issue per session before being dropped. -.Pp -.It Nm DELAY_ON_ERROR Ar "boolean" -If set to TRUE, at each bad command or error (see -.Ar MAX_ERRORS ) -a delay will be forced before accepting another command from that client. -At each error the delay will increase by one second. -.Pp -.It Nm MAX_IPS Ar "number" -Total maximum number of simultanious different IP addresses we allow -connections from. -.Pp -.It Nm MAX_PER_IP Ar "number" -Maximum simultanious connections per single IP address we accept. -.Pp -.It Nm CONNECTION_RATE Ar "number" -Maximum amount of connections to accept per IP address within -.Fa CONNECTION_PERIOD . -Can be 0 if no connection rate limits are wanted. -.Pp -.It Nm CONNECTION_PERIOD Ar "seconds" -The number of seconds within which a maximum of -.Fa CONNECTION_RATE -connections are accepted for an IP address. -.Pp -.It Nm INPUT_TIMEOUT Ar "seconds" -Maximum input/output timeout in seconds for the client connection. -The server will disconnect if this is exceeded. -.Pp -.It Nm BANDWIDTH_IN Ar "kilobytes" -This consists of the maximum bandwidth speed limit, in kilobytes per second, -to allow reading at from the client connection. 0 disables bandwidth -shaping for this entry. So this restricts the speed we may receive messages at. -.Pp -.It Nm BANDWIDTH_OUT Ar "kilobytes" -This consists of the maximum bandwidth speed limit, in kilobytes per second, -to allow writing to the client connection. 0 disables bandwidth -shaping for this entry. -.Pp -.It Nm GBANDWIDTH_IN Ar "kilobytes" -The global maximum bandwidth speed limit, in kilobytes per second, for the -whole server, at which it is allowed to read from clients. 0 for no limit. -.Pp -.It Nm GBANDWIDTH_OUT Ar "kilobytes" -The global maximum bandwidth speed limit, in kilobytes per second, for the -whole server, at which it is allowed to write to clients. 0 for no limit. -.Pp -.El -.Pp -.Nm POP3 security options -.Pp -.Bl -tag -width XXXXXXXX -offset indent -compact -.It Nm STATFAIL_LOGIN Ar "boolean" -If set to TRUE, mmpop3d will record statistics using the -.Xr mmstat 3 -facility for addresses which fail logins. -.Pp -.It Nm STATFAIL_PASSWORD Ar "boolean" -If TRUE, will record statistics using -.Xr mmstat 3 -for addresses and existing user which failed entering a proper password. -This option is mutually inclusive with -.Ar STATFAIL_LOGIN . -.El -.Pp -.Nm MySQL related parameters -.Pp -.Bl -tag -width XXXXXXXX -offset indent -compact -.It Nm DB_HOST Ar "hostname" -Host name of MySQL server we should connect to ("localhost" for UNIX socket). -.Pp -.It Nm DB_USER Ar "user" -MySQL user which has all access on -.Ar DB_DATABASE -MySQL database. -.Pp -.It Nm DB_PASSWORD Ar "password" -MySQL authentication password for -.Ar DB_USER -user. -.Pp -.It Nm DB_DATABASE Ar "database" -Name of mmmail MySQL database which the -.Ar DB_USER -user owns, typically "mmmail". -.Pp -.El -.Sh DEFAULTS -The following defaults are used: -.Pp -.Bd -literal -offset indent -compact -ASYNC_PROCESSES 3 -CHROOT_DIR "" -LOCK_PATH "/var/run/mmpop3d.lock" -PID_PATH "/var/run/mmpop3d.pid" -USER mmmail -GROUPS mmmail,mmstat -LOG_FACILITY LOG_AUTHPRIV -SERVER_NAMES "pop3.localhost" -LISTEN_IPS "127.0.0.1" -ALLOC_BUFFERS 1 -LOG_LEVEL 3 -LISTEN_PORT 110 -MAX_ERRORS 16 -DELAY_ON_ERROR FALSE -MAX_IPS 64 -MAX_PER_IP 1 -CONNECTION_RATE 10 -CONNECTION_PERIOD 30 -INPUT_TIMEOUT 120 -BANDWIDTH_IN 4 -BANDWIDTH_OUT 12 -GBANDWIDTH_IN 0 -GBANDWIDTH_OUT 0 -RESOLVE_HOSTS FALSE -DB_HOST "localhost" -DB_USER "mmmail" -DB_PASSWORD "mmmailpassword" -DB_DATABASE "mmmail" -STATFAIL_LOGIN FALSE -STATFAIL_PASSWORD FALSE -.Ed -.Sh AUTHOR -.Nm mmmail -suite was written by Matthew Mondor and is Copyright (c) 2001-2004 -Matthew Mondor, All rights reserved. -.Sh FILES -.Bl -tag -width XXXXXXXXXXXXXXXXXXXXXXXX -compact -.It Pa /etc/mmpop3d.conf -This file -.Pp -.It Pa /usr/local/sbin/mmpop3d -The actual POP3 server binary -.El -.Sh SEE ALSO -.Xr mmpop3d 8 , -.Xr mmmail 8 , -.Xr mmsmtpd 8 , -.Xr mmsmtpd.conf 5 , -.Xr mmstatd 8 , -.Xr mmstatd.conf 5 , -.Xr syslog 3 , -.Xr chroot 2 , -.Xr mysql 1 . -.Sh BUGS -Please report any bug to mmondor@accela.net diff --git a/mmsoftware/mmmail/src/mmpop3d/mmpop3d.h b/mmsoftware/mmmail/src/mmpop3d/mmpop3d.h deleted file mode 100644 index 7b5d296..0000000 --- a/mmsoftware/mmmail/src/mmpop3d/mmpop3d.h +++ /dev/null @@ -1,242 +0,0 @@ -/* $Id: mmpop3d.h,v 1.16 2005/03/05 15:33:34 mmondor Exp $ */ - -/* - * Copyright (C) 2001-2004, Matthew Mondor - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software written by Matthew Mondor. - * 4. The name of Matthew Mondor may not be used to endorse or promote - * products derived from this software without specific prior written - * permission. - * - * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - - - - -#ifndef MMPOP3D_H -#define MMPOP3D_H - - - - -/* HEADERS */ - -#include - -#include - -#include -#include -#include -#include -#include -#include -#include - - - - -/* DEFINITIONS */ - -#define DAEMON_NAME "mmpop3d" -#define DAEMON_VERSION "mmmail-0.0.24/mmondor" - -/* Negative states are used by the state swapper, others are real states */ -#define STATE_ERROR -3 -#define STATE_END -2 -#define STATE_CURRENT -1 -#define STATE_AUTH 0 -#define STATE_MAIN 1 - -/* Asynchroneous functions we attach */ -#define ASYNC_CHECKPW 1 - -/* Error registration macro */ -#define REGISTER_ERROR(x) do { \ - (x)->errors++; \ - if (CONF.DELAY_ON_ERROR) \ - pth_sleep((x)->errors); \ -} while(0) - - - - -/* STRUCTURES */ - -/* We store config file read results in this structure */ -typedef struct config { - char LOCK_PATH[256], CHROOT_DIR[256], PID_PATH[256], USER[32], GROUPS[256], - LOG_FACILITY[32], LISTEN_IPS[1024], SERVER_NAMES[1024], DB_HOST[64], - DB_USER[32], DB_PASSWORD[32], DB_DATABASE[32]; - long ALLOC_BUFFERS, LOG_LEVEL, LISTEN_PORT, MAX_ERRORS, MAX_IPS, - MAX_PER_IP, CONNECTION_RATE, CONNECTION_PERIOD, INPUT_TIMEOUT, - BANDWIDTH_IN, BANDWIDTH_OUT, GBANDWIDTH_IN, GBANDWIDTH_OUT, - ASYNC_PROCESSES; - bool RESOLVE_HOSTS, DELAY_ON_ERROR, STATFAIL_LOGIN, STATFAIL_PASSWORD; -} CONFIG; - -/* For the messages list during the session */ -typedef struct msgnode { - char id[24]; /* ID in the database */ -#if defined(MMMAIL_FILE) - char file[256]; /* Fullpath to message file */ -#elif defined(MMMAIL_MYSQL) -#else -#error "One of MMMAIL_FILE or MMMAIL_MYSQL myst be #defined!" -#endif - long size; /* Message size in bytes */ - bool retreived, deleted; /* Flags */ -} msgnode; - -/* For a loaded message body */ -typedef struct msgdata { - void *internal; - char *body; - size_t size; -} msgdata; - -/* A user environment, one copy per connected client exists */ -typedef struct clientenv { - pnode_t node; - fdbuf *fdb; /* Buffered handler around our fd */ - char *buffer; /* Buffer that points to last command line */ - char *c_hostname; /* Pointer to client's hostname */ - char *c_ipaddr; /* Pointer to client's IP address string */ - char *mailbox; /* Mailbox, this is if USER was specified */ - msgnode *index; /* Available messages with flags */ - long size; /* Size of total messages in index */ - long newsize; /* Current size of undeleted messages */ - long messages; /* Number of messages, to know index size */ - long newmessages; /* Current number of messages after deleted */ - long last; /* Last message read in current session */ - long errors; /* Total number of errors that occured */ - int timeout; /* Timeout in ms */ - unsigned long id; /* Our connection ID */ - unsigned long retreived, deleted; /* Number of msgs retrieved & deleted */ - bool login; /* For all_quit() */ - struct iface *iface; /* Current interface user connected through */ - struct async_clenv *aclenv; /* Thread context for async_call() */ - mmstat_t vstat, pstat; /* mmstat(3) handles */ -} clientenv; - -/* Used for mmfd thread support delegation/abstraction */ -struct mutexnode { - pnode_t node; - pth_mutex_t mutex; -}; - -/* This defines a state */ -typedef struct state { - int (**functions)(clientenv *); - int errcode; - char *errtext; -} state; - -/* A command of a state */ -typedef struct command { - int loglevel; - char *name; -} command; - -/* Used for fast command lookup */ -struct commandnode { - hashnode_t node; - u_int32_t hash; - int index; - struct command *command; -}; - -/* Used by our asynchoneous functions */ -struct async_checkpw_msg { - struct async_msg msg; - union { - struct { - bool matching; - } res; - struct { - char hash[48]; - char passwd[256]; - } args; - } un; -}; - - - - -/* PROTOTYPES */ - -int main(int, char **); - -static int all_noop(clientenv *); -static int all_quit(clientenv *); -static int all_beer(clientenv *); -static int auth_user(clientenv *); -static int auth_pass(clientenv *); -static int main_rset(clientenv *); -static int main_stat(clientenv *); -static int main_list(clientenv *); -static int main_retr(clientenv *); -static int main_dele(clientenv *); -static int main_last(clientenv *); -static int main_top(clientenv *); -static int main_page(clientenv *); - -static int lock_check(const char *); -static bool hash_commands(struct command *, size_t); -static u_int32_t commandnode_keyhash(const void *, size_t); -static int commandnode_keycmp(const void *, const void *, size_t); -static bool do_buildindex(clientenv *); -static bool do_message_load(msgdata *, msgnode *); -static void do_message_free(msgdata *); -static bool do_retreive(fdbuf *, msgnode *); -static bool do_top(fdbuf *, msgnode *, long); -static bool do_page(fdbuf *, msgnode *, long, long); -static bool do_update(clientenv *); -static bool msgwrite(fdbuf *, const void *, size_t); -static bool reply(fdbuf *, bool, const char *, ...); -static clientenv *alloc_clientenv(void); -static bool init_clientenv(clientenv *); -static clientenv *free_clientenv(clientenv *); -static bool valid_address(char *, char *); -static bool valid_host(char *); -inline static bool valid_char(char); - -static int handleclient(unsigned long, int, clientlistnode *, struct iface *, - struct async_clenv *); - -static void *_pth_mutex_create(void); -static void *_pth_mutex_destroy(void *); -static void _pth_mutex_lock(void *); -static void _pth_mutex_unlock(void *); -static void _pth_thread_yield(void); -static void _pth_thread_sleep(int); -static bool _pth_eintr(void); - -static void async_checkpw(struct async_msg *); -static bool checkpw(clientenv *, const char *, const char *); - - - - -#endif diff --git a/mmsoftware/mmmail/src/mmrelayd/mmrelayd.c b/mmsoftware/mmmail/src/mmrelayd/mmrelayd.c deleted file mode 100644 index 0bff0ce..0000000 --- a/mmsoftware/mmmail/src/mmrelayd/mmrelayd.c +++ /dev/null @@ -1,565 +0,0 @@ -/* $Id: mmrelayd.c,v 1.11 2005/03/26 10:44:28 mmondor Exp $ */ - -/* - * Copyright (C) 2004-2005, Matthew Mondor - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software written by Matthew Mondor. - * 4. The name of Matthew Mondor may not be used to endorse or promote - * products derived from this software without specific prior written - * permission. - * - * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -/* - * XXX TODO - * - * - We want to be able to immediately process new queued mail if - * possible. If we are multithreaded, this probably means that the main - * manager thread should be the only one catching a specific signal, which - * mmsmtpd(8) could send us whenever a new message has been queued. - * Of course, we also need to scan the queue at startup. We need to find - * a way to get the list of items minus the ones we are currently processing - * as well. We possibly could use the last_error field to mark in progress - * items, or to introduce a new field. This also implies that at startup - * initial scan that field would need to be reset. We possibly also want - * to use the LIMIT keyword to only get a number of entries we can handle - * at once. However, we don't want to only aways get the same unsuccessfully - * routed entries, so we have to respect last sent date perhaps... - * - * - We also need to manage timers properly for messages in the queue - * which are not yet delivered. - * - * - We don't want to use one thread per message to be delivered and - * all process them at once. We should have a maximum admin-specified - * delivery concurrency. - */ - - - -/* - * HEADERS - */ - -#include -#include - -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include /* strerror(3) */ - -#include -#include -#include - -#include "mmrelayd.h" - - - -MMCOPYRIGHT("@(#) Copyright (c) 2004\n\ -\tMatthew Mondor. All rights reserved.\n"); -MMRCSID("$Id: mmrelayd.c,v 1.11 2005/03/26 10:44:28 mmondor Exp $"); - - - -/* - * DEFINITIONS - */ - -#define DAEMON_NAME "mmrelayd" - -struct CONFIG { - char LOCK_PATH[256], CHROOT_DIR[256], PID_PATH[256], USER[32], - GROUPS[256], LOG_FACILITY[32], DB_HOST[64], DB_USER[32], - DB_PASSWORD[32], DB_DATABASE[32], MAIL_DIR[256], - RELAY_SMARTHOST[64], SOCKET_PATH[256]; - long INPUT_TIMEOUT, BANDWIDTH_IN, BANDWIDTH_OUT, GBANDWIDTH_IN, - GBANDWIDTH_OUT; - /* Used by main() */ - long facility; - uid_t uid; - gid_t *gids; - int ngids; -}; - -int main(int, char **); - -static int config_read(const char *); -static int lock_check(const char *); -static int prechroot_init(void); -static void pidfile_write(const char *); -static int notify_open(const char *, gid_t); -static int daemon_launch(void); -static void daemon_sighandler(int); -static void daemon_main(void); - - - -/* - * GLOBAL VARIABLES - */ - -static struct CONFIG CONF; - -static carg_t cargs[] = { - {CAT_STR, CAF_NONE, 1, 255, "LOCK_PATH", CONF.LOCK_PATH}, - {CAT_STR, CAF_NONE, 1, 255, "CHROOT_DIR", CONF.CHROOT_DIR}, - {CAT_STR, CAF_NONE, 1, 255, "PID_PATH", CONF.PID_PATH}, - {CAT_STR, CAF_NONE, 1, 31, "USER", CONF.USER}, - {CAT_STR, CAF_NONE, 1, 255, "GROUPS", CONF.GROUPS}, - {CAT_STR, CAF_NONE, 1, 31, "LOG_FACILITY", CONF.LOG_FACILITY}, - {CAT_STR, CAF_NONE, 1, 63, "DB_HOST", CONF.DB_HOST}, - {CAT_STR, CAF_NONE, 1, 31, "DB_USER", CONF.DB_USER}, - {CAT_STR, CAF_NONE, 1, 31, "DB_PASSWORD", CONF.DB_PASSWORD}, - {CAT_STR, CAF_NONE, 1, 31, "DB_DATABASE", CONF.DB_DATABASE}, - {CAT_STR, CAF_NONE, 1, 255, "MAIL_DIR", CONF.MAIL_DIR}, - {CAT_STR, CAF_NONE, 1, 63, "RELAY_SMARTHOST", CONF.RELAY_SMARTHOST}, - {CAT_STR, CAF_NONE, 1, 255, "SOCKET_PATH", CONF.SOCKET_PATH}, - {CAT_VAL, CAF_NONE, 1, 99999, "INPUT_TIMEOUT", &CONF.INPUT_TIMEOUT}, - {CAT_VAL, CAF_NONE, 0, 99999, "BANDWIDTH_IN", &CONF.BANDWIDTH_IN}, - {CAT_VAL, CAF_NONE, 0, 99999, "BANDWIDTH_OUT", &CONF.BANDWIDTH_OUT}, - {CAT_VAL, CAF_NONE, 0, 99999, "GBANDWIDTH_IN", &CONF.GBANDWIDTH_IN}, - {CAT_VAL, CAF_NONE, 0, 99999, "GBANDWIDTH_OUT", &CONF.GBANDWIDTH_OUT}, - {CAT_END, CAF_NONE, 0, 0, NULL, NULL} -}; -static const cmap_t cmap[] = { - {"LOG_AUTH", LOG_AUTH}, - {"LOG_AUTHPRIV", LOG_AUTHPRIV}, - {"LOG_CRON", LOG_CRON}, - {"LOG_DAEMON", LOG_DAEMON}, - {"LOG_FTP", LOG_FTP}, - {"LOG_LPR", LOG_LPR}, - {"LOG_MAIL", LOG_MAIL}, - {"LOG_NEWS", LOG_NEWS}, - {"LOG_SYSLOG", LOG_SYSLOG}, - {"LOG_USER", LOG_USER}, - {"LOG_UUCP", LOG_UUCP}, - {NULL, 0} -}; - -static const char *db_host; -static int notify_fd = -1; - - - -/* - * MAIN - */ - -int -main(int argc, char **argv) -{ - char *conf_file = "/usr/local/etc/mmrelayd.conf"; - - /* - * Some sanity checking on privileges - */ -#if !defined(NODROPPRIVS) - if (getuid() != 0) { - syslog(LOG_NOTICE, "Only the superuser may launch %s", - DAEMON_NAME); - exit(EXIT_FAILURE); - } -#else - if (getuid() == 0) { - syslog(LOG_NOTICE, "%s compiled with NODROPPRIVS, refusing " - "to run as the superuser", DAEMON_NAME); - exit(EXIT_FAILURE); - } -#endif - - /* - * Parse command line arguments - */ - { - extern char *optarg; - extern int optind; - int c; - - while ((c = getopt(argc, argv, "f:")) != -1) { - switch (c) { - case 'f': - conf_file = optarg; - break; - case '?': - /* FALLTHROUGH */ - default: - (void) fprintf(stderr, - "Usage: %s [-f ]\n", DAEMON_NAME); - exit(EXIT_FAILURE); - } - } - argc -= optind; - argc += optind; - } - - /* - * And parse configuration file - */ - if (config_read(conf_file) == -1) - exit(EXIT_FAILURE); - - /* - * Cause system logging to go on the administrator specified queue - * immediately - */ - openlog(DAEMON_NAME, LOG_PID | LOG_NDELAY, CONF.facility); - - /* - * Ensure that we're not already running - */ - if (lock_check(CONF.LOCK_PATH) == -1) { - syslog(LOG_NOTICE, "Coudn't lock '%s': Already running?", - CONF.LOCK_PATH); - exit(EXIT_FAILURE); - } - - /* - * Become a daemon - */ - if (daemon_launch() == -1) { - syslog(LOG_NOTICE, "Could not become a daemon (%s)", - strerror(errno)); - exit(EXIT_FAILURE); - } - - /* - * Initialize things which must be done before chroot(2)ing, in case - * we'll be - */ - if (prechroot_init() == -1) - exit(EXIT_FAILURE); - - /* - * chroot(2) if requested - */ - if (*CONF.CHROOT_DIR != '\0') { - if (chroot(CONF.CHROOT_DIR) == -1) { - syslog(LOG_NOTICE, "Could not chroot(2) - (%s)", - strerror(errno)); - exit(EXIT_FAILURE); - } - (void) chdir("/"); - } - - /* - * Finally drop privileges if necessary - */ - if (!mmdropprivs(CONF.uid, CONF.gids, CONF.ngids)) { - syslog(LOG_NOTICE, "Could not drop privileges (%s)", - strerror(errno)); - exit(EXIT_FAILURE); - } - - /* - * Main server code - */ - daemon_main(); - /* NOTREACHED */ - - exit(EXIT_SUCCESS); -} - -/* - * Reads the configuration file and performs necessary post-parsing sanity - * verifications. Returns 0 on success or -1 on error. - */ -static int -config_read(const char *file) -{ - uid_t uid; - gid_t *gids; - int ngids; - long facility; - cres_t cres; - struct stat st; - - /* - * Set defaults - */ - *CONF.CHROOT_DIR = '\0'; - (void) mm_strcpy(CONF.LOCK_PATH, "/var/run/mmrelayd.lock"); - (void) mm_strcpy(CONF.PID_PATH, "/var/run/mmrelayd.pid"); - (void) mm_strcpy(CONF.USER, "mmmail"); - (void) mm_strcpy(CONF.GROUPS, "mmmail,mmstat"); - (void) mm_strcpy(CONF.LOG_FACILITY, "LOG_AUTHPRIV"); - (void) mm_strcpy(CONF.DB_HOST, "localhost"); - (void) mm_strcpy(CONF.DB_USER, "mmmail"); - (void) mm_strcpy(CONF.DB_PASSWORD, "mmmailpassword"); - (void) mm_strcpy(CONF.DB_DATABASE, "mmmail"); - (void) mm_strcpy(CONF.MAIL_DIR, "/var/mmmail-dir"); - (void) mm_strcpy(CONF.SOCKET_PATH, "/var/run/mmrelayd.sock"); - *CONF.RELAY_SMARTHOST = '\0'; - CONF.BANDWIDTH_IN = 0; - CONF.BANDWIDTH_OUT = 0; - CONF.GBANDWIDTH_IN = 0; - CONF.GBANDWIDTH_OUT = 0; - - /* - * Parse config file - */ - if (!mmreadcfg(&cres, cargs, file)) - return -1; - - /* - * Post-parsing sanity checking - */ - if (!mmmapstring(cmap, CONF.LOG_FACILITY, &facility)) { - syslog(LOG_NOTICE, "Unknown syslog facility '%s'", - CONF.LOG_FACILITY); - return -1; - } - - if ((uid = mmgetuid(CONF.USER)) == -1) { - syslog(LOG_NOTICE, "Unknown user '%s'", CONF.USER); - return -1; - } - if ((gids = mmgetgidarray(&ngids, CONF.GROUPS)) == NULL) { - syslog(LOG_NOTICE, "At least one of groups unknown '%s'", - CONF.GROUPS); - return -1; - } - - if (mm_strcmp(CONF.DB_HOST, "localhost") == 0) - db_host = NULL; - else - db_host = CONF.DB_HOST; - - if (*CONF.MAIL_DIR != '/') { - syslog(LOG_NOTICE, - "MAIL_DIR must be an absolute pathname to a directory"); - return -1; - } - if (stat(CONF.MAIL_DIR, &st) == -1) { - syslog(LOG_NOTICE, "MAIL_DIR directory not found"); - return -1; - } - if (!S_ISDIR(st.st_mode)) { - syslog(LOG_NOTICE, "MAIL_DIR not a directory"); - return -1; - } - - /* - * Set some preprocessed values for main() - */ - CONF.facility = facility; - CONF.uid = uid; - CONF.gids = gids; - CONF.ngids = ngids; - - return 0; -} - -static int -lock_check(const char *path) -{ - int fd; - - if ((fd = open(path, O_CREAT | O_TRUNC | O_WRONLY, 0600)) != -1) { - if (flock(fd, LOCK_EX | LOCK_NB) == 0) - return 0; - (void) close(fd); - } - - return -1; -} - -/* - * Initializations which must be performed before chroot(2)ing - */ -static int -prechroot_init(void) -{ - - if ((notify_fd = notify_open(CONF.SOCKET_PATH, CONF.gids[0])) == -1) - return -1; - - /* XXX */ - - pidfile_write(CONF.PID_PATH); - - return 0; -} - -/* Writes our process ID number to specified file. To be called before - * chroot(2) or dropping privileges. - */ -static void -pidfile_write(const char *file) -{ - char str[16]; - int fd; - - if ((fd = open(file, O_CREAT | O_TRUNC | O_WRONLY, 0600)) != -1) { - (void) snprintf(str, 15, "%d\n", getpid()); - (void) write(fd, str, mm_strlen(str)); - (void) close(fd); - } else - syslog(LOG_NOTICE, "pidfile_write() - open(%s) - %s", - file, strerror(errno)); -} - -/* - * Binds AF_LOCAL SOCK_DGRAM socket at specified , with write access - * for the specified group. Returns socket filedescriptor on success, or -1 on - * failure. We use setsockopt(2) to ensure that we can atomically receive - * messages large enough for our notification message structure. - */ -static int -notify_open(const char *path, gid_t group) -{ - int sock; - struct sockaddr_un sau; - - (void) unlink(path); - if ((sock = socket(AF_LOCAL, SOCK_DGRAM, 0)) != -1) { - mm_strncpy(sau.sun_path, path, 100); - sau.sun_family = AF_UNIX; - if (bind(sock, (struct sockaddr *)&sau, - sizeof(struct sockaddr_un)) != -1) { - int opt; - - opt = (int)BALIGN_CEIL(sizeof(notify_msg_t), 1024); - if (setsockopt(sock, SOL_SOCKET, SO_RCVBUF, &opt, - sizeof(int)) == -1) - syslog(LOG_NOTICE, - "Cannot setsockopt(2) - (%s)", - strerror(errno)); - if (chmod(path, 0020) == 0) { - (void) chown(path, -1, group); - return sock; - } else - syslog(LOG_NOTICE, - "Cannot chown(2) '%s' socket (%s)", - path, strerror(errno)); - } else - syslog(LOG_NOTICE, - "Cannot bind(2) AF_LOCAL DGRAM socket '%s' (%s)", - path, strerror(errno)); - } else - syslog(LOG_NOTICE, "Cannot create filedescriptor (%s)", - strerror(errno)); - - /* - * We want this descriptor to cause a SIGIO signal to be sent to our - * process. - */ - /* XXX */ - - return -1; -} - -/* - * Transforms application into a daemon, returns 0 on success or -1 on error. - */ -static int -daemon_launch(void) -{ - pid_t pid; - int fd; - - /* - * Launch new process and discard current one - */ - if ((pid = fork()) == -1) - return -1; - if (pid == 0) - exit(EXIT_SUCCESS); - - /* - * Create new process group - */ - (void) setsid(); - (void) chdir("/"); - - /* - * Ensure that std filedescriptors be redirected to /dev/null device - */ - if ((fd = open("/dev/null", O_RDWR)) != -1) { - (void) dup2(fd, STDIN_FILENO); - (void) dup2(fd, STDOUT_FILENO); - (void) dup2(fd, STDERR_FILENO); - if (fd > STDERR_FILENO) - (void) close(fd); - } else - syslog(LOG_NOTICE, - "daemon() - Cannot open NULL device - (%s)", - strerror(errno)); - - /* - * Setup signal handler and ignore unwanted signals - */ - { - struct sigaction act; - - act.sa_handler = daemon_sighandler; - act.sa_flags = SA_NOCLDSTOP; - (void) sigemptyset(&act.sa_mask); - (void) sigaction(SIGSEGV, &act, NULL); - (void) sigaction(SIGTERM, &act, NULL); - (void) sigaction(SIGCHLD, &act, NULL); - (void) sigaction(SIGHUP, &act, NULL); - (void) sigaction(SIGALRM, &act, NULL); - (void) sigaction(SIGIO, &act, NULL); - - act.sa_handler = SIG_IGN; - (void) sigaction(SIGTTOU, &act, NULL); - (void) sigaction(SIGTTIN, &act, NULL); - (void) sigaction(SIGTSTP, &act, NULL); - } - - return 0; -} - -/* - * Function that handles signals we catch - */ -/* ARGSUSED */ -static void -daemon_sighandler(int sig) -{ - - /* XXX */ -} - -/* - * Main server loop - */ -static void -daemon_main(void) -{ - - for (;;) { - /* XXX */ - (void) pause(); - } - /* NOTREACHED */ -} diff --git a/mmsoftware/mmmail/src/mmrelayd/mmrelayd.h b/mmsoftware/mmmail/src/mmrelayd/mmrelayd.h deleted file mode 100644 index 67a7046..0000000 --- a/mmsoftware/mmmail/src/mmrelayd/mmrelayd.h +++ /dev/null @@ -1,51 +0,0 @@ -/* $Id: mmrelayd.h,v 1.1 2005/03/26 10:45:34 mmondor Exp $ */ - -/* - * Copyright (C) 2005, Matthew Mondor - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software written by Matthew Mondor. - * 4. The name of Matthew Mondor may not be used to endorse or promote - * products derived from this software without specific prior written - * permission. - * - * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - - - -#ifndef MMRELAYD_H -#define MMRELAYD_H - - - -/* - * Structure shared between mmsmtpd(8) and mmrelayd(8) to notify new queue - * additions to the scheduler - */ -typedef struct notify_msg { - int t; -} notify_msg_t; - - - -#endif diff --git a/mmsoftware/mmmail/src/mmsmtpd/Makefile b/mmsoftware/mmmail/src/mmsmtpd/Makefile deleted file mode 100644 index 877a4e2..0000000 --- a/mmsoftware/mmmail/src/mmsmtpd/Makefile +++ /dev/null @@ -1,36 +0,0 @@ -# $Id: Makefile,v 1.3 2003/07/17 22:26:51 mmondor Exp $ - -CC = gcc -MAKE = make -ECHO = echo - -CFLAGS += -D_REENTRANT -DDEBUG -Wall - -PTHINCDIR != $(ECHO) `pth-config --cflags` -PTHLIBDIR != $(ECHO) -L`pth-config --libdir` -MYSQLINCDIR != $(ECHO) `mysql_config --cflags` -MYSQLLIBDIR != $(ECHO) `mysql_config --libs` -INCDIR = -I/usr/include -I/usr/local/include -I. -I../../../mmlib $(PTHINCDIR) $(MYSQLINCDIR) -LIBDIR = -L/usr/local/lib -L/usr/lib -L/lib -L/usr/pkg/lib $(PTHLIBDIR) $(MYSQLLIBDIR) -LIBS = ../../../mmlib/libmmondor.a -lc -lresolv -lpth - -OBJS = mmsmtpd.o - -CCOBJ = $(CC) $(CFLAGS) -c $(INCDIR) - - - - -all: $(OBJS) - $(CC) $(CFLAGS) -o mmsmtpd $(INCDIR) $(LIBDIR) $(OBJS) $(LIBS) - - - -clean: - -rm -f $(OBJS) mmsmtpd - - - -mmsmtpd.o: - $(CCOBJ) mmsmtpd.c - diff --git a/mmsoftware/mmmail/src/mmsmtpd/makepart.sh b/mmsoftware/mmmail/src/mmsmtpd/makepart.sh deleted file mode 100755 index d4d8e04..0000000 --- a/mmsoftware/mmmail/src/mmsmtpd/makepart.sh +++ /dev/null @@ -1,29 +0,0 @@ -#!/bin/sh -# $Id: makepart.sh,v 1.2 2003/01/01 14:54:11 mmondor Exp $ - -. ../../../mmlib/makedefs.sh - -OBJS='mmsmtpd.o' -BIN1='mmsmtpd' - -if [ "$1" = "clean" ]; then - show $RM $OBJS $BIN1 - exit 0 -fi - -PTHINCDIR="`pth-config --cflags`" -PTHLIBDIR="-L`pth-config --libdir`" -MYSQLINCDIR="`mysql_config --cflags | $SED s/\'//g`" -MYSQLLIBDIR="`mysql_config --libs | $SED s/\'//g`" -INCDIR="-I../../../mmlib $STDINC $PTHINCDIR $MYSQLINCDIR" -LIBDIR="$STDLIB $PTHLIBDIR $MYSQLLIBDIR" -LIBS='../../../mmlib/libmmondor.a -lc -lresolv -lpth' - -for obj in $OBJS; do - if [ ! -f $obj ]; then - src=`$ECHO $obj | $SED 's/\.o$/.c/'` - show $CC $CFLAGS -c $INCDIR $src - fi -done - -show $CC $CFLAGS -o $BIN1 $INCDIR $LIBDIR $BIN1.o $LIBS diff --git a/mmsoftware/mmmail/src/mmsmtpd/mmsmtpd.8 b/mmsoftware/mmmail/src/mmsmtpd/mmsmtpd.8 deleted file mode 100644 index 662e822..0000000 --- a/mmsoftware/mmmail/src/mmsmtpd/mmsmtpd.8 +++ /dev/null @@ -1,91 +0,0 @@ -.\" $Id: mmsmtpd.8,v 1.4 2004/11/09 05:31:14 mmondor Exp $ -.\" -.\" Copyright (C) 2001-2004, Matthew Mondor -.\" All rights reserved. -.\" -.\" Redistribution and use in source and binary forms, with or without -.\" modification, are permitted provided that the following conditions -.\" are met: -.\" 1. Redistributions of source code must retain the above copyright -.\" notice, this list of conditions and the following disclaimer. -.\" 2. Redistributions in binary form must reproduce the above copyright -.\" notice, this list of conditions and the following disclaimer in the -.\" documentation and/or other materials provided with the distribution. -.\" 3. All advertising materials mentioning features or use of this software -.\" must display the following acknowledgement: -.\" This product includes software written by Matthew Mondor. -.\" 4. The name of Matthew Mondor may not be used to endorse or promote -.\" products derived from this software without specific prior written -.\" permission. -.\" -.\" THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR -.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES -.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. -.\" IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, -.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -.\" BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF -.\" USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF -.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -.\" -.Dd 11 Dec, 2002 -.Dt MMSMTPD 8 -.Os -.Sh NAME -.Nm mmsmtpd -.Nd SMTP server of the -.Xr mmmail 8 -suite -.Sh SYNOPSIS -.Nm mmsmtpd Op Ar config_file -.Sh DESCRIPTION -.Nm mmsmtpd -consists of the SMTP server of the -.Nm mmmail -suite (See -.Xr mmmail 8 -man page for more information). It's configuration file consists of -.Nm /etc/mmsmtpd.conf -(See -.Xr mmsmtpd.conf 5 -man page for details). This server requires the -.Nm mmstatd -daemon to be running to use it's statistical and who features (See -.Xr mmstatd 8 , -.Xr mmstat 8 -and -.Xr mmstat 3 -man pages for details). You will also need to run the -.Xr mmrelayd 8 -daemon if you intend to use relaying support, without which mail to be relayed -will only get queued for later processing. -.Sh AUTHOR -.Nm mmmail -and -.Nm mmsmtpd -were written by Matthew Mondor and are Copyright (c) 2001-2004, Matthew Mondor, -All rights reserved. -.Sh FILES -.Bl -tag -width XXXXXXXXXXXXXXXXXXXXXXXXX -compact -.It Pa /usr/local/sbin/mmsmtpd -The actual SMTP server binary -.Pp -.It Pa /usr/local/sbin/mmstatd -The statistics librarian daemon binary -.Pp -.It Pa /etc/mmsmtpd.conf -The configuration file for -.Nm mmsmtpd -.Sh SEE ALSO -.Xr mmmail 8 , -.Xr mmsmtpd.conf 5 , -.Xr mmrelayd 8 , -.Xr mmrelayd.conf 5 , -.Xr mmpop3d 8 , -.Xr mmpop3d.conf 5 , -.Xr mmstatd 8 , -.Xr mmstat 8 , -.Xr mmstat 3 . -.Sh BUGS -Please report any bug to mmondor@accela.net diff --git a/mmsoftware/mmmail/src/mmsmtpd/mmsmtpd.c b/mmsoftware/mmmail/src/mmsmtpd/mmsmtpd.c deleted file mode 100644 index 1001b2e..0000000 --- a/mmsoftware/mmmail/src/mmsmtpd/mmsmtpd.c +++ /dev/null @@ -1,3001 +0,0 @@ -/* $Id: mmsmtpd.c,v 1.74 2005/06/27 17:54:52 mmondor Exp $ */ - -/* - * Copyright (C) 2001-2004, Matthew Mondor - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software written by Matthew Mondor. - * 4. The name of Matthew Mondor may not be used to endorse or promote - * products derived from this software without specific prior written - * permission. - * - * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - - - - -/* HEADERS */ - -#include -#include -#include -#include -#include -#include -#include /* strerror(3) */ - -#include -#include -#include -#include -#include -#include - -#include - -#include -#include -#include -#include - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "mmsmtpd.h" -#include "../mmrelayd/mmrelayd.h" - - - - -MMCOPYRIGHT("@(#) Copyright (c) 2001-2004\n\ -\tMatthew Mondor. All rights reserved.\n"); -MMRCSID("$Id: mmsmtpd.c,v 1.74 2005/06/27 17:54:52 mmondor Exp $"); - - - - -/* GLOBAL VARIABLES */ -/* This stores the global configuration options */ -static CONFIG CONF; - -/* Here consists of the commands we support */ -static command commands[] = { - /* LogLevel, Cmd, Args, Description (NULL=unimplemented) */ - {3, "NOOP", "NOOP", "Does nothing"}, - {3, "RSET", "RSET", "Resets system to initial state"}, - {3, "QUIT", "QUIT", "Disconnects, exits"}, - {3, "HELP", "HELP []", "Gives HELP information"}, - {2, "HELO", "HELO ", "Permits to authenticate"}, - {2, "MAIL", "MAIL FROM:", "Specifies sender of message"}, - {2, "RCPT", "RCPT TO:", "Specifies a recipient"}, - {3, "DATA", "DATA", "Accepts the message ending with ."}, - {4, "BEER", NULL, NULL}, - {0, NULL, NULL, NULL} -}; - -/* The system is simple enough that only one state is required, each command - * function will perform it's own sanity checking to solidly simulate states. - */ -static int (*state_all[])(clientenv *) = { - all_noop, /* NOOP */ - all_rset, /* RSET */ - all_quit, /* QUIT */ - all_help, /* HELP */ - all_helo, /* HELO */ - all_mail, /* MAIL */ - all_rcpt, /* RCPT */ - all_data, /* DATA */ - all_beer /* BEER */ -}; - -/* The definitions of our many various states (-: */ -static const struct state states[] = { - {state_all, 0, "Abnormal error"} -}; - -/* Used for mmsyslog() */ -static int LOGLEVEL; - -/* Used for clenv allocation buffering */ -static pool_t clenv_pool; -static pth_mutex_t clenv_lock; - -/* Used for the flood protection cache */ -static pool_t hosts_pool; -static hashtable_t hosts_table; -static pth_mutex_t hosts_lock; - -/* Used for rcpt allocation buffering */ -static pool_t rcpt_pool; -static pth_mutex_t rcpt_lock; - -/* Pool used to optimize creating/destroying mmfd mutexes */ -static pth_mutex_t mutexes_lock; -static pool_t mutexes_pool; - -/* For fast command lookup */ -static pool_t command_pool; -static hashtable_t command_table; - -/* Global bandwidth shaping fdb context */ -static fdbcontext fdbc; - -/* Quick index to RCPT command replies (see mmsmtpd.h for matching defines) */ -static const struct reply_messages rcpt_msg[RCPT_MAX] = { - {250, "Recipient ok"}, - {503, "Use MAIL first"}, - {552, "Too many recipients"}, - {501, "Invalid address"}, - {501, "Unknown address"}, - {501, "Relaying denied"}, - {250, "Recipient already added"}, - {402, "Mailbox full, try again later"}, - {402, "Rate exceeded, try again later"}, - {571, "Delivery not authorized, message refused"}, - {452, "Internal error, contact administrator"} -}; - -/* Fast index to DATA replies (see headerfile for matching keywords) */ -static const struct reply_messages data_msg[DATA_MAX] = { - {354, "Submit message ending with a single ."}, - {250, "Ok, mail delivered"}, - {552, "Too much mail data"}, - {552, "Too many hops"}, - {452, "Internal error"} -}; - -/* Pth support for mmfd library (that library rocks my world :) */ -static fdfuncs gfdf = { - malloc, - free, - pth_poll, - pth_read, - pth_write, - pth_sleep, - pth_usleep, - _pth_mutex_create, - _pth_mutex_destroy, - _pth_mutex_lock, - _pth_mutex_unlock, - _pth_thread_yield, - _pth_eintr -}; - -/* - * Used to speed up VALID_ADDR_CHAR() - */ -static const unsigned char valid_addr_char_table[256] = { - 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, 1, 1, 0, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 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, 1, - 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 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, 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, 0, 0, 0, 0, 0, 0, 0 -}; -/* - * And for VALID_HOST_CHAR() - */ -static const unsigned char valid_addr_host_table[256] = { - 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, 1, 1, 0, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 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, 1, - 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 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, 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, 0, 0, 0, 0, 0, 0, 0 -}; - -/* - * Connection to mmrelayd(8) establishment - */ -int relayd_sock = -1; -pth_mutex_t relayd_lock = PTH_MUTEX_INIT; - - - - -/* MAIN */ - -int -main(int argc, char **argv) -{ - uid_t uid; - gid_t *gids; - char *conf_file = "/etc/mmsmtpd.conf"; - int ngids, ret = -1; - long facility; - char *db_host; - bool strlist; - cres_t cres; - carg_t *cargp; - carg_t cargs[] = { - {CAT_STR, CAF_NONE, 1, 255, "LOCK_PATH", CONF.LOCK_PATH}, - {CAT_STR, CAF_NONE, 1, 255, "CHROOT_DIR", CONF.CHROOT_DIR}, - {CAT_STR, CAF_NONE, 1, 255, "PID_PATH", CONF.PID_PATH}, - {CAT_STR, CAF_NONE, 1, 31, "USER", CONF.USER}, - {CAT_STR, CAF_NONE, 1, 255, "GROUPS", CONF.GROUPS}, - {CAT_STR, CAF_NONE, 1, 31, "LOG_FACILITY", CONF.LOG_FACILITY}, - {CAT_STR, CAF_NONE, 1, 1023, "SERVER_NAMES", CONF.SERVER_NAMES}, - {CAT_STR, CAF_NONE, 1, 1023, "LISTEN_IPS", CONF.LISTEN_IPS}, - {CAT_STR, CAF_NONE, 1, 63, "DB_HOST", CONF.DB_HOST}, - {CAT_STR, CAF_NONE, 1, 31, "DB_USER", CONF.DB_USER}, - {CAT_STR, CAF_NONE, 1, 31, "DB_PASSWORD", CONF.DB_PASSWORD}, - {CAT_STR, CAF_NONE, 1, 31, "DB_DATABASE", CONF.DB_DATABASE}, - {CAT_STR, CAF_NONE, 1, 255, "MAIL_DIR", CONF.MAIL_DIR}, - {CAT_STR, CAF_NONE, 1, 255, "MMRELAYD_SOCKET_PATH", - CONF.MMRELAYD_SOCKET_PATH}, - {CAT_VAL, CAF_NONE, 1, 32, "ASYNC_PROCESSES", &CONF.ASYNC_PROCESSES}, - {CAT_VAL, CAF_NONE, 1, 9999, "ALLOC_BUFFERS", &CONF.ALLOC_BUFFERS}, - {CAT_VAL, CAF_NONE, 0, 4, "LOG_LEVEL", &CONF.LOG_LEVEL}, - {CAT_VAL, CAF_NONE, 1, 65535, "LISTEN_PORT", &CONF.LISTEN_PORT}, - {CAT_VAL, CAF_NONE, 1, 1000, "MAX_ERRORS", &CONF.MAX_ERRORS}, - {CAT_VAL, CAF_NONE, 1, 99999, "MAX_IPS", &CONF.MAX_IPS}, - {CAT_VAL, CAF_NONE, 1, 99999, "MAX_PER_IP", &CONF.MAX_PER_IP}, - {CAT_VAL, CAF_NONE, 0, 99999, "CONNECTION_RATE", - &CONF.CONNECTION_RATE}, - {CAT_VAL, CAF_NONE, 1, 99999, "CONNECTION_PERIOD", - &CONF.CONNECTION_PERIOD}, - {CAT_VAL, CAF_NONE, 1, 99999, "INPUT_TIMEOUT", &CONF.INPUT_TIMEOUT}, - {CAT_VAL, CAF_NONE, 0, 99999, "BANDWIDTH_IN", &CONF.BANDWIDTH_IN}, - {CAT_VAL, CAF_NONE, 0, 99999, "BANDWIDTH_OUT", &CONF.BANDWIDTH_OUT}, - {CAT_VAL, CAF_NONE, 0, 99999, "GBANDWIDTH_IN", &CONF.GBANDWIDTH_IN}, - {CAT_VAL, CAF_NONE, 0, 99999, "GBANDWIDTH_OUT", &CONF.GBANDWIDTH_OUT}, - {CAT_VAL, CAF_NONE, 1, 99999, "MAX_RCPTS", &CONF.MAX_RCPTS}, - {CAT_VAL, CAF_NONE, 1, 999999, "MAX_DATA_LINES", - &CONF.MAX_DATA_LINES}, - {CAT_VAL, CAF_NONE, 1, 99999999, "MAX_DATA_SIZE", - &CONF.MAX_DATA_SIZE}, - {CAT_VAL, CAF_NONE, 1, 999, "MAX_HOPS", &CONF.MAX_HOPS}, - {CAT_VAL, CAF_NONE, 1, 999999, "FLOOD_MESSAGES", - &CONF.FLOOD_MESSAGES}, - {CAT_VAL, CAF_NONE, 1, 120, "FLOOD_EXPIRES", &CONF.FLOOD_EXPIRES}, - {CAT_VAL, CAF_NONE, 50, 999999, "FLOOD_CACHE", &CONF.FLOOD_CACHE}, - {CAT_BOOL, CAF_NONE, 0, 0, "RESOLVE_HOSTS", &CONF.RESOLVE_HOSTS}, - {CAT_BOOL, CAF_NONE, 0, 0, "RESOLVE_HELO", &CONF.RESOLVE_HELO}, - {CAT_BOOL, CAF_NONE, 0, 0, "RESOLVE_MX_MAIL", &CONF.RESOLVE_MX_MAIL}, - {CAT_BOOL, CAF_NONE, 0, 0, "RESOLVE_MX_RCPT", &CONF.RESOLVE_MX_RCPT}, - {CAT_BOOL, CAF_NONE, 0, 0, "REQUIRE_HELO", &CONF.REQUIRE_HELO}, - {CAT_BOOL, CAF_NONE, 0, 0, "FLOOD_PROTECTION", &CONF.FLOOD_PROTECTION}, - {CAT_BOOL, CAF_NONE, 0, 0, "STATFAIL_ADDRESS", &CONF.STATFAIL_ADDRESS}, - {CAT_BOOL, CAF_NONE, 0, 0, "STATFAIL_RELAY", &CONF.STATFAIL_RELAY}, - {CAT_BOOL, CAF_NONE, 0, 0, "STATFAIL_FLOOD", &CONF.STATFAIL_FLOOD}, - {CAT_BOOL, CAF_NONE, 0, 0, "STATFAIL_FULL", &CONF.STATFAIL_FULL}, - {CAT_BOOL, CAF_NONE, 0, 0, "STATFAIL_TIMEOUT", - &CONF.STATFAIL_TIMEOUT}, - {CAT_BOOL, CAF_NONE, 0, 0, "STATFAIL_EOF", &CONF.STATFAIL_EOF}, - {CAT_BOOL, CAF_NONE, 0, 0, "STATFAIL_FILTER", &CONF.STATFAIL_FILTER}, - {CAT_BOOL, CAF_NONE, 0, 0, "DELAY_ON_ERROR", &CONF.DELAY_ON_ERROR}, - {CAT_BOOL, CAF_NONE, 0, 0, "RELAYING", &CONF.RELAYING}, - {CAT_END, CAF_NONE, 0, 0, NULL, NULL} - }; - cmap_t cmap[] = { - {"LOG_AUTH", LOG_AUTH}, - {"LOG_AUTHPRIV", LOG_AUTHPRIV}, - {"LOG_CRON", LOG_CRON}, - {"LOG_DAEMON", LOG_DAEMON}, - {"LOG_FTP", LOG_FTP}, - {"LOG_LPR", LOG_LPR}, - {"LOG_MAIL", LOG_MAIL}, - {"LOG_NEWS", LOG_NEWS}, - {"LOG_SYSLOG", LOG_SYSLOG}, - {"LOG_USER", LOG_USER}, - {"LOG_UUCP", LOG_UUCP}, - {NULL, 0} - }; - struct async_func afuncs[] = { - {async_resquery, sizeof(struct async_resquery_msg)}, - {NULL, 0} - }; - struct mmsql_threadsupport mmsqlfuncs = { - _pth_mutex_create, - _pth_mutex_destroy, - _pth_mutex_lock, - _pth_mutex_unlock, - _pth_thread_yield, - _pth_thread_sleep - }; - mmstat_t vstat; - pth_t hosts_table_thread = NULL; - pth_t mmmail_db_gc_thread = NULL; - pth_attr_t threadattr; - - /* Set defaults */ - *CONF.CHROOT_DIR = 0; - mm_strcpy(CONF.LOCK_PATH, "/var/run/mmsmtpd.lock"); - mm_strcpy(CONF.PID_PATH, "/var/run/mmsmtpd.pid"); - mm_strcpy(CONF.USER, "mmmail"); - mm_strcpy(CONF.GROUPS, "mmmail,mmstat"); - mm_strcpy(CONF.LOG_FACILITY, "LOG_AUTHPRIV"); - mm_strcpy(CONF.SERVER_NAMES, "smtp.localhost"); - mm_strcpy(CONF.LISTEN_IPS, "127.0.0.1"); - mm_strcpy(CONF.DB_HOST, "localhost"); - mm_strcpy(CONF.DB_USER, "mmmail"); - mm_strcpy(CONF.DB_PASSWORD, "mmmailpassword"); - mm_strcpy(CONF.DB_DATABASE, "mmmail"); - mm_strcpy(CONF.MAIL_DIR, "/var/mmmail-dir"); - mm_strcpy(CONF.MMRELAYD_SOCKET_PATH, "/var/run/mmrelayd.sock"); - CONF.ASYNC_PROCESSES = 3; - CONF.ALLOC_BUFFERS = 1; - CONF.LOG_LEVEL = 3; - CONF.LISTEN_PORT = 25; - CONF.MAX_ERRORS = 16; - CONF.MAX_IPS = 64; - CONF.MAX_PER_IP = 1; - CONF.CONNECTION_RATE = 10; - CONF.CONNECTION_PERIOD = 30; - CONF.INPUT_TIMEOUT = 900; - CONF.BANDWIDTH_IN = 16; - CONF.BANDWIDTH_OUT = 4; - CONF.GBANDWIDTH_IN = 0; - CONF.GBANDWIDTH_OUT = 0; - CONF.MAX_RCPTS = 16; - CONF.MAX_DATA_LINES = 15000; - CONF.MAX_DATA_SIZE = 1048576; - CONF.MAX_HOPS = 30; - CONF.FLOOD_MESSAGES = 20; - CONF.FLOOD_EXPIRES = 30; - CONF.FLOOD_CACHE = 100; - CONF.RESOLVE_HOSTS = FALSE; - CONF.RESOLVE_HELO = FALSE; - CONF.RESOLVE_MX_MAIL = FALSE; - CONF.RESOLVE_MX_RCPT = FALSE; - CONF.REQUIRE_HELO = FALSE; - CONF.FLOOD_PROTECTION = TRUE; - CONF.STATFAIL_ADDRESS = TRUE; - CONF.STATFAIL_RELAY = TRUE; - CONF.STATFAIL_FLOOD = TRUE; - CONF.STATFAIL_FULL = TRUE; - CONF.STATFAIL_TIMEOUT = TRUE; - CONF.STATFAIL_EOF = TRUE; - CONF.STATFAIL_FILTER = TRUE; - CONF.DELAY_ON_ERROR = FALSE; - CONF.RELAYING = FALSE; - - /* Advertize */ - printf("\r\n+++ %s (%s)\r\n\r\n", DAEMON_NAME, DAEMON_VERSION); - - /* Read config file */ - if (argc == 2) - conf_file = argv[1]; - if (!mmreadcfg(&cres, cargs, conf_file)) { - /* Error parsing configuration file, report which */ - printf("\nError parsing '%s'\n", conf_file); - printf("Error : %s\n", mmreadcfg_strerr(cres.CR_Err)); - if (*(cres.CR_Data)) printf("Data : %s\n", cres.CR_Data); - if ((cargp = cres.CR_Keyword) != NULL) { - printf("Keyword: %s\n", cargp->CA_Keyword); - printf("Minimum: %ld\n", cargp->CA_Min); - printf("Maximum: %ld\n", cargp->CA_Max); - } - if (cres.CR_Line != -1) - printf("Line : %d\n", cres.CR_Line); - printf("\n"); - exit(-1); - } - - /* Ensure that only a single instance with same configuration runs */ - if (lock_check(CONF.LOCK_PATH) == -1) { - printf("\nCouldn't lock file '%s' (already running?)\n\n", - CONF.LOCK_PATH); - exit(-1); - } - - /* Post parsing */ - if (!mmmapstring(cmap, CONF.LOG_FACILITY, &facility)) { - printf("\nUnknown syslog facility %s\n\n", CONF.LOG_FACILITY); - exit(-1); - } - LOGLEVEL = CONF.LOG_LEVEL; - /* Translate to numbers the user and group we were told to run as */ - if ((uid = mmgetuid(CONF.USER)) == -1) { - printf("\nUnknown user '%s'\n\n", CONF.USER); - exit(-1); - } - if (!(gids = mmgetgidarray(&ngids, CONF.GROUPS)) == -1) { - printf("\nOne of following groups unknown: '%s'\n\n", CONF.GROUPS); - exit(-1); - } - if (!(mm_strcmp(CONF.DB_HOST, "localhost"))) db_host = NULL; - else db_host = CONF.DB_HOST; - - if (*CONF.MAIL_DIR != '/') { - printf("\nMAIL_DIR must be an absolute pathname to a directory\n\n"); - exit(-1); - } else { -#if defined(MMMAIL_FILE) - struct stat st; - - if (stat(CONF.MAIL_DIR, &st) == -1) { - printf("\nMAIL_DIR could not be found: '%s'\n\n", CONF.MAIL_DIR); - exit(-1); - } - if (!S_ISDIR(st.st_mode)) { - printf("\nMAIL_DIR not a directory: '%s'\n\n", CONF.MAIL_DIR); - exit(-1); - } -#endif /* defined(MMMAIL_FILE) */ - } - - /* Finally init everything */ - openlog(DAEMON_NAME, LOG_PID | LOG_NDELAY, facility); - -#ifndef NODROPPRIVS - if ((getuid())) { - printf("\nOnly the super user may start this daemon\n\n"); - syslog(LOG_NOTICE, "* Only superuser can start me"); - exit(-1); - } -#else /* NODROPPRIVS */ - if ((getuid()) == 0) { - printf("\nCompiled with NODROPPRIVS, refusing to run as uid 0\n\n"); - syslog(LOG_NOTICE, "* NODROPPRIVS, refusing to run as uid 0"); - exit(-1); - } -#endif /* !NODROPPRIVS */ - - /* In case we chroot(2), the following is a good idea to execute first */ - mmstat_initialize(); - mmstat_init(&vstat, TRUE, TRUE); - mmstat_transact(&vstat, TRUE); - mmstat(&vstat, STAT_DELETE, 0, "mmsmtpd|current|connections"); - mmstat(&vstat, STAT_DELETE, 0, "mmsmtpd|who|*"); - mmstat_transact(&vstat, FALSE); - res_init(); - if (!(mmsql_open(db_host, CONF.DB_USER, CONF.DB_PASSWORD, - CONF.DB_DATABASE))) { - printf("\nCould not connect to MySQLd\n\n"); - syslog(LOG_NOTICE, "* Could not connect to MySQLd"); - exit(-1); - } - - make_daemon(CONF.PID_PATH, CONF.CHROOT_DIR); - - /* After possible chroot(2) */ - async_init(afuncs, CONF.ASYNC_PROCESSES, uid, gids, ngids); - - /* Things which shouldn't be part of the async pool processes */ - pth_init(); - async_init_pth(); - pth_mutex_init(&clenv_lock); - pth_mutex_init(&hosts_lock); - pth_mutex_init(&rcpt_lock); - pth_mutex_init(&mutexes_lock); - - /* Allocate necessary pools */ - /* Client nodes */ - pool_init(&clenv_pool, "clenv_pool", malloc, free, NULL, NULL, - sizeof(clientenv), - (65536 * CONF.ALLOC_BUFFERS) / sizeof(clientenv), 0, 0); - /* RCPT nodes */ - pool_init(&rcpt_pool, "rcpt_pool", malloc, free, NULL, NULL, - sizeof(rcptnode), - (16384 * CONF.ALLOC_BUFFERS) / sizeof(rcptnode), 0, 0); - /* Mutexes pool for mmfd */ - pool_init(&mutexes_pool, "mutexes_pool", malloc, free, NULL, NULL, - sizeof(struct mutexnode), - (16384 * CONF.ALLOC_BUFFERS) / sizeof(struct mutexnode), 0, 0); - /* Rate nodes */ - if (CONF.FLOOD_PROTECTION) { - pool_init(&hosts_pool, "hosts_pool", malloc, free, NULL, NULL, - sizeof(hostnode), CONF.FLOOD_CACHE, 1, 1); - hashtable_init(&hosts_table, "hosts_table", CONF.FLOOD_CACHE, 1, - malloc, free, mm_memcmp, mm_memhash32, FALSE); - threadattr = pth_attr_new(); - pth_attr_set(threadattr, PTH_ATTR_JOINABLE, TRUE); - hosts_table_thread = pth_spawn(threadattr, hosts_expire_thread, NULL); - } - /* Launch box directories cleaning thread */ - threadattr = pth_attr_new(); - pth_attr_set(threadattr, PTH_ATTR_JOINABLE, TRUE); - mmmail_db_gc_thread = pth_spawn(threadattr, db_gc_thread, NULL); - /* mmstr nodes */ - strlist = mmstrinit(malloc, free, 65536 * CONF.ALLOC_BUFFERS); - - if (hash_commands(commands, 4) && POOL_VALID(&clenv_pool) && - POOL_VALID(&rcpt_pool) && POOL_VALID(&mutexes_pool) && strlist && - (!CONF.FLOOD_PROTECTION || (POOL_VALID(&hosts_pool) && - HASHTABLE_VALID(&hosts_table) && - hosts_table_thread != NULL))) { - fdbcinit(&gfdf, &fdbc, CONF.GBANDWIDTH_IN * 1024, - CONF.GBANDWIDTH_OUT * 1024); - mmsql_init(&mmsqlfuncs); - - tcp_server("402 Server too busy, try again\r\n", - CONF.SERVER_NAMES, CONF.LISTEN_IPS, uid, gids, ngids, - CONF.MAX_IPS, CONF.MAX_PER_IP, CONF.CONNECTION_RATE, - CONF.CONNECTION_PERIOD, CONF.INPUT_TIMEOUT, - CONF.LISTEN_PORT, CONF.RESOLVE_HOSTS, handleclient); - - mmfreegidarray(gids); - ret = 0; - mmsql_close(); - mmsql_exit(); - } else { - printf("\nOut of memory\n\n"); - syslog(LOG_NOTICE, "* Out of memory"); - } - - if (strlist) - mmstrexit(); - if (hosts_table_thread != NULL) { - pth_abort(hosts_table_thread); - pth_join(hosts_table_thread, NULL); - } - if (mmmail_db_gc_thread != NULL) { - pth_abort(mmmail_db_gc_thread); - pth_join(mmmail_db_gc_thread, NULL); - } - if (HASHTABLE_VALID(&command_table)) - hashtable_destroy(&command_table, FALSE); - if (POOL_VALID(&command_pool)) - pool_destroy(&command_pool); - if (HASHTABLE_VALID(&hosts_table)) - hashtable_destroy(&hosts_table, FALSE); - if (POOL_VALID(&mutexes_pool)) - pool_destroy(&mutexes_pool); - if (POOL_VALID(&hosts_pool)) - pool_destroy(&hosts_pool); - if (POOL_VALID(&rcpt_pool)) - pool_destroy(&rcpt_pool); - if (POOL_VALID(&clenv_pool)) - pool_destroy(&clenv_pool); - - fdbcdestroy(&fdbc); - kill(0, SIGTERM); - exit(ret); -} - - -/* Here consists of our command functions */ - - -static int -all_noop(clientenv *clenv) -{ - reply(clenv->fdb, 250, FALSE, "Ok, nothing performed"); - - return (STATE_CURRENT); -} - - -static int -all_rset(clientenv *clenv) -{ - int nextstate = STATE_CURRENT; - fdbuf *fdb = clenv->fdb; - - if (clenv->buffer[4] == 0) { - if (!init_clientenv(clenv, TRUE)) - nextstate = STATE_ERROR; - else - reply(fdb, 250, FALSE, "Reset state"); - } else { - reply(fdb, 550, FALSE, "Command syntax error"); - REGISTER_ERROR(clenv); - } - - return (nextstate); -} - - -static int -all_quit(clientenv *clenv) -{ - reply(clenv->fdb, 221, FALSE, "%s closing connection", - clenv->iface->hostname); - - return (STATE_END); -} - - -static int -all_help(clientenv *clenv) -{ - int col; - fdbuf *fdb = clenv->fdb; - char *args[3], *cmdline = clenv->buffer, *tmp; - - /* First check if a topic was specified */ - if ((col = mm_straspl(args, cmdline, 2)) == 2) { - u_int32_t chash; - struct commandnode *nod; - - /* Help requested on a topic */ - nod = NULL; - if ((chash = mm_strpack32(args[1], 4)) != 0) - nod = (struct commandnode *)hashtable_lookup(&command_table, - &chash, sizeof(u_int32_t)); - col = 0; - if (nod != NULL) { - reply(fdb, 214, TRUE, nod->command->args); - reply(fdb, 214, TRUE, " %s", nod->command->desc); - col = 2; - } - - if (col > 0) - reply(fdb, 214, FALSE, "End of HELP information"); - else { - reply(fdb, 504, FALSE, "Unknown HELP topic"); - REGISTER_ERROR(clenv); - } - - } else { - register int i; - - /* No, display the topics */ - reply(fdb, 214, TRUE, "Available topics:"); - fdbwrite(fdb, "214-", 4); - col = 1; - for (i = 0; (tmp = commands[i].name) != NULL; i++) { - if (commands[i].desc != NULL) { - if (col == 0) - fdbwrite(fdb, "\r\n214-", 6); - col++; - if (col > 4) - col = 0; - fdbprintf(fdb, " %s", tmp); - } - } - fdbwrite(fdb, "\r\n", 2); - - reply(fdb, 214, TRUE, "For more information, use HELP "); - reply(fdb, 214, FALSE, "End of HELP information"); - } - - return (STATE_CURRENT); -} - - -static int -all_helo(clientenv *clenv) -{ - fdbuf *fdb = clenv->fdb; - char *args[3], *cmdline = clenv->buffer; - - if ((mm_straspl(args, cmdline, 2)) == 2) { - if (clenv->helo == NULL) { - if (valid_host(clenv, args[1], - CONF.RESOLVE_HELO ? HOST_RES : HOST_NORES, TRUE, - TRUE)) { - if ((clenv->helo = mmstrdup(args[1])) == NULL) - DEBUG_PRINTF("all_helo", "mmstrdup(%s)", args[1]); - reply(fdb, 250, FALSE, "%s ok", clenv->iface->hostname); - } else { - reply(fdb, 501, FALSE, "Invalid hostname"); - REGISTER_ERROR(clenv); - } - } else { - reply(fdb, 503, FALSE, "Duplicate HELO, use RSET or proceed"); - REGISTER_ERROR(clenv); - } - } else { - reply(fdb, 550, FALSE, "Command syntax error"); - REGISTER_ERROR(clenv); - } - - return (STATE_CURRENT); -} - - -static int -all_mail(clientenv *clenv) -{ - int nextstate = STATE_CURRENT; - fdbuf *fdb = clenv->fdb; - char addr[128]; - bool valid; - - if (!CONF.REQUIRE_HELO || clenv->helo != NULL) { - - if (clenv->from == NULL) { - - valid = FALSE; - if ((mm_strncasecmp(" FROM:<>", &clenv->buffer[4], 8)) == 0) { - /* Some systems use empty MAIL FROM like this, make sure - * that IP address or hostname is allowed to do this. - */ - valid = check_nofrom(clenv->c_ipaddr, clenv->c_hostname); - if (valid) - *addr = '\0'; - } else - valid = valid_address(clenv, addr, 128, clenv->buffer, - (CONF.RESOLVE_MX_MAIL) ? HOST_RES_MX : HOST_NORES); - - if (valid) { - if ((clenv->from = (char *)mmstrdup(addr)) != NULL) - reply(fdb, 250, FALSE, "Sender ok"); - else - nextstate = STATE_ERROR; - } else { - reply(fdb, 501, FALSE, "Invalid address"); - REGISTER_ERROR(clenv); - } - - } else { - reply(fdb, 503, FALSE, "Sender already specified"); - REGISTER_ERROR(clenv); - } - - } else { - reply(fdb, 503, FALSE, "Use HELO first"); - REGISTER_ERROR(clenv); - } - - return (nextstate); -} - - -static int -all_rcpt(clientenv *clenv) -{ - int nextstate = STATE_CURRENT; - fdbuf *fdb = clenv->fdb; - char addr[64], foraddr[64], *line = clenv->buffer; - int reason; - struct box_info boxinfo; - u_int64_t ahash; - bool valid, relay; - - /* I have opted for an elimination process here as there are many cases - * which can cause an RCPT to be refused, and alot of indenting was to - * be avoided for clarity. Functions could also be used but it has not - * been necessary this far, and we want the code performance to be optimal. - */ - relay = FALSE; - - if (clenv->from == NULL) { - reason = RCPT_NOFROM; - goto end; - } - - /* First make sure to not allow more RCPTs than CONF.MAX_RCPTS */ - if (!(DLIST_NODES(&clenv->rcpt) < CONF.MAX_RCPTS)) { - reason = RCPT_MANY; - if (CONF.STATFAIL_FLOOD) - mmstat(&clenv->pstat, STAT_UPDATE, 1, - "mmsmtpd|failed|flood|rcpt|%s|%s", - clenv->c_ipaddr, clenv->from); - goto end; - } - - /* Only continue if address seems valid */ - if (!valid_address(clenv, addr, 64, line, HOST_NORES)) { - reason = RCPT_INVALID; - goto end; - } - - /* Verify if existing address, if it isn't verify for any alias - * matching it and of course check for address validity again for - * safety. This way we make sure that an alias pattern does not over- - * ride an existing address, and that we only archive a message into - * an existing mailbox. is not modified if there exist no alias for - * the address. Otherwise, keeps the original address. - */ - valid = FALSE; - (void) mm_strcpy(foraddr, addr); - if (!(valid = local_address(&boxinfo, addr))) { - if (check_alias(addr)) { - if (!(valid = local_address(&boxinfo, addr))) - mmsyslog(0, LOGLEVEL, "Invalid alias address (%s)", - addr); - } - } - if (!valid) - reason = RCPT_UNKNOWN; -#if defined(MMMAIL_FILE) - if (CONF.RELAYING && !valid) { - /* Address is not local. If relaying is allowed, we must be - * able to verify that the address indeed belongs to a - * non-local domain, and if so, verify that the sender is - * allowed to relay messages. If it belongs to a local domain, - * we must treat it as invalid local address, however. - */ - if ((valid = address_relay_allow(clenv, &reason, foraddr))) { - if (CONF.RESOLVE_MX_RCPT) { - /* We know that the address is in a valid format, but we are - * now required to verify that the hostname has an MX record. - */ - char *domain; - - for (domain = foraddr; *domain != '@'; domain++) ; - domain++; - if (!valid_host(clenv, domain, HOST_RES_MX, FALSE, FALSE)) { - reason = RCPT_INVALID; - goto end; - } - } - relay = TRUE; - } - } -#endif /* defined(MMMAIL_FILE) */ - if (!valid) { - switch (reason) { - case RCPT_RELAY: - if (CONF.STATFAIL_RELAY) { - mmstat(&clenv->pstat, STAT_UPDATE, 1, - "mmsmtpd|failed|relay|%s|%s|%s", - clenv->c_ipaddr, clenv->from, foraddr); - } - break; - case RCPT_UNKNOWN: - if (CONF.STATFAIL_ADDRESS) { - mmstat(&clenv->pstat, STAT_UPDATE, 1, - "mmsmtpd|failed|address|%s|%s|%s", - clenv->c_ipaddr, clenv->from, addr); - } - break; - } - goto end; - } - - /* These only apply to local addresses */ - if (!relay) { - /* Ensure to observe allow filters if any set for box */ - if (boxinfo.filter) { - if (!box_filter_allow(addr, clenv->from, boxinfo.filter_type)) { - reason = RCPT_FILTER; - if (CONF.STATFAIL_FILTER) - mmstat(&clenv->pstat, STAT_UPDATE, 1, - "mmsmtpd|failed|filter|%s|%s|%s", - clenv->c_ipaddr, clenv->from, addr); - goto end; - } - } - /* Make sure mailbox quota limits are respected */ - if (!((boxinfo.size <= boxinfo.max_size) && - (boxinfo.msgs <= boxinfo.max_msgs))) { - mmsyslog(0, LOGLEVEL, "%s mailbox full (%ld,%ld %ld,%ld)", - addr, boxinfo.max_size, boxinfo.size, boxinfo.max_msgs, - boxinfo.msgs); - reason = RCPT_FULL; - if (CONF.STATFAIL_FULL) - mmstat(&clenv->pstat, STAT_UPDATE, 1, - "mmsmtpd|failed|full|%s", addr); - goto end; - } - } - - /* Make sure that we only allow one RCPT per mailbox (alias already - * redirected to it) - */ - { - register rcptnode *rnode; - register int cnt; - - ahash = mm_strhash64(addr); - cnt = 0; - DLIST_FOREACH(&clenv->rcpt, rnode) { - if (rnode->hash == ahash) { - reason = RCPT_EXISTS; - goto end; - } - cnt++; - if (cnt > 64) { - cnt = 0; - pth_yield(NULL); - } - } - } - - /* If CONF.FLOOD_PROTECTION is TRUE, make sure that we respect the rate - * of CONF.FLOOD_MESSAGES within CONF.FLOOD_EXPIRES for this client. - */ - if (CONF.FLOOD_PROTECTION) { - register hostnode *hnod; - register size_t len; - register char *entry; - - if (clenv->c_hostname != NULL) - entry = clenv->c_hostname; - else - entry = clenv->c_ipaddr; - len = mm_strlen(entry); - - valid = TRUE; - pth_mutex_acquire(&hosts_lock, FALSE, NULL); - /* First acquire our hostnode, or create it if required */ - if ((hnod = (hostnode *)hashtable_lookup(&hosts_table, entry, len + 1)) - == NULL) { - /* Create a new entry */ - if ((hnod = (hostnode *)pool_alloc(&hosts_pool, FALSE)) != NULL) { - mm_memcpy(hnod->host, entry, len + 1); - LR_INIT(&hnod->lr, CONF.FLOOD_MESSAGES, - CONF.FLOOD_EXPIRES * 60, time(NULL)); - hashtable_link(&hosts_table, (hashnode_t *)hnod, entry, - len + 1, FALSE); - } else { - valid = FALSE; - reason = RCPT_FLOOD; - mmsyslog(0, LOGLEVEL, "FLOOD_CACHE not large enough"); - } - } - if (valid) { - /* Check and update limits */ - if (!lr_allow(&hnod->lr, 1, 0, FALSE)) { - valid = FALSE; - reason = RCPT_FLOOD; - mmsyslog(0, LOGLEVEL, - "%08X Considered flood and rejected (%ld message(s) " - "within last %ld minute(s))", - clenv->id, LR_POSTS(&hnod->lr), CONF.FLOOD_EXPIRES); - } - } - pth_mutex_release(&hosts_lock); - - if (!valid) { - if (CONF.STATFAIL_FLOOD) - mmstat(&clenv->pstat, STAT_UPDATE, 1, - "mmsmtpd|failed|flood|message|%s|%s|%s", - clenv->c_ipaddr, clenv->from, addr); - goto end; - } - } - - /* Finally append new RCPT node to list */ - { - register rcptnode *rnode; - - reason = RCPT_ERROR; - pth_mutex_acquire(&rcpt_lock, FALSE, NULL); - rnode = (rcptnode *)pool_alloc(&rcpt_pool, FALSE); - pth_mutex_release(&rcpt_lock); - if (rnode != NULL) { - mm_strcpy(rnode->address, (relay ? foraddr : addr)); - mm_strcpy(rnode->foraddress, foraddr); - rnode->hash = ahash; - rnode->relay = relay; - DLIST_APPEND(&clenv->rcpt, (node_t *)rnode); - reason = RCPT_OK; - clenv->rcpts++; - } else { - DEBUG_PRINTF("all_rcpt", "pool_alloc(rcpt_pool)"); - nextstate = STATE_ERROR; - } - } - -end: - - /* Reply with appropriate message */ - if (reason != RCPT_OK) - REGISTER_ERROR(clenv); - reply(fdb, rcpt_msg[reason].code, FALSE, rcpt_msg[reason].msg); - - return (nextstate); -} - - -static int -all_data(clientenv *clenv) -{ - int nextstate = STATE_CURRENT; - fdbuf *fdb = clenv->fdb; - - if (clenv->buffer[4] == '\0') { - if (clenv->from != NULL) { - if (DLIST_NODES(&clenv->rcpt) > 0) { - if (!do_data(clenv)) - nextstate = STATE_ERROR; - else - clenv->messages++; - } else { - reply(fdb, 502, FALSE, "Use RCPT first"); - REGISTER_ERROR(clenv); - } - } else { - reply(fdb, 503, FALSE, "Use MAIL and RCPT first"); - REGISTER_ERROR(clenv); - } - } else { - reply(fdb, 550, FALSE, "Command syntax error"); - REGISTER_ERROR(clenv); - } - - return (nextstate); -} - - -static int -all_beer(clientenv *clenv) -{ - reply(clenv->fdb, 420, FALSE, "Here, enjoy!"); - - return (STATE_CURRENT); -} - - - - -/* Used to initialize command hash table */ -static bool -hash_commands(struct command *cmd, size_t min) -{ - int i; - - /* We do not care for any unfreed resources, the program will free them - * and exit if we return FALSE. - */ - if (!pool_init(&command_pool, "command_pool", malloc, free, NULL, NULL, - sizeof(struct commandnode), 64, 1, 0) || - !hashtable_init(&command_table, "command_table", 64, 1, malloc, - free, commandnode_keycmp, commandnode_keyhash, TRUE)) - return FALSE; - - for (i = 0; cmd->name != NULL; cmd++, i++) { - struct commandnode *nod; - - if ((nod = (struct commandnode *)pool_alloc(&command_pool, FALSE)) - == NULL) - return FALSE; - if ((nod->hash = mm_strpack32(cmd->name, min)) == 0) - return FALSE; - nod->command = cmd; - nod->index = i; - if (!hashtable_link(&command_table, (hashnode_t *)nod, &nod->hash, - sizeof(u_int32_t), TRUE)) { - DEBUG_PRINTF("hash_commands", "hashtable_link(%s)", cmd->name); - return FALSE; - } - } - - return TRUE; -} - - -/* A quick hashtable_hash() replacement which already deals with unique - * 32-bit values - */ -/* ARGSUSED */ -static u_int32_t -commandnode_keyhash(const void *data, size_t len) -{ - return *((u_int32_t *)data); -} - - -/* A quick memcmp() replacement which only needs to compare two 32-bit values - */ -/* ARGSUSED */ -static int -commandnode_keycmp(const void *src, const void *dst, size_t len) -{ - return *((u_int32_t *)src) - *((u_int32_t *)dst); -} - - -/* Function used to return standard RFC result strings to the client, - * and returns FALSE on error. - * NOTE: As we are no longer calling write() directly, but fdbprintf() - * buffering function instead, it is no longer necessary to check the reply() - * return value each time it is called. We made sure to ignore SIGPIPE, - * and we let the main state switcher loop check connection state via - * fdbgets(). - */ -static bool -reply(fdbuf *fdb, int code, bool cont, const char *fmt, ...) -{ - char buf[1024]; - va_list arg_ptr; - bool err = TRUE; - - *buf = 0; - va_start(arg_ptr, fmt); - vsnprintf(buf, 1023, fmt, arg_ptr); - va_end(arg_ptr); - - if (cont) - err = fdbprintf(fdb, "%d-%s\r\n", code, buf); - else - err = fdbprintf(fdb, "%d %s\r\n", code, buf); - - mmsyslog(3, LOGLEVEL, "> %d (%s)", code, buf); - - return (err); -} - - -/* Allocate and prepare a clenv. Returns NULL on error */ -static clientenv * -alloc_clientenv(void) -{ - clientenv *clenv; - - pth_mutex_acquire(&clenv_lock, FALSE, NULL); - clenv = (clientenv *)pool_alloc(&clenv_pool, TRUE); - pth_mutex_release(&clenv_lock); - - if (clenv != NULL) { - mmstat_init(&clenv->vstat, TRUE, TRUE); - mmstat_init(&clenv->pstat, TRUE, FALSE); - } - - return (clenv); -} - - -/* Useful on RSET to reset initial clenv state, - * returns TRUE on success or FALSE on error - */ -static bool -init_clientenv(clientenv *clenv, bool helo) -{ - if (helo && clenv->helo != NULL) - clenv->helo = mmstrfree(clenv->helo); - if (clenv->from != NULL) - clenv->from = mmstrfree(clenv->from); - empty_rcpts(&clenv->rcpt); - - return (TRUE); -} - - -/* Frees all ressources allocated by a clenv */ -static clientenv * -free_clientenv(clientenv *clenv) -{ - if (clenv->helo != NULL) - mmstrfree(clenv->helo); - if (clenv->from != NULL) - mmstrfree(clenv->from); - empty_rcpts(&clenv->rcpt); - - pth_mutex_acquire(&clenv_lock, FALSE, NULL); - pool_free((pnode_t *)clenv); - pth_mutex_release(&clenv_lock); - - return (NULL); -} - - -/* Useful to free all rcpts for a clientenv. - * XXX If we used a pool_t per clientenv for these we would simply destroy - */ -static void -empty_rcpts(list_t *lst) -{ - node_t *nod, *tmp; - - pth_mutex_acquire(&rcpt_lock, FALSE, NULL); - - for (nod = DLIST_TOP(lst); nod != NULL; nod = tmp) { - tmp = DLIST_NEXT(nod); - pool_free((pnode_t *)nod); - } - DLIST_INIT(lst); - - pth_mutex_release(&rcpt_lock); -} - - -/* Checks in the list of aliases for any pattern matching the address, and - * map it to the real address to redirect to, replacing supplied address. - * The addr char array must at least be 64 bytes. Returns FALSE if no alias - * exist for the address, or TRUE on success. - * XXX Could possibly use an async function, if we allow the async processes - * to connect to MySQLd. - */ -static bool -check_alias(char *addr) -{ - bool res = FALSE; - char oaddr[64], query[1024], *user, *domain; - MYSQL_RES *mysqlres; - - /* We know that the address is valid, since it has been verified already. - * Copy it to not modify our supplied address, and to keep a backup of the - * user name when performing betch-match operation. Then split the backup - * into user and domain. - */ - (void) mm_strcpy(oaddr, addr); - for (user = oaddr, domain = oaddr; *domain != '@'; domain++) ; - *domain++ = '\0'; - - snprintf(query, 1023, "SELECT alias_pattern,alias_box FROM alias " - "WHERE alias_domain='%s'", domain); - if ((mysqlres = mmsql_query(query, mm_strlen(query))) != NULL) { - if ((mysql_num_rows(mysqlres)) > 0) { - char pat[64]; - int cur = 0, max = -1, cnt = 0; - MYSQL_ROW *row; - unsigned long *lengths; - - /* Find best match */ - while ((row = (MYSQL_ROW *)mysql_fetch_row(mysqlres)) - != NULL) { - lengths = mysql_fetch_lengths(mysqlres); - if (row[0] != NULL && row[1] != NULL) { - mm_memcpy(pat, row[0], lengths[0]); - pat[lengths[0]] = '\0'; - if ((cur = best_match(user, pat)) != -1) { - if (cur > max) { - /* Matches better, remember this one */ - max = cur; - mm_memcpy(addr, row[1], lengths[1]); - addr[lengths[1]] = '\0'; - } - } - } - cnt++; - if (cnt > 64) { - cnt = 0; - pth_yield(NULL); - } - } - if (max > -1) - res = TRUE; - } - mysqlres = mmsql_free_result(mysqlres); - } - - return (res); -} - - -/* Depending on which is set of and/or , returns TRUE if any - * of both matched an entry. - */ -static bool -check_nofrom(const char *addr, const char *host) -{ - bool res = FALSE; - MYSQL_RES *mysqlres; - - if (addr == NULL && host == NULL) - return (FALSE); - - if ((mysqlres = mmsql_query("SELECT nofrom_pattern FROM nofrom", -1)) - != NULL) { - if ((mysql_num_rows(mysqlres)) > 0) { - int cnt = 0; - MYSQL_ROW *row; - unsigned long *lengths; - - while ((row = (MYSQL_ROW *)mysql_fetch_row(mysqlres)) != NULL) { - lengths = mysql_fetch_lengths(mysqlres); - if (row[0] != NULL) { - char pat[64]; - - mm_memcpy(pat, row[0], lengths[0]); - pat[lengths[0]] = '\0'; - if (addr != NULL) { - if ((best_match(addr, pat)) != -1) { - res = TRUE; - break; - } - } - if (host != NULL) { - if ((best_match(host, pat)) != -1) { - res = TRUE; - break; - } - } - } - cnt++; - if (cnt > 64) { - cnt = 0; - pth_yield(NULL); - } - } - } - mysqlres = mmsql_free_result(mysqlres); - } - - return (res); -} - - -/* - * Used to ensure that only a single server instance with the same - * configuration runs at the same time. - */ -static int -lock_check(const char *path) -{ - int fd; - - if ((fd = open(path, O_CREAT | O_TRUNC | O_WRONLY, 0600)) != -1) { - if (flock(fd, LOCK_EX | LOCK_NB) == 0) - return 0; - (void) close(fd); - } - - return -1; -} - - -/* Returns -1 if does not match '*' and '?' wildcards pattern. - * Otherwise returns > -1, a value representing the number of literal - * characters in which exactly matched . This us useful to evaluate - * the best match among a list of patterns. - */ -static int -best_match(const char *str, const char *pat) -{ - int lit = 0; - - for (; *pat != '*'; pat++, str++) { - if (*str == '\0') { - if (*pat != '\0') - return -1; - else - return lit; - } - if (*str == *pat) - lit++; - else - if(*pat != '?') - return -1; - } - while (pat[1] == '*') - pat++; - do { - register int tmp; - - if ((tmp = best_match(str, pat + 1)) != -1) - return (lit + tmp); - } while (*str++ != '\0'); - - return -1; -} - - -/* Returns FALSE if this address doesn't exist in our local mailboxes. - * Otherwise it returns information about the mailbox via supplied pointers. - */ -static bool -local_address(struct box_info *boxinfo, const char *address) -{ - bool res = FALSE; - char line[1024]; - MYSQL_RES *mysqlres; - MYSQL_ROW *row; - unsigned int fields; - unsigned long *lengths; - - /* Query mysql to see if this address exists, and get limits/status */ - (void) snprintf(line, 1023, - "SELECT box_max_size,box_size,box_max_msgs,box_msgs,box_filter," - "box_filter_type FROM box WHERE box_address='%s'", address); - - if ((mysqlres = mmsql_query(line, mm_strlen(line))) != NULL) { - - if ((mysql_num_rows(mysqlres)) == 1 - && (row = (MYSQL_ROW *)mysql_fetch_row(mysqlres)) != NULL) { - if ((fields = mysql_num_fields(mysqlres)) == 6) { - lengths = mysql_fetch_lengths(mysqlres); - if (row[0] != NULL && row[1] != NULL && row[2] != NULL && - row[3] != NULL && row[4] != NULL && row[5] != NULL) { - int v; - - mm_memcpy(line, row[0], lengths[0]); - line[lengths[0]] = '\0'; - boxinfo->max_size = atol(line); - mm_memcpy(line, row[1], lengths[1]); - line[lengths[1]] = '\0'; - boxinfo->size = atol(line); - mm_memcpy(line, row[2], lengths[2]); - line[lengths[2]] = '\0'; - boxinfo->max_msgs = atol(line); - mm_memcpy(line, row[3], lengths[3]); - line[lengths[3]] = '\0'; - boxinfo->msgs = atol(line); - mm_memcpy(line, row[4], lengths[4]); - line[lengths[4]] = '\0'; - v = atol(line); - boxinfo->filter = (v == 1 ? TRUE : FALSE); - mm_memcpy(line, row[5], lengths[5]); - line[lengths[5]] = '\0'; - boxinfo->filter_type = *line; - res = TRUE; - } else - DEBUG_PRINTF("local_address", "row[x] == NULL"); - } else - DEBUG_PRINTF("local_address", "mysql_num_fields()"); - } - - mysqlres = mmsql_free_result(mysqlres); - } else - DEBUG_PRINTF("local_address", "mmsql_query(%s)", line); - - return res; -} - - -/* Verifies if mailbox filters allow to post */ -static bool -box_filter_allow(const char *toaddr, const char *fromaddr, char filter_type) -{ - bool res; - char query[1024]; - MYSQL_RES *mysqlres; - - /* Default return code depends on filter type */ - res = (filter_type == 'A' ? TRUE : FALSE); - - snprintf(query, 1023, - "SELECT filter_pattern FROM filter WHERE filter_address='%s'", - toaddr); - if ((mysqlres = mmsql_query(query, mm_strlen(query))) != NULL) { - if ((mysql_num_rows(mysqlres)) > 0) { - char pat[64]; - MYSQL_ROW *row; - unsigned long *lengths; - - /* - * Filter types are 'A' (allow by default) and - * 'D' (deny by * default). - */ - while ((row = (MYSQL_ROW *)mysql_fetch_row(mysqlres)) != NULL) { - lengths = mysql_fetch_lengths(mysqlres); - if (row[0] != NULL) { - (void) mm_memcpy(pat, row[0], lengths[0]); - pat[lengths[0]] = '\0'; - if (best_match(fromaddr, pat) != -1) { - /* Inverse return code and break */ - res = !res; - break; - } - } - } - - } else - DEBUG_PRINTF("box_filter_allow", "mysql_num_rows()"); - mysqlres = mmsql_free_result(mysqlres); - } else - DEBUG_PRINTF("box_filter_allow", "mmsql_query(%s)", query); - - return res; -} - - -/* Fills str which should be at least 32 bytes in length with current time */ -static void -rfc_time(char *str) -{ - /* Thu, 07 Dec 2000 07:36:15 -0000 */ - const static char *days[] = { - "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" - }; - const static char *months[] = { - "Jan", "Feb", "Mar", "Apr", "May", "Jun", - "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" - }; - time_t secs; - struct tm *gtim; - - secs = time(NULL); - gtim = gmtime(&secs); - - snprintf(str, 32, "%s, %02d %s %04d %02d:%02d:%02d -0000", - days[gtim->tm_wday], gtim->tm_mday, months[gtim->tm_mon], - gtim->tm_year + 1900, gtim->tm_hour, gtim->tm_min, - gtim->tm_sec); -} - - -/* Returns whether or not supplied address is valid, and if it is return the - * parsed address in the supplied string. String should be at least - * bytes. can only be NULL if HOST_NORES is used for . - */ -static bool -valid_address(clientenv *clenv, char *to, size_t len, char *addr, int res) -{ - char *ptr, *a, *h; - - mm_strlower(addr); - - /* First locate required @ */ - for (ptr = addr; *ptr != '\0' && *ptr != '@'; ptr++) ; - if (*ptr == '\0') - return (FALSE); - h = ptr + 1; - /* Then scan to the left */ - for (ptr--; ptr >= addr && VALID_ADDR_CHAR(*ptr); ptr--) ; - if (h - ptr < 3 || ptr < addr) - return (FALSE); - a = ++ptr; - /* Now to the right */ - for (ptr = h; *ptr != '\0' && VALID_ADDR_CHAR(*ptr); ptr++) ; - if (ptr - h < 2) - return (FALSE); - *ptr = '\0'; - /* Now validate hostname part */ - if (valid_host(clenv, h, res, FALSE, TRUE)) { - mm_strncpy(to, a, len - 1); - return (TRUE); - } - - return (FALSE); -} - - -static bool -valid_host(clientenv *clenv, char *host, int res, bool addr, bool sanity) -{ - - if (addr && res != HOST_RES_MX && valid_ipaddress(host)) - return TRUE; - - if (sanity) { - register char *ptr; - - mm_strlower(host); - - /* First make sure all characters are valid */ - for (ptr = host; *ptr != '\0'; ptr++) - if (!VALID_HOST_CHAR(*ptr)) - return FALSE; - - /* Now verify that all parts of the hostname are starting with - * an alphanumeric char - */ - ptr = host; - while (*ptr != '\0') { - if (!isalnum(*ptr)) - return FALSE; - /* Find next host part */ - while (*ptr != '\0' && *ptr != '.') ptr++; - if (*ptr == '.') { - ptr++; - continue; - } - if (*ptr == '\0') break; - ptr++; - } - } - - /* Hostname seems valid, last sanity checking test consists of optional - * resolving - */ - if (res != HOST_NORES) { - char answer[64]; - - if (res == HOST_RES_MX) { - /* Check for an MX DNS IP address entry for it */ - if ((a_res_query(clenv, host, C_IN, T_MX, answer, - sizeof(answer) - 1)) == -1) - return FALSE; - } else if (res == HOST_RES) { - /* Check if hostname resolves to normal A record */ - if ((a_res_query(clenv, host, C_IN, T_A, answer, - sizeof(answer) - 1)) == -1) - return FALSE; - } - } - - return TRUE; -} - - -/* Some more parsing magic for IP address sanity checking */ -static bool -valid_ipaddress(const char *addr) -{ - char unit[5], *uptr, *utptr; - int units; - - for (units = 0, uptr = unit, utptr = unit + 4; uptr < utptr; addr++) { - if (*addr == '\0' || *addr == '.') { - if (uptr > unit && units < 4) { - register int n; - - *uptr = '\0'; - n = atoi(unit); - if (n < 0 || n > 255) - break; - uptr = unit; - units++; - } else return (FALSE); - if (*addr == '\0') - break; - } else if (isdigit(*addr)) - *uptr++ = *addr; - else - return (FALSE); - } - if (!(units == 4 && *addr == '\0')) - return (FALSE); - - return (TRUE); -} - - -/* This function is called for every line read from the message */ -static int -validate_msg_line(char *line, ssize_t *len, int *res, void *udata) -{ - register struct validate_udata *ud = udata; - int eres = FDBRB_OK; - - /* Verify for message termination indicator, a single '.', which is both - * valid in headers or body. If we're still in headers, we ensure to - * create missing headers and append the end of headers empty line, to - * avoid broken messages. - */ - if (*len == 1 && *line == '.') { - if (ud->header) { - *line = '\0'; - *len = 0; - eres = FDBRB_ADDSTOP; - goto endheader; - } else - return FDBRB_STOP; - } - - if (ud->header) { - - /* Still reading header and expecting ones */ - char *ptr; - - /* Empty line means that body will follow */ - if (*len == 0) - goto endheader; - - /* Ensure that entry seems a valid message header or also stop. - * Basically, make sure that a header only comports alphanumeric - * characters and dashes in the name, and that the name follows by - * ': '. Also allow continueing header lines which begin with - * spaces/tabs. - */ - if (*line != '\t' && *line != ' ') { - for (ptr = line; *ptr != '\0' && (isalnum(*ptr) || *ptr == '-'); - ptr++) ; - if (*ptr != ':') - goto endheader; - } - - /* Count number of Received: headers (SMTP hops) */ - if (mm_strncmp(line, "Received:", 9) == 0) { - ud->hops++; - if (ud->hops > CONF.MAX_HOPS) { - /* Exceeded allowed number of hops, cancel reception */ - *res = CFDBRB_HOPS; - return FDBRB_STOP; - } - return FDBRB_OK; - } - - /* Now verify for existance of headers we consider mandatory. - * We'll create them if necessary. - */ - /* - * This one seems to not strictly follow RFC example as for case - * sensitivity, I often see Message-ID: and Message-id:, which caused - * mmmail to add an additional Message-Id: line if not doing this - * check case insensitively. - */ - if (mm_strncasecmp(line, "Message-Id:", 11) == 0) - ud->msgid = TRUE; - else if (mm_strncmp(line, "Date:", 5) == 0) - ud->date = TRUE; - else if (mm_strncmp(line, "From:", 5) == 0) - ud->from = TRUE; - else if (mm_strncmp(line, "To:", 3) == 0) - ud->to = TRUE; - - return FDBRB_OK; - - } else { - - /* Reading message body */ - - /* ".." lines must be converted to "." ones */ - if (*len == 2 && line[0] == '.' && line[1] == '.') { - line[1] = '\0'; - (*len)--; - } - - return FDBRB_OK; - - } - -endheader: - - /* We reached end of headers */ - ud->header = FALSE; - - { - char tline[1024], tdata[32]; - - /* Create the headers we consider mendatory if they were not supplied. - * We append them after all headers that were supplied, this way the - * Received: lines are guaranteed to be first. Note that this is only - * safe if the total expansion we cause does not exceed 1024 bytes, - * which buffer is guarenteed to have been reserved for a message line - * by mmfd(3)'s fdbreadbuf(). Our additionnal expansion will never - * exceed 320 bytes in this case. - */ - *tline = '\0'; - if (!ud->msgid) { - iso_time(tdata); - (void) snprintf(tline, 1023, "Message-Id: <%s.%08lX-%lu@%s>\r\n", - tdata, ud->clenv->id, ud->clenv->messages, - ud->clenv->iface->hostname); - } - if (!ud->date) { - rfc_time(tdata); - (void) snprintf(tline, 1023, "%sDate: %s\r\n", - tline, tdata); - } - if (!ud->from) - (void) snprintf(tline, 1023, "%sFrom: %s\r\n", - tline, ud->clenv->from); - if (!ud->to) - (void) snprintf(tline, 1023, "%sTo: undisclosed-recipients:;\r\n", - tline); - - if (*len == 0) { - /* Valid end of header, an empty line. If no headers to add, all - * is good. Otherwise, we must simply replace the current line by - * the headers plus an empty line. Because the headers already - * contain a newline, we just copy it over the current line, - * fdbreadbuf() will append an additional one automatically. - */ - if (*tline != '\0') - *len = (mm_strcpy(line, tline) - line); - } else { - /* Invalid end of header, we must insert our headers, if any, - * before the current line, along with an empty line. - * Unfortunately, this could discard some bytes at the end of the - * invalid last header line (the first body line) if it was too - * long. However, this was a malformed message anyways and needed - * major fixing. We could have errored instead if we were strict. - */ - (void) mm_strncat(tline, "\r\n", 1023); - (void) mm_strncat(tline, line, 1023); - *len = (mm_strcpy(line, tline) - line); - } - } - - return eres; -} - - -/* This function is called by STATE_DATA and permits the client to send - * the message data, respecting expected limits. Returns FALSE if the state - * should switch to STATE_ERROR, on fatal error (i.e. out of memory) - */ -static bool -do_data(clientenv *clenv) -{ - struct fdbrb_buffer *fdbrb; - int res, err = DATA_INTERNAL; - bool ok = FALSE; - struct validate_udata ud; - - reply(clenv->fdb, data_msg[DATA_SUBMIT].code, FALSE, - data_msg[DATA_SUBMIT].msg); - fdbflushw(clenv->fdb); - - /* Call our famous fdbreadbuf() which will read lines in a single buffer - * and validate them via the validate_msg_line() function (above). - * We restict the maximum length of a single line to 1024 characters - * and are starting with an initial buffer of 32K, buffer which will - * double in size whenever required. Of course don't read more than - * CONF.MAX_DATA_SIZE bytes or CONF.MAX_DATA_LINES lines. - * See mmfd(3) man page for details, and mmlib/mmfd.c - */ - ud.hops = 0; - ud.msgid = ud.date = ud.from = ud.to = FALSE; - ud.header = TRUE; - ud.clenv = clenv; - res = fdbreadbuf(&fdbrb, clenv->fdb, 32768, 1024, CONF.MAX_DATA_SIZE, - CONF.MAX_DATA_LINES, validate_msg_line, &ud, FALSE); - - /* Map results to DATA suitable ones */ - switch (res) { - case FDBRB_MEM: - mmsyslog(0, LOGLEVEL, "%08X * Out of memory", clenv->id); - err = DATA_INTERNAL; - REGISTER_ERROR(clenv); - break; - case FDBRB_OVERFLOW: - mmsyslog(0, LOGLEVEL, "%08X * Message size too large", clenv->id); - err = DATA_OVERFLOW; - REGISTER_ERROR(clenv); - break; - case FDBRB_TIMEOUT: - mmsyslog(0, LOGLEVEL, "%08X * Input timeout", clenv->id); - if (CONF.STATFAIL_TIMEOUT) - mmstat(&clenv->pstat, STAT_UPDATE, 1, "mmsmtpd|failed|timeout|%s", - clenv->c_ipaddr); - break; - case FDBRB_EOF: - mmsyslog(0, LOGLEVEL, "%08X * Unexpected EOF", clenv->id); - break; - case CFDBRB_HOPS: - mmsyslog(0, LOGLEVEL, "%08X * Too many hops", clenv->id); - err = DATA_HOPS; - REGISTER_ERROR(clenv); - break; - case FDBRB_OK: - ok = TRUE; - break; - } - - if (ok) { - - /* XXX The following could easily simply be provided by separately - * compiled mmsmtpd modules, designed to support multple storage - * methods, as do_data_store() or such. But, we only support MySQL - * and file storage for now... Which suffices for me. - */ - -#if defined(MMMAIL_MYSQL) - - ok = do_data_mysql(clenv, fdbrb); - -#elif defined(MMMAIL_FILE) - - ok = do_data_file(clenv, fdbrb); - -#else -#error "One of MMMAIL_MYSQL or MMMAIL_FILE must be #defined!" -#endif - - } - - fdbfreebuf(&fdbrb); /* Internally only frees if not already freed */ - if (ok) - err = DATA_OK; - reply(clenv->fdb, data_msg[err].code, FALSE, data_msg[err].msg); - - /* Reset mail state (and free RCPTs) */ - init_clientenv(clenv, FALSE); - - return (ok); -} - -/* Create a Received: line, isolated to prevent code duplication among - * different storage methods. Returns length of received line in bytes. - */ -inline static size_t -do_data_received(char *line, size_t len, clientenv *clenv, rcptnode *rnode, - const char *smtptime) -{ - (void) snprintf(line, len - 1, - "Received: from %s ([%s] HELO=%s)\r\n\tby %s (%s) " - "with SMTP\r\n\tid %08lX-%lu for <%s>;\r\n\t%s\r\n", - (clenv->c_hostname ? clenv->c_hostname : "(unresolved)"), - clenv->c_ipaddr, - (clenv->helo ? clenv->helo : "(unidentified)"), - clenv->iface->hostname, DAEMON_VERSION, clenv->id, - clenv->messages, rnode->foraddress, smtptime); - - return mm_strlen(line); -} - -/* Used to update mailbox quotas. Isolated to prevent code duplication among - * different storage methods. Note that this must be done under a lock when - * necessary for consistency with actual message storage data. - */ -inline static bool -do_data_update(rcptnode *rnode, size_t len) -{ - char line[1024]; - bool ok = TRUE; - - snprintf(line, 1023, - "UPDATE box SET box_size=box_size+%ld," - "box_msgs=box_msgs+1,box_in=NOW() WHERE " - "box_address='%s'", - (long)len, rnode->address); - if (!mmsql_command(line, mm_strlen(line))) { - DEBUG_PRINTF("do_data", "mmsql_command(%s)", line); - ok = FALSE; - } - - return ok; -} - -/* Record statistics using mmstat(3) facility, called by do_data to prevent - * code duplication among different storage methods. - */ -static void -do_data_stats(clientenv *clenv, rcptnode *rnode, size_t len) -{ - char *domptr; - - mmstat_transact(&clenv->pstat, TRUE); - - /* Record per-box statistics. Note that when aliases are used, the actual - * target mailbox is used. - */ - mmstat(&clenv->pstat, STAT_UPDATE, 1, "mmmail|box|%s|messages-in", - rnode->address); - mmstat(&clenv->pstat, STAT_UPDATE, len, "mmmail|box|%s|bytes-in", - rnode->address); - - /* And per-domain ones. The address was previously validated successfully - * and the '@' character is guarenteed to be present for mm_strchr(). - */ - domptr = mm_strchr(rnode->address, '@'); - domptr++; - mmstat(&clenv->pstat, STAT_UPDATE, 1, "mmmail|domain|%s|messages-in", - domptr); - mmstat(&clenv->pstat, STAT_UPDATE, len, "mmmail|domain|%s|bytes-in", - domptr); - - mmstat_transact(&clenv->pstat, FALSE); -} - - -#if defined(MMMAIL_MYSQL) - -static bool -do_data_mysql(clientenv *clenv, struct fdbrb_buffer *fdbrb) -{ - char line[1024], line2[2048], smtptime[32], *tmp, *query; - rcptnode *rnode; - bool ok = TRUE; - - /* Allocate query buffer for mysql_real_query(), should be large - * enough to handle the worst of cases where each character would - * be escaped to two chars, and must also hold the rest of the - * query string. We first process the message data through - * mysql_escape_string(), leaving enough room for the query and our - * "Received:" line, which will be copied before the message buffer - * for each RCPT. The message buffer will start at offset 2048 - * to make sure that there is enough room to insert the - * RCPT-specific data (query+received). - */ - if ((query = malloc((fdbrb->current * 2) + 2053)) != NULL) { - size_t len, qlen, tlen, clen; - - /* Prepare message buffer for mysql query */ - clen = fdbrb->current; /* Used after freeing buffer as well */ - tmp = &query[2048]; - tmp += mysql_escape_string(tmp, fdbrb->array, clen); - *tmp++ = '\''; - *tmp++ = ')'; - *tmp++ = '\0'; - qlen = tmp - &query[2048]; - rfc_time(smtptime); - - /* For each RCPT, create query and execute it */ - DLIST_FOREACH(&clenv->rcpt, rnode) { - /* Use the common message buffer, but append the query and - * message line before it (in it's 2048 bytes free area) - */ - do_data_received(line, 1024, clenv, rnode, smtptime); - tlen = mm_strlen(line) + clen; - snprintf(line2, 255, - "INSERT INTO mail (mail_box,mail_created,mail_size," - "mail_data) VALUES('%s',NOW(),%ld,'", - rnode->address, (long)tlen); - tmp = line2 + mm_strlen(line2); - tmp += mysql_escape_string(tmp, line, mm_strlen(line)); - len = tmp - line2; - tmp = &query[2048 - len]; - mm_memcpy(tmp, line2, len); - - /* Query buffer prepared, execute query. This glock is - * required for safety between the two queries which have - * to be performed within a single transaction. See - * mmlib/mmsql.c for implementation details; Currently uses - * MySQL GET_LOCK() and RELEASE_LOCK(), which contrary to - * table locking will permit to only cause the current thread - * to sleep rather than the whole process in this case. - */ - mmsql_glock("mmmail_boxmail"); - if (!mmsql_command(tmp, qlen + len)) { - mmsyslog(0, LOGLEVEL, "mmsql_command(%s)", tmp); - ok = FALSE; - break; - } else { - u_int64_t id; - - /* Obtain auto-increment value usd in last command */ - id = mmsql_last_auto_id(); - - if (!(ok = do_data_update(rnode, tlen))) { - /* Delete previous successful entry, since updating quota - * information did not succeed, and it must always be - * accurate according to actual mail data. - */ - snprintf(line, 1023, - "DELETE FROM mail WHERE mail_id=%llu", id); - (void) mmsql_command(line, mm_strlen(line)); - } - } - mmsql_gunlock("mmmail_boxmail"); - - if (!ok) - break; - - /* Everything successful, record statistics */ - do_data_stats(clenv, rnode, tlen); - } - - free(query); - } else { - DEBUG_PRINTF("do_data", - "malloc(%d)", (int)(fdbrb->current * 2) + 2053); - REGISTER_ERROR(clenv); - ok = FALSE; - } - - return ok; -} - -#elif defined(MMMAIL_FILE) - -/* Returns TRUE if the client address/hostname is allowed to relay messages - * for non-local addresses, or FALSE otherwise, with reason set to either - * RCPT_UNKNOWN (destination address on a locally handled domain but - * unexisting) or RCPT_RELAY (relay denied for sender address/hostname). - * If TRUE is returned, the post can be relayed, since it does not belong to - * any local domains we are handling and that the client has relaying rights. - */ -bool -address_relay_allow(clientenv *clenv, int *reason, const char *addr) -{ - bool res = TRUE; - char query[1024]; - const char *domain; - MYSQL_RES *mysqlres; - - /* Is address to a local domain but unknown to us? */ - - /* We know that the supplied address is valid, it thus must have '@'. - * Set domain pointer to start of domain name. - */ - for (domain = addr; *domain != '@'; domain++) ; - domain++; - /* Query database entries and search for any matching pattern. */ - (void) snprintf(query, 1023, "SELECT relaylocal_pattern FROM relaylocal"); - if ((mysqlres = mmsql_query(query, mm_strlen(query))) != NULL) { - if ((mysql_num_rows(mysqlres)) > 0) { - char pat[64]; - MYSQL_ROW *row; - unsigned long *lengths; - - while ((row = (MYSQL_ROW *)mysql_fetch_row(mysqlres)) != NULL) { - lengths = mysql_fetch_lengths(mysqlres); - if (row[0] != NULL) { - mm_memcpy(pat, row[0], lengths[0]); - pat[lengths[0]] = '\0'; - if (best_match(domain, pat) != -1) { - res = FALSE; - *reason = RCPT_UNKNOWN; - break; - } - } - } - } else - DEBUG_PRINTF("address_relay_allow", "mysql_num_rows()"); - mysqlres = mmsql_free_result(mysqlres); - } else - DEBUG_PRINTF("address_relay_allow", "mmsql_query(%s)", query); - - /* Return with error immediately if address is locally handled */ - if (!res) - return res; - - /* No, so it appears that it would need relaying. Is the client then - * allowed to relay messages through us? Verify via the client's IP - * address and/or hostname. - */ - - res = FALSE; - - (void) snprintf(query, 1023, "SELECT relayfrom_pattern FROM relayfrom"); - if ((mysqlres = mmsql_query(query, mm_strlen(query))) != NULL) { - if ((mysql_num_rows(mysqlres)) > 0) { - char pat[64]; - MYSQL_ROW *row; - unsigned long *lengths; - - while ((row = (MYSQL_ROW *)mysql_fetch_row(mysqlres)) != NULL) { - lengths = mysql_fetch_lengths(mysqlres); - if (row[0] != NULL) { - mm_memcpy(pat, row[0], lengths[0]); - pat[lengths[0]] = '\0'; - if (clenv->c_ipaddr != NULL) { - if (best_match(clenv->c_ipaddr, pat) != -1) { - res = TRUE; - break; - } - } - if (clenv->c_hostname != NULL) { - if (best_match(clenv->c_hostname, pat) != -1) { - res = TRUE; - break; - } - } - } - } - } else - DEBUG_PRINTF("address_relay_allow", "mysql_num_rows()"); - mysqlres = mmsql_free_result(mysqlres); - } else - DEBUG_PRINTF("address_relay_allow", "mmsql_query(%s)", query); - - if (!res) - *reason = RCPT_RELAY; - - return res; -} - -/* Produces time in the format yyyymmddhhmmss. Supplied string must at least - * be 15 bytes (16 recommended). - */ -static void -iso_time(char *str) -{ - time_t secs; - struct tm *gtim; - - secs = time(NULL); - gtim = gmtime(&secs); - - (void) snprintf(str, 16, "%04d%02d%02d%02d%02d%02d", - gtim->tm_year + 1900, gtim->tm_mon + 1, gtim->tm_mday, - gtim->tm_hour, gtim->tm_min, gtim->tm_sec); -} - -/* Saves a message to disk, into the directory, creating the directory - * if needed. It ensures to create a unique filename in that directory. The - * directory and the file will be located into MAIL_DIR. Locks should be held - * as necessary before calling this function is atomic behavior is required - * among other tasks. consists of the "Received:" header which will - * be written first, followed by the data held in the buffer. The - * fullpath to the created filename will be stored into supplied , which - * must be at least 256 bytes. - */ -static bool -message_write(char *path, const char *recvline, size_t recvlen, - struct fdbrb_buffer *fdbrb, const char *box) -{ - bool ok = FALSE; - char filetime[16]; - int i, fd; - - fd = -1; - - /* Make sure that directory exists, performing an mkdir(2) which will - * fail if it already does. - */ - (void) snprintf(path, 255, "%s/%s", CONF.MAIL_DIR, box); - if (mkdir(path, 00750) == -1 && errno != EEXIST) { - mmsyslog(0, LOGLEVEL, "mkdir(%s) == %s", path, strerror(errno)); - return FALSE; - } - - /* Generate unique filename to store the message within the mail - * directory. We will make 64 retries maximum in an attempt to ensure - * creation of a unique filename. - */ - iso_time(filetime); - for (i = 0; i < 64; i++) { - (void) snprintf(path, 255, "%s/%s/%s.%08X", CONF.MAIL_DIR, - box, filetime, (u_int32_t)random()); - if ((fd = open(path, O_CREAT | O_EXCL | O_WRONLY, 00640)) != -1) - break; - } - - /* Successfully created a new unique file, save message data. - * We also verify the return value of close(2), but are not calling - * fdatasync(2) or fsync(2) which would considerably impact - * performance. - */ - if (fd != -1) { - if (write(fd, recvline, recvlen) == recvlen && - write(fd, fdbrb->array, fdbrb->current) == fdbrb->current - && close(fd) == 0) - ok = TRUE; - else { - mmsyslog(0, LOGLEVEL, "write()/close()|(%s) == %s", - path, strerror(errno)); - (void) close(fd); - (void) unlink(path); - } - } else - mmsyslog(0, LOGLEVEL, "open(%s) == %s", path, strerror(errno)); - - return ok; -} - -/* For each RCPT, queue the message appropriately */ -static bool -do_data_file(clientenv *clenv, struct fdbrb_buffer *fdbrb) -{ - char smtptime[32], recvline[1024]; - rcptnode *rnode; - size_t recvlen; - bool ok; - - ok = TRUE; - rfc_time(smtptime); - - DLIST_FOREACH(&clenv->rcpt, rnode) { - /* Create Received: line */ - recvlen = do_data_received(recvline, 1024, clenv, rnode, smtptime); - /* Queue for relaying or into the mailbox if local */ - if (!rnode->relay) - ok = do_data_queue_box(clenv, recvline, recvlen, fdbrb, rnode); - else { - if (!CONF.RELAYING) - ok = FALSE; - else - ok = do_data_queue_relay(clenv, recvline, recvlen, fdbrb, - rnode); - } - if (!ok) - break; - } - - return ok; -} - -/* Queue a message to a local mailbox */ -static bool -do_data_queue_box(clientenv *clenv, const char *recvline, size_t recvlen, - struct fdbrb_buffer *fdbrb, rcptnode *rnode) -{ - char line[1024], path[256]; - bool ok = TRUE; - - /* Obtain global lock. This ensures that both file and database data - * are in sync and between both mmsmtpd and mmpop3d. Moreover, it even - * allows proper serialization of operations over NFS. - */ - mmsql_glock("mmmail_boxmail"); - - if (message_write(path, recvline, recvlen, fdbrb, rnode->address)) { - /* File written successfully, now write our corresponding MySQL - * entries. Note that we store the absolute fullpath to the - * message file into the database entry. Although this is not - * necessary, it may prove useful later on. - */ - (void) snprintf(line, 1023, - "INSERT INTO mail (mail_box,mail_created,mail_size," - "mail_file) VALUES('%s',NOW(),%ld,'%s')", - rnode->address, (long)fdbrb->current + recvlen, path); - if (mmsql_command(line, mm_strlen(line))) { - u_int64_t id; - - /* Obtain auto-increment value used in last command */ - id = mmsql_last_auto_id(); - - if (!(ok = do_data_update(rnode, fdbrb->current + recvlen))) { - /* Delete previous successful entry, since updating quota - * information did not succeed, and it must always be - * accurate according to actual mail data. - */ - (void) snprintf(line, 1023, - "DELETE FROM mail WHERE mail_id=%llu", id); - (void) mmsql_command(line, mm_strlen(line)); - } - } else { - mmsyslog(0, LOGLEVEL, "mmsql_command(%s)", line); - ok = FALSE; - } - /* If anything failed, delete stored message file. */ - if (!ok) - (void) unlink(path); - } else - ok = FALSE; - - /* We can finally safely release the global lock */ - mmsql_gunlock("mmmail_boxmail"); - - /* If everything successful, update mmstat statistics */ - if (ok) - do_data_stats(clenv, rnode, fdbrb->current + recvlen); - - return ok; -} - -/* Queue a message for relaying */ -static bool -do_data_queue_relay(clientenv *clenv, const char *recvline, size_t recvlen, - struct fdbrb_buffer *fdbrb, rcptnode *rnode) -{ - char line[1024], path[256], *user, *domain, *restore; - bool ok = TRUE; - - /* This lock allows to maintain atomicity between the message file and - * its corresponding database entry, between mmsmtpd(8) and mmrelayd(8). - */ - mmsql_glock("mmmail_relayqueue"); - - /* We know that the address is valid in the rcpt node, separate it into - * user and domain strings. - */ - for (restore = rnode->address; *restore != '@'; restore++) ; - *restore = '\0'; - user = rnode->address; - domain = &restore[1]; - - if (message_write(path, recvline, recvlen, fdbrb, "relayqueue")) { - /* Message file saved successfully, add corresponding DB entry */ - (void) snprintf(line, 1023, - "INSERT INTO relayqueue (relayqueue_from,relayqueue_ipaddr," - "relayqueue_todomain,relayqueue_touser,relayqueue_size," - "relayqueue_file,relayqueue_queued) " - "VALUES('%s','%s','%s','%s','%ld','%s',NOW())", - clenv->from, clenv->c_ipaddr, domain, user, - (long)fdbrb->current + recvlen, path); - if (!mmsql_command(line, mm_strlen(line))) { - /* SQL request failed, delete saved file */ - mmsyslog(0, LOGLEVEL, "mmsql_command(%s)", line); - (void) unlink(path); - ok = FALSE; - } - } else - ok = FALSE; - - /* Restore string to original value */ - *restore = '@'; - - mmsql_gunlock("mmmail_relayqueue"); - - /* - * We now want to notify mmrelayd that it should verify for ready to - * relay mail as soon as possible instead of waiting until its next - * scheduled round. - */ - do_data_queue_notify(clenv); - - return ok; -} - -/* - * Attempt to notify mmrelayd(8) that at least one message is ready in the - * queue to route. - * XXX Broken! Do not use RELAYING = TRUE yet! - */ -static void -do_data_queue_notify(clientenv *clenv) -{ - bool ok = FALSE; - /* - notify_msg_t msg; - */ - - /* XXX - * We now actually require the lock, since we need to send exactly one - * message per new queued mail. Fix accordingly. We however can fill in - * the data for the message before sending it to hold the lock for as - * short as possible. We also possibly only need it to verify if we're - * connected, or to mark the socket disconnected... Since we only need - * an atomic send per notification? - */ - - /* - * If we cannot obtain lock, we know that it's already being notified, and - * we don't need to do anything. - */ - if (pth_mutex_acquire(&relayd_lock, TRUE, NULL) == FALSE) - return; - - /* - * If socket wasn't open yet, attempt to open it. If we cannot, we have - * nothing to notify, since the relay daemon most probably doesn't run. - */ - if (relayd_sock == -1) { - if ((relayd_sock = do_data_queue_notify_connect()) == -1) - goto end; - } - - /* - * Send a notification packet. If we cannot send it, attempt to reconnect - * and send it again, but once only. - */ - for (;;) { - if (pth_write(relayd_sock, "N", 1) != 1) { - (void) close(relayd_sock); - if ((relayd_sock = do_data_queue_notify_connect()) != -1) - continue; - } else - ok = TRUE; - break; - } - -end: - - if (!ok) - mmsyslog(0, LOGLEVEL, - "mmrelayd(8) could not be notified (not running?)"); - - (void) pth_mutex_release(&relayd_lock); -} - -/* - * Attempt to open the mmrelayd(8) notification socket, returning the - * filedescriptor on success, or -1 on failure. - */ -static int -do_data_queue_notify_connect(void) -{ - int fd, opt; - struct sockaddr_un addr; - - if ((fd = socket(AF_LOCAL, SOCK_DGRAM, 0)) != -1) { - opt = (int)BALIGN_CEIL(sizeof(notify_msg_t), 1024); - if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &opt, sizeof(int)) == -1) - mmsyslog(0, LOGLEVEL, - "do_data_queue_notify_connect() - setsockopt() - (%s)", - strerror(errno)); - mm_memclr(&addr, sizeof(struct sockaddr_un)); - (void) mm_strncpy(addr.sun_path, CONF.MMRELAYD_SOCKET_PATH, 100); - addr.sun_family = AF_UNIX; - if ((pth_connect(fd, (struct sockaddr *)&addr, - sizeof(struct sockaddr_un))) == -1) { - (void) close(fd); - fd = -1; - } else - mmsyslog(0, LOGLEVEL, "Cannot connect to mmrelayd(8) socket " - "'%s' (%s)", addr.sun_path, strerror(errno)); - } else - mmsyslog(0, LOGLEVEL, "Cannot create socket descriptor (%s)", - strerror(errno)); - - return fd; -} - -#else -#error "One of MMMAIL_MYSQL or MMMAIL_FILE must be #defined!" -#endif - - -/* This is the main function that is called to serve a client. - * It comports the main loop and state switcher. - */ -static int -handleclient(unsigned long id, int fd, clientlistnode *clientlnode, - struct iface *iface, struct async_clenv *aclenv) -{ - char buffer[1024], ipaddr[20], *tmp; - int len, state, nstate, timeout, dstatus; - clientenv *clenv; - struct sockaddr_in *sinaddr; - fdbuf *fdb; - int64_t data_in, data_out; - unsigned long rcpt_in, messages_in, time_total; - time_t time_start, time_end; - - data_in = data_out = rcpt_in = messages_in = 0; - dstatus = MMS_RESOURCE_ERROR; - timeout = clientlnode->timeout; - clenv = NULL; - - /* Obtain IP address of client */ - sinaddr = (struct sockaddr_in *)&clientlnode->client; - if ((tmp = inet_ntoa(sinaddr->sin_addr))) mm_strncpy(ipaddr, tmp, 19); - else mm_strncpy(ipaddr, "0.0.0.0", 8); - - if (clientlnode->hostname != NULL) - /* Log user's address and hostname */ - mmsyslog(1, LOGLEVEL, "%08X Connect: [%s] - (%s)", id, ipaddr, - clientlnode->hostname); - else - /* Log user's address only */ - mmsyslog(1, LOGLEVEL, "%08X Connect: [%s]", id, ipaddr); - - time_start = time(NULL); - - if ((fdb = fdbopen(&gfdf, &fdbc, fd, 8192, 8192, CONF.BANDWIDTH_IN * 1024, - CONF.BANDWIDTH_OUT * 1024, timeout, timeout, FALSE)) - != NULL) { - - /* Allocate our clientenv to share with state functions */ - if ((clenv = alloc_clientenv()) != NULL) { - - /* Set some configuration options such as max_rcpts, - * max_mesg_lines, max_mesg_size, hostname... - */ - clenv->fdb = fdb; - clenv->buffer = buffer; - clenv->errors = 0; - clenv->timeout = timeout; - clenv->c_hostname = clientlnode->hostname; - clenv->c_ipaddr = ipaddr; - clenv->id = id; - clenv->iface = iface; - clenv->aclenv = aclenv; - - reply(fdb, 220, FALSE, "%s (%s (%s)) Service ready", - iface->hostname, DAEMON_NAME, DAEMON_VERSION); - state = STATE_ALL; - dstatus = MMS_NORMAL; - - mmstat(&clenv->pstat, STAT_UPDATE, 1, - "mmsmtpd|total|connections"); - - mmstat_transact(&clenv->vstat, TRUE); - mmstat(&clenv->vstat, STAT_UPDATE, 1, - "mmsmtpd|current|connections"); - mmstat(&clenv->vstat, STAT_UPDATE, 1, "mmsmtpd|who|%s", - clenv->c_ipaddr); - mmstat_transact(&clenv->vstat, FALSE); - - /* Main state switcher loop */ - for (;;) { - u_int32_t chash; - register struct commandnode *nod; - - fdbflushw(fdb); - if ((len = fdbgets(fdb, buffer, 1023, FALSE)) > -1) { - - /* If there were too many errors, exit accordingly */ - if (clenv->errors > CONF.MAX_ERRORS) { - reply(fdb, 421, FALSE, "Too many errors"); - dstatus = MMS_MANY_ERRORS; - break; - } - /* Verify if command matches an existing one */ - nod = NULL; - if ((chash = mm_strpack32(buffer, 4)) != 0) - nod = (struct commandnode *)hashtable_lookup( - &command_table, &chash, sizeof(u_int32_t)); - if (nod != NULL) { - register int (*func)(clientenv *); - - mmsyslog(nod->command->loglevel, LOGLEVEL, - "%08X < %s", id, buffer); - - if ((func = states[state].functions[nod->index]) - != NULL) { - - /* Valid command, process it in current state */ - nstate = func(clenv); - if (nstate == STATE_END || nstate == STATE_ERROR) - break; - if (nstate != STATE_CURRENT) - state = nstate; - - } else { - /* Unimplemented command for this state */ - REGISTER_ERROR(clenv); - if (!reply(fdb, states[state].errcode, FALSE, - states[state].errtext)) - break; - } - - } else { - mmsyslog(3, LOGLEVEL, "%08X < %s", id, buffer); - reply(fdb, 500, FALSE, - "Syntax error or unknown command, type HELP"); - REGISTER_ERROR(clenv); - } - - } else { - /* Input error */ - if (len == FDB_TIMEOUT) { - dstatus = MMS_INPUT_TIMEOUT; - break; - } else if (len == FDB_ERROR) { - dstatus = MMS_INPUT_ERROR; - if (CONF.STATFAIL_EOF) - mmstat(&clenv->pstat, STAT_UPDATE, 1, - "mmsmtpd|failed|eof|%s", clenv->c_ipaddr); - break; - } else { - dstatus = MMS_UNKNOWN; - break; - } - } - - } - - messages_in = clenv->messages; - rcpt_in = clenv->rcpts; - data_in = FDBBYTESR(fdb); - data_out = FDBBYTESW(fdb); - - mmstat_transact(&clenv->vstat, TRUE); - mmstat(&clenv->vstat, STAT_UPDATE, -1, - "mmsmtpd|who|%s", clenv->c_ipaddr); - mmstat(&clenv->vstat, STAT_UPDATE, -1, - "mmsmtpd|current|connections"); - mmstat_transact(&clenv->vstat, FALSE); - - mmstat_transact(&clenv->pstat, TRUE); - mmstat(&clenv->pstat, STAT_UPDATE, messages_in, - "mmsmtpd|total|messages-in"); - mmstat(&clenv->pstat, STAT_UPDATE, data_in, - "mmsmtpd|total|bytes-in"); - mmstat(&clenv->pstat, STAT_UPDATE, data_out, - "mmsmtpd|total|bytes-out"); - mmstat_transact(&clenv->pstat, FALSE); - - /* Free our state-shared clenv */ - clenv = free_clientenv(clenv); - } else - DEBUG_PRINTF("handleclient", "alloc_clientenv()"); - - fdbclose(fdb); - } else - DEBUG_PRINTF("handleclient", "fdbopen(%d)", fd); - - /* Log results */ - time_end = time(NULL); - time_total = time_end - time_start; - mmsyslog(1, LOGLEVEL, - "%08X Closed: [%s] - (Seconds: %lu, Data in: %lld out: %lld, " - "RCPTs: %lu, Messages in: %lu, Status: %s)", - id, ipaddr, time_total, data_in, data_out, rcpt_in, - messages_in, MMS_RSTRING(dstatus)); - - return (0); -} - - -/* mmfd library thread support functions */ - - -static void * -_pth_mutex_create(void) -{ - struct mutexnode *mnod; - - pth_mutex_acquire(&mutexes_lock, FALSE, NULL); - mnod = (struct mutexnode *)pool_alloc(&mutexes_pool, FALSE); - pth_mutex_release(&mutexes_lock); - - if (mnod != NULL) - pth_mutex_init(&mnod->mutex); - - return ((void *)mnod); -} - - -static void * -_pth_mutex_destroy(void *mtx) -{ - /* struct mutexnode *mnod = mtx; */ - - /* pth_mutex_destroy(&mnod->mutex); */ - pth_mutex_acquire(&mutexes_lock, FALSE, NULL); - pool_free(mtx); - pth_mutex_release(&mutexes_lock); - - return (NULL); -} - - -static void -_pth_mutex_lock(void *mtx) -{ - struct mutexnode *mnod = mtx; - - pth_mutex_acquire(&mnod->mutex, FALSE, NULL); -} - - -static void -_pth_mutex_unlock(void *mtx) -{ - struct mutexnode *mnod = mtx; - - pth_mutex_release(&mnod->mutex); -} - - -static void -_pth_thread_yield(void) -{ - pth_yield(NULL); -} - - -static void -_pth_thread_sleep(int secs) -{ - pth_sleep(secs); -} - - -static bool -_pth_eintr(void) -{ - if (errno == EINTR) - return TRUE; - - return FALSE; -} - - -/* Here are our real asynchroneous functions, called by the slave processes */ - - -static void -async_resquery(struct async_msg *msg) -{ - struct async_resquery_msg *amsg = (void *)msg; - - amsg->un.res.res = res_query(amsg->un.args.host, amsg->un.args.r_class, - amsg->un.args.r_type, amsg->un.res.answer, 127); -} - - -/* And our wrapper functions calling the asynchroneous device */ - - -static int -a_res_query(clientenv *clenv, const char *dname, int class, int type, - u_char *answer, int anslen) -{ - struct async_resquery_msg *amsg = (void *)clenv->aclenv->msg; - int res; - - mm_strncpy(amsg->un.args.host, dname, 127); - amsg->un.args.r_class = class; - amsg->un.args.r_type = type; - async_call(clenv->aclenv, ASYNC_RESQUERY); - if ((res = amsg->un.res.res) != -1) - mm_strncpy(answer, amsg->un.res.answer, anslen); - - return (res); -} - - -/* Here consists of our hostnode expiration thread. It asynchroneously and - * occasionally iterating through all the nodes to reset and/or expunge the - * expired ones. Doing this here prevents interfering with the normally more - * frequent lookups which can be done with hashtable_lookup() in another - * thread. We wouln't want those to need to iterate through all the nodes - * everytime. We also call a function which ensures to delete any mailbox - * files for which an entry exists in the boxdelete database table. - */ -/* ARGSUSED */ -static void * -hosts_expire_thread(void *args) -{ - struct hosts_expire_thread_iterator_udata data; - - /* Set the initial timeout to the maximum allowed */ - data.soonest = CONF.FLOOD_EXPIRES * 60; - data.cnt = 0; - for (;;) { - /* Sleep until it is known that at least one node expired */ - pth_sleep((unsigned int)data.soonest); - /* Tell our iterator function the current time and the maximum - * allowed time to wait to - */ - data.current = time(NULL); - data.soonest = CONF.FLOOD_EXPIRES * 60; - /* Lock hosts_table, expunge expired nodes and set data.soonest to the - * time of the soonest next expireing node - */ - pth_mutex_acquire(&hosts_lock, FALSE, NULL); - if (HASHTABLE_NODES(&hosts_table) > 0) - hashtable_iterate(&hosts_table, hosts_expire_thread_iterator, - &data); - pth_mutex_release(&hosts_lock); - } - - /* NOTREACHED */ - pth_exit(NULL); - return NULL; -} - -static bool -hosts_expire_thread_iterator(hashnode_t *hnod, void *udata) -{ - hostnode *nod = (hostnode *)hnod; - struct hosts_expire_thread_iterator_udata *data = udata; - time_t rem; - - /* If the node expired, free it. For nodes which do not, record the - * soonest to expire node. - */ - if ((rem = LR_REMAINS(&nod->lr, data->current)) == 0) { - /* Entry expired, free it */ - hashtable_unlink(&hosts_table, (hashnode_t *)nod); - pool_free((pnode_t *)nod); - } else { - if (data->soonest > rem) - data->soonest = rem; - } - - /* If the cache is big, prevent from interfering with other threads */ - if ((data->cnt++) == 64) { - data->cnt = 0; - pth_yield(NULL); - } - - return TRUE; -} - - -/* - * This thread performs a verification for entries in the boxdelete table, and - * deletes the dangling directories for boxes which have been deleted. This - * way we do not need the frontend process to be able to delete arbitrary - * files, while being able to provide an administration frontend to delete - * mailboxes as needed. We also perform table optimization at regular - * intervals. - */ -/* ARGSUSED */ -static void * -db_gc_thread(void *args) -{ - int rounds; - - for (rounds = 1; ; rounds++) { - MYSQL_RES *mysqlres; - - (void) pth_sleep(60); - - /* - * Perform dangling mailbox directories cleanup - */ - if ((mysqlres = mmsql_query("SELECT boxdelete_address FROM boxdelete", - -1)) != NULL) { - char addr[64]; - MYSQL_ROW *row; - unsigned long *lengths; - - while ((row = (MYSQL_ROW *)mysql_fetch_row(mysqlres)) != NULL) { - lengths = mysql_fetch_lengths(mysqlres); - if (row[0] != NULL) { - char deladdr[64], query[1024]; - bool delete; - - delete = FALSE; - *deladdr = '\0'; - mm_memcpy(addr, row[0], lengths[0]); - addr[lengths[0]] = '\0'; - - /* - * Important security sanity checking: Make sure that - * this actually consists of a valid address. We - * wouldn't want to perform anything if arbitrary - * entries were filled by a malicious user, bypassing - * the HTTP frontend security somehow, or the MySQL - * ones. We also don't need to do anything if we are not - * using files for message storage. - */ -#if defined(MMMAIL_FILE) - if (valid_address(NULL, deladdr, 64, addr, HOST_NORES)) { - MYSQL_RES *mysqlres2; - - /* - * And make sure that no box entry exists for it! - */ - (void) snprintf(query, 1023, - "SELECT box_address FROM box WHERE " - "box_address='%s'", deladdr); - if ((mysqlres2 = mmsql_query(query, mm_strlen(query))) - != NULL) { - if (mysql_num_rows(mysqlres2) == 0) - delete = TRUE; - (void) mmsql_free_result(mysqlres2); - } - } -#endif /* defined(MMMAIL_FILE) */ - /* Delete db entry unconditionally */ - (void) snprintf(query, 1023, - "DELETE FROM boxdelete WHERE " - "boxdelete_address='%s'", addr); - (void) mmsql_command(query, mm_strlen(query)); - /* Perform actual deletion, only if safe to */ - if (delete) - db_gc_thread_delete(deladdr); - } - } - (void) mmsql_free_result(mysqlres); - } - - if (rounds == 1440) { - rounds = 0; - - /* - * Perform database optimization every 24 hours - */ -#define OPTIMIZE(s) do { \ - if ((mysqlres = mmsql_query("OPTIMIZE TABLE " s, -1)) != NULL) \ - (void) mmsql_free_result(mysqlres); \ -} while (/* CONSTCOND */0) - - OPTIMIZE("alias"); - OPTIMIZE("box"); - OPTIMIZE("boxdelete"); - OPTIMIZE("filter"); - OPTIMIZE("mail"); - OPTIMIZE("nofrom"); - OPTIMIZE("relayfrom"); - OPTIMIZE("relaylocal"); - OPTIMIZE("relayqueue"); - OPTIMIZE("session"); - OPTIMIZE("user"); - -#undef OPTIMIZE - } - } - - /* NOTREACHED */ - pth_exit(NULL); - return NULL; -} - -static void -db_gc_thread_delete(const char *addr) -{ - char dirpath[256], filepath[256]; - DIR *dir; - struct dirent ent, *res; - int count; - - /* - * Now holds the actual directory to delete mail from. We know - * that there only exist first level files in it, and we must ensure to - * delete all of them, as well as the actual mailbox directory afterwards. - */ - if ((dir = opendir(dirpath)) == NULL) { - syslog(LOG_NOTICE, "db_gc_thread_delete(%s) - opendir(%s) == %s", - addr, dirpath, strerror(errno)); - return; - } - - count = 1; - while (readdir_r(dir, &ent, &res) == 0 && res == &ent) { - (void) snprintf(filepath, 255, "%s/%s", dirpath, ent.d_name); - if (unlink(filepath) != 0) - syslog(LOG_NOTICE, "db_gc_thread_delete(%s) - unlink(%s) == %s", - addr, filepath, strerror(errno)); - if (++count == 64) { - count = 0; - (void) pth_yield(NULL); - } - } - if (rmdir(dirpath) != 0) - syslog(LOG_NOTICE, "db_gc_thread_delete(%s) - rmdir(%s) == %s", - addr, dirpath, strerror(errno)); - - closedir(dir); -} diff --git a/mmsoftware/mmmail/src/mmsmtpd/mmsmtpd.conf.5 b/mmsoftware/mmmail/src/mmsmtpd/mmsmtpd.conf.5 deleted file mode 100644 index 5b9b9cd..0000000 --- a/mmsoftware/mmmail/src/mmsmtpd/mmsmtpd.conf.5 +++ /dev/null @@ -1,578 +0,0 @@ -.\" $Id: mmsmtpd.conf.5,v 1.14 2005/03/05 15:33:34 mmondor Exp $ -.\" -.\" Copyright (C) 2001-2004, Matthew Mondor -.\" All rights reserved. -.\" -.\" Redistribution and use in source and binary forms, with or without -.\" modification, are permitted provided that the following conditions -.\" are met: -.\" 1. Redistributions of source code must retain the above copyright -.\" notice, this list of conditions and the following disclaimer. -.\" 2. Redistributions in binary form must reproduce the above copyright -.\" notice, this list of conditions and the following disclaimer in the -.\" documentation and/or other materials provided with the distribution. -.\" 3. All advertising materials mentioning features or use of this software -.\" must display the following acknowledgement: -.\" This product includes software written by Matthew Mondor. -.\" 4. The name of Matthew Mondor may not be used to endorse or promote -.\" products derived from this software without specific prior written -.\" permission. -.\" -.\" THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR -.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES -.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. -.\" IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, -.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -.\" BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF -.\" USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF -.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -.\" -.Dd 12 Dec, 2002 -.Dt MMSMTPD.CONF 5 -.Os -.Sh NAME -.Nm mmsmtpd.conf -.Nd -.Xr mmsmtpd.conf 5 -configuration file for -.Xr mmsmtpd 8 -of -.Xr mmmail 8 -suite -.Sh SECURITY CONSIDERATIONS -This -.Nm /etc/mmsmtpd.conf -configuration file should only be readable and writeable by the superuser, -as the MySQL access password is stored in it. -.Bd -literal -offset indent --rw------- 1 root wheel 4504 Sep 8 00:22 /etc/mmsmtpd.conf -.Ed -.Pp -Please read the -.Xr mmmail 8 -man page for more information on other security consideration issues. -.Sh DESCRIPTION -The -.Nm /etc/mmsmtpd.conf -file may contain one or more keyword/value pairs per line, empty lines or -comments. A ';' or '#' character causes all other characters to be considered -as a comment, and to be ignored, until the end of the current line. It is -very important to enclose the value for a keyword in double quotes ('"' -characters) if the following characters are found in it: ';', '#', space and -tab. Here are documented the various possible keywords and their -allowed values, as well as their defaults. -.Pp -.Nm Daemon administration parameters -.Pp -.Bl -tag -width XXXXXXXX -offset indent -compact -.It Nm ASYNC_PROCESSES Ar "number" -The server internally launches a pool of asynchroneous processes to serve -functions which could block the main process for too long (this in fact -also means all threads with userspace threads such as provided by the Pth -library). This includes resolving hostnames and MX records. -This also allows multiprocessor systems to have an advantage when executing -these functions. It should be noted that two additional file descriptors are -required in the main process for each async process of the pool. The -administrator will hence generally configure -.Fa MAX_IPS , -.Fa MAX_PER_IP -and/or the maximum allowed number of file descriptors allowed for a process by -the kernel, accordingly. -.Pp -.It Nm CHROOT_DIR Ar "directory" -Location of the directory where the daemon optionally should -.Xr chroot 2 -in, or "" if it should not. Note that special configuration is generally -required for this. All files and directories the server will need access to -should also reside in that directory, notably in -.Nm /etc/ , -such as -.Nm /etc/resolv.conf , /etc/hosts -and -.Nm /etc/group , -depending on the system's libc. The -.Xr mmstat 3 -and -.Xr syslog 3 -facilities will be initialized before -.Xr chroot 2 -is called to ensure that they continue working properly. Similarly, -connection to the MySQL server is also established first. Note that -if the connection is lost and has to be re-established to the server, -to a local socket which is outside of the new root, it will not be able -to re-establish it, although normally local UNIX socket connections are -reliable. This is not a problem with remote server connections, where -re-establishing may happen more often, and the jail won't matter. -.Pp -.It Nm LOCK_PATH Ar "file" -Location of lock file which should be used to determine if the daemon is -already running. Note that this path resides outside of the -.Xr chroot 2 -if enabled using -.Fa CHROOT_DIR . -.Pp -.It Nm PID_PATH Ar "file" -Location where -.Nm mmsmtpd -writes it's pid file. This consists of a small file holding the process ID of -the main process, which can be used by scripts or the administrator to kill -the daemon properly. This file will be written before -.Xr chroot 2 -is called, if -.Fa CHROOT_DIR -was enabled. -.Pp -.It Nm MAIL_DIR Ar "file" -If -.Xr mmmail 8 -was compiled using -.Nm -DMMMAIL_FILE -instead of -.Nm -DMMMAIL_MYSQL , -this specifies the directory under which all mail should be stored. The -directories for the mail boxes will automatically be created as necessary, -and messages will be stored, one per message, into the corresponding ones. -Note that the user under which the -.Xr mmsmtpd 8 -and -.Xr mmpop3d 8 -processes run must have write access to this directory. In a typical install, -the directory will be owned by the user running those, and will have the group -set to the main group for that user, with a permissions mode of 0750. -.Pp -Note that using file storage for messages usually yields better performance -than using SQL storage alone. Although SQL is still used to store -corresponding mail entries, the file text/glob row of the mail table is what -consists of a considerable performance bottleneck when many large messages -exist in the database. -.Pp -.It Nm USER Ar "user" -Unprivileged user server process should run as. -.Pp -.It Nm GROUPS Ar "group,..." -Groups process should be part of, separated by commas, without spaces. -It is important to also include here the 'mmstat' group. -.Pp -.It Nm ALLOC_BUFFERS Ar "number" -On systems which are expected to operate under high load, serving a large -number of connections simultaneously, it may be ideal to increase this -variable. This pre-allocates buffers using the -.Xr mmpool 3 -library. For a system serving 64 connections, a suitable value may be 4. This -feature will mostly cause noticeable speed increase on systems which do not -provide very efficient memory allocators, such as glibc on Linux. The best -is to perform various tests and evaluate the required number to use here. -.Pp -Note that with the new (current) -.Xr mmpool 3 -implementation, this value now has little to no significance, because the -system is especially made to automatically scale properly. This value will -however still be useful to immediately scale up the system, in cases where a -large number of connections are immediately expected when launching the -system. -.Pp -.It Nm LOG_FACILITY Ar "facility" -Syslog facility which should be used for error logging. Should normally be -one of -.Sy LOG_AUTH , LOG_AUTHPRIV , LOG_CRON , LOG_DAEMON , -.Sy LOG_FTP , LOG_KERN , LOG_LPR , LOG_MAIL , -.Sy LOG_NEWS , LOG_SYSLOG , LOG_USER -or -.Sy LOG_UUCP . -See -.Xr syslog 3 -man page for more information. -.Pp -.It Nm LOG_LEVEL Ar "number" -This consists of the logging verbosity level, controlling which gravity of -messages should be logged through the -.Xr syslog 3 -.Ar LOG_FACILITY -facility. Here consists of the allowable levels and their meaning: -.Bd -literal -offset indent -0 = critical and important messages only -1 = connections are logged -2 = informational logging (HELO, MAIL and RCPT are - logged) -3 = verbose logging (all commands and replies) -4 = debugging (even message DATA lines are logged) -.Ed -.El -.Pp -.Nm TCP server parameters -.Pp -.Bl -tag -width XXXXXXXX -offset indent -compact -.It Nm LISTEN_IPS Ar "n.n.n.n ..." -IPv4 addresses we should -.Xr bind 2 -to, separated by spaces. These typically consist of the addresses bound to -a wanted interface to accept connections from. 0.0.0.0 may be used if -connections from all interfaces should be allowed. Obviously, if more than -a single address is specified, they should be enclosed in double quotes. -.Pp -.It Nm SERVER_NAMES Ar "name ..." -Advertized server hostnames, separated by spaces, there should be the same -number of entries than for -.Ar LISTEN_IPS -, and of course also should be enclosed in double quotes if more than one -hostname is specified. These can be any wanted hostnames. -.Pp -.It Nm LISTEN_PORT Ar "number" -inet TCP port we should be listening for connections on. -.Pp -.It Nm RESOLVE_HOSTS Ar "boolean" -Resolving clients hostnames for logging can make the system slow depending on -DNS server reliability and service load. boolean should be TRUE or FALSE, -respectively. -.Pp -.It Nm DELAY_ON_ERROR Ar "boolean" -If set to TRUE, at each bad command or error (see -.Ar MAX_ERRORS ) -a delay will be forced before accepting another command from that client. -At each error the delay will increase by one second. -.Pp -.It Nm MAX_ERRORS Ar "number" -Maximum number of bad commands user may issue per session before being dropped. -.Pp -.It Nm MAX_IPS Ar "number" -Total maximum number of simultanious different IP addresses we allow -connections from. -.Pp -.It Nm MAX_PER_IP Ar "number" -Maximum simultanious connections per single IP address we accept. -.Pp -.It Nm CONNECTION_RATE Ar "number" -Maximum amount of connections to accept per IP address within -.Fa CONNECTION_PERIOD . -Can be 0 if no connection rate limits are wanted. -.Pp -.It Nm CONNECTION_PERIOD Ar "seconds" -The number of seconds within which a maximum of -.Fa CONNECTION_RATE -connections are accepted for an IP address. -.Pp -.It Nm INPUT_TIMEOUT Ar "seconds" -Maximum input/output timeout in seconds for the client connection. -The server will disconnect if this is exceeded. -.Pp -.It Nm BANDWIDTH_IN Ar "kilobytes" -This consists of the maximum bandwidth speed limit, in kilobytes per second, -to allow reading at from the client connection. 0 disables bandwidth -shaping for this entry. So this restricts the speed we may receive messages at. -.Pp -.It Nm BANDWIDTH_OUT Ar "kilobytes" -This consists of the maximum bandwidth speed limit, in kilobytes per second, -to allow writing to the client connection. 0 disables bandwidth -shaping for this entry. -.Pp -.It Nm GBANDWIDTH_IN Ar "kilobytes" -The global maximum bandwidth speed limit, in kilobytes per second, for the -whole server, at which it is allowed to read from clients. 0 for no limit. -.Pp -.It Nm GBANDWIDTH_OUT Ar "kilobytes" -The global maximum bandwidth speed limit, in kilobytes per second, for the -whole server, at which it is allowed to write to clients. 0 for no limit. -.El -.Pp -.Nm SMTP specific parameters -.Pp -.Bl -tag -width XXXXXXXX -offset indent -compact -.It Nm RELAYING Ar "boolean" -Enables the relaying feature of mmmail. Note that relaying only currently -works if mmmail was compiled using MMMAIL_FILE, that is, using file storage -for message bodies along with MySQL header entries, instead of full headers -and body stored via MySQL. Moreover, the -.Xr mmrelayd 8 -daemon must be running for mail to actually be processed and sent to remote -SMTP servers, otherwise the relaying queue will fill up but not be processed. -The relaying queue will be stored into -.Nm /relayqueue/ . -.Pp -When using relaying, it is important to describe the local domains which are -handled by the local SMTP server through the -.Nm relaylocal -database table. These can use patterns as required. This allows -.Xr mmsmtpd 8 -to differenciate between unknown local addresses for locally handled domains -and mail which should be relayed out (not to a locally handled domain). -The -.Nm relayfrom -table also should hold patterns of client addresses and/or hostnames which -should be allowed to use the server for relaying. Relaying will be denied for -any non-matching clients. IPv4 addresses and hostnames may be used, depending -on weither client address resolving was enabled. -.Pp -Both tables may optionally contain wildcards for matching, using '*' and '?'. -.Pp -.It Nm MMRELAYD_SOCKET_PATH Ar "path" -Only taken in consideration if -.Nm RELAYING -is TRUE. Specifies the path to the AF_LOCAL DGRAM socket used to notify -.Xr mmrelayd 8 -when we queue new messages for relaying. Must correspond to the path used -by the relaying daemon, obviously. -.Pp -.It Nm RESOLVE_HELO Ar "boolean" -Should we only accept resolvable hostnames for the -.Sy HELO -command? This should be TRUE or FALSE, respectively. -.Pp -.It Nm RESOLVE_MX_MAIL Ar "boolean" -Should we only accept the -.Sy MAIL -command from valid addresses with MX records? Should be TRUE or FALSE. -.Pp -.It Nm RESOLVE_MX_RCPT Ar "boolean" -This option is mostly useful when used along with -.Sy RESOLVING -set to TRUE. It permits to immediately reject mail sent for non-local -destination addresses which domain does not resolve or does not provide an -MX record. The effect is that such mail will not be added to the relay queue. -Without this option, it's -.Xr mmrelayd 8 -which will have to perform the test and mail back to the originator of the -message about the error, if this mode is prefered. This option should be TRUE -or FALSE. -.Pp -.It Nm REQUIRE_HELO Ar "boolean" -Is client required to use the -.Sy HELO -command before being allowed to use the -.Sy MAIL -command? Should be TRUE or FALSE. -.Pp -.It Nm STATFAIL_ADDRESS Ar "boolean" -If TRUE, instructs -.Nm mmsmtpd -to report statistics for each host about invalid local mail address errors -using the -.Xr mmstat 3 -facility. Set to -FALSE to disable. This may be useful so that external scripts -could potentially take action against hosts, firewalling them off for instance. -The statistic key will be in the form of: -.Pp -mmsmtpd|failed|address||| -.Pp -.It Nm STATFAIL_RELAY Ar "boolean" -When RELAYING is enabled, allows to store statistics on clients which attempt -to relay mail through your server but are unauthorized to do so. The -.Xr mmstat 3 -key will be in the format: -.Pp -mmsmtpd|failed|relay||| -.Pp -.It Nm STATFAIL_FLOOD Ar "boolean" -Similarly to -.Fa STATFAIL_ADDRESS , -allows to keep statistics on the number of times hosts were rejected for -flooding (too many messages received from this host for a given period, -or more -.Sy RCPT -commands than allowed per session). -This is dependant on the -.Fa FLOOD_PROTECTION , -.Fa FLOOD_CACHE -and -.Fa MAX_RCPTS -configuration options. The statistic key will be in the form of: -.Pp -mmsmtpd|failed|flood|message||| -.Pp -or -.Pp -mmsmtpd|failed|flood|rcpt|| -.Pp -.It Nm STATFAIL_FULL Ar "boolean" -This may be useful to allow the administrator to have a view of what mailboxes -may need higher quotas, or flushing. If TRUE, statistics will be kept using -keys of the form: -.Nm mmsmtpd|failed|full| . -.Pp -.It Nm STATFAIL_TIMEOUT Ar "boolean" -This one keeps statistics of the addresses that fail for timeout after -.Sy DATA -command was issued, and before end of message could be sent, using a key of -.Nm mmsmtpd|failed|timeout| . -.Pp -.It Nm STATFAIL_EOF Ar "boolean" -This allows to keep track of addresses from which clients do not properly -issue "\\n.\\n" end of message marker or -.Sy QUIT -end of session command and are by definition not following the SMTP protocol. -The mmstat keys will be in the form -.Nm mmsmtpd|failed|eof| . -.Pp -.It Nm STATFAIL_FILTER Ar "boolean" -Keep statistics on IP addresses and MAIL FROM addresses which attempt to -send mail to a box which has filters enabled and for which no rule allows -the sender. The format of the -.Xr mmstat 3 -key is as follows: -.Pp -mmstatd|failed|filter||| -.Pp -.It Nm MAX_RCPTS Ar "number" -Maximum number of allowed destination recipients per message -.Pp -.It Nm MAX_DATA_LINES Ar "number" -Maximum -.Sy DATA -lines to accept for a message, make sure that it is enough for -general messages of -.Ar MAX_DATA_SIZE -bytes large. -.Pp -.It Nm MAX_DATA_SIZE Ar "number" -Maximum allowed -.Sy DATA -message size in bytes. Note that MySQL must also have been setup to accept -BLOB fields of -.Ar MAX_DATA_SIZE -via the -.Ar max_allowed_packet -and optionally -.Ar query_buffer_size -MySQL control variables. This can be as simple as passing -.Nm -O max_allowed_packet=10M -as command-line argument to mysqld. -.Pp -.It Nm MAX_HOPS Ar "number" -Maximum number of -.Sy Received: -lines allowed in a message to accept it. -.Pp -.It Nm FLOOD_PROTECTION Ar "boolean" -For use against mail bombing, these optionally can be set. -Turns on or off mail flood protection. Flood rate is evaluated on a -per client hostname or address basis. Should be TRUE or FALSE. -.Pp -.It Nm FLOOD_CACHE Ar "number" -Only taken into account if -.Ar FLOOD_PROTECTION -is TRUE. Make sure that this is high enough, depending on how you set -.Ar FLOOD_EXPIRES -and -.Ar FLOOD_MESSAGES . -It specifies the maximum number of cache entries which -can be remembered for hosts from which mail was recently received. -This typically can be set to the same value as -.Ar MAX_IPS -or larger. -.Pp -.It Nm FLOOD_MESSAGES Ar "number" -Specifies how many messages maximum will be accepted within -.Ar FLOOD_EXPIRES -delay. Higher than this will be considered flood, and a try again reply will -be sent to the SMTP client. -.Pp -.It Nm FLOOD_EXPIRES Ar "minutes" -This is the number of minutes for which the cache entry for a host will -be remembered. It thus consists of the number of minutes within which a -maximum of -.Ar FLOOD_MESSAGES -will be accepted, on a per-host basis. -.El -.Pp -.Nm MySQL related parameters -.Pp -.Bl -tag -width XXXXXXXX -offset indent -compact -.It Nm DB_HOST Ar "hostname" -Host name of MySQL server we should connect to ("localhost" for UNIX socket). -.Pp -.It Nm DB_USER Ar "user" -MySQL user which has all access on -.Ar DB_DATABASE -MySQL database. -.Pp -.It Nm DB_PASSWORD Ar "password" -MySQL authentication password for -.Ar DB_USER -user. -.Pp -.It Nm DB_DATABASE Ar "database" -Name of mmmail MySQL database which the -.Ar DB_USER -user owns, typically "mmmail". -.Pp -.El -.Sh DEFAULTS -The following defaults are used: -.Pp -.Bd -literal -offset indent -compact -ASYNC_PROCESSES 3 -CHROOT_DIR "" -PID_PATH "/var/run/mmsmtpd.pid" -LOCK_PATH "/var/run/mmsmtpd.lock" -MAIL_DIR "/var/mmmail-dir" -USER mmmail -GROUPS mmmail,mmstat -LOG_FACILITY LOG_AUTHPRIV -SERVER_NAMES "smtp.localhost" -LISTEN_IPS "127.0.0.1" -ALLOC_BUFFERS 1 -LOG_LEVEL 3 -LISTEN_PORT 25 -MAX_ERRORS 16 -DELAY_ON_ERROR FALSE -MAX_IPS 64 -MAX_PER_IP 1 -CONNECTION_RATE 10 -CONNECTION_PERIOD 30 -INPUT_TIMEOUT 900 -BANDWIDTH_IN 16 -BANDWIDTH_OUT 4 -GBANDWIDTH_IN 0 -GBANDWIDTH_OUT 0 -RESOLVE_HOSTS FALSE -STATFAIL_ADDRESS TRUE -STATFAIL_RELAY TRUE -STATFAIL_FLOOD TRUE -STATFAIL_FULL TRUE -STATFAIL_TIMEOUT TRUE -STATFAIL_EOF TRUE -STATFAIL_FILTER TRUE -RELAYING FALSE -MMRELAYD_SOCKET_PATH "/var/run/mmrelayd.sock" -RESOLVE_HELO FALSE -RESOLVE_MX_MAIL FALSE -REQUIRE_HELO FALSE -FLOOD_PROTECTION TRUE -MAX_RCPTS 16 -MAX_DATA_LINES 15000 -MAX_DATA_SIZE 1048576 -MAX_HOPS 30 -FLOOD_MESSAGES 20 -FLOOD_EXPIRES 30 -FLOOD_CACHE 100 -DB_HOST "localhost" -DB_USER "mmmail" -DB_PASSWORD "mmmailpassword" -DB_DATABASE "mmmail" -.Ed -.Sh AUTHOR -.Nm mmmail -suite was written by Matthew Mondor and is Copyright (c) 2001-2004 -Matthew Mondor, All rights reserved. -.Sh FILES -.Bl -tag -width XXXXXXXXXXXXXXXXXXXXXXXX -compact -.It Pa /etc/mmsmtpd.conf -This file -.Pp -.It Pa /usr/local/sbin/mmsmtpd -The actual SMTP server binary -.El -.Sh SEE ALSO -.Xr mmsmtpd 8 , -.Xr mmmail 8 , -.Xr mmpop3d 8 , -.Xr mmpop3d.conf 5 , -.Xr mmstatd 8 , -.Xr mmstatd.conf 5 , -.Xr syslog 3 , -.Xr chroot 2 , -.Xr mysql 1 . -.Sh BUGS -Please report any bug to mmondor@accela.net diff --git a/mmsoftware/mmmail/src/mmsmtpd/mmsmtpd.h b/mmsoftware/mmmail/src/mmsmtpd/mmsmtpd.h deleted file mode 100644 index 9cb9a01..0000000 --- a/mmsoftware/mmmail/src/mmsmtpd/mmsmtpd.h +++ /dev/null @@ -1,340 +0,0 @@ -/* $Id: mmsmtpd.h,v 1.33 2005/03/26 11:45:48 mmondor Exp $ */ - -/* - * Copyright (C) 2001-2004, Matthew Mondor - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software written by Matthew Mondor. - * 4. The name of Matthew Mondor may not be used to endorse or promote - * products derived from this software without specific prior written - * permission. - * - * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - - - - -#ifndef MMSMTPD_H -#define MMSMTPD_H - - - - -/* HEADERS */ - -#include -#include - -#include - -#include -#include -#include -#include -#include -#include -#include -#include - - - - -/* DEFINITIONS */ -#define DAEMON_NAME "mmsmtpd" -#define DAEMON_VERSION "mmmail-0.0.24/mmondor" - -/* Negative states are used by the state swapper, others are real states */ -#define STATE_ERROR -3 -#define STATE_END -2 -#define STATE_CURRENT -1 -#define STATE_ALL 0 - -/* Invalid RCPT reason */ -enum rcpt_reason { - RCPT_OK = 0, - RCPT_NOFROM, - RCPT_MANY, - RCPT_INVALID, - RCPT_UNKNOWN, - RCPT_RELAY, - RCPT_EXISTS, - RCPT_FULL, - RCPT_FLOOD, - RCPT_FILTER, - RCPT_ERROR, - RCPT_MAX -}; - -/* DATA errors */ -enum data_reason { - DATA_SUBMIT = 0, - DATA_OK, - DATA_OVERFLOW, - DATA_HOPS, - DATA_INTERNAL, - DATA_MAX -}; - -/* Resolving flags for valid_host() */ -#define HOST_NORES 0 -#define HOST_RES 1 -#define HOST_RES_MX 2 - -/* Custom fdbreadbuf() return result */ -#define CFDBRB_HOPS -1 - -/* Asynchroneous functions we attach */ -#define ASYNC_RESQUERY 1 - -/* Error registration macro */ -#define REGISTER_ERROR(x) do { \ - (x)->errors++; \ - if (CONF.DELAY_ON_ERROR) \ - pth_sleep((x)->errors); \ -} while(FALSE) - -/* Evaluates if a character is valid for addresses and hostnames */ -#define VALID_ADDR_CHAR(c) \ - (valid_addr_char_table[(int)((unsigned char)(c))] != 0) -#define VALID_HOST_CHAR(c) \ - (valid_addr_host_table[(int)((unsigned char)(c))] != 0) - - - -/* STRUCTURES */ - -/* We store config file read results in this structure */ -typedef struct config { - char LOCK_PATH[256], CHROOT_DIR[256], PID_PATH[256], USER[32], GROUPS[256], - LOG_FACILITY[32], SERVER_NAMES[1024], LISTEN_IPS[1024], DB_HOST[64], - DB_USER[32], DB_PASSWORD[32], DB_DATABASE[32], MAIL_DIR[256], - MMRELAYD_SOCKET_PATH[256]; - long ALLOC_BUFFERS, LOG_LEVEL, LISTEN_PORT, MAX_ERRORS, MAX_IPS, - MAX_PER_IP, CONNECTION_RATE, CONNECTION_PERIOD, INPUT_TIMEOUT, - BANDWIDTH_IN, BANDWIDTH_OUT, GBANDWIDTH_IN, GBANDWIDTH_OUT, MAX_RCPTS, - MAX_DATA_LINES, MAX_DATA_SIZE, MAX_HOPS, FLOOD_MESSAGES, - FLOOD_EXPIRES, FLOOD_CACHE, ASYNC_PROCESSES; - bool RESOLVE_HOSTS, RESOLVE_HELO, RESOLVE_MX_MAIL, RESOLVE_MX_RCPT, - REQUIRE_HELO, FLOOD_PROTECTION, STATFAIL_ADDRESS, STATFAIL_RELAY, - STATFAIL_FLOOD, STATFAIL_FULL, STATFAIL_TIMEOUT, STATFAIL_EOF, - STATFAIL_FILTER, DELAY_ON_ERROR, RELAYING; -} CONFIG; - - -/* This consists of the state-shared clientenv structure */ -typedef struct clientenv { - pnode_t node; - fdbuf *fdb; /* Buffered handler around our fd */ - char *buffer; /* Buffer that points to last command line */ - char *helo; /* Cached helo hostname */ - char *from; /* Cached mail sender address */ - char *c_hostname; /* Pointer to client's hostname */ - char *c_ipaddr; /* Pointer to client's IP address string */ - long mesg_size; /* Current cached message size in bytes */ - long errors; /* Total number of errors that occured */ - int timeout; /* Timeout in ms */ - unsigned long id; /* Our connection ID */ - unsigned long messages; /* Messages user sent us */ - unsigned long rcpts; /* Number of RCPT accepted */ - struct iface *iface; /* Current interface user connected through */ - struct async_clenv *aclenv; /* Thread context for async_call() */ - list_t rcpt; /* Cached recepients to send mail to */ - mmstat_t vstat, pstat; /* mmstat(3) handles */ -} clientenv; - -/* Used for RCPT addresses */ -typedef struct rcptnode { - pnode_t node; - char address[64], foraddress[64]; - u_int64_t hash; - bool relay; /* Non-local */ -} rcptnode; - -/* This structure is used to keep a cache of recent hosts from which mail was - * received, along with information on it to determine if the rate of messages - * is too high. - */ -typedef struct hostnode { - hashnode_t node; - char host[128]; /* Hostname, key */ - struct limitrate lr; -} hostnode; - -struct hosts_expire_thread_iterator_udata { - time_t current, soonest; - int cnt; -}; - -/* Used for mmfd thread support delegation/abstraction */ -struct mutexnode { - pnode_t node; - pth_mutex_t mutex; -}; - -/* This defines a state */ -typedef struct state { - int (**functions)(clientenv *); - int errcode; - char *errtext; -} state; - -/* A command of a state */ -typedef struct command { - int loglevel; - char *name, *args, *desc; -} command; - -/* Information for a mailbox */ -struct box_info { - long max_size, size, max_msgs, msgs; - bool filter; - char filter_type; -}; - -/* For fast command lookup */ -struct commandnode { - hashnode_t node; - u_int32_t hash; - struct command *command; - int index; -}; - -/* Used for fast index of result messages */ -struct reply_messages { - int code; - char *msg; -}; - -/* Our validate_msg_line() context */ -struct validate_udata -{ - /* Used to count number of Received: lines */ - long hops; - /* Headers we consider mandatory, which we'll add if necessary */ - bool msgid, date, from, to; - /* Still considering to read the header */ - bool header; - /* Internal linking */ - clientenv *clenv; -}; - -/* Our union for async_resquery() */ -struct async_resquery_msg { - struct async_msg msg; - union { - struct { - int res; - char answer[128]; - } res; - struct { - int r_class, r_type; - char host[128]; - } args; - } un; -}; - - - - -/* PROTOTYPES */ - -int main(int, char **); - -static int all_noop(clientenv *); -static int all_rset(clientenv *); -static int all_quit(clientenv *); -static int all_help(clientenv *); -static int all_helo(clientenv *); -static int all_mail(clientenv *); -static int all_rcpt(clientenv *); -static int all_data(clientenv *); -static int all_beer(clientenv *); - -static bool hash_commands(struct command *, size_t); -static u_int32_t commandnode_keyhash(const void *, size_t); -static int commandnode_keycmp(const void *, const void *, size_t); -static bool reply(fdbuf *, int, bool, const char *, ...); - -static clientenv *alloc_clientenv(void); -static bool init_clientenv(clientenv *, bool); -static clientenv *free_clientenv(clientenv *); -static void empty_rcpts(list_t *); -static bool check_alias(char *); -static bool check_nofrom(const char *, const char *); -static int lock_check(const char *); -static int best_match(const char *, const char *); -static bool local_address(struct box_info *, const char *); -static bool box_filter_allow(const char *, const char *, char); -static void rfc_time(char *); -static bool valid_address(clientenv *, char *, size_t, char *, int); -static bool valid_host(clientenv *, char *, int, bool, bool); -static bool valid_ipaddress(const char *); - -static int validate_msg_line(char *, ssize_t *, int *, void *); -static bool do_data(clientenv *); -inline static size_t do_data_received(char *, size_t, clientenv *, rcptnode *, - const char *); -inline static bool do_data_update(rcptnode *, size_t); -static void do_data_stats(clientenv *, rcptnode *, size_t); -#if defined(MMMAIL_MYSQL) -static bool do_data_mysql(clientenv *, struct fdbrb_buffer *); -#elif defined(MMMAIL_FILE) -static bool address_relay_allow(clientenv *, int *, const char *); -static void iso_time(char *); -static bool message_write(char *, const char *, size_t, struct fdbrb_buffer *, - const char *); -static bool do_data_file(clientenv *, struct fdbrb_buffer *); -static bool do_data_queue_box(clientenv *, const char *, size_t, struct - fdbrb_buffer *, rcptnode *); -static bool do_data_queue_relay(clientenv *, const char *, size_t, struct - fdbrb_buffer *, rcptnode *); -static void do_data_queue_notify(clientenv *); -static int do_data_queue_notify_connect(void); -#else -#error "One of MMMAIL_MYSQL or MMMAIL_FILE must be #defined!" -#endif - -static int handleclient(unsigned long, int, clientlistnode *, struct iface *, - struct async_clenv *); - -static void *_pth_mutex_create(void); -static void *_pth_mutex_destroy(void *); -static void _pth_mutex_lock(void *); -static void _pth_mutex_unlock(void *); -static void _pth_thread_yield(void); -static void _pth_thread_sleep(int); -static bool _pth_eintr(void); - -static void async_resquery(struct async_msg *); -static int a_res_query(clientenv *, const char *, int, int, u_char *, int); - -static void *hosts_expire_thread(void *); -static bool hosts_expire_thread_iterator(hashnode_t *, void *); - -static void *db_gc_thread(void *); -static void db_gc_thread_delete(const char *); - - - - -#endif diff --git a/mmsoftware/mmmail2/ChangeLog b/mmsoftware/mmmail2/ChangeLog deleted file mode 100644 index 96ed104..0000000 --- a/mmsoftware/mmmail2/ChangeLog +++ /dev/null @@ -1,4 +0,0 @@ -$Id: ChangeLog,v 1.1 2002/12/11 10:16:56 mmondor Exp $ - - - diff --git a/mmsoftware/mmmail2/README b/mmsoftware/mmmail2/README deleted file mode 100644 index 2d55c7b..0000000 --- a/mmsoftware/mmmail2/README +++ /dev/null @@ -1,14 +0,0 @@ -$Id: README,v 1.1 2002/12/11 10:16:56 mmondor Exp $ - - - -This is no way complete, nor supposed to be functional, yet. -Here are current design draft notes and test programs for the future mmmail2 -(version 2) which consists of a complete re-implementation of mmmail. - -The goal is to provide all necessary features of mail-related software, -and various data storage methods (db4 and file to SQL). - -Please see the notes/ directory documents for more details. - -Matt diff --git a/mmsoftware/mmmail2/notes/convert.sh b/mmsoftware/mmmail2/notes/convert.sh deleted file mode 100755 index 408227c..0000000 --- a/mmsoftware/mmmail2/notes/convert.sh +++ /dev/null @@ -1,7 +0,0 @@ -# First export LyX document to LaTeX then run this script - -latex mmmail-design.tex -rm -f mmmail-design.ps mmmail-design.aux mmmail-design.log -dvips -o mmmail-design.ps -t letter -D 300 -Z mmmail-design.dvi -ps2pdf mmmail-design.ps -rm -f *~ diff --git a/mmsoftware/mmmail2/notes/mmmail-design.lyx b/mmsoftware/mmmail2/notes/mmmail-design.lyx deleted file mode 100644 index d1dc11d..0000000 --- a/mmsoftware/mmmail2/notes/mmmail-design.lyx +++ /dev/null @@ -1,875 +0,0 @@ -#LyX 1.2 created this file. For more info see http://www.lyx.org/ -\lyxformat 220 -\textclass article -\language english -\inputencoding auto -\fontscheme default -\graphics default -\paperfontsize default -\spacing single -\papersize letterpaper -\paperpackage a4 -\use_geometry 1 -\use_amsmath 0 -\use_natbib 0 -\use_numerical_citations 0 -\paperorientation portrait -\leftmargin 1in -\topmargin 0.5in -\rightmargin 1in -\bottommargin 0.5in -\secnumdepth 3 -\tocdepth 3 -\paragraph_separation indent -\defskip medskip -\quotes_language english -\quotes_times 2 -\papercolumns 1 -\papersides 1 -\paperpagestyle default - -\layout Title - -mmmail v2 project design (draft 3) -\layout Author - -By Matthew Mondor -\layout Abstract - - -\series bold -mmmail -\series default - is currently being redesigned from scratch to support new features which - the current design could not be easily adapted to provide. - This design is intended for a new branch, -\series bold -mmmail -\series default - version 2. -\layout Abstract - - -\series bold -mmmail -\series default -, along with this document, are Copyright (c) 2001-2002, Matthew Mondor, - All rights reserved. -\layout Abstract - -The status of -\series bold -mmmail -\series default - version 2, and it's design is currently under development. - If you can provide any bug report or suggestions about the design, please - contact Matthew Mondor, via email at -\series bold -mmondor@gobot.ca -\layout Section - -Targetted features -\layout Standard - - -\series bold -mmmail -\series default - version 2 should support the following features. -\layout List -\labelwidthstring 00.00.0000 - -Domain\SpecialChar ~ -administration\SpecialChar ~ -delegation: It should be possible for the system manager - to delegate administration tasks to administrators of particular domains. - In the new design administrators can be specified domains they can manage, - and various access permissions can be customized per administrator. -\layout List -\labelwidthstring 00.00.0000 - -Mail\SpecialChar ~ -filtering: Mail should be able to optionally be filtered when getting - in through SMTP before being routed into mailboxes or relayed out. -\layout List -\labelwidthstring 00.00.0000 - -SMTP\SpecialChar ~ -relaying: The current released -\series bold -mmmail -\series default - implementation does not allow relaying messages at all. - The new design should allow it. - It appears to be the most requested feature which alot of users need. - It should relay using smarthosts or MX routing. -\begin_deeper -\layout Itemize - - -\emph on -Design for the smarthosts part not yet complete. -\end_deeper -\layout List -\labelwidthstring 00.00.0000 - -Bandwidth\SpecialChar ~ -shaping: -\series bold -mmmail -\series default - should continue to support per-connection and global read and write bandwidth - speed limits. -\layout List -\labelwidthstring 00.00.0000 - -POP3: -\series bold -mmmail -\series default - already supports POP3 since the start, and should continue supporting it. - The APOP extension could be supported, however this unfortunately requires - passwords to be stored in clean text into the user database rather than - as hashes. -\layout List -\labelwidthstring 00.00.0000 - -IMAP: The second most requested feature consists of IMAP support. - I intend to design the new system in a way to allow making this possible. -\layout List -\labelwidthstring 00.00.0000 - -IMAP-SMTP\SpecialChar ~ -and\SpecialChar ~ -POP3-SMTP: Several times when request was made about relaying, - mention was done about this technique; Relaying from a host would be accepted - during a customizable delay (possibly also for a customizable maximum number - of messages), after a user has successfully authenticated via POP3 or IMAP. -\layout List -\labelwidthstring 00.00.0000 - -Per-mailbox\SpecialChar ~ -auto-forwarding: This feature will allow users to auto-forward - all incomming mail for a mailbox to another email address, if relaying - is allowed for that domain, of course. -\begin_deeper -\layout Itemize - - -\emph on -Design for this not yet complete, should use permissions. -\end_deeper -\layout List -\labelwidthstring 00.00.0000 - -Mailing\SpecialChar ~ -list\SpecialChar ~ -management: It should be possible for -\series bold -mmmail -\series default - to have mailboxes in fact consisting of mailing lists, for which a list - of subscribed addreses would be kept. - Only mail originating from one of those addresses would be accepted and - would be copied locally, or relayed, to each subscribed address. - It also should be possible to provide read-only mailing lists, where only - specified mail accounts can post into. -\begin_deeper -\layout Itemize - - -\emph on -Design for this feature not yet complete. -\end_deeper -\layout List -\labelwidthstring 00.00.0000 - -Abstract\SpecialChar ~ -authentication\SpecialChar ~ -and\SpecialChar ~ -storage\SpecialChar ~ -modules: When the data layout design is - complete, I intend to write a C library of various functions which will - be required by all -\series bold -mmmail -\series default - daemons to manage mail. - An underlaying lower level library should also be implemented, and would - internally be used by the former library, via a structure of function pointers - or other method. - This will allow to abstract storage and authentication code to use for - instance (MySQL, pgsql, file, db4, etc). -\layout Section -\pagebreak_top -Database tables design -\layout Standard - - -\begin_inset Graphics FormatVersion 1 - filename /home/mmondor/mmmail/mmmail-tables.eps - display default - size_type 1 - width 8.5in - keepAspectRatio - rotate - rotateAngle 90 - rotateOrigin leftBaseline - lyxsize_type 1 - lyxwidth 7in - -\end_inset - - -\layout Author -\pagebreak_bottom -Figure 1 - Database tables design -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -admin -\emph default - The description of all allowed remote administrators, with their permissions. - These permissions are described as follows: -\begin_deeper -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -modify_admin -\emph default - Administrator is allowed to create, delete and manage list of administrators - for assigned domains. -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -modify_domain -\emph default - Permission to create, delete and manage domains. -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -modify_alias -\emph default - Right to add and delete aliases for assigned domains. -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -modify_nofrom -\emph default - Permission to add and delete entries of allowed addresses/hosts which may - use empty -\series bold -FROM -\series default - commands via SMTP. -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -modify_relayfrom -\emph default - Privilege to add and delete permanent patterns of hosts and address masks - to accept relaying from via SMTP. -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -modify_user -\emph default - Access to create and delete users within the assigned domains, which of - course may each have mailboxes. -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -modify_box -\emph default - Adminisrator may create, delete and manage mailboxes of users within the - allowed domains. -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -modify_list -\emph default - Administrator can create and delete addresses subscribed to mailing lists - within assigned domains. -\layout Standard - -It is important that the code makes sure that the login handle be unique - when inserting a new entry in this table. - Of course, an administrator who can create administrators can only lower - levels of their own administrators; They thus cannot change a disabled - permission on themselves or on their administrators. -\end_deeper -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -domain -\emph default - This table contains one entry per known domain. - Most delegated administration tasks will be performed on objects, all tied - to specific domains. - Tied to domains are the -\emph on -domain_admin -\emph default -, -\emph on -alias -\emph default -, -\emph on -nofrom -\emph default -, -\emph on -relayfrom -\emph default -, -\emph on -user -\emph default -, -\emph on -box -\emph default - and -\emph on -list -\emph default - tables. - The domain name is important, and will not be found anywhere else within - the tables. - It is important that the code makes sure that the domain name does not - already exist when inserting a new domain. - The -\emph on -lists -\emph default - boolean field disables checking for mailing lists for this domain if FALSE, - thus disabling all mailing lists temporarily for it, if any, and making - -\emph on -mmmail-smtpd -\emph default - faster. -\begin_deeper -\layout Itemize - - -\emph on -Add other domain-specific permissions and limits for boxes, users, lists, - admins, relaying. -\end_deeper -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -domain_admin -\emph default - This table specifies which domains are linked to which administrators. - Several administrators may be assigned to a domain, and several domains - can be handled by an administrator. - Of course the administrator permissions should be taken into account. -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -alias -\emph default - This holds entries for patterns matching a mailbox name which should be - remapped to specific mailboxes. - These only operate on the username part of email addresses, as they are - domain-specific. -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -nofrom -\emph default - Each domain can also be assigned a list of hostname patterns or address - masks which are allowed to use empty -\series bold -SMTP FROM -\series default - commands such as -\series bold -FROM:<> -\series default -. - This table is global rather than tied to specific domains, and only special - administrators should have access to modify it. -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -relayfrom -\emph default - Each domain are now assigned a list of client hostnames or addresses which - should be automatically accepted to relay mail for. - The expires field allows to implement the -\emph on -POP3-SMTP -\emph default - and -\emph on -IMAP-SMTP -\emph default - methods which could automatically setup an entry to allow relaying for - a length of time for hosts which successfully authenticated through POP3 - or IMAP protocols first. - Therefore, this table holds both permanent relay pattern matching entries, - and temporary ones, matching specific addresses, which can expire. - expires should be 0 for permanent entries. -\begin_deeper -\layout Standard - -Like for -\emph on -nofrom -\emph default -, only special administrators are allowed to modify the permanent entries - of this table. - They are global rather than on per-domain basis. - The temporary expiring entries are automatically managed internally, while - the permanent ones can be modified by an administrator with necessary permissio -ns. -\layout Itemize - - -\emph on -It would be trivial to have the temporary expiring entries into a memory - cache only, rather than in a table. - For performance considerations this could be done. - At least the design shows how it is possible to achieve by future -\series bold -mmmail -\series default - version 2. -\end_deeper -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -box -\emph default - Describes each mailbox, tied to their user and domain. - Each box comports the quota limits with their current counters. - It is important to make sure that the address field remain unique for the - current domain when adding new boxes. -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -list -\emph default - Similarly to -\emph on -box -\emph default -, specifies each mailing list for a domain. - If mailing lists are enabled for a domain ( -\emph on -lists -\emph default - field is TRUE in -\emph on -domain -\emph default - table) this table is verified for a match by -\emph on -mmmail-smtpd -\emph default - when receiving a message, before the box table be verified for potential - matching. - The -\emph on -from -\emph default - field specifies which -\series bold -FROM -\series default - address should be used when relaying the message to the subscribers. - It is important that the code makes sure that it does not already exist - when inserting a new list address for a domain. -\begin_deeper -\layout Itemize - - -\emph on -Several other list configuration fields may need to be added here. - Possibly also a list of users which should not be allowed to subscribe, - or rather a list of patterns which should match to choose which subscriber - hosts to accept, etc. - Also think of a control system on who is allowed to post. -\end_deeper -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -subscriber -\emph default - This table specifies all subscribed addresses for a mailing list. - These are verified by mmmail-listd before accepting the message for the - list, to make sure that only subscribers can post. - The accepted message is then dispatched to each subscriber, relayed if - necessary (if relaying is allowed of course). -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -header -\emph default - A header entry represents a message. - As the message body can be shared accross multiple instances of the same - messages in the various mailboxes, only the header is actually copied (See - -\emph on -body -\emph default - table for more information). - To this header are either linking, with mutual exclusion, an entry from - the -\emph on -filterqueue -\emph default -, -\emph on -boxqueue -\emph default -, -\emph on -listqueue -\emph default - or -\emph on -relayqueue -\emph default -. -\begin_deeper -\layout Standard - -When a header is deleted, it is important to decrease and verify the -\emph on -refcount -\emph default - field of the corresponding -\emph on -body -\emph default - table entry it links to, and to delete that body message entry when the - counter reaches zero. -\end_deeper -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -body -\emph default - This table contains all message bodies, to which are linking one or more - -\emph on -header -\emph default - table entries. - We maintain a -\emph on -refcount -\emph default - field which permits to know when this entry is no longer referenced and - may be deleted. - This design permits to reduce the frequency of database operations on very - large elements, and also to greatly optimize the operations and storage - space requirements on mailing lists where subscribers consist of mailboxes - we are providing. -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -filterqueue -\emph default - This table is only used if -\emph on -mmmail-filterd -\emph default - runs and mail filtering options have been enabled. - It allows to process all incomming mail which -\emph on -mmmail-smtpd -\emph default - receives through sanity checking scripts before it gets dispatched into - any other queues. - The -\emph on -destination -\emph default - field permits to know in which queue the message should be moved after - passing the filter successfully, if it does. - It also allows application-specific filters to be set by the system administrat -or; A different filtering operation may be wanted for mail which has to - be relayed than for mail which should be processed for a mailing list, - etc. -\begin_deeper -\layout Standard - -If mail filtering is disabled, -\emph on -mmmail-smtpd -\emph default - moves the message immediately in the queue it belongs to, rather than letting - -\emph on -mmmail-filterd -\emph default - do it asynchroneously. -\end_deeper -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -boxqueue -\emph default - All ready messages into all local mailboxes have an entry in this table. - These are ready to be accessed via -\emph on -mmmail-pop3d -\emph default - and -\emph on -mmmail-imapd -\emph default - services. -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -listqueue -\emph default - Mail which has been received for an address corresponding to a mailing - list replicator is stored into this queue for processing by the -\emph on -mmmail-listd -\emph default - service. -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -relayqueue -\emph default - All mail which should be relayed out (for both mailing list messages to - non-local mailboxes and normal relaying) is stored in this queue, for processin -g by the -\emph on -mmmail-relayd -\emph default - service. -\layout Section - -Server processes -\layout Standard - -Here now consists of a list of the planned server processes, and their intended - behavior. -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -mmmail-smtpd -\emph default - The standard SMTP server, now called -\emph on -mmsmtpd -\emph default -, which accepts mail from SMTP clients, performing flood protection and - various sanity checking, and dispatching new mail into the -\emph on -filterqueue -\emph default -, -\emph on -listqueue -\emph default -, -\emph on -boxqueue -\emph default - or -\emph on -relayqueue -\emph default -. -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -mmmail-filterd -\emph default - Processes the -\emph on -filterqueue -\emph default - entries, performs wanted sanity checking on mail, and then either drop, - or move those to the -\emph on -boxqueue -\emph default -, -\emph on -listqueue -\emph default - or -\emph on -relayqueue -\emph default -. - This process is optional as -\emph on -mmmail-smtpd -\emph default - can direct messages in their respected queues immediately if filtering - is disabled. -\begin_deeper -\layout Itemize - - -\emph on -It is still under investigation as to how known third-party filters designed - for sendmail should be used by -\emph default -mmmail-filterd -\emph on -. -\end_deeper -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -mmmail-listd -\emph default - This process handles the -\emph on -listqueue -\emph default - and makes sure that the originator address matches a subscribed address, - to then fill the -\emph on -boxqueue -\emph default - to deliver local mailoxes, or the -\emph on -relayqueue -\emph default - to relay mail out to the subscribers. -\begin_deeper -\layout Standard - -This server could also support and handle subscription and unsubscription - requests, sending a confirmation email back with a code which it would - expect back within a period of time to subscribe the new address. -\layout Itemize - - -\emph on -A new table should be created for the expected responses with expiration. - Also should be taken into account limits for maximum number of subscribers, - number of failed relayed mail to automatically unsubscribe an address, - etc. -\end_deeper -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -mmmail-relayd -\emph default - This server processes the -\emph on -relayqueue -\emph default - and attempts to act as an SMTP client to send mail out to the SMTP servers - pointed out by the MX record for the destination domain. - An entry is deleted from the -\emph on -relayqueue -\emph default - when it expires (too many retries or expired retry period), in which case - reply mail could be bounced to the mail originator, or when mail has successful -ly been relayed out. -\begin_deeper -\layout Itemize - - -\emph on -Some investigation should be done about how to effectively send mail out - using more than an RCPT when possible (with decent limit) and to send to - more than a single server at a time without slowing too much database access - because of transactions. - Moreover, perhaps that some MX address cacheing is desirable. -\end_deeper -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -mmmail-pop3d -\emph default - The standard POP3 protocol client frontend, internally processing the -\emph on -boxqueue -\emph default -. -\begin_deeper -\layout Itemize - - -\emph on -Support for APOP should be envisaged. -\end_deeper -\layout List -\labelwidthstring 00.00.0000 - - -\emph on -mmmail-imapd -\emph default - The standard IMAP protocol client frontend, internally working on the -\emph on -boxqueue -\emph default -. -\the_end diff --git a/mmsoftware/mmmail2/notes/mmmail-tables.dia b/mmsoftware/mmmail2/notes/mmmail-tables.dia deleted file mode 100644 index 4469aea6088b13b157d06f83eeb37a99331ba8cf..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3913 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 -#include -#include -#include -#include -#include -#ifdef __GLIBC__ -#include -#endif - -#include -#include - - - - -MMCOPYRIGHT("@(#) Copyright (c) 2002-2004\n\ -\tMatthew Mondor. All rights reserved.\n"); -MMRCSID("$Id: mmpasswd.c,v 1.6 2004/05/06 00:05:35 mmondor Exp $"); - - - - -/* The only reason we use structures here is for security considerations; - * We wouldn't want the caller to supply smaller buffers than required and - * cause buffer overflows. And the generated results have fixed number of - * bytes, so let's not require the user to specify the length of his buffers, - * it could be wrong anyways. - * - * XXX Using structures serves no purpose here, but they don't harm neither. - * so I left this unchanged for mmpasswd. - */ - -struct salt_md5 { - /* Restrict salt to 6 characters since PHP crypt() seems to have this - * limit. - */ - char salt[7]; -}; - -struct hash_md5 { - char hash[35]; -}; - -int main(int, char **); -void seedrandom(void); -void gensalt_md5(struct salt_md5 *); -void genhash_md5(struct hash_md5 *, const char *); -int test(const char *, const char *); - - - - -int main(int argc, char **argv) -{ - if (argc == 2) { - - struct hash_md5 hash_md5; - - seedrandom(); - - genhash_md5(&hash_md5, argv[1]); - printf("%s\n", hash_md5.hash); - - exit(0); - - } else if (argc == 3) { - - int res; - - res = test(argv[1], argv[2]); - printf("%d\n", res); - exit(res); - - } else printf("Usage: mmpasswd '' ['hash']\n"); - - exit(-1); -} - - -void seedrandom(void) -{ - int fd; - unsigned long s; - - if ((fd = open("/dev/urandom", O_RDONLY)) != -1) { - if (read(fd, &s, sizeof(unsigned long)) == sizeof(unsigned long)) - srandom(s); - close(fd); - } else - /* Not really good, but better than nothing */ - srandom(time(NULL)); -} - - -void gensalt_md5(struct salt_md5 *salt) -{ - int i, t = sizeof(salt->salt) - 1; - static const char table[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ\ -abcdefghijklmnopqrstuvwxyz"; - - for (i = 0; i < t; i++) - salt->salt[i] = table[random() % (sizeof(table) - 1)]; - salt->salt[t] = '\0'; -} - - -void genhash_md5(struct hash_md5 *hash, const char *pass) -{ - struct salt_md5 salt; - char setting[14], *ptr; - int t = sizeof(hash->hash) - 1; - - gensalt_md5(&salt); - snprintf(setting, 13, "$1$%s$", salt.salt); - if ((ptr = crypt(pass, setting)) != NULL) { - mm_memcpy(hash->hash, ptr, t); - hash->hash[t] = '\0'; - } else hash->hash[0] = '\0'; -} - - -int test(const char *passwd, const char *hash) -{ - char *ptr; - - if ((ptr = crypt(passwd, hash)) != NULL) { - if (mm_strcmp(ptr, hash) == 0) - return (0); - } - - return (-1); -} diff --git a/mmsoftware/mmsendmail/GNUmakefile b/mmsoftware/mmsendmail/GNUmakefile deleted file mode 100644 index 23072cb..0000000 --- a/mmsoftware/mmsendmail/GNUmakefile +++ /dev/null @@ -1,22 +0,0 @@ -# $Id: GNUmakefile,v 1.2 2004/06/02 01:08:26 mmondor Exp $ - -MMLIBS := $(addprefix ../mmlib/,mmfd.o mmstring.o) - -OBJS := mmsendmail.o - -CFLAGS += -Wall - - -all: mmsendmail - -%.o: %.c - cc -c ${CFLAGS} -I. -I../mmlib -o $@ $< - -mmsendmail: $(MMLIBS) $(OBJS) - cc -o $@ -lc $(OBJS) $(MMLIBS) - -install: - install -cs -o 0 -g 0 -m 550 mmsendmail /usr/local/libexec - -clean: - rm -f mmsendmail $(OBJS) $(MMLIBS) diff --git a/mmsoftware/mmsendmail/Makefile b/mmsoftware/mmsendmail/Makefile deleted file mode 100644 index 7fe3372..0000000 --- a/mmsoftware/mmsendmail/Makefile +++ /dev/null @@ -1,34 +0,0 @@ -# $Id: Makefile,v 1.3 2003/10/13 11:20:04 mmondor Exp $ - -CC = gcc -MAKE = make - -CFLAGS += -Wall -DDEBUG - -INCDIR = -I../mmlib -I/usr/include -I/usr/pkg/include -I. -LIBDIR = -L/usr/local/lib -L/usr/lib -L/lib -L/usr/pkg/lib - -LIBS = ../mmlib/libmmondor.a -lc - -OBJS = mmsendmail.o - - - -CCOBJ = $(CC) $(CFLAGS) -c $(INCDIR) - - - - -mmsendmail: $(OBJS) - $(CC) $(CFLAGS) -o mmsendmail $(INCDIR) $(LIBDIR) $(OBJS) $(LIBS) - - - - -clean: - -rm -f $(OBJS) mmsendmail - - -mmsendmail.o: - $(CCOBJ) mmsendmail.c - diff --git a/mmsoftware/mmsendmail/mmsendmail.c b/mmsoftware/mmsendmail/mmsendmail.c deleted file mode 100644 index 1344639..0000000 --- a/mmsoftware/mmsendmail/mmsendmail.c +++ /dev/null @@ -1,377 +0,0 @@ -/* $Id: mmsendmail.c,v 1.11 2004/11/09 11:53:16 mmondor Exp $ */ - -/* - * Copyright (C) 2000-2004, Matthew Mondor - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software written by Matthew Mondor. - * 4. The name of Matthew Mondor may not be used to endorse or promote - * products derived from this software without specific prior written - * permission. - * - * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -/* mmsendmail.c by Matthew Mondor - * - * USAGE: mmsendmail - * -- - * - * Useful to replace MTAs for use by Mutt and Pine. - * Replace "sendmail -oem -oi" command by: - * "mmsendmail smtp.somehost.net 25 my@address.net optionalauthname|none" - * - * Another great use for this is that it can use any port we want, it thus - * could connect to a forwarded port on localhost, say 2525 which could - * be tunneled through ssh to another SMTP server. - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - - - -int main(int, char **); -static int result(void); -static bool eintr(void); -static int validate(char *, ssize_t *, int *, void *); -static void sighandler(int); - - - -static fdbuf *fdb; -static bool endheader, toheader, fromheader; -static char *fromaddr; - - - -int -main(int argc, char **argv) -{ - struct sockaddr_in server; - struct hostent *he; - struct in_addr *ip; - fdbuf *fdbi; - int msgsock, res, ret, cnt; - char *hostname = NULL, *authcompname = NULL; - bool authok = FALSE, rcptok = FALSE; - unsigned short port = 25; - fdfuncs fdf = { - malloc, - free, - poll, - read, - write, - sleep, - usleep, - NULL, - NULL, - NULL, - NULL, - NULL, - eintr - }; - struct fdbrb_buffer *fdbrb; - struct sigaction act; - - fdbi = fdb = NULL; - msgsock = -1; - fdbrb = NULL; - ret = EXIT_FAILURE; - endheader = toheader = fromheader = FALSE; - fromaddr = NULL; - - if (argc > 6) { - hostname = argv[1]; - port = (unsigned short)atoi(argv[2]); - fromaddr = argv[3]; - authcompname = argv[4]; - } else { - syslog(LOG_NOTICE, "Usage"); - fprintf(stderr, "Usage: mmsendmail \ - -- [ ...]\n"); - goto end; - } - - /* Setup signal handler */ - act.sa_handler = sighandler; - act.sa_flags = SA_NOCLDWAIT; - sigemptyset(&act.sa_mask); - sigaction(SIGTERM, &act, NULL); - sigaction(SIGPIPE, &act, NULL); - sigaction(SIGSEGV, &act, NULL); - - /* We want to immediately read any stdin input until EOF */ - if ((fdbi = fdbopen(&fdf, NULL, fileno(stdin), 4096, 4096, 0, 0, 60000, - 60000, FALSE)) == NULL) { - fprintf(stderr, "* fdbopen(stdin)\n"); - syslog(LOG_NOTICE, "fdbopen(stdin)"); - goto end; - } - if ((res = fdbreadbuf(&fdbrb, fdbi, 32768, 1024, 0, 0, validate, NULL, - FALSE)) != FDBRB_EOF) { - fprintf(stderr, "* fdbreadbuf()\n"); - syslog(LOG_NOTICE, "fdbreadbuf()"); - goto end; - } - - /* Obtain IP address of supplied hostname if not already an address */ - if ((he = gethostbyname(hostname))) - ip = (struct in_addr *)he->h_addr_list[0]; - else { - syslog(LOG_NOTICE, "Cannot resolve hostname"); - fprintf(stderr, "* Cannot resolve hostname"); - goto end; - } - - /* Create TCP/IP communications socket */ - if ((msgsock = socket(AF_INET, SOCK_STREAM, 0)) == -1) { - syslog(LOG_NOTICE, "Cannot create socket"); - fprintf(stderr, "* Cannot create socket"); - goto end; - } - - /* Setup our structure to connect to server */ - mm_memclr(&server, sizeof(struct sockaddr_in)); - server.sin_family = AF_INET; - server.sin_addr.s_addr = ip->s_addr; - server.sin_port = htons(port); - - /* Attempt to connect to host */ - if ((connect(msgsock, (struct sockaddr *)&server, sizeof(server))) - != 0) { - fprintf(stderr, "* Cannot connect to server"); - syslog(LOG_NOTICE, "Cannot connect to server"); - goto end; - } - - if ((fdb = fdbopen(&fdf, NULL, msgsock, 4096, 4096, 0, 0, 60000, - 60000, FALSE)) == NULL) { - fprintf(stderr, "* fdbopen(sock)"); - syslog(LOG_NOTICE, "fdbopen(sock)"); - goto end; - } - - if (result() == 220) { - - /* Authenticate if wanted */ - if (mm_strcmp(authcompname, "none") != 0) { - fdbprintf(fdb, "HELO %s\r\n", authcompname); - fdbflushw(fdb); - if (result() == 250) - authok = TRUE; - else - authok = FALSE; - } else - authok = TRUE; - - if (authok) { - /* Tell server who we are sending from */ - fdbprintf(fdb, "MAIL FROM:<%s>\r\n", fromaddr); - fdbflushw(fdb); - if (result() == 250) { - - /* Tell server all addresses to send this message to; - * those were provided on commandline by Mutt/Pine - */ - rcptok = TRUE; - for (cnt = 6; cnt < argc; cnt++) { - fdbprintf(fdb, "RCPT TO:<%s>\r\n", argv[cnt]); - fdbflushw(fdb); - if (result() != 250) { - syslog(LOG_NOTICE, "RCPT TO failed"); - fprintf(stderr, " * RCPT TO failed\n"); - rcptok = FALSE; - break; - } - } - - if (rcptok) { - fdbrwrite(fdb, "DATA\r\n", 6); - if (result() == 354) { - /* Send our message data */ - if (fdbrwrite(fdb, fdbrb->array, fdbrb->current) != - fdbrb->current || - fdbrwrite(fdb, ".\r\n", 3) != 3) { - syslog(LOG_NOTICE, "Failed sending message data"); - fprintf(stderr, " * Message send failed\n"); - goto end; - } - - /* Was message accepted? */ - if (result() == 250) - ret = EXIT_SUCCESS; - else { - syslog(LOG_NOTICE, "Message refused by server"); - fprintf(stderr, " * Message refused by server\n"); - } - } - } - - } else { - syslog(LOG_NOTICE, "MAIL FROM failed"); - fprintf(stderr, " * MAIL FROM failed"); - } - } else { - syslog(LOG_NOTICE, "HELO failed"); - fprintf(stderr, " * HELO failed\n"); - } - - /* Disconnect from server */ - fdbrwrite(fdb, "QUIT\r\n", 6); - - } else { - syslog(LOG_NOTICE, "Unexpected server welcome message"); - fprintf(stderr, " * Unexpected server welcome message\n"); - } - -end: - fdbfreebuf(&fdbrb); - if (fdbi != NULL) - fdbclose(fdbi); - if (fdb != NULL) - fdbclose(fdb); - if (msgsock != -1) - close(msgsock); - - return ret; -} - - -static int -result(void) -{ - int res = -1; - char buffer[1024]; - - if ((fdbgets(fdb, buffer, 1023, TRUE)) > 0) - res = atoi(buffer); - - return res; -} - - -static bool -eintr(void) -{ - - if (errno == EINTR) - return TRUE; - - return FALSE; -} - - -/* ARGSUSED */ -static int -validate(char *line, ssize_t *len, int *res, void *udata) -{ - - if (!endheader) { - char *ptr; - - if (len == 0 || (ptr = mm_strchr(line, ':')) == NULL) - goto eh; - if (ptr[1] != ' ') - goto eh; - /* Verify if we get mandatory header lines */ - if (mm_strncmp(line, "To: ", 4) == 0) - toheader = TRUE; - if (mm_strncmp(line, "From: ", 6) == 0) - fromheader = TRUE; - } else { - /* Convert '.' to '..', we must be the one sending the final dot. */ - if (line[0] == '.' && line[1] == '\0') { - line[1] = '.'; - line[2] = '\0'; - (*len)++; - } - } - - return FDBRB_OK; - -eh: - { - char tline[1024]; - - endheader = TRUE; - - /* Add mendatory headers if necessary */ - *tline = '\0'; - if (!fromheader) - (void) snprintf(tline, 1023, "From: %s", - fromaddr); - if (!toheader) - (void) snprintf(tline, 1023, "%s\r\nTo: undisclosed-recipients:;", - tline); - if (*tline != '\0') { - if (*len != 0) { - /* Insert header before message line */ - (void) snprintf(tline, 1023, "%s\r\n\r\n%s\r\n", tline, line); - *len = mm_strncpy(line, tline, 1023); - } else { - /* Replace current empty line by header */ - (void) snprintf(line, 1023, "%s\r\n", tline); - *len = mm_strlen(line); - } - } - } - - return FDBRB_OK; -} - - -static void -sighandler(int sig) -{ - char *s = NULL; - - switch (sig) { - case SIGSEGV: - s = "SIGSEGV (segmentation fault)"; - break; - case SIGPIPE: - s = "SIGPIPE (server dropped connection)"; - break; - case SIGTERM: - s = "SIGTERM (we were killed)"; - break; - default: - s = "We received a spurious signal!"; - } - - syslog(LOG_NOTICE, "SIGNAL: %s", s); - fprintf(stderr, " * Received signal: %s\n", s); - exit(EXIT_FAILURE); -} diff --git a/mmsoftware/mmspawnd/GNUmakefile b/mmsoftware/mmspawnd/GNUmakefile deleted file mode 100644 index 05bf763..0000000 --- a/mmsoftware/mmspawnd/GNUmakefile +++ /dev/null @@ -1,22 +0,0 @@ -# $Id: GNUmakefile,v 1.7 2003/11/12 05:37:18 mmondor Exp $ - -MMLIBS := $(addprefix ../mmlib/,mmpool.o mmlog.o mmreadcfg.o mmstring.o \ -mmhash.o mmalarm.o mmheap.o mmlimitrate.o) -OBJS := mmspawnd.o -CFLAGS += -Wall -DDEBUG - -all: mmspawnd - -%.o: %.c - cc -c ${CFLAGS} -I. -I../mmlib -o $@ $< - -mmspawnd: $(MMLIBS) $(OBJS) - cc -o $@ $(OBJS) -lc $(MMLIBS) - -install: all - install -cs -o 0 -g 0 -m 500 mmspawnd /usr/local/sbin - install -c -o 0 -g 0 -m 444 mmspawnd.conf.5 /usr/local/man/man5 - install -c -o 0 -g 0 -m 444 mmspawnd.8 /usr/local/man/man8 - -clean: - rm -f mmspawnd $(OBJS) $(MMLIBS) diff --git a/mmsoftware/mmspawnd/mmspawnd.8 b/mmsoftware/mmspawnd/mmspawnd.8 deleted file mode 100644 index 84322a5..0000000 --- a/mmsoftware/mmspawnd/mmspawnd.8 +++ /dev/null @@ -1,160 +0,0 @@ -.\" $Id: mmspawnd.8,v 1.7 2003/11/10 00:52:11 mmondor Exp $ -.\" -.\" Copyright (C) 2003, Matthew Mondor -.\" All rights reserved. -.\" -.\" Redistribution and use in source and binary forms, with or without -.\" modification, are permitted provided that the following conditions -.\" are met: -.\" 1. Redistributions of source code must retain the above copyright -.\" notice, this list of conditions and the following disclaimer. -.\" 2. Redistributions in binary form must reproduce the above copyright -.\" notice, this list of conditions and the following disclaimer in the -.\" documentation and/or other materials provided with the distribution. -.\" 3. All advertising materials mentioning features or use of this software -.\" must display the following acknowledgement: -.\" This product includes software written by Matthew Mondor. -.\" 4. The name of Matthew Mondor may not be used to endorse or promote -.\" products derived from this software without specific prior written -.\" permission. -.\" 5. Redistribution of source code may not be released under the terms of -.\" any GNU Public License derivate. -.\" -.\" THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR -.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES -.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. -.\" IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, -.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -.\" BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF -.\" USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF -.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -.\" -.Dd October 28, 2003 -.Dt MMSPAWND 8 -.Os mmsoftware -.Sh NAME -.Nm mmspawnd -.Nd -.Xr inetd 8 -alternative for better security -.Sh SYNOPSIS -.Nm mmspawnd Op Ar config_file -.Sh DESCRIPTION -.Nm -is a useful replacement for -.Xr inetd 8 -when security is a concern. It only allows to serve one service per running -instance, but a different configuration file can be provided for each service -to run several ones as required. It supports connection number and rate limits -on a global and per-address basis, -.Xr chroot 2 -and -.Xr setrlimit 2 . It will only -.Xr bind 2 -to specified addresses/interfaces to listen for connections. -It also ensures to drop privileges definitely before starting operation, using -.Xr setgid 2 , -.Xr setuid 2 -and -.Xr setgroups 2 . -.Pp -Further, it comports a safe -.Xr execve 2 -wrapper which ensures to not pass unexpected arguments or environmental -variables, as well as to only execute ELF binaries which are owned by the -superuser and non-writable. No globbing whatsoever is performed by it, -and it only allows an absolute path to be provided to the application to -launch. It will not allow execution of files with the setuid or setgid -bits set. -.Pp -All connections are logged via the specified facility using -.Xr syslog 3 , -as well as the exit code of the application when closing the connection with -the client. Any security issue encoutered by the -.Xr execve 2 -wrapper is also logged. -.Pp -It's default configuration makes it harmless but useless. See the -.Xr mmspawnd.conf 5 -manual page for configuration file description. -.Pp -A good usage for -.Xr mmspawnd 8 -is for instance to run a CVS pserver in safe conditions. It then can be -restrited to the specified root directory, and access the files with read-only -access under an unprivileged user. If used for this purpose, also look at -the -.Xr mmanoncvs 8 -manual page. -.Pp -Another interesting feature is being able to specify if the currently running -clients should be interrupted or not if the -.Nm -server is to be stopped or restarted. In the case of a CVS checkout for -instance, if -.Nm KILL_CHILDREN -configuration option is FALSE, the operation will continue until it finishes -normally, even in the event of a server configuration change or restart. -.Pp -For additional security, only the superuser is allowed to launch -.Nm , -unless compiled with the -.Nm -DNODROPPRIVS -option, in which case it could be used by a nonprivileged user to bind -a service to a high port, and server refuse to be launched by root. -Other users attempting to launch the service will cause a line to be logged, -telling which user ID attempted the launch. -.Pp -When launched successfully, a status line is logged telling under which -user ID it runs, which port it listens to and on which addresses/interfaces, -as well as the configuration file location and service command. -.Pp -Note that this system redirects the stderr stream (filedescriptor 2) of -the command launched to serve the clients to the "/dev/null" device, so that -errors output from it be not disclosed to the remote user. -.Pp -Another optional interesting feature which it offers is the possibility -to use advisory locking on a special control file to synchronize the service -with another program. The lock is acquired in exclusive mode by -.Nm -when at least one connection exists being served. It is released when no -more connections remain. This way, another program can rely on the fact that -noone is using the service if it can obtain the lock. As long as the other -application holds the lock, the service will refuse new connections. Normal -service resumes immediately as the lock is released by the third party -application. Of course, it is important to properly configure the permissions -on that lock file, if the feature is enabled. -.Pp -Eventual support for bandwidth shaping/throttling and network inactivity -timeout limits are planned. However, as those will require a redesign and -rewrite of -.Nm , -all we support for now is total maximum timeout for execution of the supplied -service command. This is still better than -.Xr inetd 8 -behavior, however. -.Sh FILES -.Bl -tag -width indent -offset indent -.It Pa /usr/local/sbin/mmspawnd -The actual server binary -.It Pa /usr/local/etc/mmspawnd.conf -The default configuration file to read -.El -.Sh AUTHOR -.Nm -was written by Matthew Mondor, and is -Copyright (c) 2003, Matthew Mondor, All Rights Reserved. -.Sh SEE ALSO -.Xr mmspawnd.conf 5 , -.Xr bind 2 , -.Xr chroot 2 , -.Xr setrlimit 2 , -.Xr setuid 2 , -.Xr setgid 2 , -.Xr setgroups 2 , -.Xr mmanoncvs 8 , -.Xr inetd 8 . -.Sh BUGS -Please report any bug to mmondor@accela.net diff --git a/mmsoftware/mmspawnd/mmspawnd.c b/mmsoftware/mmspawnd/mmspawnd.c deleted file mode 100644 index 622b3ce..0000000 --- a/mmsoftware/mmspawnd/mmspawnd.c +++ /dev/null @@ -1,1709 +0,0 @@ -/* $Id: mmspawnd.c,v 1.26 2004/09/29 00:43:41 mmondor Exp $ */ - -/* - * Copyright (C) 2003, Matthew Mondor - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Matthew Mondor. - * 4. The name of Matthew Mondor may not be used to endorse or promote - * products derived from this software without specific prior written - * permission. - * 5. Redistribution of source code may not be released under the terms of - * any GNU Public License derivate. - * - * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -/* - * An inetd-like network server, but which only listens to one port and - * launches one command. It however performs alot more sanity checking, - * and has support for chroot(2), custom limits, and setrlimit(2) ones - * (for the children only, since we handle our own limits checking). - * To use it for several services, a separate configuration file should - * be provided for each. - */ - -/* TODO: - * - It would be nice to have all I/O of all client processes be tunnelled - * through a bandwidth shaper process (which could then use mmfd(3)). - * However, this is not an immediate concern. - * - Although we cannot monitor if a child process is idle properly using - * the current method, it would be nice to have a maximum time to live - * parameter in seconds. - * - The first and second problems could both be solved if we implemented - * a popen()-like system instead of having the children serve directly - * the socket. We either could have another dedicated process handle the - * I/O of many ongoing connections, or to have the main system use it - * while still verifying for connection events. Doing this would both - * allow custom bandwidth shaping, as well as real network inactivity - * monitoring. If using such a method, it would probably be nice to use - * a library like libevent or libio which could take adventage of features - * like kqueue and epoll where available. Using poll(2) wouldn't be too - * bad however... - * - Think properly. We have a recursion prevention lock, which should use - * a lock path outside of the chroot(2) if any. We have the ALOCK_PATH, - * which can also be outside the chroot(2), since only the parent locks it. - * However, there is the LOCK_PATH which should be inside the chroot(2) if - * any, because both the parent, cache manager and children processes are - * locking it. These files also need to be readable by the user or group - * under which mmspawnd(8) runs, so that shlocks_reopen() works. Hmm these - * are not used in the children processes however, only by the cache manager - * process after launch... Either we have chroot(2) called in each, in which - * case the files can be anywhere on the filesystem, or we cause those lock - * files to be created after chroot(2). Also, because we drop privileges - * after this, both can write and open the locks fine permission-wise. - * So plz hlp wut 2 doo 2 nut teh errer kthx!!11111 u teh their!?1// - * Argh!!! children processes call client_resolve()! Hence they also need - * to reopen lock file(s)... So LOCK_PATH HAS to be within the chroot(2), - * and chroot(2) needs to be called before calling shlocks_init()! - * - Fix client_resolve() so that getnameinfo(3) can be called concurrently. - * - Update man page about lock paths requirements etc! - */ - - - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include /* strerror(3) */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* For more information about these, see the manual pages in mmlib/ */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - - - -MMCOPYRIGHT("@(#) Copyright (c) 2003\n\ -\tMatthew Mondor. All rights reserved.\n"); -MMRCSID("$Id: mmspawnd.c,v 1.26 2004/09/29 00:43:41 mmondor Exp $"); - - - -/* DEFINITIONS */ - -#define DAEMON_NAME "mmspawnd" -#define DAEMON_VERSION "0.0.2/mmondor" - -struct configuration { - char CHROOT_DIR[256], LOCKS_PATH[256], LOCKS_USER[32], LOCKS_GROUP[32], - LOCKS_MODE[4], PID_PATH[256], USER[32], GROUPS[256], LOG_FACILITY[32], - LISTEN_ADDRESSES[1024], COMMAND[256], ALOCK_PATH[256], ALOCK_USER[32], - ALOCK_GROUP[32], ALOCK_MODE[4], PROCTITLE[64], STDERR_FILE[256]; - long LISTEN_PORT, MAX_CONNECTIONS, MAX_ADDRESSES, MAX_PER_ADDRESS, - CONNECTION_RATE, CONNECTION_PERIOD, MAX_ARGUMENTS, COMMAND_TIMEOUT, - LIMIT_CORE, LIMIT_CPU, LIMIT_DATA, LIMIT_FSIZE, LIMIT_MEMLOCK, - LIMIT_NOFILE, LIMIT_NPROC, LIMIT_RSS, LIMIT_STACK; - bool RESOLVE_ADDRESSES, KILL_CHILDREN, RLIMITS; -}; - -/* Necessary synchronization locks for shared memory access */ -enum shlocks { - SHLOCK_IPADDR = 0, - SHLOCK_MAX -}; - -/* Base to shared memory, significant both in the parent and children - * processes. The associated synchronization lock is commented. - */ -struct shmem_data { - /* Locked with SHLOCK_IPADDR */ - pool_t ipaddr_pool; - hashtable_t ipaddr_table; -}; - -/* Base to client-specific data, only significant for each children process */ -struct client_data { - struct ipaddr_node *ipn; - const char *ipaddr; - struct sockaddr saddr; -}; - -/* And server-specific data */ -struct server_data { - int runlock, alock, current_connections; - pool_t pid_pool; - hashtable_t pid_table; - timerctx_t timers; - char *line, *command, **argv; -}; - -/* Defines an interface to listen to */ -struct iface { - char address[16]; /* Server address string or '\0' */ - int sock; /* Bound socket (or -1) */ - struct sockaddr_in saddr; /* Address to bind(2) to */ -}; - -/* Used for the hostname cache and per-address connection rate limiting */ -struct ipaddr_node { - hashnode_t node; - struct limitrate lr; - struct sockaddr address; /* Key */ - long connections; - char hostname[64]; -}; - -/* Used to efficiently link a child pid_t to an ipaddr_node structure pointer, - * so that we may easily decrease the total connections counter for that - * address in our SIGCHLD handler. - */ -struct pid_node { - hashnode_t node; - pid_t pid; /* Key */ - timerid_t tid; - struct ipaddr_node *ipn; -}; - -/* Used to store shared context between ipddr_expire_process() and it's - * iterator function - */ -struct ipaddr_expire_process_iterator_udata { - time_t current, soonest; -}; - -/* Useful macro to cleanup code later */ -#define SETRLIMIT(restxt, resource, limit) do { \ - if ((limit) != -1) { \ - struct rlimit rl; \ - \ - rl.rlim_cur = rl.rlim_max = (rlim_t)(limit); \ - if (setrlimit((resource), &rl) == -1) \ - syslog(LOG_NOTICE, "* setrlimit(%s, %ld) - (%s)", \ - (restxt), (limit), strerror(errno)); \ - } \ -} while (/* CONSTCOND */0) - - - -/* PROTOTYPES */ - -int main(int, char **); - -static bool daemon_detach(const char *, const char *); -static void daemon_sighandler(int); -static bool client_kill_iterator(hashnode_t *, void *); -static void client_timeout(timerid_t, void *); -static void pidfile_write(const char *); -static int lock_check(const char *); -static struct iface *ifaces_parse(char *); - -static void client_resolve(void); -static void exec_command(const char *, char * const *); - -static bool connection_open(const char *); -static void connection_close(void); -static struct ipaddr_node *ipaddr_open(struct sockaddr *, const char *); -static void ipaddr_close(struct ipaddr_node *); - -static u_int32_t key_hash32(const void *, size_t); -static int key_cmp32(const void *, const void *, size_t); -static int key_pidcmp(const void *, const void *, size_t); -static u_int32_t key_pidhash(const void *, size_t); - -void launch_server_child_init(const char *, uid_t, - gid_t *, int); - -static pid_t launch_server_process(uid_t, gid_t *, int); -static void server_process_main(void); - -static pid_t launch_ipaddr_expire_process(uid_t, gid_t *, - int); -static void ipaddr_expire_process_main(void); -static bool ipaddr_expire_process_iterator(hashnode_t *, - void *); - - - -/* GLOBALS */ - -/* Normal static data used for current configuration, so that clients may keep - * their active config copy in case the server reloads it; Also is of faster - * access than shared memory, requireing no locking for synchronization. - */ -static struct configuration CONF; - -/* Base structure to all shared memory data, for which synchronization is - * required using locks. - */ -static struct shmem_data *shmem = NULL; - -/* Static data array mapping lock files descriptors to an index used with - * flock(3) for the various shared memory sections. - */ -static int locks[(enum shlocks)SHLOCK_MAX]; - -/* Static client-specific data which is only significant for each children - * process, as well as server-specific data which belongs to the listener - * process. - */ -static struct client_data client; -static struct server_data server; - - - -/* CODE */ - -int -main(int argc, char **argv) -{ - char *conf_file = "/usr/local/etc/mmspawnd.conf"; - pid_t uid; - gid_t *gids; - int ngids, backlog; - long facility; - cres_t cres; - carg_t *cargp; - carg_t cargs[] = { - {CAT_STR, CAF_NONE, 0, 255, "CHROOT_DIR", CONF.CHROOT_DIR}, - {CAT_STR, CAF_NONE, 1, 255, "LOCKS_PATH", CONF.LOCKS_PATH}, - {CAT_STR, CAF_NONE, 1, 31, "LOCKS_USER", CONF.LOCKS_USER}, - {CAT_STR, CAF_NONE, 1, 31, "LOCKS_GROUP", CONF.LOCKS_GROUP}, - {CAT_STR, CAF_NONE, 1, 3, "LOCKS_MODE", CONF.LOCKS_MODE}, - {CAT_STR, CAF_NONE, 1, 255, "PID_PATH", CONF.PID_PATH}, - {CAT_STR, CAF_NONE, 1, 31, "USER", CONF.USER}, - {CAT_STR, CAF_NONE, 1, 255, "GROUPS", CONF.GROUPS}, - {CAT_STR, CAF_NONE, 1, 31, "LOG_FACILITY", CONF.LOG_FACILITY}, - {CAT_STR, CAF_NONE, 1, 1023, "LISTEN_ADDRESSES", - CONF.LISTEN_ADDRESSES}, - {CAT_STR, CAF_NONE, 1, 255, "COMMAND", CONF.COMMAND}, - {CAT_STR, CAF_NONE, 0, 255, "ALOCK_PATH", CONF.ALOCK_PATH}, - {CAT_STR, CAF_NONE, 1, 31, "ALOCK_USER", CONF.ALOCK_USER}, - {CAT_STR, CAF_NONE, 1, 31, "ALOCK_GROUP", CONF.ALOCK_GROUP}, - {CAT_STR, CAF_NONE, 1, 3, "ALOCK_MODE", CONF.ALOCK_MODE}, - {CAT_STR, CAF_NONE, 0, 63, "PROCTITLE", CONF.PROCTITLE}, - {CAT_STR, CAF_NONE, 0, 255, "STDERR_FILE", CONF.STDERR_FILE}, - {CAT_VAL, CAF_NONE, 0, 64, "MAX_ARGUMENTS", &CONF.MAX_ARGUMENTS}, - {CAT_VAL, CAF_NONE, 1, 65535, "LISTEN_PORT", &CONF.LISTEN_PORT}, - {CAT_VAL, CAF_NONE, 1, 99999, "MAX_CONNECTIONS", - &CONF.MAX_CONNECTIONS}, - {CAT_VAL, CAF_NONE, 1, 99999, "MAX_ADDRESSES", &CONF.MAX_ADDRESSES}, - {CAT_VAL, CAF_NONE, 1, 99999, "MAX_PER_ADDRESS", - &CONF.MAX_PER_ADDRESS}, - {CAT_VAL, CAF_NONE, 0, 99999, "CONNECTION_RATE", - &CONF.CONNECTION_RATE}, - {CAT_VAL, CAF_NONE, 0, 99999, "CONNECTION_PERIOD", - &CONF.CONNECTION_PERIOD}, - {CAT_VAL, CAF_NONE, 1, 99999, "COMMAND_TIMEOUT", - &CONF.COMMAND_TIMEOUT}, - {CAT_VAL, CAF_NONE, 0, 0, "RLIMIT_CORE", &CONF.LIMIT_CORE}, - {CAT_VAL, CAF_NONE, 0, 0, "RLIMIT_CPU", &CONF.LIMIT_CPU}, - {CAT_VAL, CAF_NONE, 0, 0, "RLIMIT_DATA", &CONF.LIMIT_DATA}, - {CAT_VAL, CAF_NONE, 0, 0, "RLIMIT_FSIZE", &CONF.LIMIT_FSIZE}, - {CAT_VAL, CAF_NONE, 0, 0, "RLIMIT_MEMLOCK", &CONF.LIMIT_MEMLOCK}, - {CAT_VAL, CAF_NONE, 0, 0, "RLIMIT_NOFILE", &CONF.LIMIT_NOFILE}, - {CAT_VAL, CAF_NONE, 0, 0, "RLIMIT_NPROC", &CONF.LIMIT_NPROC}, - {CAT_VAL, CAF_NONE, 0, 0, "RLIMIT_RSS", &CONF.LIMIT_RSS}, - {CAT_VAL, CAF_NONE, 0, 0, "RLIMIT_STACK", &CONF.LIMIT_STACK}, - {CAT_BOOL, CAF_NONE, 0, 0, "RESOLVE_ADDRESSES", - &CONF.RESOLVE_ADDRESSES}, - {CAT_BOOL, CAF_NONE, 0, 0, "KILL_CHILDREN", &CONF.KILL_CHILDREN}, - {CAT_BOOL, CAF_NONE, 0, 0, "RLIMITS", &CONF.RLIMITS}, - {CAT_END, CAF_NONE, 0, 0, NULL, NULL} - }; - cmap_t cmap[] = { - {"LOG_AUTH", LOG_AUTH}, - {"LOG_AUTHPRIV", LOG_AUTHPRIV}, - {"LOG_CRON", LOG_CRON}, - {"LOG_DAEMON", LOG_DAEMON}, - {"LOG_FTP", LOG_FTP}, - {"LOG_LPR", LOG_LPR}, - {"LOG_MAIL", LOG_MAIL}, - {"LOG_NEWS", LOG_NEWS}, - {"LOG_SYSLOG", LOG_SYSLOG}, - {"LOG_USER", LOG_USER}, - {"LOG_UUCP", LOG_UUCP}, - {NULL, 0} - }; - int i, nifaces, errfd; - struct sockaddr_in server_addr; - struct iface *ifaces, *tif; - struct pollfd *fds; - - /* Set harmless defaults */ - *CONF.CHROOT_DIR = '\0'; - (void) mm_strcpy(CONF.LOCKS_PATH, "/var/run/mmspawnd.lock"); - (void) mm_strcpy(CONF.LOCKS_USER, "mmspawnd"); - (void) mm_strcpy(CONF.LOCKS_GROUP, "mmspawnd"); - (void) mm_strcpy(CONF.LOCKS_MODE, "400"); - (void) mm_strcpy(CONF.PID_PATH, "/var/run/mmspawnd.pid"); - (void) mm_strcpy(CONF.USER, "mmspawnd"); - (void) mm_strcpy(CONF.GROUPS, "mmspawnd"); - (void) mm_strcpy(CONF.LOG_FACILITY, "LOG_AUTHPRIV"); - (void) mm_strcpy(CONF.LISTEN_ADDRESSES, "127.0.0.1"); - (void) mm_strcpy(CONF.COMMAND, "/sbin/nologin"); - *CONF.ALOCK_PATH = '\0'; - (void) mm_strcpy(CONF.ALOCK_USER, "mmspawnd"); - (void) mm_strcpy(CONF.ALOCK_GROUP, "mmspawnd"); - (void) mm_strcpy(CONF.ALOCK_MODE, "400"); - *CONF.PROCTITLE = '\0'; - *CONF.STDERR_FILE = '\0'; - CONF.LISTEN_PORT = 2323; - CONF.MAX_CONNECTIONS = 32; - CONF.MAX_ADDRESSES = 32; - CONF.MAX_PER_ADDRESS = 1; - CONF.CONNECTION_RATE = 5; - CONF.CONNECTION_PERIOD = 30; - CONF.MAX_ARGUMENTS = 16; - CONF.COMMAND_TIMEOUT = 300; - CONF.LIMIT_CORE = -1; - CONF.LIMIT_CPU = -1; - CONF.LIMIT_DATA = -1; - CONF.LIMIT_FSIZE = -1; - CONF.LIMIT_MEMLOCK = -1; - CONF.LIMIT_NOFILE = -1; - CONF.LIMIT_NPROC = -1; - CONF.LIMIT_RSS = -1; - CONF.LIMIT_STACK = -1; - CONF.RESOLVE_ADDRESSES = FALSE; - CONF.KILL_CHILDREN = TRUE; - CONF.RLIMITS = FALSE; - - /* Read config file */ - if (argc == 2) - conf_file = argv[1]; - if (!mmreadcfg(&cres, cargs, conf_file)) { - /* Error parsing configuration file, report which */ - (void) fprintf(stderr, "\nError parsing '%s'\n", conf_file); - (void) fprintf(stderr, "Error : %s\n", mmreadcfg_strerr(cres.CR_Err)); - if (*(cres.CR_Data)) - (void) fprintf(stderr, "Data : %s\n", cres.CR_Data); - if ((cargp = cres.CR_Keyword) != NULL) { - (void) fprintf(stderr, "Keyword: %s\n", cargp->CA_Keyword); - (void) fprintf(stderr, "Minimum: %ld\n", cargp->CA_Min); - (void) fprintf(stderr, "Maximum: %ld\n", cargp->CA_Max); - } - if (cres.CR_Line != -1) - (void) fprintf(stderr, "Line : %d\n", cres.CR_Line); - (void) fprintf(stderr, "\n"); - exit(EXIT_FAILURE); - } - - /* Post parsing */ - if (!mmmapstring(cmap, CONF.LOG_FACILITY, &facility)) { - (void) fprintf(stderr, "\nUnknown syslog facility %s\n\n", - CONF.LOG_FACILITY); - exit(EXIT_FAILURE); - } - if ((uid = mmgetuid(CONF.USER)) == -1) { - (void) fprintf(stderr, "\nUnknown USER '%s'\n\n", CONF.USER); - exit(EXIT_FAILURE); - } - if (!(gids = mmgetgidarray(&ngids, CONF.GROUPS))) { - (void) fprintf(stderr, "\nOne of following GROUPS unknown: '%s'\n\n", - CONF.GROUPS); - exit(EXIT_FAILURE); - } - /* Initialize alock */ - if (*CONF.ALOCK_PATH != '\0') { - uid_t u; - gid_t g; - mode_t m; - - if ((u = mmgetuid(CONF.ALOCK_USER)) == -1) { - (void) fprintf(stderr, "\nUnknown ALOCK_USER '%s'\n\n", - CONF.ALOCK_USER); - exit(EXIT_FAILURE); - } - if ((g = mmgetgid(CONF.ALOCK_GROUP)) == -1) { - (void) fprintf(stderr, "\nUnknown ALOCK_GROUP '%s'\n\n", - CONF.ALOCK_GROUP); - exit(EXIT_FAILURE); - } - (void) sscanf(CONF.ALOCK_MODE, "%o", &m); - if (m == 0 || m > 0770) { - (void) fprintf(stderr, "\nIllegal ALOCK_MODE '%03o'\n\n", m); - exit(EXIT_FAILURE); - } - if ((server.alock = open(CONF.ALOCK_PATH, O_WRONLY | O_CREAT, m)) - == -1) { - (void) fprintf(stderr, "\n* main() - open(%s) - (%s)\n\n", - CONF.ALOCK_PATH, strerror(errno)); - exit(EXIT_FAILURE); - } - if (fchown(server.alock, u, g) == -1) { - (void) fprintf(stderr, "\n* main() - fchown(%s) - (%s)\n\n", - CONF.ALOCK_PATH, strerror(errno)); - exit(EXIT_FAILURE); - } - } else - server.alock = -1; - /* Initialize shlocks */ - if (CONF.SHLOCKS_PATH != '\0') { /* Actually should always be true */ - uid_t u; - gid_t g; - mode_t m; - - if ((u = mmgetuid(CONF.LOCKS_USER)) == -1) { - (void) fprintf(stderr, "\nUnknown LOCKS_USER '%s'\n\n", - CONF.ALOCK_USER); - exit(EXIT_FAILURE); - } - if ((g = mmgetgid(CONF.LOCKS_GROUP)) == -1) { - (void) fprintf(stderr, "\nUnknown LOCKS_GROUP '%s'\n\n", - CONF.ALOCK_GROUP); - exit(EXIT_FAILURE); - } - (void) sscanf(CONF.LOCKS_MODE, "%o", &m); - if (m == 0 || m > 0770) { - (void) fprintf(stderr, "\nIllegal LOCKS_MODE '%03o'\n\n", m); - exit(EXIT_FAILURE); - } - /* XXX */ - } - if ((ifaces = ifaces_parse(CONF.LISTEN_ADDRESSES)) == NULL) { - (void) fprintf(stderr, "\nInvalid LISTEN_ADDRESSES\n\n"); - exit(EXIT_FAILURE); - } - if ((server.line = _mm_strdup(CONF.COMMAND)) != NULL && (server.argv = - malloc(sizeof(char *) * (CONF.MAX_ARGUMENTS + 2))) != NULL) { - int argc; - - if (!mm_cmdparse(&server.command, &argc, server.argv, server.line, - CONF.MAX_ARGUMENTS + 2)) { - (void) fprintf(stderr, "\nError parsing COMMAND (also check \ -MAX_ARGUMENTS)\n\n"); - exit(EXIT_FAILURE); - } - } else { - (void) fprintf(stderr, "\nOut of memory\n\n"); - exit(EXIT_FAILURE); - } - - /* Config file seemed right, start with initialization */ - { - char buf[128]; - - (void) snprintf(buf, 127, "%s%c%s", DAEMON_NAME, - (*CONF.PROCTITLE != '\0' ? ':' : ' '), CONF.PROCTITLE); - openlog(buf, LOG_PID | LOG_NDELAY, facility); - } - - /* Some consistency checks according to permissions */ -#ifndef NODROPPRIVS - if (getuid() != 0) { - (void) fprintf(stderr, - "\nOnly the super user may start this daemon\n\n"); - syslog(LOG_NOTICE, - "main() - Refused to launch for uid %d attempt (not root, \ -!NODROPPRIVS)", (int)getuid()); - exit(EXIT_FAILURE); - } -#else /* NODROPPRIVS */ - if (getuid() == 0) { - (void) fprintf(stderr, "\nCompiled with NODROPPRIVS, ", - "refusing to run as uid 0\n\n"); - syslog(LOG_NOTICE, "main() - NODROPPRIVS, refusing to run as uid 0"); - exit(EXIT_FAILURE); - } -#endif /* !NODROPPRIVS */ - - /* Make sure that only one instance is running */ - if ((server.runlock = lock_check(CONF.LOCK_PATH)) == -1) { - (void) fprintf(stderr, "\n%s already running\n\n", DAEMON_NAME); - syslog(LOG_NOTICE, "main() - %s already running", DAEMON_NAME); - exit(EXIT_FAILURE); - } - - /* Things we want to do now in case we chroot(2) */ - /* XXX If we want to use mmstat(3)/mmstatd(8) we should initialize the - * system here. However, this is a problem with mmstat(3) and chroot - * setups, unless an mmstatd(8) daemon runs into the chroot itself. - * I should fix mmstatd(8) to support chroot(2). If NetBSD nullfs mounts - * allowed connections to sockets, this problem would be fixed... - * What we could do for now is to not support mmstat(3), simply. - */ - - /* Create our locks for shared memory synchronization. These lock files - * (LOCK_PATH) should also be available under the new root since they - * need to be reopened by our children. - * XXX Should be done after chroot(2), with proper permissions so that - * we may reopen the files in children processes, Moreover, the - * lock_check() lock path should be independent (since outside the chroot)? - */ - if (!shlocks_init(CONF.LOCK_PATH, locks, SHLOCK_MAX, TRUE)) { - syslog(LOG_NOTICE, "main() - shlocks_init()"); - exit(EXIT_FAILURE); - } - - /* If CONF.STDERR_FILE was specified, attempt to append to the file if - * it exists, or to create it otherwise. - */ - if (*CONF.STDERR_FILE != '\0') { - if ((errfd = open(CONF.STDERR_FILE, O_WRONLY | O_APPEND)) == -1) { - if ((errfd = open(CONF.STDERR_FILE, O_CREAT | O_WRONLY, 600)) - == -1) { - syslog(LOG_NOTICE, "main() - open(%s)", CONF.STDERR_FILE); - exit(EXIT_FAILURE); - } - } - } else - errfd = -1; - - /* Close unnecessary filedescriptors and become a daemon, chroot(2)ing - * if necessary. This also sets up the connection listener signal handlers. - */ - if (!daemon_detach(CONF.PID_PATH, CONF.CHROOT_DIR)) { - syslog(LOG_NOTICE, "main() - daemon_detach()"); - exit(EXIT_FAILURE); - } - /* Reopen lock files as needed by flock(2) */ - if (!shlocks_init(CONF.LOCK_PATH, locks, SHLOCK_MAX, FALSE)) { - syslog(LOG_NOTICE, "main() - shlocks_init(FALSE)"); - exit(EXIT_FAILURE); - } - - /* stderr is already redirected to "/dev/null". However, we want to - * redirect it to CONF.STDERR_FILE if it was specified. - */ - if (errfd != -1) { - (void) dup2(errfd, 1); - /* errfd always >2 */ - (void) close(errfd); - } - - /* Reset number of current connections to 0 and initialize the pid_t table - * and pool. - */ - server.current_connections = 0; - if (!pool_init(&server.pid_pool, "pid_pool", malloc, free, NULL, NULL, - sizeof(struct pid_node), CONF.MAX_CONNECTIONS, 1, 1)) { - syslog(LOG_NOTICE, "main() - pool_init(pid_pool)"); - exit(EXIT_FAILURE); - } - if (!hashtable_init(&server.pid_table, "pid_table", CONF.MAX_CONNECTIONS, - 1, malloc, free, key_pidcmp, key_pidhash, FALSE)) { - syslog(LOG_NOTICE, "main() - hashtable_init(pid_table)"); - exit(EXIT_FAILURE); - } - - /* Allocate some shared memory */ - if ((shmem = shmem_malloc(sizeof(struct shmem_data))) == NULL) { - syslog(LOG_NOTICE, "main() - shmem_malloc()"); - exit(EXIT_FAILURE); - } - - /* Initialize shared memory pool and table. Note that we make sure to - * allocate the memory in a single block, large enough, so that - * shmem_free() not be called by another process, which obviously would - * only munmap(2) within it's own process. - */ - if (!pool_init(&shmem->ipaddr_pool, "ipaddr_pool", - shmem_malloc, shmem_free, NULL, NULL, - sizeof(struct ipaddr_node), CONF.MAX_ADDRESSES, 1, 1)) { - syslog(LOG_NOTICE, "main() - pool_init(ipaddr_pool)"); - exit(EXIT_FAILURE); - } - if (!hashtable_init(&shmem->ipaddr_table, "ipaddr_table", - CONF.MAX_ADDRESSES, 1, shmem_malloc, shmem_free, - key_cmp32, key_hash32, FALSE)) { - syslog(LOG_NOTICE, "main() - hashtable_init(ipaddr_table)"); - exit(EXIT_FAILURE); - } - - /* Initialize our command timers context */ - if (!timer_ctx_init(&server.timers, malloc, free, time, alarm)) { - syslog(LOG_NOTICE, "main() - timer_ctx_init()"); - exit(EXIT_FAILURE); - } - - /* Start our cache entry expiration process. It would have been possible - * to use setitimer(2) and do this into the signal handler of the main - * process, alternatively. However, since we already have the interprocess - * locking glue setup, we can keep the design simple and use a process. - * Further, it allows to have the resolver fill the cache from the child - * processes asynchroneously, without causing the main listener to block. - * Note that the following function also causes the child to become - * unprivileged. - */ - if (launch_ipaddr_expire_process(uid, gids, ngids, CONF.CHROOT_PATH) < 1) { - syslog(LOG_NOTICE, "main() - launch_ipaddr_expire_process()"); - exit(EXIT_FAILURE); - } - - /* Bind our port to the various interfaces */ - for (nifaces = 0, tif = ifaces; *(tif->address); tif++) { - if ((tif->sock = socket(AF_INET, SOCK_STREAM, 0)) > -1) { - struct linger linger; - - /* Note: These options are inherited after accept(2) */ - i = 1; - if ((setsockopt(tif->sock, SOL_SOCKET, SO_REUSEADDR, &i, - sizeof(int))) == -1) - syslog(LOG_NOTICE, - "main() - setsockopt(SO_REUSEADDR %s:%d)", - tif->address, (int)CONF.LISTEN_PORT); - i = 1; - if ((setsockopt(tif->sock, SOL_SOCKET, SO_KEEPALIVE, &i, - sizeof(int))) == -1) - syslog(LOG_NOTICE, - "main() - setsockopt(SO_KEEPALIVE)"); - linger.l_onoff = 0; - linger.l_linger = 0; - if ((setsockopt(tif->sock, SOL_SOCKET, SO_LINGER, &linger, - sizeof(struct linger))) != -1) - syslog(LOG_NOTICE, - "main() - setsockopt(SO_LINGER)"); - i = 0; - mm_memclr(&server_addr, sizeof(struct sockaddr_in)); - server_addr.sin_family = AF_INET; - server_addr.sin_addr = tif->saddr.sin_addr; - server_addr.sin_port = htons(CONF.LISTEN_PORT); - if ((bind(tif->sock, (struct sockaddr *)(void *)&server_addr, - sizeof(struct sockaddr_in))) == 0) - nifaces++; - else { - syslog(LOG_NOTICE, - "main() - bind(%s:%d)", - tif->address, (int)CONF.LISTEN_PORT); - (void) close(tif->sock); - tif->sock = -1; - } - } - } - if (nifaces == 0) { - syslog(LOG_NOTICE, "No interfaces (left) to listen to"); - (void) kill(0, SIGTERM); - exit(EXIT_FAILURE); - } - - /* Drop privileges definitively */ - if (!mmdropprivs(uid, gids, ngids)) { - syslog(LOG_NOTICE, "Cannot change uid and gids to safe privs"); - exit(EXIT_FAILURE); - } - -#ifndef __GLIBC__ - setproctitle("%s Socket listener process", CONF.PROCTITLE); -#endif /* __GLIBC__ */ - - /* Start listening to bound sockets */ - backlog = CONF.MAX_ADDRESSES * CONF.MAX_PER_ADDRESS; - if (backlog > CONF.MAX_CONNECTIONS) - backlog = CONF.MAX_CONNECTIONS; - for (tif = ifaces; *(tif->address); tif++) { - if (tif->sock != -1) { - if ((listen(tif->sock, backlog)) == -1) { - syslog(LOG_NOTICE, "* main() - listen(%s)", tif->address); - (void) close(tif->sock); - tif->sock = -1; - nifaces--; - } else - syslog(LOG_NOTICE, "Listening on [%s:%d]", - tif->address, (int)CONF.LISTEN_PORT); - } - } - - /* Setup our poll() array for the interfaces we'll accept() on */ - if ((fds = malloc(sizeof(struct pollfd) * nifaces)) != NULL) { - for (i = 0, tif = ifaces; *(tif->address) != '\0'; tif++) { - if (tif->sock != -1) { - fds[i].fd = tif->sock; - fds[i].events = POLLIN; - fds[i].revents = 0; - i++; - } - } - } else { - syslog(LOG_NOTICE, "Out of memory"); - exit(EXIT_FAILURE); - } - - /* Listen for connections and dispatch them to independant processes */ - syslog(LOG_NOTICE, "Launched for uid %d with configuration file '%s', \ -listening for connections on port %d. Service is '%s'.", - uid, conf_file, (int)CONF.LISTEN_PORT, CONF.COMMAND); - for (;;) { - int i, sock, nifaces2; - socklen_t addrl = sizeof(struct sockaddr); - struct sockaddr addr; - struct ipaddr_node *ipn = NULL; - char ipaddr[20]; - - while ((nifaces2 = poll(fds, nifaces, -1)) == -1 && errno == EINTR) ; - for (i = 0; i < nifaces && nifaces2 > 0; i++) { - if (fds[i].revents & POLLIN) { - nifaces2--; - if ((sock = accept(fds[i].fd, &addr, &addrl)) != -1) { - bool connection; - - /* We got a connection, verify if within allowed limits. */ - mm_strcpy(ipaddr, "0.0.0.0"); - (void) inet_ntop(AF_INET, - &(((struct sockaddr_in *)&addr)->sin_addr), - ipaddr, 19); - if ((connection = connection_open(ipaddr)) && - (ipn = ipaddr_open(&addr, ipaddr)) != NULL) { - pid_t pid; - - /* Attempt to launch a new process */ - if ((pid = fork()) == -1) { - /* fork(2) failure! Be graceful. Fortunately, - * this is extremely unlikely to occur unless - * the configuration allows unfair limits. - */ - DEBUG_PRINTF("main", "fork()"); - ipaddr_close(ipn); - connection_close(); - (void) close(sock); - } else if (pid > 0) { - struct pid_node *pnod; - - /* Parent, close client socket, record child - * process, and continue. As we use a preallocated - * pool of enough nodes for the allowed limits, - * no need for error checking at pool_alloc(). - * If timer_init() fails, 0 is set as timerid_t, - * which is also safe. - */ - (void) close(sock); - pnod = (struct pid_node *)pool_alloc( - &server.pid_pool, FALSE); - pnod->pid = pid; - /* Note: We use an auto-restarting timer because - * otherwise the SIGALRM handler can remove it - * while the SIGCHLD handler attempts to remove it, - * after the SIGCHLD handler verified if it existed - * generating a debug warning otherwise. - */ - pnod->tid = timer_init(&server.timers, time(NULL), - (u_int32_t)CONF.COMMAND_TIMEOUT, - client_timeout, pnod, TRUE); - pnod->ipn = ipn; - (void) hashtable_link(&server.pid_table, - (hashnode_t *)pnod, &pnod->pid, - sizeof(pid_t), FALSE); - } else { - /* Child successfully launched */ - struct sigaction act; - -#ifndef __GLIBC__ - setproctitle("%s Client server process", - CONF.PROCTITLE); -#endif /* __GLIBC__ */ - - /* We create our own process group so that the - * process after execve(2) could not kill(0) - * causing the listener process to exit. - * It also allows us to optionally not interrupt - * currently served client when we restart. - */ - (void) setsid(); - - /* Change signal handling policy. If we do not - * restore the signals we ignore to the default - * action, they will be ignored by the process - * after execve(2). Signals we are handling - * will automatically be restored to the default - * action. - */ - act.sa_handler = SIG_DFL; - act.sa_flags = SA_NOCLDSTOP; - (void) sigemptyset(&act.sa_mask); - - (void) sigaction(SIGTTOU, &act, NULL); - (void) sigaction(SIGTTIN, &act, NULL); - (void) sigaction(SIGTSTP, &act, NULL); - (void) sigaction(SIGPIPE, &act, NULL); - - /* Close bound sockets which this process shouldn't - * have access to. XXX We could use fcntl(3) to - * change socket inheritance at execve(2)... - */ - for (tif = ifaces; *(tif->address); tif++) { - if (tif->sock != -1) { - (void) close(tif->sock); - tif->sock = -1; - } - } - - /* stderr is already redirected to "/dev/null", - * or to our debugging CONF.STDERR_FILE file. - * Make sure that stdin and stdout are redirected - * to the client socket, and to close the - * extraneous filedescriptor. - */ - (void) dup2(sock, 0); - (void) dup2(sock, 1); - /* sock always >2 */ - (void) close(sock); - - /* Let child know and remember these */ - client.ipn = ipn; - client.saddr = addr; - client.ipaddr = (const char *)ipaddr; - - /* Reopen lock files as needed for flock(2). - * We don't need to reopen alock since only the - * parent ever locks it. - */ - (void) shlocks_init(CONF.LOCK_PATH, locks, - SHLOCK_MAX, FALSE); - /* Resolve if CONF.RESOLVE_ADDRESSES */ - client_resolve(); - - /* We do not need to hold the lock files any - * longer, and in fact we do not want to let - * the process access them, especially if the - * administrator sets up the service to not kill - * currently running children if the service is - * shut down. - */ - (void) shlocks_destroy(CONF.LOCK_PATH, locks, - (enum shlocks)SHLOCK_MAX, FALSE); - (void) close(server.runlock); - (void) close(server.alock); - - /* If enabled, attempt to set setrlimit(2) */ - if (CONF.RLIMITS) { - SETRLIMIT("RLIMIT_CORE", RLIMIT_CORE, - CONF.LIMIT_CORE); - SETRLIMIT("RLIMIT_CPU", RLIMIT_CPU, - CONF.LIMIT_CPU); - SETRLIMIT("RLIMIT_DATA", RLIMIT_DATA, - CONF.LIMIT_DATA); - SETRLIMIT("RLIMIT_FSIZE", RLIMIT_FSIZE, - CONF.LIMIT_FSIZE); - SETRLIMIT("RLIMIT_MEMLOCK", RLIMIT_MEMLOCK, - CONF.LIMIT_MEMLOCK); - SETRLIMIT("RLIMIT_NOFILE", RLIMIT_NOFILE, - CONF.LIMIT_NOFILE); - SETRLIMIT("RLIMIT_NPROC", RLIMIT_NPROC, - CONF.LIMIT_NPROC); - SETRLIMIT("RLIMIT_RSS", RLIMIT_RSS, - CONF.LIMIT_RSS); - SETRLIMIT("RLIMIT_STACK", RLIMIT_STACK, - CONF.LIMIT_STACK); - } - - /* We can finally attempt to launch our external - * executable. - */ - exec_command(server.command, - (char * const *)server.argv); - /* NOTREACHED */ - } - } else { - /* Limits have been exceeded, close socket immediately - */ - (void) close(sock); - if (connection) - connection_close(); - if (ipn != NULL) - ipaddr_close(ipn); - } - } else - DEBUG_PRINTF("main", "accept()"); - } - } - } - - /* NOTREACHED */ - exit(EXIT_SUCCESS); -} - - -/* Uses fork(2) and setsid(2) to become a daemon, and detaches from the - * current tty. Also calls chroot(2) and chdir(2) if a jail directory is - * supplied. Returns either successful or not (TRUE, FALSE). - */ -static bool -daemon_detach(const char *pidfile, const char *jail) -{ - int pid; - -#ifdef NODETACH - pid = getpid(); -#else /* NODETACH */ - if ((pid = fork()) != -1) -#endif /* !NODETACH */ - { - -#ifndef NODETACH - if (pid > 0) { - /* Parent */ - exit(EXIT_SUCCESS); - } else -#endif /* !NODETACH */ - { - int fd; - struct sigaction act; - - /* Child */ - (void) setsid(); - (void) chdir("/"); - if ((fd = open("/dev/null", O_RDWR)) != -1) { - (void) dup2(fd, 0); - (void) dup2(fd, 1); - (void) dup2(fd, 2); - if (fd > 2) - (void) close(fd); - } - pidfile_write(pidfile); - - /* Chroot if required */ - if (jail != NULL && *jail != '\0') { - if ((chroot(jail)) == -1) { - syslog(LOG_NOTICE, - "daemon_detach() - Could not chroot(2) to '%s'", - jail); - exit(EXIT_FAILURE); - } - (void) chdir("/"); - } - - /* Setup our server signal handlers. We also want to make - * sure that we catch SIGCHLD for every process that exits. - * (we don't need SIGCHLD events for process STOP however). - */ - act.sa_handler = daemon_sighandler; - act.sa_flags = SA_NOCLDSTOP; - (void) sigemptyset(&act.sa_mask); - - (void) sigaction(SIGTERM, &act, NULL); - (void) sigaction(SIGINT, &act, NULL); - (void) sigaction(SIGCHLD, &act, NULL); - (void) sigaction(SIGSEGV, &act, NULL); - (void) sigaction(SIGALRM, &act, NULL); - - act.sa_handler = SIG_IGN; - (void) sigaction(SIGTTOU, &act, NULL); - (void) sigaction(SIGTTIN, &act, NULL); - (void) sigaction(SIGTSTP, &act, NULL); - (void) sigaction(SIGPIPE, &act, NULL); - - (void) umask(0); - - return TRUE; - } - } - - return FALSE; -} - - -/* This signal handler takes care of wanted signals in the TCP listener - * server (parent process). - */ -static void -daemon_sighandler(int sig) -{ - - switch (sig) { - case SIGSEGV: - syslog(LOG_NOTICE, "Exiting (SIGSEGV!)"); - goto end; - break; - case SIGTERM: - syslog(LOG_NOTICE, "Exiting (SIGTERM)"); - goto end; - break; - case SIGINT: - syslog(LOG_NOTICE, "Exiting (SIGINT)"); - goto end; - break; - case SIGALRM: - /* The maximum command timeout occurred for at least one of our - * children processes. We then take care to kill it. Since we do - * not handle network inactivity timeout between the client and - * the command's process yet, the maximum timeout should be set at - * a reasonable value. It currently consists of the only way to - * kill hanging connections which did not generate any network error - * or disconnection event. - */ - timer_ctx_execute(&server.timers, time(NULL)); - break; - case SIGCHLD: - { - /* At least one children process exited. We evaluate the reason - * and free up resources held for it. - */ - int status = 0; - pid_t pid; - struct pid_node *pn; - - while ((pid = wait3(&status, WNOHANG, NULL)) == -1 && - errno == EINTR) ; - if (pid != -1) { - bool quit = FALSE; - - if (WIFSIGNALED(status)) { - syslog(LOG_NOTICE, "Child %d received signal %d", (int)pid, - WTERMSIG(status)); - quit = TRUE; - } - if (WIFEXITED(status)) { - if (WEXITSTATUS(status) != 0) - syslog(LOG_NOTICE, "Child %d terminated with code %d", - (int)pid, WEXITSTATUS(status)); - else - syslog(LOG_NOTICE, "Child %d terminated normally", - (int)pid); - quit = TRUE; - } - if (quit) { - /* We have the pid of the process which just exited. - * We must decrease the total connections counter as well - * as the connections counter for that address. - * We must do this here since execve(2) does not return - * on success. We use the hashtable_t we setup to link - * pid_t -> struct ipaddr_node *. - */ - if ((pn = (struct pid_node *)hashtable_lookup( - &server.pid_table, &pid, - sizeof(pid_t))) != NULL) { - connection_close(); - ipaddr_close(pn->ipn); - timer_destroy(&server.timers, time(NULL), pn->tid); - hashtable_unlink(&server.pid_table, (hashnode_t *)pn); - (void) pool_free((pnode_t *)pn); - } - } - } - } - break; - } - - return; - -end: - /* Cleanly exit all processes. Because we use setsid(2) in the children, - * We use our table of still active children processes instead of a single - * kill(2) on the whole process group. We do that too afterwards so that - * we cleanup our server-side processes afterwards. - */ - if (CONF.KILL_CHILDREN) - hashtable_iterate(&server.pid_table, client_kill_iterator, NULL); - { - struct sigaction act; - - act.sa_handler = SIG_IGN; - act.sa_flags = SA_NOCLDSTOP; - (void) sigemptyset(&act.sa_mask); - - (void) sigaction(SIGTERM, &act, NULL); - } - (void) kill(0, SIGTERM); - shlocks_destroy(CONF.LOCK_PATH, locks, (enum shlocks)SHLOCK_MAX, TRUE); - shmem_freeall(); - exit(EXIT_SUCCESS); -} - - -/* ARGSUSED */ -static bool -client_kill_iterator(hashnode_t *hnod, void *udata) -{ - - (void) kill(((struct pid_node *)hnod)->pid, SIGTERM); - - return TRUE; -} - - -/* ARGSUSED */ -static void -client_timeout(timerid_t tid, void *udata) -{ - struct pid_node *pnode = (struct pid_node *)udata; - - /* Just send a SIGTERM to the offending process, we'll handle the cleanup - * in SIGCHLD signal handler as usual. - */ - syslog(LOG_NOTICE, "Killing child %d for total timeout (%ld seconds)", - (int)pnode->pid, CONF.COMMAND_TIMEOUT); - (void) kill(pnode->pid, SIGTERM); -} - - -/* Writes our process ID number to specified file. To be called before - * chroot(2) or dropping privileges. - */ -static void -pidfile_write(const char *file) -{ - char str[16]; - int fd; - - if ((fd = open(file, O_CREAT | O_TRUNC | O_WRONLY, 0600)) != -1) { - (void) snprintf(str, 15, "%d\n", getpid()); - (void) write(fd, str, mm_strlen(str)); - (void) close(fd); - } else - DEBUG_PRINTF("pidfile_write", "open(%s)", file); -} - - -/* This uses an fd which remains open in child processes, if they close it the - * lock is released automatically by the process. Returns -1 if the lock - * is already held, or a filedescriptor to the lock on success. - */ -static int -lock_check(const char *file) -{ - int fd; - - if ((fd = open(file, O_CREAT | O_TRUNC | O_WRONLY, 0600)) != -1) { - if ((flock(fd, LOCK_EX | LOCK_NB)) == 0) - return fd; - (void) close(fd); - } else - DEBUG_PRINTF("lock_check", "open(%s)", file); - - return -1; -} - - -/* Simple function to parse the interfaces specified on a line */ -static struct iface * -ifaces_parse(char *addresses) -{ - struct iface *ifaces; - char *ips[65]; - struct sockaddr_in saddr; - int n, i; - - ifaces = NULL; - - if (addresses != NULL && *addresses != '\0') { - if ((n = mm_straspl(ips, addresses, 64)) > 0) { - if ((ifaces = malloc(sizeof(struct iface) * (n + 1))) != NULL) { - /* Setup our array */ - for (i = 0; i < n; i++) { - mm_memclr(&saddr, sizeof(struct sockaddr_in)); - if ((inet_aton(ips[i], &(saddr.sin_addr))) == 1) { - (void) mm_strncpy(ifaces[i].address, ips[i], 15); - ifaces[i].sock = -1; - ifaces[i].saddr = saddr; - *(ifaces[i + 1].address) = '\0'; - } else { - syslog(LOG_NOTICE, - "ifaces_parse() - Invalid address [%s]", - ips[i]); - free(ifaces); - ifaces = NULL; - break; - } - } - } - } - } - - return ifaces; -} - - -/* Internal function to resove the hostname of the IP address of a connected - * client and cache it (if RESOLVE_ADDRESSES was enabled). - */ -static void -client_resolve(void) -{ - - if (CONF.RESOLVE_ADDRESSES) { - if (*client.ipn->hostname == '\0') { - int err; - - while ((err = flock(locks[SHLOCK_IPADDR], LOCK_EX)) == -1 && - errno == EINTR) ; - if (err == 0) { - if (*client.ipn->hostname == '\0') { - if ((getnameinfo(&client.saddr, sizeof(struct sockaddr_in), - client.ipn->hostname, 64, NULL, 0, 0)) - != 0) - DEBUG_PRINTF("client_resolve", "getnameinfo(%s)", - client.ipaddr); - } - (void) flock(locks[SHLOCK_IPADDR], LOCK_UN); - } - } - syslog(LOG_NOTICE, "Connection from %s (%s)", client.ipn->hostname, - client.ipaddr); - } else - syslog(LOG_NOTICE, "Connection from %s", client.ipaddr); -} - - -/* Safely executes the specified command, or exits. - * This function never returns to the caller. - * The command line can be parsed using mmstring(3)'s mm_cmdparse() first. - * Perform sanity checking on the executable we will be launching. - * We want to make sure that it is executable, but read-only, and that - * it has no setuid/setgid bit set. We also ensure that it consists of - * an executable rather than a script requireing an interpreter. - * We also verify that the file is owned by the superuser. - * We perform no globbing whatsoever and do not want to use a shell. - */ -static void -exec_command(const char *path, char * const *argv) -{ - struct stat st; - mode_t mode; - int fd; - unsigned char buf[4]; - char *const env[1] = {NULL}; - - /* First make sure that command exists and is a regular file */ - if (lstat(path, &st) != 0) { - syslog(LOG_NOTICE, "* exec_command() - lstat(%s) - (%s)", path, - strerror(errno)); - exit(EXIT_FAILURE); - } - if (!S_ISREG(st.st_mode)) { - syslog(LOG_NOTICE, "* exec_command() - Not regular file (%s)", path); - exit(EXIT_FAILURE); - } - /* Reject it if it has any setuid/setgid bit set */ - mode = st.st_mode & ~S_IFMT; - if (mode & S_ISUID) { - syslog(LOG_NOTICE, "* exec_command() - setuid file (%s)", path); - exit(EXIT_FAILURE); - } - if (mode & S_ISGID) { - syslog(LOG_NOTICE, "* exec_command() - setgid file (%s)", path); - exit(EXIT_FAILURE); - } - /* Has to be owned by root user for more safety */ - if (st.st_uid != 0) { - syslog(LOG_NOTICE, "* exec_command() - Not owned by uid 0 (%s)", path); - exit(EXIT_FAILURE); - } - /* access(2) is safer than testing the mode bits. Make sure that we cannot - * write to the file, but that we can read and execute it. - */ - if (access(path, W_OK) == 0) { - syslog(LOG_NOTICE, "* exec_command() - File writable (%s)", path); - exit(EXIT_FAILURE); - } - if (access(path, R_OK | X_OK) == -1) { - syslog(LOG_NOTICE, "* exec_command() - Not read/exec (%s)", path); - exit(EXIT_FAILURE); - } - /* Now make sure that it does not consist of a script. We only support - * ELF executables for now for simplicity (static or dynamic ones). - */ - if ((fd = open(path, O_RDONLY)) == -1) { - syslog(LOG_NOTICE, "* exec_command() - open(%s) - (%s)", path, - strerror(errno)); - exit(EXIT_FAILURE); - } - if (read(fd, buf, 4) != 4) { - syslog(LOG_NOTICE, "* exec_command() - read(%s) - (%s)", path, - strerror(errno)); - (void) close(fd); - exit(EXIT_FAILURE); - } - (void) close(fd); - if (buf[1] != 'E' || buf[2] != 'L' || buf[3] != 'F') { - syslog(LOG_NOTICE, "* exec_command() - Not ELF file (%s)", path); - exit(EXIT_FAILURE); - } - - /* Finally attempt execution */ - (void) execve(path, argv, env); - - /* If we reach this point, execve(2) failed, and we must use _exit(2) for - * safety in this case. - */ - _exit(EXIT_FAILURE); -} - - -/* If the maximum number of allowed connections has not been reached, increases - * the counter and returns TRUE. Returns FALSE otherwise, in which case the - * server should not allow more clients. - */ -static bool -connection_open(const char *ipaddr) -{ - - if (server.current_connections == 0 && server.alock != -1) { - /* Attempt to lock alock */ - if ((flock(server.alock, LOCK_EX | LOCK_NB)) == -1) { - syslog(LOG_NOTICE, "Refusing %s (ALOCK_PATH currently locked)", - ipaddr); - return FALSE; - } - } - if (server.current_connections < CONF.MAX_CONNECTIONS) { - server.current_connections++; - return TRUE; - } - syslog(LOG_NOTICE, "Refusing %s (MAX_CONNECTIONS reached = %d)", - ipaddr, server.current_connections); - - return FALSE; -} - - -/* Decreases the current number of total connections counter. */ -static void -connection_close(void) -{ - - if (server.current_connections > 0) - server.current_connections--; - else - DEBUG_PRINTF("connection_close", "server.current_connections < 1 !"); - if (server.current_connections == 0 && server.alock != -1) { - /* Release alock */ - (void) flock(server.alock, LOCK_UN); - } -} - - -/* These use shared memory and provide a nice API for use by the server. */ - -/* If necessary, adds the address to the list of currently active connections. - * If the address already was present, makes sure that it has not reached it's - * maximum number of allowed connections and connection rate limits, and - * increases it's counter. Returns a pointer to the ipaddr_node on success, - * or NULL if the server should reject the connection, in which case the - * reason and address are automatically logged via syslog(3) and mmstat(3). - */ -static struct ipaddr_node * -ipaddr_open(struct sockaddr *saddr, const char *ipaddr) -{ - struct ipaddr_node *ipn = NULL; - bool ok = FALSE; - time_t now = time(NULL); - int err; - - /* Make sure that we respect connection rates and limits for the addresses. - */ - while ((err = flock(locks[SHLOCK_IPADDR], LOCK_EX)) == -1 && - errno == EINTR) ; - if (err == 0) { - struct sockaddr_in *sinaddr = (struct sockaddr_in *)saddr; - - if ((ipn = (struct ipaddr_node *)hashtable_lookup(&shmem->ipaddr_table, - &sinaddr->sin_addr.s_addr, sizeof(u_int32_t))) - == NULL) { - /* Create new entry */ - if (HASHTABLE_NODES(&shmem->ipaddr_table) < CONF.MAX_ADDRESSES) { - if ((ipn = (struct ipaddr_node *)pool_alloc( - &shmem->ipaddr_pool, FALSE)) != NULL) { - /* Rate limiting and total connections initialization */ - LR_INIT(&ipn->lr, CONF.CONNECTION_RATE, - CONF.CONNECTION_PERIOD, now); - ipn->connections = 0; - /* Cache nodes initialization */ - ipn->address = *saddr; - *ipn->hostname = '\0'; - (void) hashtable_link(&shmem->ipaddr_table, - (hashnode_t *)ipn, - &((struct sockaddr_in *)&ipn->address)-> - sin_addr.s_addr, sizeof(u_int32_t), FALSE); - } else - DEBUG_PRINTF("ipaddr_open", "pool_alloc()"); - } else { - syslog(LOG_NOTICE, - "Refusing %s for exceeded number of addresses (%ld)", - ipaddr, CONF.MAX_ADDRESSES); - } - } - if (ipn != NULL) { - /* Either the node was found or successfully created */ - if (ipn->connections < CONF.MAX_PER_ADDRESS) { - if (CONF.CONNECTION_RATE > 0) { - if (lr_allow(&ipn->lr, 1, now, FALSE)) - ok = TRUE; - else { - syslog(LOG_NOTICE, - "Refusing %s for exceeded connection rate \ -(%ld connections in %ld seconds, %ld seconds left to clear)", - ipaddr, LR_POSTS(&ipn->lr), CONF.CONNECTION_PERIOD, - LR_REMAINS(&ipn->lr, now)); - } - } else - ok = TRUE; - } else { - syslog(LOG_NOTICE, "Refusing %s for exceeded number of \ -connections per address (%ld)", ipaddr, CONF.MAX_PER_ADDRESS); - } - } - - if (ok) - ipn->connections++; - else - ipn = NULL; - - (void) flock(locks[SHLOCK_IPADDR], LOCK_UN); - } - - return ipn; -} - - -/* Decreases the current number of connections for the specified address, and - * if necessary, frees the entry from the table if no more connections exist - * for this address. The node is expected to exist and to represent the right - * one obtained from ipaddr_open(). - */ -static void -ipaddr_close(struct ipaddr_node *ipn) -{ - - if (ipn != NULL) { - int err; - - while ((err = flock(locks[SHLOCK_IPADDR], LOCK_EX)) == -1 && - errno == EINTR) ; - if (err == 0) { - ipn->connections--; - /* Leave the cache service process delete the entries, which - * allows us to keep statistics for rate limiting, as well as - * cache hostnames on a per-address basis. - */ - (void) flock(locks[SHLOCK_IPADDR], LOCK_UN); - } - } -} - - -/* A quick hashtable_hash() and memcmp() replacements which already deal with - * unique 32-bit values. - */ - -/* ARGSUSED */ -static u_int32_t -key_hash32(const void *data, size_t len) -{ - - return *((u_int32_t *)data); -} - -/* ARGSUSED */ -static int -key_cmp32(const void *src, const void *dst, size_t len) -{ - - return *((u_int32_t *)src) - *((u_int32_t *)dst); -} - -/* And to make things right, to not assume that pid_t is a u_int32_t, do - * the same. - */ - -/* ARGSUSED */ -static u_int32_t -key_pidhash(const void *data, size_t len) -{ - - return (u_int32_t)*((pid_t *)data); -} - -/* ARGSUSED */ -static int -key_pidcmp(const void *src, const void *dst, size_t len) -{ - - return *((pid_t *)src) - *((pid_t *)dst); -} - - -/* Useful function for use by launch_server_process() and - * launch_ipaddress_expire_process() initialization - */ -void -launch_server_child_init(const char *func, uid_t uid, gid_t *gids, int ngids) -{ - - /* Reopen lock files as needed for flock(2) */ - if (shlocks_reopen(CONF.LOCK_PATH, locks, SHLOCK_MAX) == -1) { - syslog(LOG_NOTICE, "%s - Could not reopen lock files - %s", - func, strerror(errno)); - (void) kill(getppid(), SIGTERM); - exit(EXIT_FAILURE); - } - - /* chroot(2) if necessary */ - if (CONF.CHROOT_PATH != '\0') { - if ((chroot(chroot_path)) == -1) { - syslog(LOG_NOTICE, "%s - Could not chroot(2) to '%s' - %s", - func, CONF.CHROOT_PATH, strerror(errno)); - (void) kill(getppid(), SIGTERM); - exit(EXIT_FAILURE); - } - (void) chdir("/"); - } - - /* Drop privileges */ - if (!mmdropprivs(uid, gids, ngids)) { - syslog(LOG_NOTICE, "%s - Cannot change uid and gids to safe privs", - func); - (void) kill(getppid(), SIGTERM); - (void) exit(EXIT_FAILURE); - } -} - - -/* Launches the main server program, listening for connections and dispatching - * them to children processes. - */ -static pid_t -launch_server_process(uid_t uid, gid_t *gids, int ngids) -{ - /* XXX */ -} - -static void -server_process_main(void) -{ - /* XXX */ -} - - -/* Launches the ipaddress-hostname-rate cache entry expiration process, - * a kind of asynchroneous garbage collector. - */ -static pid_t -launch_ipaddr_expire_process(uid_t uid, gid_t *gids, int ngids) -{ - pid_t pid = -1; - - if ((pid = fork()) == 0) { - struct sigaction act; - - /* Reset default signal action */ - act.sa_handler = SIG_DFL; - act.sa_flags = SA_NOCLDSTOP; - (void) sigemptyset(&act.sa_mask); - - (void) sigaction(SIGTERM, &act, NULL); - (void) sigaction(SIGINT, &act, NULL); - (void) sigaction(SIGCHLD, &act, NULL); - (void) sigaction(SIGSEGV, &act, NULL); - - /* Release runlock which only our parent should hold */ - (void) close(server.runlock); - - /* Will reopen lock files, chroot(2) and drop privileges */ - launch_server_child_init("launch_ipaddr_expire_process()", - uid, gids, ngids); - - /* Finally lanch main process loop */ - ipaddr_expire_process_main(); - /* NOTREACHED */ - } - - /* Parent */ - return pid; -} - -/* This process can run independently and manage the ipaddr cache entry - * expiration events. - */ -static void -ipaddr_expire_process_main(void) -{ - struct ipaddr_expire_process_iterator_udata data; - -#ifndef __GLIBC__ - setproctitle("%s Cache service process", CONF.PROCTITLE); -#endif /* __GLIBC__ */ - - /* Set initial timeout to maximum allowed */ - data.soonest = CONF.CONNECTION_PERIOD; - for (;;) { - int err; - - /* Sleep until it is known that at least one node expired */ - (void) sleep(data.soonest); - /* Tell our iterator function the current time and the maximum - * allowed time to wait to - */ - data.current = time(NULL); - data.soonest = CONF.CONNECTION_PERIOD; - /* Lock table, reset expired nodes, garbage collect, and set - * data.soonest to the delay of the soonest next expireing node. - */ - while ((err = flock(locks[SHLOCK_IPADDR], LOCK_EX)) == -1 && - errno == EINTR) ; - if (err == 0) { - if (HASHTABLE_NODES(&shmem->ipaddr_table) > 0) - hashtable_iterate(&shmem->ipaddr_table, - ipaddr_expire_process_iterator, &data); - (void) flock(locks[SHLOCK_IPADDR], LOCK_UN); - } - } - /* NOTREACHED */ -} - -/* Internally used by the cache events expiration process */ -static bool -ipaddr_expire_process_iterator(hashnode_t *hnod, void *udata) -{ - struct ipaddr_node *node = (struct ipaddr_node *)hnod; - struct ipaddr_expire_process_iterator_udata *data = udata; - time_t rem; - - /* If the node expired, reset it. For nodes which do not, record the - * soonest to expire node's delay. For those which expire and for which - * no connections exist anymore, expunge them. - */ - if ((rem = LR_REMAINS(&node->lr, data->current)) == 0) { - /* This entry expired */ - if (node->connections == 0) { - /* Safe to expunge this entry */ - hashtable_unlink(&shmem->ipaddr_table, (hashnode_t *)node); - (void) pool_free((pnode_t *)node); - } else { - /* Reset it */ - LR_EXPIRE(&node->lr, data->current); - rem = LR_REMAINS(&node->lr, data->current); - } - } - if (rem != 0 && data->soonest > rem) - data->soonest = rem; - - return TRUE; -} diff --git a/mmsoftware/mmspawnd/mmspawnd.conf.5 b/mmsoftware/mmspawnd/mmspawnd.conf.5 deleted file mode 100644 index d2d04d7..0000000 --- a/mmsoftware/mmspawnd/mmspawnd.conf.5 +++ /dev/null @@ -1,361 +0,0 @@ -.\" $Id: mmspawnd.conf.5,v 1.12 2003/11/14 08:04:19 mmondor Exp $ -.\" -.\" Copyright (C) 2003, Matthew Mondor -.\" All rights reserved. -.\" -.\" Redistribution and use in source and binary forms, with or without -.\" modification, are permitted provided that the following conditions -.\" are met: -.\" 1. Redistributions of source code must retain the above copyright -.\" notice, this list of conditions and the following disclaimer. -.\" 2. Redistributions in binary form must reproduce the above copyright -.\" notice, this list of conditions and the following disclaimer in the -.\" documentation and/or other materials provided with the distribution. -.\" 3. All advertising materials mentioning features or use of this software -.\" must display the following acknowledgement: -.\" This product includes software written by Matthew Mondor. -.\" 4. The name of Matthew Mondor may not be used to endorse or promote -.\" products derived from this software without specific prior written -.\" permission. -.\" 5. Redistribution of source code may not be released under the terms of -.\" any GNU Public License derivate. -.\" -.\" THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR -.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES -.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. -.\" IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, -.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -.\" BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF -.\" USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF -.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -.\" -.Dd October 28, 2003 -.Dt MMSPAWND.CONF 5 -.Os mmsoftware -.Sh NAME -.Nm mmspawnd.conf -.Nd -.Xr mmspawnd.conf 5 -configuration file for -.Xr mmspawnd 8 -.Sh DESCRIPTION -The -.Nm /usr/local/etc/mmspawnd.conf -file may contain one or more keyword/value pairs per line, empty lines or -comments. A ';' or '#' character causes all other characters to be considered -as a comment, and to be ignored, until the end of the current line. It is -very important to enclose the value for a keyword in double quotes ('"' -characters) if the following characters are found in it: ';', '#', space and -tab. It is allowed to use an equal sign '=' between the keyword name and it's -argument. Here are documented the various possible keywords and their -allowed values, as well as their defaults. -.Pp -.Ss Service administration parameters -.Pp -.Bl -tag -width indent -offset indent -.It Nm CHROOT_DIR Ar "directory" -If specified and non-empty, causes the daemon to use -.Xr chroot 2 -at initialization so that it becomes jailed into the wanted alternative root -directory. Obviously, all required files for the application should have a copy -within the new root (this may include files such as -.Nm /etc/resolv.conf , -.Nm /etc/hosts , -.Nm /etc/passwd , -.Nm /etc/group , -a few shared libraries, the executable binary to be launched, and so on, -as required by the application and libc. -.It Nm LOCK_PATH Ar "fullpath" -Specifies the location where the internal synchronization (and anti-recursive -runlock) are to be created (before chrooting). Should consist of an absolute -full pathname including a filename, after which will automatically be postfixed -extensions for the various locks. -When several server instances are run concurrently to serve multiple services, -the name of the lock files should be different for each to not conflict. -.It Nm PID_PATH Ar "fullpath" -Tells where to store the file holding the server process ID to be killed with -.Dv SIGTERM -(sig 15) to cause the server to cleanly exit. This is done before chrooting. -When several server instances are run concurrently to serve multiple services, -the name of the PID files should be different for each to not conflict. -.It Nm ALOCK_PATH Ar "fullpath" -If specified and non-empty, this enables a special feature of -.Nm mmspawnd -which allows advisory locking to be used on a special file with third -party applications to synchronize with the service. This obviously should -be configured with care, but is most useful with some setups. The file -will be created before calling -.Xr chroot 2 -and thus anywhere wanted on the filesystem. -.Pp -When this facility is enabled, -.Nm mmspawnd -uses -.Xr flock 2 -to obtain an exclusive advisory lock on that file whenever one or more -connections exist, still being served by the service. This means that -whenever another application is allowed to obtain the lock using the -same manner (it should also use exclusive mode), no clients are connected -anymore to the service, and the other application may be allowed to safely -perform special operations which requires the service to be busy. For as long -as the file remains locked by the third party application, the -.Nm mmspawnd -server will refuse connections from any users (it will in fact accept them -but immediately drop the connection without running the service). As soon -as the other application releases back the lock, normal operation is resumed. -.Pp -.Xr mmanoncvs 8 -for instance uses this feature during the short amount of time required for -two rename(2) operations to update the public cvs pserver read-only repository. -It is very important to make sure that permissions are properly set on the -lock file when using this feature, to only enable the wanted application to -obtain the lock (which requires at least read access to the file). See -the few next options to do this. -.It Nm ALOCK_USER Ar "user" -When the -.Nm ALOCK_PATH -is set, enabling the alock feature, this specifies the user which should be -set to be the owner of the lock file. -.It Nm ALOCK_GROUP Ar "group" -When the alock feature is enabled, tells which group should be set for the lock -file. -.It Nm ALOCK_MODE Ar "mode" -To be used when the alock feature is enabled. Specifies the permission mode -bits to apply to the lock file, so that only the intended application can use -it. -.It Nm USER Ar "user" -At server initialization, it drops privileges from the superuser to the -specified user, definitively. This can be specified as either a username -or it's user ID number. -.It Nm GROUPS Ar "group,..." -When dropping privileges, the process will become part of these groups. -More than one group may be specified, by name or ID, separated by commas, -without spaces. The first group will be set to the real process group, and -others as secondary access ones. -.It Nm LOG_FACILITY Ar "facility" -Syslog facility which should be used for error logging. Should normally be -one of -.Dv LOG_AUTH , LOG_AUTHPRIV , LOG_CRON, LOG_DAEMON , -.Dv LOG_FTP , LOG_KERN , LOG_LPR , LOG_MAIL , -.Dv LOG_NEWS , LOG_SYSLOG , LOG_USER -or -.Dv LOG_UUCP . -See -.Xr syslog 3 -man page for more information. -.It Nm COMMAND Ar "command" -Specifies the command which should be executed for each client which will -be served. There can be as many command line arguments as -.Nm MAX_ARGUMENTS -allows. The path to the executable to launch should be absolute. No globbing -or substitution of any kind is done on the arguments. It is allowed to -delimit arguments containing spaces or tabs into single quotes ('). -.It Nm MAX_ARGUMENTS Ar "number" -This parameter sets the maximum allowed number of command line parameters which -can be passed to the application in -.Sy COMMAND . -.It Nm PROCTITLE Ar "string" -This is useful if multiple services are served using -.Xr mmspawnd 8 -for commands such as -.Xr ps 1 , -notably on BSD systems. Where available, this causes the supplied string -to prefix the comments attached to the processes using -.Xr setproctitle 3 . -It also is appended to the daemon name for -.Xr syslog 3 -at -.Xr openlog 3 -time. -.El -.Ss TCP server administration -.Bl -tag -width indent -offset indent -.It Nm LISTEN_ADDRESSES Ar "address ..." -Tells to which interfaces the server should listen to, separated by spaces. -The arguments should be enclosed in double quotes if more than one address -is supplied. These addresses are used for -.Xr bind 2 . -Specifying "0.0.0.0" causes -.Nm mmspawnd -to listen to all interfaces. -.It Nm LISTEN_PORT Ar "number" -Supplies which TCP port number to listen to. This must be a numeric port number -within the range of 1 to 65535. -.It Nm MAX_CONNECTIONS Ar "number" -The maximum number of simultaneous clients which should be served at once. -.It Nm MAX_ADDRESSES Ar "number" -The maximum number of simultaneous different client IP addresses to serve. -.It Nm MAX_PER_ADDRESS Ar "number" -The maximum number of simultaneous clients to serve at once per IP address. -.It Nm CONNECTION_RATE Ar "number" -The maximum number of connections to accept from each address within -.Nm CONNECTION_PERIOD . -Can be 0 to disable connection rate throttling. -.It Nm CONNECTION_PERIOD Ar "number" -If -.Nm CONNECTION_RATE -is non-zero, specifies the number of seconds during which a maximum of -.Nm CONNECTION_RATE -connections are to be allowed. -.It Nm RESOLVE_ADDRESSES Ar "boolean" -Specifies weither client addresses should be resolved to hostnames when -logging the connection event via -.Xr syslog 3 . -An internal cache is maintained for recently connected addresses for faster -performance. This is done in the child serving process so that it does not -slow down the listener process responsible for accepting new connections. -Should be TRUE or FALSE. -.El -.Ss Application security limits -.Bl -tag -width indent -offset indent -.It Nm COMMAND_TIMEOUT Ar "seconds" -Specifies the maximum number of seconds allowed for complete execution of -.Nm COMMAND . -If the command does not terminate before this number of seconds elapse, it -is forcefully killed using the -.Dv SIGTERM -signal. Beware to put this limit high enough so that it does not interfere -with normal service operation. This feature however allows to get rid of -eternally idle or frozen sessions. -.Pp -Eventually, support for network inactivity timeout, as well as bandwidth -shaping/throttling will be added. However, since those require a new design -and a rewrite of -.Xr mmspawnd 8 , -this is the best we can do for now. (This behavior is still better than -.Xr inetd 8 , -however). We also ensure to set the -.Dv SO_KEEPALIVE -option on the client descriptors to better recognize connection problems and -act accordingly to stop the process. -.It Nm RLIMITS Ar "boolean" -If TRUE, all following -.Nm RLIMIT_* -parameters will be applied to the children processes before launching the -application command if they are not set to -1 values. -.Xr setrlimit 2 -is called to perform this. Note that these should be set to sane values -for proper function, by a competent administrator, if this option is -enabled. When disabled, or if enabled but for each following option specified -with -1, the defaults are used, which are inherited from the server process. -.Bl -tag -width indent -offset indent -.It Nm RLIMIT_CORE Ar "value" -If not -1, the largest size (in bytes) core file that may be created. -.It Nm RLIMIT_CPU Ar "value" -If not -1, The maximum amount of cpu time (in seconds) to be used by -each process. -.It Nm RLIMIT_DATA Ar "value" --1 or The maximum size (in bytes) of the data segment for a process; -this defines how far a program may extend its break with the -.Xr sbrk 2 -or -.Xr mmap 2 -system calls. -.It Nm RLIMIT_FSIZE Ar "value" -The largest size (in bytes) file that may be created, or -1. -.It Nm RLIMIT_MEMLOCK Ar "value" -The maximum size (in bytes) which a process may lock into physical memory -(wire) using the -.Xr mlock 2 -system call function, or -1. -.It Nm RLIMIT_NOFILE Ar "value" -If not -1, the maximum number of open files for this process. -.It Nm RLIMIT_NPROC Ar "value" -The maximum number of simultaneous processes for this user ID, or -1. -.It Nm RLIMIT_RSS Ar "value" -The maximum size (in bytes) to which a process's resident -set size may grow. This imposes a limit on the amount of -physical memory to be given to a process; if memory is -tight, the system will prefer to take memory from processes -that are exceeding their declared resident set size. --1 to use the defaults. -.It Nm RLIMIT_STACK Ar "value" -If not -1, the maximum size (in bytes) of the stack segment for a -process; this defines how far a program's stack segment -may be extended. Stack extension is performed automatically by the system. -.El -.El -.Ss Debugging support -.Bl -tag -width indent -offset indent -.It Nm STDERR_FILE Ar "fullpath" -By default, the standard error stream (stderr) of -.Nm COMMAND -is redirected to the "/dev/null" device. However, it may be nice to be able -to obtain those messages from time to time, or to see if any are generated -by the application. This option, if non-empty, creates, or appends to the -specified file (outside of the chroot setup). This option should not be -used on production systems, as the file will grow without bounds. If a log -rotation system is used, -.Xr mmspawnd 8 -will require to be restarted everytime it is ran. It is merely for debugging. -.El -.Sh DEFAULTS -The following defaults are used: -.Pp -.Bd -literal -offset indent -CHROOT_DIR "" -LOCK_PATH "/var/run/mmspawnd.lock" -PID_PATH "/var/run/mmspawnd.pid" -ALOCK_PATH "" -ALOCK_USER "mmspawnd" -ALOCK_GROUP "mmspawnd" -ALOCK_MODE "400" -USER "mmspawnd" -GROUPS "mmspawnd" -LOG_FACILITY "LOG_AUTHPRIV" -COMMAND "/sbin/nologin" -MAX_ARGUMENTS 16 -PROCTITLE "" - -LISTEN_ADDRESSES "127.0.0.1" -LISTEN_PORT 2323 -MAX_CONNECTIONS 32 -MAX_ADDRESSES 32 -MAX_PER_ADDRESS 1 -CONNECTION_RATE 5 -CONNECTION_PERIOD 30 -RESOLVE_ADDRESSES FALSE - -COMMAND_TIMEOUT 300 -RLIMITS FALSE -RLIMIT_CORE -1 -RLIMIT_CPU -1 -RLIMIT_DATA -1 -RLIMIT_FSIZE -1 -RLIMIT_MEMLOCK -1 -RLIMIT_NOFILE -1 -RLIMIT_NPROC -1 -RLIMIT_RSS -1 -RLIMIT_STACK -1 - -STDERR_FILE "" -.Ed -.Sh AUTHOR -.Nm mmspawnd -was written by Matthew Mondor, and is -Copyright (c) 2003, Matthew Mondor, All rights reserved. -.Sh FILES -.Bl -tag -width indent -offset indent -.It Pa /usr/local/etc/mmspawnd.conf -This file -.It Pa /usr/local/sbin/mmspawnd -The -.Xr mmspawnd 8 -server binary itself. -.El -.Sh SEE ALSO -.Xr mmspawnd 8 , -.Xr syslog 3 , -.Xr openlog 3 , -.Xr chroot 2 , -.Xr bind 2 , -.Xr setrlimit 2 , -.Xr ps 1 , -.Xr setproctitle 3 . -.Sh BUGS -Not really a bug, but tied to each interface could be most connection control -options. -.Pp -Please report any bug to mmondor@accela.net diff --git a/mmsoftware/mmspawnd/new/GNUmakefile b/mmsoftware/mmspawnd/new/GNUmakefile deleted file mode 100644 index 89defbf..0000000 --- a/mmsoftware/mmspawnd/new/GNUmakefile +++ /dev/null @@ -1,17 +0,0 @@ -# $Id: GNUmakefile,v 1.1 2003/11/02 22:16:23 mmondor Exp $ - -all: mmspawnd - -%.o: %.c - cc -c ${CFLAGS} -I../mmlib -o $@ $< - -mmspawnd: mmspawnd.o - cc -o $@ $< -lc ../mmlib/libmmondor.a - -install: all - install -crs -o 0 -g 0 -m 500 mmspawnd /usr/local/sbin - install -cr -o 0 -g 0 -m 444 mmspawnd.conf.5 /usr/local/man/man5 - install -cr -o 0 -g 0 -m 444 mmspawnd.8 /usr/local/man/man8 - -clean: - rm -f mmspawnd mmspawnd.o diff --git a/mmsoftware/mmspawnd/new/README b/mmsoftware/mmspawnd/new/README deleted file mode 100644 index ed8b37d..0000000 --- a/mmsoftware/mmspawnd/new/README +++ /dev/null @@ -1,122 +0,0 @@ -We want to be able to perform the following: - -Parent handles incomming connections, maintaining limits, etc. It also -maintains a pool of processes. It dispatches incomming connections to -those, distributing them among them. It however must be able to know -when a connection ended from those processes, so that it may update -limits, and the hostname/rate per address cache... And it must be able -to start or kill such processes of the pool as required. - -Each of the child processes would need to be able to accept new ones -to dispatch, at which point it should fork(2), execve(2) etc a-la popen. -It must also be able to forward data between each of the active connections -and their particular serving process. For this, it would maintain a poll -array, as well as an index to tie both sockets together for each, and -a buffer for EAGAIN when writing. Each of these processes also should -help the parent master process to account when a connection is dropped. -When forwarding between the filedescriptors for each client, it should -also hold statistics to be able to perform bandwidth shaping. This implies -that it is important to be able to remove wanted filedescriptors from the -select()/poll() set, at least until the end of the current second elapsed, -at which point they have to be re-incorporated into the set. When master -global bandwidth limits are reached, it must be able to only handle new -connections, and disconnection events, but to otherwise ignore all of them -until the current second elapses. - -I need a generic library to add and remove elements from an array. The -array must be able to grow and shrink as necessary, upto the maximum allowed -limit (and thus can be pre-allocated of fixed size). When removing an element, -it has to shrink the virtual size (it can reorder elements as required so that -the last element always be the one to remove). When adding, it would simply -add at the end of the elements, and update the number of current elements -counter. Hmm this probably means that if we want to refer to an element -efficiently, we must refer it by index offset rather than run the list to -locate them. We could probably do those operations while running the list -for occurred events after poll(). - -We should only poll for read events, and then attempt to forward. We then -should account for EAGAIN, writing in non-blocking mode. If this event occurs, -we need to keep the data to forward in the buffer, to remove both -filedescriptors for that client from the poll set, continue with other events, -then go to the next poll round. After a poll round, non-commited buffers would -pass a next flush attempt, which if succeeds, causes the filedescriptors to -be reincorporated into the set. This would be done in both directions. -An index would efficiently match every filedescriptor to their companion one. -This index would only be updated at new connection or connection loss event. - -To make things more efficient, client nodes for which flushing is required -would be inserted into a temporary linked list, which would be processed -after poll return. Those which can successfully be flushed will be unlinked -from that list. Of course, on another error than EAGAIN, the client would -be discarded. - -struct client_node { - pnode_t node; - pid_t pid; /* -1 if SIGCHLD was received already */ - int connection_id; /* For master process to link? */ - int pfd, cfd; - size_t bytes; /* Number of bytes in buffer */ - unsigned char buf[4096]; -}; - -Instead of connecting the int/fd based index to another int/fd directly, we -could redirect it to the client_node pointer also. We would store an array -of the following structure, and index it with [] for access: - -struct fd_index { - int fd; /* Corresponding fd */ - int pollidx; /* Index in poll set */ - struct client_node *cnode; /* And node */ -}; - -Note that the filedescriptor used for communication with the master parent -connection dispatcher would be treated especially, would be the first in the -poll set, and would never be removed from it. When global bandwidth limits -occur, only that fd would be polled (using 1 as nfds_t argument to poll(2)), -until the remaining of the second elapsed. - -Hmm a possible problem is that there would be many filedescriptors to close -by the other process before execve(2) can safely be done, because two of them -would be used per client being served by the current server process. We could -perhaps use close-on-exec flag. -fcntl(fd, F_SETFD, 1); Of course, we would if required remove the close-on-exec -flag from some descriptors when necessary, perhaps. We would make sure that -the process to run the executable has stderr redirected to /dev/null, that -It's stdin and stdout be redirected to the wanted socket, which would be -kept open, and that all other filedescriptors be closed. - -For bandwidth shaping, the filedescriptors not to poll anymore until a specific -timeout would also have their client_node moved to another linked list, -possibly. - - -I should also think about how the pool of server processes will be maintained. -It would be nice if it could distribute the connections among them based on -real process load rather than number of connections... For that, it possibly -could broadcast a request to the processes, and see which one responds the -first (or use some kind of lock, so that the first one which can get it does). -In the first case, it of course would ensure to ignore responses for obsolete -requests (which will come in late). It should be able to detect when to start -new processes, and also when to start killing some of them which did not serve -any request for some time. A statistics system similar to when to free pool_t -pages back could be used, perhaps? - -Also, it is yet to be decided if the filedescriptor of the new connection -should be passed to the processes of the pool, or if they should be allowed -to accept(2) on a common filedescriptor... If we pass it along, we should -make sure to only close it after getting the first acknowledgement from one -of the processes of the pool, obviously. Also, the master process should still -be able to process further accept(2) and queue the request/fd until it be -dispatched, and be able to poll(2) both the bound sockets and descriptors -of each pool. It possibly also could time how much delay was necessary to -obtain a response from a process of the pool, as a clue on when to start a -new process, perhaps (but it might be better for the pool members to be able -to send load stats via the response, which could be taken into account -via statistics, etc). They could report how much bytes they transfered perhaps, -since last request or such. Check if linux has decent getrusage(2) also... - -It would be impossible to respect the KILL_CHILDREN = NO configuration -directive anymore, because our processes would be critical to their execution. - -Now also think about how the name resolving should be done, and cache -maintained. diff --git a/mmsoftware/mmspawnd/new/mmspawnd.c b/mmsoftware/mmspawnd/new/mmspawnd.c deleted file mode 100644 index 79a9721..0000000 --- a/mmsoftware/mmspawnd/new/mmspawnd.c +++ /dev/null @@ -1,1498 +0,0 @@ -/* $Id: mmspawnd.c,v 1.2 2004/06/04 01:02:34 mmondor Exp $ */ - -/* - * Copyright (C) 2003, Matthew Mondor - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Matthew Mondor. - * 4. The name of Matthew Mondor may not be used to endorse or promote - * products derived from this software without specific prior written - * permission. - * 5. Redistribution of source code may not be released under the terms of - * any GNU Public License derivate. - * - * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -/* - * An inetd-like network server, but which only listens to one port and - * launches one command. It however performs alot more sanity checking, - * and has support for chroot(2), custom limits, and setrlimit(2) ones - * (for the children only, since we handle our own limits checking). - * To use it for several services, a separate configuration file should - * be provided for each. - */ - -/* TODO: - * - It would be nice to have all I/O of all client processes be tunnelled - * through a bandwidth shaper process (which could then use mmfd(3)). - * However, this is not an immediate concern. - * - Although we cannot monitor if a child process is idle properly using - * the current method, it would be nice to have a maximum time to live - * parameter in seconds. - * - The first and second problems could both be solved if we implemented - * a popen()-like system instead of having the children serve directly - * the socket. We either could have another dedicated process handle the - * I/O of many ongoing connections, or to have the main system use it - * while still verifying for connection events. Doing this would both - * allow custom bandwidth shaping, as well as real network inactivity - * monitoring. If using such a method, it would probably be nice to use - * a library like libevent or libio which could take adventage of features - * like kqueue and epoll where available. Using poll(2) wouldn't be too - * bad however... - */ - - - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include /* strerror(3) */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* For more information about these, see the manual pages in mmlib/ */ -#include -#include -#include -#include -#include -#include -#include -#include -#include - - - -MMCOPYRIGHT("@(#) Copyright (c) 2003\n\ -\tMatthew Mondor. All rights reserved.\n"); -MMRCSID("$Id: mmspawnd.c,v 1.2 2004/06/04 01:02:34 mmondor Exp $"); - - - -/* DEFINITIONS */ - -#define DAEMON_NAME "mmspawnd" -#define DAEMON_VERSION "0.0.1/mmondor" - -struct configuration { - char CHROOT_DIR[256], LOCK_PATH[256], PID_PATH[256], - USER[32], GROUPS[256], LOG_FACILITY[32], LISTEN_ADDRESSES[1024], - COMMAND[256], ALOCK_PATH[256], ALOCK_USER[32], ALOCK_GROUP[32], - ALOCK_MODE[4]; - long LISTEN_PORT, MAX_CONNECTIONS, MAX_ADDRESSES, MAX_PER_ADDRESS, - CONNECTION_RATE, CONNECTION_PERIOD, MAX_ARGUMENTS, LIMIT_CORE, - LIMIT_CPU, LIMIT_DATA, LIMIT_FSIZE, LIMIT_MEMLOCK, LIMIT_NOFILE, - LIMIT_NPROC, LIMIT_RSS, LIMIT_STACK; - bool RESOLVE_ADDRESSES, RLIMITS; -}; - -/* Necessary synchronization locks for shared memory access */ -enum shlocks { - SHLOCK_IPADDR = 0, - SHLOCK_MAX -}; - -/* Base to shared memory, significant both in the parent and children - * processes. The associated synchronization lock is commented. - */ -struct shmem_data { - /* Locked with SHLOCK_IPADDR */ - pool_t ipaddr_pool; - hashtable_t ipaddr_table; -}; - -/* Base to client-specific data, only significant for each children process */ -struct client_data { - struct ipaddr_node *ipn; - const char *ipaddr; - struct sockaddr saddr; -}; - -/* And server-specific data */ -struct server_data { - int runlock, alock, current_connections; - pool_t pid_pool; - hashtable_t pid_table; - char *line, *command, **argv; - rlim_t *rlimits; -}; - -/* Defines an interface to listen to */ -struct iface { - char address[16]; /* Server address string or '\0' */ - int sock; /* Bound socket (or -1) */ - struct sockaddr_in saddr; /* Address to bind(2) to */ -}; - -/* Used for the hostname cache and per-address connection rate limiting */ -struct ipaddr_node { - hashnode_t node; - struct limitrate lr; - struct sockaddr address; /* Key */ - long connections; - char hostname[64]; -}; - -/* Used to efficiently link a child pid_t to an ipaddr_node structure pointer, - * so that we may easily decrease the total connections counter for that - * address in our SIGCHLD handler. - */ -struct pid_node { - hashnode_t node; - pid_t pid; /* Key */ - struct ipaddr_node *ipn; -}; - -/* Used to store shared context between ipddr_expire_process() and it's - * iterator function - */ -struct ipaddr_expire_process_iterator_udata { - time_t current, soonest; -}; - -/* Useful macro to cleanup code later */ -#define SETRLIMIT(restxt, resource, limit) do { \ - if ((limit) != -1) { \ - struct rlimit rl; \ - \ - rl.rlim_cur = rl.rlim_max = (rlim_t)(limit); \ - if (setrlimit((resource), &rl) == -1) \ - syslog(LOG_NOTICE, "* setrlimit(%s, %ld) - (%s)", \ - (restxt), (limit), strerror(errno)); \ - } \ -} while (/* CONSTCOND */0) ; - -/* Internal mappings for the rlimits in the array */ -enum rlimit { - RL_CORE = 0, - RL_CPU, - RL_DATA, - RL_FSIZE, - RL_MEMLOCK, - RL_NOFILE, - RL_NPROC, - RL_RSS, - RL_STACK -} - - - -/* PROTOTYPES */ - -int main(int, char **); - -static bool daemon_detach(const char *, const char *); -static void daemon_sighandler(int); -static bool client_kill_iterator(hashnode_t *, void *); -static void pidfile_write(const char *); -static int lock_check(const char *); -static struct iface *ifaces_parse(char *); - -static void client_resolve(void); -static bool exec_popen(int *, pid_t *, const char *, char * const *, rlim_t *, - struct iface *, int, struct ipaddr_node *, struct sockaddr *, - const char *); -static void exec_command(const char *, char * const *, rlim_t *); - -static bool connection_open(const char *); -static void connection_close(void); -static struct ipaddr_node *ipaddr_open(struct sockaddr *, const char *); -static void ipaddr_close(struct ipaddr_node *); - -static u_int32_t key_hash32(const void *, size_t); -static int key_cmp32(const void *, const void *, size_t); -static int key_pidcmp(const void *, const void *, size_t); -static u_int32_t key_pidhash(const void *, size_t); - -static pid_t launch_ipaddr_expire_process(uid_t, gid_t *, int); -static void ipaddr_expire_process(void); -static bool ipaddr_expire_process_iterator(hashnode_t *, void *); - - - -/* GLOBALS */ - -/* Normal static data used for current configuration, so that clients may keep - * their active config copy in case the server reloads it; Also is of faster - * access than shared memory, requireing no locking for synchronization. - */ -static struct configuration CONF; - -/* Base structure to all shared memory data, for which synchronization is - * required using locks. - */ -static struct shmem_data *shmem = NULL; - -/* Static data array mapping lock files descriptors to an index used with - * flock(3) for the various shared memory sections. - */ -static int locks[(enum shlocks)SHLOCK_MAX]; - -/* Static client-specific data which is only significant for each children - * process, as well as server-specific data which belongs to the listener - * process. - */ -static struct client_data client; -static struct server_data server; -rlim_t rlimits[9]; - - - -/* CODE */ - -int main(int argc, char **argv) -{ - char *conf_file = "/usr/local/etc/mmspawnd.conf"; - pid_t uid; - gid_t *gids; - int ngids, backlog; - long facility; - cres_t cres; - carg_t *cargp; - carg_t cargs[] = { - {CAT_STR, CAF_NONE, 0, 255, "CHROOT_DIR", CONF.CHROOT_DIR}, - {CAT_STR, CAF_NONE, 1, 255, "LOCK_PATH", CONF.LOCK_PATH}, - {CAT_STR, CAF_NONE, 1, 255, "PID_PATH", CONF.PID_PATH}, - {CAT_STR, CAF_NONE, 1, 31, "USER", CONF.USER}, - {CAT_STR, CAF_NONE, 1, 255, "GROUPS", CONF.GROUPS}, - {CAT_STR, CAF_NONE, 1, 31, "LOG_FACILITY", CONF.LOG_FACILITY}, - {CAT_STR, CAF_NONE, 1, 1023, "LISTEN_ADDRESSES", - CONF.LISTEN_ADDRESSES}, - {CAT_STR, CAF_NONE, 1, 255, "COMMAND", CONF.COMMAND}, - {CAT_STR, CAF_NONE, 0, 255, "ALOCK_PATH", CONF.ALOCK_PATH}, - {CAT_STR, CAF_NONE, 1, 32, "ALOCK_USER", CONF.ALOCK_USER}, - {CAT_STR, CAF_NONE, 1, 32, "ALOCK_GROUP", CONF.ALOCK_GROUP}, - {CAT_STR, CAF_NONE, 1, 32, "ALOCK_MODE", CONF.ALOCK_MODE}, - {CAT_VAL, CAF_NONE, 0, 64, "MAX_ARGUMENTS", &CONF.MAX_ARGUMENTS}, - {CAT_VAL, CAF_NONE, 1, 65535, "LISTEN_PORT", &CONF.LISTEN_PORT}, - {CAT_VAL, CAF_NONE, 1, 99999, "MAX_CONNECTIONS", - &CONF.MAX_CONNECTIONS}, - {CAT_VAL, CAF_NONE, 1, 99999, "MAX_ADDRESSES", &CONF.MAX_ADDRESSES}, - {CAT_VAL, CAF_NONE, 1, 99999, "MAX_PER_ADDRESS", - &CONF.MAX_PER_ADDRESS}, - {CAT_VAL, CAF_NONE, 0, 99999, "CONNECTION_RATE", - &CONF.CONNECTION_RATE}, - {CAT_VAL, CAF_NONE, 0, 99999, "CONNECTION_PERIOD", - &CONF.CONNECTION_PERIOD}, - {CAT_VAL, CAF_NONE, 0, 0, "RLIMIT_CORE", &CONF.LIMIT_CORE}, - {CAT_VAL, CAF_NONE, 0, 0, "RLIMIT_CPU", &CONF.LIMIT_CPU}, - {CAT_VAL, CAF_NONE, 0, 0, "RLIMIT_DATA", &CONF.LIMIT_DATA}, - {CAT_VAL, CAF_NONE, 0, 0, "RLIMIT_FSIZE", &CONF.LIMIT_FSIZE}, - {CAT_VAL, CAF_NONE, 0, 0, "RLIMIT_MEMLOCK", &CONF.LIMIT_MEMLOCK}, - {CAT_VAL, CAF_NONE, 0, 0, "RLIMIT_NOFILE", &CONF.LIMIT_NOFILE}, - {CAT_VAL, CAF_NONE, 0, 0, "RLIMIT_NPROC", &CONF.LIMIT_NPROC}, - {CAT_VAL, CAF_NONE, 0, 0, "RLIMIT_RSS", &CONF.LIMIT_RSS}, - {CAT_VAL, CAF_NONE, 0, 0, "RLIMIT_STACK", &CONF.LIMIT_STACK}, - {CAT_BOOL, CAF_NONE, 0, 0, "RESOLVE_ADDRESSES", - &CONF.RESOLVE_ADDRESSES}, - {CAT_BOOL, CAF_NONE, 0, 0, "KILL_CHILDREN", &CONF.KILL_CHILDREN}, - {CAT_BOOL, CAF_NONE, 0, 0, "RLIMITS", &CONF.RLIMITS}, - {CAT_END, CAF_NONE, 0, 0, NULL, NULL} - }; - cmap_t cmap[] = { - {"LOG_AUTH", LOG_AUTH}, - {"LOG_AUTHPRIV", LOG_AUTHPRIV}, - {"LOG_CRON", LOG_CRON}, - {"LOG_DAEMON", LOG_DAEMON}, - {"LOG_FTP", LOG_FTP}, - {"LOG_LPR", LOG_LPR}, - {"LOG_MAIL", LOG_MAIL}, - {"LOG_NEWS", LOG_NEWS}, - {"LOG_SYSLOG", LOG_SYSLOG}, - {"LOG_USER", LOG_USER}, - {"LOG_UUCP", LOG_UUCP}, - {NULL, 0} - }; - int i, nifaces; - struct sockaddr_in server_addr; - struct iface *ifaces, *tif; - struct pollfd *fds; - - /* Set harmless defaults */ - *CONF.CHROOT_DIR = '\0'; - (void) mm_strcpy(CONF.LOCK_PATH, "/var/run/mmspawnd.lock"); - (void) mm_strcpy(CONF.PID_PATH, "/var/run/mmspawnd.pid"); - (void) mm_strcpy(CONF.USER, "mmspawnd"); - (void) mm_strcpy(CONF.GROUPS, "mmspawnd"); - (void) mm_strcpy(CONF.LOG_FACILITY, "LOG_AUTHPRIV"); - (void) mm_strcpy(CONF.LISTEN_ADDRESSES, "127.0.0.1"); - (void) mm_strcpy(CONF.COMMAND, "/sbin/nologin"); - *CONF.ALOCK_PATH = '\0'; - (void) mm_strcpy(CONF.ALOCK_USER, "mmspawnd"); - (void) mm_strcpy(CONF.ALOCK_GROUP, "mmspawnd"); - (void) mm_strcpy(CONF.ALOCK_MODE, "400"); - CONF.LISTEN_PORT = 2323; - CONF.MAX_CONNECTIONS = 32; - CONF.MAX_ADDRESSES = 32; - CONF.MAX_PER_ADDRESS = 1; - CONF.CONNECTION_RATE = 5; - CONF.CONNECTION_PERIOD = 30; - CONF.MAX_ARGUMENTS = 16; - CONF.LIMIT_CORE = -1; - CONF.LIMIT_CPU = -1; - CONF.LIMIT_DATA = -1; - CONF.LIMIT_FSIZE = -1; - CONF.LIMIT_MEMLOCK = -1; - CONF.LIMIT_NOFILE = -1; - CONF.LIMIT_NPROC = -1; - CONF.LIMIT_RSS = -1; - CONF.LIMIT_STACK = -1; - CONF.RESOLVE_ADDRESSES = FALSE; - CONF.KILL_CHILDREN = TRUE; - CONF.RLIMITS = FALSE; - - /* Read config file */ - if (argc == 2) - conf_file = argv[1]; - if (!mmreadcfg(&cres, cargs, conf_file)) { - /* Error parsing configuration file, report which */ - (void) fprintf(stderr, "\nError parsing '%s'\n", conf_file); - (void) fprintf(stderr, "Error : %s\n", mmreadcfg_strerr(cres.CR_Err)); - if (*(cres.CR_Data)) - (void) fprintf(stderr, "Data : %s\n", cres.CR_Data); - if ((cargp = cres.CR_Keyword) != NULL) { - (void) fprintf(stderr, "Keyword: %s\n", cargp->CA_Keyword); - (void) fprintf(stderr, "Minimum: %ld\n", cargp->CA_Min); - (void) fprintf(stderr, "Maximum: %ld\n", cargp->CA_Max); - } - if (cres.CR_Line != -1) - (void) fprintf(stderr, "Line : %d\n", cres.CR_Line); - (void) fprintf(stderr, "\n"); - exit(EXIT_FAILURE); - } - - /* Post parsing */ - if (!mmmapstring(cmap, CONF.LOG_FACILITY, &facility)) { - (void) fprintf(stderr, "\nUnknown syslog facility %s\n\n", - CONF.LOG_FACILITY); - exit(EXIT_FAILURE); - } - if ((uid = mmgetuid(CONF.USER)) == -1) { - (void) fprintf(stderr, "\nUnknown USER '%s'\n\n", CONF.USER); - exit(EXIT_FAILURE); - } - if (!(gids = mmgetgidarray(&ngids, CONF.GROUPS))) { - (void) fprintf(stderr, "\nOne of following GROUPS unknown: '%s'\n\n", - CONF.GROUPS); - exit(EXIT_FAILURE); - } - if (*CONF.ALOCK_PATH != '\0') { - uid_t u; - gid_t g; - mode_t m; - - if ((u = mmgetuid(CONF.ALOCK_USER)) == -1) { - (void) fprintf(stderr, "\nUnknown ALOCK_USER '%s'\n\n", - CONF.ALOCK_USER); - exit(EXIT_FAILURE); - } - if ((g = mmgetgid(CONF.ALOCK_GROUP)) == -1) { - (void) fprintf(stderr, "\nUnknown ALOCK_GROUP '%s'\n\n", - CONF.ALOCK_GROUP); - exit(EXIT_FAILURE); - } - sscanf(CONF.ALOCK_MODE, "%o", &m); - if (m == 0 || m > 0770) { - (void) fprintf(stderr, "\nIllegal ALOCK_MODE '%03o'\n\n", m); - exit(EXIT_FAILURE); - } - if ((server.alock = open(CONF.ALOCK_PATH, O_WRONLY | O_CREAT, m)) - == -1) { - (void) fprintf(stderr, "\n* main() - open(%s) - (%s)\n\n", - CONF.ALOCK_PATH, strerror(errno)); - exit(EXIT_FAILURE); - } - if (fchown(server.alock, u, g) == -1) { - (void) fprintf(stderr, "\n* main() - fchown(%s) - (%s)\n\n", - CONF.ALOCK_PATH, strerror(errno)); - exit(EXIT_FAILURE); - } - } else - server.alock = -1; - if ((ifaces = ifaces_parse(CONF.LISTEN_ADDRESSES)) == NULL) { - (void) fprintf(stderr, "\nInvalid LISTEN_ADDRESSES\n\n"); - exit(EXIT_FAILURE); - } - if ((server.line = mm_strdup(CONF.COMMAND)) != NULL && (server.argv = - malloc(sizeof(char *) * (CONF.MAX_ARGUMENTS + 2))) != NULL) { - int argc; - - if (!mm_cmdparse(&server.command, &argc, server.argv, server.line, - CONF.MAX_ARGUMENTS + 2)) { - (void) fprintf(stderr, "\nError parsing COMMAND ", - "(also check MAX_ARGUMENTS)\n\n"); - exit(EXIT_FAILURE); - } - } else { - (void) fprintf(stderr, "\nOut of memory\n\n"); - exit(EXIT_FAILURE); - } - if (CONF.RLIMITS) { - rlimits[RL_CORE] = CONF.LIMIT_CORE; - rlimits[RL_CPU] = CONF.LIMIT_CPU; - rlimits[RL_DATA] = CONF.LIMIT_DATA; - rlimits[RL_FSIZE] = CONF.LIMIT_FSIZE; - rlimits[RL_MEMLOCK] = CONF.LIMIT_MEMLOCK; - rlimits[RL_NOFILE] = CONF.LIMIT_NOFILE; - rlimits[RL_NPROC] = CONF.LIMIT_NPROC; - rlimits[RL_RSS] = CONF.LIMIT_RSS; - rlimits[RL_STACK] = CONF.LIMIT_STACK; - server.rlimits = rlimits; - } else - server.rlimits = NULL; - - /* Config file seemed right, start with initialization */ - openlog(DAEMON_NAME, LOG_PID | LOG_NDELAY, facility); - - /* Some consistency checks according to permissions */ -#ifndef NODROPPRIVS - if ((getuid())) { - (void) fprintf(stderr, - "\nOnly the super user may start this daemon\n\n"); - syslog(LOG_NOTICE, - "main() - Refused to launch for uid %d attempt (not root, \ -!NODROPPRIVS)", (int)getuid()); - exit(EXIT_FAILURE); - } -#else /* NODROPPRIVS */ - if ((getuid()) == 0) { - (void) fprintf(stderr, "\nCompiled with NODROPPRIVS, ", - "refusing to run as uid 0\n\n"); - syslog(LOG_NOTICE, "main() - NODROPPRIVS, refusing to run as uid 0"); - exit(EXIT_FAILURE); - } -#endif /* !NODROPPRIVS */ - - /* Make sure that only one instance is running */ - if ((server.runlock = lock_check(CONF.LOCK_PATH)) == -1) { - (void) fprintf(stderr, "\n%s already running\n\n", DAEMON_NAME); - syslog(LOG_NOTICE, "main() - %s already running", DAEMON_NAME); - exit(EXIT_FAILURE); - } - - /* Things we want to do now in case we chroot(2) */ - /* XXX If we want to use mmstat(3)/mmstatd(8) we should initialize the - * system here. However, this is a problem with mmstat(3) and chroot - * setups, unless an mmstatd(8) daemon runs into the chroot itself, - * or that it is certain to not be restarted. - * I should fix mmstatd(8) to support chroot(2). - */ - - /* Create our locks for shared memory synchronization */ - if (!shlocks_init(CONF.LOCK_PATH, locks, SHLOCK_MAX)) { - syslog(LOG_NOTICE, "main() - shlocks_init()"); - exit(EXIT_FAILURE); - } - - /* Close unnecessary filedescriptors and become a daemon, chroot(2)ing - * if necessary. This also sets up the connection listener signal handlers. - */ - if (!daemon_detach(CONF.PID_PATH, CONF.CHROOT_DIR)) { - syslog(LOG_NOTICE, "main() - daemon_detach()"); - exit(EXIT_FAILURE); - } - - /* Reset number of current connections to 0 and initialize the pid_t table - * and pool. - */ - server.current_connections = 0; - if (!pool_init(&server.pid_pool, malloc, free, sizeof(struct pid_node), - CONF.MAX_CONNECTIONS, 1, 1)) { - syslog(LOG_NOTICE, "main() - pool_init(pid_pool)"); - exit(EXIT_FAILURE); - } - if (!hashtable_init(&server.pid_table, CONF.MAX_CONNECTIONS, 1, malloc, - free, key_pidcmp, key_pidhash, FALSE)) { - syslog(LOG_NOTICE, "main() - hashtable_init(pid_table)"); - exit(EXIT_FAILURE); - } - - /* Allocate some shared memory */ - if ((shmem = shmem_malloc(sizeof(struct shmem_data))) == NULL) { - syslog(LOG_NOTICE, "main() - shmem_malloc()"); - exit(EXIT_FAILURE); - } - - /* Initialize shared memory pool and table. Note that we make sure to - * allocate the memory in a single block, large enough, so that - * shmem_free() not be called by another process, which obviously would - * only munmap(2) within it's own process. - */ - if (!pool_init(&shmem->ipaddr_pool, shmem_malloc, shmem_free, - sizeof(struct ipaddr_node), CONF.MAX_ADDRESSES, 1, 1)) { - syslog(LOG_NOTICE, "main() - pool_init(ipaddr_pool)"); - exit(EXIT_FAILURE); - } - if (!hashtable_init(&shmem->ipaddr_table, CONF.MAX_ADDRESSES, 1, - shmem_malloc, shmem_free, key_cmp32, key_hash32, FALSE)) { - syslog(LOG_NOTICE, "main() - hashtable_init(ipaddr_table)"); - exit(EXIT_FAILURE); - } - - /* Start our cache entry expiration process. It would have been possible - * to use setitimer(2) and do this into the signal handler of the main - * process, alternatively. However, since we already have the interprocess - * locking glue setup, we can keep the design simple and use a process. - * Further, it allows to have the resolver fill the cache from the child - * processes asynchroneously, without causing the main listener to block. - * Note that the following function also causes the child to become - * unprivileged. - */ - if (launch_ipaddr_expire_process(uid, gids, ngids) < 1) { - syslog(LOG_NOTICE, "main() - launch_ipaddr_expire_process()"); - exit(EXIT_FAILURE); - } - - /* Bind our port to the various interfaces */ - for (nifaces = 0, tif = ifaces; *(tif->address) != '\0'; tif++) { - if ((tif->sock = socket(AF_INET, SOCK_STREAM, 0)) > -1) { - i = 1; - if ((setsockopt(tif->sock, SOL_SOCKET, SO_REUSEADDR, &i, - sizeof(int))) == -1) - syslog(LOG_NOTICE, - "main() - setsockopt(SO_REUSEADDR %s:%d)", - tif->address, (int)CONF.LISTEN_PORT); - i = 0; - mm_memclr(&server_addr, sizeof(struct sockaddr_in)); - server_addr.sin_family = AF_INET; - server_addr.sin_addr = tif->saddr.sin_addr; - server_addr.sin_port = htons(CONF.LISTEN_PORT); - if ((bind(tif->sock, (struct sockaddr *)(void *)&server_addr, - sizeof(struct sockaddr_in))) == 0) - nifaces++; - else { - syslog(LOG_NOTICE, - "main() - bind(%s:%d)", - tif->address, (int)CONF.LISTEN_PORT); - (void) close(tif->sock); - tif->sock = -1; - } - } - } - if (nifaces == 0) { - syslog(LOG_NOTICE, "No interfaces (left) to listen to"); - (void) kill(0, SIGTERM); - exit(EXIT_FAILURE); - } - - /* Drop privileges definitively */ - if (!mmdropprivs(uid, gids, ngids)) { - syslog(LOG_NOTICE, "Cannot change uid and gids to safe privs"); - exit(EXIT_FAILURE); - } - -#ifndef __GLIBC__ - setproctitle("Socket listener process"); -#endif /* __GLIBC__ */ - - /* Start listening to bound sockets */ - backlog = CONF.MAX_ADDRESSES * CONF.MAX_PER_ADDRESS; - if (backlog > CONF.MAX_CONNECTIONS) - backlog = CONF.MAX_CONNECTIONS; - for (tif = ifaces; *(tif->address); tif++) { - if (tif->sock != -1) { - if ((listen(tif->sock, backlog)) == -1) { - syslog(LOG_NOTICE, "* main() - listen(%s)", tif->address); - (void) close(tif->sock); - tif->sock = -1; - nifaces--; - } else - syslog(LOG_NOTICE, "Listening on [%s:%d]", - tif->address, (int)CONF.LISTEN_PORT); - } - } - - /* Setup our poll() array for the interfaces we'll accept() on */ - if ((fds = malloc(sizeof(struct pollfd) * nifaces)) != NULL) { - for (i = 0, tif = ifaces; *(tif->address) != '\0'; tif++) { - if (tif->sock != -1) { - fds[i].fd = tif->sock; - fds[i].events = POLLIN; - fds[i].revents = 0; - i++; - } - } - } else { - syslog(LOG_NOTICE, "Out of memory"); - exit(EXIT_FAILURE); - } - - /* Listen for connections and dispatch them to independant processes */ - syslog(LOG_NOTICE, "Launched for uid %d with configuration file '%s', \ -listening for connections on port %d. Service is '%s'.", - uid, conf_file, (int)CONF.LISTEN_PORT, CONF.COMMAND); - for (;;) { - int i, sock, nifaces2; - socklen_t addrl = sizeof(struct sockaddr); - struct sockaddr addr; - struct ipaddr_node *ipn = NULL; - char ipaddr[20]; - - while ((nifaces2 = poll(fds, nifaces, -1)) == -1 && errno == EINTR) ; - for (i = 0; i < nifaces && nifaces2 > 0; i++) { - if (fds[i].revents & POLLIN) { - nifaces2--; - if ((sock = accept(fds[i].fd, &addr, &addrl)) != -1) { - bool connection; - - /* We got a connection, verify if within allowed limits. */ - mm_strcpy(ipaddr, "0.0.0.0"); - (void) inet_ntop(AF_INET, - &(((struct sockaddr_in *)&addr)->sin_addr), - ipaddr, 19); - if ((connection = connection_open(ipaddr)) && - (ipn = ipaddr_open(&addr, ipaddr)) != NULL) { - pid_t pid; - int csock; - - if (exec_popen(&csock, &pid, server.command, - (char * const *)server.argv, - server.rlimits, ifaces, nifaces, - ipn, &addr, ipaddr)) { - /* Parent, record child process, and continue. - * As we use a preallocated pool of enough nodes - * for the allowed limits, no need for error - * checking at pool_alloc(). We now need to - * perform forwarding from csock to sock back - * and forth, until a socket problem occurs, or - * that an inactivity timeout occurs. - * XXX We probably do not need to handle - * SIGCHLD specially anymore, since the socket - * disconnection or SIGPIPE will now occur at - * child process termination. It also probably - * would be nice to dedicate a process to - * forwarding among those descriptors, perhaps... - * We then could be dedicated to new connections. - * We would need a lock to add new nodes to the - * forwarder process, however. It also would need - * to be able to notify us when a connection is - * dropped (or a child process exited). We probably - * also need to pass it the new client connection - * filedescriptor... If we do not dedicate a - * process for this, we should then make sure to - * always verify the status of the bound sockets - * even when sleeping for bandwidth shaping. - * If we are able to delegate the tasks to other - * processes, it also could make the system more - * scalable. The administrator could specify how - * many processes to run to handle forwarding... - */ - a pnod = (struct pid_node *)pool_alloc( - &server.pid_pool, FALSE); - pnod->pid = pid; - pnod->ipn = ipn; - (void) hashtable_link(&server.pid_table, - (hashnode_t *)pnod, &pnod->pid, - sizeof(pid_t)); - /* XXX */ - } - } else { - /* Limits have been exceeded, close socket immediately - */ - (void) close(sock); - if (connection) - connection_close(); - if (ipn != NULL) - ipaddr_close(ipn); - } - } else - DEBUG_PRINTF("main", "accept()"); - } - } - } - - /* NOTREACHED */ - exit(EXIT_SUCCESS); -} - - -/* Uses fork(2) and setsid(2) to become a daemon, and detaches from the - * current tty. Also calls chroot(2) and chdir(2) if a jail directory is - * supplied. Returns either successful or not (TRUE, FALSE). - */ -static bool daemon_detach(const char *pidfile, const char *jail) -{ - int pid; - -#ifdef NODETACH - pid = getpid(); -#else /* NODETACH */ - if ((pid = fork()) != -1) -#endif /* !NODETACH */ - { - -#ifndef NODETACH - if (pid > 0) { - /* Parent */ - exit(EXIT_SUCCESS); - } else -#endif /* !NODETACH */ - { - int fd; - struct sigaction act; - - /* Child */ - (void) setsid(); - (void) chdir("/"); - if ((fd = open("/dev/null", O_RDWR)) != -1) { - (void) dup2(fd, 0); - (void) dup2(fd, 1); - (void) dup2(fd, 2); - if (fd > 2) - (void) close(fd); - } - pidfile_write(pidfile); - - /* Chroot if required */ - if (jail != NULL && *jail != '\0') { - if ((chroot(jail)) == -1) { - syslog(LOG_NOTICE, - "daemon_detach() - Could not chroot(2) to '%s'", - jail); - exit(EXIT_FAILURE); - } - (void) chdir("/"); - } - - /* Setup our server signal handlers. We also want to make - * sure that we catch SIGCHLD for every process that exits. - * (we don't need SIGCHLD events for process STOP however). - */ - act.sa_handler = daemon_sighandler; - act.sa_flags = SA_NOCLDSTOP; - (void) sigemptyset(&act.sa_mask); - - (void) sigaction(SIGTERM, &act, NULL); - (void) sigaction(SIGINT, &act, NULL); - (void) sigaction(SIGCHLD, &act, NULL); - (void) sigaction(SIGSEGV, &act, NULL); - - act.sa_handler = SIG_IGN; - (void) sigaction(SIGTTOU, &act, NULL); - (void) sigaction(SIGTTIN, &act, NULL); - (void) sigaction(SIGTSTP, &act, NULL); - (void) sigaction(SIGPIPE, &act, NULL); - - (void) umask(0); - - return TRUE; - } - } - - return FALSE; -} - - -/* This signal handler takes care of wanted signals in the TCP listener - * server (parent process). - */ -static void daemon_sighandler(int sig) -{ - switch (sig) { - case SIGSEGV: - syslog(LOG_NOTICE, "Exiting (SIGSEGV!)"); - goto end; - break; - case SIGTERM: - syslog(LOG_NOTICE, "Exiting (SIGTERM)"); - goto end; - break; - case SIGINT: - syslog(LOG_NOTICE, "Exiting (SIGINT)"); - goto end; - break; - case SIGCHLD: - { - int status = 0; - pid_t pid; - struct pid_node *pn; - - while ((pid = wait3(&status, WNOHANG, NULL)) == -1 && - errno == EINTR) ; - if (pid != -1) { - bool quit = FALSE; - - if (WIFSIGNALED(status)) { - syslog(LOG_NOTICE, "Child %d received signal %d", (int)pid, - WTERMSIG(status)); - quit = TRUE; - } - if (WIFEXITED(status)) { - if (WEXITSTATUS(status) != 0) - syslog(LOG_NOTICE, "Child %d terminated with code %d", - (int)pid, WEXITSTATUS(status)); - else - syslog(LOG_NOTICE, "Child %d terminated normally", - (int)pid); - quit = TRUE; - } - if (quit) { - /* We have the pid of the process which just exited. - * We must decrease the total connections counter as well - * as the connections counter for that address. - * We must do this here since execve(2) does not return - * on success. We use the hashtable_t we setup to link - * pid_t -> struct ipaddr_node *. - */ - if ((pn = (struct pid_node *)hashtable_lookup( - &server.pid_table, &pid, - sizeof(pid_t))) != NULL) { - connection_close(); - ipaddr_close(pn->ipn); - hashtable_unlink(&server.pid_table, (hashnode_t *)pn); - (void) pool_free((pnode_t *)pn); - } - } - } - } - break; - } - - return; - -end: - /* Cleanly exit all processes. Because we use setsid(2) in the children, - * We use our table of still active children processes instead of a single - * kill(2) on the whole process group. We do that too afterwards so that - * we cleanup our server-side processes afterwards. - */ - if (CONF.KILL_CHILDREN) - hashtable_iterate(&server.pid_table, client_kill_iterator, NULL); - { - struct sigaction act; - - act.sa_handler = SIG_IGN; - act.sa_flags = SA_NOCLDSTOP; - (void) sigemptyset(&act.sa_mask); - - (void) sigaction(SIGTERM, &act, NULL); - } - (void) kill(0, SIGTERM); - shlocks_destroy(CONF.LOCK_PATH, locks, (enum shlocks)SHLOCK_MAX, TRUE); - shmem_freeall(); - exit(EXIT_SUCCESS); -} - - -/* ARGSUSED */ -static bool client_kill_iterator(hashnode_t *hnod, void *udata) -{ - (void) kill(((struct pid_node *)hnod)->pid, SIGTERM); - - return TRUE; -} - - -/* Writes our process ID number to specified file. To be called before - * chroot(2) or dropping privileges. - */ -static void pidfile_write(const char *file) -{ - char str[16]; - int fd; - - if ((fd = open(file, O_CREAT | O_TRUNC | O_WRONLY, 0600)) != -1) { - (void) snprintf(str, 15, "%d\n", getpid()); - (void) write(fd, str, mm_strlen(str)); - (void) close(fd); - } else - DEBUG_PRINTF("pidfile_write", "open(%s)", file); -} - - -/* This uses an fd which remains open in child processes, if they close it the - * lock is released automatically by the process. Returns -1 if the lock - * is already held, or a filedescriptor to the lock on success. - */ -static int lock_check(const char *file) -{ - int fd; - - if ((fd = open(file, O_CREAT | O_TRUNC | O_WRONLY, 0600)) != -1) { - if ((flock(fd, LOCK_EX | LOCK_NB)) == 0) - return fd; - (void) close(fd); - } else - DEBUG_PRINTF("lock_check", "open(%s)", file); - - return -1; -} - - -/* Simple function to parse the interfaces specified on a line */ -static struct iface *ifaces_parse(char *addresses) -{ - struct iface *ifaces; - char *ips[65]; - struct sockaddr_in saddr; - int n, i; - - ifaces = NULL; - - if (addresses != NULL && *addresses != '\0') { - if ((n = mm_straspl(ips, addresses, 64)) > 0) { - if ((ifaces = malloc(sizeof(struct iface) * (n + 1))) != NULL) { - /* Setup our array */ - for (i = 0; i < n; i++) { - mm_memclr(&saddr, sizeof(struct sockaddr_in)); - if ((inet_aton(ips[i], &(saddr.sin_addr))) == 1) { - (void) mm_strncpy(ifaces[i].address, ips[i], 15); - ifaces[i].sock = -1; - ifaces[i].saddr = saddr; - *(ifaces[i + 1].address) = '\0'; - } else { - syslog(LOG_NOTICE, - "ifaces_parse() - Invalid address [%s]", - ips[i]); - free(ifaces); - ifaces = NULL; - break; - } - } - } - } - } - - return ifaces; -} - - -/* Internal function to resove the hostname of the IP address of a connected - * client and cache it (if RESOLVE_ADDRESSES was enabled). - */ -static void client_resolve(void) -{ - if (CONF.RESOLVE_ADDRESSES) { - if (*client.ipn->hostname == '\0') { - if ((flock(locks[SHLOCK_IPADDR], LOCK_EX)) == 0) { - if (*client.ipn->hostname == '\0') { - if ((getnameinfo(&client.saddr, sizeof(struct sockaddr_in), - client.ipn->hostname, 64, NULL, 0, 0)) - != 0) - DEBUG_PRINTF("client_resolve", "getnameinfo(%s)", - client.ipaddr); - } - (void) flock(locks[SHLOCK_IPADDR], LOCK_UN); - } - } - syslog(LOG_NOTICE, "Connection from %s (%s)", client.ipn->hostname, - client.ipaddr); - } else - syslog(LOG_NOTICE, "Connection from %s", client.ipaddr); -} - - -/* Forks a process into the background, executing the supplied command, and - * returns a socketpair made for bidirectional transfer with the children - * process. Using this strategy the parent may have better control, like - * shaping the network bandwidth of that process, and monitoring for network - * inactivity. Returns TRUE on success, with the and pointers - * initialized, or FALSE on failure. - */ -static bool exec_popen(int *sock, pid_t *pid, const char *path, - char * const *argv, rlim_t *rl, struct iface *ifaces, int nifaces, - struct ipaddr_node *ipn, struct sockaddr *addr, const char *ipaddr) -{ - int sv[2]; - - if (socketpair(AF_LOCAL, SOCK_STREAM, 0, &sv) == -1) { - syslog(LOG_NOTICE, "exec_popen() - socketpair() - (%s)", - strerror(errno)); - return FALSE; - } - if ((*pid = fork()) == -1) { - syslog(LOG_NOTICE, "exec_popen() - fork() - (%s)", - strerror(errno)); - (void) close(sv[0]); - (void) close(sv[1]); - return FALSE; - } else if (*pid > 0) { - /* Parent, close client side of socketpair, and return successfully. */ - *sock = sv[0]; - (void) close(sv[1]); - return TRUE; - } else { - struct sigaction act; - struct ifaces *tif; - - /* Child successfully launched, we must cleanup before using - * exec_command() - */ - -#ifndef __GLIBC__ - setproctitle("Client server process"); -#endif /* __GLIBC */ - - /* We create our own process group so that the process after execve(2) - * could not kill(0) causing the listerner process to exit. It also - * allows us to optionally not interrupt currently served client when - * we restart. - */ - (void) setsid(); - - /* Change signal handling policy. If we do not restore the signals we - * ignore to the default action, they will be ignored by the process - * after execve(2). Signals we are handling will automatically be - * restored to the default action. - */ - act.sa_handler = SIG_DFL; - act.sa_flags = 0; - (void) sigemptyset(&act.sa_mask); - - (void) sigaction(SIGTTOU, &act, NULL); - (void) sigaction(SIGTTIN, &act, NULL); - (void) sigaction(SIGTSTP, &act, NULL); - (void) sigaction(SIGPIPE, &act, NULL); - - /* Let children know and remember these */ - client.ipn = ipn; - client.saddr = *addr; - client.ipaddr = ipaddr; - /* Resolve if CONF.RESOLVE_ADDRESSES */ - client_resolve(); - - /* Close all filedescriptors which this process should - * not have access it is also safe to drop the locks now. - */ - for (tif = ifaces; *(tif->address) != '\0'; tif++) { - if (tif->sock != -1) { - (void) close(tif->sock); - tif->sock = -1; - } - } - shlocks_destroy(CONF.LOCK_PATH, locks, (enum shlocks)SHLOCK_MAX, - FALSE); - (void) close(server.runlock); - (void) close(server.alock); - - /* stderr is already redirected to "/dev/null". Make sure that stdin - * and stdout are redirected to the client socket, and to close the - * extraneous filedescriptor. - */ - (void) dup2(sv[1], 0); - (void) dup2(sv[1], 1); - (void) close(sv[1]); - (void) close(sv[0]); - - /* We can finally attempt to launch our external executable after - * setting the rlimits, if any. If this fails, the process exists, - * and the parent socket end will be disconnected, a condition which - * it will need to detect. - */ - exec_command(path, argv, rl); - /* NOTREACHED */ - } -} - - -/* Safely executes the specified command, or exits. - * This function never returns to the caller. - * The command line can be parsed using mmstring(3)'s mm_cmdparse() first. - * Perform sanity checking on the executable we will be launching. - * We want to make sure that it is executable, but read-only, and that - * it has no setuid/setgid bit set. We also ensure that it consists of - * an executable rather than a script requireing an interpreter. - * We also verify that the file is owned by the superuser. - * We perform no globbing whatsoever and do not want to use a shell. - */ -static void exec_command(const char *path, char * const *argv, rlim_t *rl) -{ - struct stat st; - mode_t mode; - int fd; - unsigned char buf[4]; - char *const env[1] = {NULL}; - - /* If an array of 9 rlim_t was passed, attempt to set the ones which - * aren't supplied with -1. - */ - if (rl != NULL) { - SETRLIMIT("RLIMIT_CORE", RLIMIT_CORE, rl[RL_CORE]); - SETRLIMIT("RLIMIT_CPU", RLIMIT_CPU, rl[RL_CPU]); - SETRLIMIT("RLIMIT_DATA", RLIMIT_DATA, rl[RL_DATA]); - SETRLIMIT("RLIMIT_FSIZE", RLIMIT_FSIZE, rl[RL_FSIZE]); - SETRLIMIT("RLIMIT_MEMLOCK", RLIMIT_MEMLOCK, rl[RL_MEMLOCK]); - SETRLIMIT("RLIMIT_NOFILE", RLIMIT_NOFILE, rl[RL_NOFILE]); - SETRLIMIT("RLIMIT_NPROC", RLIMIT_NPROC, rl[RL_NPROC]); - SETRLIMIT("RLIMIT_RSS", RLIMIT_RSS, rl[RL_RSS]); - SETRLIMIT("RLIMIT_STACK", RLIMIT_STACK, rl[RL_STACK]); - } - - /* First make sure that command exists and is a regular file */ - if (lstat(path, &st) != 0) { - syslog(LOG_NOTICE, "* exec_command() - lstat(%s) - (%s)", path, - strerror(errno)); - exit(EXIT_FAILURE); - } - if (!S_ISREG(st.st_mode)) { - syslog(LOG_NOTICE, "* exec_command() - Not regular file (%s)", path); - exit(EXIT_FAILURE); - } - /* Reject it if it has any setuid/setgid bit set */ - mode = st.st_mode & ~S_IFMT; - if (mode & S_ISUID) { - syslog(LOG_NOTICE, "* exec_command() - setuid file (%s)", path); - exit(EXIT_FAILURE); - } - if (mode & S_ISGID) { - syslog(LOG_NOTICE, "* exec_command() - setgid file (%s)", path); - exit(EXIT_FAILURE); - } - /* Has to be owned by root user for more safety */ - if (st.st_uid != 0) { - syslog(LOG_NOTICE, "* exec_command() - Not owned by uid 0 (%s)", path); - exit(EXIT_FAILURE); - } - /* access(2) is safer than testing the mode bits. Make sure that we cannot - * write to the file, but that we can read and execute it. - */ - if (access(path, W_OK) == 0) { - syslog(LOG_NOTICE, "* exec_command() - File writable (%s)", path); - exit(EXIT_FAILURE); - } - if (access(path, R_OK | X_OK) == -1) { - syslog(LOG_NOTICE, "* exec_command() - Not read/exec (%s)", path); - exit(EXIT_FAILURE); - } - /* Now make sure that it does not consist of a script. We only support - * ELF executables for now for simplicity (static or dynamic ones). - */ - if ((fd = open(path, O_RDONLY)) == -1) { - syslog(LOG_NOTICE, "* exec_command() - open(%s) - (%s)", path, - strerror(errno)); - exit(EXIT_FAILURE); - } - if (read(fd, buf, 4) != 4) { - syslog(LOG_NOTICE, "* exec_command() - read(%s) - (%s)", path, - strerror(errno)); - (void) close(fd); - exit(EXIT_FAILURE); - } - (void) close(fd); - if (buf[1] != 'E' || buf[2] != 'L' || buf[3] != 'F') { - syslog(LOG_NOTICE, "* exec_command() - Not ELF file (%s)", path); - exit(EXIT_FAILURE); - } - - /* Finally attempt execution */ - (void) execve(path, argv, env); - - /* If we reach this point, execve(2) failed, and we must use _exit(2) for - * safety in this case. - */ - _exit(EXIT_FAILURE); -} - - -/* If the maximum number of allowed connections has not been reached, increases - * the counter and returns TRUE. Returns FALSE otherwise, in which case the - * server should not allow more clients. - */ -static bool connection_open(const char *ipaddr) -{ - if (server.current_connections == 0 && server.alock != -1) { - /* Attempt to lock alock */ - if ((flock(server.alock, LOCK_EX | LOCK_NB)) == -1) { - syslog(LOG_NOTICE, "Refusing %s (ALOCK_PATH currently locked)", - ipaddr); - return FALSE; - } - } - if (server.current_connections < CONF.MAX_CONNECTIONS) { - server.current_connections++; - return TRUE; - } - syslog(LOG_NOTICE, "Refusing %s (MAX_CONNECTIONS reached = %d)", - ipaddr, server.current_connections); - - return FALSE; -} - - -/* Decreases the current number of total connections counter. */ -static void connection_close(void) -{ - if (server.current_connections > 0) - server.current_connections--; - else - DEBUG_PRINTF("connection_close", "server.current_connections < 1 !"); - if (server.current_connections == 0 && server.alock != -1) { - /* Release alock */ - (void) flock(server.alock, LOCK_UN); - } -} - - -/* These use shared memory and provide a nice API for use by the server. */ - -/* If necessary, adds the address to the list of currently active connections. - * If the address already was present, makes sure that it has not reached it's - * maximum number of allowed connections and connection rate limits, and - * increases it's counter. Returns a pointer to the ipaddr_node on success, - * or NULL if the server should reject the connection, in which case the - * reason and address are automatically logged via syslog(3) and mmstat(3). - */ -static struct ipaddr_node *ipaddr_open(struct sockaddr *saddr, - const char *ipaddr) -{ - struct ipaddr_node *ipn = NULL; - bool ok = FALSE; - time_t now = time(NULL); - - /* Make sure that we respect connection rates and limits for the addresses. - */ - if ((flock(locks[SHLOCK_IPADDR], LOCK_EX)) == 0) { - struct sockaddr_in *sinaddr = (struct sockaddr_in *)saddr; - - if ((ipn = (struct ipaddr_node *)hashtable_lookup(&shmem->ipaddr_table, - &sinaddr->sin_addr.s_addr, sizeof(u_int32_t))) - == NULL) { - /* Create new entry */ - if (HASHTABLE_NODES(&shmem->ipaddr_table) < CONF.MAX_ADDRESSES) { - if ((ipn = (struct ipaddr_node *)pool_alloc( - &shmem->ipaddr_pool, FALSE)) != NULL) { - /* Rate limiting and total connections initialization */ - LR_INIT(&ipn->lr, CONF.CONNECTION_RATE, - CONF.CONNECTION_PERIOD, now); - ipn->connections = 0; - /* Cache nodes initialization */ - ipn->address = *saddr; - *ipn->hostname = '\0'; - (void) hashtable_link(&shmem->ipaddr_table, - (hashnode_t *)ipn, - &((struct sockaddr_in *)&ipn->address)-> - sin_addr.s_addr, sizeof(u_int32_t)); - } else - DEBUG_PRINTF("ipaddr_open", "pool_alloc()"); - } else { - syslog(LOG_NOTICE, - "Refusing %s for exceeded number of addresses (%ld)", - ipaddr, CONF.MAX_ADDRESSES); - } - } - if (ipn != NULL) { - /* Either the node was found or successfully created */ - if (ipn->connections < CONF.MAX_PER_ADDRESS) { - if (CONF.CONNECTION_RATE > 0) { - if (lr_allow(&ipn->lr, 1, now, FALSE)) - ok = TRUE; - else { - syslog(LOG_NOTICE, - "Refusing %s for exceeded connection rate \ -(%ld connections in %ld seconds, %ld seconds left to clear)", - ipaddr, LR_POSTS(&ipn->lr), CONF.CONNECTION_PERIOD, - LR_REMAINS(&ipn->lr, now)); - } - } else - ok = TRUE; - } else { - syslog(LOG_NOTICE, "Refusing %s for exceeded number of \ -connections per address (%ld)", ipaddr, CONF.MAX_PER_ADDRESS); - } - } - - if (ok) - ipn->connections++; - else - ipn = NULL; - - (void) flock(locks[SHLOCK_IPADDR], LOCK_UN); - } - - return ipn; -} - - -/* Decreases the current number of connections for the specified address, and - * if necessary, frees the entry from the table if no more connections exist - * for this address. The node is expected to exist and to represent the right - * one obtained from ipaddr_open(). - */ -static void ipaddr_close(struct ipaddr_node *ipn) -{ - if (ipn != NULL) { - if ((flock(locks[SHLOCK_IPADDR], LOCK_EX)) == 0) { - ipn->connections--; - /* Leave the cache service process delete the entries, which - * allows us to keep statistics for rate limiting, as well as - * cache hostnames on a per-address basis. - */ - (void) flock(locks[SHLOCK_IPADDR], LOCK_UN); - } - } -} - - -/* Launches the ipaddress-hostname-rate cache entry expiration process, - * a kind of asynchroneous garbage collector. - */ -static pid_t launch_ipaddr_expire_process(uid_t uid, gid_t *gids, int ngids) -{ - pid_t pid = -1; - - if ((pid = fork()) == 0) { - struct sigaction act; - - /* Reset default signal action */ - act.sa_handler = SIG_DFL; - act.sa_flags = SA_NOCLDSTOP; - (void) sigemptyset(&act.sa_mask); - - (void) sigaction(SIGTERM, &act, NULL); - (void) sigaction(SIGINT, &act, NULL); - (void) sigaction(SIGCHLD, &act, NULL); - (void) sigaction(SIGSEGV, &act, NULL); - - /* Release runlock which only our parent should hold */ - (void) close(server.runlock); - - /* Drop privileges */ - if (!mmdropprivs(uid, gids, ngids)) { - syslog(LOG_NOTICE, "launch_ipaddr_expire_process() - Cannot \ -change uid and gids to safe privs"); - (void) kill(0, SIGTERM); - (void) exit(EXIT_FAILURE); - } - ipaddr_expire_process(); - /* NOTREACHED */ - } - - /* Parent */ - return pid; -} - - -/* A quick hashtable_hash() and memcmp() replacements which already deal with - * unique 32-bit values. - */ - -/* ARGSUSED */ -static u_int32_t key_hash32(const void *data, size_t len) -{ - return *((u_int32_t *)data); -} - -/* ARGSUSED */ -static int key_cmp32(const void *src, const void *dst, size_t len) -{ - return *((u_int32_t *)src) - *((u_int32_t *)dst); -} - -/* And to make things right, to not assume that pid_t is a u_int32_t, do - * the same. - */ - -/* ARGSUSED */ -static u_int32_t key_pidhash(const void *data, size_t len) -{ - return (u_int32_t)*((pid_t *)data); -} - -/* ARGSUSED */ -static int key_pidcmp(const void *src, const void *dst, size_t len) -{ - return *((pid_t *)src) - *((pid_t *)dst); -} - - -/* This process can run independantly and manage the ipaddr cache entry - * expiration events. - */ -static void ipaddr_expire_process(void) -{ - struct ipaddr_expire_process_iterator_udata data; - -#ifndef __GLIBC__ - setproctitle("Cache service process"); -#endif /* __GLIBC__ */ - - /* Set initial timeout to maximum allowed */ - data.soonest = CONF.CONNECTION_PERIOD; - for (;;) { - /* Sleep until it is known that at least one node expired */ - (void) sleep(data.soonest); - /* Tell our iterator function the current time and the maximum - * allowed time to wait to - */ - data.current = time(NULL); - data.soonest = CONF.CONNECTION_PERIOD; - /* Lock table, reset expired nodes, garbage collect, and set - * data.soonest to the delay of the soonest next expireing node. - */ - if ((flock(locks[SHLOCK_IPADDR], LOCK_EX)) == 0) { - if (HASHTABLE_NODES(&shmem->ipaddr_table) > 0) - hashtable_iterate(&shmem->ipaddr_table, - ipaddr_expire_process_iterator, &data); - (void) flock(locks[SHLOCK_IPADDR], LOCK_UN); - } - } - /* NOTREACHED */ -} - - -/* Internally used by the cache events expiration process */ -static bool ipaddr_expire_process_iterator(hashnode_t *hnod, void *udata) -{ - struct ipaddr_node *node = (struct ipaddr_node *)hnod; - struct ipaddr_expire_process_iterator_udata *data = udata; - time_t rem; - - /* If the node expired, reset it. For nodes which do not, record the - * soonest to expire node's delay. For those which expire and for which - * no connections exist anymore, expunge them. - */ - if ((rem = LR_REMAINS(&node->lr, data->current)) == 0) { - /* This entry expired */ - if (node->connections == 0) { - /* Safe to expunge this entry */ - hashtable_unlink(&shmem->ipaddr_table, (hashnode_t *)node); - (void) pool_free((pnode_t *)node); - } else { - /* Reset it */ - LR_EXPIRE(&node->lr, data->current); - rem = LR_REMAINS(&node->lr, data->current); - } - } - if (rem != 0 && data->soonest > rem) - data->soonest = rem; - - return TRUE; -} diff --git a/mmsoftware/mmspawnd2/GNUmakefile b/mmsoftware/mmspawnd2/GNUmakefile deleted file mode 100644 index c297288..0000000 --- a/mmsoftware/mmspawnd2/GNUmakefile +++ /dev/null @@ -1,22 +0,0 @@ -# $Id: GNUmakefile,v 1.2 2004/10/05 15:39:47 mmondor Exp $ - -MMLIBS := $(addprefix ../mmlib/,mmpool.o mmlog.o mmreadcfg.o mmstring.o \ -mmhash.o mmalarm.o mmheap.o mmlimitrate.o mmserver2.o) -OBJS := mmspawnd.o -CFLAGS += -Wall - -all: mmspawnd - -%.o: %.c - cc -c ${CFLAGS} -I. -I../mmlib -o $@ $< - -mmspawnd: $(MMLIBS) $(OBJS) - cc -o $@ $(OBJS) -lc $(MMLIBS) - -install: all - install -cs -o 0 -g 0 -m 500 mmspawnd /usr/local/sbin -# install -c -o 0 -g 0 -m 444 mmspawnd.conf.5 /usr/local/man/man5 -# install -c -o 0 -g 0 -m 444 mmspawnd.8 /usr/local/man/man8 - -clean: - rm -f mmspawnd $(OBJS) $(MMLIBS) diff --git a/mmsoftware/mmspawnd2/mmspawnd.8 b/mmsoftware/mmspawnd2/mmspawnd.8 deleted file mode 100644 index 5c892e1..0000000 --- a/mmsoftware/mmspawnd2/mmspawnd.8 +++ /dev/null @@ -1,160 +0,0 @@ -.\" $Id: mmspawnd.8,v 1.1 2005/11/17 13:32:24 mmondor Exp $ -.\" -.\" Copyright (C) 2003, Matthew Mondor -.\" All rights reserved. -.\" -.\" Redistribution and use in source and binary forms, with or without -.\" modification, are permitted provided that the following conditions -.\" are met: -.\" 1. Redistributions of source code must retain the above copyright -.\" notice, this list of conditions and the following disclaimer. -.\" 2. Redistributions in binary form must reproduce the above copyright -.\" notice, this list of conditions and the following disclaimer in the -.\" documentation and/or other materials provided with the distribution. -.\" 3. All advertising materials mentioning features or use of this software -.\" must display the following acknowledgement: -.\" This product includes software written by Matthew Mondor. -.\" 4. The name of Matthew Mondor may not be used to endorse or promote -.\" products derived from this software without specific prior written -.\" permission. -.\" 5. Redistribution of source code may not be released under the terms of -.\" any GNU Public License derivate. -.\" -.\" THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR -.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES -.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. -.\" IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, -.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -.\" BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF -.\" USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF -.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -.\" -.Dd October 28, 2003 -.Dt MMSPAWND 8 -.Os mmsoftware -.Sh NAME -.Nm mmspawnd -.Nd -.Xr inetd 8 -alternative for better security -.Sh SYNOPSIS -.Nm mmspawnd Op Ar config_file -.Sh DESCRIPTION -.Nm -is a useful replacement for -.Xr inetd 8 -when security is a concern. It only allows to serve one service per running -instance, but a different configuration file can be provided for each service -to run several ones as required. It supports connection number and rate limits -on a global and per-address basis, -.Xr chroot 2 -and -.Xr setrlimit 2 . It will only -.Xr bind 2 -to specified addresses/interfaces to listen for connections. -It also ensures to drop privileges definitely before starting operation, using -.Xr setgid 2 , -.Xr setuid 2 -and -.Xr setgroups 2 . -.Pp -Further, it comports a safe -.Xr execve 2 -wrapper which ensures to not pass unexpected arguments or environmental -variables, as well as to only execute ELF binaries which are owned by the -superuser and non-writable. No globbing whatsoever is performed by it, -and it only allows an absolute path to be provided to the application to -launch. It will not allow execution of files with the setuid or setgid -bits set. -.Pp -All connections are logged via the specified facility using -.Xr syslog 3 , -as well as the exit code of the application when closing the connection with -the client. Any security issue encoutered by the -.Xr execve 2 -wrapper is also logged. -.Pp -It's default configuration makes it harmless but useless. See the -.Xr mmspawnd.conf 5 -manual page for configuration file description. -.Pp -A good usage for -.Xr mmspawnd 8 -is for instance to run a CVS pserver in safe conditions. It then can be -restrited to the specified root directory, and access the files with read-only -access under an unprivileged user. If used for this purpose, also look at -the -.Xr mmanoncvs 8 -manual page. -.Pp -Another interesting feature is being able to specify if the currently running -clients should be interrupted or not if the -.Nm -server is to be stopped or restarted. In the case of a CVS checkout for -instance, if -.Nm KILL_CHILDREN -configuration option is FALSE, the operation will continue until it finishes -normally, even in the event of a server configuration change or restart. -.Pp -For additional security, only the superuser is allowed to launch -.Nm , -unless compiled with the -.Nm -DNODROPPRIVS -option, in which case it could be used by a nonprivileged user to bind -a service to a high port, and server refuse to be launched by root. -Other users attempting to launch the service will cause a line to be logged, -telling which user ID attempted the launch. -.Pp -When launched successfully, a status line is logged telling under which -user ID it runs, which port it listens to and on which addresses/interfaces, -as well as the configuration file location and service command. -.Pp -Note that this system redirects the stderr stream (filedescriptor 2) of -the command launched to serve the clients to the "/dev/null" device, so that -errors output from it be not disclosed to the remote user. -.Pp -Another optional interesting feature which it offers is the possibility -to use advisory locking on a special control file to synchronize the service -with another program. The lock is acquired in exclusive mode by -.Nm -when at least one connection exists being served. It is released when no -more connections remain. This way, another program can rely on the fact that -noone is using the service if it can obtain the lock. As long as the other -application holds the lock, the service will refuse new connections. Normal -service resumes immediately as the lock is released by the third party -application. Of course, it is important to properly configure the permissions -on that lock file, if the feature is enabled. -.Pp -Eventual support for bandwidth shaping/throttling and network inactivity -timeout limits are planned. However, as those will require a redesign and -rewrite of -.Nm , -all we support for now is total maximum timeout for execution of the supplied -service command. This is still better than -.Xr inetd 8 -behavior, however. -.Sh FILES -.Bl -tag -width indent -offset indent -.It Pa /usr/local/sbin/mmspawnd -The actual server binary -.It Pa /usr/local/etc/mmspawnd.conf -The default configuration file to read -.El -.Sh AUTHOR -.Nm -was written by Matthew Mondor, and is -Copyright (c) 2003, Matthew Mondor, All Rights Reserved. -.Sh SEE ALSO -.Xr mmspawnd.conf 5 , -.Xr bind 2 , -.Xr chroot 2 , -.Xr setrlimit 2 , -.Xr setuid 2 , -.Xr setgid 2 , -.Xr setgroups 2 , -.Xr mmanoncvs 8 , -.Xr inetd 8 . -.Sh BUGS -Please report any bug to mmondor@accela.net diff --git a/mmsoftware/mmspawnd2/mmspawnd.c b/mmsoftware/mmspawnd2/mmspawnd.c deleted file mode 100644 index 7e74fdf..0000000 --- a/mmsoftware/mmspawnd2/mmspawnd.c +++ /dev/null @@ -1,794 +0,0 @@ -/* $Id: mmspawnd.c,v 1.9 2005/03/01 15:18:21 mmondor Exp $ */ - -/* - * Copyright (C) 2004, Matthew Mondor - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Matthew Mondor. - * 4. The name of Matthew Mondor may not be used to endorse or promote - * products derived from this software without specific prior written - * permission. - * 5. Redistribution of source code may not be released under the terms of - * any GNU Public License derivate. - * - * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -/* - * TODO: - * - Test setrlimit(2) support - * - Support option to allow to send the stderr output of launched processes - * to a file for debugging. This however requires support in mmserver2(3) - * for this. This would only be for debugging, especially because of the - * fact that multiple processes could attempt to append at the end of the - * same file at once, concurrently. We can't really use a lock either, since - * we won't be controlling the output, unless we used a popen(3)-like - * system. - */ - - -#include -#include -#include -#include -#include -#include -#include /* strerror(3) */ -#include -#include - -#include -#include -#include -#include - - - -/* DEFINITIONS */ - -struct config { - char PROCTITLE[256], SYSLOG_FACILITY[32], PID_PATH[256], - LOCK_PATH[256], SHLOCKS_PATH[256], ALOCK_PATH[256], - ALOCK_USER[32], ALOCK_GROUP[32], ALOCK_MODE[4], ALOCK_ERRMSG[256], - CHROOT_DIR[256], COMMAND[256], USER[32], GROUPS[64], - LISTEN_TO[256]; - long COMMAND_MAX_ARGS, CHILDREN_INITIAL, CHILDREN_MINSPARE, - CHILDREN_MAXSPARE, CHILDREN_MAXIMUM, ADDRESS_CACHE_SIZE, - ADDRESS_CACHE_RATE_LIMIT, ADDRESS_CACHE_RATE_PERIOD, - ADDRESS_CACHE_CONCURRENCY_LIMIT, LIMIT_CORE, LIMIT_CPU, - LIMIT_DATA, LIMIT_FSIZE, LIMIT_MEMLOCK, LIMIT_NOFILE, - LIMIT_NPROC, LIMIT_RSS, LIMIT_STACK; - bool EXIT_KILL_CHILDREN, CHILDREN_SETSID, ADDRESS_RESOLVE, - ADDRESS_RATE_LIMITS, ADDRESS_CONCURRENCY_LIMITS, - EXECUTABLE_SECURITY, RLIMITS; -}; - - - -/* PROTOTYPES */ - -int main(int, char **); - -static int setsockopts(int); -static int config_read(const char *, uid_t *, gid_t **, int *); -static int lock_check(const char *); -static int alock_init(const char *, uid_t, gid_t, mode_t); -static int listen_to(const char *); -static int rlimit_security(void); -static int executable_security(const char *); -static int child_init_hook(void); -static void request_handler(struct server_request *); - - - -/* GLOBALS */ - -static struct config CONF; -static char daemon_title[64]; - -static int parent_alock; -static int child_alock_fd; -static size_t alock_errmsg_len; - -static char *command_line; -static char *command; -static int command_argc; -static char **command_argv; - -/* For mmreadcfg() */ -static carg_t cargs[] = { - {CAT_STR, CAF_NONE, 1, 63, "PROCTITLE", CONF.PROCTITLE}, - {CAT_STR, CAF_NONE, 1, 31, "SYSLOG_FACILITY", CONF.SYSLOG_FACILITY}, - {CAT_STR, CAF_NONE, 1, 255, "PID_PATH", CONF.PID_PATH}, - {CAT_STR, CAF_NONE, 1, 255, "LOCK_PATH", CONF.LOCK_PATH}, - {CAT_STR, CAF_NONE, 1, 255, "SHLOCKS_PATH", CONF.SHLOCKS_PATH}, - {CAT_STR, CAF_NONE, 1, 255, "ALOCK_PATH", CONF.ALOCK_PATH}, - {CAT_STR, CAF_NONE, 1, 31, "ALOCK_USER", CONF.ALOCK_USER}, - {CAT_STR, CAF_NONE, 1, 31, "ALOCK_GROUP", CONF.ALOCK_GROUP}, - {CAT_STR, CAF_NONE, 1, 3, "ALOCK_MODE", CONF.ALOCK_MODE}, - {CAT_STR, CAF_NONE, 1, 255, "ALOCK_ERRMSG", CONF.ALOCK_ERRMSG}, - {CAT_STR, CAF_NONE, 1, 255, "CHROOT_DIR", CONF.CHROOT_DIR}, - {CAT_STR, CAF_NONE, 1, 255, "COMMAND", CONF.COMMAND}, - {CAT_STR, CAF_NONE, 1, 31, "USER", CONF.USER}, - {CAT_STR, CAF_NONE, 1, 63, "GROUPS", CONF.GROUPS}, - {CAT_STR, CAF_NONE, 1, 255, "LISTEN_TO", CONF.LISTEN_TO}, - {CAT_VAL, CAF_NONE, 1, 63, "COMMAND_MAX_ARGS", - &CONF.COMMAND_MAX_ARGS}, - {CAT_VAL, CAF_NONE, 1, 1024, "CHILDREN_INITIAL", - &CONF.CHILDREN_INITIAL}, - {CAT_VAL, CAF_NONE, 1, 1024, "CHILDREN_MINSPARE", - &CONF.CHILDREN_MINSPARE}, - {CAT_VAL, CAF_NONE, 1, 1024, "CHILDREN_MAXSPARE", - &CONF.CHILDREN_MAXSPARE}, - {CAT_VAL, CAF_NONE, 1, 1024, "CHILDREN_MAXIMUM", - &CONF.CHILDREN_MAXIMUM}, - {CAT_VAL, CAF_NONE, 0, 4096, "ADDRESS_CACHE_SIZE", - &CONF.ADDRESS_CACHE_SIZE}, - {CAT_VAL, CAF_NONE, 0, 4096, "ADDRESS_CACHE_RATE_LIMIT", - &CONF.ADDRESS_CACHE_RATE_LIMIT}, - {CAT_VAL, CAF_NONE, 0, 1800, "ADDRESS_CACHE_RATE_PERIOD", - &CONF.ADDRESS_CACHE_RATE_PERIOD}, - {CAT_VAL, CAF_NONE, 0, 1024, "ADDRESS_CACHE_CONCURRENCY_LIMIT", - &CONF.ADDRESS_CACHE_CONCURRENCY_LIMIT}, - {CAT_VAL, CAF_NONE, 0, 0, "RLIMIT_CORE", &CONF.LIMIT_CORE}, - {CAT_VAL, CAF_NONE, 0, 0, "RLIMIT_CPU", &CONF.LIMIT_CPU}, - {CAT_VAL, CAF_NONE, 0, 0, "RLIMIT_DATA", &CONF.LIMIT_DATA}, - {CAT_VAL, CAF_NONE, 0, 0, "RLIMIT_FSIZE", &CONF.LIMIT_FSIZE}, - {CAT_VAL, CAF_NONE, 0, 0, "RLIMIT_MEMLOCK", &CONF.LIMIT_MEMLOCK}, - {CAT_VAL, CAF_NONE, 0, 0, "RLIMIT_NOFILE", &CONF.LIMIT_NOFILE}, - {CAT_VAL, CAF_NONE, 0, 0, "RLIMIT_NPROC", &CONF.LIMIT_NPROC}, - {CAT_VAL, CAF_NONE, 0, 0, "RLIMIT_RSS", &CONF.LIMIT_RSS}, - {CAT_VAL, CAF_NONE, 0, 0, "RLIMIT_STACK", &CONF.LIMIT_STACK}, - {CAT_BOOL, CAF_NONE, 0, 0, "EXIT_KILL_CHILDREN", - &CONF.EXIT_KILL_CHILDREN}, - {CAT_BOOL, CAF_NONE, 0, 0, "CHILDREN_SETSID", &CONF.CHILDREN_SETSID}, - {CAT_BOOL, CAF_NONE, 0, 0, "ADDRESS_RESOLVE", &CONF.ADDRESS_RESOLVE}, - {CAT_BOOL, CAF_NONE, 0, 0, "ADDRESS_RATE_LIMITS", - &CONF.ADDRESS_RATE_LIMITS}, - {CAT_BOOL, CAF_NONE, 0, 0, "ADDRESS_CONCURRENCY_LIMITS", - &CONF.ADDRESS_CONCURRENCY_LIMITS}, - {CAT_BOOL, CAF_NONE, 0, 0, "EXECUTABLE_SECURITY", - &CONF.EXECUTABLE_SECURITY}, - {CAT_BOOL, CAF_NONE, 0, 0, "RLIMITS", &CONF.RLIMITS}, - {CAT_END, CAF_NONE, 0, 0, NULL, NULL} -}; -static cmap_t cmap[] = { - {"LOG_AUTH", LOG_AUTH}, - {"LOG_AUTHPRIV", LOG_AUTHPRIV}, - {"LOG_CRON", LOG_CRON}, - {"LOG_DAEMON", LOG_DAEMON}, - {"LOG_FTP", LOG_FTP}, - {"LOG_LPR", LOG_LPR}, - {"LOG_MAIL", LOG_MAIL}, - {"LOG_NEWS", LOG_NEWS}, - {"LOG_SYSLOG", LOG_SYSLOG}, - {"LOG_USER", LOG_USER}, - {"LOG_UUCP", LOG_UUCP}, - {NULL, 0} -}; - - - -/* TEXT */ - -int -main(int argc, char **argv) -{ - struct server_config c; - uid_t uid, alock_uid; - gid_t *gids, alock_gid; - mode_t alock_mode; - int ngids, ch; - char *conf_file = "/usr/local/etc/mmspawnd.conf"; - - /* - * Make sure that only the superuser may launch this daemon - */ - if (getuid() != 0) { - (void) fprintf(stderr, - "Only can be started by the superuser\n"); - syslog(LOG_NOTICE, - "User %d attempted to launch mmspawnd with '%s'", - getuid(), conf_file); - exit(EXIT_FAILURE); - } - - /* - * Parse command line arguments - */ - while ((ch = getopt(argc, argv, "f:")) != -1) { - switch (ch) { - case 'f': - conf_file = optarg; - break; - case '?': - /* FALLTHROUGH */ - default: - (void) fprintf(stderr, - "usage: mmspawnd [-f ]\n"); - exit(EXIT_FAILURE); - } - } - argc -= optind; - argv += optind; - - /* - * Things we must do before calling chroot(2) - */ - - /* Read config file. This also calls openlog(3) for us */ - if (config_read(conf_file, &uid, &gids, &ngids) == -1) { - (void) fprintf(stderr, "Error reading configuration file." - " Verify syslog for more details.\n"); - exit(EXIT_FAILURE); - } - - /* Make sure that we're not already running */ - if (lock_check(CONF.LOCK_PATH) == -1) { - (void) fprintf(stderr, "Already running!? - %s\n", - strerror(errno)); - syslog(LOG_NOTICE, "Already running!? - %s", strerror(errno)); - exit(EXIT_FAILURE); - } - - /* - * Verify if ALOCK is enabled, and if so, make sure to get proper - * permissions settings to create it. - */ - if ((alock_uid = mmgetuid(CONF.ALOCK_USER)) == -1) { - syslog(LOG_NOTICE, "Unknown user '%s'", CONF.ALOCK_USER); - exit(EXIT_FAILURE); - } - if ((alock_gid = mmgetgid(CONF.ALOCK_GROUP)) == -1) { - syslog(LOG_NOTICE, "Unknown group '%s'", CONF.ALOCK_GROUP); - exit(EXIT_FAILURE); - } - alock_mode = (mode_t)strtol(CONF.ALOCK_MODE, NULL, 8); - if (alock_mode == 0 || alock_mode > 0770) { - syslog(LOG_NOTICE, "\nIllegal ALOCK_MODE '%03o'", alock_mode); - exit(EXIT_FAILURE); - } - - /* - * Chroot if wanted. Note that server_start() will chdir(2), but since - * we can setup the ALOCK, we chdir(2) here as well. - */ - if (*CONF.CHROOT_DIR != '\0') { - if (chroot(CONF.CHROOT_DIR) == -1) { - syslog(LOG_NOTICE, "chroot(%s) - %s", CONF.CHROOT_DIR, - strerror(errno)); - exit(EXIT_FAILURE); - } - (void) chdir("/"); - } - - /* - * Create ALOCK file with specified permissions, so that third party - * applications, such as mmanoncvs(8) be able to update the CVS - * repository without allowing corrupted CVS checkout/update results. - */ - if (alock_init(CONF.ALOCK_PATH, alock_uid, alock_gid, alock_mode) - == -1) - exit(EXIT_FAILURE); - - /* - * Bind network server ports. - */ - server_init(); - if (listen_to(CONF.LISTEN_TO) == -1) - exit(EXIT_FAILURE); - - /* - * Finally drop privileges. - */ - if (!mmdropprivs(uid, gids, ngids)) { - syslog(LOG_NOTICE, "Error dropping privileges - %s", - strerror(errno)); - exit(EXIT_FAILURE); - } - - /* - * Initialize and start server. New user must be able to create lock - * files as necessary where specified in SHLOCKS_PATH configuration - * directive. - */ - (void) mm_memclr(&c, sizeof(struct server_config)); - (void) mm_strcpy(c.locks_path, CONF.SHLOCKS_PATH); - *c.locks_user = '\0'; - *c.locks_group = '\0'; - c.locks_mode = 0600; - (void) mm_strcpy(c.null_path, "/dev/null"); - (void) mm_strcpy(c.pid_path, CONF.PID_PATH); - (void) mm_strncpy(c.proc_title, CONF.PROCTITLE, 31); - c.children_initial = CONF.CHILDREN_INITIAL; - c.children_minspare = CONF.CHILDREN_MINSPARE; - c.children_maxspare = CONF.CHILDREN_MAXSPARE; - c.children_maximum = CONF.CHILDREN_MAXIMUM; - c.children_average_seconds = 300; - c.children_maxrequests = 1000; - c.serialization_lock = TRUE; - c.exit_interrupt_requests = CONF.EXIT_KILL_CHILDREN; - c.children_setsid = CONF.CHILDREN_SETSID; - c.using_execve = TRUE; - c.parent_init_hook = NULL; - c.parent_exit_hook = NULL; - c.parent_sighup_hook = NULL; - c.parent_timer_hook = NULL; - c.parent_timer_seconds = 0; - c.child_init_hook = child_init_hook; - c.child_exit_hook = NULL; - c.child_sigalrm_hook = NULL; - if (server_start(&c) == -1) - exit(EXIT_FAILURE); - - /* NOTREACHED */ - exit(EXIT_SUCCESS); -} - -static int -config_read(const char *file, uid_t *uid, gid_t **gids, int *ngids) -{ - cres_t cres; - long facility; - - /* - * Set configuration defaults - */ - *CONF.PROCTITLE = '\0'; - (void) mm_strcpy(CONF.SYSLOG_FACILITY, "LOG_DAEMON"); - (void) mm_strcpy(CONF.PID_PATH, "/var/run/mmspawnd.pid"); - (void) mm_strcpy(CONF.LOCK_PATH, "/var/run/mmspawnd-lock"); - (void) mm_strcpy(CONF.SHLOCKS_PATH, "/var/run/mmspawnd-lock"); - *CONF.ALOCK_PATH = '\0'; - (void) mm_strcpy(CONF.ALOCK_USER, "mmspawnd"); - (void) mm_strcpy(CONF.ALOCK_GROUP, "mmspawnd"); - (void) mm_strcpy(CONF.ALOCK_MODE, "660"); - *CONF.ALOCK_ERRMSG = '\0'; - *CONF.CHROOT_DIR = '\0'; - (void) mm_strcpy(CONF.COMMAND, "/sbin/nologin"); - (void) mm_strcpy(CONF.USER, "mmspawnd"); - (void) mm_strcpy(CONF.GROUPS, "mmspawnd"); - (void) mm_strcpy(CONF.LISTEN_TO, "127.0.0.1:2323"); - CONF.CHILDREN_INITIAL = 5; - CONF.CHILDREN_MINSPARE = 5; - CONF.CHILDREN_MAXSPARE = 10; - CONF.CHILDREN_MAXIMUM = 32; - CONF.EXIT_KILL_CHILDREN = TRUE; - CONF.CHILDREN_SETSID = FALSE; - CONF.ADDRESS_RESOLVE = FALSE; - CONF.ADDRESS_RATE_LIMITS = FALSE; - CONF.ADDRESS_CONCURRENCY_LIMITS = FALSE; - CONF.ADDRESS_CACHE_SIZE = 0; - CONF.ADDRESS_CACHE_RATE_LIMIT = 0; - CONF.ADDRESS_CACHE_RATE_PERIOD = 0; - CONF.ADDRESS_CACHE_CONCURRENCY_LIMIT = 0; - CONF.EXECUTABLE_SECURITY = FALSE; - CONF.RLIMITS = FALSE; - CONF.LIMIT_CORE = -1; - CONF.LIMIT_CPU = -1; - CONF.LIMIT_DATA = -1; - CONF.LIMIT_FSIZE = -1; - CONF.LIMIT_MEMLOCK = -1; - CONF.LIMIT_NOFILE = -1; - CONF.LIMIT_NPROC = -1; - CONF.LIMIT_RSS = -1; - CONF.LIMIT_STACK = -1; - - /* - * Read and parse configuration file - */ - if (!mmreadcfg(&cres, cargs, file)) { - syslog(LOG_NOTICE, "Cannot read configuration file '%s'", - file); - return -1; - } - - /* - * Setup syslog. First perform sanity checking on supplied syslog - * facility string. - */ - if (!mmmapstring(cmap, CONF.SYSLOG_FACILITY, &facility)) { - syslog(LOG_NOTICE, "Invalid syslog facility '%s'", - CONF.SYSLOG_FACILITY); - return -1; - } - if (*CONF.PROCTITLE != '\0') - (void) snprintf(daemon_title, 63, "mmspawnd:%s", - CONF.PROCTITLE); - else - (void) mm_strcpy(daemon_title, "mmspawnd"); - openlog(daemon_title, LOG_PID | LOG_NDELAY, facility); - - /* - * Now do some sanity checking on supplied users and groups, we'll - * return those properly if they are valid. - */ - if ((*uid = mmgetuid(CONF.USER)) == -1) { - syslog(LOG_NOTICE, "Unknown user '%s'", CONF.USER); - return -1; - } - if (!(*gids = mmgetgidarray(ngids, CONF.GROUPS))) { - syslog(LOG_NOTICE, "At least one unknown group in '%s'", - CONF.GROUPS); - return -1; - } - - /* Prepare command line for execve(2) */ - if ((command_line = _mm_strdup(CONF.COMMAND)) == NULL) { - syslog(LOG_NOTICE, "Out of memory copying '%s'", - CONF.COMMAND); - return -1; - } - if ((command_argv = malloc( - sizeof(char *) * (CONF.COMMAND_MAX_ARGS + 2))) == NULL) { - syslog(LOG_NOTICE, "Out of memory allocating argv"); - return -1; - } - if (!mm_cmdparse(&command, &command_argc, command_argv, command_line, - CONF.COMMAND_MAX_ARGS + 2)) { - syslog(LOG_NOTICE, - "Error parsing COMMAND '%s', also check COMMAND_MAX_ARGS", - CONF.COMMAND); - return -1; - } - - /* Everything successful */ - return 0; -} - -/* - * Function to verify if we are already running. Every configuration should - * provide a lock file path to do this check. - */ -static int -lock_check(const char *file) -{ - int fd; - - if ((fd = open(file, O_CREAT | O_TRUNC | O_WRONLY, 0600)) != -1) { - if (flock(fd, LOCK_EX | LOCK_NB) == 0) - return 0; - close(fd); - } - - return -1; -} - -/* - * Set wanted setsockopt(2) on sockets. This function is called internally for - * each socket we create using server_socket_bind(). - */ -static int -setsockopts(int fd) -{ - struct linger l; - -#define SETSOCKOPT(l, o) do { \ - int _o = 1; \ - if ((setsockopt(fd, (l), (o), &_o, sizeof(int))) == -1) \ - return -1; \ -} while (/* CONSTCOND */0) - - SETSOCKOPT(SOL_SOCKET, SO_REUSEADDR); - SETSOCKOPT(SOL_SOCKET, SO_REUSEPORT); - SETSOCKOPT(SOL_SOCKET, SO_KEEPALIVE); - -#undef SETSOCKOPT - - l.l_onoff = 0; - l.l_linger = 0; - if ((setsockopt(fd, SOL_SOCKET, SO_LINGER, &l, - sizeof(struct linger))) == -1) - return -1; - - return 0; -} - -static int -alock_init(const char *file, uid_t uid, gid_t gid, mode_t mode) -{ - int fd; - - parent_alock = 0; - - if (*CONF.ALOCK_PATH == '\0') - return 0; - - if ((fd = open(CONF.ALOCK_PATH, O_WRONLY | O_CREAT, mode)) == -1) { - syslog(LOG_NOTICE, "create/open('%s') - %s", - CONF.ALOCK_PATH, strerror(errno)); - goto err; - } - if (fchmod(fd, mode) == -1) { - syslog(LOG_NOTICE, "chmod('%s',%03o) - %s", - CONF.ALOCK_PATH, mode, strerror(errno)); - goto err; - } - if (fchown(fd, uid, gid) == -1) { - syslog(LOG_NOTICE, "chown('%s',%d,%d) - %s'", - CONF.ALOCK_PATH, uid, gid, strerror(errno)); - goto err; - } - - (void) close(fd); - - if (*CONF.ALOCK_ERRMSG != '\0') - alock_errmsg_len = mm_strlen(CONF.ALOCK_ERRMSG); - else - alock_errmsg_len = 0; - - parent_alock = 1; - - return 0; - -err: - if (fd != -1) - (void) close(fd); - - return -1; -} - -/* - * Starts listening to all specified address:port pairs. Returns 0 on - * success, or -1 on error, logging the error via syslog(3). - */ -static int -listen_to(const char *addresses) -{ - char *string, *tstring; - char *entries[32], *cols[3]; - int i, nentries, err; - - string = tstring = NULL; - err = 0; - - if ((string = _mm_strdup(addresses)) == NULL) { - syslog(LOG_NOTICE, "listen_to() - Out of memory error"); - return -1; - } - - if ((nentries = mm_straspl(entries, string, 31)) < 1) { - syslog(LOG_NOTICE, "No
: specified in " - "LISTEN_TO configuration directive"); - err = -1; - goto end; - } - - for (i = 0; i < nentries; i++) { - struct server_socket_config sc; - - tstring = _mm_strdup(entries[i]); - if (mm_strspl(cols, entries[i], 2, ':') != 2) { - syslog(LOG_NOTICE, - "[%s] not a valid
: entry in " - "LISTEN_TO configuration directive", tstring); - err = -1; - goto end; - } - - (void) mm_memclr(&sc, sizeof(struct server_socket_config)); - sc.family = (mm_strchr(cols[0], ':') != NULL ? - AF_INET6 : AF_INET); - sc.type = SOCK_STREAM; - sc.port = (int)strtol(cols[1], NULL, 10); - sc.backlog = CONF.CHILDREN_MAXIMUM; - sc.create_stream = FALSE; - sc.address_resolve = CONF.ADDRESS_RESOLVE; - sc.address_rate_limits = CONF.ADDRESS_RATE_LIMITS; - sc.address_concurrency_limits = - CONF.ADDRESS_CONCURRENCY_LIMITS; - sc.address_cache_size = CONF.ADDRESS_CACHE_SIZE; - sc.address_cache_rate_limit = CONF.ADDRESS_CACHE_RATE_LIMIT; - sc.address_cache_rate_period = CONF.ADDRESS_CACHE_RATE_PERIOD; - sc.address_cache_concurrency_limit = - CONF.ADDRESS_CACHE_CONCURRENCY_LIMIT; - sc.packet_size = 0; - (void) mm_strcpy(sc.bind_address, cols[0]); - *sc.socket_user = '\0'; - *sc.socket_group = '\0'; - sc.socket_mode = 0; - sc.setsockopts = setsockopts; - sc.request_handler = request_handler; - sc.reject_handler = NULL; - sc.request_interrupt_hook = NULL; - sc.request_close_hook = NULL; - sc.user_data = NULL; - if (server_socket_bind(&sc) == -1) - syslog(LOG_NOTICE, "Error binding socket for [%s]", - tstring); - - free(tstring); - tstring = NULL; - } - -end: - if (string != NULL) - free(string); - if (tstring != NULL) - free(tstring); - - return err; -} - -static int -rlimit_security(void) -{ - struct rlimit rl; - -/* - * Use some preprocessor magic to shorten and clean up repetitive code: - */ - -#define SETRLIMIT(restxt, resource, limit) do { \ - if ((limit) != -1) { \ - rl.rlim_cur = rl.rlim_max = (rlim_t)(limit); \ - if (setrlimit((resource), &rl) == -1) { \ - syslog(LOG_NOTICE, \ - "* setrlimit(R%s, %ld) - (%s)", \ - (restxt), (limit), strerror(errno)); \ - return -1; \ - } \ - } \ -} while (/* CONSTCOND */0) - -#define SETRLIMIT2(n) SETRLIMIT(#n, R##n, CONF.n) - - SETRLIMIT2(LIMIT_CORE); - SETRLIMIT2(LIMIT_CPU); - SETRLIMIT2(LIMIT_DATA); - SETRLIMIT2(LIMIT_FSIZE); - SETRLIMIT2(LIMIT_MEMLOCK); - SETRLIMIT2(LIMIT_NOFILE); - SETRLIMIT2(LIMIT_NPROC); - SETRLIMIT2(LIMIT_RSS); - SETRLIMIT2(LIMIT_STACK); - -#undef SETRLIMIT2 -#undef SETRLIMIT - - return 0; -} - -static int -executable_security(const char *file) -{ - struct stat st; - mode_t mode; - - /* - * First make sure that command exists and consists of a regular file - */ - if (lstat(file, &st) != 0) { - syslog(LOG_NOTICE, "executable_security() - lstat('%s')", - file); - return -1; - } - if (!S_ISREG(st.st_mode)) { - syslog(LOG_NOTICE, - "executable_security() - '%s' not a regular file", - file); - return -1; - } - /* - * Now make sure that it has no setuid/setgid bits set - */ - mode = st.st_mode & ~S_IFMT; - if ((mode & S_ISUID) != 0) { - syslog(LOG_NOTICE, "executable_security() - '%s' is setuid", - file); - return -1; - } - if ((mode & S_ISGID) != 0) { - syslog(LOG_NOTICE, "executable_security() - '%s' is setgid", - file); - return -1; - } - /* - * Make sure it's owned by the superuser for more safety - */ - if (st.st_uid != 0) { - syslog(LOG_NOTICE, - "executable_security() - '%s' not owned by superuser", - file); - return -1; - } - /* - * access(2) is safer than testing mode bits in this case, make sure - * that file is not writable. - */ - if (access(file, W_OK) == 0) { - syslog(LOG_NOTICE, - "executable_security() - '%s' not readonly", file); - return -1; - } - - return 0; -} - -static int -child_init_hook(void) -{ - - /* - * If the ALOCK feature is enabled, attempt to open the lock file and - * store the file descriptor for request_handler() to be able to use - * it. - */ - if (parent_alock) { - if ((child_alock_fd = open(CONF.ALOCK_PATH, O_RDONLY)) - == -1) { - syslog(LOG_NOTICE, "Could not open ALOCK '%s' - %s", - CONF.ALOCK_PATH, strerror(errno)); - return -1; - } - } else - child_alock_fd = -1; - - return 0; -} - -/* - * Actual server request handler function, internally called by the - * mmserver2(3) system. We are allowed to use execve(2) here, since - * we specified so in the initial server configuration. If we do not - * do it for whatever reason, the process can be reused to serve other - * requests for better performance. If we do call execve(2), the process - * will die and be recycled. server_execve(), unlike execve(2), never returns, - * since it will internally call _exit(2) as necessary if execve(2) fails. - */ -static void -request_handler(struct server_request *r) -{ - - syslog(LOG_NOTICE, "Request from [%s]", r->client_address_name); - - /* - * Verify if we have a filedescriptor to the ALOCK file (if any). - * If so, attempt non-blocking read/shared advisory lock in it. - * If we can't obtain the lock, abort request, optionally sending a - * message over the socket before closing the client connection. - * If we successfully obtain the lock, we know that it will - * automatically be released when the process exits (which will happen - * after we call server_execve()). - */ - if (child_alock_fd != -1) { - if (flock(child_alock_fd, LOCK_SH | LOCK_NB) == -1) { - if (alock_errmsg_len != 0) - (void) write(r->client_socket, - CONF.ALOCK_ERRMSG, alock_errmsg_len); - syslog(LOG_NOTICE, - "Closing request to [%s] (ALOCK locked)", - r->client_address_name); - /* End current request immediately */ - server_close(); - } - } - - if (CONF.RLIMITS && rlimit_security() == -1) - return; - - if (CONF.EXECUTABLE_SECURITY && executable_security(command) == -1) - return; - - (void) server_execve(command, (char * const *)command_argv, NULL); - /* NOTREACHED */ -} diff --git a/mmsoftware/mmspawnd2/mmspawnd.conf.5 b/mmsoftware/mmspawnd2/mmspawnd.conf.5 deleted file mode 100644 index 3a4f438..0000000 --- a/mmsoftware/mmspawnd2/mmspawnd.conf.5 +++ /dev/null @@ -1,361 +0,0 @@ -.\" $Id: mmspawnd.conf.5,v 1.1 2005/11/17 13:32:24 mmondor Exp $ -.\" -.\" Copyright (C) 2003, Matthew Mondor -.\" All rights reserved. -.\" -.\" Redistribution and use in source and binary forms, with or without -.\" modification, are permitted provided that the following conditions -.\" are met: -.\" 1. Redistributions of source code must retain the above copyright -.\" notice, this list of conditions and the following disclaimer. -.\" 2. Redistributions in binary form must reproduce the above copyright -.\" notice, this list of conditions and the following disclaimer in the -.\" documentation and/or other materials provided with the distribution. -.\" 3. All advertising materials mentioning features or use of this software -.\" must display the following acknowledgement: -.\" This product includes software written by Matthew Mondor. -.\" 4. The name of Matthew Mondor may not be used to endorse or promote -.\" products derived from this software without specific prior written -.\" permission. -.\" 5. Redistribution of source code may not be released under the terms of -.\" any GNU Public License derivate. -.\" -.\" THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR -.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES -.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. -.\" IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, -.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -.\" BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF -.\" USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF -.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -.\" -.Dd October 28, 2003 -.Dt MMSPAWND.CONF 5 -.Os mmsoftware -.Sh NAME -.Nm mmspawnd.conf -.Nd -.Xr mmspawnd.conf 5 -configuration file for -.Xr mmspawnd 8 -.Sh DESCRIPTION -The -.Nm /usr/local/etc/mmspawnd.conf -file may contain one or more keyword/value pairs per line, empty lines or -comments. A ';' or '#' character causes all other characters to be considered -as a comment, and to be ignored, until the end of the current line. It is -very important to enclose the value for a keyword in double quotes ('"' -characters) if the following characters are found in it: ';', '#', space and -tab. It is allowed to use an equal sign '=' between the keyword name and it's -argument. Here are documented the various possible keywords and their -allowed values, as well as their defaults. -.Pp -.Ss Service administration parameters -.Pp -.Bl -tag -width indent -offset indent -.It Nm CHROOT_DIR Ar "directory" -If specified and non-empty, causes the daemon to use -.Xr chroot 2 -at initialization so that it becomes jailed into the wanted alternative root -directory. Obviously, all required files for the application should have a copy -within the new root (this may include files such as -.Nm /etc/resolv.conf , -.Nm /etc/hosts , -.Nm /etc/passwd , -.Nm /etc/group , -a few shared libraries, the executable binary to be launched, and so on, -as required by the application and libc. -.It Nm LOCK_PATH Ar "fullpath" -Specifies the location where the internal synchronization (and anti-recursive -runlock) are to be created (before chrooting). Should consist of an absolute -full pathname including a filename, after which will automatically be postfixed -extensions for the various locks. -When several server instances are run concurrently to serve multiple services, -the name of the lock files should be different for each to not conflict. -.It Nm PID_PATH Ar "fullpath" -Tells where to store the file holding the server process ID to be killed with -.Dv SIGTERM -(sig 15) to cause the server to cleanly exit. This is done before chrooting. -When several server instances are run concurrently to serve multiple services, -the name of the PID files should be different for each to not conflict. -.It Nm ALOCK_PATH Ar "fullpath" -If specified and non-empty, this enables a special feature of -.Nm mmspawnd -which allows advisory locking to be used on a special file with third -party applications to synchronize with the service. This obviously should -be configured with care, but is most useful with some setups. The file -will be created before calling -.Xr chroot 2 -and thus anywhere wanted on the filesystem. -.Pp -When this facility is enabled, -.Nm mmspawnd -uses -.Xr flock 2 -to obtain an exclusive advisory lock on that file whenever one or more -connections exist, still being served by the service. This means that -whenever another application is allowed to obtain the lock using the -same manner (it should also use exclusive mode), no clients are connected -anymore to the service, and the other application may be allowed to safely -perform special operations which requires the service to be busy. For as long -as the file remains locked by the third party application, the -.Nm mmspawnd -server will refuse connections from any users (it will in fact accept them -but immediately drop the connection without running the service). As soon -as the other application releases back the lock, normal operation is resumed. -.Pp -.Xr mmanoncvs 8 -for instance uses this feature during the short amount of time required for -two rename(2) operations to update the public cvs pserver read-only repository. -It is very important to make sure that permissions are properly set on the -lock file when using this feature, to only enable the wanted application to -obtain the lock (which requires at least read access to the file). See -the few next options to do this. -.It Nm ALOCK_USER Ar "user" -When the -.Nm ALOCK_PATH -is set, enabling the alock feature, this specifies the user which should be -set to be the owner of the lock file. -.It Nm ALOCK_GROUP Ar "group" -When the alock feature is enabled, tells which group should be set for the lock -file. -.It Nm ALOCK_MODE Ar "mode" -To be used when the alock feature is enabled. Specifies the permission mode -bits to apply to the lock file, so that only the intended application can use -it. -.It Nm USER Ar "user" -At server initialization, it drops privileges from the superuser to the -specified user, definitively. This can be specified as either a username -or it's user ID number. -.It Nm GROUPS Ar "group,..." -When dropping privileges, the process will become part of these groups. -More than one group may be specified, by name or ID, separated by commas, -without spaces. The first group will be set to the real process group, and -others as secondary access ones. -.It Nm LOG_FACILITY Ar "facility" -Syslog facility which should be used for error logging. Should normally be -one of -.Dv LOG_AUTH , LOG_AUTHPRIV , LOG_CRON, LOG_DAEMON , -.Dv LOG_FTP , LOG_KERN , LOG_LPR , LOG_MAIL , -.Dv LOG_NEWS , LOG_SYSLOG , LOG_USER -or -.Dv LOG_UUCP . -See -.Xr syslog 3 -man page for more information. -.It Nm COMMAND Ar "command" -Specifies the command which should be executed for each client which will -be served. There can be as many command line arguments as -.Nm MAX_ARGUMENTS -allows. The path to the executable to launch should be absolute. No globbing -or substitution of any kind is done on the arguments. It is allowed to -delimit arguments containing spaces or tabs into single quotes ('). -.It Nm MAX_ARGUMENTS Ar "number" -This parameter sets the maximum allowed number of command line parameters which -can be passed to the application in -.Sy COMMAND . -.It Nm PROCTITLE Ar "string" -This is useful if multiple services are served using -.Xr mmspawnd 8 -for commands such as -.Xr ps 1 , -notably on BSD systems. Where available, this causes the supplied string -to prefix the comments attached to the processes using -.Xr setproctitle 3 . -It also is appended to the daemon name for -.Xr syslog 3 -at -.Xr openlog 3 -time. -.El -.Ss TCP server administration -.Bl -tag -width indent -offset indent -.It Nm LISTEN_ADDRESSES Ar "address ..." -Tells to which interfaces the server should listen to, separated by spaces. -The arguments should be enclosed in double quotes if more than one address -is supplied. These addresses are used for -.Xr bind 2 . -Specifying "0.0.0.0" causes -.Nm mmspawnd -to listen to all interfaces. -.It Nm LISTEN_PORT Ar "number" -Supplies which TCP port number to listen to. This must be a numeric port number -within the range of 1 to 65535. -.It Nm MAX_CONNECTIONS Ar "number" -The maximum number of simultaneous clients which should be served at once. -.It Nm MAX_ADDRESSES Ar "number" -The maximum number of simultaneous different client IP addresses to serve. -.It Nm MAX_PER_ADDRESS Ar "number" -The maximum number of simultaneous clients to serve at once per IP address. -.It Nm CONNECTION_RATE Ar "number" -The maximum number of connections to accept from each address within -.Nm CONNECTION_PERIOD . -Can be 0 to disable connection rate throttling. -.It Nm CONNECTION_PERIOD Ar "number" -If -.Nm CONNECTION_RATE -is non-zero, specifies the number of seconds during which a maximum of -.Nm CONNECTION_RATE -connections are to be allowed. -.It Nm RESOLVE_ADDRESSES Ar "boolean" -Specifies weither client addresses should be resolved to hostnames when -logging the connection event via -.Xr syslog 3 . -An internal cache is maintained for recently connected addresses for faster -performance. This is done in the child serving process so that it does not -slow down the listener process responsible for accepting new connections. -Should be TRUE or FALSE. -.El -.Ss Application security limits -.Bl -tag -width indent -offset indent -.It Nm COMMAND_TIMEOUT Ar "seconds" -Specifies the maximum number of seconds allowed for complete execution of -.Nm COMMAND . -If the command does not terminate before this number of seconds elapse, it -is forcefully killed using the -.Dv SIGTERM -signal. Beware to put this limit high enough so that it does not interfere -with normal service operation. This feature however allows to get rid of -eternally idle or frozen sessions. -.Pp -Eventually, support for network inactivity timeout, as well as bandwidth -shaping/throttling will be added. However, since those require a new design -and a rewrite of -.Xr mmspawnd 8 , -this is the best we can do for now. (This behavior is still better than -.Xr inetd 8 , -however). We also ensure to set the -.Dv SO_KEEPALIVE -option on the client descriptors to better recognize connection problems and -act accordingly to stop the process. -.It Nm RLIMITS Ar "boolean" -If TRUE, all following -.Nm RLIMIT_* -parameters will be applied to the children processes before launching the -application command if they are not set to -1 values. -.Xr setrlimit 2 -is called to perform this. Note that these should be set to sane values -for proper function, by a competent administrator, if this option is -enabled. When disabled, or if enabled but for each following option specified -with -1, the defaults are used, which are inherited from the server process. -.Bl -tag -width indent -offset indent -.It Nm RLIMIT_CORE Ar "value" -If not -1, the largest size (in bytes) core file that may be created. -.It Nm RLIMIT_CPU Ar "value" -If not -1, The maximum amount of cpu time (in seconds) to be used by -each process. -.It Nm RLIMIT_DATA Ar "value" --1 or The maximum size (in bytes) of the data segment for a process; -this defines how far a program may extend its break with the -.Xr sbrk 2 -or -.Xr mmap 2 -system calls. -.It Nm RLIMIT_FSIZE Ar "value" -The largest size (in bytes) file that may be created, or -1. -.It Nm RLIMIT_MEMLOCK Ar "value" -The maximum size (in bytes) which a process may lock into physical memory -(wire) using the -.Xr mlock 2 -system call function, or -1. -.It Nm RLIMIT_NOFILE Ar "value" -If not -1, the maximum number of open files for this process. -.It Nm RLIMIT_NPROC Ar "value" -The maximum number of simultaneous processes for this user ID, or -1. -.It Nm RLIMIT_RSS Ar "value" -The maximum size (in bytes) to which a process's resident -set size may grow. This imposes a limit on the amount of -physical memory to be given to a process; if memory is -tight, the system will prefer to take memory from processes -that are exceeding their declared resident set size. --1 to use the defaults. -.It Nm RLIMIT_STACK Ar "value" -If not -1, the maximum size (in bytes) of the stack segment for a -process; this defines how far a program's stack segment -may be extended. Stack extension is performed automatically by the system. -.El -.El -.Ss Debugging support -.Bl -tag -width indent -offset indent -.It Nm STDERR_FILE Ar "fullpath" -By default, the standard error stream (stderr) of -.Nm COMMAND -is redirected to the "/dev/null" device. However, it may be nice to be able -to obtain those messages from time to time, or to see if any are generated -by the application. This option, if non-empty, creates, or appends to the -specified file (outside of the chroot setup). This option should not be -used on production systems, as the file will grow without bounds. If a log -rotation system is used, -.Xr mmspawnd 8 -will require to be restarted everytime it is ran. It is merely for debugging. -.El -.Sh DEFAULTS -The following defaults are used: -.Pp -.Bd -literal -offset indent -CHROOT_DIR "" -LOCK_PATH "/var/run/mmspawnd.lock" -PID_PATH "/var/run/mmspawnd.pid" -ALOCK_PATH "" -ALOCK_USER "mmspawnd" -ALOCK_GROUP "mmspawnd" -ALOCK_MODE "400" -USER "mmspawnd" -GROUPS "mmspawnd" -LOG_FACILITY "LOG_AUTHPRIV" -COMMAND "/sbin/nologin" -MAX_ARGUMENTS 16 -PROCTITLE "" - -LISTEN_ADDRESSES "127.0.0.1" -LISTEN_PORT 2323 -MAX_CONNECTIONS 32 -MAX_ADDRESSES 32 -MAX_PER_ADDRESS 1 -CONNECTION_RATE 5 -CONNECTION_PERIOD 30 -RESOLVE_ADDRESSES FALSE - -COMMAND_TIMEOUT 300 -RLIMITS FALSE -RLIMIT_CORE -1 -RLIMIT_CPU -1 -RLIMIT_DATA -1 -RLIMIT_FSIZE -1 -RLIMIT_MEMLOCK -1 -RLIMIT_NOFILE -1 -RLIMIT_NPROC -1 -RLIMIT_RSS -1 -RLIMIT_STACK -1 - -STDERR_FILE "" -.Ed -.Sh AUTHOR -.Nm mmspawnd -was written by Matthew Mondor, and is -Copyright (c) 2003, Matthew Mondor, All rights reserved. -.Sh FILES -.Bl -tag -width indent -offset indent -.It Pa /usr/local/etc/mmspawnd.conf -This file -.It Pa /usr/local/sbin/mmspawnd -The -.Xr mmspawnd 8 -server binary itself. -.El -.Sh SEE ALSO -.Xr mmspawnd 8 , -.Xr syslog 3 , -.Xr openlog 3 , -.Xr chroot 2 , -.Xr bind 2 , -.Xr setrlimit 2 , -.Xr ps 1 , -.Xr setproctitle 3 . -.Sh BUGS -Not really a bug, but tied to each interface could be most connection control -options. -.Pp -Please report any bug to mmondor@accela.net diff --git a/mmsoftware/mmstatd/ChangeLog b/mmsoftware/mmstatd/ChangeLog deleted file mode 100644 index fe9850c..0000000 --- a/mmsoftware/mmstatd/ChangeLog +++ /dev/null @@ -1,197 +0,0 @@ -$Id: ChangeLog,v 1.21 2005/11/17 07:38:07 mmondor Exp $ - - - -Release: mmstatd 0.0.9 devl -Date : November 17, 2005 -By : Matthew Mondor - -* Bug fixes - - Although the documentation in mmstatd.conf(5) specified that 0 could be - specified to STATS_RATE to disable connection rate throtling for statistic - requests, the configuration file parser forced a minimum value of 1. Fixed - - A free() bug was found in mmpool(3)'s pool_free(). - - The old string library which had functions copied as-is from my old code - of the 80's was througly revised. Many functions were either rewritten or - modified for better style and performance. Moreover, all library functions - have been throughly tested using a program especially made to test them. - A bug in mm_memcmp() and one in mm_memmove() were fixed along the way. -* Reliability enhancement - - Although mmstatd(8) always verified the error status of fwrite(3) calls - when synchronizing it's database, it now also calls fsync(2) after - fflush(3) before closing it, and verifies for the success of both fsync(2) - and close(2). - - Just as before, this is done on a temporary file, which is either - discarded on error, or which atomically replaces the old database - with rename(2) if everything was successful. Because stdio uses buffering - and that actual database sync was not guaranteed by fflush(3) unless - fsync(2) was successfully used, the new behavior should be safer. -* Performance enhancements - - The configuration file parser was rewritten to be more efficient and - to hold cleaner code than the previous one which was migrated from - one of my first C programs many years ago :) The configuration files - format is now more flexible as well, although the current files will - parse properly without modifications still. - - The mmstat(8) utility now uses full buffering when writing to stdout - and stderr for performance (stdio defaults to line-buffered streams which - caused a write(2) system call to occur per line, and is thus unefficient). - - mmpool(3) was reimplemented for better efficiency. Also was added support - for object constructor/destrutor functions, but which are not currently - used by this application. -* New features - - Both crash recovery log files and the database saving format are now - platform-independent as well as architecture-independent. This means that - byte order convertions to network byte order are performed for writing, - and convertions are made back to host byte order when reading. It also - means that all elements are written with a platform-independent fixed - sized format (i.e. u_int64_t for off_t, even on platforms where off_t is - 32-bit, u_int32_t for time_t and uid_t, etc). - This means that mmstatd(8) files can be stored via NFS and used by another - architecture or platform client, and that if moving the database and logs - to another architecture while upgrading the network or such, that their - format will be valid for use on the other system. - All older database formats are recognized and automatically converted when - mmstatd(8) is launched. However, this is not true for log files. This - means that prior to upgrading, you should cleanly kill, restart mmstatd(8) - and kill it again to ensure that all recovery logs have been processed and - flushed. This can be seen by an only log file named 00000000.log of 0 - bytes. It is then safe to upgrade mmstatd(8) and to run the new version. - It is however still recommended to backup the mmstatd.db file first, in - case of failure to convert it properly. - Client programs will also need to be recompiled nevertheless, since the - log_entry structure and unions have been modified to use - platform-independent sized elements, requireing internal modifications to - the mmstat(3) client library. -* Other - - BSD-style mdoc manual pages were enhanced. - - The mmlimitrate(3) library was written to restrict the amount of code - duplication among my various software which require rate limiting in - various situations. The software now uses this API for rate throttling - (except the bandwidth shaping which is handled by mmfd(3)). - - - -Release: mmstatd 0.0.8 devl -Date : July 1, 2003 -By : Matthew Mondor - -* Bug fixes - - Although unlikely to occur, the underlaying mmhash(3) library comported - a bug which could cause the key rotation to force a table rehash during - an iteration. This could cause some matching keys to not be processed. - Both hashtable_unlink() and hashtable_link() will now refrain from - causing table rehashes when called from within an iteration for safety. - - Some annoying debugging printf(3) was removed from the mmstat(8) client, - it was left out as part of a previous test. - - When alot of mmstat() operations were performed into a transaction using - mmstat_transact(), it was possible to exceed the maximum allowed message - size for the socket transmition to the mmstatd(8) server. We now use - 4.2BSD setsockopt(2) in both mmstat(3) client API and mmstatd(8) to set - the size of the socket buffer to fix this problem. -* Other - - The mmstat(3) key names were modified to be '|' separated rather than - '.' separated. Although I have been trying to avoid such a change which - obviously requires fixing alot of scripts I am using, it was proven with - time that the '.' character was too widely used and that '|' was ideal - as a replacement. It is not hard to use a script to use the mmstat(8) - reset command and convert all old entries to the new type if necessary. - As a result, filenames which comport '.' characters for which counters - are maintained are much better to handle and to isolate. The same applies - to IP addresses. This in itself did not require any changes to the - mmstat software, but mmstat(3) and mmstatd(8) users should be aware of - this. - - apache-mmstat(8) utility was added as an mmstat(3) backend for apache - httpd(8). -* Important - - The maximum key name length now was bumped from 128 to 256 bytes. As - a result, a special log file synchronization will be required as was - the case for mmstatd 0.0.7 upgrade. This also requires mmstat clients - using the mmstat(3) API to be recompiled. See the ChangeLog notes on - mmstatd 0.0.7 release about this synchronization issue when upgrading. - - The maximum number of protected mmstat() calls within an mmstat_transact() - transaction has been bumped from 16 to 32. This requires recompilation of - mmstat(3) clients and server. - - - -Release: mmstatd 0.0.7 devl -Date : June 19, 2003 -By : Matthew Mondor - -* Important - - The recovery log format has changed. This means that when upgrading, - you should first make sure that your actual database is in sync. - There are two ways to do this a) kill mmstatd using SIGTERM and - restart it again, or b) perform any rotation operation, which forces a - full sync. You can then kill mmstatd and upgrade it. Failure to follow - these instructions may result in mmstatd crashing, and yielding undefined - behavior. Moreover, both mmstat clients (the shell standard mmstat client, - as well as any other application using the mmstat(3) interface, and the - mmstatd server need to be recompiled when upgrading. - - MMSTAT, MMSTATENT and MMSTATRES data types were changed to mmstat_t, - mmstatent_t and mmstatres_t for consistency with other mmsoftware style. - This will require minor modifications to software using the mmstat(3) API. - - The GROUPS directive now expects groups to be comma-separated instead of - space-separated. The quotes then of course also become optional if multiple - groups are specified. This was made because that mmreadcfg(3) library is - also used by other of my projects for which comma-separated groups were - required. -* Performance enhancement - - The system used to hold the keys into a sequencial linked list using a - 64-bit hash coupled with the key strings to make the list lookup faster. - Although this worked fine and was ideal for very small sets, it would - become a bottleneck for the librarian process when in heavy use with - a very large number of entries. It now uses a 32-bit string hash function, - but also a bucket-based hashing algorithm to store the entries which - provides fast indexing, considerably speeding up lookups. Moreover, the - 32-bit string hashing function collisions will not cause storage - collisions and is faster than the 64-bit one used to be on 32-bit systems. - The mmhash(3) library is used for this, which was imported from my - Xisop project. -* Bug fixes - - The default shell mmstat client would always force autoflush on updates - even if the 'a' flag was not specified. - - Because full disk database synchronization happen rarely, when mmstatd - is stopped and restarted, if alot of recent modifications were made which - are in the recover logs, their modification timestamps would only be - considered when recovering from those logs at mmstatd startup. The time - of each operation is now stored within the logs as well, so that at - recovery be applied the real modification time to the affected keys. - - - -Release: mmstatd 0.0.6 devl -Date : January 9, 2003 -By : Matthew Mondor - -* Bug fix - - Delete operations performed on unexisting keys would crash the daemon, - this was fixed. -* New features - - mmstat_init() was modifed in a way that autoflush keys (which delete - themselves automatically when reaching zero) can now consist of both - volatile or persistant keys. - - The various STAT_RESET, STAT_UPDATE and STAT_DELETE operations can - now be atomically performed on all keys matching a supplied '*' and '?' - wildcard pattern as well. - - The mmstat(8) utility now supports 'hreport' which reports more readable - results for humans (although less verbose). - - If compiled with -DNODROPPRIVS it will not attempt to perform any - credential changes before launching. In which case it will also refuse - to be started by the superuser. Userful for non-privileged users. - - The mmstatd, mmstat(3) library and as such mmstat(8) utility will now use - the MMSTATCONF environment variable if set to determine which configuration - file to use. This permits several non-privileged users to each run their - own mmstat service for instance. - - mmstatd can now be specified configuration file to use on the command - line arguments. - - - -Release: mmstatd 0.0.7 devl -Date : April 25, 2003 -By : Matthew Mondor - -* Minor interface change - - MMSTAT, MMSTATENT and MMSTATRES data types were changed to mmstat_t, - mmstatent_t and mmstatres_t for consistency with other mmsoftware style. diff --git a/mmsoftware/mmstatd/GNUmakefile b/mmsoftware/mmstatd/GNUmakefile deleted file mode 100644 index cfabd2d..0000000 --- a/mmsoftware/mmstatd/GNUmakefile +++ /dev/null @@ -1,34 +0,0 @@ -# $Id: GNUmakefile,v 1.4 2004/06/01 23:52:43 mmondor Exp $ - -MMLIB_PATH := ../mmlib - -MMLIBS := $(addprefix ${MMLIB_PATH}/,mmarch.o mmpool.o mmlog.o mmreadcfg.o \ -mmstring.o mmhash.o mmstat.o mmlimitrate.o) -OBJS := $(addprefix src/,mmstatd.o mmstat.o) -BINS := $(addprefix src/,mmstatd mmstat) -CFLAGS += -Wall - -all: $(BINS) - -%.o: %.c - cc -c ${CFLAGS} -I. -I${MMLIB_PATH} -Isrc -o $@ $< - - -src/mmstatd: $(MMLIBS) src/mmstatd.o - cc -o $@ src/mmstatd.o -lc $(MMLIBS) - -src/mmstat: $(MMLIBS) src/mmstat.o - cc -o $@ src/mmstat.o -lc $(MMLIBS) - - -install: all - install -cs -o 0 -g 0 -m 500 src/mmstatd /usr/local/sbin - install -cs -o 0 -g 0 -m 550 src/mmstat /usr/local/sbin - install -c -o 0 -g 0 -m 444 src/mmstatd.conf.5 /usr/local/man/man5 - install -c -o 0 -g 0 -m 444 src/mmstat.8 /usr/local/man/man8 - install -c -o 0 -g 0 -m 444 src/mmstatd.8 /usr/local/man/man8 - install -c -o 0 -g 0 -m 444 ../mmlib/mmstat.3 /usr/local/man/man3 - - -clean: - rm -f $(BINS) $(OBJS) $(MMLIBS) diff --git a/mmsoftware/mmstatd/README b/mmsoftware/mmstatd/README deleted file mode 100644 index c797aa6..0000000 --- a/mmsoftware/mmstatd/README +++ /dev/null @@ -1,165 +0,0 @@ -$Id: README,v 1.3 2003/06/19 15:03:29 mmondor Exp $ - -MMSTATD(8) NetBSD System Manager's Manual MMSTATD(8) - -NAME - mmstatd - Statistics librarian and logger daemon with recovery and trans- - action support - -SYNOPSIS - mmstatd [config_file] - -DESCRIPTION - mmstatd consists of a statistics librarian daemon used by the familly of - mm utilities (mmmail, mmftpd, etc). It simply allows applications to - record statistical information using a key-based system. It also supports - transactions and crash recovery logging. Moreover, it features volatile - and consistant database entries, with creation and modification times- - tamps. - - The main reason why I wrote it was because syslog does not consist of an - ideal approach to count statistics, and that using an SQL server for this - is overkill, especially with required SQL commands parsing and atomic- - safe transaction locks. db4 could have been more efficient, but it is - quite easy to rival with it according to size. The size of mmstatd and - mmstat library is very small, and it's performance is decent. Moreover a - database server would consider all keys as persistant storage, when a re- - quirement for both persistant and volatile ones was met. - - The way it works - - Basically, it starts up two asynchroneous processes, the librarian and - logger. - - The librarian is responsible for managing the database and affecting - changes while asynchroneously reading available logs. It also permits ob- - tention of statistics report connecting to a Unix domain stream socket. - The librarian synchronizes the memory database to disk every once in a - while, recording the current position in the logs, and deleting obsolete, - already synchronized logs when required. This synchronization to disk is - only performed at large time intervals, to minimize CPU and drive load, - and maximise responsiveness. The librarian also establishes a listening - UNIX stream socket, to serve reports and rotation requests. - - The logger, in turn, listens to a Unix datagram socket, and writes the - logs, syncing them to disk frequently enough, for both the librarian and - logger. This allows the logs to be used for recovery of persistant modi- - fications that were performed since the last full database sync. More- - over, it permits the clients to always be able to send more packets with- - out being subject to the librarian processing, avoiding a bottleneck. - - Every time mmstatd is started, it first ensures to run with normal user - privileges, then looks for recovery logs and performs necessary modifica- - tions to the database, before forcing a sync of the new database to disk, - deleting all recovery logs. It then launches the two asynchroneous pro- - cesses which then become ready to serve their tasks. - - Using this design allows applications to use a simple syslog-like API to - update statistics, in a very fast manner. For the various mm daemons, it - serves as a who database, using volatile storage, and as various statis- - tical tasks using persistant storage. - - The logging socket, used to send update requests, as well as the status - socket, are independant and can have specific permissions, so that only - applications who should access the service may. The user application in- - terface library is quite simple to use by programs wanting to keep their - statistics using mmstatd. See the mmstat(3) man page for more informa- - tion. - -INSTALLATION - mmstatd is installed by the make.sh scripts of both mmftpd and mmmail by - Matthew Mondor. It's administration is made through /etc/mmstatd.conf - (See mmstatd.conf(5) man page) and mmstat (See mmstat(8) man page). - - Here is an overview of standard permissions various files should have: - - Permissions Owner Group File - - -rwxr-x--- root staff /usr/local/sbin/mmstat - -rwx------ root wheel /usr/local/sbin/mmstatd - - drwxr-x--- mmstatd mmstat /var/mmstatd - - s-w--w---- mmstatd mmstat /var/mmstatd/mmstatd_log.sock - srw-rw---- mmstatd staff /var/mmstatd/mmstatd_stat.sock - - Basically, the administrator will need access to mmstat binary as well as - to both /var/mmstatd/*.sock files. Applications using the mmstat(3) fa- - cility require access to the /var/mmstatd/mmstatd_log.sock file only. On- - ly uid zero should have access to /var/mmstatd directory and mmstatd dae- - mon. - - mmstatd should normally be started before any other daemons using the - mmstat(3) interface. Although this is not obligatory, it is highly recom- - mended. As it will take the time to perform recovery before calling - fork(2) other daemons will have a clean and ready daemon to serve their - requests, so a startup script typically would use something like: - - /usr/local/sbin/mmstatd - /usr/local/sbin/mmftpd - /usr/local/sbin/mmsmtpd - /usr/local/sbin/mmpop3d - -FILES - /etc/mmstatd.conf This file consists of the configuration - file for mmstatd and controls all it's - configurable parameters. - - /var/mmstatd The mmstat environment directory, where - recovery logs and database file are lo- - cated. The default configuration also - stores UNIX domain sockets there. - - /var/mmstatd/mmstatd_stat.sock The UNIX stream socket on which listens - mmstatd librarian process, used by the - administrator via mmstat binary to query - statistical reports and request key rota- - tions. - - /var/mmstatd/mmstatd_log.sock The UNIX datagram socket on which listens - mmstatd logger process, used by all ap- - plications using the mmstat(3) interface - to update statistics. - - /var/mmstatd/mmstatd.db The database file used to permanently - store persistant storage statistical - keys. When a disk synchronization occurs, - a temporary file is used to save the - database on the same filesystem, and - rename(2) is used to move it over this - file in an atomic manner. - - /var/mmstatd/????????.log Log files internally used and automati- - cally maintained by mmstatd for crash re- - covery. - - /usr/local/sbin/mmstatd The actual daemon binary documented by - mmstatd(8) - - /usr/local/sbin/mmstat The administration utility documented by - mmstat(8) - -ENVIRONMENT - MMSTATCONF If set, specifies the absolute location of the configuration - file to load instead of the default /etc/mmstatd.conf. If a - configuration file name is specified on the command line, it - will override this environment variable even if it was set. - -AUTHOR - The suite of mmstat daemon, related utilities and documentation were - written by Matthew Mondor, and are Copyright (c) 2002-2003, Matthew Mon- - dor, All rights reserved. They were developped under NetBSD 1.5.3 for - the mmftpd and mmmail suite of daemons from the same author. - -SEE ALSO - mmstat(3), mmstatd.conf(5), mmstat(8), rename(2), fork(2), unix(4). - -BUGS - The internal format of the log files and database file are not endian-in- - dependant and will therefore only remain useable on architectures using - the same endian format. Of course it would be possible to use mmstat(8) - utility to perform exportation and importation to a new system for the - time being. This will eventually change and an endian-independant format - will be used. - -NetBSD 1.6_STABLE 11 Dec, 2002 3 diff --git a/mmsoftware/mmstatd/clean.sh b/mmsoftware/mmstatd/clean.sh deleted file mode 100755 index 77bfe13..0000000 --- a/mmsoftware/mmstatd/clean.sh +++ /dev/null @@ -1,16 +0,0 @@ -#!/bin/sh -# $Id: clean.sh,v 1.2 2003/07/02 17:20:57 mmondor Exp $ - -. ../mmlib/makefuncs.sh - -cd ../mmlib/ -clean mmlib -cd ../ - -cd mmstatd/src/ -clean mmstatd -cd ../../ - -cd apache-mmstat/ -clean apache-mmstat -cd ../ diff --git a/mmsoftware/mmstatd/etc/mmstatd.conf b/mmsoftware/mmstatd/etc/mmstatd.conf deleted file mode 100644 index 0fe9a44..0000000 --- a/mmsoftware/mmstatd/etc/mmstatd.conf +++ /dev/null @@ -1,72 +0,0 @@ -# $Id: mmstatd.conf,v 1.3 2004/02/15 16:56:22 mmondor Exp $ - -# This consists of the configuration file used by both mmstatd service -# and mmstat library initialization. See mmstatd.conf(5) man page for details. - - - -# User mmstatd should run as (usually mmstatd) -USER mmstatd -# Groups mmstatd should be part of, separated by commas -# LOG_GROUP and STAT_GROUP should be specified here as well -GROUPS mmstat,staff - - - -# syslog facility which should be used -LOG_FACILITY LOG_AUTHPRIV - - - -# Location where mmstatd writes it's pid file -PID_FILE "/var/mmstatd/mmstatd.pid" -# -# Location of mmstatd lock file, this is only used to make sure that only -# one copy of the service is running, otherwise this could lead to database -# corruption -LOCK_FILE "/var/mmstatd/mmstatd.lock" -# -# Location of statistics update request socket -LOG_SOCKET "/var/mmstatd/mmstatd_log.sock" -# -# Location of statistics report request socket -STAT_SOCKET "/var/mmstatd/mmstatd_stat.sock" -# -# Location where database and recovery logs are stored -ENV_DIR "/var/mmstatd" - - - -# LOG_SOCKET is created with mode 220. This permits users of the following -# specified group to perform statistic update requests. -LOG_GROUP mmstat -# -# STAT_SOCKET is created with mode 660. This permits users of the following -# specified group to obtain statistic reports. -STAT_GROUP staff - - - -# Specifies the interval in seconds at which the statistics db will be -# synchronized to disk. This delay can be long enough, as a good log-based -# recovery technique is used in case system crashed between two sync events. -SYNC_INTERVAL 1800 -# -# Maximum number of bytes to write to recovery logs before forcing a sync -# with physical media (using fsync()). 0 Can be specified to force a sync -# after every new entry; Higher values may cause some of the last update -# requests before a crash to be lost but will be more efficient. -SYNC_BYTES 4096 -# -# Maximum size of a recovery log file. When reaching that size internal -# rotation to other files is performed. Logs are internally maintained -# and cleaned up as necessary by mmstatd and are not user serviceable. -MAX_LOGSIZE 1048576 -# -# Maximum rate to accept statistic report requests at. This # prevents an -# application from requesting a large number of full reports thus potentially -# preventing the librarian part of mmstatd to peform it's # vital tasks. As a -# general rule reports are not requested frequently. Care should be taken to -# choose an adequate STAT_GROUP to restrict access also. STATS_RATE specifies -# maximum number of requests to accept in STATS_TIME seconds. -STATS_RATE 0 STATS_TIME 10 diff --git a/mmsoftware/mmstatd/future.txt b/mmsoftware/mmstatd/future.txt deleted file mode 100644 index 064857b..0000000 --- a/mmsoftware/mmstatd/future.txt +++ /dev/null @@ -1,70 +0,0 @@ -We could either use xdr(3) to keep a binary format, but we also optionally -could fallback to text formats (which actually also would allow other programs -to easily work with the format using simple scripts). The adventage of a text -format is that it is endian-independant. A disadventage is that more CPU time -is required; We need to convert the internal binary representation to an -ASCII text form, and need to convert that ASCII format to internal binary -numbers again. - -If using xdr(3), we should still change the current format to not use -sizeof(off_t) (which appears to be 32-bit on i386 Linux, while 64-bit on BSD). -We instead should map the off_t type to an internal u_int64_t representation -and then save that one instead. - - - -Database file format: - -5 (version) -0 (lognum) -20493 (logpos) -(value) (created) (modified) (uid) (key) -33 1057972228 1066870567 0 mmmail|box|mmondor@gobot.xisop|messages-out -3090 1057972167 1066870567 0 mmpop3d|total|bytes-in -(etc) - - - -Logentry format: - -TRANSACT (begin)1 -TRANSACT (begin)0 (commit/rollback)1 -NEWFILE 1 (lognum) -UPDATE (time)1066870567 (persistant)1 (autoflush)1 (modifier)1 (key)mmpop3d|total|bytes-in -RESET (time)1066870567 (persistant)1 (autoflush)1 (value)0 (key)mmpop3d|total|bytes-in -DELETE (persistant)1 (key)mmpop3d|total|bytes-in - - - -Report format: - -(persistant) (value) (created) (modified) (uid) (key) -1 33 1057972228 1066870567 0 mmmail|box|mmondor@gobot.xisop|messages-out -1 3090 1057972167 1066870567 0 mmpop3d|total|bytes-in - - - -It might be useful to add support for namespaces. It then would also be -possible to use simple authentication to the daemon for clients to be -able to perform modifications. If this was done using a stream rather -than datagrams, we then could also support fair remote service. -Look into libio and libevent so that the logger daemon could support a -wide range of persistant connections. I also could add optional rc4 support... -Unfortunately, all this would slow the system a little on the client side. -Using blocking writes to a stream would sometimes cause a delay, and using -unblocking writes would still cause looping as long as EAGAIN is issued... -It perhaps would be possible for the client side to keep a buffer for speed -and flush it as it can asynchroneously. However, this could possibly degrade -the service reliability (atomic transactions reliability?) also, what would -happen if the TCP connection is stalled? Would EAGAIN always be issued and -the buffer fill? If so, what should we do? Cause the process to sleep until -it can send the data? This has potentially more drawbacks than it sounds. -Or, should we keep using unreliable datagrams and not care about possible -problems, except about transaction respect? - -It is generally not a problem to have to perform conversions when reading -or writing the database. It also might be convenient so that the format -be easily manipulable by the administrator. However, the internal storage -such as recovery logs and report format would better be implemented using -a binary format like we use now, although an effort should be made to -make the format endian-independant. diff --git a/mmsoftware/mmstatd/install.sh b/mmsoftware/mmstatd/install.sh deleted file mode 100755 index cd5bc23..0000000 --- a/mmsoftware/mmstatd/install.sh +++ /dev/null @@ -1,124 +0,0 @@ -#!/bin/sh -# $Id: install.sh,v 1.6 2003/10/23 01:01:32 mmondor Exp $ - -if [ "$1" = "help" ]; then - echo - echo 'You can optionally set the following environment variables' - echo 'to customize the installation process. For each is shown an' - echo 'example using the default value followed by a breif description.' - echo - echo 'export MMLAUNCH="TRUE"' - echo ' Tells the install process to immediately launch the daemon(s)' - echo ' They are automatically killed before the binaries are copied' - echo ' over the old ones as is necessary with some systems. The' - echo ' default is to not start them back automatically.' - echo - echo 'export MMPREFIX="/usr/local"' - echo ' Allows to set the installation base directory. All files but' - echo ' configuration ones will be installed in directories relative' - echo ' to this one.' - echo - echo 'export MMCONFDIR="/etc"' - echo ' Directory in which configuration files should be stored.' - echo - echo 'export MMDEFAULTUSER="0"' - echo ' User new files and directories should be owned by, using' - echo ' user id or name.' - echo - echo 'export MMDEFAULTGROUP="0"' - echo ' Group new files and directories should be under, using id' - echo ' or name.' - echo - echo 'export MMADMINGROUP="staff"' - echo ' The administrators group, these can for instance view and the' - echo ' rotate mmstat statistics and execute mmpasswd. Will be' - echo ' created automatically if necessary.' - echo - echo 'export MMSTATDIR="/var/mmstatd"' - echo ' The directory in which mmstatd will store the stats database' - echo ' and log files.' - echo - echo 'export MMSTATDUSER="mmstatd"' - echo ' The user mmstatd will run under, to be automatically created.' - echo - echo 'export MMSTATDGROUP="mmstat"' - echo ' Group the mmstatd user should be part of, automatically' - echo ' created.' - echo - exit -fi - -# Set defaults if not set -if [ -z "$MMLAUNCH" ]; then - export MMLAUNCH='FALSE' -fi -if [ -z "$MMPREFIX" ]; then - export MMPREFIX='/usr/local' -fi -if [ -z "$MMCONFDIR" ]; then - export MMCONFDIR='/etc' -fi -if [ -z "$MMDEFAULTUSER" ]; then - export MMDEFAULTUSER='0' -fi -if [ -z "$MMDEFAULTGROUP" ]; then - export MMDEFAULTGROUP='0' -fi -if [ -z "$MMADMINGROUP" ]; then - export MMADMINGROUP='staff' -fi -if [ -z "$MMSTATDIR" ]; then - export MMSTATDIR='/var/mmstatd' -fi -if [ -z "$MMSTATDUSER" ]; then - export MMSTATDUSER='mmstatd' -fi -if [ -z "$MMSTATDGROUP" ]; then - export MMSTATDGROUP='mmstat' -fi - -. ../mmlib/makefuncs.sh - -instgroup $MMADMINGROUP - -cd ../mmlib/ -instman mmlist.3 3 -instman mmpool.3 3 -instman mmstat.3 3 -instman mmhash.3 3 -cd ../ -cd apache-mmstat/ -instman apache-mmstat.8 8 -cd ../ - -cd mmstatd/src/ -instuser $MMSTATDUSER $MMSTATDGROUP -killbin mmstatd -instbin mmstatd 700 -instbin mmstat 750 $MMADMINGROUP -instman mmstat.8 8 -instman mmstatd.8 8 -instman mmstatd.conf.5 5 -instman mmlist.3 3 -instman mmpool.3 3 -instman mmhash.3 3 -instman mmlimitrate.3 3 -cd ../etc/ -instconf mmstatd.conf 640 $MMSTATDGROUP -instdir $MMSTATDIR 750 $MMSTATDUSER $MMSTATDGROUP -if [ "$MMLAUNCH" = "TRUE" ]; then - startbin mmstatd $MMCONFDIR/mmstatd.conf -fi -cd ../../ -cd apache-mmstat/ -instbin apache-mmstat 700 - -echo -echo "*** Please read the following man pages ***" -echo -echo "mmstat(8), mmstatd(8), mmstatd.conf(5), apache-mmstat(8)" -echo "source auditors: mmstat(3), mmlist(3), mmpool(3), mmhash(3)," -echo " mmlimitrate(3)" -echo -echo "Thank you for using mmsoftware." -echo diff --git a/mmsoftware/mmstatd/make.sh b/mmsoftware/mmstatd/make.sh deleted file mode 100755 index d846943..0000000 --- a/mmsoftware/mmstatd/make.sh +++ /dev/null @@ -1,23 +0,0 @@ -#!/bin/sh -# $Id: make.sh,v 1.2 2003/07/02 17:20:57 mmondor Exp $ - -. ../mmlib/makefuncs.sh - -cd ../mmlib/ -clean mmlib -makebin mmlib -cd ../ - -cd mmstatd/src/ -clean mmstatd -makebin mmstatd -cd ../../ - -cd apache-mmstat/ -clean apache-mmstat -makebin apache-mmstat -cd ../../ - -echo -echo 'You may now ./install.sh help or ./install.sh to install/upgrade' -echo diff --git a/mmsoftware/mmstatd/src/Makefile b/mmsoftware/mmstatd/src/Makefile deleted file mode 100644 index 4ff10dc..0000000 --- a/mmsoftware/mmstatd/src/Makefile +++ /dev/null @@ -1,39 +0,0 @@ -# $Id: Makefile,v 1.2 2003/07/17 22:26:51 mmondor Exp $ - -CC = gcc -MAKE = make - -CFLAGS += -DDEBUG -Wall - -INCDIR = -I../../mmlib -I/usr/include -I/usr/pkg/include -I. -LIBDIR = -L/usr/local/lib -L/usr/lib -L/lib -L/usr/pkg/lib - -LIBS = ../../mmlib/libmmondor.a -lc - -OBJS = mmstatd.o mmstat.o - -PROG = mmstatd -PROG2 = mmstat - - -CCOBJ = $(CC) $(CFLAGS) -c $(INCDIR) - - - - -all: $(OBJS) - $(CC) $(CFLAGS) -o $(PROG) $(INCDIR) $(LIBDIR) $(PROG).o $(LIBS) - $(CC) $(CFLAGS) -o $(PROG2) $(INCDIR) $(LIBDIR) $(PROG2).o $(LIBS) - - - - -clean: - -rm -f $(OBJS) $(PROG) $(PROG2) - - -mmstatd.o: - $(CCOBJ) mmstatd.c - -mmstat.o: - $(CCOBJ) mmstat.c diff --git a/mmsoftware/mmstatd/src/makepart.sh b/mmsoftware/mmstatd/src/makepart.sh deleted file mode 100755 index 4e19f81..0000000 --- a/mmsoftware/mmstatd/src/makepart.sh +++ /dev/null @@ -1,27 +0,0 @@ -#!/bin/sh -# $Id: makepart.sh,v 1.1 2002/12/11 10:16:28 mmondor Exp $ - -. ../../mmlib/makedefs.sh - -OBJS='mmstatd.o mmstat.o' -BIN1='mmstatd' -BIN2='mmstat' - -if [ "$1" = "clean" ]; then - show $RM $OBJS $BIN1 $BIN2 - exit 0 -fi - -INCDIR="-I../../mmlib $STDINC" -LIBDIR="$STDLIB" -LIBS='../../mmlib/libmmondor.a -lc' - -for obj in $OBJS; do - if [ ! -f $obj ]; then - src=`$ECHO $obj | $SED 's/\.o$/.c/'` - show $CC $CFLAGS -c $INCDIR $src - fi -done - -show $CC $CFLAGS -o $BIN1 $INCDIR $LIBDIR $BIN1.o $LIBS -show $CC $CFLAGS -o $BIN2 $INCDIR $LIBDIR $BIN2.o $LIBS diff --git a/mmsoftware/mmstatd/src/mmstat.8 b/mmsoftware/mmstatd/src/mmstat.8 deleted file mode 100644 index 57a69b3..0000000 --- a/mmsoftware/mmstatd/src/mmstat.8 +++ /dev/null @@ -1,225 +0,0 @@ -.\" $Id: mmstat.8,v 1.8 2004/05/05 23:59:58 mmondor Exp $ -.\" -.\" Copyright (C) 2002-2004, Matthew Mondor -.\" All rights reserved. -.\" -.\" Redistribution and use in source and binary forms, with or without -.\" modification, are permitted provided that the following conditions -.\" are met: -.\" 1. Redistributions of source code must retain the above copyright -.\" notice, this list of conditions and the following disclaimer. -.\" 2. Redistributions in binary form must reproduce the above copyright -.\" notice, this list of conditions and the following disclaimer in the -.\" documentation and/or other materials provided with the distribution. -.\" 3. All advertising materials mentioning features or use of this software -.\" must display the following acknowledgement: -.\" This product includes software written by Matthew Mondor. -.\" 4. The name of Matthew Mondor may not be used to endorse or promote -.\" products derived from this software without specific prior written -.\" permission. -.\" -.\" THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR -.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES -.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. -.\" IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, -.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -.\" BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF -.\" USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF -.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -.\" -.Dd 1 Jan, 2003 -.Dt MMSTAT 8 -.Os -.Sh NAME -.Nm mmstat -.Nd Administration utility to access mmstatd functions -.Sh SYNOPSIS -.Nm mmstat [h]report -.Op Ar key|pattern -.Nm mmstat reset -.Ar p|v[a] key|pattern%value -.Op Ar key|pattern%value ... -.Nm mmstat update -.Ar p|v[a] key|pattern%value -.Op Ar key|pattern%value ... -.Nm mmstat delete -.Ar key|pattern -.Op Ar key|pattern ... -.Nm mmstat rotate -.Ar pattern prefix -.Sh DESCRIPTION -The -.Nm mmstatd -daemon is controled by parameters found in -.Nm /usr/local/etc/mmstatd.conf -file. However, a binary is also provided with it to ease administration, -.Nm mmstat , -which still uses the same configuration file. It basically internally uses -.Xr mmstat 3 -function calls to provide a shell interface to administrators. -.Nm mmstat -returns 0 on success, or -1 on error. -.Pp -.Nm mmstat [h]report -.Op Ar key|pattern -.Pp -This invokation method permits to retreive a full statistics report, if no -arguments are specified, a report on a single key, if an absolute key name -is specified, or (slower) a report on all keys matching specified pattern, -which can use '*' and '?' wildcards. If -.Nm hreport -is specified instead of -.Nm report , -a more humanly readable report form will be shown (can be useful to use -as-is for CGIs, etc). -.Pp -.Nm mmstat reset -.Ar p|v[a] key|pattern%value -.Op Ar key|pattern%value ... -.Pp -.Nm mmstat update -.Ar p|v[a] key|pattern%value -.Op Ar key|pattern%value ... -.Pp -Allow to reset or update volatile or persistent keys (either by absolute name -or pattern matching using '?' and '*'), using specified values. Key/value -pairs are specified separated with '%' character which is illegal in a key -name and thus consists of an ideal separator. If an operation is performed on -a non-existing key, it is automatically created on-the-fly (unless a pattern -rather than absolute key name). If more than a single -.Ar key|pattern%value -pairs are specified, operations are performed atomically within a protected -transaction on all of them. Of course the number of supplied pairs should not -exceed -.Ar MAX_TRANSACT -as specified in -.Aq Pa mmstat.h -headerfile. -.Ar p -means that operations should be persistent (as well as any new created key), -while -.Ar v -means that those should be volatile, and thus forgot about in case of a crash -or -.Nm mmstatd -daemon restart. If -.Ar a -is also specified, this instructs mmstatd to automatically flush/delete any -key that reaches 0 after the operation, atomically. -For reset operations, the value part of a pair specifies what -value to initialize the corresponding key to; For update operations the current -key contents is increased by the specified value (which may also be a negative -number). -.Pp -.Nm mmstat delete -.Ar key|pattern -.Op Ar key|pattern ... -.Pp -Causes specified key(s), represented by their absolute name, or ones matching -supplied '?' and '*' wildcard matching pattern, to be deleted -from the statistics database. If more than a single key is specified, a -transaction-protected atomic operation is performed on all of them, of course -still subject to -.Ar MAX_TRANSACT -according to the maximum number of keys that can be specified. -.Pp -.Nm mmstat rotate -.Ar pattern prefix -.Pp -Will perform an atomic rename operation on all persistent keys matching -specified -.Ar pattern -by inserting the specified -.Ar prefix -before each matching key name. '*' and '?' wildcards are allowed in the -.Ar pattern -argument. This is most useful to perform automatic weekly or monthly rotation -of statistical keys via a cron events scheduler for instance. This function -also causes an immediate database sync to disk. -.Sh EXAMPLES -Obtain a nicely formatted key/value based statistical report: -.Bd -literal -offset indent -compact -mmstat report | awk '{printf "%-64s %d\\n", $6, $5}' | sort -.Ed -.Pp -An alias to obtain a more sophisticated report: -.Bd -literal -offset indent -compact -alias report='printf "%10s %-13s %s\\n" "COUNTER" "MODIFIED" "KEY"; mmstat report | awk "{printf \\"date -r %d +%s\\\\\\ %s\\\\\\ %%y%%m%%d.%%H%%M%%S\\n\\", \\$4, \\$6, \\$5}" | sh | sort | awk "{printf \\"%10s %s %s\\n\\", \\$2, \\$3, \\$1}"' -.Ed -.Pp -Show who is currently connected on mmftpd: -.Bd -literal -offset indent -compact -mmstat hreport 'mmftpd|who|*' -.Ed -.Pp -Reset all keys with mmftpd in their name to 0: -.Bd -literal -offset indent -compact -mmstat reset p '*mmftpd*' - or -mmstat report | grep mmftpd | awk '{printf "%s%%%d ", $6, 0}' | - xargs mmstat reset p -.Ed -.Pp -Increase the persistent my|test key by 4 while atomically decreasing the -persistent my|test2 key by 2: -.Bd -literal -offset indent -compact -mmstat update p 'my|test%4' 'my|test2%-2' -.Ed -.Pp -Delete all keys with mmftpd in their name: -.Bd -literal -offset indent -compact -mmstat delete '*mmftpd*' - or -mmstat report | awk '{print $6}' | grep mmftpd | xargs mmstat delete -.Ed -.Pp -Example of a monthly cron entry to rename all mmftpd|* keys to YYYYMM-mmftpd|* -using the last month label: -.Bd -literal -compact -30 5 1 * * mmstat rotate 'mmftpd|*' `date -r $((\`date +%s\`-86400)) +%Y%m-` -.Ed -.Pp -Example of a daily cron entry to rename all mmftpd|* keys to YYYYMMDD-mmftpd|* -using the last day for the stamp: -.Bd -literal -compact -30 5 * * * mmstat rotate 'mmftpd|*' `date -r $((\`date +%s\`-86400)) +%Y%m%d-` -.Ed -.Sh ENVIRONMENT -.Bl -tag -width XXXXXXXXXX -.It Pa Nm MMSTATCONF -If set, specifies the absolute location of the configuration file to load -instead of the default -.Nm /usr/local/etc/mmstatd.conf . -.El -.Sh AUTHOR -mmstat and related daemon and library were written by Matthew Mondor, -and are Copyright (c) 2002-2004, Matthew Mondor, All rights reserved. -It originally was written for the mmftpd and mmmail suite of daemons by the -same author. -.Sh FILES -.Bl -tag -width XXXXXXXXXXXXXXXXXXXX -compact -.It Pa -Headerfile used both by -.Nm mmstatd -daemon and -.Nm mmstat -utility, defining related -.Ar MAX_TRANSACT -variable. -.Pp -.It Pa /usr/local/etc/mmstatd.conf -Configuration file for both -.Nm mmstatd -daemon and -.Nm mmstat -utility. -.El -.Sh SEE ALSO -.Xr mmstat 3 , -.Xr mmstatd.conf 5 , -.Xr mmstatd 8 , -.Xr apache-mmstat 8 . -.Sh BUGS -Please report any bug to mmondor@accela.net diff --git a/mmsoftware/mmstatd/src/mmstat.c b/mmsoftware/mmstatd/src/mmstat.c deleted file mode 100644 index ee5b8c1..0000000 --- a/mmsoftware/mmstatd/src/mmstat.c +++ /dev/null @@ -1,415 +0,0 @@ -/* $Id: mmstat.c,v 1.18 2004/05/22 17:44:01 mmondor Exp $ */ - -/* - * Copyright (C) 2002-2004, Matthew Mondor - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software written by Matthew Mondor. - * 4. The name of Matthew Mondor may not be used to endorse or promote - * products derived from this software without specific prior written - * permission. - * - * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - - - - -/* HEADERS */ - -#include -#include -#include -#include - -#include -#include -#include -#include -#include - - - - -MMCOPYRIGHT("@(#) Copyright (c) 2002-2004\n\ -\tMatthew Mondor. All rights reserved.\n"); -MMRCSID("$Id: mmstat.c,v 1.18 2004/05/22 17:44:01 mmondor Exp $"); - - - - -/* STRUCTURES */ - -struct entnode { - pnode_t node; - mmstatent_t ent; -}; - - - - -/* PROTOTYPES */ - -int main(int, char **); -static void usage(void); -static int stat_report(int, char **); -static int stat_hreport(int, char **); -static int stat_reset(int, char **); -static int stat_update(int, char **); -static int stat_delete(int, char **); -static int stat_rotate(int, char **); -static int compar_entnode(const void *, const void *); - - - - -/* FUNCTIONS */ - -int -main(int argc, char **argv) -{ - int ret = -1; - - /* For efficiency, put the stdout stream in fully buffered mode, so that - * we do not cause a syscall to be required for each line (which is silly) - */ - setvbuf(stdout, NULL, 0, _IOFBF); - setvbuf(stderr, NULL, 0, _IOFBF); - if (argc >= 2) { - if (!mm_strcasecmp(argv[1], "report")) - ret = stat_report(argc, argv); - else if (!mm_strcasecmp(argv[1], "hreport")) - ret = stat_hreport(argc, argv); - else if (!mm_strcasecmp(argv[1], "reset")) - ret = stat_reset(argc, argv); - else if (!mm_strcasecmp(argv[1], "update")) - ret = stat_update(argc, argv); - else if (!mm_strcasecmp(argv[1], "delete")) - ret = stat_delete(argc, argv); - else if (!mm_strcasecmp(argv[1], "rotate")) - ret = stat_rotate(argc, argv); - else usage(); - } else usage(); - - /* Make sure that buffers are flushed and return */ - fflush(stdout); - fflush(stderr); - return (ret); -} - - -static -void usage(void) -{ - fprintf(stderr, "\nUsage:\n"); - fprintf(stderr, "mmstat [h]report []\n"); - fprintf(stderr, - "mmstat reset %% \ -[%% ...]\n"); - fprintf(stderr, - "mmstat update %% \ -[%% ...]\n"); - fprintf(stderr, "mmstat delete [ ...]\n"); - fprintf(stderr, "mmstat rotate \n\n"); -} - - -static -int stat_report(int argc, char **argv) -{ - list_t list; - pool_t pool; - struct entnode *entnod; - mmstatres_t *res; - mmstatent_t *ent; - int ret = 0; - char key[KEY_SIZE]; - - if (argc == 2 || argc == 3) { - mm_memclr(key, KEY_SIZE); - if (argc == 3) mm_strncpy(key, argv[2], KEY_SIZE - 1); - - if (pool_init(&pool, "results_pool", malloc, free, NULL, NULL, - sizeof(struct entnode), 32768 / sizeof(struct entnode), - 1, 0)) { - DLIST_INIT(&list); - if ((res = mmstat_report(key))) { - /* Cache all results as fast as possible to free the librarian. - * Note that we also pre-buffered a 32768 bytes of nodes too, - * but which can grow if needed. - */ - while ((ent = mmstat_nextres(res))) { - if ((entnod = (struct entnode *)pool_alloc(&pool, - FALSE))) { - mm_memcpy(&entnod->ent, ent, sizeof(mmstatent_t)); - DLIST_APPEND(&list, (node_t *)entnod); - } else - break; - } - mmstat_freeres(res); - /* Now output list of results to stdout, which could be piped - * and processed at an arbitrary rate without hogging the - * librarian. - */ - DLIST_FOREACH(&list, entnod) { - ent = &entnod->ent; - printf("%c %d %ld %ld %lld %s\n", - ent->persistent ? 'p' : 'v', ent->uid, - (long)ent->created, (long)ent->modified, - ent->value, ent->key); - } - } else { - printf("Error connecting to mmstatd\n"); - ret = -1; - } - pool_destroy(&pool); - DLIST_INIT(&list); - } else { - printf("Error allocating memory for results\n"); - ret = -1; - } - } else { - usage(); - ret = -1; - } - - return (ret); -} - - -static -int stat_hreport(int argc, char **argv) -{ - pool_t pool; - list_t list; - struct entnode *entnod; - mmstatres_t *res; - mmstatent_t *ent; - int ret = 0; - char key[KEY_SIZE]; - bool nomem = FALSE; - - if (argc == 2 || argc == 3) { - mm_memclr(key, KEY_SIZE); - if (argc == 3) mm_strncpy(key, argv[2], KEY_SIZE - 1); - - if (pool_init(&pool, "results_pool", malloc, free, NULL, NULL, - sizeof(struct entnode), 32768 / sizeof(struct entnode), - 1, 0)) { - DLIST_INIT(&list); - if ((res = mmstat_report(key))) { - struct entnode **index; - struct tm modifiedt; - char modified[15]; - - /* Cache all results as fast as possible to free librarian */ - while ((ent = mmstat_nextres(res))) { - if ((entnod = (struct entnode *)pool_alloc(&pool, - FALSE))) { - mm_memcpy(&entnod->ent, ent, sizeof(mmstatent_t)); - DLIST_APPEND(&list, (node_t *)entnod); - } else - break; - } - mmstat_freeres(res); - /* Now output list of results to stdout, which could be piped - * and processed at an arbitrary rate without hogging the - * librarian. - */ - /* We want to sort the results by key name; We thus need to - * setup an index array. - */ - if ((index = malloc(sizeof(struct entnode *) * - (DLIST_NODES(&list) + 1))) != NULL) { - register int i = 0; - - DLIST_FOREACH(&list, entnod) - index[i++] = entnod; - index[i] = NULL; - - qsort(index, DLIST_NODES(&list), sizeof(struct entnode *), - compar_entnode); - - for (i = 0; index[i] != NULL; i++) { - ent = &(index[i])->ent; - gmtime_r(&ent->modified, &modifiedt); - strftime(modified, 14, "%y%m%d.%H%M%S", &modifiedt); - printf("%11lld %s %s\n", ent->value, modified, - ent->key); - } - - free(index); - } else - nomem = TRUE; - } else { - printf("Error connecting to mmstatd\n"); - ret = -1; - } - pool_destroy(&pool); - DLIST_INIT(&list); - } else - nomem = TRUE; - if (nomem) { - printf("Error allocating memory for results\n"); - ret = -1; - } - } else { - usage(); - ret = -1; - } - - return (ret); -} - - -static -int stat_reset(int argc, char **argv) -{ - mmstat_t mms; - int i, ret = 0; - char *arg, *args[3]; - bool persistent = FALSE, autoflush = FALSE, ok = TRUE; - - if (argc > 3) { - if (mm_strchr(argv[2], 'p')) persistent = TRUE; - if (mm_strchr(argv[2], 'v')) persistent = FALSE; - if (mm_strchr(argv[2], 'a')) autoflush = TRUE; - if (mmstat_init(&mms, persistent, autoflush)) { - mmstat_transact(&mms, TRUE); - for (i = 3; i < argc; i++) { - arg = argv[i]; - if (mm_strspl(args, arg, 2, '%') == 2) - mmstat(&mms, STAT_RESET, atol(args[1]), "%s", args[0]); - } - if (!mmstat_transact(&mms, FALSE)) { - printf("Error sending request to mmstatd\n"); - ret = -1; - } - } else { - printf("Error opening mmstatd handler\n"); - ret = -1; - } - } else - ok = FALSE; - if (!ok) { - usage(); - ret = -1; - } - - return (ret); -} - - -static int -stat_update(int argc, char **argv) -{ - mmstat_t mms; - int i, ret = 0; - char *arg, *args[3]; - bool persistent = FALSE, autoflush = FALSE, ok = TRUE; - - if (argc > 3) { - if (mm_strchr(argv[2], 'p')) persistent = TRUE; - if (mm_strchr(argv[2], 'v')) persistent = FALSE; - if (mm_strchr(argv[2], 'a')) autoflush = TRUE; - if (mmstat_init(&mms, persistent, autoflush)) { - mmstat_transact(&mms, TRUE); - for (i = 3; i < argc; i++) { - arg = argv[i]; - if (mm_strspl(args, arg, 2, '%') == 2) - mmstat(&mms, STAT_UPDATE, atol(args[1]), "%s", args[0]); - } - if (!mmstat_transact(&mms, FALSE)) { - printf("Error sending request to mmstatd\n"); - ret = -1; - } - } else { - printf("Error opening mmstatd handler\n"); - ret = -1; - } - } else - ok = FALSE; - if (!ok) { - usage(); - ret = -1; - } - - return (ret); -} - - -static int -stat_delete(int argc, char **argv) -{ - mmstat_t mms; - int i, ret = 0; - - if (argc > 2) { - if (mmstat_init(&mms, TRUE, TRUE)) { - mmstat_transact(&mms, TRUE); - for (i = 2; i < argc; i++) - mmstat(&mms, STAT_DELETE, 0, "%s", argv[i]); - if (!mmstat_transact(&mms, FALSE)) { - printf("Error sending request to mmstatd\n"); - ret = -1; - } - } else { - printf("Error opening mmstatd handler\n"); - ret = -1; - } - exit(0); - } else { - usage(); - ret = -1; - } - - return (ret); -} - - -static int -stat_rotate(int argc, char **argv) -{ - int ret = 0; - - if (argc == 4) { - if (!mmstat_rotate(argv[2], argv[3])) { - printf("Error sending request to mmstatd\n"); - ret = -1; - } - } else { - usage(); - ret = -1; - } - - return (ret); -} - - -static int -compar_entnode(const void *from, const void *to) -{ - const struct entnode **f = (const struct entnode **)from, - **t = (const struct entnode **)to; - - return (mm_strcmp((*f)->ent.key, (*t)->ent.key)); -} diff --git a/mmsoftware/mmstatd/src/mmstatd.8 b/mmsoftware/mmstatd/src/mmstatd.8 deleted file mode 100644 index df84f2a..0000000 --- a/mmsoftware/mmstatd/src/mmstatd.8 +++ /dev/null @@ -1,235 +0,0 @@ -.\" $Id: mmstatd.8,v 1.8 2004/10/18 17:34:15 mmondor Exp $ -.\" -.\" Copyright (C) 2002-2004, Matthew Mondor -.\" All rights reserved. -.\" -.\" Redistribution and use in source and binary forms, with or without -.\" modification, are permitted provided that the following conditions -.\" are met: -.\" 1. Redistributions of source code must retain the above copyright -.\" notice, this list of conditions and the following disclaimer. -.\" 2. Redistributions in binary form must reproduce the above copyright -.\" notice, this list of conditions and the following disclaimer in the -.\" documentation and/or other materials provided with the distribution. -.\" 3. All advertising materials mentioning features or use of this software -.\" must display the following acknowledgement: -.\" This product includes software written by Matthew Mondor. -.\" 4. The name of Matthew Mondor may not be used to endorse or promote -.\" products derived from this software without specific prior written -.\" permission. -.\" -.\" THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR -.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES -.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. -.\" IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, -.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -.\" BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF -.\" USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF -.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -.\" -.Dd 11 Dec, 2002 -.Dt MMSTATD 8 -.Os -.Sh NAME -.Nm mmstatd -.Nd -Statistics librarian and logger daemon with recovery and transaction support -.Sh SYNOPSIS -.Nm mmstatd Op Ar config_file -.Sh DESCRIPTION -.Nm mmstatd -consists of a statistics librarian daemon used by the familly of -mm utilities (mmmail, mmftpd, etc). It simply allows applications to record -statistical information using a key-based system. It also supports transactions -and crash recovery logging. Moreover, it features volatile and consistant -database entries, with creation and modification timestamps. -.Pp -The main reason why I wrote it was because syslog does not consist of an -ideal approach to count statistics, and that using an SQL server for this -is overkill, especially with required SQL commands parsing and atomic-safe -transaction locks. db4 could have been more efficient, but it is quite easy -to rival with it according to size. The size of mmstatd and mmstat library is -very small, and it's performance is decent. Moreover a database server would -consider all keys as persistent storage, when a requirement for both -persistent and volatile ones was met. -.Pp -.Nm The way it works -.Pp -Basically, it starts up two asynchroneous processes, the librarian and logger. -.Pp -The librarian is responsible for managing the database and affecting changes -while asynchroneously reading available logs. It also permits obtention of -statistics report connecting to a Unix domain stream socket. The librarian -synchronizes the memory database to disk every once in a while, recording -the current position in the logs, and deleting obsolete, already synchronized -logs when required. This synchronization to disk is only performed at large -time intervals, to minimize CPU and drive load, and maximise responsiveness. -The librarian also establishes a listening UNIX stream socket, to serve -reports and rotation requests. -.Pp -The logger, in turn, listens to a Unix datagram socket, and writes the logs, -syncing them to disk frequently enough, for both the librarian and logger. -This allows the logs to be used for recovery of persistent modifications that -were performed since the last full database sync. Moreover, it permits the -clients to always be able to send more packets without being subject to the -librarian processing, avoiding a bottleneck. -.Pp -Every time mmstatd is started, it first ensures to run with normal user -privileges, then looks for recovery logs and performs necessary modifications -to the database, before forcing a sync of the new database to disk, deleting -all recovery logs. It then launches the two asynchroneous processes which then -become ready to serve their tasks. -.Pp -Using this design allows applications to use a simple syslog-like API to -update statistics, in a very fast manner. For the various mm daemons, it -serves as a who database, using volatile storage, and as various statistical -tasks using persistent storage. -.Pp -The logging socket, used to send update requests, as well as the status socket, -are independent and can have specific permissions, so that only applications -who should access the service may. The user application interface library is -quite simple to use by programs wanting to keep their statistics using mmstatd. -See the -.Xr mmstat 3 -man page for more information. -.Pp -The internal format used to store the database, as well as the recovery -logging, are now endian-independent, as well as native integer size -independent. This means that those files should easily be migrated to another -operating system and/or hardware architecture without modifications. -The file formats are saved in network byte order (big endian) and the internal -memory representation is in local host order corresponding to the particular -architecture. -.Pp -.Sh INSTALLATION -.Nm mmstatd -is installed by the make.sh scripts of both -.Nm mmftpd -and -.Nm mmmail -by Matthew Mondor. It's administration is made through -.Nm /usr/local/etc/mmstatd.conf -(See -.Xr mmstatd.conf 5 -man page) and -.Nm mmstat -(See -.Xr mmstat 8 -man page). -.Pp -Here is an overview of standard permissions various files should have: -.Pp -.Bd -literal -offset indent -compact -Permissions Owner Group File - --rwxr-x--- root staff /usr/local/sbin/mmstat --rwx------ root wheel /usr/local/sbin/mmstatd - -drwxr-x--- mmstatd mmstat /var/mmstatd - -s-w--w---- mmstatd mmstat /var/mmstatd/mmstatd_log.sock -srw-rw---- mmstatd staff /var/mmstatd/mmstatd_stat.sock -.Ed -.Pp -Basically, the administrator will need access to -.Nm mmstat -binary as well as to both -.Nm /var/mmstatd/*.sock -files. Applications using the -.Xr mmstat 3 -facility require access to the -.Nm /var/mmstatd/mmstatd_log.sock -file only. Only uid zero should have access to -.Nm /var/mmstatd -directory and -.Nm mmstatd -daemon. -.Pp -.Nm mmstatd -should normally be started before any other daemons using the -.Xr mmstat 3 -interface. Although this is not obligatory, it is highly -recommended. As it will take the time to perform recovery before calling -.Xr fork 2 -other daemons will have a clean and ready daemon to serve their requests, -so a startup script typically would use something like: -.Pp -.Bd -literal -offset indent -compact -/usr/local/sbin/mmstatd -/usr/local/sbin/mmftpd -/usr/local/sbin/mmsmtpd -/usr/local/sbin/mmpop3d -.Ed -.Sh FILES -.Bl -tag -width XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX -compact -.It Pa /usr/local/etc/mmstatd.conf -This file consists of the configuration file for -.Nm mmstatd -and controls all it's configurable parameters. -.Pp -.It Pa /var/mmstatd -The mmstat environment directory, where recovery logs and database file are -located. The default configuration also stores UNIX domain sockets there. -.Pp -.It Pa /var/mmstatd/mmstatd_stat.sock -The UNIX stream socket on which listens -.Nm mmstatd -librarian process, used by the administrator via -.Nm mmstat -binary to query statistical reports and request key rotations. -.Pp -.It Pa /var/mmstatd/mmstatd_log.sock -The UNIX datagram socket on which listens -.Nm mmstatd -logger process, used by all applications using the -.Xr mmstat 3 -interface to update statistics. -.Pp -.It Pa /var/mmstatd/mmstatd.db -The database file used to permanently store persistent storage statistical -keys. When a disk synchronization occurs, a temporary file is used to save -the database on the same filesystem, and -.Xr rename 2 -is used to move it over this file in an atomic manner. -.Pp -.It Pa /var/mmstatd/????????.log -Log files internally used and automatically maintained by -.Nm mmstatd -for crash recovery. -.Pp -.It Pa /usr/local/sbin/mmstatd -The actual daemon binary documented by -.Xr mmstatd 8 -.Pp -.It Pa /usr/local/sbin/mmstat -The administration utility documented by -.Xr mmstat 8 -.El -.Sh ENVIRONMENT -.Bl -tag -width XXXXXXXXXX -.It Pa Nm MMSTATCONF -If set, specifies the absolute location of the configuration file to load -instead of the default -.Nm /usr/local/etc/mmstatd.conf . -If a configuration file name is specified on the command line, it will -override this environment variable even if it was set. -.El -.Sh AUTHOR -The suite of mmstat daemon, related utilities and documentation were written -by Matthew Mondor, and are -Copyright (c) 2002-2004, Matthew Mondor, All rights reserved. -They were developped -under NetBSD 1.5.3 for the mmftpd and mmmail suite of daemons from the same -author. -.Sh SEE ALSO -.Xr mmstat 3 , -.Xr mmstatd.conf 5 , -.Xr mmstat 8 , -.Xr apache-mmstat 8 , -.Xr rename 2 , -.Xr fork 2 , -.Xr unix 4 . -.Sh BUGS -Please report any bug to mmondor@accela.net diff --git a/mmsoftware/mmstatd/src/mmstatd.c b/mmsoftware/mmstatd/src/mmstatd.c deleted file mode 100644 index d98a07d..0000000 --- a/mmsoftware/mmstatd/src/mmstatd.c +++ /dev/null @@ -1,1991 +0,0 @@ -/* $Id: mmstatd.c,v 1.39 2004/06/09 21:12:45 mmondor Exp $ */ - -/* - * Copyright (C) 2002-2004, Matthew Mondor - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software written by Matthew Mondor. - * 4. The name of Matthew Mondor may not be used to endorse or promote - * products derived from this software without specific prior written - * permission. - * - * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -/* TODO: - * - Finish client packet uid credential checking, credentials have to be - * passed through the unix domain socket - */ - - - - -/* HEADERS */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include - - - - -MMCOPYRIGHT("@(#) Copyright (c) 2002-2004\n\ -\tMatthew Mondor. All rights reserved.\n"); -MMRCSID("$Id: mmstatd.c,v 1.39 2004/06/09 21:12:45 mmondor Exp $"); - - - - -/* GLOBALS */ - -static struct mmstat_config CONF; -static pid_t librarian_pid = -1, logger_pid = -1; -static pool_t key_pool; -static hashtable_t key_table; -static int pipefds[2], syncbytes = 0; -static bool pipesend = TRUE; - - - - -/* FUNCTIONS */ - -static pid_t -process_spawn(int (*function)(void *), void *params, bool leader) -{ - pid_t pid = -1; - int fd; - - /* Create new process */ - if (!(pid = fork())) { - struct sigaction act; - - /* Child */ - if (leader) setsid(); - chdir("/"); - umask(0); - - /* Make sure that stdin, stdout and stderr are safe */ - if ((fd = open("/dev/null", O_RDWR)) != -1) { - dup2(fd, 0); - dup2(fd, 1); - dup2(fd, 2); - if (fd > 2) - close(fd); - } - - /* Setup our break handler */ - act.sa_handler = sighandler; - act.sa_flags = SA_NOCLDWAIT; - sigemptyset(&act.sa_mask); - sigaction(SIGTERM, &act, NULL); - sigaction(SIGSEGV, &act, NULL); - sigaction(SIGPIPE, &act, NULL); - - /* Signals we want to ignore */ - signal(SIGTTOU, SIG_IGN); - signal(SIGTTIN, SIG_IGN); - signal(SIGTSTP, SIG_IGN); - - /* Simply call the wanted child function */ - exit(function(params)); - - } - - /* Parent */ - return (pid); -} - - -static void -sighandler(int sig) -{ - switch (sig) { - case SIGTERM: - syslog(LOG_NOTICE, "Received SIGTERM, cleaning up"); - signal(SIGTERM, SIG_IGN); - kill(0, SIGTERM); - exit(0); - break; - case SIGSEGV: - syslog(LOG_NOTICE, "Received SIGSEGV! Cleaning up"); - kill(0, SIGTERM); - exit(0); - break; - case SIGPIPE: - pipesend = FALSE; - break; - default: - syslog(LOG_NOTICE, "Signal handler catched unexpected signal"); - break; - } -} - - -/* This uses an fd which remains open in child processes, if they close it the - * lock seems to be released automatically. - */ -static bool -lock_check(const char *file) -{ - int fd; - - if ((fd = open(file, O_CREAT | O_TRUNC | O_WRONLY, 0600)) != -1) { - if (!(flock(fd, LOCK_EX | LOCK_NB))) - return (TRUE); - close(fd); - } - - return (FALSE); -} - - -static int -unix_init(const char *name, gid_t group, mode_t mode, int backlog, bool stream, - bool del) -{ - int sock; - struct sockaddr_un sau; - - if (del) unlink(name); - - /* Open public UNIX domain socket */ - if (stream) { - if ((sock = socket(AF_LOCAL, SOCK_STREAM, 0)) != -1) { - mm_strncpy(sau.sun_path, name, 100); - sau.sun_family = AF_UNIX; - if (bind(sock, (struct sockaddr *)&sau, sizeof(struct sockaddr_un)) - != -1) { - if (!chmod(name, mode)) { - chown(name, -1, group); - if (!(listen(sock, backlog))) - return (sock); - else - DEBUG_PRINTF("unix_init", "listen(%d)", sock); - } else - DEBUG_PRINTF("unix_init", "chmod(%s, %d)", name, mode); - } else - DEBUG_PRINTF("unix_init", "bind(%d)", sock); - close(sock); - } else - DEBUG_PRINTF("unix_init", "socket()"); - } else { - if ((sock = socket(AF_LOCAL, SOCK_DGRAM, 0)) != -1) { - int opt; - - /* Set output buffer size */ - opt = (int)BALIGN_CEIL(sizeof(struct log_entry) * MAX_TRANSACT, - 1024); - if (setsockopt(sock, SOL_SOCKET, SO_RCVBUF, &opt, sizeof(int)) - == -1) - DEBUG_PRINTF("unix_init", "setsockopt(%d, SO_RCVBUF)", sock); - - mm_strncpy(sau.sun_path, name, 100); - sau.sun_family = AF_UNIX; - if (bind(sock, (struct sockaddr *)&sau, sizeof(struct sockaddr_un)) - != -1) { - if (!chmod(name, mode)) { - chown(name, -1, group); - return (sock); - } else - DEBUG_PRINTF("unix_init", "chmod(%s, %d)", name, mode); - } else - DEBUG_PRINTF("unix_init", "bind(%d)", sock); - close(sock); - } else - DEBUG_PRINTF("unix_init", "socket()"); - } - - return (-1); -} - - -static bool -log_match(const char *str, const char *pat) -{ - for (; *pat != '*'; pat++, str++) { - if (*str == '\0') { - if (*pat != '\0') - return FALSE; - else - return TRUE; - } - if(*str != *pat && *pat != '?') - return FALSE; - } - while (pat[1] == '*') - pat++; - do - if (log_match(str, pat + 1)) - return TRUE; - while (*str++ != '\0') ; - - return FALSE; -} - - -/* Writes requested log entries to specified fd, and if required automatically - * perform logfile rotation, of course putting a mark at the end of the logfile - * pointing to the next one continueing it. Files are rotated when reaching - * approximately one megabyte in length. Returns TRUE on success or FALSE on - * fatal error (eg: disk full). - * - * Note that the logentry we are writing is always in network byte order here. - * We however receive host byte order entries, but are converting them as - * appropriate, as well as generate network order entries when a new file - * entry is required because of rotation. - */ -static bool -logentries_write(int pfd, struct log_entry *entries, int len, int *fd, - u_int32_t *lognum, off_t *logpos) -{ - ssize_t l; - bool ok; - char filename[256], dat[MAX_TRANSACT]; - - ok = TRUE; - l = len * sizeof(struct log_entry); - - /* First perform logfile rotation if required */ - if (*logpos + l > CONF.max_logsize) { - struct log_entry newentry; - int newfd; - - if ((*lognum)++ > 99999999) - *lognum = 0; - *logpos = 0; - snprintf(filename, 255, "%s/%08u.log", CONF.ENV_DIR, *lognum); - if ((newfd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0600)) - != -1) { - fsync(newfd); - mm_memclr(&newentry, sizeof(struct log_entry)); - newentry.type = (int32_t)BYTEORDER_NETWORK32( - (u_int32_t)STAT_NEWFILE); - newentry.un.newfile.lognum = BYTEORDER_NETWORK32(*lognum); - if ((write(*fd, &newentry, sizeof(struct log_entry))) != - sizeof(struct log_entry)) - DEBUG_PRINTF("logentries_write", "write(STAT_NEWFILE)"); - (void) fsync(*fd); - (void) close(*fd); - *fd = newfd; - (void) write(pfd, dat, 1); - } else { - DEBUG_PRINTF("logentries_write", "open(%s)", filename); - ok = FALSE; - } - } - - /* Write our new log entry */ - if (ok) { - time_t tim; - int i, type; - - tim = time(NULL); - for (i = 0; i < len; i++) { - /* Mark entry time, in network byte order */ - entries[i].time = BYTEORDER_NETWORK32((u_int32_t)tim); - - /* Convert entry to network byte order */ - type = (int)entries[i].type; - entries[i].type = (int32_t)BYTEORDER_NETWORK32((u_int32_t)type); - entries[i].uid = BYTEORDER_NETWORK32(entries[i].uid); - entries[i].persistent = BYTEORDER_NETWORK16( - entries[i].persistent); - entries[i].autoflush = BYTEORDER_NETWORK16(entries[i].autoflush); - if (type == STAT_TRANSACT) - entries[i].un.transact.begin = BYTEORDER_NETWORK32( - entries[i].un.transact.begin); - else if (type == STAT_UPDATE) - entries[i].un.update.modifier = (int64_t)BYTEORDER_NETWORK64( - (u_int64_t)entries[i].un.update.modifier); - else if (type == STAT_RESET) - entries[i].un.reset.value = (int64_t)BYTEORDER_NETWORK64( - (u_int64_t)entries[i].un.reset.value); - } - - /* Write to logfile */ - if (write(*fd, entries, l) == l) { - *logpos += l; - /* A trick to keep resonable physical disk sync rate */ - syncbytes += l; - if (syncbytes >= CONF.SYNC_BYTES) { - syncbytes = 0; - (void) fdatasync(*fd); - } - (void) write(pfd, dat, len); - } else { - DEBUG_PRINTF("logentries_write", "write(STAT_*)"); - ok = FALSE; - } - } - - return (ok); -} - - -/* This function attempts to read a log entry from specified fd, transparently - * rotating to the new logfile when required, and updates fd and lognum via - * provided pointers. If pfd is -1, we attempt to read an entry, and return - * FALSE if none could be read. Otherwise we attempt to read for an entry - * monitoring pfd for new data notification, until at least a full entry - * could be read. We return TRUE if an entry could be read. - * - * Note that the entries we are reading are in network byte order, which means - * that we must convert them to host byte order after reading. - */ -static bool -logentry_read(int pfd, struct log_entry *entry, int *fd, u_int32_t *lognum, - off_t *logpos) -{ - char filename[256], c; - int newfd; - bool ok, redo; - struct pollfd fds[] = { - {pfd, POLLIN, 0} - }; - - redo = TRUE; - while (redo) { - redo = FALSE; - - ok = FALSE; - if (pfd == -1) { - if ((read(*fd, entry, sizeof(struct log_entry))) == - sizeof(struct log_entry)) - ok = TRUE; - } else { - for (;;) { - if ((poll(fds, 1, 5000)) > 0) { - if (fds[0].revents & POLLIN) { - if ((read(pfd, &c, 1)) == 1) { - if ((read(*fd, entry, sizeof(struct log_entry))) - == sizeof(struct log_entry)) - ok = TRUE; - else - DEBUG_PRINTF("logentry_read", - "read(%d, %d) partial read", - *fd, (int)sizeof(struct log_entry)); - break; - } - } - } - } - } - - if (ok) { - /* Convert type to host byte order */ - entry->type = (int32_t)BYTEORDER_HOST32((u_int32_t)entry->type); - /* logentry_debug('|', entry); */ - *logpos += sizeof(struct log_entry); - /* If required switch to next logfile */ - if (entry->type == (int32_t)STAT_NEWFILE) { - /* Convert new file number to host byte order */ - entry->un.newfile.lognum = BYTEORDER_HOST32( - entry->un.newfile.lognum); - snprintf(filename, 255, "%s/%08u.log", CONF.ENV_DIR, - entry->un.newfile.lognum); - if ((newfd = open(filename, O_RDONLY)) != -1) { - close(*fd); - *fd = newfd; - *logpos = 0; - *lognum = entry->un.newfile.lognum; - redo = TRUE; - } else { - DEBUG_PRINTF("logentry_read", "open(%s)", filename); - ok = FALSE; - } - } - } - - } - - return (ok); -} - - -/* This function attempts to read at least one entry, but all entries of a - * transaction if any is detected. If pfd is -1, we return FALSE if we - * cannot read an entry or the whole transaction, otherwise we wait for - * more data to be available using fdb with poll(). We only return TRUE - * if a single entry or whole transaction could be read. - * If STAT_NEWFILE entry is encoutered, auto rotation to the new logfile - * is performed and fd,lognum,logpos are updated via the provided pointers, - * transparently. - * The STAT_TRANSACT header and footer entries are never put in the buffer, - * this way their persistent flag is ignored. - * IMPORTANT: Because of the way this function works to prevent additionnal - * memory copy operations, the supplied buffer size should at least have one - * additionnal entry than specified maximum size. - */ -static int -logentries_read(int pfd, struct log_entry *entries, int max, int *fd, - u_int32_t *lognum, off_t *logpos) -{ - struct log_entry *ptr; - int len; - bool transact; - - transact = FALSE; - ptr = entries; - len = 0; - - if (logentry_read(pfd, entries, fd, lognum, logpos)) { - /* Type already converted to host byte order */ - if (entries->type == STAT_TRANSACT) { - entries->un.transact.begin = BYTEORDER_HOST32( - entries->un.transact.begin); - if (entries->un.transact.begin) - transact = TRUE; - else { - /* Mismatch */ - len = 0; - DEBUG_PRINTF("logentries_read", - "Mismatched transaction start"); - } - } else - len++; - } - - /* If we fill the buffer of max entries or if we reach EOF before end of - * transaction marker can be found, we simply drop the whole transaction - * and return 0. That is where we need a read-ahead buffer, and require - * an additionnal entry. - */ - while (transact) { - if (len > max) { - len = 0; - syslog(LOG_NOTICE, "logentries_read() - MAX_TRANSACT exceeded"); - break; - } - if (!logentry_read(pfd, ptr, fd, lognum, logpos)) { - len = 1; - break; - } else { - /* Type already in host byte order */ - if (ptr->type == STAT_TRANSACT) { - ptr->un.transact.begin = BYTEORDER_HOST32( - ptr->un.transact.begin); - if (ptr->un.transact.begin) { - /* Mismatch */ - len = 0; - DEBUG_PRINTF("logentries_read", - "Mismatched transaction end"); - } - break; - } else { - ptr++; - len++; - } - } - } - - return (len); -} - - -/* Apply requested log entry to the live db. - * STAT_NEWFILE or STAT_TRANSACT entries are never processed through this - * function. - */ -static bool -logentry_process(struct log_entry *entry, bool tmp) -{ - enum stat_type type; - - /* logentry_debug(':', entry); */ - - /* Type already in host byte order. Convert other first level elements. */ - type = entry->type; - entry->uid = BYTEORDER_HOST32(entry->uid); - entry->time = BYTEORDER_HOST32(entry->time); - entry->persistent = BYTEORDER_HOST16(entry->persistent); - entry->autoflush = BYTEORDER_HOST16(entry->autoflush); - - if (tmp || entry->persistent) { - register size_t len; - register char *ptr; - - /* Verify if we should perform wildcard matching and perform an atomic - * operation on all matching keys - */ - for (ptr = entry->key; *ptr != '\0'; ptr++) - if (*ptr == '?' || *ptr == '*') - break; - len = mm_strlen(entry->key); - - if (*ptr == '\0') { /* Operation on single key */ - struct key_node *knod; - - /* Locate corresponding key in table */ - if ((knod = (struct key_node *)hashtable_lookup(&key_table, - entry->key, len + 1)) != NULL) { - /* Key exists, make sure that UID matches entry creator's or - * that operator was UID 0 - */ - if (knod->entry.uid != entry->uid && entry->uid != 0) { - syslog(LOG_NOTICE, "Unauthorized UID!"); - return FALSE; - } - if (type == STAT_UPDATE) - knod->entry.value += (int64_t)BYTEORDER_HOST64( - (u_int64_t)entry->un.update.modifier); - else if(type == STAT_RESET) - knod->entry.value = (int64_t)BYTEORDER_HOST64( - (u_int64_t)entry->un.reset.value); - if (type == STAT_DELETE || (knod->entry.value == 0 && - entry->autoflush)) { - /* Delete entry */ - hashtable_unlink(&key_table, (hashnode_t *)knod); - pool_free((pnode_t *)knod); - } else - knod->entry.modified = entry->time; - } else { - /* Key does not exist */ - if (type == STAT_DELETE) - return TRUE; - if (type == STAT_UPDATE || type == STAT_RESET) { - if (type == STAT_UPDATE) - entry->un.update.modifier = (int64_t)BYTEORDER_HOST64( - (u_int64_t)entry->un.update.modifier); - else - entry->un.reset.value = (int64_t)BYTEORDER_HOST64( - (u_int64_t)entry->un.reset.value); - if (!(entry->autoflush && - (type == STAT_UPDATE ? - entry->un.update.modifier : - entry->un.reset.value) == 0)) { - /* Create new entry */ - if ((knod = (struct key_node *)pool_alloc(&key_pool, - FALSE)) != NULL) { - mm_memcpy(knod->entry.key, entry->key, len + 1); - knod->entry.value = (type == STAT_UPDATE ? - entry->un.update.modifier : - entry->un.reset.value); - knod->entry.uid = (uid_t)entry->uid; - knod->entry.created = knod->entry.modified = - (time_t)entry->time; - knod->entry.persistent = (bool)entry->persistent; - knod->processed = FALSE; - hashtable_link(&key_table, (hashnode_t *)knod, - knod->entry.key, len + 1, FALSE); - } else - return FALSE; - } - } - } - - } else /* Operation on all keys matching pattern */ - hashtable_iterate(&key_table, logentry_process_iterator, entry); - - } - - return TRUE; -} - - -static bool -logentry_process_iterator(hashnode_t *hnod, void *udata) -{ - struct key_node *knod = (struct key_node *)hnod; - struct log_entry *entry = udata; - - /* Most elements have been converted to host byte order already */ - if ((knod->entry.uid == entry->uid || entry->uid == 0) && - log_match(knod->entry.key, entry->key)) { - if (entry->type == STAT_RESET) - knod->entry.value = (int64_t)BYTEORDER_HOST64( - (u_int64_t)entry->un.reset.value); - else if (entry->type == STAT_UPDATE) - knod->entry.value += (int64_t)BYTEORDER_HOST64( - (u_int64_t)entry->un.update.modifier); - if (entry->type == STAT_DELETE || (knod->entry.value == 0 && - entry->autoflush)) { - hashtable_unlink(&key_table, (hashnode_t *)knod); - pool_free((pnode_t *)knod); - } else - knod->entry.modified = (time_t)entry->time; - } - - return TRUE; -} - - -static bool -logentries_process(struct log_entry *entries, int len, bool tmp) -{ - int i; - bool ok = TRUE; - - for (i = 0; i < len; i++) { - if (!logentry_process(&entries[i], tmp)) { - ok = FALSE; - break; - } - } - - return (ok); -} - - -/* Only used for debugging when testing/developing. Might also need endian - * conversion macros. - */ -/* -static void -logentry_debug(char c, struct log_entry *entry) -{ - switch (entry->type) { - case STAT_TRANSACT: - syslog(LOG_NOTICE, "%c STAT_TRANSACT u=%d p=%d a=%d beg=%d k=%s", - c, entry->uid, entry->persistent, entry->autoflush, - entry->un.transact.begin, entry->key); - break; - case STAT_NEWFILE: - syslog(LOG_NOTICE, "%c STAT_NEWFILE u=%d p=%d a=%d num=%ld k=%s", - c, entry->uid, entry->persistent, entry->autoflush, - entry->un.newfile.lognum, entry->key); - break; - case STAT_UPDATE: - syslog(LOG_NOTICE, "%c STAT_UPDATE u=%d p=%d a=%d mod=%lld k=%s", - c, entry->uid, entry->persistent, entry->autoflush, - entry->un.update.modifier, entry->key); - break; - case STAT_RESET: - syslog(LOG_NOTICE, "%c STAT_RESET u=%d p=%d a=%d val=%lld k=%s", - c, entry->uid, entry->persistent, entry->autoflush, - entry->un.reset.value, entry->key); - break; - case STAT_DELETE: - syslog(LOG_NOTICE, "%c STAT_DELETE u=%d p=%d a=%d k=%s", - c, entry->uid, entry->persistent, entry->autoflush, - entry->key); - break; - default: - syslog(LOG_NOTICE, "%c Unknown entry type!", c); - } -} -*/ - - -/* This function prepares the db lists, and attempts to load previously saved - * database and sync state. If part of the database cannot be loaded, we - * log an error via syslog but will either provide an empty or incomplete - * db in memory. - */ -static void -db_load(u_int32_t *lognum, off_t *logpos) -{ - char filename[256]; - FILE *fh; - bool ok; - u_int32_t version, ver; - - ok = TRUE; - *lognum = 0; - *logpos = 0; - - snprintf(filename, 255, "%s/mmstatd.db", CONF.ENV_DIR); - if (pool_init(&key_pool, "key_pool", malloc, free, NULL, NULL, - sizeof(struct key_node), 32768 / sizeof(struct key_node), - 0, 0)) { - if (hashtable_init(&key_table, "key_table", HT_DEFAULT_CAPACITY, - HT_DEFAULT_FACTOR, malloc, free, - mm_memcmp, mm_memhash32, TRUE)) { - if ((fh = fopen(filename, "rb")) != NULL) { - /* We now remember how to load old mmstatd database files, - * since at times the format changes accross versions. - * First determine which version, and call the appropriate - * function. - */ - if (fread(&ver, sizeof(u_int32_t), 1, fh) == 1) { - char format; - const u_int8_t *p; - - /* We need to still support old host-order saved formats - * with little endian architectures. This means that we - * need to differenciate little endian saved version tag - * from big endian (network order) one. - * We know that we used a large word, a number substracted - * from 0xffffffff. This means that if it was saved in - * network order, we should have the first of the two - * bytes of the 32-bit word be 0xffff. Otherwise, we know - * that the two last ones should be 0xffff, and that we - * thus should use the old method. If none of these - * conditions match, we know that we need to load the very - * old v1 format, which was in host order and had no - * version tag. We use 'o' for old format, 'h' for host - * order formats with version tag and 'n' for network - * order formats with version tag. - * - * What a hack, but we don't want to have trouble loading - * our database when upgrading! Eventually, we'll even - * probably switch to a text format. - */ - p = (u_int8_t *)&ver; - if (p[0] == 0xff && p[1] == 0xff) - format = 'n'; - else if (p[2] == 0xff && p[3] == 0xff) - format = 'h'; - else - format = 'o'; - - if (format == 'n') { - /* Load new network byte order database formats with - * version tag support - */ - syslog(LOG_NOTICE, "Loading network byte order " - "database with version tag support"); - version = 0xFFFFFFFF - BYTEORDER_HOST32(ver); - switch (version) { - case 5: - ok = db_load_v5(fh, lognum, logpos); - break; - default: - ok = FALSE; - syslog(LOG_NOTICE, - "Unknown database type (version %u)", - (unsigned int)version); - break; - } - } else if (format == 'h') { - /* Load older host byte order database formats with - * version tag support - */ - syslog(LOG_NOTICE, "Loading host byte order database" - " with version tag support"); - version = 0xFFFFFFFF - ver; - switch (version) { - case 2: - { - long o_lognum; - - /* mmstatd 0.0.2, db v2 */ - ok = db_load_v2(fh, &o_lognum, logpos); - *lognum = (u_int32_t)o_lognum; - break; - } - case 3: - { - long o_lognum; - - /* mmstatd 0.0.3 db v3 */ - ok = db_load_v3(fh, &o_lognum, logpos); - *lognum = (u_int32_t)o_lognum; - break; - } - case 4: - { - long o_lognum; - - /* mmstatd 0.0.7 db v4 */ - ok = db_load_v4(fh, &o_lognum, logpos); - *lognum = (u_int32_t)o_lognum; - break; - } - default: - ok = FALSE; - syslog(LOG_NOTICE, - "Unknown database type (version %u)", - (unsigned int)version); - break; - } - } else { - /* Load the old host byte order database format - * without version tag support - */ - long o_lognum, o_logpos; - - /* Seek back to start since there was no version info - * back then - */ - syslog(LOG_NOTICE, - "Loading very first format host byte order " - " database without version tag support " - "(it was more than time to upgrade :)"); - fseek(fh, 0, SEEK_SET); - ok = db_load_v1(fh, &o_lognum, &o_logpos); - *lognum = (u_int32_t)o_lognum; - *logpos = (off_t)o_logpos; - } - } else - ok = FALSE; - (void) fclose(fh); - if (!ok) - syslog(LOG_NOTICE, - "db_load() - Error loading database (corrupt)"); - } else - syslog(LOG_NOTICE, "db_load() - New database"); - } else - DEBUG_PRINTF("db_load", "hashtable_init()"); - } else - DEBUG_PRINTF("db_load", "pool_init()"); -} - - -static bool -db_load_v1(FILE *fh, long *lognum, long *logpos) -{ - struct key_node *knod = NULL; - u_int64_t hash; - unsigned char len; - bool ok = TRUE; - - if (fread(lognum, sizeof(long), 1, fh) != 1 || - fread(logpos, sizeof(long), 1, fh) != 1) - ok = FALSE; - while (ok) { - if (fread(&hash, sizeof(u_int64_t), 1, fh) == 1) { - if ((knod = (struct key_node *)pool_alloc(&key_pool, FALSE))) { - knod->entry.persistent = TRUE; - knod->processed = FALSE; - if (fread(&knod->entry.value, sizeof(int64_t), 1, fh) != 1 - || fread(&knod->entry.created, sizeof(time_t), 1, - fh) != 1 - || fread(&knod->entry.modified, sizeof(time_t), 1, - fh) != 1 - || fread(&knod->entry.uid, sizeof(uid_t), 1, - fh) != 1 - || fread(&len, sizeof(unsigned char), 1, fh) != 1 - || fread(&knod->entry.key, len + 1, 1, fh) != 1) - ok = FALSE; - else { - if (!hashtable_link(&key_table, (hashnode_t *)knod, - knod->entry.key, len + 1, TRUE)) { - DEBUG_PRINTF("db_load_v1", "Duplicate key '%s'", - knod->entry.key); - knod = (struct key_node *)pool_free((pnode_t *)knod); - } - } - } else - ok = FALSE; - if (!ok) - if (knod) pool_free((pnode_t *)knod); - } else - break; - } - - return (ok); -} - - -static bool -db_load_v2(FILE *fh, long *lognum, off_t *logpos) -{ - struct key_node *knod = NULL; - u_int64_t hash; - unsigned char len; - bool ok = TRUE; - - if (fread(lognum, sizeof(long), 1, fh) != 1 || - fread(logpos, sizeof(off_t), 1, fh) != 1) - ok = FALSE; - while (ok) { - if (fread(&hash, sizeof(u_int64_t), 1, fh) == 1) { - if ((knod = (struct key_node *)pool_alloc(&key_pool, FALSE))) { - knod->entry.persistent = TRUE; - knod->processed = FALSE; - if (fread(&knod->entry.value, sizeof(int64_t), 1, fh) != 1 - || fread(&knod->entry.created, sizeof(time_t), 1, - fh) != 1 - || fread(&knod->entry.modified, sizeof(time_t), 1, - fh) != 1 - || fread(&knod->entry.uid, sizeof(uid_t), 1, - fh) != 1 - || fread(&len, sizeof(unsigned char), 1, fh) != 1 - || fread(&knod->entry.key, len + 1, 1, fh) != 1) - ok = FALSE; - else { - if (!hashtable_link(&key_table, (hashnode_t *)knod, - knod->entry.key, len + 1, TRUE)) { - DEBUG_PRINTF("db_load_v2", "Duplicate key '%s'", - knod->entry.key); - knod = (struct key_node *)pool_free((pnode_t *)knod); - } - } - } else - ok = FALSE; - if (!ok) - if (knod) pool_free((pnode_t *)knod); - } else - break; - } - - return (ok); -} - - -static bool -db_load_v3(FILE *fh, long *lognum, off_t *logpos) -{ - struct key_node *knod = NULL; - u_int64_t hash; - size_t len; - bool ok = TRUE; - - if (fread(lognum, sizeof(long), 1, fh) != 1 || - fread(logpos, sizeof(off_t), 1, fh) != 1) - ok = FALSE; - while (ok) { - if (fread(&hash, sizeof(u_int64_t), 1, fh) == 1) { - if ((knod = (struct key_node *)pool_alloc(&key_pool, FALSE))) { - knod->entry.persistent = TRUE; - knod->processed = FALSE; - if (fread(&knod->entry.value, sizeof(int64_t), 1, fh) != 1 - || fread(&knod->entry.created, sizeof(time_t), 1, - fh) != 1 - || fread(&knod->entry.modified, sizeof(time_t), 1, - fh) != 1 - || fread(&knod->entry.uid, sizeof(uid_t), 1, - fh) != 1 - || fread(&len, sizeof(size_t), 1, fh) != 1 - || fread(&knod->entry.key, len + 1, 1, fh) != 1) - ok = FALSE; - else { - if (!hashtable_link(&key_table, (hashnode_t *)knod, - knod->entry.key, len + 1, TRUE)) { - DEBUG_PRINTF("db_load_v3", "Duplicate key '%s'", - knod->entry.key); - knod = (struct key_node *)pool_free((pnode_t *)knod); - } - } - } else - ok = FALSE; - if (!ok) - if (knod) pool_free((pnode_t *)knod); - } else - break; - } - - return (ok); -} - - -static bool -db_load_v4(FILE *fh, long *lognum, off_t *logpos) -{ - struct key_node *knod = NULL; - int64_t val; - size_t len; - bool ok = TRUE; - - if (fread(lognum, sizeof(long), 1, fh) != 1 || - fread(logpos, sizeof(off_t), 1, fh) != 1) - ok = FALSE; - while (ok) { - if (fread(&val, sizeof(int64_t), 1, fh) == 1) { - if ((knod = (struct key_node *)pool_alloc(&key_pool, FALSE))) { - knod->entry.persistent = TRUE; - knod->entry.value = val; - knod->processed = FALSE; - if (fread(&knod->entry.created, sizeof(time_t), 1, fh) != 1 - || fread(&knod->entry.modified, sizeof(time_t), 1, - fh) != 1 - || fread(&knod->entry.uid, sizeof(uid_t), 1, - fh) != 1 - || fread(&len, sizeof(size_t), 1, fh) != 1 - || fread(&knod->entry.key, len + 1, 1, fh) != 1) - ok = FALSE; - else { - if (!hashtable_link(&key_table, (hashnode_t *)knod, - knod->entry.key, len + 1, TRUE)) { - DEBUG_PRINTF("db_load_v4", "Duplicate key '%s'", - knod->entry.key); - knod = (struct key_node *)pool_free((pnode_t *)knod); - } - } - } else - ok = FALSE; - if (!ok) - if (knod) pool_free((pnode_t *)knod); - } else - break; - } - - return (ok); -} - - -/* This format is in architecture-independent network order, and uses - * platform-independent word units instead of saving actual word sizes - * corresponding to a particular object type, - * (i.e. size_t, off_t, uid_t, time_t). - */ -static bool -db_load_v5(FILE *fh, u_int32_t *lognum, off_t *logpos) -{ - struct key_node *knod = NULL; - int64_t val64; - u_int32_t val32; - size_t len; - bool ok = FALSE; - - if (fread(lognum, sizeof(u_int32_t), 1, fh) != 1 || - fread(&val64, sizeof(u_int64_t), 1, fh) != 1) - return FALSE; - - *lognum = BYTEORDER_HOST32(*lognum); - *logpos = (off_t)BYTEORDER_HOST64((u_int32_t)val64); - - for (;;) { - /* Read and convert key current value */ - if (fread(&val64, sizeof(u_int64_t), 1, fh) == 1) { - val64 = (int64_t)BYTEORDER_HOST64(val64); - if ((knod = (struct key_node *)pool_alloc(&key_pool, FALSE))) { - - knod->entry.persistent = TRUE; - knod->entry.value = (int64_t)val64; - knod->processed = FALSE; - - /* Creation time */ - if (fread(&val32, sizeof(u_int32_t), 1, fh) != 1) - break; - knod->entry.created = (time_t)BYTEORDER_HOST32(val32); - - /* Modification time */ - if (fread(&val32, sizeof(u_int32_t), 1, fh) != 1) - break; - knod->entry.modified = (time_t)BYTEORDER_HOST32(val32); - - /* Owner user id */ - if (fread(&val32, sizeof(u_int32_t), 1, fh) != 1) - break; - knod->entry.uid = (uid_t)BYTEORDER_HOST32(val32); - - /* Key name string size */ - if (fread(&val32, sizeof(u_int32_t), 1, fh) != 1) - break; - len = (size_t)BYTEORDER_HOST32(val32); - - /* Actual key name string, this obviously doesn't need any - * kind of conversion. - */ - if (fread(&knod->entry.key, len + 1, 1, fh) != 1) - break; - - /* All entry loaded, link it in */ - if (!hashtable_link(&key_table, (hashnode_t *)knod, - knod->entry.key, len + 1, TRUE)) { - syslog(LOG_NOTICE, "db_load_v5() - Duplicate key '%s'", - knod->entry.key); - (void) pool_free((pnode_t *)knod); - } - - /* Set knod to NULL, this allows us to know to call - * pool_free() when above possible errors just break. - */ - knod = NULL; - - } else { - syslog(LOG_NOTICE, "db_load_v5() - Out of memory"); - break; - } - } else { - /* All entries were valid, but there just aren't any more */ - ok = TRUE; - break; - } - } - - if (knod != NULL) - (void) pool_free((pnode_t *)knod); - - return (ok); -} - - -/* This function syncs the memory db to disk with current sync info. - * If save was successful, obsolete recovery logs are deleted. - * We warn through syslog if the sync could not be performed. - * If all is TRUE, all logs are deleted after sync. - * The saved format is now platform independent and architecture independent. - * This means that we save everything in network order, and that we don't use - * platform dependent data types in the output, only fixed-sized words of - * known length. - */ -static void -db_sync(u_int32_t lognum, off_t logpos, bool all) -{ - char old_db[256], new_db[256]; - FILE *fh; - DIR *dir; - u_int32_t version = 0xFFFFFFFF - 5; - bool ok; - struct db_sync_iterator_udata data; - - /* We use a technique which permits to retrieve the previous state of - * the db in case we could not save properly, using rename(). - */ - ok = TRUE; - - snprintf(old_db, 255, "%s/mmstatd.db", CONF.ENV_DIR); - snprintf(new_db, 255, "%s/mmstatd.tdb", CONF.ENV_DIR); - syslog(LOG_NOTICE, "Synchronizing database to disk file '%s'", old_db); - - /* Use stdio buffering since we are about to perform many small writes */ - if ((fh = fopen(new_db, "wb")) != NULL) { - u_int64_t val64; - u_int32_t val32; - - (void) fchmod(fileno(fh), 0600); - /* Write db version and current sync state */ - version = BYTEORDER_NETWORK32(version); - val32 = BYTEORDER_NETWORK32(lognum); - val64 = BYTEORDER_NETWORK64((u_int64_t)logpos); - if (fwrite(&version, sizeof(u_int32_t), 1, fh) == 1 && - fwrite(&val32, sizeof(u_int32_t), 1, fh) == 1 && - fwrite(&val64, sizeof(u_int64_t), 1, fh) == 1) { - /* DB contents */ - data.fh = fh; - data.ok = TRUE; - hashtable_iterate(&key_table, db_sync_iterator, &data); - ok = data.ok; - } else - ok = FALSE; - - /* We always verified that fwrite(3) succeed, but we also must make - * sure that fsync(2) and close(2) do (the latter being called by - * fclose(3)). - */ - if (ok) { - if (fflush(fh) != 0 || fsync(fileno(fh)) != 0) - ok = FALSE; - } - while (fclose(fh) != 0) { - if (errno != EINTR) { - ok = FALSE; - break; - } - } - } else - ok = FALSE; - - if (ok) { - /* We are certain that the new file was properly written, we can now - * atomically replace the old database by the new one. - */ - if (rename(new_db, old_db) == -1) - ok = FALSE; - } - - if (!ok) { - /* Error while attempting to rename(), delete new saved file and keep - * old one, but log the error. - */ - unlink(new_db); - syslog(LOG_NOTICE, "db_sync() - Error writing database!"); - } else { - /* Scan for recovery logs and unlink obsolete ones */ - if ((dir = opendir(CONF.ENV_DIR))) { - char *name, filename[256]; - struct dirent *dire; - u_int32_t i; - - while ((dire = readdir(dir))) { - name = dire->d_name; - if (log_match(name, "????????.log")) { - i = (u_int32_t)strtoul(name, NULL, 10); - if (all || i < lognum) { - snprintf(filename, 255, "%s/%s", CONF.ENV_DIR, name); - unlink(filename); - } - } - } - closedir(dir); - } - } -} - - -static bool -db_sync_iterator(hashnode_t *hnod, void *udata) -{ - struct key_node *knod = (struct key_node *)hnod; - struct db_sync_iterator_udata *data = udata; - FILE *fh = data->fh; - - /* Only save persistent keys */ - if (knod->entry.persistent) { - size_t len; - u_int32_t val32; - u_int64_t val64; - - /* Default to false return value, so we can simply return on error. - * Returning with FALSE will cause the hashtable_t iteration to stop. - */ - data->ok = FALSE; - - /* Current value of key */ - val64 = BYTEORDER_NETWORK64((u_int64_t)knod->entry.value); - if (fwrite(&val64, sizeof(u_int64_t), 1, fh) != 1) - return FALSE; - - /* Creation time */ - val32 = BYTEORDER_NETWORK32((u_int32_t)knod->entry.created); - if (fwrite(&val32, sizeof(u_int32_t), 1, fh) != 1) - return FALSE; - - /* Last modification time */ - val32 = BYTEORDER_NETWORK32((u_int32_t)knod->entry.modified); - if (fwrite(&val32, sizeof(u_int32_t), 1, fh) != 1) - return FALSE; - - /* Key owner user id */ - val32 = BYTEORDER_NETWORK32((u_int32_t)knod->entry.uid); - if (fwrite(&val32, sizeof(u_int32_t), 1, fh) != 1) - return FALSE; - - /* Key name string size */ - len = mm_strlen(knod->entry.key); - val32 = BYTEORDER_NETWORK32((u_int32_t)len); - if (fwrite(&val32, sizeof(u_int32_t), 1, fh) != 1) - return FALSE; - - /* Key name string */ - if (fwrite(knod->entry.key, len + 1, 1, fh) != 1) - return FALSE; - - /* Everything ok, set success return value */ - data->ok = TRUE; - } - - return TRUE; -} - - -static void -db_free(void) -{ - if (HASHTABLE_VALID(&key_table)) - hashtable_destroy(&key_table, FALSE); - if (POOL_VALID(&key_pool)) - pool_destroy(&key_pool); -} - - -/* This function loads the db, attempts to perform recovery processing the - * logs, resyncs the db and unloads everything. This function is intended to - * be executed when the logging daemon process is not running. - */ -static void -db_recover(void) -{ - int fd; - u_int32_t lognum; - off_t logpos; - int len, total; - char filename[256]; - struct log_entry lentry[MAX_TRANSACT + 1]; - bool ok; - - syslog(LOG_NOTICE, "Recovering last db modifications from logs"); - - /* First obtain our last position in the last logfile so that we do not - * process logs which have already been applied to the db before last sync. - */ - ok = FALSE; - snprintf(filename, 255, "%s/%s", CONF.ENV_DIR, "mmstatd.db"); - db_load(&lognum, &logpos); - - /* If any, start processing logs, but make sure to only start after - * lognum/logpos, if there are any remaining, since we are only - * performing recovery of unsynced pending logs. - */ - snprintf(filename, 255, "%s/%08u.log", CONF.ENV_DIR, lognum); - total = 0; - if ((fd = open(filename, O_RDONLY)) != -1) { - if (logpos > 0) - (void) lseek(fd, logpos, SEEK_SET); - while ((len = logentries_read(-1, lentry, MAX_TRANSACT, &fd, &lognum, - &logpos))) { - total += len; - if (!logentries_process(lentry, len, FALSE)) { - DEBUG_PRINTF("db_recover", "logentries_process()"); - break; - } - } - (void) close(fd); - } - - if (total > 0) - syslog(LOG_NOTICE, "Recovered %d log entries", total); - else - syslog(LOG_NOTICE, "No entries needed recovery"); - - /* Sync back db to disk and delete obsolete recovery logs */ - lognum = 0; - logpos = 0; - db_sync(lognum, logpos, TRUE); - db_free(); -} - - -/* key char array should be KEY_SIZE bytes in size */ -static void -stats_write(int fd, const char *key) -{ - struct key_node *knod; - - /* Sanity check */ - if (key[KEY_SIZE - 1] == '\0') { - /* Verify if this consists of: '*' and/or '?' pattern, absolute - * key name or full report request - */ - if (*key == '\0') { - /* Full statistics report request */ - hashtable_iterate(&key_table, stats_write_iterator_full, &fd); - } else { - const char *ptr; - - for (ptr = key; *ptr != '\0'; ptr++) - if (*ptr == '*' || *ptr == '?') - break; - if (*ptr != '\0') { - struct stats_write_iterator_pattern_udata data; - - /* Key pattern matching report request */ - data.fd = fd; - data.pattern = key; - hashtable_iterate(&key_table, stats_write_iterator_pattern, - &data); - } else { - /* Absolute key report request */ - if ((knod = (struct key_node *)hashtable_lookup(&key_table, - key, mm_strlen(key) + 1)) != NULL) { - if (pipesend) - write(fd, &knod->entry, sizeof(mmstatent_t)); - } - } - } - } -} - - -static bool -stats_write_iterator_full(hashnode_t *hnod, void *udata) -{ - struct key_node *knod = (struct key_node *)hnod; - int fd = *(int *)udata; - - if (pipesend) - write(fd, &knod->entry, sizeof(mmstatent_t)); - - return pipesend; -} - - -static bool -stats_write_iterator_pattern(hashnode_t *hnod, void *udata) -{ - struct key_node *knod = (struct key_node *)hnod; - struct stats_write_iterator_pattern_udata *data = udata; - - if (pipesend) { - if (log_match(knod->entry.key, data->pattern)) - write(data->fd, &knod->entry, sizeof(mmstatent_t)); - } - - return pipesend; -} - - -static void -stats_rotate(const char *pattern, const char *prefix) -{ - char okey[KEY_SIZE + 1], nkey[KEY_SIZE + 1]; - - /* Sanity check */ - if (pattern[KEY_SIZE - 1] == '\0' && prefix[KEY_SIZE - 1] == '\0') { - struct stats_rotate_iterator_process_udata data; - - /* Because modifying a key requires to unlink and relink it to the - * table, and that the ordering of the iteration is undefined, we - * have to make sure not to attempt to process the same entry more - * than once, which could still be matching with the pattern. - * We therefore use a boolean flag (processed) to prevent this. - * Our iterator functions use it. - */ - data.pattern = pattern; - data.prefix = prefix; - data.okey = okey; - data.nkey = nkey; - hashtable_iterate(&key_table, stats_rotate_iterator_process, &data); - hashtable_iterate(&key_table, stats_rotate_iterator_clear, NULL); - } -} - - -static bool -stats_rotate_iterator_process(hashnode_t *hnod, void *udata) -{ - struct key_node *knod = (struct key_node *)hnod; - struct stats_rotate_iterator_process_udata *data = udata; - - /* We make sure to not process already processed keys, and to restore the - * old key if the renaming process causes a duplicate. - */ - if (knod->processed == FALSE && knod->entry.persistent && - log_match(knod->entry.key, data->pattern)) { - knod->processed = TRUE; - mm_memcpy(data->okey, knod->entry.key, KEY_SIZE); - hashtable_unlink(&key_table, (hashnode_t *)knod); - snprintf(data->nkey, KEY_SIZE - 1, "%s%s", data->prefix, - knod->entry.key); - mm_memcpy(knod->entry.key, data->nkey, KEY_SIZE); - if (!hashtable_link(&key_table, (hashnode_t *)knod, knod->entry.key, - mm_strlen(data->nkey) + 1, TRUE)) { - /* Would cause a duplicate, restore entry */ - syslog(LOG_NOTICE, "stats_rotate() - Rename of key '%s' to '%s' \ -would cause a duplicate", data->okey, data->nkey); - mm_memcpy(knod->entry.key, data->okey, KEY_SIZE); - hashtable_link(&key_table, (hashnode_t *)knod, knod->entry.key, - mm_strlen(data->okey), FALSE); - } - } - - return TRUE; -} - - -/* ARGSUSED */ -static bool -stats_rotate_iterator_clear(hashnode_t *hnod, void *udata) -{ - struct key_node *knod = (struct key_node *)hnod; - - knod->processed = FALSE; - - return TRUE; -} - - -int -main(int argc, char **argv) -{ - char *conf_file = "/usr/local/etc/mmstatd.conf", *tmp; - int ret = 0, ngids; - uid_t uid; - gid_t *gids; - long facility; - cres_t cres; - carg_t *cargp; - carg_t cargs[] = { - {CAT_STR, CAF_NONE, 1, 31, "USER", CONF.USER}, - {CAT_STR, CAF_NONE, 1, 255, "GROUPS", CONF.GROUPS}, - {CAT_STR, CAF_NONE, 1, 31, "LOG_FACILITY", CONF.LOG_FACILITY}, - {CAT_STR, CAF_NONE, 1, 255, "PID_FILE", CONF.PID_FILE}, - {CAT_STR, CAF_NONE, 1, 255, "LOCK_FILE", CONF.LOCK_FILE}, - {CAT_STR, CAF_NONE, 1, 255, "LOG_SOCKET", CONF.LOG_SOCKET}, - {CAT_STR, CAF_NONE, 1, 255, "STAT_SOCKET", CONF.STAT_SOCKET}, - {CAT_STR, CAF_NONE, 1, 127, "ENV_DIR", CONF.ENV_DIR}, - {CAT_STR, CAF_NONE, 1, 31, "LOG_GROUP", CONF.LOG_GROUP}, - {CAT_STR, CAF_NONE, 1, 31, "STAT_GROUP", CONF.STAT_GROUP}, - {CAT_VAL, CAF_NONE, 1, 99999999, "SYNC_INTERVAL", - &CONF.SYNC_INTERVAL}, - {CAT_VAL, CAF_NONE, 0, 99999999, "SYNC_BYTES", &CONF.SYNC_BYTES}, - {CAT_VAL, CAF_NONE, 1, 99999999, "MAX_LOGSIZE", &CONF.MAX_LOGSIZE}, - {CAT_VAL, CAF_NONE, 0, 9999, "STATS_RATE", &CONF.STATS_RATE}, - {CAT_VAL, CAF_NONE, 1, 9999, "STATS_TIME", &CONF.STATS_TIME}, - {CAT_END, CAF_NONE, 0, 0, NULL, NULL} - }; - cmap_t cmap[] = { - {"LOG_AUTH", LOG_AUTH}, - {"LOG_AUTHPRIV", LOG_AUTHPRIV}, - {"LOG_CRON", LOG_CRON}, - {"LOG_DAEMON", LOG_DAEMON}, - {"LOG_FTP", LOG_FTP}, - {"LOG_LPR", LOG_LPR}, - {"LOG_MAIL", LOG_MAIL}, - {"LOG_NEWS", LOG_NEWS}, - {"LOG_SYSLOG", LOG_SYSLOG}, - {"LOG_USER", LOG_USER}, - {"LOG_UUCP", LOG_UUCP}, - {NULL, 0} - }; - - /* Set defaults */ - mm_strcpy(CONF.USER, "mmstatd"); - mm_strcpy(CONF.GROUPS, "mmstat,staff"); - mm_strcpy(CONF.LOG_FACILITY, "LOG_AUTHPRIV"); - mm_strcpy(CONF.PID_FILE, "/var/mmstatd/mmstatd.pid"); - mm_strcpy(CONF.LOCK_FILE, "/var/mmstatd/mmstatd.lock"); - mm_strcpy(CONF.LOG_SOCKET, "/var/mmstatd/mmstatd_log.sock"); - mm_strcpy(CONF.STAT_SOCKET, "/var/mmstatd/mmstatd_stat.sock"); - mm_strcpy(CONF.ENV_DIR, "/var/mmstatd"); - mm_strcpy(CONF.LOG_GROUP, "mmstat"); - mm_strcpy(CONF.STAT_GROUP, "staff"); - CONF.SYNC_INTERVAL = 1800; - CONF.SYNC_BYTES = 4096; - CONF.MAX_LOGSIZE = 1048576; - CONF.STATS_RATE = 5; - CONF.STATS_TIME = 10; - - /* Read config file */ - if ((tmp = getenv("MMSTATCONF"))) - conf_file = tmp; - if (argc == 2) - conf_file = argv[1]; - if (!mmreadcfg(&cres, cargs, conf_file)) { - /* Error parsing configuration file, report which */ - printf("\nError parsing '%s'\n", conf_file); - printf("Error : %s\n", mmreadcfg_strerr(cres.CR_Err)); - if (*(cres.CR_Data)) printf("Data : %s\n", cres.CR_Data); - if ((cargp = cres.CR_Keyword) != NULL) { - printf("Keyword: %s\n", cargp->CA_Keyword); - printf("Minimum: %ld\n", cargp->CA_Min); - printf("Maximum: %ld\n", cargp->CA_Max); - } - if (cres.CR_Line != -1) - printf("Line : %d\n", cres.CR_Line); - printf("\n"); - exit(-1); - } - - if (!mmmapstring(cmap, CONF.LOG_FACILITY, &facility)) { - printf("\nUnknown syslog facility %s\n\n", CONF.LOG_FACILITY); - exit(-1); - } - openlog(DAEMON_NAME, LOG_PID | LOG_NDELAY, facility); - -#ifndef NODROPPRIVS - if ((getuid())) { - printf("\nOnly the super user may start this daemon\n\n"); - syslog(LOG_NOTICE, "* Only superuser can start me"); - exit(-1); - } -#else /* NODROPPRIVS */ - if ((getuid()) == 0) { - printf("\nCompiled with NODROPPRIVS, refusing to run as uid 0\n\n"); - syslog(LOG_NOTICE, "* NODROPPRIVS, refusing to run as uid 0"); - exit(-1); - } -#endif /* !NODROPPRIVS */ - - if (!lock_check(CONF.LOCK_FILE)) { - printf("\nmmstatd already running\n\n"); - exit(-1); - } - /* Post parsing */ - if ((uid = mmgetuid(CONF.USER)) == -1) { - printf("\nUnknown USER '%s'\n\n", CONF.USER); - exit(-1); - } - if (!(gids = mmgetgidarray(&ngids, CONF.GROUPS))) { - printf("\nOne or more of following GROUPS unknown: '%s'\n\n", - CONF.GROUPS); - exit(-1); - } - if ((CONF.log_group = mmgetgid(CONF.LOG_GROUP)) == -1) { - printf("\nUnknown LOG_GROUP '%s'\n\n", CONF.LOG_GROUP); - exit(-1); - } - if ((CONF.stat_group = mmgetgid(CONF.STAT_GROUP)) == -1) { - printf("\nUnknown STAT_GROUP '%s'\n\n", CONF.STAT_GROUP); - exit(-1); - } - CONF.max_logsize = (off_t)CONF.MAX_LOGSIZE; - - /* Initialization */ - librarian_pid = logger_pid = -1; - pipefds[0] = pipefds[1] = -1; - - printf("\r\n+++ %s (%s)\r\n\r\n", DAEMON_NAME, DAEMON_VERSION); - - /* Drop root privileges */ - if (mmdropprivs(uid, gids, ngids)) { - mmfreegidarray(gids); - /* Launch the librarian process */ - if ((librarian_pid = process_spawn(librarian_init, NULL, TRUE)) - == -1) { - DEBUG_PRINTF("main", "process_spawn(librarian)"); - ret = -1; - } - } else { - ret = -1; - DEBUG_PRINTF("main", "mmdropprivs()"); - } - - closelog(); - - return (ret); -} - - -static int -librarian_init(void *args) -{ - int fd; - - syslog(LOG_NOTICE, "Librarian process started"); - - /* Write PID file */ - if ((fd = open(CONF.PID_FILE, O_CREAT | O_TRUNC | O_WRONLY, 0600)) != -1) { - char str[16]; - snprintf(str, 15, "%d", getpid()); - write(fd, str, mm_strlen(str)); - close(fd); - } else - DEBUG_PRINTF("librarian_init", "Cannot write pid file"); - - /* Perform recovery */ - db_recover(); - - /* Prepare our notification pipe */ - if (!(pipe(pipefds))) { - - /* Start the logger process */ - if ((logger_pid = process_spawn(logger_init, NULL, FALSE)) != -1) { - char filename[256]; - u_int32_t lognum; - off_t logpos; - int ufd, lfd, max; - - close(pipefds[1]); - pipefds[1] = -1; - - if ((ufd = unix_init(CONF.STAT_SOCKET, CONF.stat_group, 0660, 16, - TRUE, TRUE)) != -1) { - /* Because we recovered, lognum and logpos will be 0 */ - db_load(&lognum, &logpos); - /* Make sure that we open for reading the file that the logger - * opened for writing - */ - snprintf(filename, 255, "%s/00000000.log", CONF.ENV_DIR); - max = 10; - while ((lfd = open(filename, O_RDONLY)) == -1 && max) { - sleep(1); - max--; - } - if (lfd != -1) { - /* Finally start main loop */ - librarian_main(pipefds[0], ufd, lfd, &lognum, &logpos); - close(lfd); - } else - DEBUG_PRINTF("librarian_init", "open(%s)", filename); - db_sync(lognum, logpos, FALSE); - db_free(); - close(ufd); - unlink(CONF.STAT_SOCKET); - } else - DEBUG_PRINTF("librarian_init", - "unix_init(%s)", CONF.STAT_SOCKET); - } else - DEBUG_PRINTF("librarian_init", "process_spawn(logger)"); - - close(pipefds[0]); - pipefds[0] = -1; - } else - DEBUG_PRINTF("librarian_init", "pipe()"); - - unlink(CONF.PID_FILE); - - /* Kill logger daemon */ - if (logger_pid != -1) { - int status; - if (!kill(logger_pid, SIGTERM)) waitpid(logger_pid, &status, 0); - } - - syslog(LOG_NOTICE, "Exiting librarian"); - return (0); -} - - -/* Here consists of the main librarian server process. It's function consists - * in following the logs created by the logger process in an ASYNC manner, - * and process them, managing the database. It also performs total SYNC of the - * database to disk at fixed intervals, cleaning up obsolete recovery logs. - */ -static void -librarian_main(int pfd, int ufd, int lfd, u_int32_t *lognum, off_t *logpos) -{ - time_t otim; - int len; - long secs; - struct log_entry entries[MAX_TRANSACT + 1]; - struct pollfd fds[] = { - {pfd, POLLIN, 0}, - {ufd, POLLIN, 0}, - }; - struct limitrate lr; - -#ifndef __GLIBC__ - setproctitle("Librarian process"); -#endif - - secs = 0; /* Used to time delay between syncs */ - LR_INIT(&lr, CONF.STATS_RATE, CONF.STATS_TIME, time(NULL)); - for (;;) { - otim = time(NULL); - if (poll(fds, 2, 60000) > 0) { - /* Process more log entries if any */ - if (fds[0].revents & POLLIN) { - if ((len = logentries_read(pfd, entries, MAX_TRANSACT, &lfd, - lognum, logpos))) - logentries_process(entries, len, TRUE); - } - /* Verify if we obtain a report request connection */ - if (fds[1].revents & POLLIN) { - socklen_t addrl; - struct sockaddr addr; - int sfd; - char key[KEY_SIZE + 1], key2[KEY_SIZE + 1], c; - - /* Accept connection and send status report */ - addrl = sizeof(struct sockaddr); - pipesend = TRUE; - if ((sfd = accept(ufd, &addr, &addrl)) != -1) { - struct pollfd fds2[] = { - {sfd, POLLIN, 0} - }; - - /* Make sure to drop connection immediately if rate - * was exceeded - */ - if (CONF.STATS_RATE == 0 || - lr_allow(&lr, 1, time(NULL), TRUE)) { - if (pipesend) write(sfd, "+", 1); - if (poll(fds2, 1, 250) == 1) { - if (fds2[0].revents & POLLIN) { - if (read(sfd, &c, 1) == 1) { - if (c == 's') { - if (read(sfd, key, KEY_SIZE) == - KEY_SIZE) { - shutdown(sfd, SHUT_RD); - if (pipesend) stats_write(sfd, key); - } - } else if (c == 'r') { - if (read(sfd, key, KEY_SIZE) == - KEY_SIZE && - read(sfd, key2, KEY_SIZE) == - KEY_SIZE) { - stats_rotate(key, key2); - /* Force immediate db sync */ - secs += CONF.SYNC_INTERVAL; - } - } else - syslog(LOG_NOTICE, - "librarian_main() - invalid req"); - } else - DEBUG_PRINTF("librarian_main", - "read(%d)", sfd); - } - } - } else if (pipesend) - write(sfd, "-", 1); - close(sfd); - } - } - } - /* Verify if it's time for a sync */ - secs += (time(NULL) - otim); - if (secs > CONF.SYNC_INTERVAL) { - secs = 0; - db_sync(*lognum, *logpos, FALSE); - } - } - /* NOTREACHED */ -} - - -static int -logger_init(void *args) -{ - char filename[256]; - u_int32_t lognum; - off_t logpos; - int ufd, lfd; - - syslog(LOG_NOTICE, "Logger process started"); - - close(pipefds[0]); - pipefds[0] = -1; - lognum = 0; - logpos = 0; - - if ((ufd = unix_init(CONF.LOG_SOCKET, CONF.log_group, 0220, 0, FALSE, - TRUE)) != -1) { - snprintf(filename, 255, "%s/00000000.log", CONF.ENV_DIR); - if ((lfd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0600)) != -1) { - fsync(lfd); - logger_main(pipefds[1], ufd, lfd, &lognum, &logpos); - close(lfd); - } else - DEBUG_PRINTF("logger_init", "open(%s)", filename); - close(ufd); - unlink(CONF.LOG_SOCKET); - } else - DEBUG_PRINTF("logger_init", "unix_init(%s)", CONF.LOG_SOCKET); - close(pipefds[1]); - pipefds[1] = -1; - - syslog(LOG_NOTICE, "Exiting logger"); - return (0); -} - - -/* Here consists of the logger process server, using unix domain sockets. - * It's function is to obtain single as well as transaction locked requests - * from the various processes, and to generate recovery logs and sync them - * to disk as soon as possible. It is of major importance that this process - * perform all required sanity checking on the input datagrams, thus preventing - * packet attacks resulting in undefined behavior. We do not allow the client - * to generate STAT_NEWFILE or STAT_TRANSACT control packets, and we ensure - * that they only send valid packet lengths, particularly valid key string - * as well. Morover, we make sure to set the euid of the datagram originator. - * - * Note that we obtain the packet in host endian order, which we dispatch to - * logentries_write(), which will perform required convertion to network - * byte order. - * - * XXX eventually add AF_LOCAL user credential checking here... - */ -static void -logger_main(int pfd, int ufd, int lfd, u_int32_t *lognum, off_t *logpos) -{ - uid_t uid, euid; - int len, l, i, i2, t; - char *tmp; - struct log_entry aentries[MAX_TRANSACT + 2], *entries; - struct pollfd fds[] = { - {ufd, POLLIN, 0} - }; - -#ifndef __GLIBC__ - setproctitle("Logger process"); -#endif - - /* Prepare transaction header and footer entries, the footer will need - * to be copied earlier in the buffer when the transaction packet is - * smaller than MAX_TRANSACT, to allow using one write() only. - */ - entries = &(aentries[1]); - mm_memclr(aentries, sizeof(struct log_entry)); - aentries->type = STAT_TRANSACT; - mm_memcpy(&(aentries[MAX_TRANSACT + 1]), aentries, - sizeof(struct log_entry)); - aentries->un.transact.begin = TRUE; - - for (;;) { - if (poll(fds, 1, -1) > 0) { - if (fds[0].revents & POLLIN) { - /* New packet to log, may consist of a single operation or - * of transaction-protected atomic operations array. - */ - if ((len = recvfrom(ufd, entries, - sizeof(struct log_entry) * MAX_TRANSACT, - MSG_WAITALL, NULL, NULL)) > 0) { - /* XXX Eventually obtain packet sender credentials */ - uid = euid = 0; - /* Perform some sanity checking, first verify packet size */ - l = 0; - i2 = 1; - if ((len >= sizeof(struct log_entry)) && - (len <= sizeof(struct log_entry) * MAX_TRANSACT) && - ((len % sizeof(struct log_entry)) == 0)) { - /* Now verify packet type and key C string validity - * XXX Should eventually use cleaner code here - */ - l = len / sizeof(struct log_entry); - for (i2 = 0; i2 < l; i2++) { - t = (int)entries[i2].type; - if (t != STAT_UPDATE && t != STAT_RESET && - t != STAT_DELETE) - break; - tmp = entries[i2].key; - for (i = 0; i < KEY_SIZE; i++) - if (tmp[i] == '\0' || tmp[i] < 33 || - tmp[i] == '%') - break; - if (i < 1 || i > KEY_SIZE - 1 || - (tmp[i] != '\0' && - (tmp[i] < 33 || tmp[i] == '%'))) - break; - /* Make sure that packet originator uid cannot be - * spoofed - */ - entries[i2].uid = (u_int32_t)euid; - } - } - if (i2 == l) { - /* This packet at least won't crash us, it will - * simply be ignored if invalid to this point. - * Perform some magic before writing the entry if - * it consists of a transaction. - */ - if (len == sizeof(struct log_entry)) { - if (!logentries_write(pfd, entries, 1, &lfd, - lognum, logpos)) - syslog(LOG_NOTICE, - "logger_main() - Error writing logs!"); - } else { - t = len / sizeof(struct log_entry); - if (t < MAX_TRANSACT) - mm_memcpy(&entries[t], - &aentries[MAX_TRANSACT + 1], - sizeof(struct log_entry)); - t += 2; - if (!logentries_write(pfd, aentries, t, &lfd, - lognum, logpos)) - syslog(LOG_NOTICE, - "logger_main() - Error writing logs!"); - } - } else - syslog(LOG_NOTICE, - "Illegal packet from uid %d, euid %d, %d bytes", - uid, euid, len); - } else - DEBUG_PRINTF("logger_main", "recvfrom(%d)", ufd); - } - } - } - /* NOTREACHED */ -} diff --git a/mmsoftware/mmstatd/src/mmstatd.conf.5 b/mmsoftware/mmstatd/src/mmstatd.conf.5 deleted file mode 100644 index a32b39d..0000000 --- a/mmsoftware/mmstatd/src/mmstatd.conf.5 +++ /dev/null @@ -1,213 +0,0 @@ -.\" $Id: mmstatd.conf.5,v 1.5 2004/05/05 23:59:58 mmondor Exp $ -.\" -.\" Copyright (C) 2002-2004, Matthew Mondor -.\" All rights reserved. -.\" -.\" Redistribution and use in source and binary forms, with or without -.\" modification, are permitted provided that the following conditions -.\" are met: -.\" 1. Redistributions of source code must retain the above copyright -.\" notice, this list of conditions and the following disclaimer. -.\" 2. Redistributions in binary form must reproduce the above copyright -.\" notice, this list of conditions and the following disclaimer in the -.\" documentation and/or other materials provided with the distribution. -.\" 3. All advertising materials mentioning features or use of this software -.\" must display the following acknowledgement: -.\" This product includes software written by Matthew Mondor. -.\" 4. The name of Matthew Mondor may not be used to endorse or promote -.\" products derived from this software without specific prior written -.\" permission. -.\" -.\" THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR -.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES -.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. -.\" IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, -.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -.\" BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF -.\" USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF -.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -.\" -.Dd 11 Dec, 2002 -.Dt MMSTATD.CONF 5 -.Os -.Sh NAME -.Nm mmstatd.conf -.Nd -.Xr mmstatd.conf 5 -configuration file for mmstatd and mmstat -.Sh DESCRIPTION -The -.Nm /usr/local/etc/mmstatd.conf -file may contain one or more keyword/value pairs per line, empty lines or -comments. A ';' or '#' character causes all other characters to be considered -as a comment, and to be ignored, until the end of the current line. It is -very important to enclose the value for a keyword in double quotes ('"' -characters) if the following characters are found in it: ';', '#', space and -tab. Here are documented the various possible keywords and their -allowed values, as well as their defaults, and mmstatd/mmstat relation. -.Pp -.Nm These are both used by mmstatd and mmstat: -.Pp -.Bl -tag -width XXXXXXXX -offset indent -compact -.It Nm LOG_SOCKET Ar "file" -Location of statistics update request socket, to which the logger process -of -.Nm mmstatd -listens to. -.Pp -.It Nm STAT_SOCKET Ar "file" -Location of statistics report request socket, to which the librarian process -of -.Nm mmstatd listens to. -.El -.Pp -.Nm These are only used by mmstatd: -.Pp -.Bl -tag -width XXXXXXXX -offset indent -compact -.It Nm USER Ar "user" -Unprivileged user service should run as. -.Pp -.It Nm GROUPS Ar "group,..." -Groups mmstatd should be part of, separated by commas, without spaces. -The groups specified -for -.Ar LOG_GROUP -and -.Ar STAT_GROUP -should also be specified here. -.Pp -.It Nm LOG_FACILITY Ar "facility" -Syslog facility which should be used for error logging. Should normally be -one of -.Sy LOG_AUTH LOG_AUTHPRIV LOG_CRON LOG_DAEMON LOG_FTP LOG_KERN LOG_LPR -.Sy LOG_MAIL LOG_NEWS LOG_SYSLOG LOG_USER LOG_UUCP -, see -.Xr syslog 3 -man page for more information. -.Pp -.It Nm PID_FILE Ar "file" -Location where mmstatd writes it's pid file. This consists of a small file -holding the process ID of -.Nm mmstatd -which can be used by scripts or the administrator to kill the daemon properly. -.Pp -.It Nm LOCK_FILE Ar "file" -Location of mmstatd lock file, this is only used to make sure that only -one copy of the service is running, otherwise this could lead to database -corruption. -.Pp -.It Nm ENV_DIR Ar "directory" -Location where database and recovery logs are internally, automatically stored -and managed by -.Nm mmstatd -daemon. -.Pp -.It Nm LOG_GROUP Ar "group" -.Ar LOG_SOCKET -is created with permission mode 220. This permits users of the specified -group to perform statistic update requests, using the -.Xr mmstat 3 -library interface. Don't forget to also specify this group name into -.Ar GROUPS -variable. -.Pp -.It Nm STAT_GROUP Ar "group" -.Ar STAT_SOCKET -is created with permission mode 660. This permits users of the specified -group to obtain statistic reports or to rotate statistics using the -.Xr mmstat 3 -library interface, or -.Xr mmstat 8 -utility. Don't forget to also specify this group name into -.Ar GROUPS -variable. -.Pp -.It Nm SYNC_INTERVAL Ar "seconds" -Specifies the interval in seconds at which the statistics db will be -synchronized to disk. This delay can be long enough, as a good log-based -recovery technique is used in case system crashed between two sync events. -.Pp -.It Nm SYNC_BYTES Ar "bytes" -Maximum number of bytes to write to recovery logs before forcing a sync -with physical media (using fdatasync()). 0 Can be specified to force a sync -after every new entry; Higher values may cause some of the last update -requests before a crash to be lost but will be more efficient. -.Pp -.It Nm MAX_LOGSIZE Ar "bytes" -Maximum size of a recovery log file, in bytes. When reaching that size -internal rotation to other files is performed. Logs are internally maintained -and cleaned up as necessary by mmstatd and are not user serviceable. -.Pp -.It Nm STATS_RATE Ar "times" -Maximum number of times Unix stream connections to -.Ar STAT_SOCKET -can be performed within -.Ar STATS_TIME -period. This prevents an application from requesting a large number of full -reports thus potentially preventing the librarian part of -.Nm mmstatd -to peform its vital tasks. As a general rule reports are not requested -frequently. If necessary, 0 can be specified for no limit, and care should be -taken to choose an adequate -.Ar STAT_GROUP -to restrict access. -.Pp -.It Nm STATS_TIME Ar "seconds" -Period in seconds during which a maximum of -.Ar STATS_RATE -connections are allowed to occur. -.El -.Sh DEFAULTS -The following defaults are used: -.Pp -.Bd -literal -offset indent -compact -USER mmstatd -GROUPS mmstat,staff -LOG_FACILITY LOG_AUTHPRIV -PID_FILE "/var/mmstatd/mmstatd.pid" -LOCK_FILE "/var/mmstatd/mmstatd.lock" -LOG_SOCKET "/var/mmstatd/mmstatd_log.sock" -STAT_SOCKET "/var/mmstatd/mmstatd_stat.sock" -ENV_DIR "/var/mmstatd" -LOG_GROUP mmstat -STAT_GROUP staff -SYNC_INTERVAL 1800 -SYNC_BYTES 4096 -MAX_LOGSIZE 1048576 -STATS_RATE 5 -STATS_TIME 10 -.Ed -.Sh AUTHOR -mmstat and related daemon and library were written by Matthew Mondor, -and are Copyright (c) 2002-2004, Matthew Mondor, All rights reserved. -It originally was written for the mmftpd and mmmail suite of daemons by the -same author. -.Sh FILES -.Bl -tag -width XXXXXXXXXXXXXXXXXXXX -compact -.It Pa -Headerfile used both by -.Nm mmstatd -daemon and -.Nm mmstat -utility, defining related -.Ar MAX_TRANSACT -variable. -.Pp -.It Pa /usr/local/etc/mmstatd.conf -Configuration file for both -.Nm mmstatd -daemon and -.Nm mmstat -utility. (This file) -.El -.Sh SEE ALSO -.Xr mmstat 3 , -.Xr mmstat 8 , -.Xr mmstatd 8 , -.Xr syslog 3 , -.Xr fdatasync 2 , -.Xr fsync 2 . -.Sh BUGS -Please report any bug to mmondor@accela.net diff --git a/mmsoftware/mmstatd/src/mmstatd.h b/mmsoftware/mmstatd/src/mmstatd.h deleted file mode 100644 index 99ddc00..0000000 --- a/mmsoftware/mmstatd/src/mmstatd.h +++ /dev/null @@ -1,128 +0,0 @@ -/* $Id: mmstatd.h,v 1.10 2004/06/01 23:52:43 mmondor Exp $ */ - -/* - * Copyright (C) 2002-2004, Matthew Mondor - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software written by Matthew Mondor. - * 4. The name of Matthew Mondor may not be used to endorse or promote - * products derived from this software without specific prior written - * permission. - * - * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - - - - -#ifndef MMSTATD_H -#define MMSTATD_H - - - - -#include - -#include -#include -#include -#include - - - - -#define DAEMON_NAME "mmstatd" -#define DAEMON_VERSION "0.0.9/mmondor" - - - - -struct key_node { - hashnode_t node; - mmstatent_t entry; - bool processed; -}; - -struct db_sync_iterator_udata { - FILE *fh; - bool ok; -}; - -struct stats_write_iterator_pattern_udata { - int fd; - const char *pattern; -}; - -struct stats_rotate_iterator_process_udata { - const char *pattern; - const char *prefix; - char *okey, *nkey; -}; - - - - -static pid_t process_spawn(int (*)(void *), void *, bool); -static void sighandler(int); -static bool lock_check(const char *); -static int unix_init(const char *, gid_t, mode_t, int, bool, bool); -static bool log_match(const char *, const char *); -static bool logentries_write(int, struct log_entry *, int, int *, - u_int32_t *, off_t *); -static bool logentry_read(int, struct log_entry *, int *, - u_int32_t *, off_t *); -static int logentries_read(int, struct log_entry *, int, int *, - u_int32_t *, off_t *); -static bool logentry_process(struct log_entry *, bool); -static bool logentry_process_iterator(hashnode_t *, void *); -static bool logentries_process(struct log_entry *, int, bool); -/* static void logentry_debug(char, struct log_entry *); */ -static void db_load(u_int32_t *, off_t *); -static bool db_load_v1(FILE *, long *, long *); -static bool db_load_v2(FILE *, long *, off_t *); -static bool db_load_v3(FILE *, long *, off_t *); -static bool db_load_v4(FILE *, long *, off_t *); -static bool db_load_v5(FILE *, u_int32_t *, off_t *); -static void db_sync(u_int32_t, off_t, bool); -static bool db_sync_iterator(hashnode_t *, void *); -static void db_free(void); -static void db_recover(void); -static void stats_write(int, const char *); -static bool stats_write_iterator_full(hashnode_t *, void *); -static bool stats_write_iterator_pattern(hashnode_t *, void *); -static void stats_rotate(const char *, const char *); -static bool stats_rotate_iterator_process(hashnode_t *, void *); -static bool stats_rotate_iterator_clear(hashnode_t *, void *); - -int main(int, char **); - -static int librarian_init(void *); -static void librarian_main(int, int, int, u_int32_t *, off_t *); -static int logger_init(void *); -static void logger_main(int, int, int, u_int32_t *, off_t *); - - - - - - -#endif diff --git a/mmsoftware/mmsucom/GNUmakefile b/mmsoftware/mmsucom/GNUmakefile deleted file mode 100644 index 9f66a3f..0000000 --- a/mmsoftware/mmsucom/GNUmakefile +++ /dev/null @@ -1,28 +0,0 @@ -# $Id: GNUmakefile,v 1.1 2004/09/24 19:40:04 mmondor Exp $ - -MMLIB_PATH := ../mmlib - -MMLIBS := $(addprefix ${MMLIB_PATH}/,mmarch.o mmpool.o mmlog.o mmstring.o) -OBJS := $(addprefix ,mmsucom.o mmsucomd.o) -BINS := $(addprefix ,mmsucom mmsucomd) -CFLAGS += -Wall - -all: $(BINS) - -%.o: %.c - cc -c ${CFLAGS} -I. -I${MMLIB_PATH} -o $@ $< - - -mmsucom: $(MMLIBS) mmsucom.o - cc -o $@ mmsucom.o -lc $(MMLIBS) - -mmsucomd: $(MMLIBS) mmsucomd.o - cc -o $@ mmsucomd.o -lc $(MMLIBS) - - -install: all - install -cs -o 0 -g 0 -m 500 mmsucomd /usr/local/sbin - install -cs -o 0 -g 0 -m 550 mmsucom /usr/local/sbin - -clean: - rm -f $(BINS) $(OBJS) $(MMLIBS) diff --git a/mmsoftware/mmsucom/Makefile b/mmsoftware/mmsucom/Makefile deleted file mode 100644 index b41b25a..0000000 --- a/mmsoftware/mmsucom/Makefile +++ /dev/null @@ -1,37 +0,0 @@ -# $Id: Makefile,v 1.1 2002/12/11 10:16:51 mmondor Exp $ - -CC = gcc -MAKE = make -#STRIP = strip - -CFLAGS += -Wall - -INCDIR = -I../mmlib -I/usr/include -I/usr/pkg/include -I. -LIBDIR = -L/usr/local/lib -L/usr/lib -L/lib -L/usr/pkg/lib - -LIBS = ../mmlib/libmmondor.a -lc - -OBJS = mmsucomd.o - - - -CCOBJ = $(CC) $(CFLAGS) -c $(INCDIR) - - - - -mmsucomd: $(OBJS) - $(CC) $(CFLAGS) -o mmsucomd $(INCDIR) $(LIBDIR) $(OBJS) $(LIBS) - $(CC) $(CFLAGS) -o mmsucom $(INCDIR) $(LIBDIR) mmsucom.c $(LIBS) -# $(STRIP) -s mmsucomd - - - - -clean: - -rm -f $(OBJS) mmsucomd mmsucom - - -mmsucomd.o: - $(CCOBJ) mmsucomd.c - diff --git a/mmsoftware/mmsucom/README b/mmsoftware/mmsucom/README deleted file mode 100644 index 754c25a..0000000 --- a/mmsoftware/mmsucom/README +++ /dev/null @@ -1,28 +0,0 @@ -$Id: README,v 1.1 2002/12/11 10:16:51 mmondor Exp $ - - -This consists of a little sudo replacement, a very simple one actually. -It is meant to allow some users right to execute specific commands without -user-provided arguments, under the priviledges of another user, like uid 0. - -I mainly use it so that users on NetBSD desktop systems can mount and umount -floppies and cdrom. To execute a command, user has to have write access to -the mmsucomd socket, thus being of the required group to access the file. -That user may then invoque commands using keys, as set in /etc/mmsucomd.conf, -via the mmsucom client. - -Here consists of a little example: - -/etc/mmsucomd.conf ------------------- -m_cd root wheel /sbin/mount /cdrom -um_cd root wheel /sbin/umount /cdrom -m_fl root wheel /sbin/mount /floppy -um_fl root wheel /sbin/umount /floppy -page root wheel /bin/ksh -c "echo -ne '\007' >/dev/ttyE0" - -$ mmsucom m_cd -$ cd /cdrom - -This mostly consists of a quick hack, although it should be pretty safe -when well setup by the administrator. diff --git a/mmsoftware/mmsucom/mmsucom.c b/mmsoftware/mmsucom/mmsucom.c deleted file mode 100644 index 1dfec4e..0000000 --- a/mmsoftware/mmsucom/mmsucom.c +++ /dev/null @@ -1,101 +0,0 @@ -/* $Id: mmsucom.c,v 1.5 2005/05/14 04:58:17 mmondor Exp $ */ - -/* - * Copyright (C) 2002-2003, Matthew Mondor - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software written by Matthew Mondor. - * 4. The name of Matthew Mondor may not be used to endorse or promote - * products derived from this software without specific prior written - * permission. - * - * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - - - - -/* HEADERS */ - -#include -#include -#include -#include -#include -#include -#include - -#include -#include - - - - -MMCOPYRIGHT("@(#) Copyright (c) 2002-2003\n\ -\tMatthew Mondor. All rights reserved.\n"); -MMRCSID("$Id: mmsucom.c,v 1.5 2005/05/14 04:58:17 mmondor Exp $"); - - - - -/* DEFINITIONS */ - -/* Where socket should be */ -#define REQSOCKET "/var/run/mmsucomd.sock" - -struct request { - u_int64_t id; -}; - -int main(int, char **); - - - - -/* FUNCTIONS */ - -int -main(int argc, char **argv) -{ - int sock; - struct sockaddr_un addr; - socklen_t addrl; - struct request req; - - if (argc == 2) { - if ((sock = socket(AF_LOCAL, SOCK_DGRAM, 0)) != -1) { - mm_strncpy(addr.sun_path, REQSOCKET, 100); - addr.sun_family = AF_UNIX; - addrl = sizeof(struct sockaddr_un); - req.id = mm_strhash64(argv[1]); - if ((sendto(sock, &req, sizeof(struct request), 0, - (struct sockaddr *)&addr, addrl)) != - sizeof(struct request)) - printf("\nError sending to mmsucomd.sock\n\n"); - close(sock); - } else - printf("\nError at socket() creation\n\n"); - } else - printf("\nUsage: mmsucom \n\n"); - - exit(0); -} diff --git a/mmsoftware/mmsucom/mmsucomd.c b/mmsoftware/mmsucom/mmsucomd.c deleted file mode 100644 index 9a97c76..0000000 --- a/mmsoftware/mmsucom/mmsucomd.c +++ /dev/null @@ -1,453 +0,0 @@ -/* $Id: mmsucomd.c,v 1.10 2005/05/14 04:58:17 mmondor Exp $ */ - -/* - * Copyright (C) 2002-2003, Matthew Mondor - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software written by Matthew Mondor. - * 4. The name of Matthew Mondor may not be used to endorse or promote - * products derived from this software without specific prior written - * permission. - * - * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - - - - -/* HEADERS */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - - - - -MMCOPYRIGHT("@(#) Copyright (c) 2002-2003\n\ -\tMatthew Mondor. All rights reserved.\n"); -MMRCSID("$Id: mmsucomd.c,v 1.10 2005/05/14 04:58:17 mmondor Exp $"); - - - - -/* DEFINITIONS */ - -/* For syslog */ -#define SERVERNAME "mmsucomd" -/* Where pid file should go */ -#define PIDFILE "/var/run/mmsucomd.pid" -/* Config file location */ -#define CONFFILE "/usr/local/etc/mmsucomd.conf" -/* Where socket should be */ -#define REQSOCKET "/var/run/mmsucomd.sock" -#define REQGROUP 100 /* Group that can send reqs */ -#define MAX_RATE 5 /* Rate we execute commands */ - -struct request { - u_int64_t id; -}; - -struct conf_node { - pnode_t node; - u_int64_t id; - int uid, gid; - char command[256]; -}; - - - - -/* PROTOTYPES */ - -static pid_t spawn_process(int (*)(void *), void *); -static bool drop_privs(int, int); -static void sighandler(int); -static int unix_init(char *, int, int, int); -static bool conf_read(void); -static void exec_command(struct conf_node *); - -int main(void); - -static int sucomd_init(void *); -static void sucomd_main(int); - - - - -/* GLOBALS */ - -static pid_t sucomd_pid; - -/* This is set to FALSE when a process receives a SIGTERM */ -static bool run = TRUE, conf = FALSE; - -/* Configuration */ -static pool_t cpool; -static list_t clist; - - - - -/* FUNCTIONS */ - -static -pid_t spawn_process(int (*function)(void *), void *params) -{ - pid_t pid = -1; - int fd; - - /* Create new process */ - if (!(pid = fork())) { - struct sigaction act; - - /* Child */ - setsid(); - chdir("/"); - umask(0); - - /* Make sure that stdin, stdout and stderr are safe */ - if ((fd = open("/dev/null", O_RDWR)) != -1) { - dup2(fd, 0); - dup2(fd, 1); - dup2(fd, 2); - if (fd > 2) - close(fd); - } - - /* Setup our break handler */ - act.sa_handler = sighandler; - act.sa_flags = SA_NOCLDWAIT; - sigemptyset(&act.sa_mask); - sigaction(SIGTERM, &act, NULL); - sigaction(SIGHUP, &act, NULL); - sigaction(SIGCHLD, &act, NULL); - - /* Signals we want to ignore */ - signal(SIGTTOU, SIG_IGN); - signal(SIGTTIN, SIG_IGN); - signal(SIGTSTP, SIG_IGN); - /*signal(SIGPIPE, SIG_IGN); */ - - /* Simply call the wanted child function */ - exit(function(params)); - - } - - /* Parent */ - return (pid); -} - - -static bool -drop_privs(int uid, int gid) -{ - int cuid, cgid; - - cuid = getuid(); - cgid = getgid(); - - if (cuid != uid || cgid != gid) { - if (!cuid) { - /* We are root and thus can/must switch */ - if ((!(setgid(gid))) && (!(setuid(uid)))) - return (TRUE); - } - } else - /* We already were running from the proper user and group */ - return (TRUE); - - return (FALSE); -} - - -static void -sighandler(int sig) -{ - switch (sig) { - case SIGTERM: - syslog(LOG_NOTICE, "Received SIGTERM, cleaning up"); - run = FALSE; - break; - case SIGHUP: - syslog(LOG_NOTICE, "Received SIGHUP, reading config"); - conf = TRUE; - break; - case SIGCHLD: - { - int s = 0; - - while ((wait3(&s, WNOHANG, NULL)) > 0) ; - } - break; - default: - syslog(LOG_NOTICE, "Signal handler catched unexpected signal"); - break; - } -} - - -static int -unix_init(char *name, int group, int mode, int backlog) -{ - int sock; - struct sockaddr_un sau; - - /* Open public UNIX domain socket */ - if ((sock = socket(AF_LOCAL, SOCK_DGRAM, 0)) != -1) { - mm_strncpy(sau.sun_path, name, 100); - sau.sun_family = AF_UNIX; - if (bind(sock, (struct sockaddr *)&sau, sizeof(struct sockaddr_un)) - != -1) { - if (!chmod(name, mode)) { - if (!chown(name, -1, group)) { - return (sock); - } else - syslog(LOG_NOTICE, "unix_init() - fchown()"); - } else - syslog(LOG_NOTICE, "unix_init() - fchmod()"); - } else - syslog(LOG_NOTICE, "unix_init() - bind()"); - close(sock); - } else - syslog(LOG_NOTICE, "unix_init() - socket()"); - - return (-1); -} - - -static bool conf_read(void) -{ - u_int64_t id; - bool ok = FALSE; - FILE *fh; - int uid, gid, l; - char buf[256], *args[5]; - struct passwd *tuid; - struct group *tgid; - struct conf_node *cnod; - - DLIST_INIT(&clist); - if (POOL_VALID(&cpool)) - pool_destroy(&cpool); - if (pool_init(&cpool, "cpool", malloc, free, NULL, NULL, - sizeof(struct conf_node), 64, 0, 0)) { - if ((fh = fopen(CONFFILE, "r")) != NULL) { - ok = TRUE; - l = 0; - while ((fgets(buf, 256, fh))) { - l++; - /* id user group command line */ - if (mm_strspl(args, buf, 4, '\t') == 4) { - if ((tuid = getpwnam(args[1])) != NULL) { - uid = tuid->pw_uid; - if ((tgid = getgrnam(args[2])) != NULL) { - gid = tgid->gr_gid; - id = mm_strhash64(args[0]); - DLIST_FOREACH(&clist, cnod) - if (cnod->id == id) - break; - if (cnod == NULL) { - if ((cnod = (struct conf_node *)pool_alloc( - &cpool, FALSE))) { - cnod->id = id; - cnod->uid = uid; - cnod->gid = gid; - mm_strncpy(cnod->command, args[3], 255); - DLIST_APPEND(&clist, (node_t *)cnod); - } else - syslog(LOG_NOTICE, - "conf_read() - pool_alloc()"); - } else - syslog(LOG_NOTICE, - "conf_read() - Duplicate key '%s'", - args[0]); - } else - syslog(LOG_NOTICE, - "conf_read() - Unknown group '%s'", - args[2]); - } else - syslog(LOG_NOTICE, - "conf_read() - Unknown user '%s'", args[1]); - } else - syslog(LOG_NOTICE, - "conf_read() - Malformed config file at line %d", - l); - } - } else - syslog(LOG_NOTICE, "conf_read() - open(%s)", CONFFILE); - fclose(fh); - } else - syslog(LOG_NOTICE, "conf_read() - pool_init()"); - - return (ok); -} - - -static void -exec_command(struct conf_node *cnod) -{ - pid_t pid; - - if ((pid = fork()) == 0) { - extern char **environ; - char *args[] = { - "sh", "-c", cnod->command, NULL - }; - - if (drop_privs(cnod->uid, cnod->gid)) { - execve("/bin/sh", args, environ); - /* If we get here an error occured */ - syslog(LOG_NOTICE, "exec_command() - Error executing '%s'", - cnod->command); - } else - syslog(LOG_NOTICE, "exec_command() - drop_privs(%d,%d)", - cnod->uid, cnod->gid); - exit(0); - } else if (pid == -1) - syslog(LOG_NOTICE, "exec_command() - fork()"); -} - - -int -main(void) -{ - openlog(SERVERNAME, LOG_PID, LOG_AUTH); - - if ((sucomd_pid = spawn_process(sucomd_init, NULL)) == -1) - syslog(LOG_NOTICE, "main() - spawn_process(sucomd_init)"); - - closelog(); - exit(0); -} - - -static int -sucomd_init(void *args) -{ - int fd, ufd; - - /* Write PID file */ - if ((fd = open(PIDFILE, O_CREAT | O_TRUNC | O_WRONLY, - S_IRUSR | S_IWUSR, 0600)) != -1) { - char str[16]; - snprintf(str, 15, "%d", getpid()); - write(fd, str, mm_strlen(str)); - close(fd); - } else - syslog(LOG_NOTICE, "* sucomd_init() - Can't write pid file"); - - /* Read configuration */ - if (conf_read()) { - /* Init and start main loop */ - if ((ufd = unix_init(REQSOCKET, REQGROUP, 0660, 1)) != -1) { - syslog(LOG_NOTICE, "Launching sucomd"); - sucomd_main(ufd); - close(ufd); - unlink(REQSOCKET); - } else - syslog(LOG_NOTICE, "sucomd_init() - Can't create socket '%s'", - REQSOCKET); - } else - syslog(LOG_NOTICE, "sucomd_init() - Can't read config file '%s'", - CONFFILE); - - if (POOL_VALID(&cpool)) - pool_destroy(&cpool); - unlink(PIDFILE); - - return (0); -} - - -/* Here consists of the main librarian server process. It's function consists - * in following the logs created by the logger process in an ASYNC manner, - * and process them, managing the database. It also performs total SYNC of the - * database to disk at fixed intervals, cleaning up obsolete recovery logs. - */ -static void -sucomd_main(int ufd) -{ - int len; - time_t otim, tim; - struct request req; - struct conf_node *cnod; - struct pollfd fds[] = { - {ufd, POLLIN, 0}, - }; - - otim = time(NULL); - conf = FALSE; - while (run) { - if (conf) { - conf = FALSE; - if (!conf_read()) { - syslog(LOG_NOTICE, - "sucomd_main() - Couldn't reload config! Exiting"); - break; - } - } - if (poll(fds, 1, -1) > 0) { - if (fds[0].revents & POLLIN) { - /* A packet arrived, perform some sanity checking and - * process it - */ - if ((len = recvfrom(ufd, &req, sizeof(struct request), - MSG_WAITALL, NULL, NULL)) == - sizeof(struct request)) { - if ((tim = time(NULL)) > otim + MAX_RATE) { - otim = tim; - DLIST_FOREACH(&clist, cnod) - if (cnod->id == req.id) - break; - if (cnod != NULL) - exec_command(cnod); - else - syslog(LOG_NOTICE, - "sucomd_main() - Received invalid request id"); - } else - syslog(LOG_NOTICE, - "sucomd_main() - Ignored request exceeding rate"); - } - } - } - } -} diff --git a/mmsoftware/mmsucom/mmsucomd.conf b/mmsoftware/mmsucom/mmsucomd.conf deleted file mode 100644 index 9bcd858..0000000 --- a/mmsoftware/mmsucom/mmsucomd.conf +++ /dev/null @@ -1,5 +0,0 @@ -m_cd root wheel /sbin/mount /cdrom -um_cd root wheel /sbin/umount /cdrom -m_fl root wheel /sbin/mount /floppy -um_fl root wheel /sbin/umount /floppy -page root wheel /bin/ksh -c "echo -ne '\007' >/dev/ttyE0" diff --git a/mmsoftware/stringtest/GNUmakefile b/mmsoftware/stringtest/GNUmakefile deleted file mode 100644 index 0b2748f..0000000 --- a/mmsoftware/stringtest/GNUmakefile +++ /dev/null @@ -1,19 +0,0 @@ -# $Id: GNUmakefile,v 1.1 2004/05/02 11:24:20 mmondor Exp $ - -MMLIBS := $(addprefix ../mmlib/,mmstring.o) - -OBJS := stringtest.o - -CFLAGS += -Wall -DDEBUG -g - - -all: stringtest - -%.o: %.c - cc -c ${CFLAGS} -I. -I../mmlib -o $@ $< - -stringtest: $(MMLIBS) $(OBJS) - cc -o $@ $(OBJS) -lc $(MMLIBS) - -clean: - rm -f stringtest $(OBJS) $(MMLIBS) diff --git a/mmsoftware/stringtest/stringtest.c b/mmsoftware/stringtest/stringtest.c deleted file mode 100644 index a8ba547..0000000 --- a/mmsoftware/stringtest/stringtest.c +++ /dev/null @@ -1,706 +0,0 @@ -/* $Id: stringtest.c,v 1.5 2004/05/31 20:28:28 mmondor Exp $ */ - -/* - * Program to test the mmstring(3) library - * Copyright (c) 2004, Matthew Mondor, - * ALL RIGHTS RESERVED. - */ - - - -#include -#include -#include - -#include -#include - - - -#define CALL(f) (void) printf("%s\n", #f); f() -#define ASSERT(c) if (!(c)) error(__func__, __LINE__, #c, c); -#define CMPCOLS(a, b, c) comp_cols(__func__, __LINE__, (a), (b), (c)); -#define CMPBYTES(a, b, c) comp_bytes(__func__, __LINE__, (a), (b), (c)); - - - -int main(void); -static void error(const char *, int, const char *, int); -static void comp_cols(const char *, int, char **, char **, int); -static void comp_bytes(const char *, int, const void *, int, size_t); -static void test_strcmp(void); -static void test_straspl(void); -static void test_strasplq(void); -static void test_cmdparse(void); -static void test_strcat(void); -static void test_strchr(void); -static void test_strcpy(void); -static void test_strdup(void); -static void test_strcasecmp(void); -static void test_strlen(void); -static void test_strncat(void); -static void test_strnchr(void); -static void test_strncmp(void); -static void test_strncpy(void); -static void test_strndup(void); -static void test_strncasecmp(void); -static void test_strnlen(void); -static void test_strnrchr(void); -static void test_strrchr(void); -static void test_strspl(void); -static void test_strlower(void); -static void test_strupper(void); -static void test_strpack32(void); -static void test_htol(void); -static void test_strrev(void); -static void test_strhash64(void); -static void test_memhash64(void); -static void test_memhash32(void); -static void test_memcasehash32(void); -static void test_memcasecmp(void); -static void test_memset(void); -static void test_memcmp(void); -static void test_memcpy(void); -static void test_memmove(void); - - - -int main(void) -{ - CALL(test_strcmp); - CALL(test_straspl); - CALL(test_strasplq); - CALL(test_cmdparse); - CALL(test_strcat); - CALL(test_strchr); - CALL(test_strcpy); - CALL(test_strdup); - CALL(test_strcasecmp); - CALL(test_strlen); - CALL(test_strncat); - CALL(test_strnchr); - CALL(test_strncmp); - CALL(test_strncpy); - CALL(test_strndup); - CALL(test_strncasecmp); - CALL(test_strnlen); - CALL(test_strnrchr); - CALL(test_strrchr); - CALL(test_strspl); - CALL(test_strlower); - CALL(test_strupper); - CALL(test_strpack32); - CALL(test_htol); - CALL(test_strrev); - CALL(test_strhash64); - CALL(test_memhash64); - CALL(test_memhash32); - CALL(test_memcasehash32); - CALL(test_memcasecmp); - CALL(test_memset); - CALL(test_memcmp); - CALL(test_memcpy); - CALL(test_memmove); - - exit(EXIT_SUCCESS); -} - - -/* Useful for your custom ASSERT() macro */ -static void error(const char *func, int line, const char *cond, int res) -{ - /* We definitely don't want to abort(3), only to report error to stderr - * and exit(3) with EXIT_FAILURE code, which is why we use our own - * assertion macro. - */ - (void) fprintf(stderr, "%s:%d - if (%s) == %d\n", func, line, cond, res); - exit(EXIT_FAILURE); -} - -/* Useful to make sure that the result strings parsed by mm_str*spl() - * functions are exact - */ -static void comp_cols(const char *func, int line, char **cols1, char **cols2, - int many) -{ - int i; - - ASSERT(cols1 != NULL && cols2 != NULL); - - for (i = 0; i < many; i++) { - if (mm_strcmp(cols1[i], cols2[i]) != 0) { - (void) fprintf(stderr, - "%s:%d - (cols1[%d](%s) != cols2[%d](%s))\n", - func, line, i, cols1[i], i, cols2[i]); - exit(EXIT_FAILURE); - } - } -} - -static void comp_bytes(const char *func, int line, const void *buf, - int c, size_t size) -{ - const unsigned char *ptr, *toptr; - - ASSERT(buf != NULL && size != 0); - - for (ptr = toptr = buf, toptr += size; ptr < toptr && *ptr == c; ptr++) ; - if (ptr != toptr) { - (void) fprintf(stderr, "%s:%d - (%p[%d] != '%c')\n", - func, line, buf, ((int)ptr - (int)buf), c); - exit(EXIT_FAILURE); - } -} - -static void test_strcmp(void) -{ - char *str = "String", *str2 = "Strin", *str3 = "string"; - - ASSERT(mm_strcmp(str, str) == 0); - ASSERT(mm_strcmp(str, str2) != 0); - ASSERT(mm_strcmp(str2, str) != 0); - ASSERT(mm_strcmp(str, str3) != 0); -} - -static void test_straspl(void) -{ - char str[] = "this is\ta \t\t test\t as you can see. "; - char str2[] = "this is\ta \t\t test\t as you can see. "; - char str3[] = "this is\ta \t\t test\t as you can see. "; - char str4[] = "and another test!"; - char *cols[9]; - char *cstr[] = { - "this", - "is", - "a", - "test", - "as", - "you", - "can", - "see." - }; - char *cstr2[] = { - "and", - "another", - "test!" - }; - int v; - - v = mm_straspl(cols, str, 8); - ASSERT(v == 8); - CMPCOLS(cols, cstr, v); - - v = mm_straspl(cols, str2, 7); - ASSERT(v == 7); - CMPCOLS(cols, cstr, v); - - v = mm_straspl(cols, str3, 9); - ASSERT(v == 8); - CMPCOLS(cols, cstr, v); - - v = mm_straspl(cols, str4, 2); - ASSERT(v == 2); - CMPCOLS(cols, cstr2, v); -} - -static void test_strasplq(void) -{ - char str[] = "this is\ta \t'test1 test2'\t\t as you can see. "; - char str2[] = "this is\ta \t'test1 test2'\t\t as you can see. "; - char str3[] = "this is\ta \t'test1 test2'\t\t as you can see. "; - char str4[] = "And another\r\nhello"; - char str5[] = "And another\nhello"; - char str6[] = "And another\nhello"; - char str7[] = "this is\ta \t'test1 test2\t\t as you can see. "; - char str8[] = "this is 'a \n test\r ahah' yet\n again"; - char *cols[9]; - char *cstr[] = { - "this", - "is", - "a", - "test1 test2", - "as", - "you", - "can", - "see." - }; - char *cstr2[] = { - "And", - "another" - }; - int v; - - v = mm_strasplq(cols, str, 8); - ASSERT(v == 8); - CMPCOLS(cols, cstr, v); - - v = mm_strasplq(cols, str2, 7); - ASSERT(v == 7); - CMPCOLS(cols, cstr, v); - - v = mm_strasplq(cols, str3, 9); - ASSERT(v == 8); - CMPCOLS(cols, cstr, v); - - v = mm_strasplq(cols, str4, 8); - ASSERT(v == 2); - CMPCOLS(cols, cstr2, v); - - v = mm_strasplq(cols, str5, 3); - ASSERT(v == 2); - CMPCOLS(cols, cstr2, v); - - v = mm_strasplq(cols, str6, 1); - ASSERT(v == 1); - CMPCOLS(cols, cstr2, v); - - v = mm_strasplq(cols, str7, 8); - ASSERT(v == -1); - - v = mm_strasplq(cols, str8, 8); - ASSERT(v == -1); -} - -static void test_cmdparse(void) -{ - char str[] = "/bin/ls -l '/home/some user' /\n"; - char str2[] = "ls -l"; - char *cstr[] = { - "ls", - "-l", - "/home/some user", - "/" - }; - char *argv[5], *path; - int argc; - - ASSERT(mm_cmdparse(&path, &argc, argv, str, 5) == TRUE); - ASSERT(mm_strcmp(path, "/bin/ls") == 0); - ASSERT(argc == 4); - CMPCOLS(argv, cstr, argc); - - ASSERT(mm_cmdparse(&path, &argc, argv, str2, 5) != TRUE); -} - -static void test_strcat(void) -{ - char *str = "Matthew ", *str2 = "Mondor"; - char dstr[32], *tmp; - - *dstr = '\0'; - ASSERT(mm_strcat(dstr, str) != NULL); - ASSERT(mm_strcmp(dstr, str) == 0); - ASSERT(mm_strcat(dstr, str2) != NULL); - ASSERT(mm_strcmp(dstr, "Matthew Mondor") == 0); - - *dstr = '\0'; - ASSERT((tmp = mm_strcat(dstr, str)) != NULL); - ASSERT(mm_strcmp(dstr, str) == 0); - ASSERT(mm_strcat(tmp, str2) != NULL); - ASSERT(mm_strcmp(dstr, "Matthew Mondor") == 0); -} - -static void test_strchr(void) -{ - char *str = "String-t"; - - ASSERT(mm_strchr(str, 't') == &str[1]); - ASSERT(mm_strchr(str, 'z') == NULL); -} - -static void test_strcpy(void) -{ - char *str = "Matthew ", *str2 = "Mondor"; - char dstr[32], *tmp; - - *dstr = '\0'; - ASSERT((tmp = mm_strcpy(dstr, str)) != NULL); - ASSERT(mm_strcmp(dstr, str) == 0); - ASSERT(mm_strcpy(tmp, str2) != NULL); - ASSERT(mm_strcmp(dstr, "Matthew Mondor") == 0); -} - -#define STR "Some string" -static void test_strdup(void) -{ - char *new; - - ASSERT((new = _mm_strdup(STR)) != NULL); - ASSERT(mm_strcmp(STR, new) == 0); - free(new); -} -#undef STR - -static void test_strcasecmp(void) -{ - char *str = "String", *str2 = "Strin", *str3 = "string"; - - ASSERT(mm_strcasecmp(str, str) == 0); - ASSERT(mm_strcasecmp(str, str2) != 0); - ASSERT(mm_strcasecmp(str2, str) != 0); - ASSERT(mm_strcasecmp(str, str3) == 0); - ASSERT(mm_strcasecmp(str3, str) == 0); -} - -static void test_strlen(void) -{ - ASSERT(mm_strlen("String") == 6); -} - -static void test_strncat(void) -{ - char *str = "Matthew ", *str2 = "Mondor"; - char dstr[32], *tmp; - - *dstr = '\0'; - ASSERT(mm_strncat(dstr, str, 31) != NULL); - ASSERT(mm_strcmp(dstr, str) == 0); - ASSERT(mm_strncat(dstr, str2, 31 - 8) != NULL); - ASSERT(mm_strcmp(dstr, "Matthew Mondor") == 0); - - *dstr = '\0'; - ASSERT((tmp = mm_strncat(dstr, str, 31)) != NULL); - ASSERT(mm_strcmp(dstr, str) == 0); - ASSERT(mm_strncat(tmp, str2, 31 - 8) != NULL); - ASSERT(mm_strcmp(dstr, "Matthew Mondor") == 0); - - *dstr = '\0'; - ASSERT(mm_strncat(dstr, str, 3) != NULL); - ASSERT(mm_strlen(dstr) != 3); -} - -static void test_strnchr(void) -{ - char *str = "String"; - size_t len = mm_strlen(str); - - ASSERT(mm_strnchr(str, 't', len) == &str[1]); - ASSERT(mm_strnchr(str, 'z', len) == NULL); - ASSERT(mm_strnchr(str, 'i', 3) == NULL); - ASSERT(*(mm_strnchr(str, 'i', 4)) == 'i'); - ASSERT(mm_strnchr(str, 'S', 0) == NULL); -} - -static void test_strncmp(void) -{ - char *str = "String", *str2 = "Strin", *str3 = "string"; - - ASSERT(mm_strncmp(str, str2, 10) != 0); - ASSERT(mm_strncmp(str, str2, 5) == 0); - ASSERT(mm_strncmp(str2, str, 5) == 0); - ASSERT(mm_strncmp(str, str2, 6) != 0); - ASSERT(mm_strncmp(str2, str, 6) != 0); - ASSERT(mm_strncmp(str2, str, 10) != 0); - ASSERT(mm_strncmp(str, str3, 6) != 0); -} - -static void test_strncpy(void) -{ - char dstr[32], *str = "Matthew ", *str2 = "Mondor"; - size_t len; - - ASSERT((len = mm_strncpy(dstr, str, 31)) == mm_strlen(str)); - ASSERT(mm_strcmp(dstr, str) == 0); - ASSERT(mm_strncpy(&dstr[len], str2, 31 - len) == mm_strlen(str2)); - ASSERT(mm_strcmp(dstr, "Matthew Mondor") == 0); - ASSERT(mm_strncpy(dstr, str, 3) == 3); - ASSERT(mm_strlen(dstr) == 3); -} - -#define STR "some string" -static void test_strndup(void) -{ - char *new; - - ASSERT((new = mm_strndup(STR, 11)) != NULL); - ASSERT(mm_strcmp(STR, new) == 0); - free(new); - - ASSERT((new = mm_strndup(STR, 8)) != NULL); - ASSERT(mm_strncmp(STR, new, 8) == 0); - ASSERT(mm_strlen(new) == 8); - free(new); -} -#undef STR - -static void test_strncasecmp(void) -{ - char *str = "String", *str2 = "Strin", *str3 = "string"; - - ASSERT(mm_strncasecmp(str, str2, 10) != 0); - ASSERT(mm_strncasecmp(str, str2, 5) == 0); - ASSERT(mm_strncasecmp(str2, str, 5) == 0); - ASSERT(mm_strncasecmp(str, str2, 6) != 0); - ASSERT(mm_strncasecmp(str2, str, 6) != 0); - ASSERT(mm_strncasecmp(str2, str, 10) != 0); - ASSERT(mm_strncasecmp(str, str3, 6) == 0); -} - -static void test_strnlen(void) -{ - char *str = "String"; - - ASSERT(mm_strnlen(str, 6) == 6); - ASSERT(mm_strnlen(str, 31) == 6); - ASSERT(mm_strnlen(str, 5) == 5); - ASSERT(mm_strnlen(str, 0) == 0); -} - -static void test_strnrchr(void) -{ - char *str = "Stringing"; - size_t len = mm_strlen(str); - - ASSERT(mm_strnrchr(str, 't', len) == &str[1]); - ASSERT(mm_strnrchr(str, 'z', len) == NULL); - ASSERT(mm_strnrchr(str, 'i', 3) == NULL); - ASSERT(*(mm_strnrchr(str, 'i', 4)) == 'i'); - ASSERT(mm_strnrchr(str, 'S', 0) == NULL); - ASSERT(mm_strnrchr(str, 'i', len) == &str[6]); -} - -static void test_strrchr(void) -{ - char *str = "String-t"; - - ASSERT(mm_strrchr(str, 't') == &str[7]); - ASSERT(mm_strrchr(str, 'z') == NULL); -} - -static void test_strspl(void) -{ - char str[] = "|These|are||separated|words|"; - char str2[] = "|These|are||separated|words|"; - char *cstr[] = { - "", - "These", - "are", - "", - "separated", - "words" - }; - char *cols[7]; - int v; - - v = mm_strspl(cols, str, 7, '|'); - ASSERT(v == 6); - CMPCOLS(cols, cstr, v); - - v = mm_strspl(cols, str2, 5, '|'); - ASSERT(v == 5); - CMPCOLS(cols, cstr, v); -} - -static void test_strlower(void) -{ - char *str = "somemixedcaseword", str2[] = "SomeMixedCaseWord"; - - mm_strlower(str2); - ASSERT(mm_strcmp(str2, str) == 0); -} - -static void test_strupper(void) -{ - char *str = "SOMEMIXEDCASEWORD", str2[] = "SomeMixedCaseWord"; - - mm_strupper(str2); - ASSERT(mm_strcmp(str2, str) == 0); -} - -static void test_strpack32(void) -{ - char *str = "CMD", *str2 = "CMD1"; - u_int32_t v; - - v = mm_strpack32(str, 3); - ASSERT(v == 0x00434d44U); - - v = mm_strpack32(str2, 4); - ASSERT(v == 0x434d4431U); -} - -static void test_htol(void) -{ - ASSERT(mm_htol("01aBcDeF") == 0x01abcdefU); -} - -static void test_strrev(void) -{ - char str[] = "Hello", *str2 = "olleH"; - - mm_strrev(str); - ASSERT(mm_strcmp(str, str2) == 0); -} - -static void test_strhash64(void) -{ - ASSERT(mm_strhash64("This is a test!") == 0x3cafb1cf796f93bcULL); -} - -static void test_memhash64(void) -{ - char *str = "This is a test!"; - - ASSERT(mm_memhash64(str, mm_strlen(str)) == 0x3cafb1cf796f93bcULL); -} - -static void test_memhash32(void) -{ - char *str = "This is a test!"; - - ASSERT(mm_memhash32(str, mm_strlen(str)) == 0x34a6c6fcU); -} - -static void test_memcasehash32(void) -{ - char *str1 = "This is a test!", *str2 = "THiS Is A TeSt!"; - - ASSERT(mm_memcasehash32(str1, mm_strlen(str1)) == 0x8202b2fcU); - ASSERT(mm_memcasehash32(str1, mm_strlen(str1)) == - mm_memcasehash32(str2, mm_strlen(str2))); -} - -static void test_memcasecmp(void) -{ - ASSERT(mm_memcasecmp("1", "2", 1) == -1); - ASSERT(mm_memcasecmp("1", "1", 1) == 0); - ASSERT(mm_memcasecmp("2", "1", 1) == 1); - ASSERT(mm_memcasecmp("a", "B", 1) == -1); - ASSERT(mm_memcasecmp("a", "A", 1) == 0); - ASSERT(mm_memcasecmp("b", "A", 1) == 1); -} - -/* To properly test the following, aligned and unaligned memory must be used. - * We also need to test small and large memory areas. This is because these - * functions perform optimizations to operate faster on aligned and large - * areas. - */ - -#define BSIZ 4096 - -static void test_memset(void) -{ - char *mem; - - ASSERT((mem = malloc(BSIZ)) != NULL); - - ASSERT(mm_memset(mem, 'x', BSIZ) == mem); - CMPBYTES(mem, 'x', BSIZ); - - ASSERT(mm_memset(mem, 'y', 8) == mem); - CMPBYTES(mem, 'y', 8); - - ASSERT(mm_memset(mem + 2, 'z', BSIZ - 2) == mem + 2); - CMPBYTES(mem + 2, 'z', BSIZ - 2); - - free(mem); -} - -static void test_memcmp(void) -{ - char *mem, *mem2; - - ASSERT(mm_memcmp("1", "2", 1) == -1); - ASSERT(mm_memcmp("1", "1", 1) == 0); - ASSERT(mm_memcmp("2", "1", 1) == 1); - - ASSERT((mem = malloc(BSIZ)) != NULL); - - (void) mm_memset(mem, 'z', BSIZ); - mem[BSIZ - 1] = '\0'; - - ASSERT((mem2 = _mm_strdup(mem)) != NULL); - - ASSERT(mm_memcmp(mem, mem2, BSIZ) == 0); - ASSERT(mm_memcmp(mem + 1, mem2 + 1, BSIZ - 1) == 0); - ASSERT(mm_memcmp(mem + 2, mem2 + 2, BSIZ - 2) == 0); - ASSERT(mm_memcmp(mem + 3, mem2 + 3, BSIZ - 3) == 0); - ASSERT(mm_memcmp(mem + 4, mem2 + 4, BSIZ - 4) == 0); - - mem[BSIZ / 2] = '\0'; - ASSERT(mm_memcmp(mem, mem2, BSIZ) != 0); - ASSERT(mm_memcmp(mem + 1, mem2 + 1, BSIZ - 1) != 0); - ASSERT(mm_memcmp(mem + 2, mem2 + 2, BSIZ - 2) != 0); - ASSERT(mm_memcmp(mem + 3, mem2 + 3, BSIZ - 3) != 0); - ASSERT(mm_memcmp(mem + 4, mem2 + 4, BSIZ - 4) != 0); - - ASSERT(mm_memcmp(mem + 2, mem2, BSIZ - 2) != 0); - ASSERT(mm_memcmp(mem + 3, mem2, BSIZ - 3) != 0); - - free(mem2); - free(mem); -} - -static void test_memcpy(void) -{ - char *mem, *mem2; - - ASSERT((mem = malloc(BSIZ)) != NULL); - ASSERT((mem2 = malloc(BSIZ)) != NULL); - - (void) mm_memset(mem, 'x', BSIZ); - ASSERT(mm_memcpy(mem2, mem, BSIZ) == mem2); - ASSERT(mm_memcmp(mem2, mem, BSIZ) == 0); - mem[BSIZ / 2] = '\0'; - ASSERT(mm_memcmp(mem2, mem, BSIZ) != 0); - - (void) mm_memset(mem, 'y', BSIZ); - ASSERT(mm_memcpy(mem2 + 1, mem + 1, BSIZ - 1) == mem2 + 1); - ASSERT(mm_memcmp(mem2 + 1, mem + 1, BSIZ - 1) == 0); - mem[BSIZ / 2] = '\0'; - ASSERT(mm_memcmp(mem2 + 1, mem + 1, BSIZ - 1) != 0); - - (void) mm_memset(mem, 'z', BSIZ); - ASSERT(mm_memcpy(mem2 + 1, mem, BSIZ - 2) == mem2 + 1); - ASSERT(mm_memcmp(mem2 + 1, mem, BSIZ - 2) == 0); - mem[BSIZ / 2] = '\0'; - ASSERT(mm_memcmp(mem2 + 1, mem, BSIZ - 2) != 0); - - free(mem2); - free(mem); -} - -#define SSIZ 64 -static void test_memmove(void) -{ - char *mem, *mem2; - - ASSERT((mem = malloc(BSIZ)) != NULL); - ASSERT((mem2 = malloc(SSIZ)) != NULL); - - { - unsigned char *ptr, *toptr, c; - - for (ptr = toptr = mem2, toptr += SSIZ, c = 1; ptr < toptr; ptr++) - *ptr = c++; - } - - (void) mm_memset(mem, 'x', BSIZ); - (void) mm_memcpy(mem + 16, mem2, SSIZ); - - /* Supposed to not do anything */ - ASSERT(mm_memmove(mem + 16, mem + 16, SSIZ) == mem + 16); - ASSERT(mm_memcmp(mem + 16, mem2, SSIZ) == 0); - - /* Word-optimized increasing order move */ - ASSERT(mm_memmove(mem + 8, mem + 16, SSIZ) == mem + 8); - ASSERT(mm_memcmp(mem + 8, mem2, SSIZ) == 0); - - /* Word-optimized decreasing order move */ - ASSERT(mm_memmove(mem + 16, mem + 8, SSIZ) == mem + 16); - ASSERT(mm_memcmp(mem + 16, mem2, SSIZ) == 0); - - /* Increasing order move */ - ASSERT(mm_memmove(mem + 15, mem + 16, SSIZ) == mem + 15); - ASSERT(mm_memcmp(mem + 15, mem2, SSIZ) == 0); - - /* Decreasing order move */ - ASSERT(mm_memmove(mem + 16, mem + 15, SSIZ) == mem + 16); - ASSERT(mm_memcmp(mem + 16, mem2, SSIZ) == 0); - - free(mem2); - free(mem); -} -#undef SSIZ - -#undef BSIZ diff --git a/site/contributors.html b/site/contributors.html deleted file mode 100644 index 42b5895..0000000 --- a/site/contributors.html +++ /dev/null @@ -1,95 +0,0 @@ - - - - -Matthew Mondor's Software Site - Contributors - - - - - - - - - -

- - - - - -

- - -
-Sections
-
-

Main 
Software 
Donations 
Contributors 
Philosophy 
CVS 
Projects 
Mirrors 
Contact 
-

-
Image Copyright (c) 2002-2003, Matthew Mondor -
-
-MMSoftware Contributors
-
- -

-These contributors generally consist of various mmsoftware users across the -globe who are developing related third-party software, to add features such as -frontends and configuration tools. They are free to release their software -under their prefered licenses and conditions. The contributors maintain -their own software themselves, so bug reports or suggestions should be sent -directly to them. The content of their section become their own responsibility. -To obtain sources for the official mmsoftware releases please visit the -software area. -

-I personally thank the contributors for their interest in my projects. -

- - - - - - -
-Jeroen Oostendorp -
-Daniel DeMaggio -
-Alexander Schlett -
-Jonas Koch Bentzen -
-Vahram Igityan -
-
-
-Languages
-
-English 
French 
-

-

Mirrors
-
-Canada 
United-States 
Holland 
-

- -$Id: contributors.html,v 1.5 2003/06/04 12:17:47 mmondor Exp $
-This site Copyright (c) 2002-2003, Matthew Mondor, ALL RIGHTS RESERVED. -
- diff --git a/site/cvs.html b/site/cvs.html deleted file mode 100644 index d5f219a..0000000 --- a/site/cvs.html +++ /dev/null @@ -1,89 +0,0 @@ - - - - -Matthew Mondor's Software Site - CVS - - - - - - - - - -

- - - - - -

- - -
-Sections
-
-

Main 
Software 
Donations 
Contributors 
Philosophy 
CVS 
Projects 
Mirrors 
Contact 
-

-
Image Copyright (c) 2002-2003, Matthew Mondor -
-
-MMSoftware CVS Repository
-
- -

-Note that the CVS repository contains the latest actual development tree -and should not be used by the general public, it is mostly intended for -developpers, maintainers and beta-testers. There also can be found some -software which never have previously been released in the form of an archive. -

-The HTTP cvsweb interface to the CVS repository is no longer available at -present time. However, read-only public CVS pserver access is provided. -I highly suggest getting aquainted with the command-line cvs(1) utility -for best results. -

-Those who want to access the repository via public pserver to access all -of my BSD-style licensed open source software can use the following command: -

-% cvs -z6 -d:pserver:anoncvs@cvs.accela.net:/cvsroot co mmondor -

-To only obtain the Xisop kernel, mmondor/Xisop may be used as the -module name. To only retreive the mmsoftware directory, -mmondor/mmsoftware will be used.

-The service is provided using the cvs(1), mmspawnd(8) and mmanoncvs(8) -utilities as an alternative to the common setup using inetd(8). This -ensures a secure public pserver setup which cannot affect the original -repositories or the rest of the system if exploited. - -

-Languages
-
-English 
French 
-

-

Mirrors
-
-Canada 
United-States 
Holland 
-

- -$Id: cvs.html,v 1.7 2003/12/10 20:33:58 mmondor Exp $
-This site Copyright (c) 2002-2003, Matthew Mondor, ALL RIGHTS RESERVED. -
- diff --git a/site/donations.html b/site/donations.html deleted file mode 100644 index d57d2f1..0000000 --- a/site/donations.html +++ /dev/null @@ -1,115 +0,0 @@ - - - - -Matthew Mondor's Software Site - Donations - - - - - - - - - -

- - - - - -

- - -
-Sections
-
-

Main 
Software 
Donations 
Contributors 
Philosophy 
CVS 
Projects 
Mirrors 
Contact 
-

-
Image Copyright (c) 2002-2003, Matthew Mondor -
-
-Donations to support MMSoftware
-
- -

-Free opensource software projects need the support of their users to evolve. -Programmers spend alot of time trying to make the code efficient, bug-free, -and user-friendly. In an area where security is a primary concern, like for -public internet services, the time required to roll safe code is often higher -than it would be for some other projects. There are design, coding, testing, -debugging, and auditing sessions. And at times even restructuring when -required to allow the software to be more manageable for the forthcoming new -features it should integrate. -

-There are many ways one can use to support the project. One is to help it -maintain public exposure by providing hosting and bandwidth for a mirror. -This obviously only should be done on stable connections, it can then become -useful for people to find a mirror closer to their location, and so that at -all times at least one mirror remains up if others are ever temporarily down. -The mmondor.gobot.ca DNS pool of addresses can then be updated once a day, -verifying the availability and stability of the mirrors. An FTP account or -other method can be used to mirror the site regularly. -

-Another way is to audit the software against potential security, portability -issues and bugs, as well as test the software. Beta testers are welcome to -track the CVS repository tree and to propose diff/patches whenever necessary. -Also considered a donation is the time other programmers can put in -contributing third party software related to the projects. Although this may -not necessarily affect mmsoftware directly, some of that software may be very -useful for many mmsoftware users. -

-It is also possible to sponsor the work on a particular opensource project or -feature of one of the existing project by material and/or money donations. -For example, one may want the software to support another UNIX-style system -for which the software does not currently work for some reason, and provide -the necessary hardware and/or software tools to allow to do it. -

-As we are mostly dealing with BSD licensed software it is also possible to -hire us in order to develop a closed-source implementation with -custom-specific needs of one of the existing projects. The customer would -decide whether or not the new branch should eventually be donated, back to the -main public repository, to fall back under the same BSD-license as well -(possibly with an advertising clause in the license text). -

-Finally, a thing which is always encouraging and doesn't cost much, is to -send positive feedback, via email or on freshmeat, about the features you -enjoy in mmsoftware, if you decide to use it. Gifts are also welcome. -

-We appreciate your contribution to our common goal. We would like to assure -you that we use it to the best of our efforts to help the project evolve. -

-Matthew Mondor -

- -
-Languages
-
-English 
French 
-

-

Mirrors
-
-Canada 
United-States 
Holland 
-

- -$Id: donations.html,v 1.6 2003/06/04 12:17:47 mmondor Exp $
-This site Copyright (c) 2002-2003, Matthew Mondor, ALL RIGHTS RESERVED. -
- diff --git a/site/favicon.ico b/site/favicon.ico deleted file mode 100644 index dcac83569bdadde1f9f299583a29f14a75982a71..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 318 zcmbV_F%H5|3`5_SkUEld=n9{yw?L>j>YaL>o`sPCAsoV&%FqRWIhGU4ABvutX&x90 z48SOwcot*72U}L=+eGMpa3OwjuQm1Z)%3-yNAsTIpSTKATh9AFdutw=Z+8`{_1^vR RafB_OCkV!Fn+^ZE!W*5;8Z7_- diff --git a/site/images/CVS.jpg b/site/images/CVS.jpg deleted file mode 100644 index 4e382fc57d885ff355c9dfb26338c8c55a548ae7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1366 zcmb7Q)DB(dG=+#dos*PYk_pF7 zl9q!=?sLuM*?DSn2}kZnp^HK%hZMELb6(Wj^ZmX1exC2=d4B3)^%Q{daOb%LAP@-P zZXBTg0_eF!2}4f{kC0-Io{k{-MF>e8-ac>j7~ldRG&JBEFa#V9M zrbJWv|Bm`0fJFclAQ%EN1Hf1i1PfAk0R#YmYFz*)d1lC zVGtMsKw%nKoGDy~wukN$dO_DroLTq)&)|o(KY6#Y(c6@LYXd+4cvD8eZuXFG5eN&$ z(M;8&0BWNKV(ssKCDz!aZLz#pypp=ID`t;QP4~b# zhlusy;F>voQBBDZZrI9j`as)Ody$m0ThT&2HW5b|jAV9FleT%D*b!~+Xorp*TO8W* z2Cu?)mMgnyk(5XNzh%>3Q4Nv-u?H8ruy@K89`^Y!JysiZAo*S9dres@UEO`jhV1=;zt8wb-A>od1CU3=La zejEuF-WcVyWMKSMn4F5uuPP3I88KR_C79q1mz~PEf2QAE)@SB79dvLs1M@;QSoAo{ zS>Ws?syZ+B?~*BgJzPa}lScDQ<=1k&@7r+JG#IMR{9pR59|s=h66Dt4MDC;YPVJSz zyikR)Q{aA!!lP_Fq8(E`HGeR#ly0C&kaXBM%FQ)RK1dkBaq^h0OQlE=(#kg@#BBO` zg<(vRmG4OIZ4aeH*+h|_&l6<-jF>ySh)c7M9E&Y59lurcNj71;`gwX@*R4K|H=zS? z?J79r{il!%dd2$d9gwsdh!NDL zS?cp<@s`P%wSxzu(CA|FYj5YfveZVs^i}5WIcCx!$yz;Yxy@4)FPjZyRMa_^RneBA z=fYB=SIV2hrFSKFJgQ50UcOF~cyhcLcm`N-GG9S^QL_Blk6-bCwCwx-V;;SZ2c?A6vmTdT^sY<`^+ywyGkIAy@#YvSYc4oM{Cd~EcTGvP z4)I5NhnU=x4e0Gg93)H&}B$C z>Y0^w>{f?vNv&t|&feRVoS=4N@mz!T)uk&-QQR`V?A3evMM(}()3^4pPY3TWAcUA0 ln=Hjd3wi~1l5Cl$>z%5rS08GMU7%|*X(){Qormg?e*u!pFLVF^ diff --git a/site/images/key.jpg b/site/images/key.jpg deleted file mode 100644 index 6b3f9df6193c7f18a2fb847b2074bd84630e05ed..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1977 zcmb7>c{JOJ7RP@-5nEbBP_2lJrP_(;AeN9)F`_{vqNJs^ifvjdVyUgQGtV)zT$sk=CBrN_r@RYO9?tuX#Fi&b;&feE0r!@BQ5GJ@;POq--8gcO#KW z00@G>;cW+GUx8iDabY3xVN7&VRD2}*NMsn=l}w?@B!DxpQ&|P3qO=pH0#jApsfIvn zAv81)CP>sSwAp_1AI$b+O)c%*94xI#Hm2A^3@4I@C*7NF;oukIM+L0LZ~X2pp6>1atvF4)Se{|C#~_Rg{OwZ5Qp- z0T2R_gFuuO6d->A;5ID}g)3-$XYPe?3eh&Oz!5`PSDH1o4C!$_idHAs1vSQ&&fC3v zzM=o43(9Y2pbFbH6F49z2g*Sq(0`_aa&U;eIaCAjos*^)OUuB*(n=->b4AFava~TPD}FqVYwk3PGIQq} z4^1zSS9}%~`VI~@E{mz*!VWd)mV%&`ol)Erp87i9EAv?p?`}`Hz~xL0Uq6#3M3wc( z09cpl37c*cMbg5A>TBQc`F!d8Y(@*j5cGf zD~0MAKGefWs-EIXt+gmJ`K`iTJrmEf}C~hWt(|4q7 z9wLCC^P({D?CimWcPSk!C%)jYrMlU=@50swOV>Z;Xz0m+V+UuIUxw1$mQ9#XZP`eq zTF=Rnl>&y-jK&@h7%b^SBJ$j&1c(sZQN@M(*kYle6KH7K~jZMt0Y-{uKZZC=kzmI&PdtCdsja;Q>khU zX>B_?U$enUTJ{<&rHVQ$hkqUrCSKM7HeppXTImtHCP6S*eQE=9ol3V>bnv20!Nden zy4H(^{W`BI=|Am3j%Q9ST?`CGzFg%CuLkJl!uu6Vi0a7)DD$Mt49cM7=&OK=j~VAU zktOd4l0WXKeU0g5y5H@EIZPCu?sQg{j)z5>TzMimKGXO$4YlJwNjJZNRUGP={T5ohR)maEvoaS{X^=xb8HjdOG<0g zpXt`eyw!+Go|o)Jg{SWH>o4WyJc_(@-Le1DOqGS+uO>ZA20ryURg%WEeYyE>b877h4cstrJHOL zfx-EbGc)aqBEyp84e zf*)rtbL*CN$BdP58dgpwPzEMfQs|;L>?O@gY1ra)THj)ndNuLh8;i|xwMrBz(5~lg zBVM$xI*P6LI~RMcY1_7^xX-D0YLx5^ADbRODK$6U~nq8k<-bKoA!caq9&+9 V<0wIi>NU!zai~b*c}LmQe*iScVd($> diff --git a/site/images/sigil.jpg b/site/images/sigil.jpg deleted file mode 100644 index d6f6aceaa809168de37ca8b9eadb53e15b4f192f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3311 zcmbW3cU05o8pq$?@0Ueb2_`X2lMo?@BaFr%V1FQlfQ*2sEIlkSv``74C>EPwf*2x% zVHqNZjh6X=M5f`!8vg$xA_TF-_!+Za{_de&m?{l8>eaGi{|9H=L@&4iy zK=KiI3jhRw{#xxx02Y4-@Ztj?2RIA{i^1TqSS%ig!|RZUIs}5wG6Mr$(sBx&PNUFh z#%Aml#-=P&8jWGgU|Ct)*x1l5)^JyIxNMG%HH5?CbqG2}M52+k3C+a%e~!f-s0siG z3;hw`uYh0#gT>(qIz(LvAb)C0^PlQ}h$H|*2m)ge>{1s?$7C#N07J$aG40%N%L1eD zED6P4mS0J*bZ@#!4H|sGc8E^S)FINA(~V75T3K^!RylseThDjeu*t(y;3f3-*%G{U z+xC#qu$b6gal6IwpQNPj-IuoiK>A_1BI`)@QDs44(eW>fOG?YCs%vWN>Kjg0|w$Azx$p5DIxYu9fK4Uc^H{k{7?j6NOLKKpUv`AR`6|M!is9F_(DV51w9GimdS zvQl}5a&SoB3k%{3Ak=zRDsKF||Eok*6G)rL0jefx@=|hZIbFDi-l@TKX-!jjS?0mONMRz^ zw1V$Q9ACrKL3KwYzF367@28}#2)v;&C?B#t%{q5;sKf}}=cqw=XzVT=Yij^MVK9<9y2t5j9z)3uTu+2Zx}i02Tt{of zA}|occhy}ao|h^qc&~;y;l$OE7gW=41CPO*i@-(r4>6=?ub6Q8qO)Oyph`}h8Isaj z&WGN9f(pZd9>rt+{e>CAyT%%IQt_Ppym&e#n}$-d#O8O8aJq^s}ewTwA964X8R`=@+KgL}=t z``+l9!?mQZ4nnBW&O6Ox8>75wTuQW*K^{Hl=VI6+f{ee3Xq*4sSvoZ)LReAB$C4sN zq#0W!YJCF7b8WkY$5@O&NOt+t#Ch3cN8kZxl~xC~$?{QZacXvdq_Jx}!`Pa^`Kd~3 zT&_%TU&ADN*Xc^O&nk%r3)jt6w=a7j>u7xfTGYA*RJVxO*S`487dbdV0AHhX$~WOa zUb@I`boph1ZvKPO&CE@o^ z9aT=z9an3lnI7&9u_q@A&9at0PgTQ|EC|7IANxcKVBqA`Ye(21^7S;=C)7Bl6E}q$ z`r{-@Ob)GkV%H*w%dL*7=6hOXmKZQ%Z7T86v(>gGQLUk^nuxh6UwnMUrtJD$8_Xo`-0#=cPO|B!!@8ouJ&9o2Ky zk-~FkxDfB+XNm*-$q|a+Uf`wEW-K8q@aDR08sgjKZ9}KlVoKckdR|rq&^;^9T*htY zg21g81V^KD;{}j%u9oFdSEE6e_P|p>80i45C^-EfaJv*XYnT5wn75 zpGlwBf)xoDR$K@_9eQE+S%my-SNG0{%iUk-xwYTD4DEAseC?}yJzMJ!=N1t%YJ$SU zzNPPx9B0qhV7IvRn|`gMWydH zo<&IqME4&w*VYc6%nLP`7GdnF$J7dg-i7R5u)3gmEbA3raLKTiVJn=Cjcjnw<<}v` z*&?0=z80*)`3;XrQ!C6kb%z@|?xy32SoV>D<9-AkX7LUDo-O^9 z&i(l#FNDv(7L!-qO`2{x@I>zwhyL5ex8W_hPRZ9Uri+ll3E@AcP87DJ`j9~T1$+7S zS5cOX(^nlK>Vvxz`fWz3(|uBN$Kq}CoJV`CFSdGXNO_i{qpKDc+KUS)sT3&mWM_X_ z68lCRXorrO+r`(r^p=S9gEG6&n-T1_rC3{05 zyZPm9X^a%Hwk`zI7A`lyo0C5uwq55oOaagG*Q9Y_@5glqMTo9Yn|iK2eXj=3EDIH} zqk}Zqg(g(jWqYBY;wV+}Lj%-D+bp_A0RE?VDJW^7F7%SNYo?O|(RW2;8T0!cLFqyg zo2W-o_ISUl%kKhUkwRAd?y$yXvou(L}FfLkBGevCH$HBX6~Nfy44Hj^^2 zY`d*(M&Wrs`{Iv=7RDtVK0O-`l+N`Zheh%?x^kd2>Oyf|I?D54NNjE{eKq0b?0C4z z+Sp1>#$hv>d?9KL-5;10_5!`1>7yNM9x7}h+Wt4=IUuKkPnlI{v_mpNi`3`6=fX~N zpzZRojLwL6BJLKM62~XG+Az#azgOdav^;*T4koD%6K?juZD=&`K<)4N<6)`g15=~k z#}zHG*?CNapgiU>0atpmSj?x_A-LekLq!Bca;z>V8_Nvw!F_ORlYj&5zioH)R4pV) z`CTGRC(_5I%hJqL!-gf6F|HiaiM;RC-6=8kQ>ZkxpE9SLmBb;zUg_?pXxE?a@Gp}H zOQ~XLEKu^3mB40fQ<~&$z>lZ_op8kmppU1^*1{D<5g2nD0&l=>sk;w*{_vN7+spxk Q1I>XO@7w>d04)CSFS%hIjQ{`u diff --git a/site/index.html b/site/index.html deleted file mode 100644 index 25f857c..0000000 --- a/site/index.html +++ /dev/null @@ -1,91 +0,0 @@ - - - - -Matthew Mondor's Software Site - Main - - - - - - - - - -

- - - - - - - - -
-Sections
-
-Main 
Software 
Donations 
Contributors 
Philosophy 
CVS 
Projects 
Mirrors 
Contact 
-
-
Image Copyright (c) 2002-2003, Matthew Mondor -
-
-The MMSoftware Project
- -Image Copyright (c) 2002-2003, Matthew Mondor - -
- -

-NOTE: The french version is not yet available. Additionally, only -the master site currently works, mirrors should be up soon. -

-This site provides various opensource software -to anyone who find it useful. The projects mostly were written from scratch by -Matthew Mondor and released under a BSD-style license, but we also host -third-party software related to these projects, provided by other -contributors worldwide who wanted to make -their related work freely available. -

-The hosting and bandwidth for the mirrors is -provided by various donators accross the globe. -Although we attempt to encourage contributors and donators, we try to keep -this site as free from commercial ads as possible as a convenience to our -users. The main mmsoftware project philosophy -will describe our goals with more details. -

-Matthew Mondor can be contacted via email at -mmondor@gobot.ca for suggestions, -contributions, donations, flames, thanks, business and bug reports. -

-
-
-Languages
-

-English 
French 
-

-

Mirrors
-
-Canada 
United-States 
Holland 
-

- -$Id: index.html,v 1.9 2003/09/30 05:34:53 mmondor Exp $
-This site Copyright (c) 2002-2003, Matthew Mondor, ALL RIGHTS RESERVED. -
- diff --git a/site/mirrors.html b/site/mirrors.html deleted file mode 100644 index ec0583f..0000000 --- a/site/mirrors.html +++ /dev/null @@ -1,92 +0,0 @@ - - - - -Matthew Mondor's Software Site - Mirrors - - - - - - - - - -

- - - - - -

- - -
-Sections
-
-

Main 
Software 
Donations 
Contributors 
Philosophy 
CVS 
Projects 
Mirrors 
Contact 
-

-
Image Copyright (c) 2002-2003, Matthew Mondor -
-
-MMSoftware Mirrors
-
- -

-Here are the current site mirrors and their generous providers, appearing -in setup chronological order (first setup to last): -

- - - - - - - - - - - - - - - -
LocationProviderURL
United-StatesMatthew J Backeshttp://gobot.accela.net
HollandJeroen Oostendorphttp://mmondor.oostendorp-ict.nl
CanadaRyan Werberhttp://mmondor.dynup.net
-

-If you would like to provide hosting space and bandwidth for a new mirror, -this would consist of a service donation, please consult the -donations area for more information about the -requirements and suggestions. -

-
-
-Languages
-
-English 
French 
-

-

Mirrors
-
-Canada 
United-States 
Holland 
-

- -$Id: mirrors.html,v 1.5 2003/06/04 12:17:47 mmondor Exp $
-This site Copyright (c) 2002-2003, Matthew Mondor, ALL RIGHTS RESERVED. -
- diff --git a/site/new/TODO b/site/new/TODO deleted file mode 100644 index 9a13720..0000000 --- a/site/new/TODO +++ /dev/null @@ -1,93 +0,0 @@ -Because there is a general layout already for the site (left column with -a main menu, center column with the content for the current page and -right column for the language menu), I should design something which uses -templates or such. I did so for the spes project a while back, but which -dynamically generated the pages, using php. - -I want something with which I can simply type a make command for it to -generate the static html pages. There would be a list of languages and -for each the process would be automatically repeated, also. For each -language, a left main menu and right language selection menu would be -generated, and it's center column contents generated from a template. -Example of a possible layout: - -build/ -build/lang/english -build/lang/english/menu_main.txt -build/lang/english/menu_lang.txt -build/lang/english/page_index.txt -build/lang/english/page_cvs.txt -build/lang/english/page_software.txt -build/lang/english/page_philosophy.txt -build/lang/english/soft_descriptions.txt -build/soft_releases.txt -build/languages.txt -htdocs/ - -The files would be generated into htdocs/ -The soft_descriptions.txt file would hold a map of software name->short -description -> long description, to be used by build/soft_releases.txt in -lang/page_software.txt . -build/languages would hold a list of available languages. - -The format of the manu_*.txt files would be as follows: - -" -"" "" - -I.E. - -"Sections" -"index" "Main" -"software" "Software" -"donations" "Donations" -"contributors" "Contributors" -"philosophy" "Philosophy" -"cvs" "CVS" -"projects" "Projects" -"mirrors" "Mirrors" -"contact" "Contact" - -The filename will be suffixed with the language suffix in the htdocs/ -directory, and the file will be generated from the language/page_.txt -file. - -The format of the languages.txt file would be as follows: - - - -I.E. - -en english -fr french -ru russian - -The format of the page_*.txt files would be standard HTML, but which could -contain special keywords. The only current special keyword consists of: -.soft releases -which should be beginning at the first column, as-is, on a single line. -This keyword causes the soft_releases.txt file to be parsed with the -language/soft_descriptions.txt, to create a table of all the available -software. -It is possible to do: -.soft maintained -.soft unmaintained -etc. - -I also need a special command for mirrors table. And one for links. -The one for links would automatically append the language suffix and -html extension... - -.link -.mirrors - -A similar system for FAQs: - -.faq - - -TODO -==== - -- Verify apache multilingual support to use the same convention for - page storage... diff --git a/site/new/build/GNUmakefile b/site/new/build/GNUmakefile deleted file mode 100644 index f04011d..0000000 --- a/site/new/build/GNUmakefile +++ /dev/null @@ -1,20 +0,0 @@ -# $Id: GNUmakefile,v 1.1 2004/04/30 00:05:53 mmondor Exp $ - -MMLIBS := $(addprefix ../../../mmsoftware/mmlib/,mmpool.o mmlog.o \ -mmreadcfg.o mmstring.o mmhash.o) - -OBJS := mmsite.o - -CFLAGS += -Wall -DDEBUG - - -all: mmsite - -%.o: %.c - cc -c ${CFLAGS} -I. -I../../../mmsoftware/mmlib -o $@ $< - -mmsite: $(MMLIBS) $(OBJS) - cc -o $@ $(OBJS) -lc $(MMLIBS) - -clean: - rm -f mmsite $(OBJS) $(MMLIBS) diff --git a/site/new/build/README b/site/new/build/README deleted file mode 100644 index dc99aeb..0000000 --- a/site/new/build/README +++ /dev/null @@ -1,16 +0,0 @@ -It was chosen to use a simple system based on text files which could be -used with CVS rather than another type of database to build the site's -HTML files from. This allows contributors to simply send a diff generated -by CVS containing their update suggestions and additions. Also, generating -a static site has advantages, allowing cacheing for efficiency by HTTP -cache proxies, and easing the task of mirrors updating their copy of the site. - -This system makes it easy to maintain the list of available software, as -well as to translate the site to other languages, while avoiding HTML -formatting and linking bugs. It also enforces the site's general layout -through all the site pages. The generated pages are made from an W3C validated -template for HTML 4.0 Transitional. - -The default language causes the pages to have no special suffix, while -the others will have the short description of the language appended before -the '.html' extension. diff --git a/site/new/build/TODO b/site/new/build/TODO deleted file mode 100644 index 0832775..0000000 --- a/site/new/build/TODO +++ /dev/null @@ -1,10 +0,0 @@ -- Although it is nice to have a few hash tables for fast lookup, some - files need to be considered as sequencial lists and will be have to - be processed accordingly (using a hash table causes the entries to - become in an arbitrary order). Actually, most or all of the base files - are lists, while language-specific ones are tables for fast lookup - when mapping. - -- Work out a system for FAQs similar to the one for software. - -- Work out a system for news. This would be nice to announce releases etc diff --git a/site/new/build/english/faq_mmftpd.txt b/site/new/build/english/faq_mmftpd.txt deleted file mode 100644 index 886f552..0000000 --- a/site/new/build/english/faq_mmftpd.txt +++ /dev/null @@ -1,17 +0,0 @@ -# First line consists of FAQ .title -# Then following are .section for a section, -# .question <num> <title> for a question. Each can be followed by -# arbitrary HTML, and an index will be automatically generated. -# -.title "Frequently asked questions about mmftpd" -This FAQ deals with most common problems which first time users encounter. -.section "Installation" -This section deals exclusively with mmftpd installation. -.question "How do I compile mmftpd?" -Answer to the question -.question "Another question?" -Another answer. -.section "Configuration" -This section deals with mmftpd configuration. -.question "Blah??" -Answer!? diff --git a/site/new/build/english/faq_mmmail.txt b/site/new/build/english/faq_mmmail.txt deleted file mode 100644 index e69de29..0000000 diff --git a/site/new/build/english/faq_mmstatd.txt b/site/new/build/english/faq_mmstatd.txt deleted file mode 100644 index e69de29..0000000 diff --git a/site/new/build/english/menu_languages.txt b/site/new/build/english/menu_languages.txt deleted file mode 100644 index b7033ae..0000000 --- a/site/new/build/english/menu_languages.txt +++ /dev/null @@ -1,6 +0,0 @@ -# One line per entry, two columns: -# <language> <description> -# -MENU Languages -english English -french French diff --git a/site/new/build/english/menu_main.txt b/site/new/build/english/menu_main.txt deleted file mode 100644 index ad38d2a..0000000 --- a/site/new/build/english/menu_main.txt +++ /dev/null @@ -1,10 +0,0 @@ -MENU Sections -index Main -software Software -donations Donations -contributors Contributors -philosophy Philosophy -cvs CVS -projects Projects -mirrors Mirrors -contact Contact diff --git a/site/new/build/english/menu_mirrors.txt b/site/new/build/english/menu_mirrors.txt deleted file mode 100644 index 6ef947b..0000000 --- a/site/new/build/english/menu_mirrors.txt +++ /dev/null @@ -1,7 +0,0 @@ -# One line per entry, two columns: -# <name> <description> -# -MENU Mirrors -canada Canada -united-states United-States -holland Holland diff --git a/site/new/build/english/page_contact.txt b/site/new/build/english/page_contact.txt deleted file mode 100644 index e69de29..0000000 diff --git a/site/new/build/english/page_contributors.txt b/site/new/build/english/page_contributors.txt deleted file mode 100644 index e69de29..0000000 diff --git a/site/new/build/english/page_cvs.txt b/site/new/build/english/page_cvs.txt deleted file mode 100644 index e69de29..0000000 diff --git a/site/new/build/english/page_donations.txt b/site/new/build/english/page_donations.txt deleted file mode 100644 index e69de29..0000000 diff --git a/site/new/build/english/page_index.txt b/site/new/build/english/page_index.txt deleted file mode 100644 index 5604f94..0000000 --- a/site/new/build/english/page_index.txt +++ /dev/null @@ -1,22 +0,0 @@ -<p><b>NOTE:</b> The French version of this site is not yet available. -Additionally, only the master site currently works, mirrors should -be back up eventually.</p> -<p>This site provides various open source -.link software software -to anyone who finds it useful. The projects mostly were written from -scratch by Matthew Mondor and released under a BSD-style license, -but we also host third-party software related to these projects, -provided by other -.link contributors contributors -worldwide who wanted to make their related work freely available.</p> -<p>The hosting and bandwidth for the -.link mirrors mirrors -is provided by various -.link donations donators -accross the globe. Although we attempt to encourage contributors and -donators, we try to keep this site as free from commercial ads as -possible as a convenience to our users. The main mmsoftware project -.links philosophy philosophy will describe our goals with more details.</p> -<p>Matthew Mondor can be contacted via email at -<a href="mailto:mmondor@accela.net">mmondor@accela.net</a> for suggestions, -contributions, donations, flames, thanks, business and bug reports.</p> diff --git a/site/new/build/english/page_mirrors.txt b/site/new/build/english/page_mirrors.txt deleted file mode 100644 index e69de29..0000000 diff --git a/site/new/build/english/page_philosophy.txt b/site/new/build/english/page_philosophy.txt deleted file mode 100644 index e69de29..0000000 diff --git a/site/new/build/english/page_projects.txt b/site/new/build/english/page_projects.txt deleted file mode 100644 index e69de29..0000000 diff --git a/site/new/build/english/page_software.txt b/site/new/build/english/page_software.txt deleted file mode 100644 index e69de29..0000000 diff --git a/site/new/build/english/soft_descriptions.txt b/site/new/build/english/soft_descriptions.txt deleted file mode 100644 index 3c2bc21..0000000 --- a/site/new/build/english/soft_descriptions.txt +++ /dev/null @@ -1,15 +0,0 @@ -# Each project is separated by an empty line. The first line of a project -# consists of it's name, the second line of it's short description, and -# the following lines, in HTML format, of the long description, ending -# with an empty line. -# -mmftpd -Unprivileged virtual users FTP server -Long multiline description follows in HTML, until empty line. -So it continues... -And still... - -ginseng-ftpd -blah -blah - diff --git a/site/new/build/mmsite.c b/site/new/build/mmsite.c deleted file mode 100644 index 2ef1395..0000000 --- a/site/new/build/mmsite.c +++ /dev/null @@ -1,557 +0,0 @@ -/* $Id: mmsite.c,v 1.1 2004/04/30 00:05:53 mmondor Exp $ */ - -/* - * Copyright (C) 2003, Matthew Mondor - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Matthew Mondor. - * 4. The name of Matthew Mondor may not be used to endorse or promote - * products derived from this software without specific prior written - * permission. - * 5. Redistribution of source code may not be released under the terms of - * any GNU Public License derivate. - * - * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -/* This program builds the site in htdocs/. Please read mmsite.8 for more - * information on the required layout. This is a helper to generate correct - * HTML, translate the site into multiple languages and build the software - * and faq indexes and tables, using static pages only. This then allows - * acceleration using cacheing HTTP proxies and helps to more easily provide - * a straightforward method for mirror providers. - */ - - - -/* HEADERS */ - -#include <sys/types.h> -#include <stdlib.h> -#include <stdio.h> - -#include <mmtypes.h> -#include <mmstring.h> -#include <mmpool.h> -#include <mmlist.h> -#include <mmhash.h> - - - -/* DEFINITIONS */ - -/* The following system allows to load arbitrary tables from files. The - * table field is only used for language map to the project descriptions. - */ -#define COLS_MAX 4 -#define LINE_MAX 256 - -typedef struct table_node { - hashnode_t node; - char line[LINE_MAX]; - char *columns[COLS_MAX]; - size_t lengths[COLS_MAX]; - hashtable_t *table; -} table_node; - -/* And for linked lists, where order is necessary */ -typedef struct list_node { - node_t node; - char line[LINE_MAX]; - char *columns[COLS_MAX]; - size_t lengths[COLS_MAX]; - u_int32_t hash; -} list_node; - -/* The following are definitions helping to use each of our known tables. - * Let's start with the global ones: - * -------------------------------- - */ - -/* table_languages */ -enum columns_languages { - LANGUAGES_NAME = 0, - LANGUAGES_MAX -}; -#define LANGUAGES_KEY LANGUAGES_NAME - -/* table_mirrors */ -enum columns_mirrors { - MIRRORS_NAME = 0, - MIRRORS_URL, - MIRRORS_CONTRIBUTOR, - MIRRORS_MAX -}; -#define MIRRORS_KEY MIRRORS_URL - -/* table_pages */ -enum columns_pages { - PAGES_NAME = 0, - PAGES_MAX -}; -#define PAGES_KEY PAGES_NAME - -/* table_software */ -enum columns_software { - SOFTWARE_CATEGORY = 0, - SOFTWARE_DIRECTORY, - SOFTWARE_MAX -}; -#define SOFTWARE_KEY SOFTWARE_CATEGORY - -/* For each of table_software, projects */ -enum columns_projects { - PROJECTS_NAME = 0, - PROJECTS_STATUS, - PROJECTS_VERSION, - PROJECTS_MAX -}; -#define PROJECTS_KEY PROJECTS_NAME - -/* Following are the language-specific tables: - * ------------------------------------------ - */ - -/* table_menu_languages */ -enum columns_menu_languages { - MENU_LANGUAGES_NAME = 0, - MENU_LANGUAGES_DESCR -}; - -#define MENU_LANGUAGES_KEY MENU_LANGUAGES_NAME - -/* This structure is hold to hold nodes in project description tables */ - -#define TDN_PROJECT_MAX 32 -#define TDN_DESCR_S_MAX 256 -#define TDN_DESCR_L_MAX 2048 - -typedef struct table_descr_node { - hashnode_t node; - char project[TDN_PROJECT_MAX]; - char descr_s[TDN_DESCR_S_MAX]; - char descr_l[TDN_DESCR_L_MAX]; -} table_descr_node; - - - -/* PROTOTYPES */ - -int main(void); - -static hashtable_t *table_load(const char *, unsigned int, unsigned int); -static hashtable_t *table_descr_load(const char *); -static hashtable_t *table_free(hashtable_t *); -static list_t *list_load(const char *, unsigned int, unsigned int); -static list_t *list_free(list_t *); - - - -/* GLOBALS */ - -static pool_t table_pool, table_descr_pool, list_pool; -static hashtable_t *table_languages = NULL, *table_descriptions = NULL; - -static list_t *list_software = NULL; - - - -/* CODE */ - -int main(void) -{ - /* First initialize our fast memory management pools */ - if (!pool_init(&table_pool, malloc, free, sizeof(table_node), - 65536 / sizeof(table_node), 0, 0)) { - (void) fprintf(stderr, "main() - pool_init(table_pool)\n"); - return EXIT_FAILURE; - } - if (!pool_init(&table_descr_pool, malloc, free, sizeof(table_descr_node), - 65536 / sizeof(table_descr_node), 0, 0)) { - (void) fprintf(stderr, "main() - pool_init(table_descr_pool)\n"); - return EXIT_FAILURE; - } - if (!pool_init(&list_pool, malloc, free, sizeof(list_node), - 65536 / sizeof(list_node), 0, 0)) { - (void) fprintf(stderr, "main() - pool_init(list_pool)\n"); - return EXIT_FAILURE; - } - - /* Load global lists */ - if ((list_languages = list_load("site_languages.txt", LANGUAGES_MAX, - LANGUAGES_KEY)) == NULL) { - (void) fprintf(stderr, "main() - list_load(site_languages.txt)\n"); - return EXIT_FAILURE; - } - if ((list_mirrors = list_load("site_mirrors.txt", MIRRORS_MAX, - MIRRORS_KEY)) == NULL) { - (void) fprintf(stderr, "main() - list_load(site_mirrors.txt)\n"); - return EXIT_FAILURE; - } - - /* Load required lists */ - if ((table_languages = table_load("site_languages.txt", LANGUAGES_MAX, - LANGUAGES_KEY)) == NULL) { - (void) fprintf(stderr, "main() - table_load(languages)\n"); - return EXIT_FAILURE; - } - if ((table_descriptions = table_descr_load( - "english/soft_descriptions.txt")) == NULL) { - (void) fprintf(stderr, "main() - table_descr_load(descriptions)\n"); - return EXIT_FAILURE; - } - if ((list_software = list_load("site_software.txt", SOFTWARE_MAX, - SOFTWARE_KEY)) == NULL) { - (void) fprintf(stderr, "main() - list_load(software)\n"); - return EXIT_FAILURE; - } else { - list_node *node; - - for (node = DLIST_TOP(list_software); node != NULL; - node = DLIST_NEXT(node)) - (void) printf("'%s|%s'\n", node->columns[SOFTWARE_CATEGORY], - node->columns[SOFTWARE_DIRECTORY]); - } - - { - table_node *node; - - if ((node = (table_node *)hashtable_lookup(table_languages, - "english", 7)) - != NULL) - (void) printf("%s\n", node->columns[LANGUAGES_NAME]); - else - (void) printf("'english' not found!\n"); - } - { - table_descr_node *node; - - if ((node = (table_descr_node *)hashtable_lookup(table_descriptions, - "mmftpd", 6)) != NULL) { - (void) printf("Project: %s\n", node->project); - (void) printf("Descr_s: %s\n", node->descr_s); - (void) printf("Descr_l: %s", node->descr_l); - } else - (void) printf("'mmftpd' not found!\n"); - } - - table_languages = table_free(table_languages); - return EXIT_SUCCESS; -} - - -/* Loads a hash table from the specified text file which holds one entry - * per line and <cols> expected columns each. Links each entry to the table - * using <key> column as the lookup key. Returns the hashtable_t pointer on - * success, or NULL. - */ -static hashtable_t *table_load(const char *file, unsigned int cols, - unsigned int key) -{ - hashtable_t *table = NULL; - table_node *node = NULL; - FILE *fh; - - if (cols >= COLS_MAX || key >= COLS_MAX) { - (void) fprintf(stderr, "table_load(%s) - cols|key >= COLS_MAX!\n", - file); - return NULL; - } - - if ((fh = fopen(file, "r")) != NULL) { - if ((table = malloc(sizeof(hashtable_t))) != NULL) { - if (hashtable_init(table, HT_DEFAULT_CAPACITY, HT_DEFAULT_FACTOR, - malloc, free, mm_memcmp, hashtable_hash, TRUE)) { - for (;;) { - int i; - size_t len; - - if (node == NULL && (node = (table_node *)pool_alloc( - &table_pool, FALSE)) == NULL) { - (void) fprintf(stderr, - "table_load(%s) - pool_alloc()\n", - file); - break; - } - if (fgets(node->line, LINE_MAX - 1, fh) == NULL) { - node = (table_node *)pool_free((pnode_t *)node); - break; - } - len = mm_strlen(node->line); - if (len > 0 && node->line[len - 1] == '\n') - len--; - if (len > 0 && node->line[len - 1] == '\r') - len--; - node->line[len] = '\0'; - if (*node->line == '#' || *node->line == '\0') - continue; - if (mm_strasplq(node->columns, node->line, cols) != cols) { - (void) fprintf(stderr, - "table_load(%s) - Invalid line '%s'\n", - file, node->line); - break; - } - for (i = 0; i < cols; i++) - node->lengths[i] = mm_strlen(node->columns[i]); - node->table = NULL; - if (!hashtable_link(table, (hashnode_t *)node, - node->columns[key], node->lengths[key])) { - (void) fprintf(stderr, - "table_load(%s) - link key '%s'\n", - file, node->columns[key]); - break; - } - node = NULL; - } - if (node != NULL) - table = table_free(table); - } else { - (void) fprintf(stderr, "table_load(%s) - hashtable_init()\n", - file); - free(table); - table = NULL; - } - } else - (void) fprintf(stderr, "table_load(%s) - malloc(table)\n", - file); - (void) fclose(fh); - } - - return table; -} - -/* This function loads another type of file-based table. For each entry, - * the first line consists of the project name, the second of the short - * project description, and the following lines of the long description - * with allowed embedded HTML for emphasis, ending with an empty line. - */ -static hashtable_t *table_descr_load(const char *file) -{ - hashtable_t *table = NULL; - table_descr_node *node = NULL; - FILE *fh; - char line[TDN_DESCR_S_MAX]; - - if ((fh = fopen(file, "r")) != NULL) { - if ((table = malloc(sizeof(hashtable_t))) != NULL) { - if (hashtable_init(table, HT_DEFAULT_CAPACITY, HT_DEFAULT_FACTOR, - malloc, free, mm_memcmp, hashtable_hash, TRUE)) { - int field = 0; - size_t descr_l_len = 0, project_len = 0; - - for (;;) { - size_t len; - - if (node == NULL) { - if ((node = (table_descr_node *)pool_alloc( - &table_descr_pool, FALSE)) == NULL) { - (void) fprintf(stderr, - "table_descr_load(%s) - pool_alloc()\n", - file); - break; - } - /* New entry */ - field = 0; - descr_l_len = project_len = 0; - } - if (fgets(line, TDN_DESCR_S_MAX - 1, fh) == NULL) { - if (field == 0) - node = (table_descr_node *)pool_free( - (pnode_t *)node); - else - (void) fprintf(stderr, "table_descr_load(%s) - \ -Incomplete entry for '%s'\n", file, node->project); - break; - } - len = mm_strlen(line); - if (len > 0 && line[len - 1] == '\n') - len--; - if (len > 0 && line[len - 1] == '\r') - len--; - line[len] = '\0'; - if (*line == '#' && field == 0) - continue; - if (len == 0) { - /* Empty line, ignore if between two entries, - * consider it as entry termination if we filled - * them all, generate an error otherwise. - */ - if (field == 0) - continue; - else if (field == 2) { - /* Link entry and force new entry creation */ - if (!hashtable_link(table, (hashnode_t *)node, - node->project, project_len)) { - (void) fprintf(stderr, "table_descr_load(%s) \ -- hashtable_link(%s)\n", file, node->project); - break; - } - node = NULL; - continue; - } else { - (void) fprintf(stderr, "table_descr_load(%s) - \ -Incomplete entry for '%s'\n", file, node->project); - break; - } - } - if (field == 0) { - /* Project field entry */ - if (len > TDN_PROJECT_MAX - 1) { - (void) fprintf(stderr, "table_descr_load(%s) - \ -Project field exceeds %d for '%s'\n", file, TDN_PROJECT_MAX - 1, - node->project); - break; - } - mm_memcpy(node->project, line, len + 1); - project_len = len; - field = 1; - continue; - } else if (field == 1) { - /* Short description entry */ - if (len > TDN_DESCR_S_MAX - 1) { - (void) fprintf(stderr, "table_descr_load(%s) - \ -Short description field exceeds %d for '%s'\n", file, TDN_DESCR_S_MAX - 1, - node->project); - break; - } - mm_memcpy(node->descr_s, line, len + 1); - field = 2; - continue; - } else if (field == 2) { - /* Long description entry */ - if (descr_l_len + len + 1 > TDN_DESCR_L_MAX - 1) { - (void) fprintf(stderr, "table_descr_load(%s) - \ -Long description field exceeds %d for '%s'\n", file, TDN_DESCR_L_MAX - 1, - node->project); - break; - } - mm_memcpy(&node->descr_l[descr_l_len], line, len); - descr_l_len += len; - node->descr_l[descr_l_len++] = '\n'; - continue; - } - } - if (node != NULL) - table = table_free(table); - } else { - (void) fprintf(stderr, - "table_descr_load(%s) - hashtable_init()\n", - file); - free(table); - table = NULL; - } - } else - (void) fprintf(stderr, "table_descr_load(%s) - malloc(table)\n", - file); - (void) fclose(fh); - } - - return table; -} - -static hashtable_t *table_free(hashtable_t *table) -{ - if (table != NULL) { - (void) hashtable_destroy(table, TRUE); - free(table); - } - - return NULL; -} - - -static list_t *list_load(const char *file, unsigned int cols, unsigned int key) -{ - list_t *list = NULL; - list_node *node = NULL; - FILE *fh; - - if (cols >= COLS_MAX || key >= COLS_MAX) { - (void) fprintf(stderr, "list_load(%s) - cols|key >= COLS_MAX!\n", - file); - return NULL; - } - - if ((fh = fopen(file, "r")) != NULL) { - if ((list = malloc(sizeof(list_t))) != NULL) { - DLIST_INIT(list); - for (;;) { - int i; - size_t len; - - if (node == NULL && (node = (list_node *)pool_alloc(&list_pool, - FALSE)) == NULL) { - (void) fprintf(stderr, - "list_load(%s) - pool_alloc()\n", - file); - break; - } - if (fgets(node->line, LINE_MAX - 1, fh) == NULL) { - node = (list_node *)pool_free((pnode_t *)node); - break; - } - len = mm_strlen(node->line); - if (len > 0 && node->line[len - 1] == '\n') - len--; - if (len > 0 && node->line[len - 1] == '\r') - len--; - node->line[len] = '\0'; - if (*node->line == '#' || *node->line == '\0') - continue; - if (mm_strasplq(node->columns, node->line, cols) != cols) { - (void) fprintf(stderr, - "list_load(%s) - Invalid line '%s'\n", - file, node->line); - break; - } - for (i = 0; i < cols; i++) - node->lengths[i] = mm_strlen(node->columns[i]); - node->hash = hashtable_hash(node->columns[key], - node->lengths[key]); - DLIST_APPEND(list, (node_t *)node); - node = NULL; - } - if (node != NULL) - list = list_free(list); - } else - (void) fprintf(stderr, "list_load(%s) - malloc(list)\n", - file); - (void) fclose(fh); - } - - return list; -} - -static list_t *list_free(list_t *list) -{ - if (list != NULL) { - node_t *node, *tnode; - - for (node = DLIST_TOP(list); node != NULL; node = tnode) { - tnode = DLIST_NEXT(node); - (void) pool_free((pnode_t *)node); - } - free(list); - } - - return NULL; -} diff --git a/site/new/build/mmsite.conf b/site/new/build/mmsite.conf deleted file mode 100644 index 586b8b5..0000000 --- a/site/new/build/mmsite.conf +++ /dev/null @@ -1,12 +0,0 @@ -# General configuration options about the site - -TITLE "Matthew Mondor's Software Site" -BGCOLOR #f0f0ff -TEXT #000000 -LINK #3535c5 -VLINK #700080 -MENU_BGCOLOR #d0d0f0 -MENU_TEXT #000066 -FONT "helvetica, arial" -HTDOCS ../htdocs -DEFAULT_LANG en diff --git a/site/new/build/site_faq.txt b/site/new/build/site_faq.txt deleted file mode 100644 index ad51485..0000000 --- a/site/new/build/site_faq.txt +++ /dev/null @@ -1,6 +0,0 @@ -# One FAQ entry per line, one column: -# <faq> -# -mmftpd -mmmail -mmstatd diff --git a/site/new/build/site_languages.txt b/site/new/build/site_languages.txt deleted file mode 100644 index 27884a1..0000000 --- a/site/new/build/site_languages.txt +++ /dev/null @@ -1,5 +0,0 @@ -# One language description per line, one column: -# <language> -# -english -french diff --git a/site/new/build/site_mirrors.txt b/site/new/build/site_mirrors.txt deleted file mode 100644 index 8e6834f..0000000 --- a/site/new/build/site_mirrors.txt +++ /dev/null @@ -1,6 +0,0 @@ -# One line per entry, three columns: -# <name> <URL> <provider> -# -canada http://mmondor.dynup.net/ "Ryan Werber" -united-states http://gobot.accela.net/ "Matthew J Backes" -holland http://mmondor.oostendorp-ict.nl/ "Jeroen Oostendorp" diff --git a/site/new/build/site_pages.txt b/site/new/build/site_pages.txt deleted file mode 100644 index 8d18ccc..0000000 --- a/site/new/build/site_pages.txt +++ /dev/null @@ -1,12 +0,0 @@ -# One entry per line, one column: -# <name> -# -index -software -donations -contributors -philosophy -cvs -projects -mirrors -contact diff --git a/site/new/build/site_software.txt b/site/new/build/site_software.txt deleted file mode 100644 index 19c965c..0000000 --- a/site/new/build/site_software.txt +++ /dev/null @@ -1,6 +0,0 @@ -# Each software category, one entry per line, two columns: -# <category> <location> -# -stable software/stable -development software/devl -old software/old diff --git a/site/new/build/soft_development.txt b/site/new/build/soft_development.txt deleted file mode 100644 index 8f9ebe9..0000000 --- a/site/new/build/soft_development.txt +++ /dev/null @@ -1,4 +0,0 @@ -# One project per line, three columns per project: -# <projname> <status> <version> -# -mmftpd devl 1.0 diff --git a/site/new/build/soft_old.txt b/site/new/build/soft_old.txt deleted file mode 100644 index 0874594..0000000 --- a/site/new/build/soft_old.txt +++ /dev/null @@ -1,4 +0,0 @@ -# One project per line, three columns per project: -# <projname> <status> <version> -# -ginseng-ftpd devl 1.0 diff --git a/site/new/build/soft_stable.txt b/site/new/build/soft_stable.txt deleted file mode 100644 index 0874594..0000000 --- a/site/new/build/soft_stable.txt +++ /dev/null @@ -1,4 +0,0 @@ -# One project per line, three columns per project: -# <projname> <status> <version> -# -ginseng-ftpd devl 1.0 diff --git a/site/philosophy.html b/site/philosophy.html deleted file mode 100644 index bb21770..0000000 --- a/site/philosophy.html +++ /dev/null @@ -1,133 +0,0 @@ -<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> - -<html><head> -<!-- $Id: philosophy.html,v 1.5 2003/06/04 12:17:47 mmondor Exp $ - Copyright (c) 2002-2003 Matthew Mondor, ALL RIGHTS RESERVED. ---> -<title>Matthew Mondor's Software Site - Philosophy - - - - - - - - - -

- - - - - -

- - -
-Sections
-
-

Main 
Software 
Donations 
Contributors 
Philosophy 
CVS 
Projects 
Mirrors 
Contact 
-

-
Image Copyright (c) 2002-2003, Matthew Mondor -
-
-Philosophy behind mmsoftware
-
- -

-Several primary goals were the basis to write my suite of public internet -services. One consisted of a challenge to learn UNIX APIs deeply at the time -I decided to write my own. This is no longer the case, but probably that the -most driving factor still has to do with my need to run such services. -

-Of course there exists a variety of solutions for each of the standard -protocols. However, most of them seemed historical, based on code which made -it though the years through extreme amounts of patching and kludges. This -generally results in solutions which are hard to configure, and have gone -though a long history of security-related issues. Most of them even support -obsolete protocols, or experimental ones which never have been widely used. -

-Moreover, several of them were subject to strict licensing issues. Writing -my own software I have the full rights to do whatever I want with it, as -well as eventually distribute closed-source special editions commercially. -But I didn't want to be the only one to be able to do this, hence I decided -to release my software under a BSD-style license (Berkeley Source Distribution -license), allowing others to modify it and distribute it under source or -binary form as wanted, only requireing them to add an aknowledgement to their -product documentation: -

-  This product includes software written by Matthew Mondor. -

-It however obviously is encouraged to report to me the various bug fixes -or enhancement patch so that the main publicly available tree also evolve. -

-Considering security aspects, most traditional UNIX internet services software -ran as the superuser, and required standard UNIX users to be added for each -remote user. I strongly beleive that most administrators enjoy being able to -only create virtual users rather than real UNIX shell ones for each of their -users. My daemons therefore only allow virtual users. Moreover, none of them -require to run as the superuser after their initial startup. Most of them -have support for chroot(2) as well, for administrators who need it. -

-These are public services afterall. I personally use them to contribute -software and services which are often free of charge, and would find it quite -discouraging if my services were exploited and servers virtually destroyed. -I think that many administrators are in the same position and would like to -run safe services. -

-Special care is taken when writing this software to avoid memory leaks (which -could be a threat to service uptime and autonomy), and to check every -error condition possible. As the services should remain up, system resources -error conditions are treated in a safe way; The current operation is cancelled -to not execute code which requires these new resources to be allocated, -previous allocated ones are released if any (but not all) were obtained for -the current operation as well, and the service keeps running as usual. This -also implies using I/O operations which can timeout where required. Also, -libc functions with uncertain delay periods get executed by an asynchroneous -parallel system when userspace threading systems are used, to prevent locking -the main process, allowing service to remain fluid among the multiple -simultaneous connections being served. Some effort was also made to keep the -code clean. -

-C was chosen to write these for several reasons. I am well used to it, having -used it for many years, was a primary one. A second worthwhile reason is that -the UNIX, POSIX and BSD APIs are intended for C programs. It is also possible -for the programmer to optimize C code decently rather than only relying on -compiler effeciency to do it, gaining speed advantage without having to use -less maintainable and unportable assembly. When well written, C programs are -also portable. C++ could have suited, but would result in generally less -efficient code. I also like to control my own use of memory useage, object -allocation and garbage collection, hence it was not worth it to use C++. -Where possible, ANSI-C compliance is observed. -

-Matthew Mondor -

-
-
-Languages
-
-English 
French 
-

-

Mirrors
-
-Canada 
United-States 
Holland 
-

- -$Id: philosophy.html,v 1.5 2003/06/04 12:17:47 mmondor Exp $
-This site Copyright (c) 2002-2003, Matthew Mondor, ALL RIGHTS RESERVED. -
- diff --git a/site/projects.html b/site/projects.html deleted file mode 100644 index 199eeb2..0000000 --- a/site/projects.html +++ /dev/null @@ -1,146 +0,0 @@ - - - - -Matthew Mondor's Software Site - Projects - - - - - - - - - -

- - - - - -

- - -
-Sections
-
-

Main 
Software 
Donations 
Contributors 
Philosophy 
CVS 
Projects 
Mirrors 
Contact 
-

-
Image Copyright (c) 2002-2003, Matthew Mondor -
-
-Various Projects and Background
-
- -

-Here first follows an overview of my computer-related background -

-A brief overview of my computer-related background: I touched my first computer -at the age of 8, an Apple][+, on which I immediately wanted to code games, -mostly of the text type. I then learned 6802 assembly, as AppleSoft BASIC was -pretty much useless, and Z80 assembly, which was available through a -bridgeboard. I then wrote disk utilities (backup, data retreival). -

-I then at my great joy had my first Amiga when I was 15, on which I first used -mildly AmigaBASIC. Of course a year later I was learning C, and using the RKMS -wrote AstralPortal (Voice/Fido/Data Modem software) in the few following years. -I then did some 2d vector graphics using 68000 assembly, C and sometimes -BlitzBasic (which accepted direct assembly instructions and had nice blit -routines. Some playing with ARexx was then done. The last thing I developed -on Amiga was audio sample manipulation and compression utilities in C, along -with a programmable binaural/hemisync braintrainer, and some custom GUI -library. Another latest project was a control server which allowed several -RS232 terminals to be synchronized with a main multitasking server application -with abstract tty-specific code translation. What a great box that was. -

-Then finally, I decided to get an i386DX/40/8megs although it was already -deprecated, and started doing some Turbo-C and 8088 assembly on MS-DOS, -and wrote trivial 2d 8-bit functions library along with a graphic -block-oriented editor for game programming. Some scrolling, sprite and -collision code was added, and it was a ready system to write games, but none -were ever written using it, but demos. As I always was avoiding windows since, -but had to at least confront it for a while, I finally bought an AMD-K6-2/450 -and learned some Win32 API, tried various compilers with GUI frontends. I -unfortunately was pretty discouraged after a year or so, and was not -programming anything useful; Until I made a discovery... Linux 2.0.35 (RH6) -in December 1999. -

-I used RedHat 6 for a few months, getting aquainted with GCC (which fortunately -had the same base interface than DICE dcc from Matt Dillon which I was using -on the Amiga). And, glibc info/man pages. Linux finally made me discover that -a PC could finally be worth it, as much as Amiga was. I soon found that on -an AmigaOS shell I couldn't remember the commands, erroneously typing unix -ones. The switch was made. I then tried Debian Potato, which of course made -me forget RH for good, and eventually did my own distribution, Ginseng, a -security and server related distribution. Between Debian and Ginseng I started -to write a few meaningful TCP server-oriented utilities. I then discovered -NetBSD. -

-Obviously I then had found my ideal OS, which I am still heavily using today -on all my systems. I finally was free from glibc, which was everyday getting -more bloated without true additionnal functionallity. -I tried other BSD variants and was more convinced that NetBSD still was my -choice. My current publically available software was written using it. -Current projects include mmmail, which is now being totally redesigned from -scratch (for v2), mmftpd, Xisop (a portable non-SMP multitasking preemptive -microkernel similar to AmigaOS), an arbitrary math library and C-like -simplified tokernizer/interpretor language for secure distributed network -clients, along with their distributed server counterpart. I mostly emphasize -my projects on security (good tactics as running virtual services as -non-privileged users, encryption, etc are useful). -

-I also use and test various tunnelling/VPN solutions, am working on a secure -NFS alternative, and various work-related projects. I try to eventually -provide easy solutions to common administration nightmares such as sendmail -administration, localized central data servers and similar tasks. Some work -has also been done on a state-persistant secure HTTP authentication protocol -where unique cookies are exchanged between each page under SSL, and their -origin/expiration verified on the server-side, possibly that an ssl-httpd will -eventually become available for safe remote administration of mmmail, mmftpd -and other server daemons I write. -

-I often don't mind to redesign existing systems, re-invent them or be -inspired by their concepts, when a happy result can be obtained afterwards. -Other non-computer related interests exist, including swimming, martial -arts, meditation (often a necessity for relaxation), international foods, -microbrewered and rare dark, rich beers, especially the stout type. -Writing also has always been a great passion all my life, from adventure -novels (unpublished) to technical documentation (some only has been made -available publically). And music. What a great way to express soul states, -composing and producing various music types, especially Fusion-Jazz remains -a never-ending story. -

-Matt -

-
-
-Languages
-
-English 
French 
-

-

Mirrors
-
-Canada 
United-States 
Holland 
-

- -$Id: projects.html,v 1.6 2003/06/04 12:17:47 mmondor Exp $
-This site Copyright (c) 2002-2003, Matthew Mondor, ALL RIGHTS RESERVED. -
- diff --git a/site/software.html b/site/software.html deleted file mode 100644 index 7781a2a..0000000 --- a/site/software.html +++ /dev/null @@ -1,342 +0,0 @@ - - - - -Matthew Mondor's Software Site - Software - - - - - - - - - -

- - - - - -

- - -
-Sections
-
-

Main 
Software 
Donations 
Contributors 
Philosophy 
CVS 
Projects 
Mirrors 
Contact 
-

-
Image Copyright (c) 2002-2003, Matthew Mondor -
-
-The Software
-
- -

-This page contains the various official mmsoftware sources. The third-party -software provided by the contributors can be found in the -contributors area. Access to the current -official mmsoftware sources CVS repository is available (via pserver). Details -about CVS can be found under the CVS section. It is -also possible to obtain the source archives of the old releases for reference -only, in the software archive: software/. -

Featured software:

-This software is actively maintained and in constant development. This also -means that bug reports are taken into consideration as fast as -possible and new version released accordingly, shortly after bug discovery, -when a suitable solution has been implemented to solve the issue. -

-
- - - - - - - - - - - -
mmftpd0.0.17Devl -ChangeLog -MD5
-Unprivileged virtual users FTP server -
-

-mmftpd was written from scratch from the ground up, and consists of a -featureful yet very security-aware FTP server. It is released under the -terms and conditions of the BSD license with advertizing clause to keep -credits to the author. It ensures to run under a single unprivileged user, -and provides FTP virtual users, as opposed to traditional UNIX/system ones, -which each appear jailed in their home directory using extensive path sanity -checking. It optionally also can chroot(2) at program startup for the whole -service and all users to be setup under a real alternative root jail. -Runs great on BSD and Linux systems and is fairly small in size. Well -documented via UNIX manual pages. -

-The server can limit the connection rate from an address and also has -bandwidth traffic shaping capabilities for both control and data ports, -on a per-connection and global basis. It also can limit the connections -on several factors (maximum number of addresses, number of allowed -connections per address and how many simultaneous connections from the same -FTP user to accept). Client hostname resolving is optional for speed, and -is performed using asynchroneous methods when enabled. -

-For each FTP user a number of permission parameters can be customized, -including the maximum upload and download speed, umask, rights to -change umask, upload, modify, maximum home directory tree size (even safe -among multiple simultaneous connections of the same user). Various techniques -were implemented to prevent common Denial Of Service attempts. -

-Various statistics are reliably maintained using the mmstat facility, -and the administrator can set the verbosity of wanted events and statistics -to be output via syslog. Moreover it uses efficient custom I/O buffering -around filedescriptors for speed. Users are stored in a configuration file -with their permissions, using native crypt(3) password hashes -(both MD5 and DES), so that it is possible to create virtual mmftpd users -from system ones using the same password hashes. The server options are -configured via a second configuration file. -


- - - - - - - - -
mmmail0.0.23Devl -ChangeLog -MD5
-Unprivileged virtual SMTP+POP3 server suite using MySQL -
-

-mmmail was written from scratch from the ground up, and consists of a -suite of SMTP and POP3 servers which can fully run under a unprivileged -user. It is released under the terms and conditions of the BSD license with -advertizing clause to keep credits to the author. It provides virtual mail -users, contrary to traditional UNIX/system ones. It optionally supports -chroot(2) at program startup to enclose itself into a jail. It runs great -on both BSD and Linux systems and is fairly small in size. Well documented -via UNIX manual pages. -

-The servers can limit their clients connection rate on a per-address basis, -and also support bandwidth traffic shaping capabilities in both directions -(connection-specific and global ones). It also can limit the connections -on several factors (maximum number of addresses, number of allowed -connections per address and how many simultaneous connections from the same -POP3 user to accept). Client hostname resolving is optional for speed, and -is performed using asynchroneous methods when enabled. Moreover, the -administrator may decide weither to check for HELO and/or MAIL MX DNS -records before accepting SMTP mail from a client. Use of HELO may be -disabled or enforced. Various techniques were implemented to prevent common -Denial Of Service attempts. -

-For each mail user/password pair can exist several email addresses at several -virtual hosts, and aliasing is supported to map unexisting addresses to others, -even optionally using wildcard pattern matching. The administrator can also -set the address and/or hostname masks through which mail with empty FROM -address is sent. Each mailbox can be set customizeable quotas (mailbox total -size and total number of messages). All storage (users, mailboxes, and mail -itself) uses MySQL, which permits a global database server to be used, even -remotely. A persistant connection is established and maintained with the -server by each daemon, and re-established if ever necessary. -

-Various statistics are reliably maintained using the mmstat facility, -and the administrator can set the verbosity of wanted events and statistics -to be output via syslog. Moreover it uses efficient custom I/O buffering -around filedescriptors for speed. Each daemon is configured through it's -own configuration file. User password hashes are stored in native crypt(3) -format (both MD5 and DES work) so it is possible to translate system users -to mmmail virtual ones keeping the same hash. -Also, mmpop3d supports unstandard POP3 PAGE command (better than TOP) which -was especially implemented for human POP3 clients. -

-Unfortunately, support for relaying will only be added for mmmail2, a future -re-implementation of mmmail which also should support multiple authentication -and storage methods, as well as many other features including IMAP. -If you are interested in knowing more about mmmail2 ongoing engineering -(diagrams and documentation), and to make suggestions, you can download this -mmmail-design.pdf document. -


- - - - - - - - - -
mmstatd0.0.8Devl -ChangeLog -MD5
-Statistics maintenance server daemon and client library -
-

-mmstatd was originally written for mmftpd and mmmail to asynchroneously -maintain statistical counters in an efficient manner. I however also -release it separately as it is used by some other people in their projects. -These include an IRC network services system which although using db4 for -most data storage uses mmstat(3) library for various statistics counters. -

-It does not require any additional libraries, and provides a simple API -for applications to either update counters or query statistics. The update -requests are done using an AF_LOCAL/UNIX datagram to the mmstatd service's -log socket, similarly to the way syslog(3) works. Statistics are queried -via an AF_LOCAL/UNIX stream on another mmstatd socket. Permissions for access -to both sockets can be different. -

-The service consists of a librarian and logger. The logger accumulates -requests filling a recovery log, while the librarian processes those logs -asynchroneously and syncs the disk database with them from time to time. -In case of a crash, the recovery logs which weren't synchronized yet to -disk are used to recover from the crash. Provided with it is a utility -to query and update statistics from the shell as well. -


Unmaintained software:

-

-This section contains software which works but which I am no longer -maintaining. These are Linux-specific and are getting old. I keep them -here because a fair amount of people find these handy and download -them. -

- - - - - - - - - -
mmtcpfwd0.1.0Stable -ChangeLog -MD5
-Port forwarder, MASQ fake identd and FTP proxy for Linux -
-

-Written from scratch, consists of a superserver daemon made of two parts, -one process running as the superuser (to perform tasks like modifying -firewall rules) and the other running as an unprivileged user performing -all the work. This privilege separation system is quite effective for -a secure setup. Linux 2.2 specific (can work with 2.4+ but without the -transparent proxying support). Released under the terms of the GPL -(GNU Public License). -

-mmtcpfwd provides transparent TCP/IP connection proxying from a MASQ enabled -gateway to other machines, including FTP connections, via a special userspace -passive FTP connections proxy supporting PASV, LPSV and EPSV, and masking the -actual FTP server LAN address by supplying the gateway's address instead. -Supports SMP where hardware permits. Uses a main configuration file to -configure all it's parameters. Also provides an optional random UNIX-ident -protocol daemon, allowing masqueraded connections behind the gateway to -use services requireing identd to run. -

-For each forwarded port, a non-privileged process is setup to listen to -and forward multiple connections to their configured destination. This -destination can consist of a remote or local host, and it is possible to -make it resolve IP address by hostname or to specify absolute IP addresses -for speed. -

-Several interesting techniques are implemented to counter Denial of Service -attacks: Total number of forwarded simultanious connections per port can be -set, as well as per address. Also permits to deny automatically an IP address -overflowing connections with a threshold, in which case each active connection -from that address are cleanly closed before applying the firewall deny rule, -executing a command of our choice. Allows to fake services like portsentry to -immediately DENY an IP address that connects, sending a configurable message -before closing and applying the DENY rule. Can also automatically undeny IP -addresses of offendants after a certain amount of minutes, or indefinitely. -As many IP addresses to never deny as we want can be specified. -

-Uses syslog logging, to keep log of connections/bytes transfered, elapsed -time, etc... Hostnames can be resolved or not, on a per-port basis. -Supports kernel's IP Transparent Proxying support to fake client's IP address -when forwarding. Aimed towards security as much as possible. -Fairly small, executable around 30k only. -

-

- - - - - - - - -
ginseng-ftpd1.6Devl -ChangeLog -MD5
-Security-enhanced Linux ftpd based on bsd-ftpd (NetBSD) -
-

-This server originally consisted of a port of BSD-FTPd to Linux, and various -custom features were added which I personally needed at the time. Since I -wrote mmftpd which much better suits my needs I no longer maintain -ginseng-ftpd. It's still available though, for people who need to run FTP -services with real users (where security is generally not that much of a -concern). An FTP server allowing use of standard UNIX system users obviously -required to run as the superuser. Run mmftpd if you need better security. -Was released under the terms of the BSD license. -

-I here describe a list of the various changes that I made on the original -BSD-FTPd code: The popular recently discovered single-byte vulnerability of -bsd-ftpd was fixed, some better sanity checking around seteuid(), -setegid() and fork() was added. Was fixed against the recursive LIST/NLST -problems which alot of FTP servers are vulnerable to, including BSD-FTPd -at the time. Now only requires a single configuration file (/etc/ftpusers) -for all account options, and users MUST be present in it to be allowed -FTP access (contrary to traditional behavior). The configuration file now -uses one user per line, and one column per user configurable option. -

-Support for read-only accounts, umask specification and homedir total size -limits on a per/user basis and number of connections per user was added, -note that the tree size quotas are only safe if only one simultaneous logins -of that user are allowed. Shared memory and semaphores would have been -required otherwise which would have strongly impacted performance. -This is not the case for mmftpd where threads are used and quotas are -safe no matter what. The server was modified to only accept to be launched -by the superuser. -

-The following command switches/parameters were added when starting the -daemon: -q to not display ftpd type/version to clients, -n to not resolve -hostnames for speed, -x to allow masquerading actual LAN IP address to -0.0.0.0 for passive replies (not that not all clients will work, I -recommend using mmtcpfwd passive FTP proxying to masquerade those to -the actual gateway's IP address instead, where all clients will work. -Finally, -q was added to specify which port to listen to. -

-
-
-
-Languages
-
-English 
French 
-

-

Mirrors
-
-Canada 
United-States 
Holland 
-

- -$Id: software.html,v 1.15 2003/12/12 15:58:39 mmondor Exp $
-This site Copyright (c) 2002-2003, Matthew Mondor, ALL RIGHTS RESERVED. -
- diff --git a/tests/js-test/README b/tests/js-test/README deleted file mode 100644 index 5922bc8..0000000 --- a/tests/js-test/README +++ /dev/null @@ -1,7 +0,0 @@ -$Id: README,v 1.3 2006/07/22 04:47:43 mmondor Exp $ - -Note that this was a test tree only. The current code which is in use -can be found under /cvsroot/mmondor/mmsoftware/js/ - -Thanks, -Matt diff --git a/tests/js-test/js/copy.js b/tests/js-test/js/copy.js deleted file mode 100644 index 49dcc82..0000000 --- a/tests/js-test/js/copy.js +++ /dev/null @@ -1,37 +0,0 @@ -/* $Id: copy.js,v 1.2 2005/07/01 13:11:08 mmondor Exp $ */ - -/* - * Test our read() and write() methods, mapping to unix read(2) and write(2). - * Seems to be working great so far. - */ - -src = '/tmp/src.bin' -dst = '/tmp/dst.bin' - -err = new FD(); -err.set(FD.STDERR_FILENO); - -srcfd = new FD(); -dstfd = new FD(); - -try { - srcfd.open(src, FD.O_RDONLY); - dstfd.open(dst, FD.O_WRONLY | FD.O_CREAT); - - var data; - - while (data = srcfd.read(16384)) { - if (data.length == 0) - break; - err.put('Copied ' + data.length + " bytes block\n"); - dstfd.write(data); - } - - srcfd.close(); - dstfd.fdatasync(); - dstfd.close(); -} catch (x) { - err.put(x + "\n"); -} - -err.close(); diff --git a/tests/js-test/js/fd.js b/tests/js-test/js/fd.js deleted file mode 100644 index 8e8f998..0000000 --- a/tests/js-test/js/fd.js +++ /dev/null @@ -1,35 +0,0 @@ -/* $Id: fd.js,v 1.5 2005/02/10 10:34:25 mmondor Exp $ */ - -var out = new FD(); -out.set(FD.STDOUT_FILENO); - -try { - - var fd = new FD(); - - { - var size; - - fd.open("/tmp/test.txt", FD.O_WRONLY | FD.O_CREAT); - size = fd.put("Yo!\n"); - out.put('Wrote bytes=' + size + ' to file="' + fd.path + - '", fd=' + fd.fd + ', created with mode=' + fd.mode + - "\n"); - fd.close(); - } - - { - var buf; - - fd.open("/tmp/test.txt", FD.O_RDONLY); - while (buf = fd.get()) - out.put(buf); - fd.close(); - } - -} catch (x) { - out.put(x + "\n"); -} - -out.put("done\n"); -out.close(); diff --git a/tests/js-test/js/game/objects.js b/tests/js-test/js/game/objects.js deleted file mode 100644 index 96fdfe7..0000000 --- a/tests/js-test/js/game/objects.js +++ /dev/null @@ -1,341 +0,0 @@ -/* $Id: objects.js,v 1.7 2005/11/25 07:38:35 mmondor Exp $ */ - -/* - * Copyright (c) 2005, Matthew Mondor - * ALL RIGHTS RESERVED. - */ - -/* - * Primarily written in JavaScript for easy and quick prototyping/testing. - * May eventually be written in C afterwards if need be, but the game might - * actually be implemented in JavaScript too depending on future decisions. - */ - -/* - * Simple but rather realistic implementation of items and containers. - * We observe containers maximum allowed volume and weight, container neck - * size, as well as expandable containers, such as cloth bags which can expand - * to eventually fill their parent container too. We allow nestled - * containers. We also add the concept of arbitrary marks which a user might - * add to objects to differenciate them. Containers can be open or closed, - * and obviously need to be open for items to be added/removed from them. - * We flag containers as such to accept either only solids or liquids, or - * both. We could consider flowing sand or salt as a liquid in this case. - * - * XXX - * - For liquids, we might need to provide a special object or flag for them - * to be able to have a volume/weight ratio, so that we could perform - * actions easily such as fill bottles/bags, etc. - * - The code dealing with the container parents, weights and expanding volume - * containers needs to throughly be tested, both for success and failing - * cases of all kinds. - * - XXX FIXME XXX - * When removing items from a container, the container's weight becomes 0, - * as well as its volume if it is expandable. - */ - - - -/* - * Describes a game item or object. - */ - -/* Used so that every item has a unique index ID */ -var unique = 0; - -/* Content types */ -const CT_SOLID = (1 << 0); -const CT_LIQUID = (1 << 1); -/* And container type flag */ -const CT_EXPAND = (1 << 2); -/* If item cannot be taken */ -const CT_FIXED = (1 << 3); - -function Item(name, description, volume, weight, ctype) -{ - this.name = name; - this.description = description; - this.volume = volume; - this.weight = weight; - this.ctype = ctype; - - this.marks = []; - - this.index = ++unique; -} - -Item.prototype = { - isContainer: function() - { - return false; - }, - - getVolume: function() - { - return this.volume; - }, - - getWeight: function() - { - return this.weight; - }, - - mark: function(mark) - { - this.mark.push(mark); - } -}; - - -/* - * And an object to handle container Items, inheriting the Item object. - */ - -const E_OK = 0; -const E_TOOLARGE = "Item too large"; -const E_TOOHEAVY = "Item too heavy"; -const E_FIXED = "Item is well fixed and cannot move"; -const E_BADTYPE = "Item is not of proper type (solid vs liquid)"; -const E_SELF = "Item could not penetrate itself"; -const E_NONE = "Item not in container"; -const E_ALREADY = "Item already in container"; -const E_CLOSED = "Container is closed"; - -function ContainerItem(name, description, volume, weight, ctype, - con_neck, con_volume_max, con_weight_max, con_ctype) -{ - this.base = Item; - this.base(name, description, volume, weight, ctype); - - this.con_neck = con_neck; - this.con_volume_max = con_volume_max; - this.con_weight_max = con_weight_max; - this.con_volume_cur = this.con_weight_cur = 0; - this.con_ctype = con_ctype; - - this.con_items = {}; - this.con_items_cnt = 0; - this.con_open = false; -} - -ContainerItem.prototype = { - isContainer: function() - { - return true; - }, - - open: function() - { - this.con_open = true; - }, - - close: function() - { - this.con_open = false; - }, - - getVolume: function() - { - /* - * Objects which expand, such as bags, need to report proper - * volume relating to the volume of the objects they hold. - * Objects which do not expand, such as boxes or bottles, - * need to always report the same volume. - */ - return ((this.con_ctype & CT_EXPAND) != 0) ? - this.volume + this.con_volume_cur : - this.volume; - }, - - getWeight: function() - { - return this.weight + this.con_weight_cur; - }, - - items: function() - { - return this.con_items_cnt; - }, - - add: function(item) - { - /* We can't accept fixed items */ - if ((item.ctype & CT_FIXED) != 0) - return E_FIXED; - - /* Can only add items to open container */ - if (!this.con_open) - return E_CLOSED; - - /* We can't add ourself in :) */ - if (item.index == this.index) - return E_SELF; - - /* Nor items which are already inside */ - if (this.con_items[item.index] != undefined) - return E_ALREADY; - - /* We only accept items of proper type */ - if ((this.con_ctype & item.ctype) == 0) - return E_BADTYPE; - - /* We can only hold objects which are small enough */ - if (this.con_neck < item.getVolume()) - return E_TOOLARGE; - if (this.con_volume_cur + item.getVolume() > - this.con_volume_max) - return E_TOOLARGE; - - /* We can only hold objects which are light enough */ - if (this.con_weight_cur + item.getWeight() > - this.con_weight_max) - return E_TOOHEAVY; - - this.con_volume_cur += item.getVolume(); - this.con_weight_cur += item.getWeight(); - - /* - * The weight has to propagate to parent containers. - * The volume also has, in the case where containers - * are expandable. - * We need to observe the maximum allowed volume and weight - * of all parent containers as well. - * We need to climb through the parents list to do this. - * Moreover, we need to actually reverse any changes made - * to the parents in case of any volume/weight exceeded. - */ - var err = E_OK; - for (var o = this.con_parent; o != undefined; - o = o.con_parent) { - if (o.getWeight() + this.getWeight() > - o.con_weight_max) { - err = E_TOOHEAVY; - break; - } - o.con_weight_cur += this.getWeight(); - if ((o.con_ctype & CT_EXPAND) != 0) { - if (o.getVolume() + this.getVolume() > - o.con_volume_max) { - err = E_TOOLARGE; - break; - } - o.con_volume_cur += this.getVolume(); - } - } - if (err != E_OK) { - for (var t = this.con_parent; t != o; - t = t.con_parent) { - t.con_weight -= this.getWeight(); - if ((t.con_ctype & CT_EXPAND) != 0) - t.con_volume_cur -= this.getVolume(); - } - if (err == E_TOOLARGE) - o.con_weight_cur -= this.getWeight(); - - this.con_volume_cur -= item.getVolume(); - this.con_weight_cur -= item.getWeight(); - - return err; - } - - if (item.isContainer()) - item.con_parent = this; - this.con_items[item.index] = item; - this.con_items_cnt++; - - return E_OK; - }, - - remove: function(item) - { - /* Can only remove items from open container */ - if (!this.con_open) - return E_CLOSED; - - /* We can only remove items which are really inside */ - if (this.con_items[item.index] == undefined) - return E_NONE; - - /* - * Climb up our parents containers list to update them - */ - for (var o = this.con_parent; o != undefined; - o = o.con_parent) { - o.con_weight_cur -= this.getWeight(); - if ((o.con_ctype & CT_EXPAND) != 0) - o.con_volume_cur -= this.getVolume(); - } - - if (item.isContainer()) - delete item.con_parent; - this.con_weight_cur -= item.getWeight(); - this.con_volume_cur -= item.getVolume(); - delete this.con_items[item.index]; - this.con_items_cnt--; - - return E_OK; - }, - - inventory: function(level) - { - print(' - ' + level + - ': name=' + this.name + - ' volume=' + this.getVolume() + - ' weight=' + this.getWeight()); - - if (!this.con_open) - return 'already_open!'; - - level++; - for (var i in this.con_items) { - /* - if (this.con_items[i] == undefined) - continue; - */ - print(level + - ': name=' + this.con_items[i].name + - ' volume=' + this.con_items[i].getVolume() + - ' weight=' + this.con_items[i].getWeight()); - if (this.con_items[i].isContainer()) - this.con_items[i].inventory(level); - } - level--; - } -}; - - - -/* - * Some testing in order - */ - -/* Create an item */ -var i = new Item('a ring', 'the one ring', 5, 5, CT_SOLID); - -/* Create a container item */ -var c = new ContainerItem('a bag', 'a soft cloth bag', 5, 5, CT_SOLID, - 10, 15, 20, CT_SOLID | CT_EXPAND); - -/* Create a second container item */ -var c2 = new ContainerItem('another bag', 'another soft cloth bag', 5, 5, - CT_SOLID, 10, 15, 20, CT_SOLID | CT_EXPAND); - -print(); -c2.inventory(0); -print(); - -c.open(); -print(c.add(i)); -c.close(); -c2.open(); -print(c2.add(c)); -c2.close(); - -c.open(); -c2.open(); -c2.inventory(0); -c.remove(i); -c2.remove(c); - -print(); -c2.inventory(0); diff --git a/tests/js-test/js/httpd/httpd.js b/tests/js-test/js/httpd/httpd.js deleted file mode 100644 index 0b24dac..0000000 --- a/tests/js-test/js/httpd/httpd.js +++ /dev/null @@ -1,1679 +0,0 @@ -/* $Id: httpd.js,v 1.74 2005/12/17 00:13:12 mmondor Exp $ */ - -/* - * Copyright (c) 2005, Matthew Mondor - * ALL RIGHTS RESERVED. - */ - -/* - * Little example showing the versatility of ECMAScript, provided with a - * custom library for BSD socket support, using SpiderMonkey. - * This tiny HTTPd allows simultaneous concurrent client connections without - * using multiple threads or processes. Must be ran through - * ../../src/js-server. - * - * Configuration options can be found in options.js - * - * XXX Possibly that with close var semantics changes or such, we could - * support Keep-Alive for HTTP/1.1 connections. This however is not a - * priority at current time. - * - * TODO: - * - There is a problem if the remote socket closes when we're sending - * alot of data, poll(2) apparently doesn't always save us from this - * despite checking POLLHUP/POLLERR events. A SIGPIPE signal is sent - * to the process, and we must be able to handle some common signals. - * This would also allow an opportunity for applications to register - * server cleanup handlers when SIGTERM is received, i.e. to save to - * disk in-memory cached data, etc. - * We probably should ensure to block the signal when executing the - * user function for an occurred signal... - * - Read http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html - * and see if we meet conformance, adjust as needed. - * - We might want to check Accept-Language: for multilingual sites... - * - See what to do for HEAD and PUT - * - Possibly limit rate of connections per address like I did in - * mmftpd/mmsmtpd/mmpop3d/mmspawnd, a requested feature of 3s4i. - * If enabling this, it probably should be per-vhost configurable. - * I'm not sure HTTP protocol is well suited to this type of limiting, - * some testing will be required. - * - Provide a function to scripts to redirect to another page. - * - We might want vhost-specific default login page, and provide - * an easy means in the server to have non-authenticated users automatically - * redirected to that page using server-side magic. A special session - * variable should be used for this, and registered when the user logins. - * - Similar to the above, but should be special provision for scripts to - * easily specify their required access level, which must be met by a - * currently logged in user, which is otherwise redirected to an - * application-specific page. - * - Implement logging - * - It might be nice to implement a minimal database-like facility, where - * for performance we could log changes, and only sync to disk once in a - * while, discarding obsolete logs... JSON would be used - * - Enhance the JS shell to report errors better - * - We should have support for easy storage and safe export of files, - * like I implemented for ascpi.com. This is useful for instance to - * store and post user profile images and related thumbnail, etc. - */ - - - -/* - * Server identification - */ -SERVER_VERSION = 'mmondor_js_httpd/0.0.1 (NetBSD)'; -SERVER_CVSID = '$Id: httpd.js,v 1.74 2005/12/17 00:13:12 mmondor Exp $'; - - - -/* - * Open standard error FD for diagnostics output - */ -err = new FD(); -err.set(FD.STDERR_FILENO); - - - -/* - * Import needed functionality from external modules, since #include is - * missing :) - */ -function file_read(file) -{ - var contents = ''; - var data; - var fd; - - try { - /* - * If we were provided with a filename, rather than a - * filedescriptor object, open the filename. Otherwise, - * read from the specified FD. - */ - if (typeof file != 'object') { - fd = new FD(); - fd.open(file, FD.O_RDONLY); - } else - fd = file; - } catch (x) { - err.put(x + " at file_read()\n"); - } - - try { - for (;;) { - data = fd.read(65536); - if (data.length == 0) - break; - contents += data; - } - } catch (x) { - err.put(x + " at file_read()\n"); - } - - fd.close(); - - return contents; -} - -try { - eval(file_read('options.js')); /* Configuration */ -} catch (x) { - err.put(x + " while reading options file\n"); - exit(); -} -eval(file_read('string.js')); /* startsWith()/endsWith() */ -eval(file_read('ml.js')); /* MLTag object for HTML generation */ -eval(file_read('root.js')); /* Root object for virtual chroot(2) */ - - - -/* - * Client states (enumeration) - */ -const STATE_TRANSFER_READ = 1; -const STATE_TRANSFER_WRITE = 2; - - - -/* - * Quick lookup object from virtual hosts names and aliases to VHost objects - */ -var vhosts_table = {}; -var default_vhost = undefined; - -/* - * The VHost object - */ -function VHost(o) -{ - /* - * A few properties are definitely required - */ - if (o.name == undefined || o.root == undefined) - throw ('name and root properties missing from vhost'); - o.name = o.name.toLowerCase(); - this.name = o.name; - - if (o.scripts != undefined && o.scripts == true) { - this.scripts = true; - this.globals = {}; - } - - /* - * Create VHost object - */ - try { - this.htdocs_root = new Root(o.root); - } catch (x) { - throw (x); - } - - if (o.charset != undefined) - this.charset = o.charset; - if (o.session_exp != undefined) - this.session_exp = o.session_exp; - - /* - * Link object to vhosts table - */ - if (vhosts_table[o.name] != undefined) - throw ('Conflicting vhost name: ' + o.name); - vhosts_table[o.name] = this; - - if (o.aliases != undefined) { - for (i in o.aliases) { - o.aliases[i] = o.aliases[i].toLowerCase(); - if (vhosts_table[o.aliases[i]] != undefined) - throw ('Conflicting vhost alias: ' + - o.aliases[i]); - vhosts_table[o.aliases[i]] = this; - } - } -} - - - -/* - * The Session object allows to maintain persistent session variables among - * multiple client queries. We are using a cookie with the unique session ID - * to link those queries together into a session. - */ - -var o = new FD(); -o.set(FD.STDOUT_FILENO); - -var session_randfd = new FD(); -try { - session_randfd.open('/dev/urandom', FD.O_RDONLY); -} catch (x) { - err.put(x + " while attempting to open /dev/urandom\n"); - exit(); -} - -var session_charlist = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' + - 'abcdefghijklmnopqrstuvwxyz' + - '0123456789-_'; - -var sessions_table = {}; - -/* - * To be called at regular but spaced intervals, - * destroys expired session objects. - */ -function session_gc(time) -{ - var i; - - for (i in sessions_table) { - if (sessions_table[i].expires <= time) - delete sessions_table[i]; - } -} - -function Session(time, exp) -{ - var rval; - var i; - - try { - rval = session_randfd.read(options.sess_id_size); - } catch (x) { - err.put(x + " while attempting to read from /dev/urandom\n"); - } - this.sessid = ''; - for (i = 0; i < options.sess_id_size; i++) - this.sessid += - session_charlist.charAt(rval.charCodeAt(i) & 0x3f); - - this.variables = {}; - this.expires = time + exp; - sessions_table[this.sessid] = this; -} - - - -/* - * For the mime types database - */ - -var mimetypes_table = {}; - - - -/* - * And our pre-evaluated scripts cache - */ -var jso_cache = {}; - -function JSO(path, time, script) -{ - this.path = path; - this.time = time; - this.script = script; - - jso_cache[this.path] = this; -} - - - -/* - * The HTTPReply object allows to cache addHeader() and addContent() requests, - * and to eventually flush the whole reply to an arbitrary FD afterwards. - */ - -/* - * Constructor - */ -function HTTPReply(code, desc, type) -{ - this.code = code; - this.desc = desc; - this.headers = []; - this.contents = []; - this.type = type; - - /* - * Insert our standard headers. - */ - this.gmttime = (new Date()).toGMTString(); - this.headers.push('Date: ' + this.gmttime); - this.headers.push('Server: ' + SERVER_VERSION); - this.headers.push('Connection: close'); - this.headers.push('Accept-Ranges: bytes'); -} -/* - * HTTPReply prototype object - */ -HTTPReply.prototype = { - setType: function(type) - { - - this.type = type; - }, - - addHeader: function(data) - { - - this.headers.push(data); - }, - - addNoCacheHeaders: function() - { - - this.headers.push('Expires: Mon, 26 Jul 1997 05:00:00 GMT'); - this.headers.push('Last-Modified: ' + this.gmttime); - this.headers.push('Cache-Control: no-cache, must-revalidate'); - this.headers.push('Pragma: no-cache'); - }, - - addContent: function(data) - { - - this.contents.push(data); - }, - - flush: function(fd, size) - { - var headers = ''; - var contents = ''; - var i; - - for (i = 0; i < this.contents.length; i++) - contents += this.contents[i]; - - this.headers.push('Content-Length: ' + - (size == null ? contents.length : size)); - headers += 'HTTP/1.1 ' + this.code + ' ' + this.desc + "\r\n"; - for (i = 0; i < this.headers.length; i++) - headers += this.headers[i] + "\r\n"; - if (this.type != null) - headers += 'Content-Type: ' + this.type + "\r\n"; - headers += "\r\n"; - - try { - if (!fd.http_old_get) - fd.bwrite(headers + contents); - else - fd.bwrite(contents); - } catch (x) {} - } -} - -/* - * Generates an error page and sends it to specified FD object. - */ -function http_error(fd, code, desc, ldesc) -{ - var res = new HTTPReply(code, desc, 'text/html; charset=' + - options.default_charset); - res.addNoCacheHeaders(); - - res.addContent('' + code + ' ' + desc + - '

' + code + ' ' + desc + '

' + - '

' + ldesc + '


'); - - res.addContent(fd.httpDebug()); - - res.addContent('
' + SERVER_VERSION + '
' + SERVER_CVSID + - '
'); - - res.flush(fd, null); - delete res; -} - - - -/* - * Add new methods to the FD prototype object (JavaScript is great like that). - * We need to add these properties one by one to the prototype object of FD, - * since we wouldn't want to override its default prototype, just expand it. - */ - -/* - * Handles request query processing, to be assigned to FD.process() while in - * the request query state. This then invokes parseRequest() which may - * either send an HTTP response and/or cause a switch to another state. - */ -function process_query(time) -{ - var len; - var done = false; - var close = false; - var idx; - var data; - - /* - * If we reach a request of 32768 bytes, stop reading. - * If there is no request terminating line within - * that buffer, close connection. - * - * On read error, close connection. - * - * If we do find request terminating line, parse query - * to modify state accordingly, and let the parsing - * method decide if we'll close the connection. - */ - len = 32768 - this.request_data.length; - if (len < 1) { - done = true; - close = true; - } else { - try { - data = this.read(len); - if (data.length == 0) - close = true; - this.request_data += data; - this.updateTimeout(time); - } catch (x) { - if (this.errno != Errno.EAGAIN) - close = true; - } - } - if ((idx = this.request_data.indexOf("\r\n\r\n")) != -1) - done = true; - else if (!this.checked_old && - (idx = this.request_data.indexOf("\r\n")) != -1) { - var w; - - this.checked_old = true; - if ((w = (this.request_data.substr(0, idx)).split(' ')). - length == 2 && w[0] == 'GET') { - /* - * Old-style HTTP request, we need to respond - * immediately in this case without any headers. - */ - done = true; - } - } - - if (done && !close) { - /* - * Store remaining of buffer after "\r\n" so that we may - * be able to process POST for instance, without needing - * to read one char at a time here. - * Then call our request parsing function. - */ - idx += 4; - this.bread_buffer = this.request_data.substr(idx); - this.request_data = this.request_data.substr(0, idx); - close = this.parseRequest(time); - } - - return close; -} - -function process_post(time) -{ - var close = false; - var len; - var data; - - if (this.post_data.length < this.http_content_length) { - len = this.http_content_length - this.post_data.length; - if (len > options.readbuf_size) - len = options.readbuf_size; - try { - data = this.read(len); - if (data.length == 0) - close = true; - this.post_data += data; - this.updateTimeout(time); - } catch (x) { - if (this.errno != Errno.EAGAIN) - close = true; - } - } else - close = this.parsePost(time); - - return close; -} - -/* - * Handles transfer processing, to be assigned to FD.process() while in the - * transfer state. - */ -function process_transfer(time) -{ - var close = false; - - /* - * STATE_TRANSFER_READ specifies that we're reading to the - * buffer from transfer_src, or writing from the buffer - * to transfer_dst (STATE_TRANSFER_WRITE). We can do - * both steps one after another if possible. - * On EAGAIN errors, simply pass our turn to go back to - * polling. On EOF reading from either end, exit transfer - * state after writing to the other and syncing/closing, - * and order to close client descriptor. - * We take care not to transfer more than this.transfer_size bytes - * outbound. - * XXX We might also want to observe it for inbound, where it could - * be set to the user-provided Content-Length... for PUT ? - */ - if (this.transfer_state == STATE_TRANSFER_READ) { - if (this == this.transfer_src) { - /* Reading from client */ - try { - this.transfer_data = - this.read(options.readbuf_size); - if (this.transfer_data.length == 0) - this.transfer_eof = true; - this.transfer_state = STATE_TRANSFER_WRITE; - this.updateTimeout(time); - } catch (x) { - if (this.errno != Errno.EAGAIN) { - close = true; - try { - this.transfer_dst.fdatasync(); - } catch (x) {} - try { - this.transfer_dst.close(); - } catch (x) {} - } - } - } else { - /* Reading from file */ - var bufsiz = this.transfer_size; - if (bufsiz > options.readbuf_size) - bufsiz = options.readbuf_size; - try { - if (bufsiz > 0) - this.transfer_data = - this.transfer_src.read(bufsiz); - else - this.transfer_data = ''; - if (this.transfer_data.length == 0) - this.transfer_eof = true; - this.transfer_size -= - this.transfer_data.length; - this.transfer_state = STATE_TRANSFER_WRITE; - } catch (x) { - close = true; - try { - this.transfer_src.close(); - } catch (x) {} - } - } - } - if (this.transfer_state == STATE_TRANSFER_WRITE) { - if (this == this.transfer_dst) { - /* Writing to client */ - if (this.transfer_eof) - close = true; - else { - try { - this.bwrite(this.transfer_data); - this.transfer_state = - STATE_TRANSFER_READ; - this.updateTimeout(time); - } catch (x) { - if (this.errno != Errno.EAGAIN) - close = true; - } - } - if (close) { - try { - this.transfer_src.close(); - } catch (x) {} - } - } else { - /* Writing to file */ - if (this.transfer_eof) - close = true; - else { - try { - this.transfer_dst.write( - this.transfer_data); - this.transfer_state = - STAT_TRANSFER_READ; - } catch (x) { - close = true; - } - } - if (close) { - try { - this.transfer_dst.fdatasync(); - } catch (x) {} - try { - this.transfer_dst.close(); - } catch (x) {} - } - } - } - - return close; -} - -/* - * Updates input timeout expiration time for this FD. - * To be passed current time in seconds since epoch. - */ -FD.prototype.updateTimeout = function(time) -{ - /* Update input timeout expiration time */ - this.expires = time + options.io_timeout; -} - -/* - * Reset FD to a consistent known state, to use after accept(2). - * To be passed current time in seconds since epoch, and unique id to be used - * as an index. - */ -FD.prototype.init = function(time, idx) -{ - /* Not a bound socket */ - this.bound = false; - /* Initial state */ - this.transfer_state = STATE_TRANSFER_READ; - this.transfer_src = this.transfer_dst = null; - this.transfer_eof = false; - this.transfer_data = ''; - /* Empty request string */ - this.request_data = ''; - /* Unique index */ - this.index = idx; - /* - * Events interested in. FD.POLLOUT to be eventually used instead in - * transfer state if transfering from server to client. - */ - this.events = FD.POLLIN; - /* - * Default handler to process this FD, the request state one. - * To be changed to the transfer one as needed for transfer state. - */ - this.process = process_query; - /* Initial input timeout */ - this.updateTimeout(time); - /* For process_request()/process_post() */ - this.bread_buffer = this.bwrite_buffer = ''; - /* - * Flag used to speed up request parsing related to old HTTP requests - */ - this.checked_old = false; - - /* - * Note that fcntl(2) and setsockopt(2) flags applied to the bound - * descriptors are inherited to their children sockets at accept(2). - * We therefore can save a few syscalls here. - * We are non-blocking already, for instance. - */ -} - -/* - * To be called once our client request has been read, to decide what action - * to perform and modify state accordingly. - */ -FD.prototype.parseRequest = function(time) -{ - var close = valid = false; - var evil_browser = evil_os = false; - var vhost = ''; - var lines; - var words; - var i; - var sessid, sess; - - this.http_protocol = ''; - this.http_method = ''; - this.http_vhost = undefined; - this.http_path = ''; - this.http_vars_get = {}; - this.http_vars_post = {}; - this.http_vars_cookies = {}; - this.http_agent = ''; - this.http_content_length = -1; - this.http_modified_since = undefined; - this.http_sessid = undefined; - this.http_vars_session = {}; - this.http_range = undefined; - this.http_old_get = false; - - /* Split request lines */ - lines = this.request_data.split("\r\n"); - - /* Verify if first line has a request which seems valid */ - if (lines.length > 0) { - words = lines[0].split(' '); - if (words.length == 2 && words[0] == 'GET') { - this.http_method = words[0]; - this.http_path = words[1]; - this.http_protocol = 'HTTP/1.1'; - this.http_old_get = true; - valid = true; - } else if (words.length == 3) { - if ((words[0] == 'GET' || words[0] == 'POST' || - words[0] == 'PUT') && (words[2] == 'HTTP/1.0' || - words[2] == 'HTTP/1.1')) { - this.http_method = words[0]; - this.http_path = words[1]; - this.http_protocol = words[2]; - valid = true; - } - } - } - - /* - * Parse header for interesting lines. - */ - if (valid && !this.http_old_get) { - for (i in lines) { - words = lines[i].split(' '); - if (words[0] == 'Host:' && words.length == 2) { - var i2; - - if ((i2 = words[1].indexOf(':')) != -1) - words[1] = words[1].substr(0, i2); - vhost = words[1]; - } else if (words[0] == 'Cookie:') { - words = (lines[i].substr(8)).split('='); - if (words.length == 2) - property_add(this.http_vars_cookies, - words[0], words[1]); - } else if (words[0] == 'User-Agent:') { - this.http_agent = lines[i].substr(12); - if (options.ban_msie == true && - this.http_agent.indexOf('MSIE') != -1) - evil_browser = true; - if (options.ban_windows == true && - this.http_agent.indexOf('Windows') != -1) - evil_os = true; - } else if (words[0] == 'Content-Length:' && - words.length == 2) - this.http_content_length = words[1].valueOf(); - else if (words[0] == 'If-Modified-Since:') - this.http_modified_since = Math.round( - Date.parse(lines[i].substr(19)) / 1000); - else if (words[0] == 'Range:') - this.http_range = lines[i].substr(7); - } - } - - /* - * If no Host: line set the vhost, set the default one. - * If there is a vhost set, but is unknown, also set the default one. - * Also set the name of the vhost to the actual vhost name despite - * it possibly being resolved through an alias. - */ - if (vhost == '' || vhosts_table[vhost] == undefined) - vhost = options.default_vhost; - this.http_vhost = vhosts_table[vhost.toLowerCase()]; - - if (this.http_old_get && this.http_vhost.scripts) { - /* - * We only allow old style GET request on static vhosts - */ - this.bwrite('' + - 'Unsupported Old-Style Request' + - '

Unsupported Old-Style Request

' + - 'Your browser sent an old-style HTTP request, which ' + - 'this server does not support on dynamic content ' + - 'virtual hosts. Use an HTTP/1.0 or HTTP/1.1 compliant ' + - 'client to continue.

' + "\r\n"); - return true; - } - - /* - * Filter out definitely invalid requests - */ - if (!valid) { - http_error(this, 400, 'Bad Request', - 'Your browser sent an invalid HTTP query.'); - return true; - } - - /* - * Block out evil Microsoft products. Start reporting the OS - * so that an unwanted windows user doesn't install another - * browser to then realize that his OS is banned nevertheless :) - */ - if (evil_os) { - http_error(this, 666, 'Evil Operating System Banished!', - 'Your Operating System is evil born.
At least ' + - 'upgrade to a ' + - 'decent OS to survive on these grounds.
'); - return true; - } else if (evil_browser) { - http_error(this, 666, 'Evil Browser Banished!', - 'Your browser is evil born.
At least ' + - 'upgrade to a ' + - 'decent browser to survive on these grounds.
'); - return true; - } - - /* - * Verify if client sent a session cookie, and if it consists of a - * valid one, load session data. - */ - if (this.http_vhost.scripts && - (sessid = this.http_vars_cookies['SessionID']) != undefined && - (sess = sessions_table[sessid]) != undefined && - sess.expires > time) { - this.http_sessid = sessid; - this.http_vars_session = sess.variables; - } - - /* - * Fill in associative array with any GET-style supplied - * variables (as part of the URL). We want this even for POST method. - */ - if ((i = this.http_path.lastIndexOf('?')) != -1) { - var v; - var t; - - v = this.http_path.substr(i + 1); - /* - * Note: substring() and substr() are semantically different - */ - this.http_path = this.http_path.substring(0, i); - words = v.split('&'); - for (i in words) { - t = words[i].split('='); - if (t.length == 2) - property_add(this.http_vars_get, t[0], t[1]); - } - } - - /* - * Switch to POST parsing mode if needed. - * This will then call this.parsePost() rather than this function, - * which will also invoke this.httpRespond(). - */ - if (this.http_method == 'POST' && this.http_content_length != -1) { - /* - * First obtain buffer stored by process_query() for us, - * if any. If the full post_data was found in it, simply - * call the parsing function immediately, which will return - * with close status after invking httpRespond(). - */ - this.post_data = ''; - if (this.bread_buffer.length > 0) { - this.post_data += this.bread_buffer; - this.bread_buffer = ''; - } - if (this.post_data.length < this.http_content_length) { - /* Go back to polling, activating process_post */ - this.post_data = ''; - this.process = process_post; - return false; - } else - return this.parsePost(time); - } - - if (!close) - close = this.httpRespond(time); - - return close; -} - -/* - * After reading post data, allows to parse it down into variables - */ -FD.prototype.parsePost = function(time) -{ - var words; - var i; - var t; - - /* - * We need to strip "\n", "\r" and "\r\n" which may be sent by broken - * clients. - */ - this.post_data = this.post_data.replace(/\n/g, ''); - this.post_data = this.post_data.replace(/\r/g, ''); - - words = this.post_data.split('&'); - for (i in words) { - t = words[i].split('='); - if (t.length == 2) - property_add(this.http_vars_post, t[0], t[1]); - } - - delete this.post_data; - - return this.httpRespond(time); -} - -/* - * Finally respond to client request - */ -FD.prototype.httpRespond = function(time) -{ - var path, fd, st, res, ext, mimetype, i, sess, size; - - /* - * Verify if requested path is valid - */ - path = this.http_vhost.htdocs_root.valid_virtual(this.http_path); - if (!path) { - http_error(this, 403, 'Permission Denied', - 'You do not have the permission to access this ' + - 'resource.'); - return true; - } - - /* - * We now want to verify if the client sent a valid session cookie. - * If it didn't, we present it with a page with meta-tag reload - * instructions to attempt to reload the originally supplied URL, - * and with a new session cookie. - */ - if (this.http_vhost.scripts == true && this.http_sessid == undefined) { - var doc, sess; - - doc = new HTTPReply(200, 'OK', - 'text/html; charset=' + options.default_charset); - doc.addNoCacheHeaders(); - - sess = new Session(time, - (this.http_vhost.session_exp != undefined ? - this.http_vhost.session_exp : - options.default_session_exp)); - sess.variables.count = 0; - doc.addHeader('Set-Cookie: SessionID=' + sess.sessid + - '; expires=' + - (new Date(sess.expires * 1000)).toGMTString() + - '; path=/'); - doc.addContent('Cookies' + - '

Cookies

We are ' + - ' verifying if your browser supports HTTP cookies. ' + - 'These are required to keep persistent session data ' + - 'among your connections. The destination page should ' + - 'load automatically in a few seconds.

' + - '

If it doesn\'t, please ensure that HTTP cookies ' + - 'are enabled on your HTTP client and are accepted ' + - 'from this server.

' + - '

If the page still does not load properly after a ' + - 'few seconds, please click on this link.


' + SERVER_VERSION + - '
' + SERVER_CVSID + '
'); - doc.flush(this, null); - - return true; - } - if (this.http_sessid != undefined) - this.http_vars_session.count++; - - - /* - * Attempt to find requested file. - * If it consists of a directory, attempt to first find index.html in - * it, then index.jso in it, fail with error 403 if none can be found. - * Otherwise, continue forward keeping the descriptor open, and the - * stat information already filled in. - */ - fd = new FD(); - - try { - fd.open(path.real, FD.O_RDONLY); - st = fd.fstat(); - } catch (x) { - http_error(this, 404, 'Not Found', - path.virtual + ' could not be found.'); - return true; - } - - if ((st.st_mode & FD.S_IFDIR) != 0) { - var error = false; - - fd.close(); - try { - fd.open(path.real + '/index.html', FD.O_RDONLY); - st = fd.fstat(); - path.real += '/index.html'; - path.virtual += '/index.html'; - } catch (x) { - try { - fd.open(path.real + '/index.jso', - FD.O_RDONLY); - st = fd.fstat(); - path.real += '/index.jso'; - path.virtual += '/index.jso'; - } catch (x) { - error = true; - } - } - if (error) { - http_error(this, 403, 'Permission Denied', - 'You do not have the permission to access the ' + - 'resource ' + path.virtual); - return true; - } - } - - /* - * Only continue if file is a regular file - */ - if ((st.st_mode & FD.S_IFREG) == 0) { - fd.close(); - http_error(this, 403, 'Permission Denied', - 'You do not have the permission to access the resource ' + - path.virtual); - return true; - } - - /* - * Extract file extension, as well as its mime type. - */ - if ((i = path.virtual.lastIndexOf('.')) != -1) { - ext = (path.virtual.substr(i + 1)).toLowerCase(); - if (ext.lastIndexOf('/') == -1 && - mimetypes_table[ext] != undefined) - mimetype = mimetypes_table[ext]; - else - mimetype = options.default_mimetype; - } - mimetype += '; charset=' + (this.http_vhost.charset == undefined ? - options.default_charset : this.http_vhost.charset); - - /* - * JavaScript Object (JSO) files are processed especially. - * We interpret them, providing them with an environment to - * access the needed resources (get/post/cookie/session variables, - * as well as the document object they can use). - * We flush the document once the script returns, or fire up - * an error if something fails. - */ - if (ext == 'jso') { - - /* Create document object */ - var obj = new HTTPReply(200, 'OK', mimetype); - obj.addNoCacheHeaders(); - - /* XXX Add other objects to export as necessary */ - obj.get = this.http_vars_get; - obj.post = this.http_vars_post; - obj.cookie = this.http_vars_cookies; - obj.session = this.http_vars_session; - obj.global = this.http_vhost.globals; - - /* - * Check if object in our cache already and file not modified - * since cached entry. Reuse function then. Otherwise, - * load in function from file, store a cache entry for it - * and launch it. - */ - - var data, s, o; - - if (((o = jso_cache[path.real]) != undefined) && - o.time == st.st_mtime) { - obj.script = o.script; - fd.close(); - } else { - try { - /* file_read() closes fd for us */ - data = file_read(fd); - s = 'obj.script = function() {' + data + '}'; - eval(s); - o = new JSO(path.real, st.st_mtime, - obj.script); - } catch (x) { - err.put(x + ' evaluating script ' + path.real - + "\n"); - http_error(this, 500, 'Internal Server Error', - 'Please try again later.'); - return true; - } - } - - try { - if (obj.script != undefined) - obj.script(); - obj.flush(this, null); - } catch (x) { - err.put(x + ' executing script ' + path.real + "\n"); - http_error(this, 500, 'Internal Server Error', - 'Please try again later.'); - } - - delete obj; - return true; - } - - /* - * If client only wanted the document if it wasn't modified since - * a certain time, report that it wasn't if it is the case. - */ - if (this.http_modified_since != undefined && - st.st_mtime <= this.http_modified_since) { - fd.close(); - res = new HTTPReply(304, 'Not Modified', null); - res.flush(this, null); - return true; - } - - /* - * If client only requested a range of bytes of the file, verify if - * the range is valid, and if so, arrange to only transfer the - * requested part of the file. - * In any other case, simply transfer the whole file. - */ - if (this.http_range != undefined) { - var w; - - if (this.http_range.startsWith('bytes')) - this.http_range = this.http_range.substr(5); - if (this.http_range.length > 0 && - this.http_range.charAt(0) == '=') - this.http_range = this.http_range.substr(1); - if ((w = this.http_range.split('-')).length == 2) { - var from, to; - - if (w[0] == '') - w[0] = 0; - if (w[1] == '') - w[1] = st.st_size - 1; - from = Number(w[0]); - to = Number(w[1]); - - if (from >= 0 && to < st.st_size && to >= from) { - res = new HTTPReply(206, 'Partial Content', - mimetype); - res.addHeader('Content-Range: bytes ' + - from + '-' + to + '/' + st.st_size); - this.transfer_size = Math.abs((to - from) + 1); - if (from > 0) { - try { - fd.lseek(from, FD.SEEK_SET); - } catch(x) { - err.put(x + " at lseek()\n"); - } - } - } - } - } - if (res == undefined) { - res = new HTTPReply(200, 'OK', mimetype); - this.transfer_size = Math.abs(st.st_size); - } - - /* - * Flush HTTP header, sending it - */ - res.flush(this, this.transfer_size); - - /* - * Switch to outbound transfer mode. - */ - this.transfer_src = fd; - this.transfer_dst = this; - this.process = process_transfer; - this.events = FD.POLLOUT; - - /* - * Return with close=false, to delegate operations to - * process_transfer(). - */ - return false; -} - -FD.prototype.httpDebug = function() -{ - table = new MLTag('table', true); - table.addAttr('width', '100%'); - table.addAttr('border', '1'); - - var tr = new MLTag('tr', true); - var td = new MLTag('td', true); - td.addAttr('width', '10%'); - td.addAttr('align', 'right'); - td.addContent('You are:'); - tr.addContent(td); - td = new MLTag('td', true); - td.addAttr('width', '90%'); - td.addContent(this.client_addr + ':' + this.client_port); - tr.addContent(td); - table.addContent(tr); - - tr = new MLTag('tr', true); - td = new MLTag('td', true); - td.addAttr('align', 'right'); - td.addContent('You sent:'); - tr.addContent(td); - td = new MLTag('td', true); - var font = new MLTag('font', true); - font.addAttr('size', '-3'); - pre = new MLTag('pre', true); - pre.addContent(this.request_data); - font.addContent(pre); - td.addContent(font); - tr.addContent(td); - table.addContent(tr); - - tr = new MLTag('tr', true); - td = new MLTag('td', true); - td.addAttr('align', 'right'); - td.addContent('GET vars:'); - tr.addContent(td); - td = new MLTag('td', true); - var font = new MLTag('font', true); - font.addAttr('size', '-3'); - pre = new MLTag('pre', true); - pre.addContent(uneval(this.http_vars_get)); - font.addContent(pre); - td.addContent(font); - tr.addContent(td); - table.addContent(tr); - - tr = new MLTag('tr', true); - td = new MLTag('td', true); - td.addAttr('align', 'right'); - td.addContent('POST vars:'); - tr.addContent(td); - td = new MLTag('td', true); - var font = new MLTag('font', true); - font.addAttr('size', '-3'); - pre = new MLTag('pre', true); - pre.addContent(uneval(this.http_vars_post)); - font.addContent(pre); - td.addContent(font); - tr.addContent(td); - table.addContent(tr); - - tr = new MLTag('tr', true); - td = new MLTag('td', true); - td.addAttr('align', 'right'); - td.addContent('Cookies:'); - tr.addContent(td); - td = new MLTag('td', true); - var font = new MLTag('font', true); - font.addAttr('size', '-3'); - pre = new MLTag('pre', true); - pre.addContent(uneval(this.http_vars_cookies)); - font.addContent(pre); - td.addContent(font); - tr.addContent(td); - table.addContent(tr); - - tr = new MLTag('tr', true); - td = new MLTag('td', true); - td.addAttr('align', 'right'); - td.addContent('VHost:'); - tr.addContent(td); - td = new MLTag('td', true); - td.addContent(this.http_vhost.name); - tr.addContent(td); - table.addContent(tr); - - tr = new MLTag('tr', true); - td = new MLTag('td', true); - td.addAttr('align', 'right'); - td.addContent('Path:'); - tr.addContent(td); - td = new MLTag('td', true); - td.addContent(this.http_path); - tr.addContent(td); - table.addContent(tr); - - tr = new MLTag('tr', true); - td = new MLTag('td', true); - td.addAttr('align', 'right'); - td.addContent('Mod-Since:'); - tr.addContent(td); - td = new MLTag('td', true); - td.addContent(this.http_modified_since); - tr.addContent(td); - table.addContent(tr); - - tr = new MLTag('tr', true); - td = new MLTag('td', true); - td.addAttr('align', 'right'); - td.addContent('SessID:'); - tr.addContent(td); - td = new MLTag('td', true); - td.addContent(this.http_sessid); - tr.addContent(td); - table.addContent(tr); - - tr = new MLTag('tr', true); - td = new MLTag('td', true); - td.addAttr('align', 'right'); - td.addContent('Sess vars:'); - tr.addContent(td); - td = new MLTag('td', true); - var font = new MLTag('font', true); - font.addAttr('size', '-3'); - pre = new MLTag('pre', true); - pre.addContent(uneval(this.http_vars_session)); - font.addContent(pre); - td.addContent(font); - tr.addContent(td); - table.addContent(tr); - - return table.toStr(0); -} - -/* - * Since we are using nonblocking mode, it is possible for write(2) to return - * a short count, in which case we must be able to resume writing the - * remaining buffer after poll(2). There are currently two write paths, - * HTTPResponse.flush() and process_transfer(). Both can use this function - * instead of write(2). The main poll(2) based loop can then handle buffer - * flushing. However, we first attempt to immediately write as much as we can - * before queuing what needs to be written again. - * Like write(2), this function can throw an exception on error, including - * for EAGAIN. - */ -FD.prototype.bwrite = function(data) -{ - var size; - - if ((size = this.write(data)) == data.length) - return data.length; - - this.bwrite_buffer += data.substr(size); - return size; -} - -/* - * Called by the main loop to know if there exists queued data, and to write - * it out if needed. Returns an object with two properties, done which if - * true tells the caller that there is no more data to flush, and close - * which if true means that an error occurred in which case connection should - * be closed. - */ -FD.prototype.bwrite_flush = function() -{ - var size; - var obj = new Object(); - - obj.done = obj.close = false; - - if (this.bwrite_buffer.length == 0) { - obj.done = true; - return obj; - } - - b = true; - try { - size = this.write(this.bwrite_buffer); - this.bwrite_buffer = this.bwrite_buffer.substr(size); - if (this.bwrite_buffer.length == 0) - obj.done = true; - } catch (x) { - if (this.errno != Errno.EAGAIN) - obj.close = true; - } - - return obj; -} - -/* - * Verifies if property name ends with [], which considers it as an array of - * values which are then pushed into that array. - * Sets the property normally otherwise. - */ -function property_add(obj, prop, val) -{ - prop = unescape(prop.replace(/\+/g, ' ')); - val = unescape(val.replace(/\+/g, ' ')); - - if (prop.endsWith('[]')) { - /* Array entry */ - prop = prop.substring(0, prop.length - 2); - if (obj[prop] == undefined) - obj[prop] = []; - obj[prop].push(val); - } else - obj[prop] = val; -} - - - -/* - * To know how many connections we are currently serving and limit them - */ -var counters_connections = 0; -var counters_addresses = []; - -function counters_inc(fd) -{ - - if (counters_connections >= options.max_connections) - return false; - - var addr = fd.client_addr; - if ((addrcnt = counters_addresses[addr]) != undefined && - addrcnt >= options.max_connections_addr) - return false; - - if (addrcnt == undefined) - addrcnt = 1; - else - addrcnt++; - counters_addresses[addr] = addrcnt; - - return true; -} - -function counters_dec(fd) -{ - var addr = fd.client_addr; - if ((--counters_addresses[addr]) == 0) - delete counters_addresses[addr]; - - counters_connections--; -} - - - -/* - * Main program - */ -function main() { - var i; - var sess_gc_secs = 0; - - /* - * Populate vhosts database - */ - for (i in vhosts) { - try { - var v = new VHost(vhosts[i]); - } catch (x) { - err.put(x + " creating VHost object\n"); - } - } - /* - * Attempt to link default_vhost to a VHost object - */ - if (options.default_vhost == undefined) { - err.put("No default_vhost property in options, exiting\n"); - exit(); - } - if (vhosts_table[options.default_vhost.toLowerCase()] != undefined) - default_vhost = - vhosts_table[options.default_vhost.toLowerCase()]; - else { - err.put('Default vhost ' + options.default_vhost + - " unkonwn, exiting\n"); - exit(); - } - - /* - * Populate mimetypes database. - * XXX If this got very large, because the destination strings - * are rather large, it might be good to use indexes or references - * to them rather than provide the string for each extension. - */ - for (i in mimetypes) { - var i2, ext; - - for (i2 in mimetypes[i]) { - ext = mimetypes[i][i2].toLowerCase(); - if (mimetypes_table[ext] != undefined) { - err.put('Conflicting mime type ' + - ext + ' -> ' + i + "\n"); - continue; - } - mimetypes_table[ext] = i; - } - } - if (mimetypes_table['jso'] == undefined) - mimetypes_table['jso'] = 'text/html'; - - /* - * Create polling array set - */ - var set = []; - - /* - * Initialize server - */ - for (i in listen) { - var fd; - - try { - fd = new FD(); - fd.socket(FD.AF_INET, FD.SOCK_STREAM, 0); - fd.bind(listen[i].address, listen[i].port); - - fd.setsockopt(FD.SO_REUSEADDR, 1); - fd.setsockopt(FD.SO_LINGER, -1); - fd.setsockopt(FD.SO_KEEPALIVE, 1); - fd.setsockopt(FD.TCP_NODELAY, 1); - - fd.fcntl(FD.F_SETFL, FD.O_NONBLOCK); - fd.listen(options.max_connections); - /* - * Mark socket as bound one and add it to - * polling set - */ - fd.events = FD.POLLIN; - fd.bound = true; - set.push(fd); - } catch (x) { - err.put(x + " preparing listening socket\n"); - } - } - if (set.length == 0) - exit(); - - /* - * Used for unique index associated to each FD for efficient removal - * from polling set when closing connection - */ - var count = listen.length + 1; - - /* - * Main loop - */ - for (;;) { - /* - * Determine next to expire FD event, so that we can sleep - * as long as possible. Also get rid of expired requests. - */ - var cur = Date.parse(new Date) / 1000; - var exp = cur + 3600; - var old; - for (i in set) { - var fd; - - if ((fd = set[i]) == undefined || fd.bound == true) - continue; - if (fd.expires <= cur) { - /* - * Request timeout expired for this - * descriptor, we must close it. - */ - fd.close(); - counters_dec(fd); - delete set[fd.index]; - delete fd; - continue; - } - if (fd.expires < exp) - exp = fd.expires; - } - exp -= cur; - - /* - * Poll our set of descriptors for events - */ - var e; - try { - e = FD.poll(set, exp * 1000); - } catch (x) { - err.put(x + " for poll(2)\n"); - continue; - } - - /* - * Verify if a timeout occurred. Because our poll - * implementation returns an array, its timeout event can be - * checked against by verifying if the set is empty. - */ - if (e.length == 0) { - /* Timeout */ - continue; - } - - /* - * XXX We should perhaps check timeouts after, so that we - * only need to query time once per loop? - * We could only do this if we knew that all our next - * processing is non-blocking, however. Otherwise we - * would loose track of actual time. - */ - old = cur; - cur = (Date.parse(new Date) / 1000); - - /* - * Verify if we should call the session gc, and if so, do so. - */ - sess_gc_secs += cur - old; - if (sess_gc_secs >= options.sess_gc_interval) { - sess_gc_secs = 0; - session_gc(cur); - } - - /* - * Run through set of descriptors with pending events - */ - for (i in e) { - if (e[i] == undefined) - continue; - - /* - * If descriptor is a bound one, attempt to accept - * the new client connection - */ - if (e[i].bound == true && - (e[i].revents & FD.POLLIN) != 0) { - var fd; - - try { - fd = e[i].accept(); - if (!counters_inc(fd)) { - http_error(fd, 403.9, - 'Too Many Connections', - 'Your browser has exceeded its maximum ' + - 'allowed number of concurrent ' + - 'connections.'); - fd.close(); - delete fd; - } else { - /* - * Setup client's initial - * state and add FD to polling - * set - */ - if (++count > 999999) - count = listen.length - + 1; - fd.init(cur, count); - set[count] = fd; - } - } catch (x) { - err.put(x + " at accept(2)\n"); - } - continue; - } - - /* - * Not a new connection event - */ - var close = false; - - /* - * Close connection on error conditions, - * Call the FD's process function on interesting - * events, which will tell when we should drop the - * client. - */ - if ((e[i].revents & (FD.POLLHUP | FD.POLLERR)) != 0) - close = true; - else if ((e[i].revents & (FD.POLLIN | FD.POLLOUT)) - != 0) { - /* - * Flush output queue if any and possible, - * since we're in non-blocking mode writes - * can yield short counts. - */ - var o = e[i].bwrite_flush(); - - if (o.close == true) - close = true; - else if (o.done == true) - close = e[i].process(cur); - } - - if (close) { - try { - e[i].close(); - } catch (x) {} - counters_dec(e[i]); - delete set[e[i].index]; - delete e[i]; - } - } - } - - /* NOTREACHED */ - err.close(); -} - -main(); -/* NOTREACHED */ -exit(0); diff --git a/tests/js-test/js/httpd/ml_clean.js b/tests/js-test/js/httpd/ml_clean.js deleted file mode 100644 index e0283c6..0000000 --- a/tests/js-test/js/httpd/ml_clean.js +++ /dev/null @@ -1,192 +0,0 @@ -/* $Id: ml_clean.js,v 1.4 2005/07/12 13:09:07 mmondor Exp $ */ - -/* - * Copyright (c) 2004-2005, Matthew Mondor - * ALL RIGHTS RESERVED. - */ - - -/* - * Useful object to use for HTML tags. consists of tag name, and - * of a boolean specifying of the tag requires a corresponding closing - * tag (i.e.

[body]

. Attributes and body elements may be added after - * creating such an object, and toStr() used to obtain the actual HTML result. - */ -function MLTag(tag, close) -{ - /* - * MLTag object properties - */ - this.tag = tag; - this.close = close; - this.attributes = {}; /* Object with associative attributes */ - this.contents = []; /* Array object */ -} - -/* - * Define prototype of the MLTag object. - */ -MLTag.prototype = { - /* A string of spaces to be used for indenting */ - indent_str: ' ' + - ' ', - - /* - * Wraps specified string at 70 columns and observing specified - * indentation level - */ - wrap: function(str, level) - { - var len = str.length; - var newstr = this.indent_str.substr(0, level); - var newlen = level; - - /* - * Could find index to space and use substring as necessary - * instead of looping through every character. Of course, - * this could also all be implemented using C native code. - */ - for (var i = 0; i < len; i++) { - var c = str.charAt(i); - - if (c == ' ' && newlen > 70) { - newstr += "\n" + this.indent_str.substr(0, - level); - newlen = level; - continue; - } - newstr += c; - newlen++; - } - - return newstr; - }, - - /* - * Add specified attribute with optional associated value to this tag - */ - addAttr: function(attr, value) - { - - this.attributes[attr] = value; - }, - - /* - * Add arbitrary content in sequencial order to this tag's body - */ - addContent: function(item) - { - - this.contents.push(item); - }, - - /* - * Returns a string consisting of this tag along with its body and - * optional corresponding closing tag - */ - toStr: function(level) - { - var str = ''; - - /* Null tags may exist as containers */ - if (this.tag != null) { - - /* Open opening tag */ - str += this.indent_str.substr(0, level) + '<' + - this.tag; - - /* - * Store these immediately for performance since we - * use those values several times later on, also - * saves typing - */ - var taglen = this.tag.length; - var len = level + taglen + 1; - var attributes = this.attributes; - - /* - * Add each of our attributes with their optional - * values - */ - for (var attr in attributes) { - var value; - - if (len > 70) { - len = level + taglen + 1; - str += "\n" + - this.indent_str.substr(0, len); - } - str += ' ' + attr; - len += taglen + 1; - if ((value = attributes[attr]) != null) { - str += '="' + value + '"'; - len += value.length + 3; - } - } - - /* Close opening tag */ - str += ">\n"; - - /* Increase our indent level for content */ - level++; - - } - - /* - * Now add all our contents, if any, which may consist of - * other MLTag objects or arbitrary String objects. Use - * recursion to process MLTag ones, which may also have their - * respective bodies and closing tags. - */ - for (var i in this.contents) { - var a = this.contents[i]; - - if (typeof a == 'object') - str += a.toStr(level); - else - str += this.wrap(a, level) + "\n"; - } - - /* - * If this tag required closing, do so - */ - if (this.tag != null) { - level--; - if (this.close) - str += this.indent_str.substr(0, level) + - '\n"; - } - - return str; - } -} - - -var entitites_table = { - '<': '<', - '>': '>', - '&': '&', - '"': '"', - "`": '‘', - "'": '’' -}; - -/* - * Function to convert a supplied string to use HTML/SGML special entitites. - * This also allows HTML escaping from user-supplied strings. - */ -function toHTMLEntities(str) -{ - var s = ''; - var i, t, c, e; - - for (i = 0, t = str.length; i < t; i++) { - c = str.charAt(i); - if ((e = entitites_table[c]) != undefined) - s += e; - else - s += c; - } - - return s; -} diff --git a/tests/js-test/js/httpd/ml_machine.js b/tests/js-test/js/httpd/ml_machine.js deleted file mode 100644 index d9783e9..0000000 --- a/tests/js-test/js/httpd/ml_machine.js +++ /dev/null @@ -1,146 +0,0 @@ -/* $Id: ml_machine.js,v 1.4 2005/07/12 13:09:07 mmondor Exp $ */ - -/* - * Copyright (c) 2004-2005, Matthew Mondor - * ALL RIGHTS RESERVED. - */ - - -/* - * Useful object to use for HTML tags. consists of tag name, and - * of a boolean specifying of the tag requires a corresponding closing - * tag (i.e.

[body]

. Attributes and body elements may be added after - * creating such an object, and toStr() used to obtain the actual HTML result. - */ -function MLTag(tag, close) -{ - /* - * MLTag object properties - */ - this.tag = tag; - this.close = close; - this.attributes = {}; /* Object with associative attributes */ - this.contents = []; /* Array object */ -} - -/* - * Define prototype of the MLTag object. - */ -MLTag.prototype = { - - /* - * Add specified attribute with optional associated value to this tag - */ - addAttr: function(attr, value) - { - - this.attributes[attr] = value; - }, - - /* - * Add arbitrary content in sequencial order to this tag's body - */ - addContent: function(item) - { - - this.contents.push(item); - }, - - /* - * Returns a string consisting of this tag along with its body and - * optional corresponding closing tag - */ - toStr: function(level) - { - var str = ''; - - /* Null tags may exist as containers */ - if (this.tag != null) { - - /* Open opening tag */ - str += '<' + this.tag; - - /* - * Store these immediately for performance since we - * use those values several times later on, also - * saves typing - */ - var taglen = this.tag.length; - var len = taglen + 1; - var attributes = this.attributes; - - /* - * Add each of our attributes with their optional - * values - */ - for (var attr in attributes) { - var value; - - str += ' ' + attr; - len += taglen + 1; - if ((value = attributes[attr]) != null) { - str += '="' + value + '"'; - len += value.length + 3; - } - } - - /* Close opening tag */ - str += ">"; - - } - - /* - * Now add all our contents, if any, which may consist of - * other MLTag objects or arbitrary String objects. Use - * recursion to process MLTag ones, which may also have their - * respective bodies and closing tags. - */ - for (var i in this.contents) { - var a = this.contents[i]; - - if (typeof a == 'object') - str += a.toStr(0); - else - str += a; - } - - /* - * If this tag required closing, do so - */ - if (this.tag != null && this.close) - str += '"; - - return str; - } - -} - - -var entitites_table = { - '<': '<', - '>': '>', - '&': '&', - '"': '"', - "`": '‘', - "'": '’' -}; - -/* - * Function to convert a supplied string to use HTML/SGML special entitites. - * This also allows HTML escaping from user-supplied strings. - */ -function toHTMLEntities(str) -{ - var s = ''; - var i, t, c, e; - - for (i = 0, t = str.length; i < t; i++) { - c = str.charAt(i); - if ((e = entitites_table[c]) != undefined) - s += e; - else - s += c; - } - - return s; -} diff --git a/tests/js-test/js/httpd/options.js b/tests/js-test/js/httpd/options.js deleted file mode 100644 index b0e49c0..0000000 --- a/tests/js-test/js/httpd/options.js +++ /dev/null @@ -1,82 +0,0 @@ -/* $Id: options.js,v 1.17 2005/12/12 09:55:45 mmondor Exp $ */ - -var options = { - max_connections: 32, - max_connections_addr: 4, - io_timeout: 60, - readbuf_size: 65536, - default_vhost: "chat.pulsar-zone.net", - default_mimetype: "application/octet-stream", - default_charset: "us-ascii", - default_session_exp: 1800, - sess_gc_interval: 600, - sess_id_size: 64, - ban_msie: false, - ban_windows: false -}; - -/* Address:port combinations to listen to */ -var listen = [ - { - address: "127.0.0.1", - port: 8080 - }, - { - address: "192.168.1.12", - port: 8080 - } -]; - -var vhosts = [ - /* Default virtual host */ - { - name: "chat.pulsar-zone.net", - root: "/home/mmondor/jswww/chat", - scripts: true, - charset: 'iso-8859-1', - session_exp: 14400 - }, - - /* Dynamic application virtual host for ascpi.com */ - { - name: "ascpi.com", - aliases: [ "www.ascpi.com", "ascpi.hal.xisop", - "ascpi.hal" ], - root: "/home/mmondor/jswww/ascpi.com" - }, - - /* Static only test virtual host */ - { - name: "test.localhost", - root: "/home/mmondor/jswww/welcome" - } -]; - -var mimetypes = { - 'text/html': [ "html", "htm", "dhtml", - "jso" ], - 'text/plain': [ "txt" ], - 'text/css': [ "css" ], - 'application/x-xpinstall': [ "xpi" ], - 'application/vnd.mozilla.xul+xml': [ "xul" ], - 'text/rdf': [ "rdf" ], - 'application/pdf': [ "pdf" ], - 'application/postscript': [ "ps" ], - 'application/x-tar': [ "tar" ], - 'application/x-gzip': [ "gz" ], - 'application/x-bzip2': [ "bz2" ], - 'application/zip': [ "zip" ], - 'application/x-javascript': [ "js" ], - 'application/x-c': [ "c", "h", "cpp", "cc" ], - 'application/x-sh': [ "sh" ], - 'application/x-shockwave-flash': [ "swf" ], - 'application/xml': [ "xml" ], - 'application/xml-dtd': [ "dtd" ], - 'image/jpg': [ "jpg", "jpeg" ], - 'image/png': [ "png" ], - 'image/gif': [ "gif" ], - 'image/x-icon': [ "ico" ], - 'video/mpeg': [ "mpeg", "mpg" ], - 'video/quicktime': [ "mov" ], - 'video/x-msvideo': [ "asf", "asx", "wmv", "avi" ] -}; diff --git a/tests/js-test/js/httpd/root.js b/tests/js-test/js/httpd/root.js deleted file mode 100644 index c46fd2b..0000000 --- a/tests/js-test/js/httpd/root.js +++ /dev/null @@ -1,197 +0,0 @@ -/* $Id: root.js,v 1.1 2005/07/07 00:11:43 mmondor Exp $ */ - -/* - * Copyright (c) 2005, Matthew Mondor - * ALL RIGHTS RESERVED. - */ - -/* - * Implementation of a virtual chroot(2) system. - */ - - - -const PATH_MAX = 255; - - - -function Root(root) -{ - - if (!(this.root = this.valid_path(root))) - throw('Invalid root path "' + root + '"!'); - this.cwd = '/'; -} - -Root.prototype = { - - /* - * Quick lookup table of valid characters within pathnames. - * Currently 'a'-'z', 'A'-'Z', '0'-'9', '.', '/', '-', '_' - */ - valid_char_table: [ - 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, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, - 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 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, 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, 0, 0, 0, 0, 0, 0, 0 - ], - - /* - * Base function to return formatted valid path, or false. - * Returned path will always begin with '/' and not have any - * trailing '/'. Multiple '/' are also collapsed into one. - */ - valid_path: function(path) - { - var i, t, l, p, c, code; - - p = l = '/'; - for (i = 0, t = path.length; i < t; i++) { - c = path.charAt(i); - if (l == '/') { - /* Collapse multiple '/' */ - if (c == '/') - continue; - /* Prohibit '.' at start of element */ - if (c == '.') - return false; - } - /* Validate chars */ - code = c.charCodeAt(0); - if (code != code & 0xff || - this.valid_char_table[code] == 0) - return false; - p += c; - l = c; - } - /* Strip last '/' if any, unless only one */ - if (c == '/' && p.length > 1) - p = p.substr(0, p.length - 1); - - /* If exceeding PATH_MAX, return false */ - if (p.length > PATH_MAX) - return false; - - return p; - }, - - /* - * Returns version of provided path which points to the parent - * directory if possible. Returns false otherwise. - * Should only be used with paths first copied using valid_path(). - * Can typically be used with the cwd. - */ - parent: function(path) - { - var i; - - /* First make sure that path starts with '/' */ - if (path.length < 2 || path.charAt(0) != '/') - return false; - - /* Strip trailing '/' chars */ - for (i = path.length - 1; i >= 0 && path.charAt(i) == '/'; - i--) ; - if (i <= path.length) - path = path.substr(0, i + 1); - - /* - * Locate previous '/', strip everything after it, including - * it, except in the case where it is the only remaining, - * where path must remain '/'. - */ - if ((i = path.lastIndexOf('/')) == 0) - i = 1; - return path.substring(0, i); - }, - - /* - * This function should always be called when processing user-supplied - * paths. The application should then only trust the object it - * returns. Returns false if the path is invalid. - * Otherwise, returns an object, with the following properties: - * - * System-wide absolute real fullpath, to be used by the - * application to access the files/directories in - * question. - * Virtual root based absolute fullpath, useful to report - * to the user. Can also be useful to change a Root - * object's cwd to, after verifying that really - * points to an existing directory. - */ - valid_virtual: function(path) - { - var cwd, c, l, t, o; - - o = {}; - cwd = this.cwd; - - /* - * Look for '~', or '/' in the beginning of the path, - * in which case cwd gets cleared to '/'. Also look for - * './' or '.[EOS]', which we simply skip, meaning the - * current directory. - */ - if ((l = path.length) > 0) { - c = path.charAt(0); - if (c == '~' || c == '/') { - cwd = '/'; - path = path.substr(1); - } else if (c == '.') { - if (l == 1) - path = ''; - else if (l > 1 && path.charAt(1) == '/') - path = path.substr(2); - } - } - - /* - * Now process all starting '..' or '../', modifying cwd if - * allowed, and stripping them from path. In case of '../', - * also strip any multiple '/'. - */ - for (;;) { - t = false; - l = path.length; - if ((l == 2 && path == '..') || - (l > 2 && path.substr(0, 2) == '..' && - (t = (path.charAt(2) == '/')))) { - if (!(cwd = this.parent(cwd))) - return false; - if (t) { - for (i = 2; path.charAt(i) == '/'; - i++) ; - path = path.substr(i); - continue; - } else { - path = path.substr(2); - continue; - } - } - break; - } - - /* - * Now attempt to fill in our object properties. - * this.valid_path() performs necessary sanity checking. - */ - if (!(o.virtual = this.valid_path('/' + cwd + '/' + path))) - return false; - o.real = this.root + o.virtual; - - return o; - } - -}; diff --git a/tests/js-test/js/httpd/string.js b/tests/js-test/js/httpd/string.js deleted file mode 100644 index 5ed8080..0000000 --- a/tests/js-test/js/httpd/string.js +++ /dev/null @@ -1,34 +0,0 @@ -/* $Id: string.js,v 1.1 2005/07/11 01:50:56 mmondor Exp $ */ - -/* - * Copyright (c) 2005, Matthew Mondor - * ALL RIGHTS RESERVED. - */ - -/* - * Extension to the standard String object to support startsWith() and - * endsWith() very useful methods. - */ - - - -String.prototype.startsWith = function(str) -{ - var t = str.length; - - if (this.length >= t && this.substr(0, t) == str) - return true; - - return false; -} - -String.prototype.endsWith = function(str) -{ - var t1 = str.length; - var t2 = this.length; - - if (t2 >= t1 && this.substr(t2 - t1, t1) == str) - return true; - - return false; -} diff --git a/tests/js-test/js/ml.js b/tests/js-test/js/ml.js deleted file mode 100644 index dd09de6..0000000 --- a/tests/js-test/js/ml.js +++ /dev/null @@ -1,176 +0,0 @@ -/* $Id: ml.js,v 1.4 2005/06/27 18:21:21 mmondor Exp $ */ - -/* - * Copyright (c) 2004-2005, Matthew Mondor - * ALL RIGHTS RESERVED. - */ - -const ident_str = ' ' + - ' '; - -function indent(level) -{ - if (level == null) - return false; - - return ident_str.substr(0, level); -} - -function wrap(str, level) -{ - if (str == null || level == null) - return false; - - var len = str.length; - var newstr = indent(level); - var newlen = level; - var i; - - /* - * Could find index to space and use substring as necessary instead of - * looping through every character. Of course, this could also all be - * implemented using C native code. - */ - for (i = 0; i < len; i++) { - if (str.charAt(i) == ' ' && newlen > 70) { - newstr += "\n" + indent(level); - newlen = level; - continue; - } - newstr += str.charAt(i); - newlen++; - } - - return newstr; -} - - - -function MLAttr(attr, value) -{ - /* We allow null value */ - if (attr == null) - return false; - - this.attr = attr; - this.value = value; -} - - - -/* - * XXX Using prototypes to inherit functions would be ideal for speed, - * probably, to avoid having to redeclare the functions in the constructor for - * every new object instance. However, I seemed to have problems when using - * that method, mostly related to the constructor of the object no longer - * being tracable. - */ - -function MLTag(attr, close) -{ - if (attr == null || close == null) - return false; - - this.attr = attr; - this.close = close; - this.attributes = new Array(); - this.attributes_count = 0; - this.contents = new Array(); - this.contents_count = 0; - - this.addAttr = function(attr, value) - { - if (attr == null) - return false; - - this.attributes[this.attributes_count++] = - new MLAttr(attr, value); - } - - this.addContent = function(item) - { - if (item == null) - return false; - - this.contents[this.contents_count++] = item; - } - - this.toStr = function(level) - { - if (level == null) - return false; - - var str = ''; - var i, attrlen, len, a; - - str += indent(level) + '<' + this.attr; - attrlen = this.attr.length; - len = level + attrlen + 1; - for (i = 0; i < this.attributes_count; i++) { - a = this.attributes[i]; - - if (len > 70) { - len = level + attrlen + 1; - str += "\n" + indent(len); - } - str += ' ' + a.attr; - len += attrlen + 1; - if (a.value != null) { - var value = a.value; - str += '="' + value + '"'; - len += value.length + 3; - } - } - str += ">\n"; - - if (this.close) - level++; - - for (i = 0; i < this.contents_count; i++) { - a = this.contents[i]; - - if (typeof a == 'object') - str += a.toStr(level); - else - str += wrap(a, level) + "\n"; - } - - if (this.close) - str += indent(--level) + '\n"; - - return str; - } -} - - - -/* - * Now test our functionality - */ - -out = new FD(); -out.set(FD.STDOUT_FILENO); - -for (i = 0; i < 1000; i++) { - -var table = new MLTag('table', true); -table.addAttr('border', 0); -table.addAttr('width', '100%'); - -var tr = new MLTag('tr', true); - -var td = new MLTag('td', true); -td.addAttr('bgcolor', '#000000'); - -var p = new MLTag('p', true); -p.addContent('Hello'); - -td.addContent(p); -tr.addContent(td); -table.addContent(tr); - -out.put(table.toStr(8)); - -//out.put(uneval(table)); - -} diff --git a/tests/js-test/js/ml2.js b/tests/js-test/js/ml2.js deleted file mode 100644 index 6a1eab6..0000000 --- a/tests/js-test/js/ml2.js +++ /dev/null @@ -1,209 +0,0 @@ -/* $Id: ml2.js,v 1.3 2005/06/27 19:45:22 mmondor Exp $ */ - -/* - * Copyright (c) 2004-2005, Matthew Mondor - * ALL RIGHTS RESERVED. - */ - -/* - * XXX Using prototypes to inherit functions would be ideal for speed, - * probably, to avoid having to redeclare the functions in the constructor for - * every new object instance. However, I seemed to have problems when using - * that method, mostly related to the constructor of the object no longer - * being tracable. At worse, we could define our methods as MLTag_foo() and - * in the constructor function just assign them to the proper properties... - * The currently used method at least has advantage of being cleaner to read. - */ - -/* - * Useful object to use for HTML tags. consists of tag name, and - * of a boolean specifying of the tag requires a corresponding closing - * tag (i.e.

[body]

. Attributes and body elements may be added after - * creating such an object, and toStr() used to obtain the actual HTML result. - */ -function MLTag(tag, close) -{ - if (tag == null || close == null) - return false; - - /* - * A few utility functions used by our methods later on. - * Adding these here prevents namespace pollution. - */ - - /* Returns a string with requested number of spaces */ - const indent_str = ' ' + - ' '; - this.indent = function(level) - { - if (level == null) - return false; - - return indent_str.substr(0, level); - } - - /* - * Wraps specified string at 70 columns and observing specified - * indentation level - */ - this.wrap = function(str, level) - { - if (str == null || level == null) - return false; - - var len = str.length; - var newstr = this.indent(level); - var newlen = level; - - /* - * Could find index to space and use substring as necessary - * instead of looping through every character. Of course, - * this could also all be implemented using C native code. - */ - for (var i = 0; i < len; i++) { - var c = str.charAt(i); - - if (c == ' ' && newlen > 70) { - newstr += "\n" + this.indent(level); - newlen = level; - continue; - } - newstr += c; - newlen++; - } - - return newstr; - } - - - /* - * MLTag object properties - */ - this.tag = tag; - this.close = close; - this.attributes = new Object(); - this.contents = new Array(); - this.contents_count = 0; - - /* - * Add specified attribute with optional associated value to this tag - */ - this.addAttr = function(attr, value) - { - if (attr == null) - return false; - - eval('this.attributes.' + attr + ' = \'' + value + '\''); - } - - /* - * Add arbitrary content in sequencial order to this tag's body - */ - this.addContent = function(item) - { - if (item == null) - return false; - - this.contents[this.contents_count++] = item; - } - - /* - * Returns a string consisting of this tag along with its body and - * optional corresponding closing tag - */ - this.toStr = function(level) - { - if (level == null) - return false; - - /* Open opening tag */ - var str = this.indent(level) + '<' + this.tag; - - /* - * Store these immediately for performance since we use those - * values several times later on, also saves typing - */ - var taglen = this.tag.length; - var len = level + taglen + 1; - var attributes = this.attributes; - - /* Add each of our attributes with their optional values */ - for (var attr in attributes) { - var value; - - if (len > 70) { - len = level + taglen + 1; - str += "\n" + this.indent(len); - } - str += ' ' + attr; - len += taglen + 1; - if ((value = attributes[attr]) != null) { - str += '="' + value + '"'; - len += value.length + 3; - } - } - /* Close opening tag */ - str += ">\n"; - - /* Increase our indent level for content */ - level++; - - /* - * Now add all our contents, if any, which may consist of - * other MLTag objects or arbitrary String objects. Use - * recursion to process MLTag ones, which may also have their - * respective bodies and closing tags. - */ - for (var i = 0; i < this.contents_count; i++) { - var a = this.contents[i]; - - if (typeof a == 'object') - str += a.toStr(level); - else - str += this.wrap(a, level) + "\n"; - } - - /* - * This tag required closing, so add corresponding closing tag - * and decrease indent level. - */ - level--; - if (this.close) - str += this.indent(level) + '\n"; - - return str; - } -} - - - -/* - * Now test our functionality - */ - -out = new FD(); -out.set(FD.STDOUT_FILENO); - -for (i = 0; i < 1000; i++) { - -var table = new MLTag('table', true); -table.addAttr('border', 0); -table.addAttr('width', '100%'); - -var tr = new MLTag('tr', true); - -var td = new MLTag('td', true); -td.addAttr('bgcolor', '#000000'); - -var p = new MLTag('p', true); -p.addContent('Hello'); - -td.addContent(p); -tr.addContent(td); -table.addContent(tr); - -out.put(table.toStr(8)); - -//out.put(uneval(table)); - -} diff --git a/tests/js-test/js/ml3.js b/tests/js-test/js/ml3.js deleted file mode 100644 index 3b8cb3b..0000000 --- a/tests/js-test/js/ml3.js +++ /dev/null @@ -1,209 +0,0 @@ -/* $Id: ml3.js,v 1.3 2005/06/27 19:45:22 mmondor Exp $ */ - -/* - * Copyright (c) 2004-2005, Matthew Mondor - * ALL RIGHTS RESERVED. - */ - -/* - * XXX Using prototypes to inherit functions would be ideal for speed, - * probably, to avoid having to redeclare the functions in the constructor for - * every new object instance. However, I seemed to have problems when using - * that method, mostly related to the constructor of the object no longer - * being tracable. At worse, we could define our methods as MLTag_foo() and - * in the constructor function just assign them to the proper properties... - * The currently used method at least has advantage of being cleaner to read. - */ - -/* - * Useful object to use for HTML tags. consists of tag name, and - * of a boolean specifying of the tag requires a corresponding closing - * tag (i.e.

[body]

. Attributes and body elements may be added after - * creating such an object, and toStr() used to obtain the actual HTML result. - */ -function MLTag(tag, close) -{ - if (tag == null || close == null) - return false; - - /* - * A few utility functions used by our methods later on. - * Adding these here prevents namespace pollution. - */ - - /* Returns a string with requested number of spaces */ - const indent_str = ' ' + - ' '; - this.indent = function(level) - { - if (level == null) - return false; - - return indent_str.substr(0, level); - } - - /* - * Wraps specified string at 70 columns and observing specified - * indentation level - */ - this.wrap = function(str, level) - { - if (str == null || level == null) - return false; - - var len = str.length; - var newstr = this.indent(level); - var newlen = level; - - /* - * Could find index to space and use substring as necessary - * instead of looping through every character. Of course, - * this could also all be implemented using C native code. - */ - for (var i = 0; i < len; i++) { - var c = str.charAt(i); - - if (c == ' ' && newlen > 70) { - newstr += "\n" + this.indent(level); - newlen = level; - continue; - } - newstr += c; - newlen++; - } - - return newstr; - } - - - /* - * MLTag object properties - */ - this.tag = tag; - this.close = close; - this.attributes = new Array(); /* Associative */ - this.contents = new Array(); - this.contents_count = 0; - - /* - * Add specified attribute with optional associated value to this tag - */ - this.addAttr = function(attr, value) - { - if (attr == null) - return false; - - this.attributes[attr] = value; - } - - /* - * Add arbitrary content in sequencial order to this tag's body - */ - this.addContent = function(item) - { - if (item == null) - return false; - - this.contents[this.contents_count++] = item; - } - - /* - * Returns a string consisting of this tag along with its body and - * optional corresponding closing tag - */ - this.toStr = function(level) - { - if (level == null) - return false; - - /* Open opening tag */ - var str = this.indent(level) + '<' + this.tag; - - /* - * Store these immediately for performance since we use those - * values several times later on, also saves typing - */ - var taglen = this.tag.length; - var len = level + taglen + 1; - var attributes = this.attributes; - - /* Add each of our attributes with their optional values */ - for (var attr in attributes) { - var value; - - if (len > 70) { - len = level + taglen + 1; - str += "\n" + this.indent(len); - } - str += ' ' + attr; - len += taglen + 1; - if ((value = attributes[attr]) != null) { - str += '="' + value + '"'; - len += value.length + 3; - } - } - /* Close opening tag */ - str += ">\n"; - - /* Increase our indent level for content */ - level++; - - /* - * Now add all our contents, if any, which may consist of - * other MLTag objects or arbitrary String objects. Use - * recursion to process MLTag ones, which may also have their - * respective bodies and closing tags. - */ - for (var i = 0; i < this.contents_count; i++) { - var a = this.contents[i]; - - if (typeof a == 'object') - str += a.toStr(level); - else - str += this.wrap(a, level) + "\n"; - } - - /* - * This tag required closing, so add corresponding closing tag - * and decrease indent level. - */ - level--; - if (this.close) - str += this.indent(level) + '\n"; - - return str; - } -} - - - -/* - * Now test our functionality - */ - -out = new FD(); -out.set(FD.STDOUT_FILENO); - -for (i = 0; i < 1000; i++) { - -var table = new MLTag('table', true); -table.addAttr('border', 0); -table.addAttr('width', '100%'); - -var tr = new MLTag('tr', true); - -var td = new MLTag('td', true); -td.addAttr('bgcolor', '#000000'); - -var p = new MLTag('p', true); -p.addContent('Hello'); - -td.addContent(p); -tr.addContent(td); -table.addContent(tr); - -out.put(table.toStr(8)); - -//out.put(uneval(table)); - -} diff --git a/tests/js-test/js/ml4.js b/tests/js-test/js/ml4.js deleted file mode 100644 index e2d3383..0000000 --- a/tests/js-test/js/ml4.js +++ /dev/null @@ -1,203 +0,0 @@ -/* $Id: ml4.js,v 1.5 2005/06/27 19:45:22 mmondor Exp $ */ - -/* - * Copyright (c) 2004-2005, Matthew Mondor - * ALL RIGHTS RESERVED. - */ - - -/* - * Useful object to use for HTML tags. consists of tag name, and - * of a boolean specifying of the tag requires a corresponding closing - * tag (i.e.

[body]

. Attributes and body elements may be added after - * creating such an object, and toStr() used to obtain the actual HTML result. - */ -function MLTag(tag, close) -{ - if (tag == null || close == null) - return false; - - /* - * MLTag object properties - */ - this.tag = tag; - this.close = close; - this.attributes = {}; /* Object with associative attributes */ - this.contents = []; /* Array object */ -} - -/* - * Force creation of prototype object - */ -MLTag('prototype', false); - -/* - * A few utility functions used by our methods later on. - * Adding these here prevents namespace pollution. - */ - -MLTag.prototype.indent_str = ' ' + - ' '; -/* Returns a string with requested number of spaces */ -MLTag.prototype.indent = function(level) -{ - if (level == null) - return false; - - return this.indent_str.substr(0, level); -} - -/* - * Wraps specified string at 70 columns and observing specified - * indentation level - */ -MLTag.prototype.wrap = function(str, level) -{ - if (str == null || level == null) - return false; - - var len = str.length; - var newstr = this.indent(level); - var newlen = level; - - /* - * Could find index to space and use substring as necessary - * instead of looping through every character. Of course, - * this could also all be implemented using C native code. - */ - for (var i = 0; i < len; i++) { - var c = str.charAt(i); - - if (c == ' ' && newlen > 70) { - newstr += "\n" + this.indent(level); - newlen = level; - continue; - } - newstr += c; - newlen++; - } - - return newstr; -} - -/* - * Add specified attribute with optional associated value to this tag - */ -MLTag.prototype.addAttr = function(attr, value) -{ - if (attr == null) - return false; - - this.attributes[attr] = value; -} - -/* - * Add arbitrary content in sequencial order to this tag's body - */ -MLTag.prototype.addContent = function(item) -{ - if (item == null) - return false; - - this.contents.push(item); -} - -/* - * Returns a string consisting of this tag along with its body and - * optional corresponding closing tag - */ -MLTag.prototype.toStr = function(level) -{ - if (level == null) - return false; - - /* Open opening tag */ - var str = this.indent(level) + '<' + this.tag; - - /* - * Store these immediately for performance since we use those - * values several times later on, also saves typing - */ - var taglen = this.tag.length; - var len = level + taglen + 1; - var attributes = this.attributes; - - /* Add each of our attributes with their optional values */ - for (var attr in attributes) { - var value; - - if (len > 70) { - len = level + taglen + 1; - str += "\n" + this.indent(len); - } - str += ' ' + attr; - len += taglen + 1; - if ((value = attributes[attr]) != null) { - str += '="' + value + '"'; - len += value.length + 3; - } - } - /* Close opening tag */ - str += ">\n"; - - /* Increase our indent level for content */ - level++; - - /* - * Now add all our contents, if any, which may consist of - * other MLTag objects or arbitrary String objects. Use - * recursion to process MLTag ones, which may also have their - * respective bodies and closing tags. - */ - for (var i = 0; i < this.contents.length; i++) { - var a = this.contents[i]; - - if (typeof a == 'object') - str += a.toStr(level); - else - str += this.wrap(a, level) + "\n"; - } - - /* - * This tag required closing, so add corresponding closing tag - * and decrease indent level. - */ - level--; - if (this.close) - str += this.indent(level) + '\n"; - - return str; -} - - - -/* - * Now test our functionality - */ - -out = new FD(); -out.set(FD.STDOUT_FILENO); - -for (i = 0; i < 1000; i++) { - -var table = new MLTag('table', true); -table.addAttr('border', 0); -table.addAttr('width', '100%'); - -var tr = new MLTag('tr', true); - -var td = new MLTag('td', true); -td.addAttr('bgcolor', '#000000'); - -var p = new MLTag('p', true); -p.addContent('Hello'); - -td.addContent(p); -tr.addContent(td); -table.addContent(tr); - -out.put(table.toStr(8)); - -//out.put(uneval(table)); - -} diff --git a/tests/js-test/js/ml5.js b/tests/js-test/js/ml5.js deleted file mode 100644 index 9c7b772..0000000 --- a/tests/js-test/js/ml5.js +++ /dev/null @@ -1,213 +0,0 @@ -/* $Id: ml5.js,v 1.3 2005/06/27 19:45:22 mmondor Exp $ */ - -/* - * Copyright (c) 2004-2005, Matthew Mondor - * ALL RIGHTS RESERVED. - */ - - -/* - * Useful object to use for HTML tags. consists of tag name, and - * of a boolean specifying of the tag requires a corresponding closing - * tag (i.e.

[body]

. Attributes and body elements may be added after - * creating such an object, and toStr() used to obtain the actual HTML result. - */ -function MLTag(tag, close) -{ - /* - if (tag == null || close == null) - return false; - */ - - /* - * MLTag object properties - */ - this.tag = tag; - this.close = close; - this.attributes = {}; /* Object with associative attributes */ - this.contents = []; /* Array object */ -} - -/* - * Define prototype of the MLTag object. - */ -MLTag.prototype = { - /* A string of spaces to be used by indent() */ - indent_str: ' ' + - ' ', - - /* Returns a string with requested number of spaces */ - indent: function(level) - { - /* - if (level == null) - return false; - */ - - return this.indent_str.substr(0, level); - }, - - /* - * Wraps specified string at 70 columns and observing specified - * indentation level - */ - wrap: function(str, level) - { - /* - if (str == null || level == null) - return false; - */ - - var len = str.length; - var newstr = this.indent(level); - var newlen = level; - - /* - * Could find index to space and use substring as necessary - * instead of looping through every character. Of course, - * this could also all be implemented using C native code. - */ - for (var i = 0; i < len; i++) { - var c = str.charAt(i); - - if (c == ' ' && newlen > 70) { - newstr += "\n" + this.indent(level); - newlen = level; - continue; - } - newstr += c; - newlen++; - } - - return newstr; - }, - - /* - * Add specified attribute with optional associated value to this tag - */ - addAttr: function(attr, value) - { - /* - if (attr == null) - return false; - */ - - this.attributes[attr] = value; - }, - - /* - * Add arbitrary content in sequencial order to this tag's body - */ - addContent: function(item) - { - /* - if (item == null) - return false; - */ - - this.contents.push(item); - }, - - /* - * Returns a string consisting of this tag along with its body and - * optional corresponding closing tag - */ - toStr: function(level) - { - /* - if (level == null) - return false; - */ - - /* Open opening tag */ - var str = this.indent(level) + '<' + this.tag; - - /* - * Store these immediately for performance since we use those - * values several times later on, also saves typing - */ - var taglen = this.tag.length; - var len = level + taglen + 1; - var attributes = this.attributes; - - /* Add each of our attributes with their optional values */ - for (var attr in attributes) { - var value; - - if (len > 70) { - len = level + taglen + 1; - str += "\n" + this.indent(len); - } - str += ' ' + attr; - len += taglen + 1; - if ((value = attributes[attr]) != null) { - str += '="' + value + '"'; - len += value.length + 3; - } - } - /* Close opening tag */ - str += ">\n"; - - /* Increase our indent level for content */ - level++; - - /* - * Now add all our contents, if any, which may consist of - * other MLTag objects or arbitrary String objects. Use - * recursion to process MLTag ones, which may also have their - * respective bodies and closing tags. - */ - for (var i = 0; i < this.contents.length; i++) { - var a = this.contents[i]; - - if (typeof a == 'object') - str += a.toStr(level); - else - str += this.wrap(a, level) + "\n"; - } - - /* - * This tag required closing, so add corresponding closing tag - * and decrease indent level. - */ - level--; - if (this.close) - str += this.indent(level) + '\n"; - - return str; - } -} - - - - -/* - * Now test our functionality - */ - -out = new FD(); -out.set(FD.STDOUT_FILENO); - -for (i = 0; i < 1000; i++) { - -var table = new MLTag('table', true); -table.addAttr('border', 0); -table.addAttr('width', '100%'); - -var tr = new MLTag('tr', true); - -var td = new MLTag('td', true); -td.addAttr('bgcolor', '#000000'); - -var p = new MLTag('p', true); -p.addContent('Hello'); - -td.addContent(p); -tr.addContent(td); -table.addContent(tr); - -out.put(table.toStr(8)); - -//out.put(uneval(table)); - -} diff --git a/tests/js-test/js/ml6.js b/tests/js-test/js/ml6.js deleted file mode 100644 index 15c3b8c..0000000 --- a/tests/js-test/js/ml6.js +++ /dev/null @@ -1,182 +0,0 @@ -/* $Id: ml6.js,v 1.1 2005/11/16 00:35:01 mmondor Exp $ */ - -/* - * Copyright (c) 2004-2005, Matthew Mondor - * ALL RIGHTS RESERVED. - */ - - -/* - * Useful object to use for HTML tags. consists of tag name, and - * of a boolean specifying of the tag requires a corresponding closing - * tag (i.e.

[body]

. Attributes and body elements may be added after - * creating such an object, and toStr() used to obtain the actual HTML result. - */ -function MLTag(tag, close) -{ - /* - * MLTag object properties - */ - this.tag = tag; - this.close = close; - this.attributes = {}; /* Object with associative attributes */ - this.contents = []; /* Array object */ -} - -/* - * Define prototype of the MLTag object. - */ -MLTag.prototype = { - /* A string of spaces to be used for indenting */ - indent_str: ' ' + - ' ', - - /* - * Wraps specified string at 70 columns and observing specified - * indentation level - */ - wrap: function(str, level) - { - var len = str.length; - var newstr = this.indent_str.substr(0, level); - var newlen = level; - - /* - * Could find index to space and use substring as necessary - * instead of looping through every character. Of course, - * this could also all be implemented using C native code. - */ - for (var i = 0; i < len; i++) { - var c = str.charAt(i); - - if (c == ' ' && newlen > 70) { - newstr += "\n" + this.indent_str.substr(0, - level); - newlen = level; - continue; - } - newstr += c; - newlen++; - } - - return newstr; - }, - - /* - * Add specified attribute with optional associated value to this tag - */ - addAttr: function(attr, value) - { - - this.attributes[attr] = value; - }, - - /* - * Add arbitrary content in sequencial order to this tag's body - */ - addContent: function(item) - { - - this.contents.push(item); - }, - - /* - * Returns a string consisting of this tag along with its body and - * optional corresponding closing tag - */ - toStr: function(level) - { - - /* Open opening tag */ - var str = this.indent_str.substr(0, level) + '<' + this.tag; - - /* - * Store these immediately for performance since we use those - * values several times later on, also saves typing - */ - var taglen = this.tag.length; - var len = level + taglen + 1; - var attributes = this.attributes; - - /* Add each of our attributes with their optional values */ - for (var attr in attributes) { - var value; - - if (len > 70) { - len = level + taglen + 1; - str += "\n" + this.indent_str.substr(0, len); - } - str += ' ' + attr; - len += taglen + 1; - if ((value = attributes[attr]) != null) { - str += '="' + value + '"'; - len += value.length + 3; - } - } - /* Close opening tag */ - str += ">\n"; - - /* Increase our indent level for content */ - level++; - - /* - * Now add all our contents, if any, which may consist of - * other MLTag objects or arbitrary String objects. Use - * recursion to process MLTag ones, which may also have their - * respective bodies and closing tags. - */ - for (var i = 0; i < this.contents.length; i++) { - var a = this.contents[i]; - - if (typeof a == 'object') - str += a.toStr(level); - else - str += this.wrap(a, level) + "\n"; - } - - /* - * This tag required closing, so add corresponding closing tag - * and decrease indent level. - */ - level--; - if (this.close) - str += this.indent_str.substr(0, level) + '\n"; - - return str; - } -} - - - - -/* - * Now test our functionality - */ - -out = new FD(); -out.set(FD.STDOUT_FILENO); - -for (i = 0; i < 1000; i++) { - -var table = new MLTag('table', true); -table.addAttr('border', 0); -table.addAttr('width', '100%'); - -var tr = new MLTag('tr', true); - -var td = new MLTag('td', true); -td.addAttr('bgcolor', '#000000'); - -var p = new MLTag('p', true); -p.addContent('Hello'); - -td.addContent(p); -tr.addContent(td); -table.addContent(tr); - -out.put(table.toStr(8)); - -//out.put(uneval(table)); - -} diff --git a/tests/js-test/js/parse.js b/tests/js-test/js/parse.js deleted file mode 100644 index 672b115..0000000 --- a/tests/js-test/js/parse.js +++ /dev/null @@ -1,318 +0,0 @@ -/* $Id: parse.js,v 1.1 2005/11/16 00:36:58 mmondor Exp $ */ - -/* - * Copyright (c) 2004-2005, Matthew Mondor - * ALL RIGHTS RESERVED. - */ - - -/* - * Useful object to use for HTML tags. consists of tag name, and - * of a boolean specifying of the tag requires a corresponding closing - * tag (i.e.

[body]

. Attributes and body elements may be added after - * creating such an object, and toStr() used to obtain the actual HTML result. - */ -function MLTag(tag, close) -{ - /* - * MLTag object properties - */ - this.tag = tag; - this.close = close; - this.attributes = {}; /* Object with associative attributes */ - this.contents = []; /* Array object */ -} - -/* - * Define prototype of the MLTag object. - */ -MLTag.prototype = { - - /* - * Add specified attribute with optional associated value to this tag - */ - addAttr: function(attr, value) - { - - this.attributes[attr] = value; - }, - - /* - * Add arbitrary content in sequencial order to this tag's body - */ - addContent: function(item) - { - - this.contents.push(item); - }, - - /* - * Returns a string consisting of this tag along with its body and - * optional corresponding closing tag - */ - toStr: function(level) - { - var str = ''; - - /* Null tags may exist as containers */ - if (this.tag != null) { - - /* Open opening tag */ - str += '<' + this.tag; - - /* - * Store these immediately for performance since we - * use those values several times later on, also - * saves typing - */ - var taglen = this.tag.length; - var len = taglen + 1; - var attributes = this.attributes; - - /* - * Add each of our attributes with their optional - * values - */ - for (var attr in attributes) { - var value; - - str += ' ' + attr; - len += taglen + 1; - if ((value = attributes[attr]) != null) { - str += '="' + value + '"'; - len += value.length + 3; - } - } - - /* Close opening tag */ - str += ">"; - - } - - /* - * Now add all our contents, if any, which may consist of - * other MLTag objects or arbitrary String objects. Use - * recursion to process MLTag ones, which may also have their - * respective bodies and closing tags. - */ - for (var i = 0; i < this.contents.length; i++) { - var a = this.contents[i]; - - if (typeof a == 'object') - str += a.toStr(0); - else - str += a; - } - - /* - * If this tag required closing, do so - */ - if (this.tag != null && this.close) - str += '"; - - return str; - } - -} - - -var entitites_table = { - '<': '<', - '>': '>', - '&': '&', - '"': '"', - "`": '‘', - "'": '’' -}; - -/* - * Function to convert a supplied string to use HTML/SGML special entitites. - * This also allows HTML escaping from user-supplied strings. - */ -function toHTMLEntities(str) -{ - var s = ''; - var i, t, c, e; - - for (i = 0, t = str.length; i < t; i++) { - c = str.charAt(i); - if ((e = entitites_table[c]) != undefined) - s += e; - else - s += c; - } - - return s; -} - - - -/* - * This is a parsing test. - * - * For now, we want to simply accept patterns in the form %x{...}, where - * there may be arbitrary data within the { }, and function %x must be - * performed on it. For instance, %b{some text} would denote this text - * as bold. However, we should also allow %b{some text %i{more text}} and - * such. Because %, { and } characters are special, we should support - * escaping with \. Because \ is special for escaping, we should support - * \\ meaning a single \. Unclosed } should either generate an error, - * or we might want to automatically consider them all closed at the end - * of the user provided string. - * - * XXX One level currently seems to work, but there are recursive problems! - * Also, we are loosing a space. - */ - -function tokenize(str, ctag, i) -{ - var t, c; - var escaped = false; - var tags = new MLTag(null, false); - var ss = ''; - - if (ctag != undefined) { - if (str.length < i || str.charAt(i) != '{') - return tags; - i++; - } else - i = 0; - - for (t = str.length; i < t; i++) { - c = str.charAt(i); - - /* Handle '\' escaping, valid for the whole string */ - if (escaped && - (c == '\\' || c == '%' || c == '{' || c == '}')) { - ss += c; - escaped = false; - continue; - } - if (c == '\\') { - escaped = true; - continue; - } - - /* - * Special character which delimits commands strings. Since - * we are recursively called we must detect this condition and - * exit just like for the end of string. - */ - if (c == '{' || c == '}' && ctag == undefined) { - ss += c; - continue; - } - if (c == '}') { -// i++; /* XXX */ - break; - } - - /* - * Command mode. Make sure that we do have a command name, - * followed with required '{'. If an invalid command, simply - * allow enclosed string to be litteral. For valid commands, - * create the required tag and recurse into ourselves on the - * enclosed string in '{' and '}'. - */ - /* Command mode */ - if (c == '%') { - var ntag, o; - - if (/*ctag != undefined ||*/ i > t - 1 || - str.charAt(i + 2) != '{') { - ss += c; - continue; - } - - /* Flush current string if any */ - if (ss.length > 0) { - tags.addContent(ss); - ss = ''; - } - - switch (str.charAt(i + 1)) { - case 'b': - ntag = new MLTag('b', true); - break; - case 'i': - ntag = new MLTag('i', true); - break; - case 'e': - ntag = new MLTag('em', true); - break; - case 'S': - ntag = new MLTag('sup', true); - break; - case 's': - ntag = new MLTag('sub', true); - break; - case 't': - ntag = new MLTag('tt', true); - break; - case 'h': - ntag = new MLTag('h', true); - break; - case 'p': - ntag = new MLTag('p', true); - break; - case 'n': - ntag = new MLTag('br', false); - break; - case 'A': - ntag = new MLTag('a', true); - break; - case 'I': - ntag = new MLTag('img', true); - break; - default: - ntag = new MLTag(null, false); - } - - o = tokenize(str, ntag, i + 2); - i = o.i; - /* XXX */ - ntag.addContent(o.tags.toStr(0)); - tags.addContent(ntag); - continue; - } - - ss += c; - } - - /* - * Now that we gathered command-enclosed string, take care of special - * cases for and , and add resulting content. - */ - if (ctag != undefined) { - switch (ctag.tag) { - case 'a': - ctag.addAttr('href', ss); - break; - case 'img': - ctag.addAttr('src', ss); - break; - } - if (ss.length > 0) { - ctag.addContent(ss); - ss = ''; - } - tags.addContent(ctag); - } - if (ss.length > 0) - tags.addContent(ss); - - /* - * Return object with current index in string, as well as new tag - * container - */ - var obj = {}; - obj.i = i; - obj.tags = tags; - - return obj; -} - - -//print((tokenize('Some %b{enclosed} string', undefined, 0)).tags.toStr(0)); -print((tokenize('Some %b{enclo%i{s}ed} string', undefined, 0)).tags.toStr(0)); -//print(uneval((tokenize('Some %b{enclo%i{s}ed} string', undefined, 0)).tags)); diff --git a/tests/js-test/js/poll_test.js b/tests/js-test/js/poll_test.js deleted file mode 100644 index ba04111..0000000 --- a/tests/js-test/js/poll_test.js +++ /dev/null @@ -1,93 +0,0 @@ -/* $Id: poll_test.js,v 1.3 2005/02/13 09:02:06 mmondor Exp $ */ - -function events(e) -{ - s = new String(); - - if (e & FD.POLLIN) - s += 'POLLIN '; - if (e & FD.POLLOUT) - s += 'POLLOUT '; - if (e & FD.POLLERR) - s += 'POLLERR '; - if (e & FD.POLLHUP) - s += 'POLLHUP '; - if (e & FD.POLLNVAL) - s += 'POLLNVAL '; - - return s; -} - -output = new FD(); -output.set(FD.STDOUT_FILENO); - -try { - - input = new FD(); - input.set(FD.STDIN_FILENO); - - set = new Array(); - - /* - * This is interesting, because we allow normal arrays, either dense - * using set.push(), or sparse using set[n] = fd, but we also allow - * associative array elements such as set['string'] = fd. The - * returned set only holds FD objects for which events occurred. One - * can run through that resulting set using for (v in a) ... format, - * but it is also possible to verify the associative string name to - * see if it is in the set. As usual with poll(2), POLLHUP, POLLERR - * and POLLNVAL are always possible even for descriptor objects with - * their events property set to 0. The default events property for - * an FD object is also 0. The user can then verify the revents - * property of each FD object in the result set, and take appropriate - * I/O action or processing. - * - * Moreover, JavaScript being a very dynamic language, one can add - * properties or methods to individual FD objects as wanted, and of - * course can use those to say, process events of the descriptors - * returned in the set with pending events. This means that a common - * method name with different functionality added to each FD object - * permits a main events loop to transparently execute the wanted code - * using, for instance, fd.process(). In a case where two types of - * common events, input and output need to be processed differently - * with such a method, one can even define new methods as a prototype - * for base custom FD objects, which can be automatically inherited - * in shared mode by all children objects derived from the prototype - * object. - */ - - /* - * Set interesting events mask for our FD objects to monitor - */ - input.events = FD.POLLIN; - output.events = 0; - - /* - * Add our FD objects to an array - */ - set.push(input); - set.push(output); - - /* - * Invoke poll(2) on descriptors in our FD objects array, sleep for a - * maximum of 5000 milliseconds (5 seconds). Retreive result set of - * FD objects with pending events into rset. - */ - rset = FD.poll(set, 5000); - - /* - * Display rset contents - */ - output.put('Set size: ' + rset.length + "\n"); - for (var i in rset) { - output.put(i + ': type=' + rset[i].constructor.name + ' fd=' + - rset[i].fd + ' revents=' + events(rset[i].revents) + "\n"); - } - - input.close(); - -} catch (x) { - output.put(x + "\n"); -} - -output.close(); diff --git a/tests/js-test/js/sock_httpd.js b/tests/js-test/js/sock_httpd.js deleted file mode 100644 index d12533b..0000000 --- a/tests/js-test/js/sock_httpd.js +++ /dev/null @@ -1,292 +0,0 @@ -/* $Id: sock_httpd.js,v 1.8 2005/06/28 00:19:03 mmondor Exp $ */ - -/* - * Little example showing the versatility of ECMAScript, provided with a - * custom library for BSD socket support, using SpiderMonkey. - * This tiny HTTPd allows simultaneous concurrent client connections without - * using multiple threads or processes. Must be ran through ../src/test, - * compiled with ../src/classes/js_fd.[ch] support. - * - * We support a maximum of 4 concurrent connections per IP address, as well - * as a total maximum of 32 concurrent connections by default. - * Change MAX_CONNECTIONS and MAX_CONNECTIONS_ADDR as needed. - * - * We also support per-connection request timeouts, defaulting to 60 seconds. - * Change REQUEST_TIMEOUT as wanted. - * Note that this timeout consists of a total timeout for the connection, - * rather than an actual input timeout (which would require to reset the - * expiration time everytime user input occurs). Using the current timeout - * method suits most embedded applications, but would not be adequate to - * support large file transfers or the like (connection would timeout - * during a long transfer). However, if we supported this kind of long - * file transfers, because we are single-threaded, we would need two states - * in our machine, one where small requests take place, and another - * specialized for ongoing file transfers. Although this could be - * implemented, it was not a required functionality for this demonstration. - */ - - - -/* - * Configuration - */ -const MAX_CONNECTIONS = 32; -const MAX_CONNECTIONS_ADDR = 4; -const REQUEST_TIMEOUT = 60; - - - -/* - * Open standard error FD for diagnostics output - */ -err = new FD(); -err.set(FD.STDERR_FILENO); - - -/* - * Initialize server - */ -try { - sock = new FD(); - sock.socket(FD.AF_INET, FD.SOCK_STREAM, 0); - sock.bind('127.0.0.1', 1337); - sock.listen(MAX_CONNECTIONS); -} catch (x) { - err.put(x + "\n"); - exit(); -} - - -/* - * Create polling array set and insert bound socket in it - */ -var set = []; -sock.events = FD.POLLIN; -set['bind'] = sock; - - - -/* - * Used for unique index associated to each FD for efficient removal from - * polling set when closing connection - */ -var count = 0; - - -/* - * To know how many connections we are currently serving and limit them - */ -var connections = 0; -var addresses = []; - -function counters_inc(fd) -{ - if (connections >= MAX_CONNECTIONS) - return false; - - var addr = fd.client_addr; - if ((addrcnt = addresses[addr]) != undefined && - addrcnt >= MAX_CONNECTIONS_ADDR) - return false; - - if (addrcnt == undefined) - addrcnt = 1; - else - addrcnt++; - addresses[addr] = addrcnt; - - return true; -} - -function counters_dec(fd) -{ - var addr = fd.client_addr; - if ((--addresses[addr]) == 0) - delete addresses[addr]; - - connections--; -} - - -/* - * Handles client request and replies back - */ -function handle_request(fd) -{ - try { - fd.put( - "HTTP/1.1 200\r\n" + - "Content-type: text/html\r\n" + - "Server: js/1\r\n" + - "Connection: close\r\n" + - "\r\n" + - "Detected!\n" + - "
\n" +
-		    "You are: " + fd.client_addr + ":" + fd.client_port +
-		    "\n" + "ID: " + fd.index + "\n\n" +
-		    "You sent:\n" + fd.request + "\n" +
-		    "
\n" + "

Tracing in progress...

\n" + - "\r\n"); - } catch (x) {} -} - - -/* - * Main server loop - */ -for (;;) { - try { - - /* - * Determine next to expire FD event, so that we can sleep - * as long as possible. Also get rid of expired requests. - */ - var cur = Date.parse(new Date) / 1000; - var exp = cur + 3600; - for (i in set) { - var fd; - - if ((fd = set[i]) == undefined || i == 'bind') - continue; - if (fd.expires <= cur) { - /* - * Request timeout expired for this - * descriptor, we must close it. - */ - fd.close(); - counters_dec(fd); - delete set[fd.index]; - delete fd; - continue; - } - if (fd.expires < exp) - exp = fd.expires; - } - exp -= cur; - - /* - * Poll our set of descriptors for events - */ - var e = FD.poll(set, exp * 1000); - /* - * Verify if a timeout occurred. Because our poll - * implementation returns an array, its timeout event can be - * checked against by verifying if the set is empty. However, - * associative-array/object-attributes are not accounted - * properly with 'length' in JS, so also test fo the case of - * the 'bind' entry. - */ - if (e.length == 0 && e['bind'] == undefined) { - /* Timeout */ - continue; - } - - /* - * Process occurred events. First handle new connections, - * if any. - */ - if (e['bind'] != undefined) { - /* - * New connection, accept it - */ - var fd = sock.accept(); - - if (!counters_inc(fd)) { - fd.put("HTTP/1.1 403.9\r\n" + - "Connection: close\r\n\r\n"); - fd.close(); - delete fd; - } else { - /* - * Associate a new string with FD object - * which will serve to hold the client request - */ - fd.request = ''; - /* - * We add this custom property to efficiently - * be able to remove FD from polling set later - * on - */ - count++; - fd.index = count; - /* - * Set its interesting polling event to - * POLLIN - */ - fd.events = FD.POLLIN; - /* - * Also set request expiration time - */ - fd.expires = (Date.parse(new Date) / 1000) + - REQUEST_TIMEOUT; - /* - * Add descriptor to polling set - */ - set[count] = fd; - } - } - - /* - * Run through set of descriptors with pending events - */ - for (i in e) { - if (e[i] == undefined || i == 'bind') - continue; - if ((e[i].revents & (FD.POLLHUP | FD.POLLERR)) != 0) { - /* - * Unexpected connection loss, close and - * remove from polling set. - */ - e[i].close(); - counters_dec(e[i]); - delete set[e[i].index]; - delete e[i]; - } else if ((e[i].revents & FD.POLLIN) != 0) { - var l, close = false; - - /* - * New data to read from this FD, add to - * request string, verify if we should answer - * and close connection. - * We also limit the maximum request length. - */ - if ((l = e[i].get()) != null) { - if (e[i].length > 32768) - close = true; - else - e[i].request += l; - if (e[i].request.search("\r\n\r\n") - != -1) - close = true; - } else - close = true; - - if (close) { - /* - * Answer and close connection - */ - handle_request(e[i]); - e[i].close(); - counters_dec(e[i]); - /* - * Nice feature, we can hint the - * GC about the fact that we no - * longer refer to, or want these, - * which saves RAM since it avoids - * caching many old useless objects. - */ - delete set[e[i].index]; - delete e[i]; - } - } - } - } catch (x) { - err.put(x + "\n"); - } -} - - -/* NOTREACHED */ - -sock.close(); -err.close(); diff --git a/tests/js-test/js/sock_listen.js b/tests/js-test/js/sock_listen.js deleted file mode 100644 index 55f19e1..0000000 --- a/tests/js-test/js/sock_listen.js +++ /dev/null @@ -1,52 +0,0 @@ -/* $Id: sock_listen.js,v 1.4 2005/04/20 08:50:06 mmondor Exp $ */ - -err = new FD(); -err.set(FD.STDERR_FILENO); - -try { - sock = new FD(); - sock.socket(FD.AF_INET, FD.SOCK_STREAM, 0); - sock.bind('127.0.0.1', 1337); - sock.listen(10); -} catch (x) { - err.put(x + "\n"); -} - -var req = ''; -for (;;) { - try { - fd = sock.accept(); - try { - req = ''; - while (req.length < 32768) { - if ((l = fd.get()) == null) - break; - req += l; - if (l.search("\r\n\r\n")) - break; - } - /* - * Note: because I was asked a few times about this, - * the following is a joke :) we actually don't care - * about the client and won't traceroute, we only - * want client connections to test the program. - */ - fd.put("HTTP/1.1 200\r\nServer: js/1\r\n" + - "Connection: close\r\n" + "\r\n"); - fd.put("Detected!" + - "
\n");
-			fd.put("You are: " + fd.client_addr + ":" +
-			    fd.client_port + "\n\nYou sent:\n" + req + "\n");
-			fd.put("
\n" + "

Tracing in progress...

\n" + - "\r\n"); - } catch (y) { - err.put(y + "\n"); - } - fd.close(); - } catch (x) { - err.put(x + "\n"); - } -} - -sock.close(); -err.close(); diff --git a/tests/js-test/js/test.js b/tests/js-test/js/test.js deleted file mode 100644 index b27fc71..0000000 --- a/tests/js-test/js/test.js +++ /dev/null @@ -1,84 +0,0 @@ -/* $Id: test.js,v 1.1 2004/12/27 11:16:15 mmondor Exp $ */ - - -/* - * The following test should not succeed on API class - */ - -/* {{ */ - -/* -function test(s) -{ - API.print(s); -} - -API.test = test; -API.test("Successfully added method to API class!\n"); -API.test2 = "Successfully added property to API class!\n"; -API.test(API.test2 + "\n"); -*/ - -/* }} */ - - - -/* - * These tests should succeed, however. - */ - -API.print("API.property1 = " + API.property1 + "\n"); -API.print("API.property2 = " + API.property2 + "\n"); -API.print("API.property3 = " + API.property3 + "\n\n"); - -/* - * XXX - * These fail, even though these are permanent and read/write properties. - * JS_SealObject() seems to be converting all read/write properties to - * report an error if setProperty() method is called. However, interestingly - * enough, read/only properties report no error. However, they obviously - * are not modified despite attempts to assign them new values. - */ -API.property1 = 'Hello'; -API.property2 = 911; - -/* - * Following statement should fail, but is simply internally ignored at least - * (the setProperty() method is not called). - */ -API.property3 = 'READONLY!'; - -API.print("API.property1 = " + API.property1 + "\n"); -API.print("API.property2 = " + API.property2 + "\n"); -API.print("API.property3 = " + API.property3 + "\n\n"); - - - -/* - * Perform a test loop, during which callMe() should be called by the - * application code, via the break callback handler function. - */ -for (i = 0; i < 3; i++) { - API.print("We are the: " + Date() + "\n"); - API.print("NetBSD Kernel size is " + - (API.fileSize("/netbsd") / 1024 / 1024) + "MB\n"); - API.print("END\n"); -} -API.print("\n"); - - - -/* - * Will be called by our application if we create it - */ -function callMe(n) -{ - API.print("CallMe(" + n + ")!\n"); -} - - - -/* - * Interesting feature where we can return a value explicitely, optionally - */ -10 diff --git a/tests/js-test/js/test2.js b/tests/js-test/js/test2.js deleted file mode 100644 index 5cb105e..0000000 --- a/tests/js-test/js/test2.js +++ /dev/null @@ -1,15 +0,0 @@ -/* $Id: test2.js,v 1.4 2005/02/05 08:07:35 mmondor Exp $ */ - -var file; - -try { - file = new API.File("/etc/hosts"); - API.print("File '" + file.path + "' is loaded (" + file.size + - ") bytes.\n"); - file.release(); -} catch (x) { - API.print("Exception: " + x + "\n"); - exit(); -} - -API.print("Finishing...\n"); diff --git a/tests/js-test/js/test3.js b/tests/js-test/js/test3.js deleted file mode 100644 index c683324..0000000 --- a/tests/js-test/js/test3.js +++ /dev/null @@ -1,46 +0,0 @@ -function Employee() { - this.name = ""; - this.dept = "general"; -} - -function Manager() { - this.reports = []; -} -Manager.prototype = new Employee; - -function WorkerBee() { - this.projects = []; -} -WorkerBee.prototype = new Employee; - -function SalesPerson() { - this.dept = "sales"; - this.quota = 100; -} -SalesPerson.prototype = new WorkerBee; - -function Engineer() { - this.dept = "engineering"; - this.machine = ""; -} -Engineer.prototype = new WorkerBee; - -t = Engineer; -var eng -eng = new t(); -API.print(typeof eng + ', ' + eng.constructor.name); -API.print("\n"); - -/* Interesting test. Dynamically create properties i0 - i9 */ -list = new Object; -for (i = 0; i < 10; i++) { - c = 'list.i' + i + ' = ' + i * i; - eval(c); -} -/* And print the values of our i0 - i9 previously created properties. */ -for (i = 0; i < 10; i++) { - c = 'API.print(\'' + 'list.i' + i + ' = \' + list.i' + i + ' + "\\n")'; - eval(c); -} - -API.print("\nEND\n"); diff --git a/tests/js-test/src/GNUmakefile b/tests/js-test/src/GNUmakefile deleted file mode 100644 index 828cfae..0000000 --- a/tests/js-test/src/GNUmakefile +++ /dev/null @@ -1,29 +0,0 @@ -# $Id: GNUmakefile,v 1.9 2006/07/17 08:55:18 mmondor Exp $ - -#CFLAGS += -g -CFLAGS += -Wall - -JS_CFLAGS := $(shell spidermonkey-config -dc) -JS_LDFLAGS := $(shell spidermonkey-config -dl) - -PG_CFLAGS := $(shell pg_config --cppflags) -PG_LDFLAGS := $(shell pg_config --ldflags) -PG_LDFLAGS += -lpq - -OBJS := $(addprefix classes/,js_fd.o js_errno.o js_signal.o js_pgsql.o) -OBJS += js-server.o - -CFLAGS += $(JS_CFLAGS) $(PG_CFLAGS) -Iclasses -Wall -LDFLAGS += $(JS_LDFLAGS) $(PG_LDFLAGS) - - -all: js-server - -%.o: %.c - cc -c ${CFLAGS} -o $@ $< - -js-server: $(OBJS) - cc -o $@ -lc ${LDFLAGS} $(OBJS) - -clean: - rm -f js-server $(OBJS) diff --git a/tests/js-test/src/classes/js_errno.c b/tests/js-test/src/classes/js_errno.c deleted file mode 100644 index af58462..0000000 --- a/tests/js-test/src/classes/js_errno.c +++ /dev/null @@ -1,222 +0,0 @@ -/* $Id: js_errno.c,v 1.3 2005/12/12 09:55:15 mmondor Exp $ */ - -/* - * Copyright (c) 2005, Matthew Mondor - * ALL RIGHTS RESERVED. - */ - -/* - * Basic UNIX errno services for ECMAScript - */ - - - -#include - -#include -#include -#include -#include -#include - -#include - - - -/* Utility macros */ -#define QUEUE_EXCEPTION(s) do { \ - JS_SetPendingException(cx, \ - STRING_TO_JSVAL(JS_NewStringCopyZ(cx, (s)))); \ -} while (/* CONSTCOND */0) - - - -/* Prototypes */ -static JSBool errno_sm_strerror(JSContext *, JSObject *, uintN, jsval *, - jsval *); - - - -/* Actual class parameters */ -static JSClass errno_class = { - "Errno", 0, JS_PropertyStub, JS_PropertyStub, - JS_PropertyStub, JS_PropertyStub, JS_EnumerateStub, - JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub -}; - -/* Provided static methods */ -static JSFunctionSpec errno_smethods[] = { - { "strerror", errno_sm_strerror, 1, 0, 0 }, - { NULL, NULL, 0, 0, 0 } -}; - -/* - * Provided static properties. - * We use these to provide ECMAScript with the ability to use system-specific - * standard C constant macros without us having to tidiously map them - * individually, or to require other scripts to be used as headers to define - * them. Another possibility would have been to supply these parameters as - * string, but this would have required even slower remapping because of the - * parsing and string comparisions. - * We only include those which we consider necessary for now, others may be - * added easily as needed, provided that they are added in all three maps. - * I might perhaps develop macros and/or functions to map all these easily - * from a single map. - */ - -struct property_spec { - const char *name; - int value; -}; - -#define SP(n) \ - { #n, n } - -static struct property_spec errno_sprops[] = { - SP(EPERM), - SP(ENOENT), - SP(EINTR), - SP(EIO), - SP(ENXIO), - SP(EBADF), - SP(EACCES), - SP(ENOTBLK), - SP(EBUSY), - SP(EEXIST), - SP(EXDEV), - SP(ENODEV), - SP(ENOTDIR), - SP(EISDIR), - SP(EINVAL), - SP(ENFILE), - SP(EMFILE), - SP(ENOTTY), - SP(ETXTBSY), - SP(EFBIG), - SP(ENOSPC), - SP(ESPIPE), - SP(EROFS), - SP(EMLINK), - SP(EPIPE), - SP(EAGAIN), - SP(EINPROGRESS), - SP(EALREADY), - SP(ENOTSOCK), - SP(EDESTADDRREQ), - SP(EMSGSIZE), - SP(EPROTOTYPE), - SP(EPROTONOSUPPORT), - SP(EOPNOTSUPP), - SP(EPFNOSUPPORT), - SP(EAFNOSUPPORT), - SP(EADDRINUSE), - SP(EADDRNOTAVAIL), - SP(ENETDOWN), - SP(ENETUNREACH), - SP(ENETRESET), - SP(ECONNABORTED), - SP(ECONNRESET), - SP(ENOBUFS), - SP(EISCONN), - SP(ENOTCONN), - SP(ESHUTDOWN), - SP(ETIMEDOUT), - SP(ECONNREFUSED), - SP(ELOOP), - SP(ENAMETOOLONG), - SP(EHOSTDOWN), - SP(EHOSTUNREACH), - SP(ENOTEMPTY), - SP(EDQUOT), - SP(ESTALE), - SP(ENOLCK), - SP(ENOSYS), - SP(EFTYPE), - SP(ENOMSG), - SP(ENOTSUP), - SP(ECANCELED), - SP(EBADMSG), - SP(ENODATA), - SP(ETIME), - - { NULL, 0 } -}; - -#undef SP - - - -/* - * Class control functions - */ - -JSObject * -js_InitErrnoClass(JSContext *cx, JSObject *obj) -{ - JSObject *proto, *ctor; - struct property_spec *sp; - - if ((proto = JS_InitClass(cx, obj, NULL, &errno_class, NULL, - 0, NULL, NULL, NULL, errno_smethods)) == NULL) { - (void) fprintf(stderr, "Error initializing Errno class\n"); - return NULL; - } - - /* Create static properties */ - if ((ctor = JS_GetConstructor(cx, proto)) == NULL) { - (void) fprintf(stderr, "Errno: JS_GetConstructor == NULL\n"); - return NULL; - } - for (sp = errno_sprops; sp->name != NULL; sp++) { - if (JS_DefineProperty(cx, ctor, sp->name, - INT_TO_JSVAL(sp->value), NULL, NULL, - JSPROP_READONLY | JSPROP_PERMANENT) == JS_FALSE) { - (void) fprintf(stderr, - "Errno: Error defining property %s\n", sp->name); - return NULL; - } - } - - return proto; -} - - - -/* - * Static properties functions - */ - - - -/* - * Static methods - */ - -static JSBool -errno_sm_strerror(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - int error; - JSString *string; - - *rval = OBJECT_TO_JSVAL(NULL); - - if (argc != 1) { - QUEUE_EXCEPTION("Wrong number of arguments"); - return JS_FALSE; - } - if (!JSVAL_IS_INT(*argv)) { - QUEUE_EXCEPTION("Argument not an integer"); - return JS_FALSE; - } - error = (int)JSVAL_TO_INT(*argv); - - if ((string = JS_NewStringCopyZ(cx, strerror(error))) == NULL) { - QUEUE_EXCEPTION("Out of memory"); - return JS_FALSE; - } - - *rval = STRING_TO_JSVAL(string); - - return JS_TRUE; -} diff --git a/tests/js-test/src/classes/js_errno.h b/tests/js-test/src/classes/js_errno.h deleted file mode 100644 index 22b69bb..0000000 --- a/tests/js-test/src/classes/js_errno.h +++ /dev/null @@ -1,13 +0,0 @@ -/* $Id: js_errno.h,v 1.2 2005/07/19 19:25:44 mmondor Exp $ */ - -/* - * Copyright (c) 2005, Matthew Mondor - * ALL RIGHTS RESERVED. - */ - -#ifndef JSERRNO_H -#define JSERRNO_H - -extern JSObject *js_InitErrnoClass(JSContext *, JSObject *); - -#endif diff --git a/tests/js-test/src/classes/js_fd.c b/tests/js-test/src/classes/js_fd.c deleted file mode 100644 index e0f8a1a..0000000 --- a/tests/js-test/src/classes/js_fd.c +++ /dev/null @@ -1,1971 +0,0 @@ -/* $Id: js_fd.c,v 1.42 2006/07/11 10:25:51 mmondor Exp $ */ - -/* - * Copyright (c) 2005, Matthew Mondor - * ALL RIGHTS RESERVED. - */ - -/* - * Basic UNIX filedescriptor and BSD sockets services for ECMAScript - */ - -/* - * XXX TODO XXX - * - Possibly create a byte buffer type - * - Add path based restrictions to open() - * - (maybe) add restrictions to bind() - * - Either add stat() or provide equivalent properties - * - Enhance exception reports. Function name should be provided, and - * errno should be displayed when wanted. Perhaps that jsfd->error could - * also be set automatically when an exception is generated which involves - * errno. - * - Check JS_ReportError(). - * - Moving finalizer freeing and close() freeing code to a common function - * might be a good idea. - * - Add getnameinfo()/getaddrinfo() ? Or should we transparently allow this - * through properties? - * - Add send()/sendto()/sendmsg(), recv()/recvfrom()/recvmsg() ... - * - It is possible that we need to verify that obj is instance of FD in all - * methods, perhaps. I.E. consider an FD method assigned on another object. - * - Add hostname to address and address to hostname resolution facilities - * - Maybe also add properties for local end address/port of socket - * - Would be nice to experiment with a fork(2) heh. If so, if we allow - * fcntl(2) close-on-exec flag, we should make sure to mark FD objects as - * closed too for an execve(2) wrapper. - * - popen(2), would probably need to use custom execve(2) wrapper above... - * - Add support to easily restrict an application's right to functions. - * Path and mode sanity checking functions should also be written and their - * parameters set on a per-application basis. - * - Maybe virtual chdir(2) - * - A stdio FILE extension object might be nice, with stuff like fdopen() to - * create one... This would be most useful for buffered lined based input - * and output. Exporting mmfd library to js might also be nice perhaps, - * either as alternative or addition. - * - mmap(2) - how could I do this without some byte class and associated - * methods? Seems way tricky. - * - lstat(2), fstat(2) - * - dup2() - * - rename(2), unlink(2) etc would be useful, but we need another class - * for this (maybe a VFS static class?) Maybe even something calling - * execve(2) and fork(2), those primitives... popen(3) also. - * - Also opendir(3) and friends wrapper... - */ - - - -#include -#include -#include - -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include - - - -/* Utility macros */ -#define QUEUE_EXCEPTION(s) do { \ - JS_SetPendingException(cx, \ - STRING_TO_JSVAL(JS_NewStringCopyZ(cx, (s)))); \ -} while (/* CONSTCOND */0) - - -/* Internal representation of FD object */ -typedef struct jsfd { - int fd; - int type; - union { - struct { - char *path; - int flags; - mode_t mode; - } file; - struct { - int domain, type, protocol; - struct sockaddr_in caddr; - } socket; - } u; - /* For polling; Interesting events, and occurred events */ - short events, revents; - /* Last error for this descriptor */ - int error; -} jsfd_t; - -enum jsfd_types { - /* Type */ - JSFD_NONE = 0, /* Initial */ - JSFD_STD = (1 << 1), - JSFD_FILE = (1 << 2), - JSFD_SOCKET = (1 << 3) -}; - -/* Used our fd_sm_poll() function */ -struct poll_fdsi { - char *name; /* Property name or NULL */ - jsval fdobj; /* Pointer to FD object */ - jsfd_t *jsfd; /* Pointer to FD object data */ -}; - -struct poll_fds { - struct pollfd *entries; /* Passed to poll(2) */ - struct poll_fdsi *info; - int count, size; -}; - -/* Functions arguments types */ -enum jsarg_types { - JSAT_INTEGER = 1, - JSAT_DOUBLE, - JSAT_STRING, - JSAT_OBJECT -}; - - -/* Prototypes */ -static JSBool fd_constructor(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static void fd_finalize(JSContext *, JSObject *); - -static JSBool fd_getProperty(JSContext *, JSObject *, jsval, jsval *); -static JSBool fd_setProperty(JSContext *, JSObject *, jsval, jsval *); - -static JSBool fd_m_open(JSContext *, JSObject *, uintN, jsval *, jsval *); -static JSBool fd_m_set(JSContext *, JSObject *, uintN, jsval *, jsval *); -static JSBool fd_m_close(JSContext *, JSObject *, uintN, jsval *, jsval *); -static JSBool fd_m_truncate(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool fd_m_put(JSContext *, JSObject *, uintN, jsval *, jsval *); -static JSBool fd_m_get(JSContext *, JSObject *, uintN, jsval *, jsval *); -static JSBool fd_m_socket(JSContext *, JSObject *, uintN, jsval *, jsval *); -static JSBool fd_m_connect(JSContext *, JSObject *, uintN, jsval *, jsval *); -static JSBool fd_m_bind(JSContext *, JSObject *, uintN, jsval *, jsval *); -static JSBool fd_m_listen(JSContext *, JSObject *, uintN, jsval *, jsval *); -static JSBool fd_m_accept(JSContext *, JSObject *, uintN, jsval *, jsval *); -static JSBool fd_m_shutdown(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool fd_m_setsockopt(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool fd_m_getsockopt(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool fd_m_fcntl(JSContext *, JSObject *, uintN, jsval *, jsval *); -static JSBool fd_m_write(JSContext *, JSObject *, uintN, jsval *, jsval *); -static JSBool fd_m_read(JSContext *, JSObject *, uintN, jsval *, jsval *); -static JSBool fd_m_fdatasync(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool fd_m_lseek(JSContext *, JSObject *, uintN, jsval *, jsval *); -static JSBool fd_m_fchmod(JSContext *, JSObject *, uintN, jsval *, jsval *); -static JSBool fd_m_flock(JSContext *, JSObject *, uintN, jsval *, jsval *); -static JSBool fd_m_fstat(JSContext *, JSObject *, uintN, jsval *, jsval *); - -static JSBool fd_sm_poll(JSContext *, JSObject *, uintN, jsval *, jsval *); -static JSBool fd_sm_poll_mkset(JSContext *, jsval *, jsval *, void *); - -static JSBool object_iterate(JSContext *, JSObject *, void *, - JSBool (*)(JSContext *, jsval *, jsval *, void *)); -static int fd_path_allow(const char *); -static mode_t fd_mode_allow(mode_t); -static int fd_flags_allow(int); -static jsfd_t * fd_methods_args_check(JSContext *, JSObject *, const char *, - int, int, jsval *, int); - - - -/* Actual class parameters */ -static JSClass fd_class = { - "FD", JSCLASS_HAS_PRIVATE, JS_PropertyStub, JS_PropertyStub, - fd_getProperty, fd_setProperty, JS_EnumerateStub, JS_ResolveStub, - JS_ConvertStub, fd_finalize -}; - -enum fd_methods_args_enum { - FDMA_SET, - FDMA_CLOSE, - FDMA_TRUNCATE, - FDMA_GET, - FDMA_SOCKET, - FDMA_CONNECT, - FDMA_BIND, - FDMA_LISTEN, - FDMA_ACCEPT, - FDMA_SHUTDOWN, - FDMA_SETSOCKOPT, - FDMA_GETSOCKOPT, - FDMA_FCNTL, - FDMA_READ, - FDMA_WRITE, - FDMA_FDATASYNC, - FDMA_LSEEK, - FDMA_FCHMOD, - FDMA_FLOCK, - FDMA_FSTAT, - FDMA_MAX -}; - -static int fd_methods_args_array[FDMA_MAX][6] = { - { 1, JSAT_INTEGER }, /* SET */ - { 0 }, /* CLOSE */ - { 1, JSAT_DOUBLE }, /* TRUNCATE */ - { 0 }, /* GET */ - { 3, JSAT_INTEGER, JSAT_INTEGER, JSAT_INTEGER },/* SOCKET */ - { 2, JSAT_STRING, JSAT_INTEGER }, /* CONNECT */ - { 2, JSAT_STRING, JSAT_INTEGER }, /* BIND */ - { 1, JSAT_INTEGER }, /* LISTEN */ - { 0 }, /* ACCEPT */ - { 1, JSAT_INTEGER }, /* SHUTDOWN */ - { 2, JSAT_INTEGER, JSAT_INTEGER }, /* SETSOCKOPT */ - { 1, JSAT_INTEGER }, /* GETSOCKOPT */ - { 2, JSAT_INTEGER, JSAT_INTEGER }, /* FCNTL */ - { 1, JSAT_INTEGER }, /* READ */ - { 1, JSAT_STRING }, /* WRITE */ - { 0 }, /* FDATASYNC */ - { 2, JSAT_DOUBLE, JSAT_INTEGER }, /* LSEEK */ - { 1, JSAT_INTEGER }, /* FCHMOD */ - { 1, JSAT_INTEGER }, /* FLOCK */ - { 0 } /* FSTAT */ -}; - -/* Provided methods/functions */ -static JSFunctionSpec fd_methods[] = { - { "open", fd_m_open, 0, 0, 0 }, /* Variable 2-3 parameters */ - { "set", fd_m_set, 1, 0, 0 }, - { "close", fd_m_close, 0, 0, 0 }, - { "truncate", fd_m_truncate, 1, 0, 0 }, - { "put", fd_m_put, 1, 0, 0 }, - { "get", fd_m_get, 0, 0, 0 }, - { "socket", fd_m_socket, 3, 0, 0 }, - { "connect", fd_m_connect, 2, 0, 0 }, - { "bind", fd_m_bind, 2, 0, 0 }, - { "listen", fd_m_listen, 1, 0, 0 }, - { "accept", fd_m_accept, 0, 0, 0 }, - { "shutdown", fd_m_shutdown, 1, 0, 0 }, - { "setsockopt", fd_m_setsockopt, 2, 0, 0 }, - { "getsockopt", fd_m_getsockopt, 1, 0, 0 }, - { "fcntl", fd_m_fcntl, 2, 0, 0 }, - { "read", fd_m_read, 1, 0, 0 }, - { "write", fd_m_write, 1, 0, 0 }, - { "fdatasync", fd_m_fdatasync, 0, 0, 0 }, - { "lseek", fd_m_lseek, 2, 0, 0 }, - { "fchmod", fd_m_fchmod, 1, 0, 0 }, - { "flock", fd_m_flock, 1, 0, 0 }, - { "fstat", fd_m_fstat, 0, 0, 0 }, - { NULL, NULL, 0, 0, 0 } -}; - -/* Provided static methods */ -static JSFunctionSpec fd_smethods[] = { - { "poll", fd_sm_poll, 2, 0, 0 }, - { NULL, NULL, 0, 0, 0 } -}; - -/* Provided properties */ -enum fd_props { - FD_P_PATH = 0, - FD_P_FD, - FD_P_MODE, - FD_P_EVENTS, - FD_P_REVENTS, - FD_P_ERRNO, - FD_P_CLIENT_ADDR, - FD_P_CLIENT_PORT, - FD_P_MAX -}; - -static JSPropertySpec fd_properties[FD_P_MAX + 1] = { - { "path", FD_P_PATH, JSPROP_ENUMERATE | JSPROP_READONLY, NULL, NULL }, - { "fd", FD_P_FD, JSPROP_ENUMERATE | JSPROP_READONLY, NULL, NULL }, - { "mode", FD_P_MODE, JSPROP_ENUMERATE | JSPROP_READONLY, NULL, NULL }, - { "events", FD_P_EVENTS, JSPROP_ENUMERATE, NULL, NULL }, - { "revents", FD_P_REVENTS, JSPROP_ENUMERATE | JSPROP_READONLY, NULL, - NULL }, - { "errno", FD_P_ERRNO, JSPROP_ENUMERATE | JSPROP_READONLY, NULL, - NULL }, - { "client_addr", FD_P_CLIENT_ADDR, JSPROP_ENUMERATE | JSPROP_READONLY, - NULL, NULL }, - { "client_port", FD_P_CLIENT_PORT, JSPROP_ENUMERATE | JSPROP_READONLY, - NULL, NULL }, - { NULL, 0, 0, NULL, NULL } -}; - -/* - * Provided static properties. - * We use these to provide ECMAScript with the ability to use system-specific - * standard C constant macros without us having to tidiously map them - * individually, or to require other scripts to be used as headers to define - * them. Another possibility would have been to supply these parameters as - * string, but this would have required even slower remapping because of the - * parsing and string comparisions. - * We only include those which we consider necessary for now, others may be - * added easily as needed, provided that they are added in all three maps. - * I might perhaps develop macros and/or functions to map all these easily - * from a single map. - */ - -struct property_spec { - const char *name; - int value; -}; - -#define SP(n) \ - { #n, n } - -static struct property_spec fd_sprops[] = { - SP(STDIN_FILENO), - SP(STDOUT_FILENO), - SP(STDERR_FILENO), - - SP(O_RDONLY), - SP(O_WRONLY), - SP(O_RDWR), - SP(O_APPEND), - SP(O_CREAT), - SP(O_TRUNC), - SP(O_NONBLOCK), - - SP(POLLIN), - SP(POLLRDNORM), - SP(POLLRDBAND), - SP(POLLPRI), - SP(POLLOUT), - SP(POLLWRNORM), - SP(POLLWRBAND), - SP(POLLERR), - SP(POLLHUP), - SP(POLLNVAL), - - SP(SHUT_RD), - SP(SHUT_WR), - SP(SHUT_RDWR), - - SP(AF_INET), - SP(SOCK_STREAM), - SP(SOCK_DGRAM), - - SP(SO_REUSEADDR), - SP(SO_REUSEPORT), - SP(SO_KEEPALIVE), - SP(SO_DONTROUTE), - SP(SO_LINGER), - SP(SO_BROADCAST), - SP(SO_OOBINLINE), - SP(SO_SNDBUF), - SP(SO_RCVBUF), - SP(SO_SNDLOWAT), - SP(SO_RCVLOWAT), - SP(SO_SNDTIMEO), - SP(SO_RCVTIMEO), - SP(SO_TIMESTAMP), - SP(SO_TYPE), - SP(SO_ERROR), - SP(TCP_NODELAY), - - SP(F_SETFL), - SP(F_GETFL), - - SP(SEEK_SET), - SP(SEEK_CUR), - SP(SEEK_END), - - SP(S_IRWXU), - SP(S_IRUSR), - SP(S_IWUSR), - SP(S_IXUSR), - SP(S_IRWXG), - SP(S_IRGRP), - SP(S_IXGRP), - SP(S_IRWXO), - SP(S_IROTH), - SP(S_IWOTH), - SP(S_IXOTH), - SP(S_ISUID), - SP(S_ISGID), - SP(S_ISVTX), - SP(S_IFMT), - SP(S_IFIFO), - SP(S_IFCHR), - SP(S_IFDIR), - SP(S_IFBLK), - SP(S_IFREG), - SP(S_IFLNK), - SP(S_IFSOCK), - SP(S_IFWHT), - SP(UF_NODUMP), - SP(UF_IMMUTABLE), - SP(UF_APPEND), - SP(UF_OPAQUE), - SP(SF_ARCHIVED), - SP(SF_IMMUTABLE), - SP(SF_APPEND), - - SP(LOCK_SH), - SP(LOCK_EX), - SP(LOCK_NB), - SP(LOCK_UN), - - { NULL, 0 } -}; - -#undef SP - - - -/* - * Miscelaneous static globals - */ - -/* XXX Should be initialized at main process startup ideally */ -static int tcp_proto = -1; -static char *read_charbuf = NULL; -static size_t read_charbuf_size = 0; - - - -/* - * Class control functions - */ - -JSObject * -js_InitFDClass(JSContext *cx, JSObject *obj) -{ - JSObject *proto, *ctor; - struct property_spec *sp; - - if ((proto = JS_InitClass(cx, obj, NULL, &fd_class, fd_constructor, - 0, fd_properties, fd_methods, NULL, fd_smethods)) - == NULL) { - (void) fprintf(stderr, "Error initializing FD class\n"); - return NULL; - } - - /* Create static properties. Should probably be a function. */ - - if ((ctor = JS_GetConstructor(cx, proto)) == NULL) { - (void) fprintf(stderr, "FD: JS_GetConstructor == NULL\n"); - return NULL; - } - for (sp = fd_sprops; sp->name != NULL; sp++) { - if (JS_DefineProperty(cx, ctor, sp->name, - INT_TO_JSVAL(sp->value), NULL, NULL, - JSPROP_READONLY | JSPROP_PERMANENT) == JS_FALSE) { - (void) fprintf(stderr, - "FD: Error defining property %s\n", sp->name); - return NULL; - } - } - - return proto; -} - -static JSBool -fd_constructor(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - jsfd_t *jsfd = NULL; - - if (argc != 0) { - QUEUE_EXCEPTION("Wrong number of arguments"); - goto err; - } - - /* - * IMPORTANT: We must verify if the caller attempts to execute us as a - * normal function rather than as a constructor. Otherwise, the - * caller can cause the interpreter to abort(3) in an assertion in - * JS_SetPrivate()! - */ - if (!JS_IsConstructing(cx)) { - QUEUE_EXCEPTION("Constructor called as a function"); - goto err; - } - - if ((jsfd = JS_malloc(cx, sizeof(jsfd_t))) == NULL) { - QUEUE_EXCEPTION("Out of memory"); - goto err; - } - - jsfd->fd = -1; - jsfd->type = JSFD_NONE; - jsfd->events = jsfd->revents = 0; - jsfd->error = 0; - - if (!JS_SetPrivate(cx, obj, jsfd)) { - QUEUE_EXCEPTION("Internal error!"); - goto err; - } - - return JS_TRUE; - -err: - if (jsfd != NULL) - JS_free(cx, jsfd); - - *rval = OBJECT_TO_JSVAL(NULL); - return JS_FALSE; -} - -static void -fd_finalize(JSContext *cx, JSObject *obj) -{ - jsfd_t *jsfd; - - if ((jsfd = JS_GetInstancePrivate(cx, obj, &fd_class, NULL)) != NULL) { - /* Only close if not one of std descriptors */ - if (jsfd->fd != -1 && jsfd->type != JSFD_STD) { - (void) close(jsfd->fd); - jsfd->fd = -1; - jsfd->type = JSFD_NONE; - } - if (jsfd->type == JSFD_FILE && jsfd->u.file.path != NULL) { - JS_free(cx, jsfd->u.file.path); - jsfd->u.file.path = NULL; - } - JS_free(cx, jsfd); - (void) JS_SetPrivate(cx, obj, NULL); - } -} - - -/* - * Property functions - */ - -static JSBool -fd_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) -{ - jsfd_t *jsfd; - jsint p; - - if (!JSVAL_IS_INT(id)) - return JS_TRUE; - p = (int)JSVAL_TO_INT(id); - - if ((jsfd = JS_GetInstancePrivate(cx, obj, &fd_class, NULL)) == NULL) - return JS_TRUE; - if (jsfd->fd == -1) - return JS_TRUE; - - switch (p) { - case FD_P_PATH: - if (jsfd->type == JSFD_FILE) { - JSString *string; - - if ((string = JS_NewStringCopyZ(cx, - jsfd->u.file.path)) == NULL) { - QUEUE_EXCEPTION("Out of memory"); - return JS_FALSE; - } - *vp = STRING_TO_JSVAL(string); - } - break; - case FD_P_FD: - *vp = INT_TO_JSVAL(jsfd->fd); - break; - case FD_P_MODE: - if (jsfd->type == JSFD_FILE) - *vp = INT_TO_JSVAL((int)jsfd->u.file.mode); - break; - case FD_P_EVENTS: - *vp = INT_TO_JSVAL((int)jsfd->events); - break; - case FD_P_REVENTS: - *vp = INT_TO_JSVAL((int)jsfd->revents); - break; - case FD_P_ERRNO: - *vp = INT_TO_JSVAL(jsfd->error); - break; - case FD_P_CLIENT_ADDR: - if (jsfd->type == JSFD_SOCKET) { - char addr[16]; - JSString *string; - - if (inet_ntop(AF_INET, &jsfd->u.socket.caddr.sin_addr, - addr, 15) == NULL) { - QUEUE_EXCEPTION(strerror(errno)); - return JS_FALSE; - } - if ((string = JS_NewStringCopyZ(cx, addr)) == NULL) { - QUEUE_EXCEPTION("Out of memory"); - return JS_FALSE; - } - *vp = STRING_TO_JSVAL(string); - } - break; - case FD_P_CLIENT_PORT: - if (jsfd->type == JSFD_SOCKET) { - *vp = INT_TO_JSVAL((int)ntohs(jsfd-> - u.socket.caddr.sin_port)); - } - break; - } - - return JS_TRUE; -} - -static JSBool -fd_setProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) -{ - jsfd_t *jsfd; - jsint p; - - if (!JSVAL_IS_INT(id)) - return JS_TRUE; - p = (int)JSVAL_TO_INT(id); - - if ((jsfd = JS_GetInstancePrivate(cx, obj, &fd_class, NULL)) == NULL) - return JS_TRUE; - if (jsfd->fd == -1) - return JS_TRUE; - - switch (p) { - case FD_P_EVENTS: - if (!JSVAL_IS_INT(*vp)) { - QUEUE_EXCEPTION( - "FD_P_EVENTS property requires an int"); - return JS_FALSE; - } - jsfd->events = (short)JSVAL_TO_INT(*vp); - break; - } - - return JS_TRUE; -} - - -/* - * Static properties functions - */ - - -/* - * Method functions - */ - -static JSBool -fd_m_open(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - jsfd_t *jsfd; - int fd, flags; - mode_t mode = 0644; - char *bytes; - - *rval = OBJECT_TO_JSVAL(NULL); - - /* - * We use custom arguments checking here since we can accept both - * 2 or 3. - */ - if (argc < 2 || argc > 3) { - QUEUE_EXCEPTION("Wrong number of arguments"); - return JS_FALSE; - } - if (!JSVAL_IS_STRING(argv[0])) { - QUEUE_EXCEPTION("First argument must be a string"); - return JS_FALSE; - } - if (!JSVAL_IS_INT(argv[1])) { - QUEUE_EXCEPTION("Second argument must be an integer"); - return JS_FALSE; - } - if (argc == 3 && !JSVAL_IS_INT(argv[2])) { - QUEUE_EXCEPTION("Third argument must be an integer"); - return JS_FALSE; - } - if ((jsfd = JS_GetInstancePrivate(cx, obj, &fd_class, NULL)) == NULL) { - QUEUE_EXCEPTION("Null private data!"); - return JS_FALSE; - } - if (jsfd->type != JSFD_NONE) { - QUEUE_EXCEPTION("Descriptor already open"); - return JS_FALSE; - } - - if (argc == 3) { - /* - * Mode, supplied as an int. - */ - mode = (mode_t)JSVAL_TO_INT(argv[2]); - if ((mode = fd_mode_allow(mode)) == (mode_t)-1) { - QUEUE_EXCEPTION("Mode not permitted"); - return JS_FALSE; - } - } - - /* - * Flags, provided as an int. - */ - flags = JSVAL_TO_INT(argv[1]); - if ((flags = fd_flags_allow(flags)) == -1) { - QUEUE_EXCEPTION("Flag not permitted"); - return JS_FALSE; - } - - /* Path, provided as a string */ - if ((bytes = JS_GetStringBytes(JSVAL_TO_STRING(argv[0]))) == NULL) { - QUEUE_EXCEPTION("Internal error"); - return JS_FALSE; - } - /* Perform path sanity checking */ - if (fd_path_allow(bytes) == -1) { - QUEUE_EXCEPTION("Invalid path"); - return JS_FALSE; - } - if ((jsfd->u.file.path = JS_strdup(cx, bytes)) == NULL) { - QUEUE_EXCEPTION("Out of memory"); - return JS_FALSE; - } - - /* We can finally attempt to open(2) */ - if ((fd = open(bytes, flags, mode)) == -1) { - jsfd->error = errno; - /* - * XXX strerror() seems to always need to load up the - * nls table file, which is way silly for performance. - * This is related to locale stuff, and should be able - * to simply be disabled, even. - * Since this event occurs often in httpd.js, let's just - * output a fixed string for now. - * I should actually fix NetBSD libc on this matter. - */ -/* QUEUE_EXCEPTION(strerror(errno)); */ - QUEUE_EXCEPTION("open() error"); - JS_free(cx, jsfd->u.file.path); - return JS_FALSE; - } - - /* Success! */ - jsfd->fd = fd; - jsfd->type = JSFD_FILE; - jsfd->u.file.flags = flags; - jsfd->u.file.mode = mode; - - return JS_TRUE; -} - -static JSBool -fd_m_set(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - int32_t fd; - jsfd_t *jsfd; - - *rval = OBJECT_TO_JSVAL(NULL); - - if ((jsfd = fd_methods_args_check(cx, obj, "set", FDMA_SET, argc, - argv, JSFD_NONE)) == NULL) - return JS_FALSE; - - fd = JSVAL_TO_INT(*argv); - if (fd < STDIN_FILENO || fd > STDERR_FILENO) { - QUEUE_EXCEPTION("Unknown standard descriptor"); - return JS_FALSE; - } - jsfd->fd = fd; - jsfd->type = JSFD_STD; - - return JS_TRUE; -} - -static JSBool -fd_m_close(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - jsfd_t *jsfd; - int error; - - *rval = OBJECT_TO_JSVAL(NULL); - - if ((jsfd = fd_methods_args_check(cx, obj, "close", FDMA_CLOSE, argc, - argv, JSFD_STD | JSFD_FILE | JSFD_SOCKET)) == NULL) - return JS_FALSE; - - if (jsfd->type == JSFD_STD) { - jsfd->fd = -1; - jsfd->type = JSFD_NONE; - return JS_TRUE; - } - - if (jsfd->type == JSFD_FILE) { - if (jsfd->u.file.path != NULL) { - JS_free(cx, jsfd->u.file.path); - jsfd->u.file.path = NULL; - } - } - - error = close(jsfd->fd); - jsfd->fd = -1; - jsfd->type = JSFD_NONE; - - if (error == -1) { - jsfd->error = errno; - QUEUE_EXCEPTION(strerror(errno)); - return JS_FALSE; - } - - return JS_TRUE; -} - -static JSBool -fd_m_truncate(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - jsfd_t *jsfd; - off_t size; - jsdouble dsize; - - *rval = OBJECT_TO_JSVAL(NULL); - - if ((jsfd = fd_methods_args_check(cx, obj, "truncate", FDMA_TRUNCATE, - argc, argv, JSFD_FILE)) == NULL) - return JS_FALSE; - - if (!JS_ValueToNumber(cx, *argv, &dsize)) { - QUEUE_EXCEPTION("Internal error"); - return JS_FALSE; - } - size = (off_t)dsize; - - if (ftruncate(jsfd->fd, size) == -1) { - jsfd->error = errno; - QUEUE_EXCEPTION(strerror(errno)); - return JS_FALSE; - } - - return JS_TRUE; -} - -static JSBool -fd_m_put(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - jsfd_t *jsfd; - JSString *str; - char *bytes; - ssize_t size; - - *rval = OBJECT_TO_JSVAL(NULL); - - if (argc != 1) { - QUEUE_EXCEPTION("Wrong number of arguments"); - return JS_FALSE; - } - - if ((jsfd = JS_GetInstancePrivate(cx, obj, &fd_class, NULL)) == NULL) { - QUEUE_EXCEPTION("Null private data!"); - return JS_FALSE; - } - - if (jsfd->fd == -1) { - QUEUE_EXCEPTION("Descriptor closed"); - return JS_FALSE; - } - - /* - * Instead of verifying if supplied value really is a JSString, and - * using JSVAL_TO_STRING(), we convert the value to a string in this - * case. - */ - if ((str = JS_ValueToString(cx, *argv)) == NULL || - (bytes = JS_GetStringBytes(str)) == NULL) { - QUEUE_EXCEPTION("Internal error"); - return JS_FALSE; - } - - size = strlen(bytes); - if ((size = write(jsfd->fd, bytes, size)) == -1) { - jsfd->error = errno; - QUEUE_EXCEPTION(strerror(errno)); - return JS_FALSE; - } - - *rval = INT_TO_JSVAL((int)size); - - return JS_TRUE; -} - -static JSBool -fd_m_get(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - jsfd_t *jsfd; - char bytes[4096]; - ssize_t size; - JSString *string; - - *rval = OBJECT_TO_JSVAL(NULL); - - if ((jsfd = fd_methods_args_check(cx, obj, "get", FDMA_GET, argc, - argv, JSFD_STD | JSFD_FILE | JSFD_SOCKET)) == NULL) - return JS_FALSE; - - if ((size = read(jsfd->fd, bytes, 4096)) == -1) { - jsfd->error = errno; - QUEUE_EXCEPTION(strerror(errno)); - return JS_FALSE; - } - if (size == 0) - return JS_TRUE; - - if ((string = JS_NewStringCopyN(cx, bytes, size)) == NULL) { - QUEUE_EXCEPTION("Out of memory"); - return JS_FALSE; - } - *rval = STRING_TO_JSVAL(string); - - return JS_TRUE; -} - -static JSBool -fd_m_socket(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - int domain, type, protocol, error; - jsfd_t *jsfd; - - *rval = OBJECT_TO_JSVAL(NULL); - - if ((jsfd = fd_methods_args_check(cx, obj, "socket", FDMA_SOCKET, - argc, argv, JSFD_NONE)) == NULL) - return JS_FALSE; - - domain = (int)JSVAL_TO_INT(argv[0]); - type = (int)JSVAL_TO_INT(argv[1]); - protocol = (int)JSVAL_TO_INT(argv[2]); - - /* Sanity checking on currently supported protocols */ - if (domain != AF_INET || (type != SOCK_DGRAM && type != SOCK_STREAM) - || protocol != 0) { - QUEUE_EXCEPTION("Unsupported protocol"); - return JS_FALSE; - } - - if ((error = socket(domain, type, protocol)) == -1) { - jsfd->error = errno; - QUEUE_EXCEPTION(strerror(errno)); - return JS_FALSE; - } - - jsfd->fd = error; - jsfd->type = JSFD_SOCKET; - jsfd->u.socket.domain = domain; - jsfd->u.socket.type = type; - jsfd->u.socket.protocol = protocol; - - return JS_TRUE; -} - -/* - * We currently make this rather simple; If the supplied string doesn't - * consist of a valid IPv4 address, we simply attempt to resolve it, and on - * success then attempt connection. - */ -static JSBool -fd_m_connect(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - jsfd_t *jsfd; - char *address; - struct sockaddr_in sinaddr; - - *rval = OBJECT_TO_JSVAL(NULL); - - if ((jsfd = fd_methods_args_check(cx, obj, "connect", FDMA_CONNECT, - argc, argv, JSFD_SOCKET)) == NULL) - return JS_FALSE; - - address = JS_GetStringBytes(JSVAL_TO_STRING(argv[0])); - if (inet_pton(AF_INET, address, &sinaddr.sin_addr) != 1) { - struct hostent *h; - - /* - * Not a valid IPv4 address, consider it as a hostname and - * attempt to resolve it. - * XXX Note: Not thread safe unless a global mutex/rwlock is - * used. Should use getaddrinfo(3) instead. Especially if we - * someday want to support other address families than - * AF_INET. - */ - if ((h = gethostbyname(address)) == NULL) { - jsfd->error = errno; - QUEUE_EXCEPTION("Invalid address or hostname"); - return JS_FALSE; - } - sinaddr.sin_addr.s_addr = - ((struct in_addr *)h->h_addr_list[0])->s_addr; - } - sinaddr.sin_port = htons((in_port_t)JSVAL_TO_INT(argv[1])); - - if (connect(jsfd->fd, (struct sockaddr *)&sinaddr, - sizeof(struct sockaddr_in)) == -1) { - jsfd->error = errno; - QUEUE_EXCEPTION(strerror(errno)); - return JS_FALSE; - } - - return JS_TRUE; -} - -static JSBool -fd_m_bind(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - jsfd_t *jsfd; - struct sockaddr_in sinaddr; - char *address; - - *rval = OBJECT_TO_JSVAL(NULL); - - if ((jsfd = fd_methods_args_check(cx, obj, "bind", FDMA_BIND, argc, - argv, JSFD_SOCKET)) == NULL) - return JS_FALSE; - - address = JS_GetStringBytes(JSVAL_TO_STRING(argv[0])); - if (inet_pton(AF_INET, address, &sinaddr.sin_addr) != 1) { - QUEUE_EXCEPTION("Invalid IP address"); - return JS_FALSE; - } - sinaddr.sin_port = htons((in_port_t)JSVAL_TO_INT(argv[1])); - - if (bind(jsfd->fd, (struct sockaddr *)&sinaddr, - sizeof(struct sockaddr_in)) == -1) { - jsfd->error = errno; - QUEUE_EXCEPTION(strerror(errno)); - return JS_FALSE; - } - - return JS_TRUE; -} - -static JSBool -fd_m_listen(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - jsfd_t *jsfd; - - *rval = OBJECT_TO_JSVAL(NULL); - - if ((jsfd = fd_methods_args_check(cx, obj, "listen", FDMA_LISTEN, - argc, argv, JSFD_SOCKET)) == NULL) - return JS_FALSE; - - if (listen(jsfd->fd, (int)JSVAL_TO_INT(*argv)) == -1) { - jsfd->error = errno; - QUEUE_EXCEPTION(strerror(errno)); - return JS_FALSE; - } - - return JS_TRUE; -} - -static JSBool -fd_m_accept(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - jsfd_t *jsfd, *njsfd; - int sock; - struct sockaddr_in sinaddr; - socklen_t socklen; - JSObject *nobj; - - *rval = OBJECT_TO_JSVAL(NULL); - - if ((jsfd = fd_methods_args_check(cx, obj, "accept", FDMA_ACCEPT, - argc, argv, JSFD_SOCKET)) == NULL) - return JS_FALSE; - - socklen = sizeof(struct sockaddr_in); - if ((sock = accept(jsfd->fd, (struct sockaddr *)&sinaddr, &socklen)) - == -1) { - jsfd->error = errno; - QUEUE_EXCEPTION(strerror(errno)); - return JS_FALSE; - } - - /* - * Success, create new FD object, fill it and return it. - */ - if ((nobj = JS_ConstructObject(cx, &fd_class, obj, obj)) == NULL) { - (void) close(sock); - QUEUE_EXCEPTION("Out of resources"); - return JS_FALSE; - } - njsfd = JS_GetInstancePrivate(cx, nobj, &fd_class, NULL); - njsfd->fd = sock; - njsfd->type = JSFD_SOCKET; - njsfd->u.socket.domain = jsfd->u.socket.domain; - njsfd->u.socket.type = jsfd->u.socket.type; - njsfd->u.socket.protocol = jsfd->u.socket.protocol; - (void) memcpy(&njsfd->u.socket.caddr, &sinaddr, - sizeof(struct sockaddr_in)); - - *rval = OBJECT_TO_JSVAL(nobj); - - return JS_TRUE; -} - -static JSBool -fd_m_shutdown(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - jsfd_t *jsfd; - - *rval = OBJECT_TO_JSVAL(NULL); - - if ((jsfd = fd_methods_args_check(cx, obj, "shutdown", FDMA_SHUTDOWN, - argc, argv, JSFD_SOCKET)) == NULL) - return JS_FALSE; - - if (shutdown(jsfd->fd, (int)JSVAL_TO_INT(*argv)) == -1) { - jsfd->error = errno; - QUEUE_EXCEPTION(strerror(errno)); - return JS_FALSE; - } - - return JS_TRUE; -} - -/* - * XXX Not thread safe ATM as it uses a static int with test-and-set operation - * to only query the protocol database once. This could be done at early - * process initialization alternatively. - * Unlike BSD/POSIX setsockopt(2), always requires a single integer value (-1 - * in the case of SO_LINGER to disable it, or the number of seconds to - * linger to enable it). - */ -static JSBool -fd_m_setsockopt(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - jsfd_t *jsfd; - void *opt; - struct linger l; - int optname, level, optval; - socklen_t optlen; - - *rval = OBJECT_TO_JSVAL(NULL); - - if ((jsfd = fd_methods_args_check(cx, obj, "setsockopt", - FDMA_SETSOCKOPT, argc, argv, JSFD_SOCKET)) == NULL) - return JS_FALSE; - - optname = JSVAL_TO_INT(argv[0]); - optval = JSVAL_TO_INT(argv[1]); - - /* - * Work out special case for SO_LINGER - */ - if (optname == SO_LINGER) { - if (optval == -1) { - l.l_onoff = 0; - l.l_linger = 0; - } else { - l.l_onoff = 1; - l.l_linger = optval; - } - opt = &l; - optlen = sizeof(struct linger); - } else { - opt = &optval; - optlen = sizeof(int); - optval = (optval != 0 ? 1 : 0); - } - - /* - * And for TCP_NODELAY which must use tcp_proto as level - */ - if (optname == TCP_NODELAY) { - if (tcp_proto == -1) { - struct protoent *pent; - - if ((pent = getprotobyname("TCP")) != NULL) - tcp_proto = pent->p_proto; - else - tcp_proto = 4; /* Generally allright */ - } - level = tcp_proto; - } else - level = SOL_SOCKET; - - if (setsockopt(jsfd->fd, level, optname, opt, optlen) == -1) { - jsfd->error = errno; - QUEUE_EXCEPTION(strerror(errno)); - return JS_FALSE; - } - - return JS_TRUE; -} - -/* - * XXX Not thread safe ATM as it uses a static int with test-and-set operation - * to only query the protocol database once. This could be done at early - * process initialization alternatively. - * Unlike BSD/POSIX getsockopt(2), always returns a single integer value (-1 - * in the case of SO_LINGER disabled, or the number of seconds assigned to - * wait if enabled). - */ -static JSBool -fd_m_getsockopt(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - jsfd_t *jsfd; - void *opt; - struct linger l; - int i, optname, level, result; - socklen_t optlen; - - *rval = OBJECT_TO_JSVAL(NULL); - - if ((jsfd = fd_methods_args_check(cx, obj, "getsockopt", - FDMA_GETSOCKOPT, argc, argv, JSFD_SOCKET)) == NULL) - return JS_FALSE; - - optname = JSVAL_TO_INT(*argv); - - /* - * Special case for SO_LINGER which expects a structure rather than an - * integer - */ - if (optname == SO_LINGER) { - opt = &l; - optlen = sizeof(struct linger); - } else { - opt = &i; - optlen = sizeof(int); - } - - /* - * And for TCP_NODELAY which must use TCP protocol number rather than - * SOL_SOCKET level - */ - if (optname == TCP_NODELAY) { - if (tcp_proto == -1) { - struct protoent *pent; - - if ((pent = getprotobyname("TCP")) != NULL) - tcp_proto = pent->p_proto; - else - tcp_proto = 4; /* Generally allright */ - } - level = tcp_proto; - } else - level = SOL_SOCKET; - - if (getsockopt(jsfd->fd, level, optname, opt, &optlen) == -1) { - jsfd->error = errno; - QUEUE_EXCEPTION(strerror(errno)); - return JS_FALSE; - } - - /* - * To simplify the implementation, special case of SO_LINGER result; - * We return -1 if lingering is disabled, or otherwise return the - * number of seconds it should linger for maximum. - */ - if (optname == SO_LINGER) { - if (l.l_onoff != 0) - result = l.l_linger; - else - result = -1; - } else - /* - * These are booleans, so ensure proper return value despite - * several implementations which return a mask rather than 0/1 - */ - result = (i != 0 ? 1 : 0); - - *rval = INT_TO_JSVAL(result); - - return JS_TRUE; -} - -/* - * Unlike POSIX fcntl(2), only currently supports F_GETFL and F_SETFL along - * with O_NONBLOCK and O_APPEND. The flags argument is also mandatory, which - * will serve as a result mask for F_GETFL or to set wanted flags using - * F_SETFL. The previous flags are returned as usual (but will only ever - * include 0, O_NONBLOCK and/or O_APPEND). - */ -static JSBool -fd_m_fcntl(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - jsfd_t *jsfd; - int cmd, flags, error; - - *rval = INT_TO_JSVAL(0); - - if ((jsfd = fd_methods_args_check(cx, obj, "fcntl", FDMA_FCNTL, - argc, argv, JSFD_FILE | JSFD_SOCKET)) == NULL) - return JS_FALSE; - - cmd = JSVAL_TO_INT(argv[0]); - flags = JSVAL_TO_INT(argv[1]); - - if (cmd != F_GETFL && cmd != F_SETFL) { - QUEUE_EXCEPTION("Unimplemented fcntl() command"); - return JS_FALSE; - } - flags &= (O_NONBLOCK | O_APPEND); - if ((flags & O_NONBLOCK) == 0 && (flags & O_APPEND) == 0) { - QUEUE_EXCEPTION("Unimplemented fcntl() flag"); - return JS_FALSE; - } - - if (cmd == F_GETFL) { - if ((error = fcntl(jsfd->fd, cmd, NULL)) == -1) { - jsfd->error = errno; - QUEUE_EXCEPTION(strerror(errno)); - return JS_FALSE; - } - error &= flags; - } else { - if ((error = fcntl(jsfd->fd, cmd, flags)) == -1) { - jsfd->error = errno; - QUEUE_EXCEPTION(strerror(errno)); - return JS_FALSE; - } - error &= (O_NONBLOCK | O_APPEND); - } - - *rval = INT_TO_JSVAL(error); - - return JS_TRUE; -} - -static JSBool -fd_m_read(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - jsfd_t *jsfd; - size_t size; - ssize_t rsize; - JSString *string; - - *rval = OBJECT_TO_JSVAL(NULL); - - if ((jsfd = fd_methods_args_check(cx, obj, "read", FDMA_READ, - argc, argv, JSFD_FILE | JSFD_SOCKET)) == NULL) - return JS_FALSE; - - size = (size_t)JSVAL_TO_INT(*argv); - if (size < 1) { - QUEUE_EXCEPTION("read() requested size smaller than 1"); - return JS_FALSE; - } - - /* - * Ensure that our read buffer is ready, and of a large enough size - * to accomodate read. - */ - if (read_charbuf_size < size) { - if (read_charbuf == NULL) { - /* Never allocated yet, simply allocate */ - if ((read_charbuf = malloc(size)) == NULL) { - QUEUE_EXCEPTION("Cannot allocate read buffer"); - return JS_FALSE; - } - } else { - char *ptr; - - /* Buffer too small, attempt to increase it */ - if ((ptr = realloc(read_charbuf, size)) == NULL) { - QUEUE_EXCEPTION( - "Cannot reallocate read buffer"); - return JS_FALSE; - } - read_charbuf = ptr; - } - read_charbuf_size = size; - } - - if ((rsize = read(jsfd->fd, read_charbuf, size)) == -1) { - /* - * XXX Should we really throw an exception, or simply return - * an error? For instance, if using nonblocking mode and - * expecting EAGAIN, would using an exception clubber - * unnecessarily the code? - */ - jsfd->error = errno; - QUEUE_EXCEPTION(strerror(errno)); - return JS_FALSE; - } - if (size == 0) - return JS_TRUE; - - if ((string = JS_NewStringCopyN(cx, read_charbuf, rsize)) == NULL) { - QUEUE_EXCEPTION("Couldn't allocate read result string"); - return JS_FALSE; - } - *rval = STRING_TO_JSVAL(string); - - return JS_TRUE; -} - -static JSBool -fd_m_write(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - jsfd_t *jsfd; - ssize_t rsize; - JSString *str; - char *bytes; - - *rval = OBJECT_TO_JSVAL(NULL); - - if ((jsfd = fd_methods_args_check(cx, obj, "write", FDMA_WRITE, - argc, argv, JSFD_FILE | JSFD_SOCKET)) == NULL) - return JS_FALSE; - - str = JSVAL_TO_STRING(*argv); - if ((bytes = JS_GetStringBytes(str)) == NULL) { - QUEUE_EXCEPTION("Internal error"); - return JS_FALSE; - } - - if ((rsize = write(jsfd->fd, bytes, JS_GetStringLength(str))) == -1) { - jsfd->error = errno; - QUEUE_EXCEPTION(strerror(errno)); - return JS_FALSE; - } - *rval = INT_TO_JSVAL((int)rsize); - - return JS_TRUE; -} - -static JSBool -fd_m_fdatasync(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - jsfd_t *jsfd; - - *rval = OBJECT_TO_JSVAL(NULL); - - if ((jsfd = fd_methods_args_check(cx, obj, "fdatasync", - FDMA_FDATASYNC, argc, argv, JSFD_FILE)) == NULL) - return JS_FALSE; - - if (fdatasync(jsfd->fd) == -1) { - jsfd->error = errno; - QUEUE_EXCEPTION(strerror(errno)); - return JS_FALSE; - } - - return JS_TRUE; -} - -static JSBool -fd_m_lseek(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - jsfd_t *jsfd; - off_t off, newoff; - int whence; - jsdouble doff; - - *rval = OBJECT_TO_JSVAL(NULL); - - if ((jsfd = fd_methods_args_check(cx, obj, "lseek", FDMA_LSEEK, - argc, argv, JSFD_FILE)) == NULL) - return JS_FALSE; - - if (!JS_ValueToNumber(cx, argv[0], &doff)) { - QUEUE_EXCEPTION("Internal error"); - return JS_FALSE; - } - off = (off_t)doff; - whence = JSVAL_TO_INT(argv[1]); - - if ((newoff = lseek(jsfd->fd, off, whence)) == -1) { - jsfd->error = errno; - QUEUE_EXCEPTION(strerror(errno)); - return JS_FALSE; - } - - if (!JS_NewDoubleValue(cx, (jsdouble)newoff, rval)) { - QUEUE_EXCEPTION("Internal error"); - return JS_FALSE; - } - - return JS_TRUE; -} - -static JSBool -fd_m_fchmod(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - jsfd_t *jsfd; - mode_t mode; - - *rval = OBJECT_TO_JSVAL(NULL); - - if ((jsfd = fd_methods_args_check(cx, obj, "fchmod", FDMA_FCHMOD, - argc, argv, JSFD_FILE)) == NULL) - return JS_FALSE; - - mode = (mode_t)JSVAL_TO_INT(*argv); - if ((mode = fd_mode_allow(mode)) == (mode_t)-1) { - QUEUE_EXCEPTION("Mode not permitted"); - return JS_FALSE; - } - - if (fchmod(jsfd->fd, mode) == -1) { - jsfd->error = errno; - QUEUE_EXCEPTION(strerror(errno)); - return JS_FALSE; - } - - return JS_TRUE; -} - -static JSBool -fd_m_flock(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - jsfd_t *jsfd; - int op; - - *rval = OBJECT_TO_JSVAL(NULL); - - if ((jsfd = fd_methods_args_check(cx, obj, "flock", FDMA_FLOCK, - argc, argv, JSFD_FILE)) == NULL) - return JS_FALSE; - - op = JSVAL_TO_INT(*argv); - if (flock(jsfd->fd, op) == -1) { - jsfd->error = errno; - QUEUE_EXCEPTION(strerror(errno)); - return JS_FALSE; - } - - return JS_TRUE; -} - -static JSBool -fd_m_fstat(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - jsfd_t *jsfd; - struct stat st; - JSObject *array = NULL; - jsval val; - - *rval = OBJECT_TO_JSVAL(NULL); - - if ((jsfd = fd_methods_args_check(cx, obj, "fstat", FDMA_FSTAT, - argc, argv, JSFD_FILE)) == NULL) - return JS_FALSE; - - if (fstat(jsfd->fd, &st) == -1) { - jsfd->error = errno; - QUEUE_EXCEPTION(strerror(errno)); - return JS_FALSE; - } - - /* - * Note: We immediately link newly created objects to avoid GC - * problems. For the simplicity of this task we don't need an - * additional root to be created using JS_AddRoot(), since *rval - * is already rooted. Moreover, the double objects we create are - * immediately added as propery as well. - */ - - if ((array = JS_NewObject(cx, NULL, NULL, NULL)) == NULL) { - QUEUE_EXCEPTION("Out of memory"); - goto err; - } - *rval = OBJECT_TO_JSVAL(array); - -#define DEFINE_INT_PROP(n, i) do { \ - val = INT_TO_JSVAL((int)(i)); \ - if (!JS_DefineProperty(cx, array, (n), val, NULL, NULL, \ - JSPROP_ENUMERATE)) { \ - QUEUE_EXCEPTION("Internal error!"); \ - goto err; \ - } \ -} while (/* CONSTCOND */0) - -#define DEFINE_DOUBLE_PROP(n, d) do { \ - if (!JS_NewDoubleValue(cx, (jsdouble)(d), &val)) \ - goto err; \ - if (!JS_DefineProperty(cx, array, (n), val, NULL, NULL, \ - JSPROP_ENUMERATE)) { \ - QUEUE_EXCEPTION("Internal error!"); \ - goto err; \ - } \ -} while (/* CONSTCOND */0) - - DEFINE_INT_PROP("st_dev", st.st_dev); - DEFINE_INT_PROP("st_ino", st.st_ino); - DEFINE_INT_PROP("st_mode", st.st_mode); - DEFINE_INT_PROP("st_nlink", st.st_nlink); - DEFINE_INT_PROP("st_uid", st.st_uid); - DEFINE_INT_PROP("st_gid", st.st_gid); - DEFINE_INT_PROP("st_rdev", st.st_rdev); - DEFINE_DOUBLE_PROP("st_atime", st.st_atime); - DEFINE_DOUBLE_PROP("st_mtime", st.st_mtime); - DEFINE_DOUBLE_PROP("st_ctime", st.st_ctime); - DEFINE_DOUBLE_PROP("st_size", st.st_size); - DEFINE_DOUBLE_PROP("st_blocks", st.st_blocks); - DEFINE_INT_PROP("st_blksize", st.st_blksize); - DEFINE_INT_PROP("st_flags", st.st_flags); - DEFINE_INT_PROP("st_gen", st.st_gen); - -#undef DEFINE_INT_PROP -#undef DEFINE_DOUBLE_PROP - - return JS_TRUE; - -err: - *rval = OBJECT_TO_JSVAL(NULL); - - return JS_FALSE; -} - - - -/* - * Static methods - */ - -static JSBool -fd_sm_poll(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - struct poll_fds fds; - int nfds, timeout, i; - JSObject *array = NULL; - jsint index; - - *rval = OBJECT_TO_JSVAL(NULL); - - if (argc != 2) { - QUEUE_EXCEPTION("Wrong number of arguments"); - return JS_FALSE; - } - - /* - * First make sure that user supplied object really consists of an - * array. - */ - if (!JSVAL_IS_OBJECT(argv[0]) || - !JS_IsArrayObject(cx, JSVAL_TO_OBJECT(argv[0]))) { - QUEUE_EXCEPTION("First argument must be Array object"); - return JS_FALSE; - } - - /* - * Obtain timeout from argv[1] - */ - if (!JSVAL_IS_INT(argv[1])) { - QUEUE_EXCEPTION("Second argument must be timeout integer"); - return JS_FALSE; - } - timeout = (int)JSVAL_TO_INT(argv[1]); - - /* - * Create our pollfd array, iterating through all FD objects of the - * user-provided array object. - */ - if ((fds.entries = malloc(sizeof(struct pollfd) * 16)) == NULL) { - QUEUE_EXCEPTION("Out of memory"); - goto err; - } - if ((fds.info = malloc(sizeof(struct poll_fdsi) * 16)) == NULL) { - QUEUE_EXCEPTION("Out of memory"); - goto err; - } - fds.count = 0; - fds.size = 16; - if (!object_iterate(cx, JSVAL_TO_OBJECT(argv[0]), &fds, - fd_sm_poll_mkset)) - goto err; - - /* - * Finally perform actual polling - */ - if ((nfds = poll(fds.entries, fds.count, timeout)) == -1) { - QUEUE_EXCEPTION(strerror(errno)); - goto err; - } - - /* - * Now set FD objects event field and create custom array object to - * return to the caller, only holding entries for which events - * occurred. - * Link object immediately to avoid GC problems or needing - * JS_AddRoot(). - */ - if ((array = JS_NewArrayObject(cx, 0, NULL)) == NULL) { - QUEUE_EXCEPTION("Out of memory"); - goto err; - } - *rval = OBJECT_TO_JSVAL(array); - - for (i = 0, index = 0; i < fds.count && nfds != 0; i++) { - if (fds.entries[i].revents != 0) { - nfds--; - fds.info[i].jsfd->revents = fds.entries[i].revents; - /* - * Add an element if numeric index entry, or a - * property if name based/associative entry. - */ - if (fds.info[i].name == NULL) { - if (!JS_DefineElement(cx, array, index++, - fds.info[i].fdobj, NULL, NULL, - JSPROP_ENUMERATE)) { - QUEUE_EXCEPTION("Internal error!"); - goto err; - } - } else { - if (!JS_DefineProperty(cx, array, - fds.info[i].name, fds.info[i].fdobj, NULL, - NULL, JSPROP_ENUMERATE)) { - QUEUE_EXCEPTION("Internal error!"); - goto err; - } - } - } - } - - free(fds.entries); - free(fds.info); - - return JS_TRUE; - -err: - *rval = OBJECT_TO_JSVAL(NULL); - if (fds.entries != NULL) - free(fds.entries); - if (fds.info != NULL) - free(fds.info); - - return JS_FALSE; -} - -static JSBool -fd_sm_poll_mkset(JSContext *cx, jsval *id, jsval *val, void *udata) -{ - struct poll_fds *fds = (struct poll_fds *)udata; - JSObject *o; - jsfd_t *jsfd; - - if (!JSVAL_IS_OBJECT(*val) || - !JS_InstanceOf(cx, (o = JSVAL_TO_OBJECT(*val)), &fd_class, NULL)) { - QUEUE_EXCEPTION("Not FD object"); - return JS_FALSE; - } - if ((jsfd = JS_GetInstancePrivate(cx, o, &fd_class, NULL)) == NULL) { - QUEUE_EXCEPTION("Null private data!"); - return JS_FALSE; - } - - if (fds->count == fds->size) { - void *ptr; - - /* Need to grow entries and names */ - if ((ptr = realloc(fds->entries, - sizeof(struct pollfd) * fds->size * 2)) == NULL) { - QUEUE_EXCEPTION("Out of memory"); - return JS_FALSE; - } - fds->entries = ptr; - if ((ptr = realloc(fds->info, - sizeof(struct poll_fdsi) * fds->size * 2)) == NULL) { - QUEUE_EXCEPTION("Out of memory"); - return JS_FALSE; - } - fds->info = ptr; - fds->size *= 2; - } - - /* - * Add new entry. If it's a property (associative array entry), also - * fill in the name pointer which will be used to recreate the result - * array with those names as well. We set the name to NULL for index - * based array entries. - */ - fds->entries[fds->count].fd = jsfd->fd; - fds->entries[fds->count].events = jsfd->events; - fds->entries[fds->count].revents = 0; - if (JSVAL_IS_STRING(*id)) - fds->info[fds->count].name = - JS_GetStringBytes(JSVAL_TO_STRING(*id)); - else - fds->info[fds->count].name = NULL; - fds->info[fds->count].fdobj = *val; - fds->info[fds->count++].jsfd = jsfd; - - return JS_TRUE; -} - - -/* - * Utility functions - */ - -/* - * Was written to be able to iterate over all elements of an array object, - * despite being an associated array or not, or a mix of both. Unfortunately - * uses marked as private JSIdArray structure. - * This was needed because arrays are using indexes, while associative arrays - * are nothing more than an object with its properties. This function can - * deal with both. - */ -static JSBool -object_iterate(JSContext *cx, JSObject *obj, void *udata, - JSBool (*func)(JSContext *, jsval *, jsval *, void *)) -{ - JSIdArray *a; - jsval id, val; - char *name; - jsint i; - JSBool ret = JS_FALSE; - - if ((a = JS_Enumerate(cx, obj)) != NULL) { - for (i = 0; i < a->length; i++) { - JS_IdToValue(cx, a->vector[i], &id); - if (JSVAL_IS_STRING(id)) { - /* - * Property id is a string, attempt to - * lookup its value by name. - */ - name = JS_GetStringBytes(JSVAL_TO_STRING(id)); - if (!JS_LookupProperty(cx, obj, name, &val)) - continue; - } else { - /* - * Property id is a number, attempt to - * lookup its array element by index. - */ - if (!JS_LookupElement(cx, obj, - JSVAL_TO_INT(id), &val)) - continue; - } - if (!JSVAL_IS_VOID(val)) { - if (!(ret = func(cx, &id, &val, udata))) - break; - } - } - JS_DestroyIdArray(cx, a); - } - - return ret; -} - -/* - * Utility function return 0 if user supplied path should be allowed, or -1 if - * it should be rejected (invalid, or permission denied). This can for - * instance be used to restrict the program in a virtual chroot(2)-like jail. - */ -/* ARGSUSED */ -static int -fd_path_allow(const char *path) -{ - /* XXX */ - - return 0; -} - -/* ARGSUSED */ -static mode_t -fd_mode_allow(mode_t mode) -{ - /* XXX */ - - return mode; -} - -/* ARGSUSED */ -static int -fd_flags_allow(int flags) -{ - /* XXX */ - - return flags; -} - -/* - * Useful to ensure that a function's arguments are as expected, and to - * retrieve the private data associated with the FD object. Implemented to - * minimize code duplication among common functions. - */ -static jsfd_t * -fd_methods_args_check(JSContext *cx, JSObject *obj, const char *fun, int id, - int argc, jsval *argv, int type) -{ - int *p = fd_methods_args_array[id], i; - char line[1024]; - jsfd_t *jsfd; - - if (*p != argc) { - (void) snprintf(line, 1023, - "%s() - Wrong number of arguments (%d), expected %d", - fun, argc, *p); - QUEUE_EXCEPTION(line); - return NULL; - } - - for (p++, i = 0; i < argc; i++) { - switch (p[i]) { - case JSAT_INTEGER: - if (!JSVAL_IS_INT(argv[i])) { - (void) snprintf(line, 1023, - "%s() - argument #%d not an integer", - fun, i + 1); - QUEUE_EXCEPTION(line); - return NULL; - } - break; - case JSAT_DOUBLE: - if (!JSVAL_IS_DOUBLE(argv[i]) && - !JSVAL_IS_INT(argv[i])) { - (void) snprintf(line, 1023, - "%s() - argument #%d not a double", - fun, i + 1); - QUEUE_EXCEPTION(line); - return NULL; - } - break; - case JSAT_STRING: - if (!JSVAL_IS_STRING(argv[i])) { - (void) snprintf(line, 1023, - "%s() - argument #%d not a string", - fun, i + 1); - QUEUE_EXCEPTION(line); - return NULL; - } - break; - default: - (void) snprintf(line, 1023, - "%s() - Unexpected argument type #%d", - fun, i + 1); - QUEUE_EXCEPTION(line); - return NULL; - } - } - - if ((jsfd = JS_GetInstancePrivate(cx, obj, &fd_class, NULL)) - == NULL) { - (void) snprintf(line, 1023, "%s() - NULL private data!", fun); - QUEUE_EXCEPTION(line); - return NULL; - } - - if (type == JSFD_NONE && jsfd->type != JSFD_NONE) { - (void) snprintf(line, 1023, - "%s() - Descriptor is already open", - fun); - QUEUE_EXCEPTION(line); - return NULL; - } else - return jsfd; - - if ((jsfd->type & type) == 0) { - (void) snprintf(line, 1023, - "%s() - Descriptor is closed or of wrong type", - fun); - QUEUE_EXCEPTION(line); - return NULL; - } - - return jsfd; -} diff --git a/tests/js-test/src/classes/js_fd.h b/tests/js-test/src/classes/js_fd.h deleted file mode 100644 index 9ef410e..0000000 --- a/tests/js-test/src/classes/js_fd.h +++ /dev/null @@ -1,15 +0,0 @@ -/* $Id: js_fd.h,v 1.4 2006/07/11 06:45:14 mmondor Exp $ */ - -/* - * Copyright (c) 2005, Matthew Mondor - * ALL RIGHTS RESERVED. - */ - -#ifndef JSFD_H -#define JSFD_H - -#include - -extern JSObject *js_InitFDClass(JSContext *, JSObject *); - -#endif diff --git a/tests/js-test/src/classes/js_global.c b/tests/js-test/src/classes/js_global.c deleted file mode 100644 index c386b11..0000000 --- a/tests/js-test/src/classes/js_global.c +++ /dev/null @@ -1,150 +0,0 @@ -/* $Id: js_global.c,v 1.1 2006/07/22 03:43:09 mmondor Exp $ */ - -/* - * Copyright (c) 2006, Matthew Mondor - * ALL RIGHTS RESERVED. - */ - -/* - * Provide a means to set and access global objects and global properties. - * Basically we can store hashtable objects, which each can store arbitrary - * String/String tuples, but using shared memory instead of a database. - * Internally a hash table would also be used to index hash tables by String. - * There would be a single synchronization lock around the system. - * We would need to initially work with an allocated buffer of shared memory, - * which only needed pages are used. For this, mmpool(3) could be used. - * A pool of hash tables would be necessary, as well as one for the data - * pair items. To be linked among those. - * - * I yet have to find a proper interface. We optionally could have stuff - * like: - * - * Global.getProperty(table, property); - * Global.setProperty(table, property, string); - * - * But would it also be possible to use lazy allocation such that this would - * be possible, although of course enforcing the same internal behavior: - * - * Global.table.property would be read or set as necessary. - * - * Or: - * table = new Global(tablename); - * table.prop = 'string'; - * out.put(table.prop + "\n"); - */ - - - -#include - -#include -#include -#include -#include -#include - -#include - -#include - - - -#define QUEUE_EXCEPTION(s) do { \ - JS_SetPendingException(cx, \ - STRING_TO_JSVAL(JS_NewStringCopyZ(cx, (s)))); \ -} while (/* CONSTCOND */0) - - - -/* - * Static prototypes - */ -static JSBool global_constructor(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static void global_finalize(JSContext *, JSObject *); - -static JSBool global_getProperty(JSContext *, JSObject *, jsval, jsval *); -static JSBool global_setProperty(JSContext *, JSObject *, jsval, jsval *); - - - -/* - * Static globals - */ - -/* Global class */ -static JSClass pg_class = { - "Global", JCLASS_HAS_PRIVATE, JS_PropertyStub, JS_PropertyStub, - global_getProperty, global_setProperty, JS_EnumerateStub, - JS_ResolveStub, JS_ConvertStub, global_finalize -}; - - - -/* - * Global object control - */ - -JSObject * -js_InitGlobalClass(JSContext *cx, JSObject *obj) -{ - JSObject *proto, *ctor; - struct property_spec *sp; - - if ((proto = JS_InitClass(cx, obj, NULL, &global_class, - global_constructor, 0, NULL, NULL, NULL, NULL)) == NULL) { - (void) fprintf(stderr, "Error initializing Global class\n"); - goto err; - } - - /* XXX Initialize shared memory and lock */ - - return proto; - -err: - - return NULL; -} - -static JSBool -global_constructor(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - - if (argc != 1) { - QUEUE_EXCEPTION("Wrong number of arguments"); - goto err; - } - - if (!JS_IsConstructing(cx)) { - QUEUE_EXCEPTION("Constructor called as a function"); - goto err; - } - - /* XXX */ - - return JS_TRUE; - -err: - *rval = OBJECT_TO_JSVAL(NULL); - - return JS_FALSE; -} - -static JSBool -global_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) -{ - - /* XXX */ - - return JS_TRUE; -} - -static JSBool -global_setProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) -{ - - /* XXX */ - - return JS_TRUE; -} diff --git a/tests/js-test/src/classes/js_global.h b/tests/js-test/src/classes/js_global.h deleted file mode 100644 index 725ffea..0000000 --- a/tests/js-test/src/classes/js_global.h +++ /dev/null @@ -1,15 +0,0 @@ -/* $Id: js_global.h,v 1.1 2006/07/22 03:43:09 mmondor Exp $ */ - -/* - * Copyright (c) 2006, Matthew Mondor - * ALL RIGHTS RESERVED. - */ - -#ifndef JSGLOBAL_H -#define JSGLOBAL_H - -#include - -extern JSObject *js_InitGlobalClass(JSContext *, JSObject *); - -#endif diff --git a/tests/js-test/src/classes/js_mysql.c b/tests/js-test/src/classes/js_mysql.c deleted file mode 100644 index 8126f72..0000000 --- a/tests/js-test/src/classes/js_mysql.c +++ /dev/null @@ -1,6 +0,0 @@ -/* $Id: js_mysql.c,v 1.1 2006/07/09 00:30:30 mmondor Exp $ */ - -/* - * Copyright (c) 2006, Matthew Mondor - * ALL RIGHTS RESERVED. - */ diff --git a/tests/js-test/src/classes/js_mysql.h b/tests/js-test/src/classes/js_mysql.h deleted file mode 100644 index c04eee4..0000000 --- a/tests/js-test/src/classes/js_mysql.h +++ /dev/null @@ -1,13 +0,0 @@ -/* $Id: js_mysql.h,v 1.1 2006/07/09 00:30:30 mmondor Exp $ */ - -/* - * Copyright (c) 2006, Matthew Mondor - * ALL RIGHTS RESERVED. - */ - -#ifndef JSMYSQL_H -#define JSMYSQL_H - -extern JSObject *js_InitMySQLClass(JSContext *, JSObject *); - -#endif diff --git a/tests/js-test/src/classes/js_pgsql.c b/tests/js-test/src/classes/js_pgsql.c deleted file mode 100644 index e697b99..0000000 --- a/tests/js-test/src/classes/js_pgsql.c +++ /dev/null @@ -1,3493 +0,0 @@ -/* $Id: js_pgsql.c,v 1.17 2006/07/22 03:22:05 mmondor Exp $ */ - -/* - * Copyright (c) 2006, Matthew Mondor - * ALL RIGHTS RESERVED. - */ - -/* - * XXX TODO XXX - * - Verify if JS_GetStringLength() really safe to continue using - * - Perhaps provide simpler replacement functions for query functions - * allowing separate parameters to be set (perhaps not necessary considering - * that we can provide null to an array). - * - All functions creating doubles or strings should check if NULL is - * returned. Verify this. - * - (maybe) make reentrant by causing optimization buffers to be part of - * generated objects instances's private data (using structures as necessary - * instead of simply wrapping around the native object's pointer - * (actually PGconn object). - * - 28.10. Notice Processing - * Either place one(s) that use syslog(3) transparently, or somehow allow - * the user to set a custom handler - * - Large objects API - * - Compare to PHP library to verify if missing any nice functions ideas - * - See what to do about the following functions: - * - PQgetssl() (returns an SSL object!) - * - PQprint() (writes to a supplied FILE *) - */ - - - -#include - -#include -#include -#include -#include -#include - -#include - -#include - -#include - - - -/* - * PostgreSQL services for ECMAScript - * - * NOTES: - * We create a parent PG object which allows us to store static first-level - * methods as well as numeric properties required to work with the libpq - * library. Almost all other functionality is available through PGconn and - * PGresult objects afterwards. - * - * If supporting the asynchroneous part of the API, it should also be possible - * for us to return an FD object for a PGconn * so that polling could be used, - * etc. Or at least just return the fd int which can be used easily to create - * an FD object with afterwards by the caller. - */ - - - -#define QUEUE_EXCEPTION(s) do { \ - JS_SetPendingException(cx, \ - STRING_TO_JSVAL(JS_NewStringCopyZ(cx, (s)))); \ -} while (/* CONSTCOND */0) - - -struct property_spec { - const char *name; - int value; -}; - - - -/* - * Static prototypes - */ -static int buffer_grow(size_t); -static int param_grow(int); - -static JSBool pg_constructor(JSContext *, JSObject *, uintN, jsval *, - jsval *); - -static JSBool pg_sm_PQconndefaults(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool pg_sm_PQconnectdb(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool pg_sm_PQconnectStart(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool pg_sm_PQresStatus(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool pg_sm_PQunescapeBytea(JSContext *, JSObject *, uintN, jsval *, - jsval *); - -static JSObject *js_InitPGconnClass(JSContext *, JSObject *); -static JSBool pgconn_constructor(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static void pgconn_finalize(JSContext *, JSObject *); - -static JSBool pgconn_m_PQfinish(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool pgconn_m_PQconnectPoll(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool pgconn_m_PQreset(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool pgconn_m_PQresetStart(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool pgconn_m_PQresetPoll(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool pgconn_m_PQdb(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool pgconn_m_PQuser(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool pgconn_m_PQpass(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool pgconn_m_PQhost(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool pgconn_m_PQport(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool pgconn_m_PQtty(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool pgconn_m_PQoptions(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool pgconn_m_PQstatus(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool pgconn_m_PQtransactionStatus(JSContext *, JSObject *, uintN, - jsval *, jsval *); -static JSBool pgconn_m_PQparameterStatus(JSContext *, JSObject *, uintN, - jsval *, jsval *); -static JSBool pgconn_m_PQprotocolVersion(JSContext *, JSObject *, uintN, - jsval *, jsval *); -static JSBool pgconn_m_PQserverVersion(JSContext *, JSObject *, uintN, - jsval *, jsval *); -static JSBool pgconn_m_PQerrorMessage(JSContext *, JSObject *, uintN, - jsval *, jsval *); -static JSBool pgconn_m_PQsocket(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool pgconn_m_PQbackendPID(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool pgconn_m_PQexec(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool pgconn_m_PQsendQuery(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool pgconn_m_PQexecParams2(JSContext *, JSObject *, uintN, jsval *, - jsval *, int); -static JSBool pgconn_m_PQexecParams(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool pgconn_m_PQsendQueryParams(JSContext *, JSObject *, uintN, - jsval *, jsval *); -static JSBool pgconn_m_PQprepare2(JSContext *, JSObject *, uintN, jsval *, - jsval *, int); -static JSBool pgconn_m_PQprepare(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool pgconn_m_PQsendPrepare(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool pgconn_m_PQexecPrepared2(JSContext *, JSObject *, uintN, - jsval *, jsval *, int); -static JSBool pgconn_m_PQexecPrepared(JSContext *, JSObject *, uintN, - jsval *, jsval *); -static JSBool pgconn_m_PQsendQueryPrepared(JSContext *, JSObject *, uintN, - jsval *, jsval *); -static JSBool pgconn_m_PQmakeEmptyPGresult(JSContext *, JSObject *, uintN, - jsval *, jsval *); -static JSBool pgconn_m_PQescapeStringConn(JSContext *, JSObject *, uintN, - jsval *, jsval *); -static JSBool pgconn_m_PQescapeByteaConn(JSContext *, JSObject *, uintN, - jsval *, jsval *); -static JSBool pgconn_m_PQgetCancel(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool pgconn_m_PQnotifies(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool pgconn_m_PQgetResult(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool pgconn_m_PQconsumeInput(JSContext *, JSObject *, uintN, - jsval *, jsval *); -static JSBool pgconn_m_PQisBusy(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool pgconn_m_PQsetnonblocking(JSContext *, JSObject *, uintN, - jsval *, jsval *); -static JSBool pgconn_m_PQisnonblocking(JSContext *, JSObject *, uintN, - jsval *, jsval *); -static JSBool pgconn_m_PQflush(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool pgconn_m_PQsetErrorVerbosity(JSContext *, JSObject *, uintN, - jsval *, jsval *); -static JSBool pgconn_m_PQtrace(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool pgconn_m_PQuntrace(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool pgconn_m_PQputCopyData(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool pgconn_m_PQputCopyEnd(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool pgconn_m_PQgetCopyData(JSContext *, JSObject *, uintN, jsval *, - jsval *); - -static JSObject *js_InitPGresultClass(JSContext *, JSObject *); -static JSBool pgresult_constructor(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static void pgresult_finalize(JSContext *, JSObject *); - -static JSBool pgresult_m_PQclear(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool pgresult_m_PQresultStatus(JSContext *, JSObject *, uintN, - jsval *, jsval *); -static JSBool pgresult_m_PQresultErrorMessage(JSContext *, JSObject *, uintN, - jsval *, jsval *); -static JSBool pgresult_m_PQresultErrorField(JSContext *, JSObject *, uintN, - jsval *, jsval *); -static JSBool pgresult_m_PQntuples(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool pgresult_m_PQnfields(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool pgresult_m_PQfname(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool pgresult_m_PQfnumber(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool pgresult_m_PQftable(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool pgresult_m_PQftablecol(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool pgresult_m_PQfformat(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool pgresult_m_PQftype(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool pgresult_m_PQfmod(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool pgresult_m_PQfsize(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool pgresult_m_PQbinaryTuples(JSContext *, JSObject *, uintN, - jsval *, jsval *); -static JSBool pgresult_m_PQgetvalue(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool pgresult_m_PQgetisnull(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool pgresult_m_PQgetlength(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool pgresult_m_PQcmdStatus(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool pgresult_m_PQcmdTuples(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool pgresult_m_PQoidValue(JSContext *, JSObject *, uintN, jsval *, - jsval *); - -static JSObject *js_InitPGcancelClass(JSContext *, JSObject *); -static JSBool pgcancel_constructor(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static void pgcancel_finalize(JSContext *, JSObject *); - -static JSBool pgcancel_m_PQfreeCancel(JSContext *, JSObject *, uintN, - jsval *, jsval *); -static JSBool pgcancel_m_PQcancel(JSContext *, JSObject *, uintN, jsval *, - jsval *); - - - -/* - * Static globals - */ - -/* - * General purpose string buffer (note that this is not thread-safe). - * Allows to optimize functions such as PQescapeStringConn(). - */ -static char *buffer = NULL; -static size_t buffer_size = 0; -static Oid *param_types = NULL; -static char **param_values = NULL; -static int *param_lengths = NULL; -static int *param_formats = NULL; -static int param_entries = 0; - -/* PG class */ -static JSClass pg_class = { - "PG", 0, JS_PropertyStub, JS_PropertyStub, - JS_PropertyStub, JS_PropertyStub, JS_EnumerateStub, JS_ResolveStub, - JS_ConvertStub, JS_FinalizeStub -}; - -/* Provided static methods */ -static JSFunctionSpec pg_smethods[] = { - { "connDefaults", pg_sm_PQconndefaults, 0, 0, 0 }, - { "connectDb", pg_sm_PQconnectdb, 1, 0, 0 }, - { "connectStart", pg_sm_PQconnectStart, 1, 0, 0 }, - { "resStatus", pg_sm_PQresStatus, 1, 0, 0 }, - { "unescapeBytea", pg_sm_PQunescapeBytea, 2, 0, 0 }, - { NULL, NULL, 0, 0, 0 } -}; - -/* Provided static properties */ - -#define SP(n) \ - { #n, n } - -static struct property_spec pg_sprops[] = { - SP(PGRES_POLLING_OK), - SP(PGRES_POLLING_READING), - SP(PGRES_POLLING_WRITING), - SP(PGRES_POLLING_FAILED), - SP(PGRES_EMPTY_QUERY), - SP(PGRES_COMMAND_OK), - SP(PGRES_TUPLES_OK), - SP(PGRES_COPY_OUT), - SP(PGRES_COPY_IN), - SP(PGRES_BAD_RESPONSE), - SP(PGRES_NONFATAL_ERROR), - SP(PGRES_FATAL_ERROR), - SP(PG_DIAG_SEVERITY), - SP(PG_DIAG_SQLSTATE), - SP(PG_DIAG_MESSAGE_PRIMARY), - SP(PG_DIAG_MESSAGE_DETAIL), - SP(PG_DIAG_MESSAGE_HINT), - SP(PG_DIAG_STATEMENT_POSITION), - SP(PG_DIAG_INTERNAL_POSITION), - SP(PG_DIAG_INTERNAL_QUERY), - SP(PG_DIAG_CONTEXT), - SP(PG_DIAG_SOURCE_FILE), - SP(PG_DIAG_SOURCE_LINE), - SP(PG_DIAG_SOURCE_FUNCTION), - SP(CONNECTION_OK), - SP(CONNECTION_BAD), - SP(CONNECTION_STARTED), - SP(CONNECTION_MADE), - SP(CONNECTION_AWAITING_RESPONSE), - SP(CONNECTION_AUTH_OK), - SP(CONNECTION_SSL_STARTUP), - SP(CONNECTION_SETENV), - SP(PQTRANS_IDLE), - SP(PQTRANS_ACTIVE), - SP(PQTRANS_INTRANS), - SP(PQTRANS_INERROR), - SP(PQTRANS_UNKNOWN), - SP(InvalidOid), - SP(PQERRORS_TERSE), - SP(PQERRORS_DEFAULT), - SP(PQERRORS_VERBOSE), - { NULL, 0 } -}; - -#undef SP - - -/* PGconn class */ -static JSClass pgconn_class = { - "PGConn", JSCLASS_HAS_PRIVATE, JS_PropertyStub, JS_PropertyStub, - JS_PropertyStub, JS_PropertyStub, JS_EnumerateStub, - JS_ResolveStub, JS_ConvertStub, pgconn_finalize -}; - -/* Provided methods/functions */ -static JSFunctionSpec pgconn_methods[] = { - { "finish", pgconn_m_PQfinish, 0, 0, 0 }, - { "connectPoll", pgconn_m_PQconnectPoll, 0, 0, 0 }, - { "reset", pgconn_m_PQreset, 0, 0, 0 }, - { "resetStart", pgconn_m_PQresetStart, 0, 0, 0 }, - { "resetPoll", pgconn_m_PQresetPoll, 0, 0, 0 }, - { "db", pgconn_m_PQdb, 0, 0, 0 }, - { "user", pgconn_m_PQuser, 0, 0, 0 }, - { "pass", pgconn_m_PQpass, 0, 0, 0 }, - { "host", pgconn_m_PQhost, 0, 0, 0 }, - { "port", pgconn_m_PQport, 0, 0, 0 }, - { "tty", pgconn_m_PQtty, 0, 0, 0 }, - { "options", pgconn_m_PQoptions, 0, 0, 0 }, - { "status", pgconn_m_PQstatus, 0, 0, 0 }, - { "transactionStatus", pgconn_m_PQtransactionStatus, 0, 0, 0 }, - { "parameterStatus", pgconn_m_PQparameterStatus, 1, 0, 0 }, - { "protocolVersion", pgconn_m_PQprotocolVersion, 0, 0, 0 }, - { "serverVersion", pgconn_m_PQserverVersion, 0, 0, 0 }, - { "errorMessage", pgconn_m_PQerrorMessage, 0, 0, 0 }, - { "socket", pgconn_m_PQsocket, 0, 0, 0 }, - { "backendPid", pgconn_m_PQbackendPID, 0, 0, 0 }, - { "exec", pgconn_m_PQexec, 1, 0, 0 }, - { "sendQuery", pgconn_m_PQsendQuery, 1, 0, 0 }, - { "execParams", pgconn_m_PQexecParams, 7, 0, 0 }, - { "sendQueryParams", pgconn_m_PQsendQueryParams, 7, 0, 0 }, - { "prepare", pgconn_m_PQprepare, 4, 0, 0 }, - { "sendPrepare", pgconn_m_PQsendPrepare, 4, 0, 0 }, - { "execPrepared", pgconn_m_PQexecPrepared, 6, 0, 0 }, - { "sendQueryPrepared", pgconn_m_PQsendQueryPrepared, 6, 0, 0 }, - { "makeEmptyPGResult", pgconn_m_PQmakeEmptyPGresult, 1, 0, 0 }, - { "escapeStringConn", pgconn_m_PQescapeStringConn, 1, 0, 0 }, - { "escapeByteaConn", pgconn_m_PQescapeByteaConn, 1, 0, 0 }, - { "getCancel", pgconn_m_PQgetCancel, 0, 0, 0 }, - { "notifies", pgconn_m_PQnotifies, 0, 0, 0 }, - { "getResult", pgconn_m_PQgetResult, 0, 0, 0 }, - { "consumeInput", pgconn_m_PQconsumeInput, 0, 0, 0 }, - { "isBusy", pgconn_m_PQisBusy, 0, 0, 0 }, - { "setNonBlocking", pgconn_m_PQsetnonblocking, 1, 0, 0 }, - { "isNonBlocking", pgconn_m_PQisnonblocking, 0, 0, 0 }, - { "flush", pgconn_m_PQflush, 0, 0, 0 }, - { "setErrorVerbosity", pgconn_m_PQsetErrorVerbosity, 1, 0, 0 }, - { "trace", pgconn_m_PQtrace, 0, 0, 0 }, - { "untrace", pgconn_m_PQuntrace, 0, 0, 0 }, - { "putCopyData", pgconn_m_PQputCopyData, 1, 0, 0 }, - { "putCopyEnd", pgconn_m_PQputCopyEnd, 1, 0, 0 }, - { "getCopyData", pgconn_m_PQgetCopyData, 1, 0, 0 }, - { NULL, NULL, 0, 0, 0 } -}; - - -/* PGresult class */ -static JSClass pgresult_class = { - "PGResult", JSCLASS_HAS_PRIVATE, JS_PropertyStub, JS_PropertyStub, - JS_PropertyStub, JS_PropertyStub, JS_EnumerateStub, - JS_ResolveStub, JS_ConvertStub, pgresult_finalize -}; - -/* Provided methods/functions */ -static JSFunctionSpec pgresult_methods[] = { - { "clear", pgresult_m_PQclear, 0, 0, 0 }, - { "resultStatus", pgresult_m_PQresultStatus, 0, 0, 0 }, - { "resultErrorMessage", pgresult_m_PQresultErrorMessage, 0, 0, 0 }, - { "resultErrorField", pgresult_m_PQresultErrorField, 1, 0, 0 }, - { "nTuples", pgresult_m_PQntuples, 0, 0, 0 }, - { "nFields", pgresult_m_PQnfields, 0, 0, 0 }, - { "fName", pgresult_m_PQfname, 1, 0, 0 }, - { "fNumber", pgresult_m_PQfnumber, 1, 0, 0 }, - { "fTable", pgresult_m_PQftable, 1, 0, 0 }, - { "fTableCol", pgresult_m_PQftablecol, 1, 0, 0 }, - { "fFormat", pgresult_m_PQfformat, 1, 0, 0 }, - { "fType", pgresult_m_PQftype, 1, 0, 0 }, - { "fMod", pgresult_m_PQfmod, 1, 0, 0 }, - { "fSize", pgresult_m_PQfsize, 1, 0, 0 }, - { "binaryTuples", pgresult_m_PQbinaryTuples, 1, 0, 0 }, - { "getValue", pgresult_m_PQgetvalue, 2, 0, 0 }, - { "getIsNull", pgresult_m_PQgetisnull, 2, 0, 0 }, - { "getLength", pgresult_m_PQgetlength, 2, 0, 0 }, - { "cmdStatus", pgresult_m_PQcmdStatus, 0, 0, 0 }, - { "cmdTuples", pgresult_m_PQcmdTuples, 0, 0, 0 }, - { "oidValue", pgresult_m_PQoidValue, 0, 0, 0 }, - { NULL, NULL, 0, 0, 0 } -}; - - -/* PGcancel class */ -static JSClass pgcancel_class = { - "PGCancel", JSCLASS_HAS_PRIVATE, JS_PropertyStub, JS_PropertyStub, - JS_PropertyStub, JS_PropertyStub, JS_EnumerateStub, - JS_ResolveStub, JS_ConvertStub, pgcancel_finalize -}; - -/* Provided methods/functions */ -static JSFunctionSpec pgcancel_methods[] = { - { "freeCancel", pgcancel_m_PQfreeCancel, 0, 0, 0 }, - { "cancel", pgcancel_m_PQcancel, 1, 0, 0 }, - { NULL, NULL, 0, 0, 0 } -}; - - - -static int -buffer_grow(size_t required) -{ - size_t new; - void *ptr; - - if (required <= buffer_size) - return 0; - - for (new = buffer_size; new < required; new *= 2) ; - - if ((ptr = realloc(buffer, new)) == NULL) - return -1; - - buffer = ptr; - buffer_size = new; - - return 0; -} - -static int -param_grow(int required) -{ - int new; - void *types, *values, *lengths, *formats; - - if (required <= param_entries) - return 0; - - for (new = param_entries; new < required; new *= 2) ; - - if ((types = realloc(param_types, sizeof(Oid) * new)) == NULL || - (values = realloc(param_values, sizeof(char *) * new)) == NULL || - (lengths = realloc(param_lengths, sizeof(int) * new)) == NULL || - (formats = realloc(param_formats, sizeof(int) * new)) == NULL) - return -1; - - param_types = types; - param_values = values; - param_lengths = lengths; - param_formats = formats; - param_entries = new; - - return 0; -} - - -/* - * PG object control - */ - -JSObject * -js_InitPGClass(JSContext *cx, JSObject *obj) -{ - JSObject *proto, *ctor; - struct property_spec *sp; - - if ((proto = JS_InitClass(cx, obj, NULL, &pg_class, pg_constructor, 0, - NULL, NULL, NULL, pg_smethods)) == NULL) { - (void) fprintf(stderr, "Error initializing PG class\n"); - goto err; - } - - /* Create static properties */ - if ((ctor = JS_GetConstructor(cx, proto)) == NULL) { - (void) fprintf(stderr, "PG: JS_GetConstructor == NULL\n"); - goto err; - } - for (sp = pg_sprops; sp->name != NULL; sp++) { - if (!JS_DefineProperty(cx, ctor, sp->name, - INT_TO_JSVAL(sp->value), NULL, NULL, - JSPROP_READONLY | JSPROP_PERMANENT)) { - (void) fprintf(stderr, - "PG: Error defining property %s\n", sp->name); - goto err; - } - } - - /* Initialize PGconn class since we'll need to instanciate it from C */ - if (js_InitPGconnClass(cx, obj) == NULL) { - (void) fprintf(stderr, "PG: InitPGconnClass()\n"); - goto err; - } - /* Same for PGresult class */ - if (js_InitPGresultClass(cx, obj) == NULL) { - (void) fprintf(stderr, "PG: InitPGresultClass()\n"); - goto err; - } - /* And PGcancel class */ - if (js_InitPGcancelClass(cx, obj) == NULL) { - (void) fprintf(stderr, "PG: InitPGcancelClass()\n"); - goto err; - } - - /* - * Note that the following buffers, although allowing optimizations, - * cause the functions using them to not be reentrant. For reentrancy - * similar buffers could be attached to the object instances instead. - * This would of course however mean a larger memory footprint. - * If doing this, we would also need a custom structure for the - * private data instead of simply wrapping around the native pointers. - */ - - /* Allocate an initial general purpose buffer */ - if ((buffer = malloc(16384)) == NULL) { - (void) fprintf(stderr, "PG: malloc()\n"); - goto err; - } - buffer_size = 16384; - - /* As well as buffers for the *Params() parameter arrays */ - if ((param_types = malloc(sizeof(Oid) * 16)) == NULL || - (param_values = malloc(sizeof(char *) * 16)) == NULL || - (param_lengths = malloc(sizeof(int) * 16)) == NULL || - (param_formats = malloc(sizeof(int) * 16)) == NULL) { - (void) fprintf(stderr, "PG: malloc()\n"); - goto err; - } - param_entries = 16; - - return proto; - -err: - if (buffer != NULL) - free(buffer); - if (param_types != NULL) - free(param_types); - if (param_values != NULL) - free(param_values); - if (param_lengths != NULL) - free(param_lengths); - if (param_formats != NULL) - free(param_formats); - - return NULL; -} - -/* Non instanciable */ -static JSBool -pg_constructor(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - - QUEUE_EXCEPTION("PG class uninstanciable"); - - return JS_FALSE; -} - - -/* - * PG object static methods - */ - -/* - * Returns an array of objects which each contain the various parameters - * returned by PQconndefaults(). - * XXX Could be more useful if it returned an object of objects using the - * keyword as property in the first object level, perhaps. - */ -static JSBool -pg_sm_PQconndefaults(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - PQconninfoOption *in = NULL, *p; - JSObject *array = NULL; - JSString *str; - int i; - - if (argc != 0) { - QUEUE_EXCEPTION("Wrong number of arguments"); - return JS_FALSE; - } - - if ((in = PQconndefaults()) == NULL) { - QUEUE_EXCEPTION("PQconndefaults() == NULL"); - goto err; - } - if ((array = JS_NewArrayObject(cx, 0, NULL)) == NULL) { - QUEUE_EXCEPTION("Out of memory"); - goto err; - } - /* Root immediately */ - *rval = OBJECT_TO_JSVAL(array); - -#define DEFINE_STRING_PROP(n, s) do { \ - if ((str = JS_NewStringCopyZ(cx, (char *)(s))) == NULL) { \ - QUEUE_EXCEPTION("Out of memory!"); \ - goto err; \ - } \ - if (!JS_DefineProperty(cx, o, (n), STRING_TO_JSVAL(str), NULL, \ - NULL, JSPROP_ENUMERATE)) { \ - QUEUE_EXCEPTION("Internal error!"); \ - goto err; \ - } \ -} while (/* CONSTCOND */0) - -#define DEFINE_STRING_PROP2(n, s, l) do { \ - if ((str = JS_NewStringCopyN(cx, (char *)(s), (l))) == NULL) { \ - QUEUE_EXCEPTION("Out of memory!"); \ - goto err; \ - } \ - if (!JS_DefineProperty(cx, o, (n), STRING_TO_JSVAL(str), NULL, \ - NULL, JSPROP_ENUMERATE)) { \ - QUEUE_EXCEPTION("Internal error!"); \ - goto err; \ - } \ -} while (/* CONSTCOND */0) - -#define DEFINE_INT_PROP(n, i) do { \ - if (!JS_DefineProperty(cx, array, (n), INT_TO_JSVAL((int)(i)), \ - NULL, NULL, JSPROP_ENUMERATE)) { \ - QUEUE_EXCEPTION("Internal error!"); \ - goto err; \ - } \ -} while (/* CONSTCOND */0) - - - /* Polulate array with objects */ - for (i = 0, p = in; p->keyword != NULL; p++, i++) { - JSObject *o; - - if ((o = JS_NewObject(cx, NULL, NULL, NULL)) == NULL) { - QUEUE_EXCEPTION("Out of memory"); - goto err; - } - /* Root immediately by inserting object into array */ - if (!JS_DefineElement(cx, array, i, OBJECT_TO_JSVAL(o), - NULL, NULL, JSPROP_ENUMERATE)) { - QUEUE_EXCEPTION("Internal error!"); - goto err; - } - - /* Populate object with properties */ - DEFINE_STRING_PROP("keyword", p->keyword); - DEFINE_STRING_PROP("envvar", p->envvar); - DEFINE_STRING_PROP("compiled", p->compiled); - DEFINE_STRING_PROP("val", p->val); - DEFINE_STRING_PROP("label", p->label); - DEFINE_STRING_PROP2("dispchar", p->dispchar, 1); - DEFINE_INT_PROP("dispsize", p->dispsize); - } - -#undef DEFINE_STRING_PROP -#undef DEFINE_STRING_PROP2 -#undef DEFINE_INT_PROP - - PQconninfoFree(in); - - return JS_TRUE; - -err: - *rval = OBJECT_TO_JSVAL(NULL); - if (in != NULL) - PQconninfoFree(in); - - return JS_FALSE; -} - -static JSBool -pg_sm_PQconnectdb(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - JSObject *o; - PGconn *pgc = NULL; - char *str; - - if (argc != 1) { - QUEUE_EXCEPTION("Wrong number of arguments"); - goto err; - } - if (!JSVAL_IS_STRING(argv[0])) { - QUEUE_EXCEPTION("Argument not a String"); - goto err; - } - str = JS_GetStringBytes(JSVAL_TO_STRING(argv[0])); - - if ((o = JS_NewObject(cx, &pgconn_class, NULL, NULL)) == NULL) { - QUEUE_EXCEPTION("Internal error!"); - goto err; - } - /* Root immediately */ - *rval = OBJECT_TO_JSVAL(o); - - if ((pgc = PQconnectdb(str)) == NULL) { - QUEUE_EXCEPTION("PQconnectdb"); - goto err; - } - if (!JS_SetPrivate(cx, o, pgc)) { - QUEUE_EXCEPTION("Internal error!"); - goto err; - } - - return JS_TRUE; - -err: - if (pgc != NULL) - PQfinish(pgc); - - *rval = OBJECT_TO_JSVAL(NULL); - - return JS_FALSE; -} - -static JSBool -pg_sm_PQconnectStart(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - JSObject *o; - PGconn *pgc = NULL; - char *str; - - if (argc != 1) { - QUEUE_EXCEPTION("Wrong number of arguments"); - goto err; - } - if (!JSVAL_IS_STRING(argv[0])) { - QUEUE_EXCEPTION("Argument not a String"); - goto err; - } - str = JS_GetStringBytes(JSVAL_TO_STRING(argv[0])); - - if ((o = JS_NewObject(cx, &pgconn_class, NULL, NULL)) == NULL) { - QUEUE_EXCEPTION("Internal error!"); - goto err; - } - /* Root immediately */ - *rval = OBJECT_TO_JSVAL(o); - - if ((pgc = PQconnectStart(str)) == NULL) { - QUEUE_EXCEPTION("PQconnectStart"); - goto err; - } - if (!JS_SetPrivate(cx, o, pgc)) { - QUEUE_EXCEPTION("Internal error!"); - goto err; - } - - return JS_TRUE; - -err: - if (pgc != NULL) - PQfinish(pgc); - - *rval = OBJECT_TO_JSVAL(NULL); - - return JS_FALSE; -} - -static JSBool -pg_sm_PQresStatus(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - char *res; - - if (argc != 1) { - QUEUE_EXCEPTION("Wrong number of arguments"); - goto err; - } - if (!JSVAL_IS_INT(argv[0])) { - QUEUE_EXCEPTION("Argument not an int"); - goto err; - } - - res = PQresStatus(JSVAL_TO_INT(argv[0])); - *rval = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, res)); - - return JS_TRUE; - -err: - *rval = OBJECT_TO_JSVAL(NULL); - - return JS_FALSE; -} - -/* - * Note: Semantics are different from C PQunescapeBytea() in that it is - * supplied a single String and that it returns a resulting String, or null on - * error. Much easier to work with within ECMAScript this way. - */ -static JSBool -pg_sm_PQunescapeBytea(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - PGconn *pgc; - JSString *str; - char *from; - size_t reslen; - unsigned char *res = NULL; - - if (argc != 1) { - QUEUE_EXCEPTION("Wrong number of arguments"); - goto err; - } - if (!JSVAL_IS_STRING(argv[0])) { - QUEUE_EXCEPTION("Argument not a String"); - goto err; - } - - pgc = JS_GetInstancePrivate(cx, obj, &pgconn_class, NULL); - assert(pgc != NULL); - - from = JS_GetStringBytes(JSVAL_TO_STRING(argv[0])); - - if ((res = PQunescapeBytea(from, &reslen)) == NULL) { - QUEUE_EXCEPTION("PQescapeByteaConn()"); - goto err; - } - - if ((str = JS_NewStringCopyN(cx, res, reslen)) == NULL) { - QUEUE_EXCEPTION("Out of memory!"); - goto err; - } - - PQfreemem(res); - *rval = STRING_TO_JSVAL(str); - - return JS_TRUE; - -err: - if (res != NULL) - PQfreemem(res); - - *rval = OBJECT_TO_JSVAL(NULL); - - return JS_FALSE; -} - - -/* - * PGconn object control - */ - -static JSObject * -js_InitPGconnClass(JSContext *cx, JSObject *obj) -{ - - return (JS_InitClass(cx, obj, NULL, &pgconn_class, pgconn_constructor, - 0, NULL, pgconn_methods, NULL, NULL)); -} - -/* Non instanciable */ -static JSBool -pgconn_constructor(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - - QUEUE_EXCEPTION("PGconn class not user-instanciable"); - - return JS_FALSE; -} - -static void -pgconn_finalize(JSContext *cx, JSObject *obj) -{ - PGconn *pgc; - - if ((pgc = JS_GetInstancePrivate(cx, obj, &pgconn_class, NULL)) - != NULL) { - PQfinish(pgc); - (void) JS_SetPrivate(cx, obj, NULL); - } -} - - -/* - * PGconn object methods - */ - -static JSBool -pgconn_m_PQfinish(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - PGconn *pgc; - - if (argc != 0) { - QUEUE_EXCEPTION("Function allows no arguments"); - *rval = OBJECT_TO_JSVAL(NULL); - return JS_FALSE; - } - - if ((pgc = JS_GetInstancePrivate(cx, obj, &pgconn_class, NULL)) - != NULL) { - PQfinish(pgc); - (void) JS_SetPrivate(cx, obj, NULL); - } - - *rval = OBJECT_TO_JSVAL(NULL); - - return JS_TRUE; -} - -static JSBool -pgconn_m_PQconnectPoll(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - PGconn *pgc; - PostgresPollingStatusType s; - - if (argc != 0) { - QUEUE_EXCEPTION("Function allows no arguments"); - *rval = OBJECT_TO_JSVAL(NULL); - return JS_FALSE; - } - - pgc = JS_GetInstancePrivate(cx, obj, &pgconn_class, NULL); - assert(pgc != NULL); - - s = PQconnectPoll(pgc); - *rval = INT_TO_JSVAL((int)s); - - return JS_TRUE; -} - -static JSBool -pgconn_m_PQreset(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - PGconn *pgc; - - if (argc != 0) { - QUEUE_EXCEPTION("Function allows no arguments"); - *rval = OBJECT_TO_JSVAL(NULL); - return JS_FALSE; - } - - pgc = JS_GetInstancePrivate(cx, obj, &pgconn_class, NULL); - assert(pgc != NULL); - - PQreset(pgc); - *rval = OBJECT_TO_JSVAL(NULL); - - return JS_TRUE; -} - -static JSBool -pgconn_m_PQresetStart(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - PGconn *pgc; - - if (argc != 0) { - QUEUE_EXCEPTION("Function allows no arguments"); - *rval = OBJECT_TO_JSVAL(NULL); - return JS_FALSE; - } - - pgc = JS_GetInstancePrivate(cx, obj, &pgconn_class, NULL); - assert(pgc != NULL); - - *rval = INT_TO_JSVAL(PQconnectPoll(pgc)); - - return JS_TRUE; -} - -static JSBool -pgconn_m_PQresetPoll(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - PGconn *pgc; - PostgresPollingStatusType s; - - if (argc != 0) { - QUEUE_EXCEPTION("Function allows no arguments"); - *rval = OBJECT_TO_JSVAL(NULL); - return JS_FALSE; - } - - pgc = JS_GetInstancePrivate(cx, obj, &pgconn_class, NULL); - assert(pgc != NULL); - - s = PQresetPoll(pgc); - *rval = INT_TO_JSVAL((int)s); - - return JS_TRUE; -} - -static JSBool -pgconn_m_PQdb(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - PGconn *pgc; - char *str; - - if (argc != 0) { - QUEUE_EXCEPTION("Function allows no arguments"); - *rval = OBJECT_TO_JSVAL(NULL); - return JS_FALSE; - } - - pgc = JS_GetInstancePrivate(cx, obj, &pgconn_class, NULL); - assert(pgc != NULL); - - str = PQdb(pgc); - *rval = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, str)); - - return JS_TRUE; -} - -static JSBool -pgconn_m_PQuser(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - PGconn *pgc; - char *str; - - if (argc != 0) { - QUEUE_EXCEPTION("Function allows no arguments"); - *rval = OBJECT_TO_JSVAL(NULL); - return JS_FALSE; - } - - pgc = JS_GetInstancePrivate(cx, obj, &pgconn_class, NULL); - assert(pgc != NULL); - - str = PQuser(pgc); - *rval = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, str)); - - return JS_TRUE; -} - -static JSBool -pgconn_m_PQpass(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - PGconn *pgc; - char *str; - - if (argc != 0) { - QUEUE_EXCEPTION("Function allows no arguments"); - *rval = OBJECT_TO_JSVAL(NULL); - return JS_FALSE; - } - - pgc = JS_GetInstancePrivate(cx, obj, &pgconn_class, NULL); - assert(pgc != NULL); - - str = PQpass(pgc); - *rval = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, str)); - - return JS_TRUE; -} - -static JSBool -pgconn_m_PQhost(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - PGconn *pgc; - char *str; - - if (argc != 0) { - QUEUE_EXCEPTION("Function allows no arguments"); - *rval = OBJECT_TO_JSVAL(NULL); - return JS_FALSE; - } - - pgc = JS_GetInstancePrivate(cx, obj, &pgconn_class, NULL); - assert(pgc != NULL); - - str = PQhost(pgc); - *rval = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, str)); - - return JS_TRUE; -} - -static JSBool -pgconn_m_PQport(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - PGconn *pgc; - char *str; - - if (argc != 0) { - QUEUE_EXCEPTION("Function allows no arguments"); - *rval = OBJECT_TO_JSVAL(NULL); - return JS_FALSE; - } - - pgc = JS_GetInstancePrivate(cx, obj, &pgconn_class, NULL); - assert(pgc != NULL); - - str = PQport(pgc); - *rval = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, str)); - - return JS_TRUE; -} - -static JSBool -pgconn_m_PQtty(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - PGconn *pgc; - char *str; - - if (argc != 0) { - QUEUE_EXCEPTION("Function allows no arguments"); - *rval = OBJECT_TO_JSVAL(NULL); - return JS_FALSE; - } - - pgc = JS_GetInstancePrivate(cx, obj, &pgconn_class, NULL); - assert(pgc != NULL); - - str = PQtty(pgc); - *rval = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, str)); - - return JS_TRUE; -} - -static JSBool -pgconn_m_PQoptions(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - PGconn *pgc; - char *str; - - if (argc != 0) { - QUEUE_EXCEPTION("Function allows no arguments"); - *rval = OBJECT_TO_JSVAL(NULL); - return JS_FALSE; - } - - pgc = JS_GetInstancePrivate(cx, obj, &pgconn_class, NULL); - assert(pgc != NULL); - - str = PQoptions(pgc); - *rval = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, str)); - - return JS_TRUE; -} - -static JSBool -pgconn_m_PQstatus(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - PGconn *pgc; - ConnStatusType s = CONNECTION_BAD; - - if (argc != 0) { - QUEUE_EXCEPTION("Function allows no arguments"); - *rval = OBJECT_TO_JSVAL(NULL); - return JS_FALSE; - } - - pgc = JS_GetInstancePrivate(cx, obj, &pgconn_class, NULL); - assert(pgc != NULL); - - s = PQstatus(pgc); - *rval = INT_TO_JSVAL((int)s); - - return JS_TRUE; -} - -static JSBool -pgconn_m_PQtransactionStatus(JSContext *cx, JSObject *obj, uintN argc, - jsval *argv, jsval *rval) -{ - PGconn *pgc; - PGTransactionStatusType s; - - if (argc != 0) { - QUEUE_EXCEPTION("Function allows no arguments"); - *rval = OBJECT_TO_JSVAL(NULL); - return JS_FALSE; - } - - pgc = JS_GetInstancePrivate(cx, obj, &pgconn_class, NULL); - assert(pgc != NULL); - - s = PQtransactionStatus(pgc); - *rval = INT_TO_JSVAL((int)s); - - return JS_TRUE; -} - -static JSBool -pgconn_m_PQparameterStatus(JSContext *cx, JSObject *obj, uintN argc, - jsval *argv, jsval *rval) -{ - PGconn *pgc; - char *param; - const char *str; - - *rval = OBJECT_TO_JSVAL(NULL); - - if (argc != 1) { - QUEUE_EXCEPTION("Wrong number of arguments"); - return JS_FALSE; - } - if (!JSVAL_IS_STRING(argv[0])) { - QUEUE_EXCEPTION("Argument not a String"); - return JS_FALSE; - } - param = JS_GetStringBytes(JSVAL_TO_STRING(argv[0])); - - pgc = JS_GetInstancePrivate(cx, obj, &pgconn_class, NULL); - assert(pgc != NULL); - - if ((str = PQparameterStatus(pgc, param)) != NULL) - *rval = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, str)); - - return JS_TRUE; -} - -static JSBool -pgconn_m_PQprotocolVersion(JSContext *cx, JSObject *obj, uintN argc, - jsval *argv, jsval *rval) -{ - PGconn *pgc; - - if (argc != 0) { - QUEUE_EXCEPTION("Function allows no arguments"); - *rval = OBJECT_TO_JSVAL(NULL); - return JS_FALSE; - } - - pgc = JS_GetInstancePrivate(cx, obj, &pgconn_class, NULL); - assert(pgc != NULL); - - *rval = INT_TO_JSVAL(PQprotocolVersion(pgc)); - - return JS_TRUE; -} - -static JSBool -pgconn_m_PQserverVersion(JSContext *cx, JSObject *obj, uintN argc, - jsval *argv, jsval *rval) -{ - PGconn *pgc; - - if (argc != 0) { - QUEUE_EXCEPTION("Function allows no arguments"); - *rval = OBJECT_TO_JSVAL(NULL); - return JS_FALSE; - } - - pgc = JS_GetInstancePrivate(cx, obj, &pgconn_class, NULL); - assert(pgc != NULL); - - *rval = INT_TO_JSVAL(PQserverVersion(pgc)); - - return JS_TRUE; -} - -static JSBool -pgconn_m_PQerrorMessage(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - PGconn *pgc; - char *str; - - if (argc != 0) { - QUEUE_EXCEPTION("Function allows no arguments"); - *rval = OBJECT_TO_JSVAL(NULL); - return JS_FALSE; - } - - pgc = JS_GetInstancePrivate(cx, obj, &pgconn_class, NULL); - assert(pgc != NULL); - - str = PQerrorMessage(pgc); - *rval = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, str)); - - return JS_TRUE; -} - -static JSBool -pgconn_m_PQsocket(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - PGconn *pgc; - - if (argc != 0) { - QUEUE_EXCEPTION("Function allows no arguments"); - *rval = OBJECT_TO_JSVAL(NULL); - return JS_FALSE; - } - - pgc = JS_GetInstancePrivate(cx, obj, &pgconn_class, NULL); - assert(pgc != NULL); - - *rval = INT_TO_JSVAL(PQsocket(pgc)); - - return JS_TRUE; -} - -static JSBool -pgconn_m_PQbackendPID(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - PGconn *pgc; - - if (argc != 0) { - QUEUE_EXCEPTION("Function allows no arguments"); - *rval = OBJECT_TO_JSVAL(NULL); - return JS_FALSE; - } - - pgc = JS_GetInstancePrivate(cx, obj, &pgconn_class, NULL); - assert(pgc != NULL); - - *rval = INT_TO_JSVAL(PQbackendPID(pgc)); - - return JS_TRUE; -} - -static JSBool -pgconn_m_PQexec(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - JSObject *o; - PGconn *pgc; - PGresult *pgr = NULL; - char *str; - - if (argc != 1) { - QUEUE_EXCEPTION("Wrong number of arguments"); - goto err; - } - - if (!JSVAL_IS_STRING(argv[0])) { - QUEUE_EXCEPTION("Argument not a String"); - goto err; - } - str = JS_GetStringBytes(JSVAL_TO_STRING(argv[0])); - - pgc = JS_GetInstancePrivate(cx, obj, &pgconn_class, NULL); - assert(pgc != NULL); - - if ((o = JS_NewObject(cx, &pgresult_class, NULL, NULL)) == NULL) { - QUEUE_EXCEPTION("Internal error!"); - goto err; - } - /* Root immediately */ - *rval = OBJECT_TO_JSVAL(o); - - if ((pgr = PQexec(pgc, str)) == NULL) { - QUEUE_EXCEPTION("PQexec()"); - goto err; - } - if (!JS_SetPrivate(cx, o, pgr)) { - QUEUE_EXCEPTION("Internal error!"); - goto err; - } - - return JS_TRUE; - -err: - if (pgr != NULL) - PQclear(pgr); - - *rval = OBJECT_TO_JSVAL(NULL); - - return JS_FALSE; -} - -static JSBool -pgconn_m_PQsendQuery(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - PGconn *pgc; - char *str; - - if (argc != 1) { - QUEUE_EXCEPTION("Wrong number of arguments"); - goto err; - } - - if (!JSVAL_IS_STRING(argv[0])) { - QUEUE_EXCEPTION("Argument not a String"); - goto err; - } - str = JS_GetStringBytes(JSVAL_TO_STRING(argv[0])); - - pgc = JS_GetInstancePrivate(cx, obj, &pgconn_class, NULL); - assert(pgc != NULL); - - *rval = INT_TO_JSVAL(PQsendQuery(pgc, str)); - - return JS_TRUE; - -err: - *rval = INT_TO_JSVAL(0); - - return JS_FALSE; -} - -/* - * A fairly hairy function. - */ -static JSBool -pgconn_m_PQexecParams2(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval, int async) -{ - int nargs, i; - JSObject *arrays[4], *o; - JSIdArray *a = NULL; - Oid *types = NULL; - char **values = NULL; - int *lengths = NULL; - int *formats = NULL; - char str[256]; - PGconn *pgc; - PGresult *pgr = NULL; - - if (argc != 7) { - QUEUE_EXCEPTION("Wrong number of arguments"); - goto err; - } - if (!JSVAL_IS_STRING(argv[0])) { - QUEUE_EXCEPTION("Argument 1 not a String"); - goto err; - } - if (!JSVAL_IS_INT(argv[1])) { - QUEUE_EXCEPTION("Argument 2 not an int"); - goto err; - } - for (i = 2; i < 6; i++) { - if (JSVAL_IS_NULL(argv[i])) { - arrays[i - 2] = NULL; - continue; - } - if (!JSVAL_IS_OBJECT(argv[i]) || - !JS_IsArrayObject(cx, - (arrays[i - 2] = JSVAL_TO_OBJECT(argv[i])))) { - (void) snprintf(str, 255, "Argument %d not an Array", - i + 1); - QUEUE_EXCEPTION(str); - goto err; - } - } - if (!JSVAL_IS_INT(argv[6])) { - QUEUE_EXCEPTION("Argument 7 not an int"); - goto err; - } - - /* Array arguments processing */ - nargs = JSVAL_TO_INT(argv[1]); - if (nargs < 0) { - QUEUE_EXCEPTION("Argument 2 negative"); - goto err; - } - - if (arrays[0] != NULL) - types = param_types; - if (arrays[1] != NULL) - values = param_values; - if (arrays[2] != NULL) - lengths = param_lengths; - if (arrays[3] != NULL) - formats = param_formats; - - for (i = 0; i < 4; i++) { - jsint len; - - if (arrays[i] == NULL) - continue; - - if (!JS_GetArrayLength(cx, arrays[i], &len) || len != nargs) { - (void) snprintf(str, 255, - "Argument %d Array not holding %d elements", - i + 2, nargs); - QUEUE_EXCEPTION(str); - goto err; - } - } - - if (param_grow(nargs) == -1) { - QUEUE_EXCEPTION("Out of memory!"); - goto err; - } - - /* param_types */ - if (types != NULL) { - jsval id, val; - int i2; - jsdouble v; - - o = arrays[0]; - if ((a = JS_Enumerate(cx, o)) == NULL) { - QUEUE_EXCEPTION("Internal error!"); - goto err; - } - for (i2 = i = 0; i < a->length; i++) { - JS_IdToValue(cx, a->vector[i], &id); - if (JS_LookupElement(cx, o, JSVAL_TO_INT(id), &val) && - !JSVAL_IS_VOID(val)) { - if (!JSVAL_IS_NUMBER(val)) { - (void) snprintf(str, 255, - "Argument 3 Array's element %d " - "not a number", i2); - QUEUE_EXCEPTION(str); - goto err; - } - if (!JS_ValueToNumber(cx, val, &v)) { - QUEUE_EXCEPTION("Internal error!"); - goto err; - } - types[i2++] = (Oid)v; - } - } - JS_DestroyIdArray(cx, a); - a = NULL; - } - - /* param_values */ - if (values != NULL) { - jsval id, val; - int i2; - - o = arrays[1]; - if ((a = JS_Enumerate(cx, o)) == NULL) { - QUEUE_EXCEPTION("Internal error!"); - goto err; - } - for (i2 = i = 0; i < a->length; i++) { - JS_IdToValue(cx, a->vector[i], &id); - if (JS_LookupElement(cx, o, JSVAL_TO_INT(id), &val) && - !JSVAL_IS_VOID(val)) { - if (JSVAL_IS_NULL(val)) { - values[i2++] = NULL; - continue; - } - if (!JSVAL_IS_STRING(val)) { - (void) snprintf(str, 255, - "Argument 4 Array's element %d " - "not a String", i2); - QUEUE_EXCEPTION(str); - goto err; - } - values[i2++] = JS_GetStringBytes( - JSVAL_TO_STRING(val)); - } - } - JS_DestroyIdArray(cx, a); - a = NULL; - } - - /* param_lengths */ - if (lengths != NULL) { - jsval id, val; - int i2; - - o = arrays[2]; - if ((a = JS_Enumerate(cx, o)) == NULL) { - QUEUE_EXCEPTION("Internal error!"); - goto err; - } - for (i2 = i = 0; i < a->length; i++) { - JS_IdToValue(cx, a->vector[i], &id); - if (JS_LookupElement(cx, o, JSVAL_TO_INT(id), &val) && - !JSVAL_IS_VOID(val)) { - if (!JSVAL_IS_INT(val)) { - (void) snprintf(str, 255, - "Argument 5 Array's element %d " - "not an int", i2); - QUEUE_EXCEPTION(str); - goto err; - } - lengths[i2++] = JSVAL_TO_INT(val); - } - } - JS_DestroyIdArray(cx, a); - a = NULL; - } - - /* param_formats */ - if (formats != NULL) { - jsval id, val; - int i2; - - o = arrays[3]; - if ((a = JS_Enumerate(cx, o)) == NULL) { - QUEUE_EXCEPTION("Internal error!"); - goto err; - } - for (i2 = i = 0; i < a->length; i++) { - JS_IdToValue(cx, a->vector[i], &id); - if (JS_LookupElement(cx, o, JSVAL_TO_INT(id), &val) && - !JSVAL_IS_VOID(val)) { - if (!JSVAL_IS_INT(val)) { - (void) snprintf(str, 255, - "Argument 6 Array's element %d " - "not an int", i2); - QUEUE_EXCEPTION(str); - goto err; - } - formats[i2++] = JSVAL_TO_INT(val); - } - } - JS_DestroyIdArray(cx, a); - a = NULL; - } - - pgc = JS_GetInstancePrivate(cx, obj, &pgconn_class, NULL); - assert(pgc != NULL); - - if (!async) { - - if ((pgr = PQexecParams(pgc, - JS_GetStringBytes(JSVAL_TO_STRING(argv[0])), - nargs, types, (const char * const *)values, lengths, - formats, JSVAL_TO_INT(argv[6]))) == NULL) { - QUEUE_EXCEPTION("PQexecParams()"); - goto err; - } - - if ((o = JS_NewObject(cx, &pgresult_class, NULL, NULL)) - == NULL) { - QUEUE_EXCEPTION("Internal error!"); - goto err; - } - /* Root immediately */ - *rval = OBJECT_TO_JSVAL(o); - - if (!JS_SetPrivate(cx, o, pgr)) { - QUEUE_EXCEPTION("Internal error!"); - goto err; - } - - } else { - - *rval = INT_TO_JSVAL(PQsendQueryParams(pgc, - JS_GetStringBytes(JSVAL_TO_STRING(argv[0])), - nargs, types, (const char * const *)values, lengths, - formats, JSVAL_TO_INT(argv[6]))); - - } - - return JS_TRUE; - -err: - if (pgr != NULL) - PQclear(pgr); - if (a != NULL) - JS_DestroyIdArray(cx, a); - - if (!async) - *rval = OBJECT_TO_JSVAL(NULL); - else - *rval = INT_TO_JSVAL(0); - - return JS_FALSE; -} - -static JSBool -pgconn_m_PQexecParams(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - - return pgconn_m_PQexecParams2(cx, obj, argc, argv, rval, 0); -} - -static JSBool -pgconn_m_PQsendQueryParams(JSContext *cx, JSObject *obj, uintN argc, - jsval *argv, jsval *rval) -{ - - return pgconn_m_PQexecParams2(cx, obj, argc, argv, rval, 1); -} - -static JSBool -pgconn_m_PQprepare2(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval, int async) -{ - int nargs, i; - JSObject *array, *o; - JSIdArray *a = NULL; - Oid *types = NULL; - char str[256]; - PGconn *pgc; - PGresult *pgr = NULL; - - if (argc != 4) { - QUEUE_EXCEPTION("Wrong number of arguments"); - goto err; - } - if (!JSVAL_IS_STRING(argv[0])) { - QUEUE_EXCEPTION("Argument 1 not a String"); - goto err; - } - if (!JSVAL_IS_STRING(argv[1])) { - QUEUE_EXCEPTION("Argument 2 not a String"); - goto err; - } - if (!JSVAL_IS_INT(argv[2])) { - QUEUE_EXCEPTION("Argument 3 not an int"); - goto err; - } - if (JSVAL_IS_NULL(argv[3])) - array = NULL; - else { - if (!JSVAL_IS_OBJECT(argv[3]) || - !JS_IsArrayObject(cx, - (array = JSVAL_TO_OBJECT(argv[3])))) { - QUEUE_EXCEPTION("Argument 4 not an Array"); - goto err; - } - } - - /* Array arguments processing */ - nargs = JSVAL_TO_INT(argv[2]); - if (nargs < 0) { - QUEUE_EXCEPTION("Argument 3 negative"); - goto err; - } - - if (array != NULL) { - jsint len; - - types = param_types; - - if (!JS_GetArrayLength(cx, array, &len) || len != nargs) { - (void) snprintf(str, 255, - "Argument 4 Array not holding %d elements", - nargs); - QUEUE_EXCEPTION(str); - goto err; - } - } - - if (param_grow(nargs) == -1) { - QUEUE_EXCEPTION("Out of memory!"); - goto err; - } - - /* param_types */ - if (types != NULL) { - jsval id, val; - int i2; - jsdouble v; - - o = array; - if ((a = JS_Enumerate(cx, o)) == NULL) { - QUEUE_EXCEPTION("Internal error!"); - goto err; - } - for (i2 = i = 0; i < a->length; i++) { - JS_IdToValue(cx, a->vector[i], &id); - if (JS_LookupElement(cx, o, JSVAL_TO_INT(id), &val) && - !JSVAL_IS_VOID(val)) { - if (!JSVAL_IS_NUMBER(val)) { - (void) snprintf(str, 255, - "Argument 4 Array's element %d " - "not a number", i2); - QUEUE_EXCEPTION(str); - goto err; - } - if (!JS_ValueToNumber(cx, val, &v)) { - QUEUE_EXCEPTION("Internal error!"); - goto err; - } - types[i2++] = (Oid)v; - } - } - JS_DestroyIdArray(cx, a); - a = NULL; - } - - pgc = JS_GetInstancePrivate(cx, obj, &pgconn_class, NULL); - assert(pgc != NULL); - - if (!async) { - - if ((pgr = PQprepare(pgc, - JS_GetStringBytes(JSVAL_TO_STRING(argv[0])), - JS_GetStringBytes(JSVAL_TO_STRING(argv[1])), - nargs, types)) == NULL) { - QUEUE_EXCEPTION("PQprepare()"); - goto err; - } - - if ((o = JS_NewObject(cx, &pgresult_class, NULL, NULL)) - == NULL) { - QUEUE_EXCEPTION("Internal error!"); - goto err; - } - /* Root immediately */ - *rval = OBJECT_TO_JSVAL(o); - - if (!JS_SetPrivate(cx, o, pgr)) { - QUEUE_EXCEPTION("Internal error!"); - goto err; - } - - } else { - - *rval = INT_TO_JSVAL(PQsendPrepare(pgc, - JS_GetStringBytes(JSVAL_TO_STRING(argv[0])), - JS_GetStringBytes(JSVAL_TO_STRING(argv[1])), - nargs, types)); - - } - - return JS_TRUE; - -err: - if (pgr != NULL) - PQclear(pgr); - - if (!async) - *rval = OBJECT_TO_JSVAL(NULL); - else - *rval = INT_TO_JSVAL(0); - - return JS_FALSE; -} - -static JSBool -pgconn_m_PQprepare(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - - return pgconn_m_PQprepare2(cx, obj, argc, argv, rval, 0); -} - -static JSBool -pgconn_m_PQsendPrepare(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - - return pgconn_m_PQprepare2(cx, obj, argc, argv, rval, 1); -} - -/* - * Also rather hairy - */ -static JSBool -pgconn_m_PQexecPrepared2(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval, int async) -{ - int nargs, i; - JSObject *arrays[3], *o; - JSIdArray *a = NULL; - char **values = NULL; - int *lengths = NULL; - int *formats = NULL; - char str[256]; - PGconn *pgc; - PGresult *pgr = NULL; - - if (argc != 6) { - QUEUE_EXCEPTION("Wrong number of arguments"); - goto err; - } - if (!JSVAL_IS_STRING(argv[0])) { - QUEUE_EXCEPTION("Argument 1 not a String"); - goto err; - } - if (!JSVAL_IS_INT(argv[1])) { - QUEUE_EXCEPTION("Argument 2 not an int"); - goto err; - } - for (i = 2; i < 5; i++) { - if (JSVAL_IS_NULL(argv[i])) { - arrays[i - 2] = NULL; - continue; - } - if (!JSVAL_IS_OBJECT(argv[i]) || - !JS_IsArrayObject(cx, - (arrays[i - 2] = JSVAL_TO_OBJECT(argv[i])))) { - (void) snprintf(str, 255, "Argument %d not an Array", - i + 1); - QUEUE_EXCEPTION(str); - goto err; - } - } - if (!JSVAL_IS_INT(argv[5])) { - QUEUE_EXCEPTION("Argument 7 not an int"); - goto err; - } - - /* Array arguments processing */ - nargs = JSVAL_TO_INT(argv[1]); - if (nargs < 0) { - QUEUE_EXCEPTION("Argument 2 negative"); - goto err; - } - - if (arrays[0] != NULL) - values = param_values; - if (arrays[1] != NULL) - lengths = param_lengths; - if (arrays[2] != NULL) - formats = param_formats; - - for (i = 0; i < 3; i++) { - jsint len; - - if (arrays[i] == NULL) - continue; - - if (!JS_GetArrayLength(cx, arrays[i], &len) || len != nargs) { - (void) snprintf(str, 255, - "Argument %d Array not holding %d elements", - i + 2, nargs); - QUEUE_EXCEPTION(str); - goto err; - } - } - - if (param_grow(nargs) == -1) { - QUEUE_EXCEPTION("Out of memory!"); - goto err; - } - - /* param_values */ - if (values != NULL) { - jsval id, val; - int i2; - - o = arrays[0]; - if ((a = JS_Enumerate(cx, o)) == NULL) { - QUEUE_EXCEPTION("Internal error!"); - goto err; - } - for (i2 = i = 0; i < a->length; i++) { - JS_IdToValue(cx, a->vector[i], &id); - if (JS_LookupElement(cx, o, JSVAL_TO_INT(id), &val) && - !JSVAL_IS_VOID(val)) { - if (JSVAL_IS_NULL(val)) { - values[i2++] = NULL; - continue; - } - if (!JSVAL_IS_STRING(val)) { - (void) snprintf(str, 255, - "Argument 3 Array's element %d " - "not a String", i2); - QUEUE_EXCEPTION(str); - goto err; - } - values[i2++] = JS_GetStringBytes( - JSVAL_TO_STRING(val)); - } - } - JS_DestroyIdArray(cx, a); - a = NULL; - } - - /* param_lengths */ - if (lengths != NULL) { - jsval id, val; - int i2; - - o = arrays[1]; - if ((a = JS_Enumerate(cx, o)) == NULL) { - QUEUE_EXCEPTION("Internal error!"); - goto err; - } - for (i2 = i = 0; i < a->length; i++) { - JS_IdToValue(cx, a->vector[i], &id); - if (JS_LookupElement(cx, o, JSVAL_TO_INT(id), &val) && - !JSVAL_IS_VOID(val)) { - if (!JSVAL_IS_INT(val)) { - (void) snprintf(str, 255, - "Argument 4 Array's element %d " - "not an int", i2); - QUEUE_EXCEPTION(str); - goto err; - } - lengths[i2++] = JSVAL_TO_INT(val); - } - } - JS_DestroyIdArray(cx, a); - a = NULL; - } - - /* param_formats */ - if (formats != NULL) { - jsval id, val; - int i2; - - o = arrays[2]; - if ((a = JS_Enumerate(cx, o)) == NULL) { - QUEUE_EXCEPTION("Internal error!"); - goto err; - } - for (i2 = i = 0; i < a->length; i++) { - JS_IdToValue(cx, a->vector[i], &id); - if (JS_LookupElement(cx, o, JSVAL_TO_INT(id), &val) && - !JSVAL_IS_VOID(val)) { - if (!JSVAL_IS_INT(val)) { - (void) snprintf(str, 255, - "Argument 5 Array's element %d " - "not an int", i2); - QUEUE_EXCEPTION(str); - goto err; - } - formats[i2++] = JSVAL_TO_INT(val); - } - } - JS_DestroyIdArray(cx, a); - a = NULL; - } - - pgc = JS_GetInstancePrivate(cx, obj, &pgconn_class, NULL); - assert(pgc != NULL); - - if (!async) { - - if ((pgr = PQexecPrepared(pgc, - JS_GetStringBytes(JSVAL_TO_STRING(argv[0])), - nargs, (const char * const *)values, lengths, formats, - JSVAL_TO_INT(argv[5]))) == NULL) { - QUEUE_EXCEPTION("PQexecPrepared()"); - goto err; - } - - if ((o = JS_NewObject(cx, &pgresult_class, NULL, NULL)) - == NULL) { - QUEUE_EXCEPTION("Internal error!"); - goto err; - } - /* Root immediately */ - *rval = OBJECT_TO_JSVAL(o); - - if (!JS_SetPrivate(cx, o, pgr)) { - QUEUE_EXCEPTION("Internal error!"); - goto err; - } - - } else { - - *rval = INT_TO_JSVAL(PQsendQueryPrepared(pgc, - JS_GetStringBytes(JSVAL_TO_STRING(argv[0])), - nargs, (const char * const *)values, lengths, formats, - JSVAL_TO_INT(argv[5]))); - - } - - return JS_TRUE; - -err: - if (pgr != NULL) - PQclear(pgr); - if (a != NULL) - JS_DestroyIdArray(cx, a); - - if (!async) - *rval = OBJECT_TO_JSVAL(NULL); - else - *rval = INT_TO_JSVAL(0); - - return JS_FALSE; -} - -static JSBool -pgconn_m_PQexecPrepared(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - - return pgconn_m_PQexecPrepared2(cx, obj, argc, argv, rval, 0); -} - -static JSBool -pgconn_m_PQsendQueryPrepared(JSContext *cx, JSObject *obj, uintN argc, - jsval *argv, jsval *rval) -{ - - return pgconn_m_PQexecPrepared2(cx, obj, argc, argv, rval, 1); -} - -static JSBool -pgconn_m_PQmakeEmptyPGresult(JSContext *cx, JSObject *obj, uintN argc, - jsval *argv, jsval *rval) -{ - JSObject *o; - PGconn *pgc; - PGresult *pgr = NULL; - - if (argc != 1) { - QUEUE_EXCEPTION("Wrong number of arguments"); - goto err; - } - if (!JSVAL_IS_INT(argv[0])) { - QUEUE_EXCEPTION("Argument not an int"); - goto err; - } - - pgc = JS_GetInstancePrivate(cx, obj, &pgconn_class, NULL); - assert(pgc != NULL); - - if ((o = JS_NewObject(cx, &pgresult_class, NULL, NULL)) == NULL) { - QUEUE_EXCEPTION("Internal error!"); - goto err; - } - /* Root immediately */ - *rval = OBJECT_TO_JSVAL(o); - - if ((pgr = PQmakeEmptyPGresult(pgc, JSVAL_TO_INT(argv[0]))) == NULL) { - QUEUE_EXCEPTION("PQmakeEmptyPGresult()"); - goto err; - } - - if (!JS_SetPrivate(cx, o, pgr)) { - QUEUE_EXCEPTION("Internal error!"); - goto err; - } - - return JS_TRUE; - -err: - if (pgr != NULL) - PQclear(pgr); - - *rval = OBJECT_TO_JSVAL(NULL); - - return JS_FALSE; -} - -/* - * Note: Semantics are different from C PQescapeStringConn() in that it is - * supplied a single String and that it returns a resulting String, or null on - * error. Much easier to work with within ECMAScript this way. - */ -static JSBool -pgconn_m_PQescapeStringConn(JSContext *cx, JSObject *obj, uintN argc, - jsval *argv, jsval *rval) -{ - PGconn *pgc; - JSString *str; - char *from; - size_t len; - int ret; - - if (argc != 1) { - QUEUE_EXCEPTION("Wrong number of arguments"); - goto err; - } - if (!JSVAL_IS_STRING(argv[0])) { - QUEUE_EXCEPTION("Argument not a String"); - goto err; - } - - pgc = JS_GetInstancePrivate(cx, obj, &pgconn_class, NULL); - assert(pgc != NULL); - - str = JSVAL_TO_STRING(argv[0]); - from = JS_GetStringBytes(str); - len = JS_GetStringLength(str); - - if (buffer_grow((len * 2) + 2) == -1) { - QUEUE_EXCEPTION("Out of memory!"); - goto err; - } - - len = PQescapeStringConn(pgc, buffer, from, len, &ret); - if (ret != 0) { - QUEUE_EXCEPTION("PQescapeStringConn()"); - goto err; - } - - if ((str = JS_NewStringCopyN(cx, buffer, len)) == NULL) { - QUEUE_EXCEPTION("Out of memory!"); - goto err; - } - *rval = STRING_TO_JSVAL(str); - - return JS_TRUE; - -err: - *rval = OBJECT_TO_JSVAL(NULL); - - return JS_FALSE; -} - -/* PQescapeString() deprecated in favor of PQescapeStringConn() */ - -/* - * Note: Semantics are different from C PQescapeByteaConn() in that it is - * supplied a single String and that it returns a resulting String, or null on - * error. Much easier to work with within ECMAScript this way. - */ -static JSBool -pgconn_m_PQescapeByteaConn(JSContext *cx, JSObject *obj, uintN argc, - jsval *argv, jsval *rval) -{ - PGconn *pgc; - JSString *str; - char *from; - size_t fromlen, reslen; - unsigned char *res = NULL; - - if (argc != 1) { - QUEUE_EXCEPTION("Wrong number of arguments"); - goto err; - } - if (!JSVAL_IS_STRING(argv[0])) { - QUEUE_EXCEPTION("Argument not a String"); - goto err; - } - - pgc = JS_GetInstancePrivate(cx, obj, &pgconn_class, NULL); - assert(pgc != NULL); - - str = JSVAL_TO_STRING(argv[0]); - from = JS_GetStringBytes(str); - fromlen = JS_GetStringLength(str); - - if ((res = PQescapeByteaConn(pgc, from, fromlen, &reslen)) == NULL) { - QUEUE_EXCEPTION("PQescapeByteaConn()"); - goto err; - } - - if ((str = JS_NewStringCopyN(cx, res, reslen)) == NULL) { - QUEUE_EXCEPTION("Out of memory!"); - goto err; - } - - PQfreemem(res); - *rval = STRING_TO_JSVAL(str); - - return JS_TRUE; - -err: - if (res != NULL) - PQfreemem(res); - - *rval = OBJECT_TO_JSVAL(NULL); - - return JS_FALSE; -} - -static JSBool -pgconn_m_PQgetCancel(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - JSObject *o; - PGconn *pgc; - PGcancel *pgcn = NULL; - - if (argc != 0) { - QUEUE_EXCEPTION("Function allows no arguments"); - *rval = OBJECT_TO_JSVAL(NULL); - return JS_FALSE; - } - - pgc = JS_GetInstancePrivate(cx, obj, &pgconn_class, NULL); - assert(pgc != NULL); - - if ((o = JS_NewObject(cx, &pgcancel_class, NULL, NULL)) == NULL) { - QUEUE_EXCEPTION("Internal error!"); - goto err; - } - /* Root immediately */ - *rval = OBJECT_TO_JSVAL(o); - - if ((pgcn = PQgetCancel(pgc)) == NULL) { - QUEUE_EXCEPTION("PQgetCancel()"); - goto err; - } - if (!JS_SetPrivate(cx, o, pgcn)) { - QUEUE_EXCEPTION("Internal error!"); - goto err; - } - - return JS_TRUE; - -err: - if (pgcn != NULL) - PQfreeCancel(pgcn); - - *rval = OBJECT_TO_JSVAL(NULL); - - return JS_FALSE; -} - -/* - * Note: Unlike C native PQnotifies(), returns null or a normal object with - * the three properties set, rather than specifically a PQnotify object. - */ -static JSBool -pgconn_m_PQnotifies(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - JSObject *o; - PGconn *pgc; - PGnotify *pgn = NULL; - - if (argc != 0) { - QUEUE_EXCEPTION("Function allows no arguments"); - *rval = OBJECT_TO_JSVAL(NULL); - return JS_FALSE; - } - - pgc = JS_GetInstancePrivate(cx, obj, &pgconn_class, NULL); - assert(pgc != NULL); - - if ((pgn = PQnotifies(pgc)) == NULL) { - *rval = OBJECT_TO_JSVAL(NULL); - return JS_TRUE; - } - - if ((o = JS_NewObject(cx, NULL, NULL, NULL)) == NULL) { - QUEUE_EXCEPTION("Internal error!"); - goto err; - } - /* Root immediately */ - *rval = OBJECT_TO_JSVAL(o); - - if (!JS_DefineProperty(cx, o, "relname", STRING_TO_JSVAL( - JS_NewStringCopyZ(cx, pgn->relname)), NULL, NULL, - JSPROP_ENUMERATE)) { - QUEUE_EXCEPTION("Internal error!"); - goto err; - } - if (!JS_DefineProperty(cx, o, "be_pid", INT_TO_JSVAL(pgn->be_pid), - NULL, NULL, JSPROP_ENUMERATE)) { - QUEUE_EXCEPTION("Internal error!"); - goto err; - } - if (!JS_DefineProperty(cx, o, "extra", STRING_TO_JSVAL( - JS_NewStringCopyZ(cx, pgn->extra)), NULL, NULL, - JSPROP_ENUMERATE)) { - QUEUE_EXCEPTION("Internal error!"); - goto err; - } - - PQfreemem(pgn); - - return JS_TRUE; - -err: - if (pgn != NULL) - PQfreemem(pgn); - - *rval = OBJECT_TO_JSVAL(NULL); - - return JS_FALSE; -} - -static JSBool -pgconn_m_PQgetResult(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - JSObject *o; - PGconn *pgc; - PGresult *pgr = NULL; - - if (argc != 0) { - QUEUE_EXCEPTION("Function allows no arguments"); - *rval = OBJECT_TO_JSVAL(NULL); - return JS_FALSE; - } - - pgc = JS_GetInstancePrivate(cx, obj, &pgconn_class, NULL); - assert(pgc != NULL); - - if ((pgr = PQgetResult(pgc)) == NULL) { - *rval = OBJECT_TO_JSVAL(NULL); - return JS_TRUE; - } - - if ((o = JS_NewObject(cx, &pgresult_class, NULL, NULL)) == NULL) { - QUEUE_EXCEPTION("Internal error!"); - goto err; - } - /* Root immediately */ - *rval = OBJECT_TO_JSVAL(o); - - if (!JS_SetPrivate(cx, o, pgr)) { - QUEUE_EXCEPTION("Internal error!"); - goto err; - } - - return JS_TRUE; - -err: - if (pgr != NULL) - PQclear(pgr); - - *rval = OBJECT_TO_JSVAL(NULL); - - return JS_FALSE; -} - -static JSBool -pgconn_m_PQconsumeInput(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - PGconn *pgc; - - if (argc != 0) { - QUEUE_EXCEPTION("Function allows no arguments"); - *rval = OBJECT_TO_JSVAL(NULL); - return JS_FALSE; - } - - pgc = JS_GetInstancePrivate(cx, obj, &pgconn_class, NULL); - assert(pgc != NULL); - - *rval = INT_TO_JSVAL(PQconsumeInput(pgc)); - - return JS_TRUE; -} - -static JSBool -pgconn_m_PQisBusy(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - PGconn *pgc; - - if (argc != 0) { - QUEUE_EXCEPTION("Function allows no arguments"); - *rval = OBJECT_TO_JSVAL(NULL); - return JS_FALSE; - } - - pgc = JS_GetInstancePrivate(cx, obj, &pgconn_class, NULL); - assert(pgc != NULL); - - *rval = INT_TO_JSVAL(PQisBusy(pgc)); - - return JS_TRUE; -} - -static JSBool -pgconn_m_PQsetnonblocking(JSContext *cx, JSObject *obj, uintN argc, - jsval *argv, jsval *rval) -{ - PGconn *pgc; - - if (argc != 1) { - QUEUE_EXCEPTION("Wrong number of arguments"); - goto err; - } - if (!JSVAL_IS_INT(argv[0])) { - QUEUE_EXCEPTION("Argument 1 not an int"); - goto err; - } - - pgc = JS_GetInstancePrivate(cx, obj, &pgconn_class, NULL); - assert(pgc != NULL); - - *rval = INT_TO_JSVAL(PQsetnonblocking(pgc, JSVAL_TO_INT(argv[0]))); - - return JS_TRUE; - -err: - *rval = OBJECT_TO_JSVAL(NULL); - - return JS_FALSE; -} - -static JSBool -pgconn_m_PQisnonblocking(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - PGconn *pgc; - - if (argc != 0) { - QUEUE_EXCEPTION("Function allows no arguments"); - *rval = OBJECT_TO_JSVAL(NULL); - return JS_FALSE; - } - - pgc = JS_GetInstancePrivate(cx, obj, &pgconn_class, NULL); - assert(pgc != NULL); - - *rval = INT_TO_JSVAL(PQisnonblocking(pgc)); - - return JS_TRUE; -} - -static JSBool -pgconn_m_PQflush(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - PGconn *pgc; - - if (argc != 0) { - QUEUE_EXCEPTION("Function allows no arguments"); - *rval = OBJECT_TO_JSVAL(NULL); - return JS_FALSE; - } - - pgc = JS_GetInstancePrivate(cx, obj, &pgconn_class, NULL); - assert(pgc != NULL); - - *rval = INT_TO_JSVAL(PQflush(pgc)); - - return JS_TRUE; -} - -static JSBool -pgconn_m_PQsetErrorVerbosity(JSContext *cx, JSObject *obj, uintN argc, - jsval *argv, jsval *rval) -{ - PGconn *pgc; - - if (argc != 1) { - QUEUE_EXCEPTION("Wrong number of arguments"); - goto err; - } - if (!JSVAL_IS_INT(argv[0])) { - QUEUE_EXCEPTION("Argument 1 not an int"); - goto err; - } - - pgc = JS_GetInstancePrivate(cx, obj, &pgconn_class, NULL); - assert(pgc != NULL); - - *rval = INT_TO_JSVAL(PQsetErrorVerbosity(pgc, JSVAL_TO_INT(argv[0]))); - - return JS_TRUE; - -err: - *rval = OBJECT_TO_JSVAL(NULL); - - return JS_FALSE; -} - -static JSBool -pgconn_m_PQtrace(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - PGconn *pgc; - - if (argc != 0) { - QUEUE_EXCEPTION("Function allows no arguments"); - *rval = OBJECT_TO_JSVAL(NULL); - return JS_FALSE; - } - - pgc = JS_GetInstancePrivate(cx, obj, &pgconn_class, NULL); - assert(pgc != NULL); - - *rval = OBJECT_TO_JSVAL(NULL); - PQtrace(pgc, stderr); - - return JS_TRUE; -} - -static JSBool -pgconn_m_PQuntrace(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - PGconn *pgc; - - if (argc != 0) { - QUEUE_EXCEPTION("Function allows no arguments"); - *rval = OBJECT_TO_JSVAL(NULL); - return JS_FALSE; - } - - pgc = JS_GetInstancePrivate(cx, obj, &pgconn_class, NULL); - assert(pgc != NULL); - - *rval = OBJECT_TO_JSVAL(NULL); - PQuntrace(pgc); - - return JS_TRUE; -} - -static JSBool -pgconn_m_PQputCopyData(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - PGconn *pgc; - JSString *str; - - if (argc != 1) { - QUEUE_EXCEPTION("Wrong number of arguments"); - goto err; - } - if (!JSVAL_IS_STRING(argv[0])) { - QUEUE_EXCEPTION("Argument not a String"); - goto err; - } - - pgc = JS_GetInstancePrivate(cx, obj, &pgconn_class, NULL); - assert(pgc != NULL); - - str = JSVAL_TO_STRING(argv[0]); - *rval = INT_TO_JSVAL(PQputCopyData(pgc, JS_GetStringBytes(str), - JS_GetStringLength(str))); - - return JS_TRUE; - -err: - *rval = OBJECT_TO_JSVAL(NULL); - - return JS_FALSE; -} - -static JSBool -pgconn_m_PQputCopyEnd(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - PGconn *pgc; - - if (argc != 1) { - QUEUE_EXCEPTION("Wrong number of arguments"); - goto err; - } - if (!JSVAL_IS_STRING(argv[0]) && !JSVAL_IS_NULL(argv[0])) { - QUEUE_EXCEPTION("Argument not a String or null"); - goto err; - } - - pgc = JS_GetInstancePrivate(cx, obj, &pgconn_class, NULL); - assert(pgc != NULL); - - *rval = INT_TO_JSVAL(PQputCopyEnd(pgc, (JSVAL_IS_NULL(argv[0]) ? NULL : - JS_GetStringBytes(JSVAL_TO_STRING(argv[0]))))); - - return JS_TRUE; - -err: - *rval = OBJECT_TO_JSVAL(NULL); - - return JS_FALSE; -} - -/* - * Note: unlike native PQgetCopyData(), which returns an int but is supplied a - * pointer to a pointer to be set, this implementation returns an object which - * holds two elements: result (the integer) and data (null or String), to make - * it easier to use with ECMAScript. - */ -static JSBool -pgconn_m_PQgetCopyData(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - JSObject *o; - PGconn *pgc; - char *data = NULL; - int res; - - if (argc != 1) { - QUEUE_EXCEPTION("Wrong number of arguments"); - goto err; - } - if (!JSVAL_IS_INT(argv[0])) { - QUEUE_EXCEPTION("Argument 1 not an int"); - goto err; - } - - pgc = JS_GetInstancePrivate(cx, obj, &pgconn_class, NULL); - assert(pgc != NULL); - - res = PQgetCopyData(pgc, &data, JSVAL_TO_INT(argv[0])); - - if ((o = JS_NewObject(cx, NULL, NULL, NULL)) == NULL) { - QUEUE_EXCEPTION("Internal error!"); - goto err; - } - /* Root immediately */ - *rval = OBJECT_TO_JSVAL(o); - - if (!JS_DefineProperty(cx, o, "result", INT_TO_JSVAL(res), - NULL, NULL, JSPROP_ENUMERATE)) { - QUEUE_EXCEPTION("Internal error!"); - goto err; - } - if (data != NULL && res > 0) { - if (!JS_DefineProperty(cx, o, "data", STRING_TO_JSVAL( - JS_NewStringCopyN(cx, data, res)), NULL, NULL, - JSPROP_ENUMERATE)) { - QUEUE_EXCEPTION("Internal error!"); - goto err; - } - PQfreemem(data); - } else { - if (!JS_DefineProperty(cx, o, "data", OBJECT_TO_JSVAL(NULL), - NULL, NULL, JSPROP_ENUMERATE)) { - QUEUE_EXCEPTION("Internal error!"); - goto err; - } - } - - return JS_TRUE; - -err: - if (data != NULL) - PQfreemem(data); - - *rval = OBJECT_TO_JSVAL(NULL); - - return JS_FALSE; -} - - -/* - * PGresult object control - */ - -static JSObject * -js_InitPGresultClass(JSContext *cx, JSObject *obj) -{ - - return (JS_InitClass(cx, obj, NULL, &pgresult_class, - pgresult_constructor, 0, NULL, pgresult_methods, NULL, NULL)); -} - -/* Non instanciable */ -static JSBool -pgresult_constructor(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - - QUEUE_EXCEPTION("PGresult class not user-instanciable"); - - return JS_FALSE; -} - -static void -pgresult_finalize(JSContext *cx, JSObject *obj) -{ - PGresult *pgr; - - if ((pgr = JS_GetInstancePrivate(cx, obj, &pgresult_class, NULL)) - != NULL) { - PQclear(pgr); - (void) JS_SetPrivate(cx, obj, NULL); - } -} - - -/* - * PGresult object methods - */ - -static JSBool -pgresult_m_PQclear(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - PGresult *pgr; - - if (argc != 0) { - QUEUE_EXCEPTION("Function allows no arguments"); - *rval = OBJECT_TO_JSVAL(NULL); - return JS_FALSE; - } - - if ((pgr = JS_GetInstancePrivate(cx, obj, &pgresult_class, NULL)) - != NULL) { - PQclear(pgr); - (void) JS_SetPrivate(cx, obj, NULL); - } - - *rval = OBJECT_TO_JSVAL(NULL); - - return JS_TRUE; -} - -static JSBool -pgresult_m_PQresultStatus(JSContext *cx, JSObject *obj, uintN argc, - jsval *argv, jsval *rval) -{ - PGresult *pgr; - ExecStatusType s; - - if (argc != 0) { - QUEUE_EXCEPTION("Function allows no arguments"); - *rval = OBJECT_TO_JSVAL(NULL); - return JS_FALSE; - } - - pgr = JS_GetInstancePrivate(cx, obj, &pgresult_class, NULL); - assert(pgr != NULL); - - s = PQresultStatus(pgr); - *rval = INT_TO_JSVAL((int)s); - - return JS_TRUE; -} - -static JSBool -pgresult_m_PQresultErrorMessage(JSContext *cx, JSObject *obj, uintN argc, - jsval *argv, jsval *rval) -{ - PGresult *pgr; - char *res; - - if (argc != 0) { - QUEUE_EXCEPTION("Function allows no arguments"); - *rval = OBJECT_TO_JSVAL(NULL); - return JS_FALSE; - } - - pgr = JS_GetInstancePrivate(cx, obj, &pgresult_class, NULL); - assert(pgr != NULL); - - res = PQresultErrorMessage(pgr); - *rval = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, res)); - - return JS_TRUE; -} - -static JSBool -pgresult_m_PQresultErrorField(JSContext *cx, JSObject *obj, uintN argc, - jsval *argv, jsval *rval) -{ - PGresult *pgr; - char *res; - - if (argc != 1) { - QUEUE_EXCEPTION("Wrong number of arguments"); - goto err; - } - if (!JSVAL_IS_INT(argv[0])) { - QUEUE_EXCEPTION("Argument not an int"); - goto err; - } - - pgr = JS_GetInstancePrivate(cx, obj, &pgresult_class, NULL); - assert(pgr != NULL); - - res = PQresultErrorField(pgr, JSVAL_TO_INT(argv[0])); - *rval = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, res)); - - return JS_TRUE; - -err: - *rval = OBJECT_TO_JSVAL(NULL); - - return JS_FALSE; -} - -static JSBool -pgresult_m_PQntuples(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - PGresult *pgr; - - if (argc != 0) { - QUEUE_EXCEPTION("Function allows no arguments"); - *rval = OBJECT_TO_JSVAL(NULL); - return JS_FALSE; - } - - pgr = JS_GetInstancePrivate(cx, obj, &pgresult_class, NULL); - assert(pgr != NULL); - - *rval = INT_TO_JSVAL(PQntuples(pgr)); - - return JS_TRUE; -} - -static JSBool -pgresult_m_PQnfields(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - PGresult *pgr; - - if (argc != 0) { - QUEUE_EXCEPTION("Function allows no arguments"); - *rval = OBJECT_TO_JSVAL(NULL); - return JS_FALSE; - } - - pgr = JS_GetInstancePrivate(cx, obj, &pgresult_class, NULL); - assert(pgr != NULL); - - *rval = INT_TO_JSVAL(PQnfields(pgr)); - - return JS_TRUE; -} - -static JSBool -pgresult_m_PQfname(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - PGresult *pgr; - char *res; - - if (argc != 1) { - QUEUE_EXCEPTION("Wrong number of arguments"); - goto err; - } - if (!JSVAL_IS_INT(argv[0])) { - QUEUE_EXCEPTION("Argument not an int"); - goto err; - } - - pgr = JS_GetInstancePrivate(cx, obj, &pgresult_class, NULL); - assert(pgr != NULL); - - res = PQfname(pgr, JSVAL_TO_INT(argv[0])); - *rval = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, res)); - - return JS_TRUE; - -err: - *rval = OBJECT_TO_JSVAL(NULL); - - return JS_FALSE; -} - -static JSBool -pgresult_m_PQfnumber(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - PGresult *pgr; - char *str; - - if (argc != 1) { - QUEUE_EXCEPTION("Wrong number of arguments"); - goto err; - } - if (!JSVAL_IS_STRING(argv[0])) { - QUEUE_EXCEPTION("Argument not a String"); - goto err; - } - str = JS_GetStringBytes(JSVAL_TO_STRING(argv[0])); - - pgr = JS_GetInstancePrivate(cx, obj, &pgresult_class, NULL); - assert(pgr != NULL); - - *rval = INT_TO_JSVAL(PQfnumber(pgr, str)); - - return JS_TRUE; -err: - *rval = OBJECT_TO_JSVAL(NULL); - - return JS_FALSE; -} - -/* - * Note: Oid is a typedef to unsigned int. Thus, we use a double object to - * make sure that we do not loose any precision. - */ -static JSBool -pgresult_m_PQftable(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - PGresult *pgr; - Oid oid; - - if (argc != 1) { - QUEUE_EXCEPTION("Wrong number of arguments"); - goto err; - } - if (!JSVAL_IS_INT(argv[0])) { - QUEUE_EXCEPTION("Argument not an int"); - goto err; - } - - pgr = JS_GetInstancePrivate(cx, obj, &pgresult_class, NULL); - assert(pgr != NULL); - - oid = PQftable(pgr, JSVAL_TO_INT(argv[0])); - - if (!JS_NewDoubleValue(cx, (jsdouble)oid, rval)) { - QUEUE_EXCEPTION("Internal error!"); - goto err; - } - - return JS_TRUE; - -err: - *rval = OBJECT_TO_JSVAL(NULL); - - return JS_FALSE; -} - -static JSBool -pgresult_m_PQftablecol(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - PGresult *pgr; - - if (argc != 1) { - QUEUE_EXCEPTION("Wrong number of arguments"); - goto err; - } - if (!JSVAL_IS_INT(argv[0])) { - QUEUE_EXCEPTION("Argument not an int"); - goto err; - } - - pgr = JS_GetInstancePrivate(cx, obj, &pgresult_class, NULL); - assert(pgr != NULL); - - *rval = INT_TO_JSVAL(PQftablecol(pgr, JSVAL_TO_INT(argv[0]))); - - return JS_TRUE; - -err: - *rval = OBJECT_TO_JSVAL(NULL); - - return JS_FALSE; -} - -/* - * Note: we possibly could return a boolean value instead of an integer - * but the documentation says that other values are reserved for possible - * future use. I am surprised that they do not return enumerated values. - */ -static JSBool -pgresult_m_PQfformat(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - PGresult *pgr; - - if (argc != 1) { - QUEUE_EXCEPTION("Wrong number of arguments"); - goto err; - } - if (!JSVAL_IS_INT(argv[0])) { - QUEUE_EXCEPTION("Argument not an int"); - goto err; - } - - pgr = JS_GetInstancePrivate(cx, obj, &pgresult_class, NULL); - assert(pgr != NULL); - - *rval = INT_TO_JSVAL(PQfformat(pgr, JSVAL_TO_INT(argv[0]))); - - return JS_TRUE; - -err: - *rval = OBJECT_TO_JSVAL(NULL); - - return JS_FALSE; -} - -/* - * Note: Oid is a typedef to unsigned int. Thus, we use a double object to - * make sure that we do not loose any precision. - */ -static JSBool -pgresult_m_PQftype(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - PGresult *pgr; - Oid oid; - - if (argc != 1) { - QUEUE_EXCEPTION("Wrong number of arguments"); - goto err; - } - if (!JSVAL_IS_INT(argv[0])) { - QUEUE_EXCEPTION("Argument not an int"); - goto err; - } - - pgr = JS_GetInstancePrivate(cx, obj, &pgresult_class, NULL); - assert(pgr != NULL); - - oid = PQftype(pgr, JSVAL_TO_INT(argv[0])); - - if (!JS_NewDoubleValue(cx, (jsdouble)oid, rval)) { - QUEUE_EXCEPTION("Internal error!"); - goto err; - } - - return JS_TRUE; - -err: - *rval = OBJECT_TO_JSVAL(NULL); - - return JS_FALSE; -} - -static JSBool -pgresult_m_PQfmod(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - PGresult *pgr; - - if (argc != 1) { - QUEUE_EXCEPTION("Wrong number of arguments"); - goto err; - } - if (!JSVAL_IS_INT(argv[0])) { - QUEUE_EXCEPTION("Argument not an int"); - goto err; - } - - pgr = JS_GetInstancePrivate(cx, obj, &pgresult_class, NULL); - assert(pgr != NULL); - - *rval = INT_TO_JSVAL(PQfmod(pgr, JSVAL_TO_INT(argv[0]))); - - return JS_TRUE; - -err: - *rval = OBJECT_TO_JSVAL(NULL); - - return JS_FALSE; -} - -static JSBool -pgresult_m_PQfsize(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - PGresult *pgr; - - if (argc != 1) { - QUEUE_EXCEPTION("Wrong number of arguments"); - goto err; - } - if (!JSVAL_IS_INT(argv[0])) { - QUEUE_EXCEPTION("Argument not an int"); - goto err; - } - - pgr = JS_GetInstancePrivate(cx, obj, &pgresult_class, NULL); - assert(pgr != NULL); - - *rval = INT_TO_JSVAL(PQfsize(pgr, JSVAL_TO_INT(argv[0]))); - - return JS_TRUE; - -err: - *rval = OBJECT_TO_JSVAL(NULL); - - return JS_FALSE; -} - -static JSBool -pgresult_m_PQbinaryTuples(JSContext *cx, JSObject *obj, uintN argc, - jsval *argv, jsval *rval) -{ - PGresult *pgr; - - if (argc != 0) { - QUEUE_EXCEPTION("Function allows no arguments"); - *rval = OBJECT_TO_JSVAL(NULL); - return JS_FALSE; - } - - pgr = JS_GetInstancePrivate(cx, obj, &pgresult_class, NULL); - assert(pgr != NULL); - - *rval = INT_TO_JSVAL(PQbinaryTuples(pgr)); - - return JS_TRUE; -} - -/* - * Note: The semantics of PGresult.PQgetvalue() is different from the actual - * libpq C function PQgetvalue() in that we return null on NULL fields, and - * that we always return either the binary or text data into the field as a - * String (since ECMAScript Strings objects allow NUL characters). - */ -static JSBool -pgresult_m_PQgetvalue(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - PGresult *pgr; - JSString *str; - char *res; - int row, col; - - if (argc != 2) { - QUEUE_EXCEPTION("Wrong number of arguments"); - goto err; - } - if (!JSVAL_IS_INT(argv[0])) { - QUEUE_EXCEPTION("Argument 1 not an int"); - goto err; - } - if (!JSVAL_IS_INT(argv[1])) { - QUEUE_EXCEPTION("Argument 2 not an int"); - goto err; - } - - pgr = JS_GetInstancePrivate(cx, obj, &pgresult_class, NULL); - assert(pgr != NULL); - - row = JSVAL_TO_INT(argv[0]); - col = JSVAL_TO_INT(argv[1]); - - if (PQgetisnull(pgr, row, col)) { - *rval = OBJECT_TO_JSVAL(NULL); - return JS_TRUE; - } - - if ((res = PQgetvalue(pgr, row, col)) == NULL) { - QUEUE_EXCEPTION("PQgetvalue() == NULL"); - goto err; - } - - if ((str = JS_NewStringCopyN(cx, res, PQgetlength(pgr, row, col))) - == NULL) { - QUEUE_EXCEPTION("Out of memory!"); - goto err; - } - *rval = STRING_TO_JSVAL(str); - - return JS_TRUE; - -err: - *rval = OBJECT_TO_JSVAL(NULL); - - return JS_FALSE; -} - -/* - * Note: unlike the C PQgetisnull() function, which returns 0 or 1, we return - * true or false, since ECMAScript supports booleans. - * Moreover, one no longer needs to call this function in JS if it is to - * subsequently use PQgetvalue(), since ours can return null. - * May still be useful in some situations. - */ -static JSBool -pgresult_m_PQgetisnull(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - PGresult *pgr; - - if (argc != 2) { - QUEUE_EXCEPTION("Wrong number of arguments"); - goto err; - } - if (!JSVAL_IS_INT(argv[0])) { - QUEUE_EXCEPTION("Argument 1 not an int"); - goto err; - } - if (!JSVAL_IS_INT(argv[1])) { - QUEUE_EXCEPTION("Argument 2 not an int"); - goto err; - } - - pgr = JS_GetInstancePrivate(cx, obj, &pgresult_class, NULL); - assert(pgr != NULL); - - *rval = BOOLEAN_TO_JSVAL(PQgetisnull(pgr, JSVAL_TO_INT(argv[0]), - JSVAL_TO_INT(argv[1]))); - - return JS_TRUE; - -err: - *rval = OBJECT_TO_JSVAL(NULL); - - return JS_FALSE; -} - -static JSBool -pgresult_m_PQgetlength(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - PGresult *pgr; - - if (argc != 2) { - QUEUE_EXCEPTION("Wrong number of arguments"); - goto err; - } - if (!JSVAL_IS_INT(argv[0])) { - QUEUE_EXCEPTION("Argument 1 not an int"); - goto err; - } - if (!JSVAL_IS_INT(argv[1])) { - QUEUE_EXCEPTION("Argument 2 not an int"); - goto err; - } - - pgr = JS_GetInstancePrivate(cx, obj, &pgresult_class, NULL); - assert(pgr != NULL); - - *rval = INT_TO_JSVAL(PQgetlength(pgr, JSVAL_TO_INT(argv[0]), - JSVAL_TO_INT(argv[1]))); - - return JS_TRUE; - -err: - *rval = OBJECT_TO_JSVAL(NULL); - - return JS_FALSE; -} - -static JSBool -pgresult_m_PQcmdStatus(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - PGresult *pgr; - - if (argc != 0) { - QUEUE_EXCEPTION("Function allows no arguments"); - *rval = OBJECT_TO_JSVAL(NULL); - return JS_FALSE; - } - - pgr = JS_GetInstancePrivate(cx, obj, &pgresult_class, NULL); - assert(pgr != NULL); - - *rval = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, PQcmdStatus(pgr))); - - return JS_TRUE; -} - -/* - * We could return a double easily, but like the C API are returning a string - */ -static JSBool -pgresult_m_PQcmdTuples(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - PGresult *pgr; - - if (argc != 0) { - QUEUE_EXCEPTION("Function allows no arguments"); - *rval = OBJECT_TO_JSVAL(NULL); - return JS_FALSE; - } - - pgr = JS_GetInstancePrivate(cx, obj, &pgresult_class, NULL); - assert(pgr != NULL); - - *rval = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, PQcmdTuples(pgr))); - - return JS_TRUE; -} - -/* - * Note: Oid is a typedef to unsigned int. Thus, we use a double object to - * make sure that we do not loose any precision. - */ -static JSBool -pgresult_m_PQoidValue(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - PGresult *pgr; - Oid oid; - - if (argc != 0) { - QUEUE_EXCEPTION("Function allows no arguments"); - *rval = OBJECT_TO_JSVAL(NULL); - return JS_FALSE; - } - - pgr = JS_GetInstancePrivate(cx, obj, &pgresult_class, NULL); - assert(pgr != NULL); - - oid = PQoidValue(pgr); - - if (!JS_NewDoubleValue(cx, (jsdouble)oid, rval)) { - QUEUE_EXCEPTION("Internal error!"); - goto err; - } - - return JS_TRUE; - -err: - *rval = OBJECT_TO_JSVAL(NULL); - - return JS_FALSE; -} - - - -/* - * PGcancel object control - */ - -static JSObject * -js_InitPGcancelClass(JSContext *cx, JSObject *obj) -{ - - return (JS_InitClass(cx, obj, NULL, &pgcancel_class, - pgcancel_constructor, 0, NULL, pgcancel_methods, NULL, NULL)); -} - -/* Non instanciable */ -static JSBool -pgcancel_constructor(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - - QUEUE_EXCEPTION("PGcancel class not user-instanciable"); - - return JS_FALSE; -} - -static void -pgcancel_finalize(JSContext *cx, JSObject *obj) -{ - PGcancel *pgc; - - if ((pgc = JS_GetInstancePrivate(cx, obj, &pgcancel_class, NULL)) - != NULL) { - PQfreeCancel(pgc); - (void) JS_SetPrivate(cx, obj, NULL); - } -} - - -/* - * PGcancel object methods - */ - -static JSBool -pgcancel_m_PQfreeCancel(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - PGcancel *pgc; - - if (argc != 0) { - QUEUE_EXCEPTION("Function allows no arguments"); - *rval = OBJECT_TO_JSVAL(NULL); - return JS_FALSE; - } - - if ((pgc = JS_GetInstancePrivate(cx, obj, &pgcancel_class, NULL)) - != NULL) { - PQfreeCancel(pgc); - (void) JS_SetPrivate(cx, obj, NULL); - } - - *rval = OBJECT_TO_JSVAL(NULL); - - return JS_TRUE; -} - -static JSBool -pgcancel_m_PQcancel(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - PGcancel *pgc; - char *str; - size_t len; - JSString *s; - - if (argc != 1) { - QUEUE_EXCEPTION("Wrong number of arguments"); - goto err; - } - if (!JSVAL_IS_STRING(argv[0])) { - QUEUE_EXCEPTION("Argument 1 not a String"); - goto err; - } - - pgc = JS_GetInstancePrivate(cx, obj, &pgcancel_class, NULL); - assert(pgc != NULL); - - s = JSVAL_TO_STRING(argv[0]); - str = JS_GetStringBytes(s); - len = JS_GetStringLength(s); - - *rval = INT_TO_JSVAL(PQcancel(pgc, str, len)); - - return JS_TRUE; - -err: - *rval = OBJECT_TO_JSVAL(NULL); - - return JS_FALSE; -} diff --git a/tests/js-test/src/classes/js_pgsql.h b/tests/js-test/src/classes/js_pgsql.h deleted file mode 100644 index 935b85c..0000000 --- a/tests/js-test/src/classes/js_pgsql.h +++ /dev/null @@ -1,15 +0,0 @@ -/* $Id: js_pgsql.h,v 1.3 2006/07/11 10:25:51 mmondor Exp $ */ - -/* - * Copyright (c) 2006, Matthew Mondor - * ALL RIGHTS RESERVED. - */ - -#ifndef JSPGSQL_H -#define JSPGSQL_H - -#include - -extern JSObject *js_InitPGClass(JSContext *, JSObject *); - -#endif diff --git a/tests/js-test/src/classes/js_signal.c b/tests/js-test/src/classes/js_signal.c deleted file mode 100644 index c93cc69..0000000 --- a/tests/js-test/src/classes/js_signal.c +++ /dev/null @@ -1,231 +0,0 @@ -/* $Id: js_signal.c,v 1.3 2005/12/12 18:34:46 mmondor Exp $ */ - -/* - * Copyright (c) 2005, Matthew Mondor - * ALL RIGHTS RESERVED. - */ - -/* - * Basic UNIX signal services for ECMAScript - * - * XXX - * I have to see what interface I want to export. There are several - * possibilities we could use: - * - Have the shell register signal handlers fore interesting events at - * startup and allow scripts to provide a handler function, which if - * exists upon reception of a signal, gets executed. We might then - * need to add extra custom checks in the shell for special signals - * which if the script doesn't end, might still end the process - * i.e. function would be expected to cause the script to exit upon - * reception of a SIGTERM signal... - * - Provide a sigaction-style interface so that the script would be - * able to define functions to execute upon reception of certain - * signals, or null or such for the signal to be ignored. - * This solution might allow better application customization. - * If assuming this interface, the following would be exported: - * - Signal class, through which static signal properties could be - * accessed for signal numbers I.E. Signal.SIGTERM. - * - A static method to create/set/unset/ignore a signal/handler - * Signal.sigaction()? - * It could be provided with the parameters: - * Signal.sigaction(FD.SIG*, obj); - * Where obj would contain fields sa_handler, sa_mask, sa_flags? - * Signal.kill(pid, sig); - * Signal.sigaltstack(...) ? - * Signal.sigprocmask(...) - * Signal.sigsuspend(mask) - * And maybe provide the sigsetops(3)? We possibly could just allow an - * array or such instead of sigset_t though if wanted. - * I'm not sure I want to provide sigsetjmp()/siglongjmp(). JavaScript has - * exceptions anyways. If allowing sigaltstack(), C would need to allocate - * the stacks, so a stack object would need to be exported or such. I - * don't think I want to support this as it's probably not needed by any of - * the applications I'll write, I don't want to write a threading library - * in JS. - * - I wonder if it would be safe to invoke a JS function in a signal handler. - * If it wasn't, I could simply queue the signal events and then call the - * functions for the queue in normal process context. - * If doing this, sigaction has to be called to catch any signal the - * application wishes, and we would be rolling our own signal handling, - * so if wanted we could potentially provide another interface than - * sigaction to ECMAScript... - */ - - - -#include - -#include -#include -#include -#include -#include - -#include - - - -/* Utility macros */ -#define QUEUE_EXCEPTION(s) do { \ - JS_SetPendingException(cx, \ - STRING_TO_JSVAL(JS_NewStringCopyZ(cx, (s)))); \ -} while (/* CONSTCOND */0) - - - -/* Prototypes */ -static JSBool signal_sm_strerror(JSContext *, JSObject *, uintN, jsval *, - jsval *); - - - -/* Actual class parameters */ -static JSClass signal_class = { - "Signal", 0, JS_PropertyStub, JS_PropertyStub, - JS_PropertyStub, JS_PropertyStub, JS_EnumerateStub, - JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub -}; - -/* Provided static methods */ -static JSFunctionSpec signal_smethods[] = { - { "strerror", signal_sm_strerror, 1, 0, 0 }, - { NULL, NULL, 0, 0, 0 } -}; - -/* - * Provided static properties. - * We use these to provide ECMAScript with the ability to use system-specific - * standard C constant macros without us having to tidiously map them - * individually, or to require other scripts to be used as headers to define - * them. Another possibility would have been to supply these parameters as - * string, but this would have required even slower remapping because of the - * parsing and string comparisions. - * We only include those which we consider necessary for now, others may be - * added easily as needed, provided that they are added in all three maps. - * I might perhaps develop macros and/or functions to map all these easily - * from a single map. - */ - -struct property_spec { - const char *name; - int value; -}; - -#define SP(n) \ - { #n, n } - -static struct property_spec signal_sprops[] = { - SP(SIGHUP), - SP(SIGINT), - SP(SIGQUIT), - SP(SIGILL), - SP(SIGTRAP), - SP(SIGABRT), - SP(SIGEMT), - SP(SIGFPE), - SP(SIGKILL), - SP(SIGBUS), - SP(SIGSEGV), - SP(SIGSYS), - SP(SIGPIPE), - SP(SIGALRM), - SP(SIGTERM), - SP(SIGURG), - SP(SIGSTOP), - SP(SIGTSTP), - SP(SIGCONT), - SP(SIGCHLD), - SP(SIGTTIN), - SP(SIGTTOU), - SP(SIGIO), - SP(SIGXCPU), - SP(SIGXFSZ), - SP(SIGVTALRM), - SP(SIGPROF), - SP(SIGWINCH), - SP(SIGINFO), - SP(SIGUSR1), - SP(SIGUSR2), - SP(SIGPWR), - - { NULL, 0 } -}; - -#undef SP - - - -/* - * Class control functions - */ - -JSObject * -js_InitSignalClass(JSContext *cx, JSObject *obj) -{ - JSObject *proto, *ctor; - struct property_spec *sp; - - if ((proto = JS_InitClass(cx, obj, NULL, &signal_class, NULL, - 0, NULL, NULL, NULL, signal_smethods)) == NULL) { - (void) fprintf(stderr, "Error initializing Signal class\n"); - return NULL; - } - - /* Create static properties */ - if ((ctor = JS_GetConstructor(cx, proto)) == NULL) { - (void) fprintf(stderr, "Signal: JS_GetConstructor == NULL\n"); - return NULL; - } - for (sp = signal_sprops; sp->name != NULL; sp++) { - if (JS_DefineProperty(cx, ctor, sp->name, - INT_TO_JSVAL(sp->value), NULL, NULL, - JSPROP_READONLY | JSPROP_PERMANENT) == JS_FALSE) { - (void) fprintf(stderr, - "Signal: Error defining property %s\n", sp->name); - return NULL; - } - } - - return proto; -} - - - -/* - * Static properties functions - */ - - - -/* - * Static methods - */ - -static JSBool -signal_sm_strerror(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - int error; - JSString *string; - - *rval = OBJECT_TO_JSVAL(NULL); - - if (argc != 1) { - QUEUE_EXCEPTION("Wrong number of arguments"); - return JS_FALSE; - } - if (!JSVAL_IS_INT(*argv)) { - QUEUE_EXCEPTION("Argument not an integer"); - return JS_FALSE; - } - error = (int)JSVAL_TO_INT(*argv); - - if ((string = JS_NewStringCopyZ(cx, "testXXX")) == NULL) { - QUEUE_EXCEPTION("Out of memory"); - return JS_FALSE; - } - - *rval = STRING_TO_JSVAL(string); - - return JS_TRUE; -} diff --git a/tests/js-test/src/classes/js_signal.h b/tests/js-test/src/classes/js_signal.h deleted file mode 100644 index 3690d12..0000000 --- a/tests/js-test/src/classes/js_signal.h +++ /dev/null @@ -1,13 +0,0 @@ -/* $Id: js_signal.h,v 1.1 2005/07/19 19:27:28 mmondor Exp $ */ - -/* - * Copyright (c) 2005, Matthew Mondor - * ALL RIGHTS RESERVED. - */ - -#ifndef JSSIGNAL_H -#define JSSIGNAL_H - -extern JSObject *js_InitSignalClass(JSContext *, JSObject *); - -#endif diff --git a/tests/js-test/src/js-server.c b/tests/js-test/src/js-server.c deleted file mode 100644 index f0bfe38..0000000 --- a/tests/js-test/src/js-server.c +++ /dev/null @@ -1,328 +0,0 @@ -/* $Id: js-server.c,v 1.7 2006/07/12 13:47:14 mmondor Exp $ */ - -/* - * Copyright (c) 2004-2005, Matthew Mondor - */ - -/* - * TODO: - * - * - Verify with Brendan Eich: - * - If reusing the context to execute several other scripts, it is - * important that they not be able to add global properties or methods. - * This seems to currently work using a custom api_class_property_add(). - * This however also required standard properties to be shared - * (JS_PROP_SHARED), otherwise api_class_property_add() would be called - * and even setting values to existing API system properties would fail in - * user scripts. Scealing was also too strict. - * I assumed that JS_AddNamedRoot() was required for the API class to - * never be freed, so that it can be reused after a call to - * js_context_reset(). Perhaps this is not necessary. - */ - - - -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#include - -#include -#include -#include -#include - - - -/* - * DEFINITIONS - */ - -/* Size runtime objects must take to run the GC */ -#define GCBYTES 1048576 /* 1MB */ - -/* Size of stack to allocate for every context */ -#define STACKBYTES 8192 /* 8KB */ - -/* - * Structure used to link a context with custom objects we need to perform - * some cleanup from before destroying the context. Ideally managed via - * mmpool(3) in a real world application for slap management and recycling. - * We'll have one of these per process in our pool of processes. - */ -typedef struct { - JSRuntime *rt; - JSContext *ctx; - JSObject *global, *class_fd, *class_errno, *class_signal, - *class_pgsql; -} js_context_t; - -/* - * To hold loaded file objects (actually mmap(2)ed) - */ -typedef struct { - void *data; - size_t size; -} file_t; - -/* - * Defaults for the global class - */ -static JSClass global_class = { - "global", 0, JS_PropertyStub, JS_PropertyStub, - JS_PropertyStub, JS_PropertyStub, JS_EnumerateStub, - JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub -}; - - - -/* - * PROTOTYPES - */ - -int main(int, char **); -static JSBool branch_callback(JSContext *, JSScript *); - -static file_t *file_load(const char *); -static void file_free(file_t *); - -/* Could be an exported API later on */ -static js_context_t *js_context_init(size_t, size_t); -static void js_context_destroy(js_context_t *); -/* XXX static void js_context_reset(js_context_t *);*/ - - - - - -int -main(int argc, char **argv) -{ - file_t *file; - js_context_t *cctx; - - if (argc != 2) { - (void) fprintf(stderr, "Usage: test \n"); - exit(EXIT_FAILURE); - } - if ((file = file_load(argv[1])) == NULL) { - (void) fprintf(stderr, "Error loading '%s'\n", argv[1]); - exit(EXIT_FAILURE); - } - - /* - * We always need at least one runtime per process, at least one - * context per thread and at least a global object per context - * (standard classes, like Date). - */ - if ((cctx = js_context_init(GCBYTES, STACKBYTES)) == NULL) { - file_free(file); - (void) fprintf(stderr, "js_context_init()\n"); - exit(EXIT_FAILURE); - } - - /* - * This is a very useful and important feature, enable our callback - * function which will get called whenever the script branches - * backwards, returns from a function or exits. It allows us to - * even maintain control in cases where the script loops endlessly. - */ - (void) JS_SetBranchCallback(cctx->ctx, branch_callback); - - /* - * Now enable addProperty() protection for all classes using our - * custom api_class_property_add() function. This will prevent user - * code from adding properties or methods to the API class for - * instance. - * This however requires that properties use the JSPROP_SHARED flag - * since addProperty() method would internally get called to create - * shadow copies for the runtime otherwise. - */ - /* - api_class_protect = JS_TRUE; - */ - - /* - * Now execute script loaded into our file_t. - * We simplify this process by calling JS_EvaluateScript() which - * will first tokenize/compile the result, and then interpret/run it. - * Moreover, it allows the script to optionally return a value - * directly like if it was a function. - * Alternatively, we could use JS_CompileFile() or JS_CompileScript() - * to pre-tokenize the script, and JS_ExecuteScript() to interpret it. - */ - { - jsval rval, pval; - JSString *str; - int i; - - if (JS_EvaluateScript(cctx->ctx, cctx->global, file->data, - file->size, argv[1], 1, &rval)) { - str = JS_ValueToString(cctx->ctx, rval); - (void) printf("Script result: %s\n", - JS_GetStringBytes(str)); - /* - * Attempt to call JS function "callMe" if the script - * created it. - */ - for (i = 0; i < 10; i++) { - pval = INT_TO_JSVAL(i); - if (!JS_CallFunctionName(cctx->ctx, - cctx->global, "callMe", 1, &pval, &rval)) - break; - } - } else { - /* XXX how to obtain error and stack backtrace? */ - } - } - - /* Cleanup */ - file_free(file); - js_context_destroy(cctx); - - exit(EXIT_SUCCESS); -} - -/* - * This function is called during the execution of the script so that we can - * remain in control of the application. If we only allow the scripts to - * define functions for callbacks, we can use the first instance if this event - * to abort the script if wanted, as well. We can then set an alternative - * callback function and execute the script provided functions at specific - * events. Of course, it also would be possible to use setitimer(2) to have - * a SIGALRM signal trigger a function at regular set intervals. - */ -/* ARGSUSED */ -static JSBool -branch_callback(JSContext *ctx, JSScript *script) -{ - static int count = 0; - - if (++count > 1000) { - count = 0; - JS_MaybeGC(ctx); - } - - /* Returning JS_FALSE here aborts the script */ - return JS_TRUE; -} - - - -/* - * Could be an exported API - */ - -static js_context_t * -js_context_init(size_t gc_size, size_t stack_size) -{ - js_context_t *cctx; - - if ((cctx = malloc(sizeof(js_context_t))) == NULL || - (cctx->rt = JS_NewRuntime(gc_size)) == NULL || - (cctx->ctx = JS_NewContext(cctx->rt, stack_size)) == NULL || - (cctx->global = JS_NewObject(cctx->ctx, &global_class, NULL, - NULL)) == NULL || - !JS_InitStandardClasses(cctx->ctx, cctx->global) || - (cctx->class_fd = js_InitFDClass(cctx->ctx, cctx->global)) - == NULL || - (cctx->class_errno = js_InitErrnoClass(cctx->ctx, cctx->global)) - == NULL || - (cctx->class_signal = js_InitSignalClass(cctx->ctx, cctx->global)) - == NULL || - (cctx->class_pgsql = js_InitPGClass(cctx->ctx, cctx->global)) - == NULL) { - /* An error, free any partially allocated resources */ - if (cctx != NULL) - js_context_destroy(cctx); - - return NULL; - } - - return cctx; -} - -static void -js_context_destroy(js_context_t *cctx) -{ - - assert(cctx != NULL); - - if (cctx->ctx != NULL) - JS_DestroyContext(cctx->ctx); - if (cctx->rt != NULL) - JS_DestroyRuntime(cctx->rt); - - free(cctx); -} - -/* - * This function should permit to restore the context to a consistent, known - * state before a new script can be executed using the same context instead of - * having to destroy and recreate contexts everytime. - */ -/* ARGSUSED */ -/* XXX -static void -js_context_reset(js_context_t *cctx) -{ - -} -*/ - - -/* - * Loads specified file and returns a file_t pointer. Note that we only - * internally keep the memory map for read access to the file, rather than an - * open filedescriptor. - */ -static file_t * -file_load(const char *filename) -{ - int fd; - struct stat st; - file_t *file; - - assert(filename != NULL); - - if ((fd = open(filename, O_RDONLY)) != -1) { - if (fstat(fd, &st) == 0) { - if ((file = malloc(sizeof(file_t))) != NULL) { - file->size = (size_t)st.st_size; - - if ((file->data = mmap(NULL, file->size, - PROT_READ, MAP_FILE | MAP_PRIVATE, fd, 0)) - != MAP_FAILED) { - (void) close(fd); - return file; - } - - free(file); - } - } - (void) close(fd); - } - - return NULL; -} - -/* - * Frees a file_t which was returned by a previous file_load() call. - */ -static void -file_free(file_t *file) -{ - - assert(file != NULL); - - (void) munmap(file->data, file->size); - free(file); -} diff --git a/tests/js-test/util/spidermonkey-config b/tests/js-test/util/spidermonkey-config deleted file mode 100755 index 6a3cc76..0000000 --- a/tests/js-test/util/spidermonkey-config +++ /dev/null @@ -1,49 +0,0 @@ -#!/bin/sh -# -# Configure script to help build scripts -# by Matthew Mondor - -CFLAGS='-I/usr/local/spidermonkey/include -DXP_UNIX' -LDFLAGS='-Wl,-R/usr/local/spidermonkey/lib -L/usr/local/spidermonkey/lib -ljs' - -DLDFLAGS='-Wl,-R/usr/local/spidermonkey/lib -L/usr/local/spidermonkey/lib -ljs_dbg' - -VERSION='SpiderMonkey 1.5-rc6A' - -usage() -{ - echo - echo 'Usage: spidermonkey-config [-v] [-c] [-l] [-d]' - echo - echo ' -v : Shows SpiderMonkey version' - echo ' -c : Shows flags suitable to append to $CFLAGS' - echo ' -l : Shows flags suitable to append to $LDFLAGS' - echo ' -d : Shows debugging versions of the flags' - echo -} - -if [ -z $@ ]; then - usage - exit 0 -fi - -while getopts dclv c; do - case $c in - d) - LDFLAGS="$DLDFLAGS" - ;; - c) - echo $CFLAGS - ;; - l) - echo $LDFLAGS - ;; - v) - echo $VERSION - ;; - *) - usage - exit 0 - ;; - esac -done diff --git a/tests/kqueue/GNUmakefile b/tests/kqueue/GNUmakefile deleted file mode 100644 index 4ae0616..0000000 --- a/tests/kqueue/GNUmakefile +++ /dev/null @@ -1,22 +0,0 @@ -# $Id: GNUmakefile,v 1.7 2006/04/23 04:40:14 mmondor Exp $ - -MMLIB_PATH := ../../mmlib - -MMLIBS := $(addprefix ${MMLIB_PATH}/,mmpool.o mmstring.o mmarch.o) -LIBS := -lc -OBJS := main.o net.o kqueue.o sendq.o recvq.o packets.o daemon.o client.o -CFLAGS += -Wall -BINS := daemon - -all: $(BINS) - -%.o: %.c - cc -c ${CFLAGS} -I. -I${MMLIB_PATH} -o $@ $< - - -daemon: $(MMLIBS) $(LIBS) $(OBJS) - cc -o $@ $(MMLIBS) $(OBJS) $(LIBS) - - -clean: - rm -f $(BINS) $(OBJS) $(MMLIBS) $(LIBS) diff --git a/tests/kqueue/README b/tests/kqueue/README deleted file mode 100644 index 3d58fc4..0000000 --- a/tests/kqueue/README +++ /dev/null @@ -1,48 +0,0 @@ -$Id: README,v 1.3 2006/04/23 04:40:14 mmondor Exp $ - -If this all works fine, it might be the basis of a multiplayer -networking game. However, this is primarily a test to learn kqueue -and observe its efficiency, as well as to experiment using TCP for -such massive multiplayer games on today's networks and internet. - -Many games that used TCP for LAN use a while back had to develop -UDP based protocols to be efficient enough for dialup users or -other internet users. We will determine if this is still necessary -on today's average internet latencies. - -We probably still want to prioritize some packets over others. -For instance, we might drop outgoing position update packets which -cannot be sent immediately instead of queueing them, allowing less -frequent updates to slow connections while not needing to provide -multiple independent game heart rates. - -We do not expect to have to port the server part of the game, wich -is intended to run on BSD systems supporting the kqueue(2) system -only. We could have used an event library, however we wanted to -avoid including non-BSD licensed components. Although there is a -BSD licensed libevent as part of NetBSD, using kqueue directly -allows a few fancy optimization tricks which would require a new -portable events library to be designed to allow to achieve the full -advantages of kqueue using an abstracted library. - -The client code, however, will be portable and should be able to -run on Win32 without needing the cygwin runtime libraries. The -SDL dependency is reasonable, and can be provided separately, so -mingw will be used to create those executables. The client will -however primarily be developped on NetBSD. Like SDL, OpenGL -libraries shouldn't be a problem either. - -To avoid using cygwin, and because Win32 winsock does not conform -to BSD sockets API standards, we expect to have to write a simple -portable networking layer for use in the client. - - -TODO - -- At this point to advance further a client needs to be written. -- We probably don't need to align packets, we only need to make sure - that their size is 32-bit aligned, and that their 16-bit fields are - 16-bit aligned, their 32-bit ones 32-bit aligned. Packet type field - can be 8-bit. If we allowed arbitrary offsets when buffering packets, - we would need to copy each packet to an aligned buffer as we process them. - Evaluate which is more advantageous. diff --git a/tests/kqueue/client.c b/tests/kqueue/client.c deleted file mode 100644 index 7fa0caa..0000000 --- a/tests/kqueue/client.c +++ /dev/null @@ -1,152 +0,0 @@ -/* $Id: client.c,v 1.9 2006/04/23 04:40:14 mmondor Exp $ */ - -/* - * Copyright (c) 2006, Matthew Mondor - * ALL RIGHTS RESERVED. - */ - - - -#include -#include -#include -#include -#include - -#include "client.h" -#include "kqueue.h" -#include "conf.h" - - - -list_t clients_list; -static list_t clients_gc_list; -static pool_t clients_pool; - - - -void -client_init(void) -{ - DLIST_INIT(&clients_list); - DLIST_INIT(&clients_gc_list); - - if (!pool_init(&clients_pool, "clients_pool", malloc, free, NULL, - NULL, sizeof(client_t), MAX_CLIENTS + 1, 1, 1)) { - syslog(LOG_NOTICE, "pool_init() - %s", strerror(errno)); - exit(EXIT_FAILURE); - } - -} - -/* - * Enables the write polling filter for the specified client. - */ -void -client_enable_write_polling(void *args) -{ - client_t *c = (client_t *)args; - - if (c->writepolling) - return; - - kqueue_sev_alloc(1); - kqueue_sev_add(c->fd, EVFILT_WRITE, EV_ENABLE, 0, 0, (intptr_t)c); - - c->writepolling = 1; -} - -inline int -client_write(client_t *c, uint8_t *buf, size_t size, int buffer) -{ - - if (sendq_write(&c->sendq, buf, size, client_enable_write_polling, c, - buffer) == -1) { - syslog(LOG_NOTICE, "sendq_write(%u) - %s", size, - strerror(errno)); - return -1; - } - - return 0; -} - -/* - * XXX We should do connection rate/concurrency checking on address here. - */ -client_t * -client_create(int fd, struct sockaddr *saddr) -{ - client_t *c; - - if (DLIST_NODES(&clients_list) > MAX_CLIENTS) { - syslog(LOG_NOTICE, "client_create() - MAX_CLIENTS reached"); - return NULL; - } - - if ((c = (client_t *)pool_alloc(&clients_pool, FALSE)) != NULL) { - if (sendq_init(&c->sendq, fd, SENDQ_SIZE) != -1) { - if (recvq_init(&c->recvq, fd, RECVQ_SIZE) != -1) { - c->fd = fd; - (void) memcpy(&c->saddr, saddr, - sizeof(struct sockaddr)); - - c->object.x = random() % WORLD_X_MAX; - c->object.y = random() % WORLD_Y_MAX; - c->object.direction = c->object.i_direction = - random() % 1000; - c->object.thrust = c->object.i_thrust = - random() % 20; - - c->authenticated = c->todestroy = - c->toclose = 0; - c->writepolling = 1; - c->askedping = 0; - - DLIST_APPEND(&clients_list, &c->node); - - return c; - } - syslog(LOG_NOTICE, - "client_create() - recvq_init() - %s", - strerror(errno)); - sendq_destroy(&c->sendq); - } - syslog(LOG_NOTICE, - "client_create() - sendq_init() - %s", strerror(errno)); - pool_free((pnode_t *)c); - } - syslog(LOG_NOTICE, "client_create() - pool_alloc() - %s", - strerror(errno)); - - return NULL; -} - -void -client_destroy_mark(client_t *c) -{ - - if (c->todestroy) - return; - - c->todestroy = 1; - DLIST_SWAP(&clients_gc_list, &clients_list, &c->node, FALSE); -} - -void -client_destroy_marked(void) -{ - node_t *i, *n; - client_t *c; - - for (i = DLIST_TOP(&clients_gc_list); i != NULL; i = n) { - n = DLIST_NEXT(i); - - c = (client_t *)i; - /* Closing descriptor automatically deletes its kevents */ - (void) close(c->fd); - recvq_destroy(&c->recvq); - sendq_destroy(&c->sendq); - pool_free((pnode_t *)c); - } - DLIST_INIT(&clients_gc_list); -} diff --git a/tests/kqueue/client.h b/tests/kqueue/client.h deleted file mode 100644 index a9575fc..0000000 --- a/tests/kqueue/client.h +++ /dev/null @@ -1,59 +0,0 @@ -/* $Id: client.h,v 1.7 2006/04/03 08:56:45 mmondor Exp $ */ - -/* - * Copyright (c) 2006, Matthew Mondor - * ALL RIGHTS RESERVED. - */ - - - -#ifndef _CLIENT_H_ -#define _CLIENT_H_ - - - -#include -#include - -#include -#include - -#include "sendq.h" -#include "recvq.h" - - - -typedef struct object { - node_t region; - int32_t direction, thrust; - int32_t i_direction, i_thrust; - int32_t x, y; -} object_t; - -typedef struct client { - node_t node; - object_t object; - sendq_t sendq; - recvq_t recvq; - int fd; - struct sockaddr saddr; - int authenticated, todestroy, toclose, writepolling, - askedping; -} client_t; - - - -extern list_t clients_list; - - -void client_init(void); -void client_enable_write_polling(void *); -inline int client_write(client_t *, u_int8_t *, size_t, int); - -client_t *client_create(int, struct sockaddr *); -void client_destroy_mark(client_t *); -void client_destroy_marked(void); - - - -#endif diff --git a/tests/kqueue/conf.h b/tests/kqueue/conf.h deleted file mode 100644 index 5720c1a..0000000 --- a/tests/kqueue/conf.h +++ /dev/null @@ -1,39 +0,0 @@ -/* $Id: conf.h,v 1.9 2006/04/23 04:40:14 mmondor Exp $ */ - -/* - * Copyright (c) 2006, Matthew Mondor - * ALL RIGHTS RESERVED. - */ - - - -#ifndef _CONF_H_ -#define _CONF_H_ - - - -#define SERVER_VERSION 1 -#define SERVER_STRING "daemon/mmondor\n" - -#define FPS 10 -#define FPS_MS (1000 / FPS) - -#define WORLD_X_MAX 640 -#define WORLD_Y_MAX 480 - -#define MAX_CLIENTS 128 -#define R_EVENTS (MAX_CLIENTS * 2) -#define S_EVENTS (MAX_CLIENTS * 2) - -#define SENDQ_SIZE 16384 -#define RECVQ_SIZE 1024 - -/* In seconds */ -#define INPUT_TIMEOUT 30 -#define PING_TIMEOUT 5 - -#define PIDFILE "/tmp/daemon.pid" - - - -#endif diff --git a/tests/kqueue/daemon.c b/tests/kqueue/daemon.c deleted file mode 100644 index bd38e65..0000000 --- a/tests/kqueue/daemon.c +++ /dev/null @@ -1,124 +0,0 @@ -/* $Id: daemon.c,v 1.3 2006/04/23 04:40:14 mmondor Exp $ */ - -/* - * Copyright (c) 2006, Matthew Mondor - * ALL RIGHTS RESERVED. - */ - -/* - * Procedure for becoming an detached daemon, as well as to prepare process - * signal handling. Since we'll be catching signal events through kqueue(2), - * We simply ignore all signals for which we don't want the default behavior. - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include - - - -static int signal_init(void); -static void pidfile_write(const char *); - - - -static int -signal_init(void) -{ - struct sigaction act; - int i; - int sigs[] = { - SIGHUP, - SIGINT, - SIGPIPE, - SIGALRM, - SIGTERM, - SIGTTIN, - SIGTTOU, - SIGIO, - SIGXCPU, - SIGXFSZ, - SIGVTALRM, - SIGPROF, - SIGUSR1, - SIGUSR2 - }; - - act.sa_handler = SIG_IGN; - act.sa_flags = SA_NOCLDSTOP; - (void) sigemptyset(&act.sa_mask); - - for (i = 0; i < (sizeof(sigs) / sizeof(int)); i++) { - if (sigaction(sigs[i], &act, NULL) != 0) { - syslog(LOG_NOTICE, - "signal_init() - sigaction(%d) - %s", - sigs[i], strerror(errno)); - return -1; - } - } - - return 0; -} - -/* - * Writes our process ID number to specified file. To be called before - * chroot(2) or dropping privileges. - */ -static void -pidfile_write(const char *file) -{ - char str[16]; - int fd; - - if ((fd = open(file, O_CREAT | O_TRUNC | O_WRONLY, 0600)) != -1) { - (void) snprintf(str, 15, "%d\n", getpid()); - (void) write(fd, str, mm_strlen(str)); - (void) close(fd); - } else - syslog(LOG_NOTICE, "pidfile_write() - open(%s) - %s", - file, strerror(errno)); -} - -int -daemon_init(void) -{ - pid_t pid; - int fd; - - /* Create new process */ - if ((pid = fork()) == -1) { - syslog(LOG_NOTICE, "fork() - %s", strerror(errno)); - return -1; - } - if (pid != 0) - exit(EXIT_SUCCESS); - - pidfile_write(PIDFILE); - - /* Create new process group and detach */ - (void) setsid(); - (void) chdir("/"); - if ((fd = open("/dev/null", O_RDWR)) != -1) { - (void) dup2(fd, STDIN_FILENO); - (void) dup2(fd, STDOUT_FILENO); - (void) dup2(fd, STDERR_FILENO); - if (fd > STDERR_FILENO) - (void) close(fd); - } else - syslog(LOG_NOTICE, "daemon_init() - open(/dev/null) - %s", - strerror(errno)); - - if (signal_init() != 0) - return -1; - - return 0; -} diff --git a/tests/kqueue/daemon.h b/tests/kqueue/daemon.h deleted file mode 100644 index 32ec407..0000000 --- a/tests/kqueue/daemon.h +++ /dev/null @@ -1,19 +0,0 @@ -/* $Id: daemon.h,v 1.2 2006/03/31 20:57:08 mmondor Exp $ */ - -/* - * Copyright (c) 2006, Matthew Mondor - * ALL RIGHTS RESERVED. - */ - - - -#ifndef _DAEMON_H_ -#define _DAEMON_H_ - - - -int daemon_init(void); - - - -#endif diff --git a/tests/kqueue/kqueue.c b/tests/kqueue/kqueue.c deleted file mode 100644 index a5a8628..0000000 --- a/tests/kqueue/kqueue.c +++ /dev/null @@ -1,329 +0,0 @@ -/* $Id: kqueue.c,v 1.11 2006/04/03 21:06:08 mmondor Exp $ */ - -/* - * Copyright (c) 2006, Matthew Mondor - * ALL RIGHTS RESERVED. - */ - - - -#include -#include -#include -#include -#include -#include - -#include - -#include "kqueue.h" -#include "client.h" -#include "packets.h" -#include "net.h" -#include "conf.h" - - - -static struct kevent rev[R_EVENTS], sev[S_EVENTS]; -static int kqid, rev_cnt, sev_cnt; - - - -/* The following functions simply exit on failure, because they are critical */ - -void -kqueue_init(void) -{ - - if ((kqid = kqueue()) == -1) { - syslog(LOG_NOTICE, "kqueue_init() - kqueue() - %s", - strerror(errno)); - exit(EXIT_FAILURE); - } - - sev_cnt = 0; -} - -void -kqueue_addlisten(int fd) -{ - - EV_SET(sev, fd, EVFILT_READ, EV_ADD | EV_ENABLE, 0, 0, (intptr_t)NULL); - if (kevent(kqid, sev, 1, NULL, 0, NULL) == -1) { - syslog(LOG_NOTICE, "kqueue_addlisten() - kevent(%d) - %s", - fd, strerror(errno)); - exit(EXIT_FAILURE); - } - -} - -void -kqueue_addsignal(int sig) -{ - - EV_SET(sev, sig, EVFILT_SIGNAL, EV_ADD | EV_ENABLE, 0, 0, - (intptr_t)NULL); - if (kevent(kqid, sev, 1, NULL, 0, NULL) == -1) { - syslog(LOG_NOTICE, "kqueue_addsignal() - kevent(%d) - %s", - sig, strerror(errno)); - exit(EXIT_FAILURE); - } -} - -void -kqueue_addtimer0(int64_t ms) -{ - - EV_SET(sev, 0, EVFILT_TIMER, EV_ADD | EV_ENABLE, 0, ms, - (intptr_t)NULL); - if (kevent(kqid, sev, 1, NULL, 0, NULL) == -1) { - syslog(LOG_NOTICE, "kevent_addtimer0() - kevent(%lld) - %s", - ms, strerror(errno)); - exit(EXIT_FAILURE); - } -} - -void -kqueue_main(void) -{ - - for (;;) { - int e, i; - struct kevent *kev; - client_t *c; - - /* - * Fusion to only require one kevent(2) call if there are - * events to send. Wait for events to occur. - * This also allows us to catch error events. - */ - if ((rev_cnt = kevent(kqid, sev, sev_cnt, rev, R_EVENTS, NULL)) - == -1) { - syslog(LOG_NOTICE, "kevent(1) - %s", strerror(errno)); - continue; - } - sev_cnt = 0; - - /* Run through received events and process them */ - for (e = 0; e < rev_cnt; e++) { - kev = &rev[e]; - - /* Report errors if any */ - if ((kev->flags & EV_ERROR) != 0) { - syslog(LOG_NOTICE, - "EV_ERROR: ident=%d filter=%d flags=%d " - "fflags=%d data=%lld udata=%p", - kev->ident, kev->filter, kev->flags, - kev->fflags, kev->data, - (void *)kev->udata); - continue; - } - - /* Process signals if any */ - if (kev->filter == EVFILT_SIGNAL) { - switch (kev->ident) { - case SIGTERM: - syslog(LOG_NOTICE, - "Received SIGTERM, exiting"); - exit(EXIT_SUCCESS); - break; - } - continue; - } - - /* Process timeouts if any */ - if (kev->filter == EVFILT_TIMER) { - if (kev->ident == 0) { - /* Main heartrate timer */ - update(); - continue; - } - /* - * XXX input timeout timers? - * We'll need an input timer per socket, - * which gets canceled whenever input arrives, - * but once expires launches a server ping - * request which then should trigger another - * timer, which if pong occurs gets restarted, - * but if it exceeds client gets dropped. - * We'll also need to match timer events with - * filedescriptors, as well as reason for - * timeout... And we'll need to make sure to - * clean out any pending timers when marking a - * client to be destroyed (or when destroying - * them, at least). - */ - } - - if (kev->ident == listen_fd) { - int t; - - /* - * Accept new connections, create clients and - * add new descriptors to the kqueue set, - * buffering them to minimize syscalls. - */ - for (i = 0, t = kev->data; i < t; i++) { - struct sockaddr saddr; - socklen_t saddrl; - client_t *c; - int fd; - - saddrl = sizeof(struct sockaddr); - if ((fd = accept(listen_fd, &saddr, - &saddrl)) == -1) - continue; - - if ((c = client_create(fd, &saddr)) - == NULL) { - (void) close(fd); - continue; - } - - kqueue_sev_alloc(2); - kqueue_sev_add(fd, EVFILT_READ, - EV_ADD | EV_EOF | EV_ENABLE, 0, 0, - (intptr_t)c); - kqueue_sev_add(fd, EVFILT_WRITE, - EV_ADD | EV_EOF | EV_ENABLE, 0, 0, - (intptr_t)c); - - /* Send auth request packet */ - if (spacket_auth_send(c) == -1) { - client_destroy_mark(c); - continue; - } - } - continue; - } - - /* - * Don't process any more events for marked to be - * destroyed clients, or those without an associated - * udata. When client descriptor must be closed and - * client structure freed, we make sure to first mark - * it as to destroy within this loop, since it's - * possible for more than one event to occur for a - * single descriptor. We use client_destroy_mark() - * for this purpose. - */ - if ((c = (client_t *)kev->udata) == NULL) { - /* XXX */ - syslog(LOG_NOTICE, "udata == NULL"); - continue; - } - if (c->todestroy) - continue; - - if ((kev->flags & EV_EOF) != 0) { - client_destroy_mark(c); - continue; - } - - if (kev->filter == EVFILT_WRITE) { - int f; - - /* - * If there's a sendq for the client, - * attempt to flush it. If there's an error, - * drop client. - * If client was marked to be closed, and we - * finished flusing data, also mark it to be - * destroyed, since were done with it. - */ - if ((f = sendq_flush(&c->sendq, kev->data)) - == -1 || (f == 0 && c->toclose)) - client_destroy_mark(c); - else if (f == 0) { - /* - * No more data to send, we can thus - * temporarily disable write events - * for this descriptor. client_write() - * will re-enable it as necessary. - */ - kqueue_sev_alloc(1); - kqueue_sev_add(c->fd, EVFILT_WRITE, - EV_DISABLE, 0, 0, (intptr_t)c); - c->writepolling = 0; - } - continue; - } - - if (kev->filter == EVFILT_READ) { - int16_t *buf; - size_t size; - - /* - * Data to read from client. Simply read it - * into a queue to process it at the next - * server heartbeat event. - * XXX We should be able to process ping - * requests immediately. This means that - * we need recvq_read() to report more - * information. - */ - if (recvq_read(&c->recvq, &buf, &size) == -1) { - client_destroy_mark(c); - continue; - } - - /* Convert packet_type to host endian */ - *buf = BYTEORDER_HOST16(*buf); - - /* - * If ping packet, process immediately - * and discard packet from recvq. - * We only allow ping if c->askedping - * is unset, and we set it. It will be unset - * at the next server heart beat. This way, - * we only allow clients to ping once per - * frame at most. - */ - if (*buf == CPACKET_PING && - size == sizeof(struct cpacket_ping)) { - if (!c->authenticated || - c->askedping || - spacket_pong_send(c) == -1) { - client_destroy_mark(c); - continue; - } - recvq_rewind(&c->recvq, size); - } - - continue; - } - - } - /* Destroy marked to be destroyed clients */ - client_destroy_marked(); - } - /* NOTREACHED */ -} - - -/* Utility functions */ - -inline void -kqueue_sev_alloc(int n) -{ - - if (sev_cnt > S_EVENTS - n) { - if (kevent(kqid, sev, sev_cnt, NULL, 0, NULL) == -1) - syslog(LOG_NOTICE, - "kqueue_sev_alloc() - kevent() - %s", - strerror(errno)); - sev_cnt = 0; - } -} - -inline void -kqueue_sev_add(uintptr_t ident, uint32_t filter, uint32_t flags, - uint32_t fflags, int64_t data, intptr_t udata) -{ - - /* - * Be careful to avoid macro side effects, do not use &sev[sev_cnt++] - */ - EV_SET(&sev[sev_cnt], ident, filter, flags, fflags, data, udata); - sev_cnt++; -} diff --git a/tests/kqueue/kqueue.h b/tests/kqueue/kqueue.h deleted file mode 100644 index 700e063..0000000 --- a/tests/kqueue/kqueue.h +++ /dev/null @@ -1,31 +0,0 @@ -/* $Id: kqueue.h,v 1.4 2006/04/01 00:46:24 mmondor Exp $ */ - -/* - * Copyright (c) 2006, Matthew Mondor - * ALL RIGHTS RESERVED. - */ - - - -#ifndef _KQUEUE_H_ -#define _KQUEUE_H_ - - - -#include -#include - - - -void kqueue_init(void); -void kqueue_addlisten(int); -void kqueue_addsignal(int); -void kqueue_addtimer0(int64_t); -void kqueue_main(void); -inline void kqueue_sev_alloc(int); -inline void kqueue_sev_add(uintptr_t, uint32_t, uint32_t, uint32_t, - int64_t, intptr_t); - - - -#endif diff --git a/tests/kqueue/main.c b/tests/kqueue/main.c deleted file mode 100644 index 24611a0..0000000 --- a/tests/kqueue/main.c +++ /dev/null @@ -1,62 +0,0 @@ -/* $Id: main.c,v 1.10 2006/04/01 00:46:24 mmondor Exp $ */ - -/* - * Copyright (c) 2006, Matthew Mondor - * ALL RIGHTS RESERVED. - */ - - - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include "daemon.h" -#include "net.h" -#include "sendq.h" -#include "packets.h" -#include "client.h" -#include "conf.h" -#include "kqueue.h" - - - -int main(int, char **); - - - -int -main(int argc, char **argv) -{ - - (void) openlog("daemon", LOG_NDELAY | LOG_PID, LOG_USER); - - if (daemon_init() != 0) - exit(EXIT_FAILURE); - syslog(LOG_NOTICE, "Started"); - - /* - * The following funtions exit whenever there's a problem, because - * they set up critical things. - */ - client_init(); - kqueue_init(); - net_init(); - kqueue_addlisten(listen_fd); - kqueue_addsignal(SIGTERM); - kqueue_addtimer0(FPS_MS); - - kqueue_main(); - /* NOTREACHED */ - - return EXIT_SUCCESS; -} diff --git a/tests/kqueue/net.c b/tests/kqueue/net.c deleted file mode 100644 index 6202c98..0000000 --- a/tests/kqueue/net.c +++ /dev/null @@ -1,131 +0,0 @@ -/* $Id: net.c,v 1.2 2006/04/01 00:46:24 mmondor Exp $ */ - -/* - * Copyright (c) 2006, Matthew Mondor - * ALL RIGHTS RESERVED. - */ - - - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include - -#include "net.h" -#include "conf.h" - - - -static int net_listen(const char *, int, int); - - - -int listen_fd; - - - -void -net_init(void) -{ - - if ((listen_fd = net_listen("0.0.0.0", 7777, MAX_CLIENTS)) == -1) { - syslog(LOG_NOTICE, "net_listen() - %s", strerror(errno)); - exit(EXIT_FAILURE); - } -} - -static int -net_listen(const char *addr, int port, int backlog) -{ - int fd, opt; - struct linger linger; - struct protoent *pent; - struct sockaddr_in server; - - fd = -1; - - mm_memclr(&server, sizeof(struct sockaddr_in)); - server.sin_family = AF_INET; - if (inet_pton(AF_INET, addr, &server.sin_addr) != 1) { - syslog(LOG_NOTICE, "inet_pton(%s) - %s", addr, - strerror(errno)); - goto err; - } - server.sin_port = htons((short)port); - - if ((fd = socket(AF_INET, SOCK_STREAM, 0)) == -1) { - syslog(LOG_NOTICE, "socket() - %s", strerror(errno)); - goto err; - } - - opt = 1; - if ((setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(int))) - == -1) - syslog(LOG_NOTICE, "setsockopt(SO_REUSEADDR) - %s", - strerror(errno)); - if ((setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &opt, sizeof(int))) - == -1) - syslog(LOG_NOTICE, "setsockopt(SO_KEEPALIVE) - %s", - strerror(errno)); - - linger.l_onoff = 0; - linger.l_linger = 0; - if ((setsockopt(fd, SOL_SOCKET, SO_LINGER, &linger, - sizeof(struct linger))) == -1) - syslog(LOG_NOTICE, "setsockopt(SO_LINGER) - %s", - strerror(errno)); - - /* XXX Set buffer sizes? */ - - if ((pent = getprotobyname("TCP")) != NULL) { - opt = 1; - if ((setsockopt(fd, pent->p_proto, TCP_NODELAY, &opt, - sizeof(int))) == -1) - syslog(LOG_NOTICE, "setsockopt(TCP_NODELAY) - %s", - strerror(errno)); - } else - syslog(LOG_NOTICE, "getprotobyname(TCP) - %s", - strerror(errno)); - - if ((opt = fcntl(fd, F_GETFL, NULL)) != -1) { - if (fcntl(fd, F_SETFL, opt | O_NONBLOCK) == -1) { - syslog(LOG_NOTICE, "fcntl(F_SETFL) - %s", - strerror(errno)); - goto err; - } - } else - syslog(LOG_NOTICE, "fcntl(F_GETFL) - %s", strerror(errno)); - - if ((bind(fd, (struct sockaddr *)&server, sizeof(struct sockaddr_in))) - != 0) { - syslog(LOG_NOTICE, "bind(%s:%d) - %s", addr, port, - strerror(errno)); - goto err; - } - - if (listen(fd, backlog) == -1) { - syslog(LOG_NOTICE, "listen(%s:%d) - %s", addr, port, - strerror(errno)); - goto err; - } - - return fd; - -err: - if (fd != -1) - (void) close(fd); - - return -1; -} diff --git a/tests/kqueue/net.h b/tests/kqueue/net.h deleted file mode 100644 index 3607cc9..0000000 --- a/tests/kqueue/net.h +++ /dev/null @@ -1,23 +0,0 @@ -/* $Id: net.h,v 1.2 2006/04/01 00:46:24 mmondor Exp $ */ - -/* - * Copyright (c) 2006, Matthew Mondor - * ALL RIGHTS RESERVED. - */ - - - -#ifndef _NET_H_ -#define _NET_H_ - - - -void net_init(void); - - - -extern int listen_fd; - - - -#endif diff --git a/tests/kqueue/packets.c b/tests/kqueue/packets.c deleted file mode 100644 index 83da958..0000000 --- a/tests/kqueue/packets.c +++ /dev/null @@ -1,327 +0,0 @@ -/* $Id: packets.c,v 1.14 2006/04/03 20:38:43 mmondor Exp $ */ - -/* - * Copyright (c) 2006, Matthew Mondor - * ALL RIGHTS RESERVED. - */ - -/* - * To validate a received packet, we'll make sure that it's larger than - * sizeof(int), and that the first integer consists of a valid expected packet - * type ID within range. If so, we verify if packet length really corresponds - * to the packet type, and then can interpret the packet information. - * An actual network packet may contain a number of these packets. - * We thus must be able to efficiently determine each packet's length. - * Since we're using TCP, it should be enough. However, on the server side - * especially, proper sanity checking on input must be done to avoid crashing - * the server because of unexpected data processing. - */ - - - -#include - -#include -#include - -#include "packets.h" -#include "client.h" -#include "sendq.h" -#include "conf.h" - - - -static int cpacket_auth_handler(client_t *, uint16_t *); -static int cpacket_ping_handler(client_t *, uint16_t *); -static int cpacket_pong_handler(client_t *, uint16_t *); -static int cpacket_direction_handler(client_t *, uint16_t *); -static int cpacket_thrust_handler(client_t *, uint16_t *); -static int cpacket_torp_handler(client_t *, uint16_t *); -static int cpacket_quit_handler(client_t *, uint16_t *); - - - -static struct packet_index cpacket_index[CPACKET_MAX] = { - {sizeof(struct cpacket_auth), cpacket_auth_handler}, - {sizeof(struct cpacket_ping), cpacket_ping_handler}, - {sizeof(struct cpacket_pong), cpacket_pong_handler}, - {sizeof(struct cpacket_direction), cpacket_direction_handler}, - {sizeof(struct cpacket_thrust), cpacket_thrust_handler}, - {sizeof(struct cpacket_torp), cpacket_torp_handler}, - {sizeof(struct cpacket_quit), cpacket_quit_handler} -}; - - - -void -update(void) -{ - node_t *nod, *next; - client_t *c; - uint8_t *data; - size_t size, off; - - for (nod = DLIST_TOP(&clients_list); nod != NULL; nod = next) { - next = DLIST_NEXT(nod); - c = (client_t *)nod; - - if (c->todestroy || c->toclose) - continue; - - /* Reset ping request throttling flag */ - c->askedping = 0; - - /* First process incomming packets */ - recvq_content(&c->recvq, &data, &size); - for (off = 0; off < size; ) { - int16_t *id; - - /* - * Verify packet type ID. It already has been - * converted to host endian byte order, unlike other - * packet fields. - */ - id = (int16_t *)&data[off]; - *id = BYTEORDER_HOST16(*id); - if (*id < 0 || *id > CPACKET_MAX) { - client_destroy_mark(c); - break; - } - - /* There must be enough data for packet size */ - if (size - off < cpacket_index[*id].size) { - /* - * Kill authentucated client if other packets - * than CPACKET_AUTH - */ - if (!c->authenticated && *id != CPACKET_AUTH) - goto k; - - /* - * XXX We'll need to also do special - * processing for chat packets, since they'll - * have arbitrary length. They'll still need - * to be 16-bit aligned, too. - */ - - /* Kill client on packet processing error */ - if (cpacket_index[*id].handler(c, - (uint16_t *)off) == -1) - goto k; - } else - goto k; - - /* Process next packet if any */ - off += cpacket_index[*id].size; - continue; - -k: - client_destroy_mark(c); - break; - } - - /* - * XXX I don't like having to run the clients list all over, - * to then run through all objects (including clients) to - * update them, and then yet again through all objects to send - * updates to all clients. - */ - - /* Run game frame on all objects */ - DLIST_FOREACH(&clients_list, nod) { - client_t *c = (client_t *)nod; - - /* - if (!c->authenticated) - continue; - */ - - /* - * First update direction and thrust. - * XXX I could do a small function or macro to deal - * with similar situations, i.e. - * change_update(int goal, int *current, int steps); - */ - if (c->object.direction < c->object.i_direction) - c->object.direction++; - else if (c->object.direction > c->object.i_direction) - c->object.direction--; - if (c->object.thrust < c->object.i_thrust) - c->object.thrust++; - else if (c->object.thrust > c->object.i_thrust) - c->object.thrust--; - - /* - * Translate object x/y position according to - * trust/direction. When reaching borders we simply - * bounce for now. - * (I need to review my trigonometry a bit again :) - */ - /* XXX */ - c->object.x += 1 - (random() & 2); - if (c->object.x < 0) - c->object.x = 0; - else if (c->object.x > WORLD_X_MAX - 1) - c->object.x = WORLD_X_MAX - 1; - c->object.y += 1 - (random() & 2); - if (c->object.y < 0) - c->object.y = 0; - else if (c->object.y > WORLD_Y_MAX - 1) - c->object.y = WORLD_Y_MAX - 1; - } - - /* Send update frame information packets */ - DLIST_FOREACH(&clients_list, nod) { - client_t *c = (client_t *)nod; - node_t *nod2; - - /* - if (!c->authenticated) - continue; - */ - - DLIST_FOREACH(&clients_list, nod2) { - if (spacket_position_send(c, - &((client_t *)nod2)->object) == -1) - break; - } - (void) sendq_flush(&c->sendq, -1); - } - } -} - - -int -spacket_auth_send(client_t *c) -{ - struct spacket_auth p; - - p.packet_type = BYTEORDER_NETWORK16(SPACKET_AUTH); - mm_memclr(p.string, 32); - mm_strncpy(p.string, SERVER_STRING, 31); - p.protocol_version = BYTEORDER_NETWORK16(SERVER_VERSION); - /* XXX */ - - return client_write(c, (uint8_t *)&p, sizeof(p), 0); -} - -int -spacket_pong_send(client_t *c) -{ - struct spacket_pong p; - - p.packet_type = BYTEORDER_NETWORK16(SPACKET_PONG); - - return client_write(c, (uint8_t *)&p, sizeof(p), 0); -} - -int -spacket_position_send(client_t *c, object_t *o) -{ - struct spacket_position p; - - p.packet_type = BYTEORDER_NETWORK16(SPACKET_POSITION); - p.object_id = p.object_type = BYTEORDER_NETWORK16(0); /* XXX */ - p.x = BYTEORDER_NETWORK16(o->x); - p.y = BYTEORDER_NETWORK16(o->y); - p.direction - BYTEORDER_NETWORK16(o->direction); - - return client_write(c, (uint8_t *)&p, sizeof(p), 1); -} - - -/* Note that only the packet_type field is endian converted yet. */ - -static int -cpacket_auth_handler(client_t *c, uint16_t *ptr) -{ - struct cpacket_auth *p = (struct cpacket_auth *)ptr; - - p->protocol_version = BYTEORDER_HOST16(p->protocol_version); - - if (c->authenticated || p->protocol_version < SERVER_VERSION) - return -1; - - /* - * XXX For now. Eventually also check user/password - * We could use APOP-like authentication where server sends random - * data as part of the authentication greeting/request, and expect - * client to append password to random data and send hashed result. - * We also should do user concurrency checking here. - */ - c->authenticated = 1; - - return 0; -} - -/* - * Note: Following function is never used, since pings are handled in the - * kqueue main loop code. - */ -/* ARGSUSED */ -static int -cpacket_ping_handler(client_t *c, uint16_t *ptr) -{ - /* NOOP */ - - return 0; -} - -static int -cpacket_pong_handler(client_t *c, uint16_t *ptr) -{ -/* struct cpacket_pong *p = (struct cpacket_pong *)ptr;*/ - - /* XXX */ - - return 0; -} - -static int -cpacket_direction_handler(client_t *c, uint16_t *ptr) -{ - struct cpacket_direction *p = (struct cpacket_direction *)ptr; - - p->direction = BYTEORDER_HOST16(p->direction); - - /* Radians */ - if (p->direction < 0 || p->direction > 1000) - return -1; - - c->object.i_direction = p->direction; - - return 0; -} - -static int -cpacket_thrust_handler(client_t *c, uint16_t *ptr) -{ - struct cpacket_thrust *p = (struct cpacket_thrust *)ptr; - - p->thrust = BYTEORDER_HOST16(p->thrust); - - if (p->thrust < 0 || p->thrust > 20) - return -1; - - c->object.i_thrust = p->thrust; - - return 0; -} - -static int -cpacket_torp_handler(client_t *c, uint16_t *ptr) -{ -/* struct cpacket_torp *p = (struct cpacket_torp *)ptr;*/ - - /* XXX */ - - return 0; -} - -/* ARGSUSED */ -static int -cpacket_quit_handler(client_t *c, uint16_t *ptr) -{ - - return -1; -} diff --git a/tests/kqueue/packets.h b/tests/kqueue/packets.h deleted file mode 100644 index ea668d1..0000000 --- a/tests/kqueue/packets.h +++ /dev/null @@ -1,160 +0,0 @@ -/* $Id: packets.h,v 1.9 2006/04/03 20:37:51 mmondor Exp $ */ - -/* - * Copyright (c) 2006, Matthew Mondor - * ALL RIGHTS RESERVED. - */ - -/* - * All packets must begin with an int16_t packet_type. All fields of a packet - * must either be [u]int8_t, [u]int16_t, [u]int32+t or [u]int64_t. Moreover, - * all fields will be passed in network/big endian byte order when sent over - * the network. We could have used a union, but this would have resulted in - * larger, fixed sized packets wasting bandwidth. Since a server to client - * update will generally involve sending more than one such packet in a row, - * it is important to minimize their size. - * Note: If using 32-bit fields within a packet, make sure that they are - * 32-bit aligned. - */ - - - -#ifndef _PACKETS_H_ -#define _PACKETS_H_ - - - -#include - -#include "client.h" - - - -/* - * For every server packet type we support, an index array will be used - * with this structure to call the associated handler function, which will - * perform sanity checking and process the packet, returning 0 on success or - * -1 on error. The size field will allow to perform sanity checking on - * data size prior to calling the handler function, as well as to increase the - * buffer pointer. - */ -struct packet_index { - size_t size; - int (*handler)(client_t *, uint16_t *); -}; - - - -/* - * Server to client packets - */ - -enum spacket_types { - SPACKET_AUTH = 0, - SPACKET_PING, - SPACKET_PONG, - SPACKET_POSITION, - SPACKET_COLLISION, - SPACKET_MAX -}; - -/* Used to request client authentication and respond success/failure */ -struct spacket_auth { - int16_t packet_type; - int16_t protocol_version; - char string[32]; - /* XXX */ -} __attribute__((__packed__)); - -/* And for server to test idle connections */ -struct spacket_ping { - int16_t packet_type; -} __attribute__((__packed__)); - -/* And clients to test latency */ -struct spacket_pong { - int16_t packet_type; -} __attribute__((__packed__)); - -/* Object position/direction update to client */ -struct spacket_position { - int16_t packet_type; - int16_t object_id, object_type; - int16_t x, y, direction; -} __attribute__((__packed__)); - -/* Collision/detonation update to client */ -struct spacket_collision { - int16_t packet_type; - int16_t collision_type; - int16_t object1_id, object2_id; -} __attribute__((__packed__)); - - - -/* - * Client to server packets - */ - -enum cpacket_tyoes { - CPACKET_AUTH = 0, - CPACKET_PING, - CPACKET_PONG, - CPACKET_DIRECTION, - CPACKET_THRUST, - CPACKET_TORP, - CPACKET_QUIT, - CPACKET_MAX -}; - -/* Used to respond to server authentication request */ -struct cpacket_auth { - int16_t packet_type; - int16_t protocol_version; - char string[32]; - /* XXX */ -} __attribute__((__packed__)); - -/* To ping server for latency mesurement */ -struct cpacket_ping { - int16_t packet_type; -} __attribute__((__packed__)); - -/* To respond to server ping requests */ -struct cpacket_pong { - int16_t packet_type; -} __attribute__((__packed__)); - -/* Angle/direction change request */ -struct cpacket_direction { - int16_t packet_type; - int16_t direction; -} __attribute__((__packed__)); - -/* Speed change request */ -struct cpacket_thrust { - int16_t packet_type; - int16_t thrust; -} __attribute__((__packed__)); - -/* Torpedo fire request */ -struct cpacket_torp { - int16_t packet_type; - int16_t direction; -} __attribute__((__packed__)); - -/* Game quit request */ -struct cpacket_quit { - int16_t packet_type; -} __attribute__((__packed__)); - - - -void update(void); -int spacket_auth_send(client_t *); -int spacket_pong_send(client_t *); -int spacket_position_send(client_t *, object_t *); - - - -#endif diff --git a/tests/kqueue/recvq.c b/tests/kqueue/recvq.c deleted file mode 100644 index 663ecba..0000000 --- a/tests/kqueue/recvq.c +++ /dev/null @@ -1,84 +0,0 @@ -/* $Id: recvq.c,v 1.5 2006/04/05 09:19:16 mmondor Exp $ */ - -/* - * Copyright (c) 2006, Matthew Mondor - * ALL RIGHTS RESERVED. - */ - -#include -#include -#include -#include - -#include "recvq.h" - - - -int -recvq_init(recvq_t *q, int fd, size_t size) -{ - - if ((q->buffer = malloc(size)) == NULL) - return -1; - - q->fd = fd; - q->size = size; - q->tail = 0; - - return 0; -} - -void -recvq_destroy(recvq_t *q) -{ - - free(q->buffer); -} - -int -recvq_read(recvq_t *q, int16_t **buf, size_t *size) -{ - ssize_t s; - - if (q->tail == q->size) - return -1; - - if (((s = read(q->fd, &q->buffer[q->tail], q->size - q->tail)) == -1 && - errno != EAGAIN) || s == 0) - return -1; - - /* - * Verify that s is a multiple of 2 before accepting it. This will - * allow all packets and their fields to be 16-bit aligned. - */ - if (((uint16_t)s & 1) != 0) { - syslog(LOG_NOTICE, "Uneven packet from %d", q->fd); - return -1; - } - - *buf = (int16_t *)&q->buffer[q->tail]; - *size = s; - q->tail += s; - - return 0; -} - -/* - * Useful to discard ping packets which we process on the fly - */ -void -recvq_rewind(recvq_t *q, size_t size) -{ - - if (q->tail >= size) - q->tail -= size; -} - -void -recvq_content(recvq_t *q, uint8_t **buf, size_t *size) -{ - - *buf = (uint8_t *)q->buffer; - *size = q->tail; - q->tail = 0; -} diff --git a/tests/kqueue/recvq.h b/tests/kqueue/recvq.h deleted file mode 100644 index 54e6eee..0000000 --- a/tests/kqueue/recvq.h +++ /dev/null @@ -1,35 +0,0 @@ -/* $Id: recvq.h,v 1.4 2006/04/03 20:37:51 mmondor Exp $ */ - -/* - * Copyright (c) 2006, Matthew Mondor - * ALL RIGHTS RESERVED. - */ - - - -#ifndef _RECVQ_H_ -#define _RECVQ_H_ - - - -#include - - - -typedef struct recvq { - int fd; - uint8_t *buffer; - size_t size, tail; -} recvq_t; - - - -int recvq_init(recvq_t *, int, size_t); -void recvq_destroy(recvq_t *); -int recvq_read(recvq_t *, int16_t **, size_t *); -void recvq_rewind(recvq_t *, size_t); -void recvq_content(recvq_t *, uint8_t **, size_t *); - - - -#endif diff --git a/tests/kqueue/sendq.c b/tests/kqueue/sendq.c deleted file mode 100644 index 7ea0052..0000000 --- a/tests/kqueue/sendq.c +++ /dev/null @@ -1,148 +0,0 @@ -/* $Id: sendq.c,v 1.5 2006/04/03 20:37:51 mmondor Exp $ */ - -/* - * Copyright (c) 2006, Matthew Mondor - * ALL RIGHTS RESERVED. - */ - - - -#include -#include -#include -#include -#include -#include - -#include -#include - -#include "sendq.h" - - - -/* - * Initializes a sendq object, allocating the queue buffer - */ -int -sendq_init(sendq_t *q, int fd, size_t size) -{ - - if ((q->buffer = malloc(size)) == NULL) - return -1; - - q->fd = fd; - q->size = size; - q->head = q->tail = 0; - - return 0; -} - -/* - * Destroys a sendq object, freeing the queue buffer - */ -void -sendq_destroy(sendq_t *q) -{ - - free(q->buffer); -} - -/* - * Attempts to write to the non-blocking socket. In the case of a partial - * write, queue the remaining of the buffer. In case where the buffer is - * full, return an error (-1), in which case the sendq has been exceeded and - * client should be dropped. We only write(2) if there is no pending buffers - * already, of course. If bfunc is not NULL, it will be called with passed - * argument bfuncarg whenever data that couldn't be written immediately is - * buffered. If buffer is true, no attempt will be made at a real write; Data - * will simply be buffered if possible. - * - * XXX There is a potential problem where if the client is lagged enough to - * have filled the buffer, although not enough to exceed the queue but that - * the client cannot recoup, we could consider the queue filled and drop the - * client. This because we aren't really using a real FIFO buffer. This code - * should probably use the mmfifo(3) library after the multiple bytes buffer - * functions in it have been debugged and tested properly. - * However, since if the user was this behind this would in practice mean that - * his commands would take a while to be reflected, this is probably okay for - * the time being. - */ -int -sendq_write(sendq_t *q, uint8_t *buf, size_t size, - void (*bfunc)(void *), void *bfuncarg, int buffer) -{ - - /* Sendq empty? Attempt to write(2) immediately */ - if (!buffer && q->head == q->tail) { - ssize_t s; - - /* Only allow EAGAIN */ - if ((s = write(q->fd, buf, size)) == -1 && errno != EAGAIN) - return -1; - - /* - * If we wrote everything, simply return successfuly. - * Otherwise, fix our arguments to the unwritten buffer. - */ - if (s == size) - return 0; - else { - size -= s; - buf += s; - } - } - - /* - * If the sendq buffer wasn't empty, or that there remains bytes - * after a partial write(2), queue the buffer if there's enough room. - * If there isn't enough room, return error. If a function was - * supplied with an argument, also call this function if we buffer - * data. This may allow our caller to enable back write polling - * events for this descriptor. - */ - if (size > 0) { - if (q->tail + size > q->size) - return -1; - - (void) mm_memcpy(&q->buffer[q->tail], buf, size); - q->tail += size; - if (bfunc != NULL) - bfunc(bfuncarg); - } - - return 0; -} - -/* - * If any pending buffers exist, attempts to write them. In the case of an - * error, returns -1, in which case the client should be dropped. If there - * still remains unflushed data, 1 is returned. Otherwise, 0 is. - */ -int -sendq_flush(sendq_t *q, size_t room) -{ - - if (q->head != q->tail) { - ssize_t ns; - size_t os = q->tail - q->head; - - /* Only attempt write(2) syscall if there is room */ - if (room == 0) - return 1; - - if ((ns = write(q->fd, &q->buffer[q->head], os)) == -1) { - if (errno == EAGAIN) - return 1; - else - return -1; - } - - if ((q->head += ns) == q->tail) - q->head = q->tail = 0; - else - return 1; - } - - return 0; -} diff --git a/tests/kqueue/sendq.h b/tests/kqueue/sendq.h deleted file mode 100644 index 022543f..0000000 --- a/tests/kqueue/sendq.h +++ /dev/null @@ -1,37 +0,0 @@ -/* $Id: sendq.h,v 1.5 2006/04/03 20:37:51 mmondor Exp $ */ - -/* - * Copyright (c) 2006, Matthew Mondor - * ALL RIGHTS RESERVED. - */ - - - -#ifndef _SENDQ_H_ -#define _SENDQ_H_ - - - -#include - -#include - - - -typedef struct sendq { - int fd; - uint8_t *buffer; - size_t size, head, tail; -} sendq_t; - - - -int sendq_init(sendq_t *, int, size_t); -void sendq_destroy(sendq_t *); -int sendq_write(sendq_t *, uint8_t *, size_t, void (*)(void *), void *, - int); -int sendq_flush(sendq_t *, size_t); - - - -#endif diff --git a/tests/memory/README b/tests/memory/README deleted file mode 100644 index 52e022f..0000000 --- a/tests/memory/README +++ /dev/null @@ -1,42 +0,0 @@ -$Id: README,v 1.1 2005/11/14 01:55:20 mmondor Exp $ - -The goal of this project is to create new memory allocation functions -which can work on arbitrary pools of memory, while making sure to always -favor low, already used pages. - -For instance, let's consider the case where multiple processes need to -use a fair amount of shared memory. The master process can allocate a -region of memory using mmap(2) with MAP_ANON, making sure to use a large -enough block that'll ever be used by the application. This region can -be shared. - -UVM lazily allocates pages which need to be accessed on-the-fly. This -means that we must use a system similar to brk(2)/sbrk(2) and favor reusing -already allocated memory in order to avoid the application growing too much. -The process stack is for instance allocated this way; A single very large -region is mapped, and the actual memory in use increases with the process' -requirements for more stack space. In the case of stacks, since they are -indeed stacks, the growing problem is not critical and easily determined. - -In the case of a general purpose memory allocator, this is more complex. -mmpool(3) library allows to allocate fixed sized objects efficiently and -makes sure to favor recently used pages in the allocations, which is okay. -However, mmpool(3) must also be given allocation/freeing functions which -it must use to allocate more pages as needed, or to free pages which haven't -been used for some time. This is where the general purpose allocators -are used. - -In the case where a whole fixed large region can be used to allocate a single -type of objects, we can simply use mmpool(3) and let it manage the large -memory page. However, needing to allocate a new memory segment per memory -pool or object type appears problematic in some situations. Moreover, being -able to use a single large region for shared memory, another one for -process-specific or shared mlocked memory per application, makes things -easier. It also allows server administrators to more easily modify various -parameters relating to application scalibility. - -So this general purpose memory allocator should work on arbitrary regions -of memory, and should automatically perform any necessary locking for -synchronization. This is quite similar to the libmm library in use by the -Apache server. This however should be released under BSD license, and -especially favor reusing of previously allocated pages. diff --git a/tests/pthread_utils/GNUmakefile b/tests/pthread_utils/GNUmakefile deleted file mode 100644 index dd75e9c..0000000 --- a/tests/pthread_utils/GNUmakefile +++ /dev/null @@ -1,35 +0,0 @@ -# $Id: GNUmakefile,v 1.5 2005/11/22 09:24:42 mmondor Exp $ - -MMLIB_PATH := ../../mmlib -MMLIBS := $(addprefix ${MMLIB_PATH}/,mmlog.o mmpool.o mmstring.o) -OBJS := mm_pthread_msg.o mm_pthread_sleep.o mm_pthread_pool.o mm_pthread_poll.o -BINS := tests/msg_test tests/poll_test - -CFLAGS += -Wall -#CFLAGS += -DDEBUG -DPTHREAD_DEBUG -g3 - -LDFLAGS += -lc -lpthread -#LDFLAGS += -lpthread_dbg - - -all: $(BINS) - - -%.o: %.c - cc -c ${CFLAGS} -I. -I$(MMLIB_PATH) -o $@ $< - - -tests/msg_test: tests/msg_test.o $(MMLIBS) $(OBJS) - cc ${CFLAGS} -o $@ $@.c $(OBJS) -I. -I$(MMLIB_PATH) ${LDFLAGS} \ - $(MMLIBS) - -tests/poll_test: tests/poll_test.o $(MMLIBS) $(OBJS) - cc ${CFLAGS} -o $@ $@.c $(OBJS) -I. -I$(MMLIB_PATH) ${LDFLAGS} \ - $(MMLIBS) - - -install: all - - -clean: - rm -f tests/msg_test.o tests/poll_test.o $(BINS) $(OBJS) $(MMLIBS) diff --git a/tests/pthread_utils/README b/tests/pthread_utils/README deleted file mode 100644 index 8054ac7..0000000 --- a/tests/pthread_utils/README +++ /dev/null @@ -1,197 +0,0 @@ -This library is an attempt to provide pth library like API to NetBSD SA -threads and kqueue. - -What we find are missing from the POSIX standard are added here: - -- Implementation of efficient messages to communicate among threads. These - messages are queued using an efficient pointer linking mechanism. It must be - possible for a thread to wait for messages while sleeping and to be awaken - when a message is available. It also must be possible to observe a maximum - timeout to wait for. -- Implementation of filedescriptors and above mentionned thread messages - notification multiplexing, with support for timer. An example of this is - pth library's pth_poll_ev(). A timer event can interrupt thread-safe - filedescriptor polling, as well as thread messages arriving on a port. - -We beleive that it is possible to implement this using the kqueue(2)/kevent(2) -system. The new call would be similar to: - -pthread_poll(struct pollfd *fds, int nfds, - struct pthread_port *ports, int nports, - struct pthread_sigs *sigs, int nsigs, - struct pthread_timers *timers, int ntimers) - -or similar system. This would allow multiplexing of various events into a -single application loop. - - -pthread_cond_timedwait() seems especially useful, either with a signal handler -or perhaps using kqueue concurrently... pthread_cond_timedwait() will allow -processes to wait for message arrival though a port, while -pthread_cond_signal() or pthread_cond_broadcast() will be able to awaken them -as messages are queued to the message port. However, we would ideally want to -only signal a wanted thread waiting for a port... But, normally only one -thread should be listening for messages on any given port. I have to see what -I'll do for a thread listening for messages on multiple ports at a time... -Perhaps that multiple ports could use the same conditional wait variable so -that the process would only wait on that one, and then verify the message -queue for each before going back in waiting mode. - - -TODO: -==== -- Replace mutex and conditonal variable initializers, as well as attributes, - with static initializers. -- Provide similar static intializer macros as part of our API where possible. -- I have a working message passing implementation, with possibility of a - waiter on as many ports as wanted. I however still have a challenge: - Multiplex system calls such as select(2), poll(2), connect(2) and accept(2) - with the messaging capability. One must be able to cause the other to - return. This could be tricky to properly implement. Maybe think about the - following ideas: - - Dedicate a thread to serve a syscall, with which communication is solely - done using messages. This however implies that only a single syscall at a - time can be processed by such a thread. This probably means that a pool of - such threads would become necessary. This also assumes that the syscall in - question do not block the whole process, but only the intended thread. - Alot of assumptions, but this would now work properly on all BSDs and - on Linux. Possibly also on Solaris. - - Use a mix of signals and syscalls, since signals can interrupt syscalls. - However, this implies adding capability in our message system to trigger - signals rather than only using a conditional variable to notify of message - arrival. This also probably means that the same signal handler must be - shared by the whole process, that is, all the threads. - - Use kqueue in a thread-safe manner with thread-specific signals (if - possible). kqueue can be used to track signals without the need for an - actual signal handler. It would also track filedescriptor changes at the - same time. This also probably means that we need to use kqueue user - events if possible, triggered from the message passing system. It also - means non-portable code outside of the realm of BSD systems. - - - -RECENT REVIEW AFTER SOME REFLECTION -=================================== - -Currently, pth_accept_ev() and pth_connect_ev() are the only two cases of -special PTh functions which my software uses, notably mmftpd(8). These could -easily be implemented using a random thread in the pool whenever necessary, -with which communication would entirely use messages only. This thread could -be told: Perform syscall in non-blocking mode using the supplied -filedescriptor and notify me weither it succeeded, failed because of a timeout, -if any, or was interrupted by a message event occuring on the specified ring, -if any. The application however has to know that if it was interrupted by -an event, the connection still occurs asynchroneously within the system. -We should verify what could be done to cancel a not yet completed connection, -if possible. This call could also report if the call was interrupted by a -signal arrival (EINTR), optionally. If the socket was supplied in blocking -mode, it would have to be switched to non-blocking mode by the system and -then back into blocking mode. The caller could ensure to set it into -non-blocking mode for enhanced performance if no blocking mode is required. -The challenge would be finding a both efficient and portable solution to -have select()/poll() awake upon reception of notification events on a ring. -Perhaps that a global filedescriptor could be used for this, SOCK_DGRAM and -one byte sent, or that a signal handler with a signal generation should be -used... Both methods would probably awake the whole process, however. -pthread_sigmask() could be used perhaps... I wouldn't want to have a special -fd required for each ready thread of the pool, ideally. - -Implement: -mm_pthread_io pthread_poll_ev(), pthread_accept_ev(), - pthread_connect_ev() -mm_pthread_alarm pthread_sleep(), etc. - -or maybe: - -mm_pthread_misc For all of them - -Perhaps reimplement the system I worked on in mmserver(3) as well. This might -be necessary for operations which really should be dedicated to a non-threaded -process at occasions, and the subsystem should be available. It should probably -use a pool using mmpool(3) as well, just like we are doing with threads. - -It would be interesting to implement better GC for mmpool(3)'s. Currently, -pool_free() will discard pages which are no longer in use since some time, -but the time cannot be linear, since it only accounts a certain number of -calls made to it. It should instead be possible to use time intervals, and -to let the application invoke the GC at wanted fixed intervals. This would -allow to use time based average statistics rather than function call times -based ones, without clobbering process or thread timers which the application -might need. It simply has to provide its own and to call the GC function -regularily. - -Hmm also, would be nice to be able to store the port_t pointer of the port -which triggered notification on a ring_t, so that callers don't need to -run through several ports attached on a ring... Maybe that it would be -problematic however, since we can't guarantee atomicity between messages and -messages processing, unless we kludged the whole thing with locks and lost -efficiency. And because we only trigger notification to wakeup a waiting -thread when a message is queued on an empty port, it's possible that the -applicaton sleeps forever on a port if it didn't totally empty it, unless -there was a way for the sleep function to immediately return if called on -non-empty ports (as it's only alled on rings, and that rings don't have -access to a list of ports in current implementation (only the ports can -know which ring they are tied to)... I could implement something to have -rings see their attached ports with a list, however. But this again means -looping among ports to see if they're non-empty, heh, so why not let the -application do it as they do now. - - - -IMPORTANT -========= - -I did a test where multiple threads were polling on a single filedescriptor -consisting of a socketpair, which other side was used to wake them up. -Only one random thread would wake up. - -Using a signal to cause all threads to wake would not work either, because -then again only a random thread will awake. - -It appears that the only way to ensure to wake wanted threads is using -conditional variables and for them to only sleep on these. - -SIGIO possibility... threads would be sleeping on a conditional wait variable -corresponding to the filedescriptor. For polling, the fd would be made in -non-blocking I/O, with SIGIO sent to process. The fd and associated cond var -would be added to a table. The SIGIO signal handler would need to check all -fds in the set for possible I/O and awake corresponding threads waiting on -cond var. A problem exists: How to check a filedescriptor for pending event? -How to know if event is read or write, or hup, etc? Maybe using more ore less -standard FION ioctls? poll/select with 0 timeout maybe, but that is still -troublesome in terms of performance I beleive. - -fd cond interesting_events occured_events? - -Hmm and what if a thread was allocated to start polling, and another wrapper -thread wait for it sleeping on a cond var? Would it be sane to do this? -When a timeout or message occurs however detected by the wrapper thread, -how would we stop the other thread polling? We still have a problem. -If we left pending polling threads, how would a future thread with successful -polling on the same descriptor ever wake up, a random one would. -Why does POSIX threads suck so much as to not provide any decent way to -work with filedescriptors!? If at least I had pthread_signal() it would -help. I could send a signal to interrupt the wanted thread when it was polling. -Or if only there was a way to set the wanted signal mask for wanted processes -as necessary, so that I would only have the wanted one process a particular -signal I could send to interrupt it and then restore the masks, and do this -somehow atomically. If POSIX had any of these requirements in mind while -developing the standard, pthread_poll_condwait() or pthread_signal() would -already exist anyways! - - -HMM -=== - -A thread reserved for polling would seem best. We need to be able to interrupt -that thread whenever needed using a signal, which all other processes must -be blocking. We could use SIGIO, or SIGUSR2 for instance. That thread would -process thread messages and go back to polling. It probably could handle -timeouts as well, but this is probably not necessary. If it did, would -probably free other threads from calling gettimeofday() too often. The thread -has to remove the fd from the polling list when an event returned on it -anyways, and so it could also send a reply message for timeout. It has to -be interrupted anyways when a new fd is to be added, and this means that -it could fix the poll timer before calling it each time to fit the soonest -to expire fd... I could probably use kqueue too, or libevent in that thread -to make it high performance as possible. diff --git a/tests/pthread_utils/mm_pthread_debug.h b/tests/pthread_utils/mm_pthread_debug.h deleted file mode 100644 index 2db67bc..0000000 --- a/tests/pthread_utils/mm_pthread_debug.h +++ /dev/null @@ -1,63 +0,0 @@ -/* $Id: mm_pthread_debug.h,v 1.2 2006/02/05 13:00:48 mmondor Exp $ */ - -/* - * Copyright (C) 2004-2005, Matthew Mondor - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Matthew Mondor. - * 4. The name of Matthew Mondor may not be used to endorse or promote - * products derived from this software without specific prior written - * permission. - * 5. Redistribution of source code may not be released under the terms of - * any GNU Public License derivate. - * - * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - - - -#ifndef MM_PTHREAD_DEBUG_H -#define MM_PTHREAD_DEBUG_H - - - -#include -#include - - - -#ifdef PTHREAD_DEBUG - -#define DEBUG_PTHREAD_ENTRY() \ - syslog(LOG_NOTICE, "> TID=%p FN=%s", pthread_self(), __func__) - -#define DEBUG_PTHREAD_EXIT() \ - syslog(LOG_NOTICE, "< TID=%p FN=%s", pthread_self(), __func__) - -#else -#define DEBUG_PTHREAD_ENTRY() -#define DEBUG_PTHREAD_EXIT() -#endif - - - -#endif diff --git a/tests/pthread_utils/mm_pthread_msg.c b/tests/pthread_utils/mm_pthread_msg.c deleted file mode 100644 index d1fc7f0..0000000 --- a/tests/pthread_utils/mm_pthread_msg.c +++ /dev/null @@ -1,466 +0,0 @@ -/* $Id: mm_pthread_msg.c,v 1.14 2006/02/05 13:00:48 mmondor Exp $ */ - -/* - * Copyright (C) 2005, Matthew Mondor - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Matthew Mondor. - * 4. The name of Matthew Mondor may not be used to endorse or promote - * products derived from this software without specific prior written - * permission. - * 5. Redistribution of source code may not be released under the terms of - * any GNU Public License derivate. - * - * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -/* - * It is almost a shame that POSIX did not define a standard API for - * inter-thread asynchroneous and synchroneous messaging. So, here is my - * implementation. Note that for asynchroneous operation it is recommended to - * use a memory pool such as mmpool(3) to allocate and free messages in an - * efficient way, in cases where messages will need to be sent to the other - * end without expecting a response back before the current function ends - * (in which case a message obviously can't be on the stack). - */ - - - -#include -#include -#include -#include -#include - -#include -#include - -#include -#include -/*#include */ - - - -MMCOPYRIGHT("@(#) Copyright (c) 2005\n\ -\tMatthew Mondor. All rights reserved.\n"); -MMRCSID("$Id: mm_pthread_msg.c,v 1.14 2006/02/05 13:00:48 mmondor Exp $"); - - - -/* - * Allows to initialize a polling notification handle. When attached to a - * port, a message arriving on an empty port causes the associated ring to - * wake the thread from pthread_ring_wait(). - */ -int -pthread_ring_init(pthread_ring_t *ring) -{ - int error; - - DEBUG_PTHREAD_ENTRY(); - DEBUG_ASSERT(ring != NULL && ring->magic != PRING_MAGIC); - - if ((error = pthread_cond_init(&ring->cond, NULL)) == 0) { - if ((error = pthread_mutex_init(&ring->mutex, NULL)) == 0) { - ring->magic = PRING_MAGIC; - ring->event = ring->mevent = 0; - DEBUG_PTHREAD_EXIT(); - return 0; - } - (void) pthread_cond_destroy(&ring->cond); - } - - DEBUG_PTHREAD_EXIT(); - return error; -} - -/* - * Returns TRUE if the supplied ring is a valid/usable one, or FALSE - * otherwise. Useful to conditionally destroy it. - */ -int -pthread_ring_valid(pthread_ring_t *ring) -{ - - DEBUG_PTHREAD_ENTRY(); - - DEBUG_PTHREAD_EXIT(); - return (ring != NULL && ring->magic == PRING_MAGIC); -} - -/* - * Destroys a ring. Note that all message ports attached to this ring should - * first be detached or destroyed. - */ -int -pthread_ring_destroy(pthread_ring_t *ring) -{ - int error; - - DEBUG_PTHREAD_ENTRY(); - DEBUG_ASSERT(ring != NULL && ring->magic == PRING_MAGIC); - - if ((error = pthread_mutex_destroy(&ring->mutex)) == 0) - error = pthread_cond_destroy(&ring->cond); - ring->magic = 0; - - DEBUG_PTHREAD_EXIT(); - return error; -} - -/* - * Causes the current thread to sleep until a message arrives on an empty port - * associated with this ring. In normal operation, a thread only goes in wait - * mode after it processed all queued messages on all interesting ports. - * However, provision is made so that a the function returns immediately if - * messages already were received on a port attached to this ring since the - * last call to pthread_ring_wait(). - * Although using such an absolute time timespec might be disadvantageous for - * the API compared to a timeout in milliseconds for instance, this was chosen - * to remain API-compatible with pthread_cond_timedwait(), and upwards - * compatible with systems where nanosecond precision can be achieved. - */ -int -pthread_ring_wait(pthread_ring_t *ring, const struct timespec *abstime) -{ - int error = 0; - - DEBUG_PTHREAD_ENTRY(); - DEBUG_ASSERT(ring != NULL && ring->magic == PRING_MAGIC); - - /* We must hold the condition variable's mutex */ - if (pthread_mutex_lock(&ring->mutex) != 0) { - error = -1; - goto err; - } - - /* As long as we don't have confirmation that we must stop waiting */ - for (ring->event = 0; ring->mevent == 0 && - !ring->event && error == 0; ) { - /* - * Wait on conditional variable, which will automatically - * and atomically release the mutex and return with the mutex - * locked again, as soon as the conditional variable gets - * signaled. - */ - if (abstime != NULL) { - error = pthread_cond_timedwait(&ring->cond, - &ring->mutex, abstime); - } else - error = pthread_cond_wait(&ring->cond, &ring->mutex); - } - ring->mevent = 0; - - /* - * And we know that conditional waiting functions returned with mutex - * locked, so now release it back. - */ - (void) pthread_mutex_unlock(&ring->mutex); - -err: - DEBUG_PTHREAD_EXIT(); - return error; -} - -/* - * Allows to wake up waiter(s) on the specified ring, which are sleeping - * threads within pthread_ring_wait(). This can be used to simulate the - * arrival of a message on an empty port. Also useful to use rings as a - * notification system only when no message passing is needed. - */ -int -pthread_ring_notify(pthread_ring_t *ring) -{ - int error; - - DEBUG_PTHREAD_ENTRY(); - DEBUG_ASSERT(ring != NULL && ring->magic == PRING_MAGIC); - - if ((error = pthread_mutex_lock(&ring->mutex)) == 0) { - ring->mevent++; - ring->event = 1; - (void) pthread_cond_signal(&ring->cond); - (void) pthread_mutex_unlock(&ring->mutex); - } - - DEBUG_PTHREAD_EXIT(); - return error; -} - -/* - * Allows to initialize/create a message port. - */ -int -pthread_port_init(pthread_port_t *port) -{ - int error; - - DEBUG_PTHREAD_ENTRY(); - DEBUG_ASSERT(port != NULL && port->magic != PPORT_MAGIC); - - if ((error = pthread_mutex_init(&port->lock, NULL)) != 0) - goto err; - - port->magic = PPORT_MAGIC; - port->ring = NULL; - DLIST_INIT(&port->messages); - -err: - DEBUG_PTHREAD_EXIT(); - return error; -} - -/* - * Returns TRUE if the supplied port is valid/usable, or FALSE otherwise. - * Useful to conditionally destroy a port, for instance. - */ -int -pthread_port_valid(pthread_port_t *port) -{ - - DEBUG_PTHREAD_ENTRY(); - - DEBUG_PTHREAD_EXIT(); - return (port != NULL && port->magic == PPORT_MAGIC); -} - -/* - * Destroys the specified port, previously created using pthread_port_init(). - */ -int -pthread_port_destroy(pthread_port_t *port) -{ - - DEBUG_PTHREAD_ENTRY(); - DEBUG_ASSERT(port != NULL && port->magic == PPORT_MAGIC); - - port->magic = 0; - - DEBUG_PTHREAD_EXIT(); - return pthread_mutex_destroy(&port->lock); -} - -/* - * Attaches a port to a ring. Multiple ports may be attached to a ring. A - * message arriving on an empty port will cause the attached ring to be - * notified, if any, and as such to cause a thread waiting on the ring to - * be awakened. - */ -int -pthread_port_set_ring(pthread_port_t *port, pthread_ring_t *ring) -{ - - DEBUG_PTHREAD_ENTRY(); - DEBUG_ASSERT(port != NULL && port->magic == PPORT_MAGIC && - (ring == NULL || ring->magic == PRING_MAGIC)); - - port->ring = ring; - - DEBUG_PTHREAD_EXIT(); - return 0; -} - -/* - * Allows to initialize a message before it can be sent over a port. The - * message only needs to be initialized once in general, even if it will be - * used for bidirectional transmission for synchronous operation. If the - * reply port needs to be changed, however, this function should be used again - * to set the new reply port. - */ -int -pthread_msg_init(pthread_msg_t *msg, pthread_port_t *rport) -{ - - DEBUG_PTHREAD_ENTRY(); - DEBUG_ASSERT(msg != NULL && msg->magic != PMESG_MAGIC && - (rport == NULL || rport->magic == PPORT_MAGIC)); - - msg->magic = PMESG_MAGIC; - msg->reply = rport; - msg->size = 0; - msg->message = NULL; - - DEBUG_PTHREAD_EXIT(); - return 0; -} - -/* - * Returns TRUE if supplied message is valid/usable or FALSE otherwise. - */ -int -pthread_msg_valid(pthread_msg_t *msg) -{ - - DEBUG_PTHREAD_ENTRY(); - - DEBUG_PTHREAD_EXIT(); - return (msg != NULL && msg->magic == PMESG_MAGIC); -} - -/* - * Invalidates a message, so that it can no longer be sent over ports. - */ -int -pthread_msg_destroy(pthread_msg_t *msg) -{ - - DEBUG_PTHREAD_ENTRY(); - DEBUG_ASSERT(msg != NULL && msg->magic == PMESG_MAGIC); - - msg->magic = 0; - - DEBUG_PTHREAD_EXIT(); - return 0; -} - -/* - * If any message exists in the queue of the specified port, unqueues it and - * returns it. Otherwise, NULL is returned. In normal operation, all messages - * queued to a port are processed before putting the thread back into sleep, - * mainly for efficiency, but also because it eases synchronization. - */ -pthread_msg_t * -pthread_msg_get(pthread_port_t *port) -{ - pthread_msg_t *msg = NULL; - - DEBUG_PTHREAD_ENTRY(); - DEBUG_ASSERT(port != NULL && port->magic == PPORT_MAGIC); - - if (pthread_mutex_lock(&port->lock) != 0) - goto err; - - if ((msg = DLIST_TOP(&port->messages)) != NULL) { - DEBUG_ASSERT(msg->magic == PMESG_MAGIC); - DLIST_UNLINK(&port->messages, (node_t *)msg); - } - - (void) pthread_mutex_unlock(&port->lock); - -err: - DEBUG_PTHREAD_EXIT(); - return (pthread_msg_t *)msg; -} - -/* - * Queues the specified message to the specified port, returning 0 on success. - * Note that the message data is not copied or moved, but that a pointer - * system is used to queue the message. Thus, the message's shared memory - * region is leased temporarily to the other end. One has to be careful to - * not allocate this message space on the stack when asynchroneous operation - * is needed. In synchroneous operation mode, it is not a problem, since the - * sender does not have to modify the data until the other end replies back - * with the same message after modifying the message if necessary. In - * synchroneous mode, we simply delegate that message memory region to the - * other end until it notifies us with a reply that it is done working with - * it. Returns 0 on success, or an error number. - */ -int -pthread_msg_put(pthread_port_t *port, pthread_msg_t *msg) -{ - int error = 0; - - DEBUG_PTHREAD_ENTRY(); - DEBUG_ASSERT(port != NULL && port->magic == PPORT_MAGIC && - msg != NULL && msg->magic == PMESG_MAGIC); - - if ((error = pthread_mutex_lock(&port->lock)) != 0) - goto err; - - DLIST_APPEND(&port->messages, (node_t *)msg); - if (port->ring != NULL) { - if (DLIST_NODES(&port->messages) == 1) { - /* - * We know that there previously were no messages, - * and that the reading thread then waits for any - * message to be available. Signal it that there at - * least is one message ready. The other end should - * normally process all available messages before - * going back into waiting. - */ - if ((error = pthread_mutex_lock(&port->ring->mutex)) - == 0) { - port->ring->event = 1; - (void) pthread_cond_signal(&port->ring->cond); - (void) pthread_mutex_unlock( - &port->ring->mutex); - } - } - /* - * If the other end, however, is already locked - * waiting for the ring to be notified while - * there already are messages, we still trigger mevent - * to cause it to unlock, however. This behavior is - * useful in the polling system code, for instance. - */ - /* XXX We don't use a mutex for now... */ - port->ring->mevent++; - } - - (void) pthread_mutex_unlock(&port->lock); - -err: - DEBUG_PTHREAD_EXIT(); - return error; -} - -/* - * Meant to be used in synchroneous message transfer mode. The initial sender - * sends a message to the other end, which then uses this function to notify - * back the initial sender that it is done, often with a success/failure - * result as part of the message. Returns 0 on success, or an error number. - */ -int -pthread_msg_reply(pthread_msg_t *msg) -{ - - DEBUG_PTHREAD_ENTRY(); - DEBUG_ASSERT(msg != NULL && msg->magic == PMESG_MAGIC && - msg->reply != NULL); - - DEBUG_PTHREAD_EXIT(); - return pthread_msg_put(msg->reply, msg); -} - -/* - * Returns the number of pending messages tied to the port, if any, or -1 - * on error. - */ -int -pthread_port_pending(pthread_port_t *port) -{ - int pending = -1; - - DEBUG_PTHREAD_ENTRY(); - DEBUG_ASSERT(port != NULL && port->magic == PPORT_MAGIC); - - if (pthread_mutex_lock(&port->lock) != 0) - goto err; - - pending = (int)DLIST_NODES(&port->messages); - - (void) pthread_mutex_unlock(&port->lock); - -err: - DEBUG_PTHREAD_EXIT(); - return pending; -} diff --git a/tests/pthread_utils/mm_pthread_msg.h b/tests/pthread_utils/mm_pthread_msg.h deleted file mode 100644 index 287bc2e..0000000 --- a/tests/pthread_utils/mm_pthread_msg.h +++ /dev/null @@ -1,110 +0,0 @@ -/* $Id: mm_pthread_msg.h,v 1.3 2005/09/15 11:46:58 mmondor Exp $ */ - -/* - * Copyright (C) 2005, Matthew Mondor - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Matthew Mondor. - * 4. The name of Matthew Mondor may not be used to endorse or promote - * products derived from this software without specific prior written - * permission. - * 5. Redistribution of source code may not be released under the terms of - * any GNU Public License derivate. - * - * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - - - -#ifndef MM_PTHREAD_MSG_H -#define MM_PTHREAD_MSG_H - - - -#include - -#include -#include - - - -#define PRING_MAGIC 0x50524e47 -#define PPORT_MAGIC 0x50505254 -#define PMESG_MAGIC 0x504d5347 - -typedef struct { - u_int32_t magic; - pthread_cond_t cond; - pthread_mutex_t mutex; - int mode; - int event; - int mevent; -} pthread_ring_t; - -enum pthread_ring_modes { - PTHREAD_RMOD_NOWAIT, - PTHREAD_RMOD_CONDWAIT, - PTHREAD_RMOD_FDWAIT -}; - -typedef struct { - u_int32_t magic; - pthread_ring_t *ring; - pthread_mutex_t lock; - list_t messages; -} pthread_port_t; - -typedef struct { - node_t node; - u_int32_t magic; - pthread_port_t *reply; - size_t size; - void *message; -} pthread_msg_t; - - - -extern int pthread_ring_init(pthread_ring_t *); -extern int pthread_ring_valid(pthread_ring_t *); -extern int pthread_ring_destroy(pthread_ring_t *); -extern int pthread_ring_wait(pthread_ring_t *, - const struct timespec *); -extern int pthread_ring_notify(pthread_ring_t *); - -extern int pthread_port_init(pthread_port_t *); -extern int pthread_port_valid(pthread_port_t *); -extern int pthread_port_destroy(pthread_port_t *); -extern int pthread_port_set_ring(pthread_port_t *, - pthread_ring_t *); -extern int pthread_msg_init(pthread_msg_t *, - pthread_port_t *); -extern int pthread_msg_valid(pthread_msg_t *); -extern int pthread_msg_destroy(pthread_msg_t *); -extern pthread_msg_t *pthread_msg_get(pthread_port_t *); -extern int pthread_msg_put(pthread_port_t *, - pthread_msg_t *); -extern int pthread_msg_reply(pthread_msg_t *); -extern int pthread_port_pending(pthread_port_t *); - - - -#endif diff --git a/tests/pthread_utils/mm_pthread_poll.c b/tests/pthread_utils/mm_pthread_poll.c deleted file mode 100644 index 36508b4..0000000 --- a/tests/pthread_utils/mm_pthread_poll.c +++ /dev/null @@ -1,1116 +0,0 @@ -/* $Id: mm_pthread_poll.c,v 1.15 2006/02/05 13:00:48 mmondor Exp $ */ - -/* - * Copyright (C) 2005, Matthew Mondor - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Matthew Mondor. - * 4. The name of Matthew Mondor may not be used to endorse or promote - * products derived from this software without specific prior written - * permission. - * 5. Redistribution of source code may not be released under the terms of - * any GNU Public License derivate. - * - * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -/* - * I consider this code to be a major hack around the inherent problems unix - * systems face because of the lack of support for filedescriptor polling in - * the POSIX threads API. Although pthread defines methods for thread - * synchronization and polling waiting for events (using conditionnal - * variables), and that unix provides polling on filedescriptors using - * select(2), poll(2), kqueue(2) and other mechanisms, both are totally - * distinct entities which can be considered to either conflict with - * eachother or to not be related enough in a unified way. The current - * situation makes it almost impossible for a thread to both be polling for - * interthread efficient messages implementations built upon pthread, and - * filedescriptor events, concurrently. - * - * The GNU PTH library implements non-standard functions which allow to - * multiplex interthread messages and filedescriptor events, using for - * instance pth_poll_ev(), pth_select_ev(), pth_accept_ev(), pth_connect_ev(), - * etc. However, this is internally implemented using a single large select(2) - * based loop along with a slow large loop looking for non-fd events based on - * the principles of libevent. This threading library has other disadventages, - * such as not providing a preemptive scheduler (being a fully userspace - * implementation) and not allowing to scale to multiple processors on SMP - * systems. This interface however shows how good the POSIX threads API could - * have been, if it was better designed with unix systems in mind. This - * library also being the most portable threads library alternative for quite - * some time, because of the fact that Operating Systems implemented POSIX - * threads inconsistently, or not at all, caused us to use PTH during some - * time to develop software in cases where a pool of processes was not ideal - * because of the frequency of shared memory synchronization needs. - * - * With the advent of POSIX threads implementations on more unix and unix-like - * systems and of modern implementations behaving more consistently, which can - * scale on SMP systems and provide preemptive scheduling, it was considered - * worthwhile for us to adapt our software again to use the standard POSIX - * API. Especially considering that NetBSD which had no OS provided threads - * implementation for applications now has an awesome pthreads implementation - * starting with version 2.0. However, we encountered difficulties with some - * software which used the complex multiplexing of thread events and - * filedescriptor ones. This module provides a solution to port this software. - * It however is somewhat a hack. - * - * The downsides of this implementation are as follows. We originally intended - * to develop a system which would scale among an increasing number of threads - * in a ready pool of threads, scaling with concurrency of the polling calls. - * This however proved difficult, or impossible to achieve, the main reasons - * being that 1) A signal delivered to a process is only received by a random - * thread that is not blocking it. 2) In the case where multiple threads are - * polling on a common file descriptor, similarily only one random thread - * is awaken. 3) pthread_cond_signal() and pthread_cond_broadcast() cannot - * wake threads waiting in filedescriptor polling. 4) to achieve what we - * needed, two descriptors would have been necessary per notification ring. - * this was considered an aweful solution and was promptly rejected. 5) The - * POSIX API does not define a way for a process to set or change the signal - * blocking masks of other threads on the fly. - * - * Our solution then had to rely on a main descriptor polling manager thread - * which would be used to poll file descriptors, and would as a device serve - * client threads via efficient interthread messages. An issue still arises - * when a client thread sends a message to the polling thread to add new - * descriptors for polling or to cancel polling and remove descriptors. - * The polling thread must be able to immediately process these events - * awaking from filedescriptor polling. Two possible hacks could be used for - * this. 1) Use an AF_LOCAL SOCK_DGRAM socketpair(), which one side would - * be used to trigger an event writing some data, and the other side always - * included by the polling thread within the set of descriptors. 2) Send a - * signal to the process which only the polling thread is not blocking, - * to ensure that it be the one catching it, as such to awake from polling - * with an EINTR error code. This second solution was considered more elegant - * and is used as the basis of this implementation. We currently are - * clubbering the SIGUSR2 signal to achieve this. - */ - - - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include -#include -#include -#include - - - -MMCOPYRIGHT("@(#) Copyright (c) 2005\n\ -\tMatthew Mondor. All rights reserved.\n"); -MMRCSID("$Id: mm_pthread_poll.c,v 1.15 2006/02/05 13:00:48 mmondor Exp $"); - - - -/* - * Synchroneous communications message between arbitrary threads and the - * polling thread. Since communication is synchroneous, we only need to - * allocate one such message per thread. We are always expecting a reply - * back after sending a query before reusing the buffer. In fact, the - * message passing system only serves as a means for synchronization around - * the message, which is a shared memory object. - */ -struct poll_msg { - pthread_msg_t msgnode; - /* Passed as parameters */ - bool cancel; - struct pollfd *fds; - nfds_t nfds; - int timeout; - /* Returned as result */ - int ready, error; - /* Internally used */ - struct timeval expires; -}; - -/* - * An index is maintained of descriptor number -> poll_msg_index - * structures. Each of wich has information on the message the descriptor - * belongs to, and the index into the pollfd array so that it be easy to - * efficiently do per-fd work. - */ -struct poll_idx { - int idx; - struct poll_msg *msg; -}; - -/* - * Thread specific needed resources to use our special polling - */ -struct poll_data { - pthread_port_t port; - struct poll_msg msg; -}; - - - -#define POLLWAKE() do { \ - pollingevents++; \ - if (polling != 0) \ - (void) kill(process_id, SIGUSR2); \ -} while (/* CONSTCOND */0) - - - -/* - * Static functions prototypes - */ -static int pthread_poll_proc_init(void); -static void pthread_poll_proc_init2(void); -static int pthread_poll_thread_init(struct poll_data **); -static void pthread_poll_thread_exit(void *); -static void *poll_thread(void *); -static int poll_thread_attach_fds(struct poll_msg *); -static void poll_thread_detach_fds(struct poll_msg *); -static void poll_thread_sighandler(int); - -/* - * Static process specific storage - */ -static bool pthread_poll_initialized = FALSE; -static pthread_once_t pthread_poll_proc_initialized = PTHREAD_ONCE_INIT; -static pthread_key_t pthread_poll_proc_key; -static pthread_ring_t pthread_poll_thread_started_ring; -static pthread_port_t pthread_poll_thread_port; -static pid_t process_id; -static int polling = 0; -static int pollingevents = 0; - -/* - * Static global poll_thread storage. No synhronization is necessary when - * using these, since only the polling thread does. - */ -static struct poll_idx *poll_idx; -static nfds_t poll_idx_size; -static struct pollfd *poll_fds; -static nfds_t poll_fds_size; -static nfds_t poll_nfds; - - - -/* - * Static internal functions - */ - -static int -pthread_poll_proc_init(void) -{ - int error; - struct sigaction act; - - DEBUG_PTHREAD_ENTRY(); - - if ((error = pthread_key_create(&pthread_poll_proc_key, - pthread_poll_thread_exit)) != 0) - goto err; - - act.sa_handler = poll_thread_sighandler; - act.sa_flags = 0; - (void) sigemptyset(&act.sa_mask); - (void) sigaddset(&act.sa_mask, SIGUSR2); - if (sigaction(SIGUSR2, &act, NULL) != 0) { - error = errno; - goto err; - } - - process_id = getpid(); - - DEBUG_PTHREAD_EXIT(); - return 0; - -err: - DEBUG_PTHREAD_EXIT(); - return error; -} - -static void -pthread_poll_proc_init2(void) -{ - int error; - - DEBUG_PTHREAD_ENTRY(); - - if ((error = pthread_poll_proc_init()) != 0) { - (void) fprintf(stderr, "pthread_poll_proc_init() - %s\n", - strerror(error)); - DEBUG_PTHREAD_EXIT(); - exit(EXIT_FAILURE); - } - - DEBUG_PTHREAD_EXIT(); -} - -static int -pthread_poll_thread_init(struct poll_data **res) -{ - int error; - struct poll_data *data; - sigset_t set; - - DEBUG_PTHREAD_ENTRY(); - - (void) sigemptyset(&set); - (void) sigaddset(&set, SIGUSR2); - (void) pthread_sigmask(SIG_BLOCK, &set, NULL); - - if ((data = malloc(sizeof(struct poll_data))) == NULL) { - error = ENOMEM; - goto err; - } - - if ((error = pthread_port_init(&data->port)) != 0) - goto err; - if ((error = pthread_msg_init(&data->msg.msgnode, &data->port)) != 0) - goto err; - - if ((error = pthread_setspecific(pthread_poll_proc_key, data)) != 0) - goto err; - - *res = data; - - DEBUG_PTHREAD_EXIT(); - return 0; - -err: - if (data != NULL) { - (void) pthread_port_destroy(&data->port); - free(data); - } - - DEBUG_PTHREAD_EXIT(); - return error; -} - -static void -pthread_poll_thread_exit(void *specific) -{ - struct poll_data *data = (struct poll_data *)specific; - - DEBUG_PTHREAD_ENTRY(); - - (void) pthread_port_destroy(&data->port); - (void) pthread_msg_destroy(&data->msg.msgnode); - free(data); - - /* - * Some implementations need this - */ - (void) pthread_setspecific(pthread_poll_proc_key, NULL); - - DEBUG_PTHREAD_EXIT(); -} - - -/* - * Actual polling thread, with which we communicate using messages polling on - * pthread_port_t and pthread_ring_t. This is the only thread that should be - * catching SIGUSR2 signals (used to wake us up and reiterate our main loop. - * Note: Although less efficient than using kqueue(2) or libevent(3), after - * discussion with 3s4i we settled to using poll(2) for now, which minimizes - * OS dependencies as well as third party software dependencies. Because - * pthread_poll_ring(2) is only sparsely used by our software (migrating from - * using PTH library which provided pth_poll_ev()), and that we only provide - * it small pollfd arrays, this implementation was considered to meet our - * needs using poll(2). This also met the requirements for Tact group. - */ -/* ARGSUSED */ -static void * -poll_thread(void *args) -{ - sigset_t set; - pthread_ring_t ring; - list_t msg_list; - register int i; - - DEBUG_PTHREAD_ENTRY(); - - /* - * This initialization shouldn't fail. If it did, it would be nice to - * be able to simply panic eventually. XXX - */ - - /* - * Create set for SIGUSR2 which we'll unblock/block - */ - (void) sigemptyset(&set); - (void) sigaddset(&set, SIGUSR2); - - /* - * Allocate an initial buffer size for our pollfd array as well as for - * our descriptor based index. We'll double these buffers as - * necessary at runtime. - */ - poll_fds_size = 64; - poll_fds = malloc(sizeof(struct pollfd) * poll_fds_size); - poll_nfds = 0; - poll_idx_size = 64; - poll_idx = malloc(sizeof(struct poll_msg) * poll_idx_size); - for (i = 0; i < poll_idx_size; i++) - poll_idx[i].msg = NULL; - DLIST_INIT(&msg_list); - - /* - * Initialize message port and associated ring. The message port is - * module global, so that it be public to pthread_poll_ring(). - */ - (void) pthread_port_init(&pthread_poll_thread_port); - (void) pthread_ring_init(&ring); - (void) pthread_port_set_ring(&pthread_poll_thread_port, &ring); - - /* - * Notify parent that we're ready. - */ - (void) pthread_ring_notify(&pthread_poll_thread_started_ring); - - /* - * Main loop from which we never exit - */ - for (;;) { - register int n; - int timeout; - struct timeval tv, ttv; - struct poll_msg *msg, *nextmsg; - - /* - * Get time of day in a rather high resolution. We need to - * do this to be able to evaluate timeouts later on. We - * attempt to only require one time syscall per loop. - */ - (void) gettimeofday(&tv, NULL); - - pollingevents = 0; - - /* - * Process any messages. We need to add the descriptors if - * they aren't already added. Also store yet unsatisfied - * request messages into a list. - */ - while ((msg = (struct poll_msg *)pthread_msg_get( - &pthread_poll_thread_port)) != NULL) { - if (msg->cancel) { - /* - * Immediately satisfy request on demand - */ - msg->error = ECANCELED; - DLIST_UNLINK(&msg_list, (node_t *)msg); - poll_thread_detach_fds(msg); - (void) pthread_msg_reply(&msg->msgnode); - continue; - } - if (poll_thread_attach_fds(msg) == 0) { - msg->ready = msg->error = 0; - if (msg->timeout != -1) { - /* - * Convert millisecond timeout to an - * absolute time timeval - */ - msg->expires.tv_sec = tv.tv_sec; - msg->expires.tv_usec = tv.tv_usec; - ttv.tv_sec = msg->timeout / 1000; - ttv.tv_usec = (msg->timeout % 1000) - * 1000; - timeradd(&msg->expires, &ttv, - &msg->expires); - } - DLIST_APPEND(&msg_list, (node_t *)msg); - } else { - msg->ready = 0; - msg->error = EINVAL; - (void) pthread_msg_reply(&msg->msgnode); - } - } - - /* - * Process timeouts. For request messages which timed out, - * satisfy them immediately using ETIMEDOUT error. - * This also allows to evaluate which is the soonest to expire - * entry, which poll(2) will have to use as timeout. - */ - ttv.tv_sec = ttv.tv_usec = 99999; - for (msg = DLIST_TOP(&msg_list); msg != NULL; msg = nextmsg) { - nextmsg = DLIST_NEXT(msg); - - if (msg->timeout == -1) - continue; - if (timercmp(&msg->expires, &tv, <)) { - msg->error = ETIMEDOUT; - DLIST_UNLINK(&msg_list, (node_t *)msg); - poll_thread_detach_fds(msg); - (void) pthread_msg_reply(&msg->msgnode); - } else if (timercmp(&msg->expires, &ttv, <)) { - ttv.tv_sec = msg->expires.tv_sec; - ttv.tv_usec = msg->expires.tv_usec; - } - } - - /* - * If there are no registered descriptors to poll for, wait - * using the thread friendly ring until messages occur, and - * reiterate. - */ - if (poll_nfds == 0) { - (void) pthread_ring_wait(&ring, NULL); - continue; - } - - /* - * Perform polling. poll(2) for as much time as possible, - * although making sure to allow the soonest to expire query - * to stop polling. Next to expire entry time is in ttv and - * current time in tv. Calculate difference and convert to - * milliseconds. - */ - if (ttv.tv_sec == 99999 && ttv.tv_usec == 99999) - timeout = -1; - else { - timersub(&ttv, &tv, &ttv); - timeout = (ttv.tv_sec * 1000) + (ttv.tv_usec / 1000); - } - - /* - * Unblock the SIGUSR2 signal, which we should be the only - * thread to receive, all other threads blocking it. - * Only leave it unblocked for the duration of the poll(2) - * syscall. We cause our loop to reiterate in any case of - * error, EINTR or no file descriptor with pending event. - */ - (void) pthread_sigmask(SIG_UNBLOCK, &set, NULL); - polling++; - - n = 0; - if (pollingevents != 0) - goto unblock; - - n = poll(poll_fds, poll_nfds, timeout); - -unblock: - polling--; - (void) pthread_sigmask(SIG_BLOCK, &set, NULL); - if (pollingevents != 0 || n < 1) - continue; - - /* - * Verify which descriptors have interesting events set, - * increasing events counter of corresponding requests. - */ - for (i = 0; n != 0 && i < poll_nfds; i++) { - if (poll_fds[i].revents != 0) { - (poll_idx[poll_fds[i].fd].msg->ready)++; - n--; - } - } - /* - * Now verify pending request messages for events, and satisfy - * the requests of those who do. - */ - for (msg = DLIST_TOP(&msg_list); msg != NULL; msg = nextmsg) { - nextmsg = DLIST_NEXT(msg); - - if (msg->ready != 0) { - /* - * ready and error fields are already set - */ - DLIST_UNLINK(&msg_list, (node_t *)msg); - poll_thread_detach_fds(msg); - (void) pthread_msg_reply(&msg->msgnode); - } - } - } - - /* NOTREACHED */ - DEBUG_PTHREAD_EXIT(); - pthread_exit(NULL); - return NULL; -} - -/* - * Permits to merge supplied pollfd set with the main set - */ -static int -poll_thread_attach_fds(struct poll_msg *msg) -{ - register int i, fd, idx; - - DEBUG_PTHREAD_ENTRY(); - - for (i = 0; i < msg->nfds; i++) { - fd = msg->fds[i].fd; - - /* - * Ignore unset descriptors - */ - if (fd == -1) - continue; - - /* - * Grow index buffer if necessary. Either grow by doubling - * size, or even more if necessary to hold index to fd. - * If we only grew to hold fd, we might need to realloc(3) too - * often. Take care to also NULL msg field of new entries. - */ - if (poll_idx_size <= fd) { - struct poll_idx *idx; - int size, i2; - - size = poll_idx_size * 2; - if (fd > size) - size = fd; - if ((idx = realloc(poll_idx, - sizeof(struct poll_idx) * size)) == NULL) - goto err; - poll_idx = idx; - for (i2 = poll_idx_size; i2 < size; i2++) - poll_idx[i2].msg = NULL; - poll_idx_size = size; - } - - /* - * Error if descriptor not unique before adding to set. - * We do not allow multiple threads polling on the same - * descriptor at the same time in our system. We would - * otherwise need to gracefully handle duplicates, - * multiplexing them, which isn't required at all by our - * applications. So let's keep things simple. - */ - if (poll_idx[fd].msg != NULL) - goto err; - - /* - * Resize pollfd array if needed. Grow by doubling. - * This should happen very rarely. - * XXX We could check this condition only once at the - * top of this fonction and take in consideration the - * number of descriptors to add, if wanted for optimization. - */ - if (poll_fds_size <= poll_nfds) { - struct pollfd *ptr; - - if ((ptr = realloc(poll_fds, - sizeof(struct pollfd) * (poll_fds_size * 2))) - == NULL) - goto err; - poll_fds = ptr; - poll_fds_size *= 2; - } - - /* - * Finally add descriptor to set and register it for indexing. - * We simply need to append it to the existing entries in our - * global polling set array. - */ - idx = poll_nfds; - poll_fds[idx].fd = fd; - poll_fds[idx].events = msg->fds[i].events; - poll_fds[idx].revents = 0; - poll_idx[fd].msg = msg; - poll_idx[fd].idx = idx; - poll_nfds = ++idx; - } - - DEBUG_PTHREAD_EXIT(); - return 0; - -err: - (void) poll_thread_detach_fds(msg); - - DEBUG_PTHREAD_EXIT(); - return -1; -} - -/* - * Permits to disunite supplied pollfd set from the main set. Also sets the - * revents fields of the supplied set to the ones of the main set. - */ -static void -poll_thread_detach_fds(struct poll_msg *msg) -{ - register int i, fd, idx; - - DEBUG_PTHREAD_ENTRY(); - - for (i = 0; i < msg->nfds; i++) { - fd = msg->fds[i].fd; - - /* - * Make sure fd was properly registered - */ - if (poll_idx[fd].msg != msg) - continue; - - /* - * Find index in global pollfd set for this fd - */ - idx = poll_idx[fd].idx; - - /* - * Update pollfd entry according to global one - */ - msg->fds[i].revents = poll_fds[idx].revents; - - /* - * Unlink fd from the global set. The removal method is - * simple; Take the last entry of the global set and move it - * over the current entry, updating index links, and lower - * the gobal nfds by one. If we're the last entry, simply - * remove it invalidating its index entry lowering the global - * nfds. - */ - - if (--poll_nfds != idx) { - /* - * Not last entry, move last entry over entry to - * delete. - */ - register struct pollfd *deleted, *last; - int deleted_fd, deleted_idx; - - last = &poll_fds[poll_nfds]; - deleted = &poll_fds[idx]; - deleted_fd = deleted->fd; - deleted_idx = poll_idx[deleted_fd].idx; - - /* Copy last entry over deleted one */ - deleted->fd = last->fd; - deleted->events = last->events; - deleted->revents = last->revents; - - /* - * Reindex last entry which was moved, don't touch - * the msg pointer though. - */ - poll_idx[last->fd].idx = deleted_idx; - - /* And finally invalidate last entry */ - poll_idx[deleted_fd].msg = NULL; - } else { - /* Invalidate last entry */ - poll_idx[poll_fds[poll_nfds].fd].msg = NULL; - } - } - - DEBUG_PTHREAD_EXIT(); -} - -/* - * Called upon reception of SIGUSR2 - */ -/* ARGSUSED */ -static void -poll_thread_sighandler(int sig) -{ - - DEBUG_PTHREAD_ENTRY(); - - pollingevents++; - - DEBUG_PTHREAD_EXIT(); -} - - - -/* - * Public API exported functions - */ - -/* - * Must be called before launching any thread. Sets up the signal mask and - * launches the dedicated poll slave thread. Important note: this system - * clobbers the SIGUSR2 signal, which the application can no longer use for - * other purposes. The only solution to wake the thread manager thread from - * poll(2) is either to trigger an event through a dedicated filedescriptor, - * or to send a signal to the process which only the polling thread allows. - */ -int -pthread_poll_init(void) -{ - int error; - sigset_t set; - pthread_attr_t attr; - pthread_t thread; - - DEBUG_PTHREAD_ENTRY(); - - if (pthread_poll_initialized) { - error = 0; - goto err; - } - - /* - * First block SIGUSR2 signal in the parent. The reason why this must - * be called before the application launches any thread is that - * threads inherit the sigmask of their parent, and that all threads, - * but the polling thread, must block the signal. This ensures that - * only the wanted thread wakes up when a SIGUSR2 signal is received. - * This way, we can interrupt the polling thread in poll(2), for - * instance, and cause it to reiterate its main loop. - */ - (void) sigemptyset(&set); - (void) sigaddset(&set, SIGUSR2); - if ((error = pthread_sigmask(SIG_BLOCK, &set, NULL)) != 0) - goto err; - - /* - * We'll use this pthread_ring_t to get notification from child that - * it is ready to process requests before proceeding. - */ - if ((error = pthread_ring_init(&pthread_poll_thread_started_ring)) - != 0) - goto err; - - /* - * We may now launch the poll thread and wait for notification from it - * that it is ready to serve requests. We won't need to exit this - * thread, so it can be launched in detached state. - */ - if ((error = pthread_attr_init(&attr)) != 0) - goto err; - if ((error = pthread_attr_setdetachstate(&attr, TRUE)) != 0) - goto err; - if ((error = pthread_create(&thread, &attr, poll_thread, NULL)) != 0) - goto err; - - /* - * Wait until thread is ready to serve requests - */ - (void) pthread_ring_wait(&pthread_poll_thread_started_ring, NULL); - - pthread_poll_initialized = TRUE; - - return 0; - -err: - DEBUG_PTHREAD_EXIT(); - return error; -} - -/* - * poll(2) replacement which can also be awakened by a notification happening - * on the specified ring. This for instance allows to process thread messages - * as well as descriptor events. Like poll(2), returns the number of - * descriptors with events on success (can be 0), or returns -1 with the - * specified error set in errno. Unlike poll, the error ETIMEDOUT will occur - * if the timeout expires before an event existed, or ECANCELLED if a ring - * notification event occurred instead of a filedescriptor one. Can also - * return errors such as EINVAL. - * XXX Check for ETIMEDOUT! We probably don't do this yet. Also, we could - * return 0 in this case like poll(2). - */ -int -pthread_poll_ring(struct pollfd *fds, nfds_t nfds, int timeout, - pthread_ring_t *ring) -{ - int error; - struct poll_data *data; - pthread_ring_t *oring; - - DEBUG_PTHREAD_ENTRY(); - - if (!pthread_poll_initialized) { - error = EINVAL; - goto err; - } - - /* - * Implicit process and thread specific initializations - */ - if ((error = pthread_once(&pthread_poll_proc_initialized, - pthread_poll_proc_init2)) != 0) - goto err; - /* - * XXX Use a mutex or pthread_once() equivalent here too? - */ - if ((data = pthread_getspecific(pthread_poll_proc_key)) == NULL) { - if ((error = pthread_poll_thread_init(&data)) != 0) - goto err; - } - - /* - * Perform some sanity checking on supplied arguments - */ - if (fds == NULL || nfds < 1 || ring == NULL || ring->magic != - PRING_MAGIC) { - error = EINVAL; - goto err; - } - - /* - * Ensure that our message port's ring uses the same ring which - * the user supplies us. If we didn't do this we would need to - * be able to wait for events on more than one ring simultaneously. - * Because we don't have a ring multiplexer object yet (which would - * be needed since a ring maps to a conditional variable among other - * things), we need to do process this way. - * XXX Could there be a race condition here? It needs to be stressed. - */ - { - int mevent; - - mevent = (data->port.ring != NULL ? - data->port.ring->mevent : 0); - oring = data->port.ring; - (void) pthread_port_set_ring(&data->port, ring); - data->port.ring->mevent = mevent; - } - - /* - * Send query to polling thread. It is safe to simply reuse our - * message since we then expect a reply back and synchronize it. - */ - data->msg.cancel = FALSE; - data->msg.fds = fds; - data->msg.nfds = nfds; - data->msg.timeout = timeout; - if ((error = pthread_msg_put(&pthread_poll_thread_port, - &data->msg.msgnode)) != 0) - goto err; - - /* - * Interrupt polling thread which may still be waiting in poll(2). - * We do this by sending SIGUSR2 to the process, which only the - * polling thread is not blocking. This causes the thread to reiterate - * its main loop, thus processing this message and going back to - * sleep in poll(2). - */ - POLLWAKE(); - - /* - * Wait until en event occurs and notifies our ring. An event could - * either be triggered by the poll request ending or by another - * interrupting event on the supplied ring. If a message is queued - * on the port between pthread_port_set_ring() and - * pthread_ring_wait(), the latter immediately returns. - */ - if ((error = pthread_ring_wait(ring, NULL)) != 0) - goto err; - if (pthread_msg_get(&data->port) == NULL) { - /* - * No message replied back from poll thread yet, this means - * that our ring was notified by another event. Cancel request - * by sending event back with the cancel flag, and wait for - * reply message to occur (which will be the original request - * results we were waiting for). error field will be set to - * ECANCELED by the poll thread. - */ - data->msg.cancel = TRUE; - (void) pthread_msg_put(&pthread_poll_thread_port, - &data->msg.msgnode); - POLLWAKE(); - while (pthread_msg_get(&data->port) == NULL) - (void) pthread_ring_wait(ring, NULL); - } - /* Unclobber user supplied ring from our port events */ - (void) pthread_port_set_ring(&data->port, oring); - - /* - * Error, return error number. - */ - if (data->msg.error != 0) { - error = data->msg.error; - goto err; - } - - /* - * Success, return number of descriptors with detected events. - */ - DEBUG_PTHREAD_EXIT(); - return data->msg.ready; - -err: - errno = error; - - DEBUG_PTHREAD_EXIT(); - return -1; -} - -/* - * accept(2) replacement which can both observe a timeout and be interrupted - * via pthread_ring_t events. Internally implemented using - * pthread_poll_ring(). Will internally set the descriptor in non-blocking - * mode if necessary, then reverting it to the mode it was supplied in. - * Returns a new descriptor on success, or -1 on error, in which case errno - * is set. errno can then be EINVAL, ETIMEDOUT, ECANCELED, or others. - * Timeout is in milliseconds, like for poll(2) and can be -1. - */ -int -pthread_accept_ring(int s, struct sockaddr *addr, socklen_t *addrlen, - int timeout, pthread_ring_t *ring) -{ - int oflags, nflags, d, error = 0; - struct pollfd fd; - - DEBUG_PTHREAD_ENTRY(); - - if (!pthread_poll_initialized) { - errno = EINVAL; - goto err; - } - - /* - * First get current fcntl status flags, and set descriptor to - * non-blocking mode if necessary. - */ - if ((oflags = nflags = fcntl(s, F_GETFL)) == -1) - goto err; - if ((oflags & O_NONBLOCK) == 0) { - nflags |= O_NONBLOCK; - if (fcntl(s, F_SETFL, nflags) == -1) - goto err; - } - - if ((d = accept(s, addr, addrlen)) == -1) { - if (errno != EAGAIN) /* XXX Add others? */ - goto end; - } else - goto end; - - /* - * EAGAIN, poll until completion, timeout or ring event. - */ - fd.fd = d; - fd.events = POLLIN; - if ((error = pthread_poll_ring(&fd, 1, timeout, ring)) == 1 && - (fd.revents & POLLIN) != 0) - error = 0; - else - error = errno; - -end: - /* - * Restore supplied descriptor fcntl status flags if necessary - */ - if (nflags != oflags) - (void) fcntl(s, F_SETFL, oflags); - - if (error != 0) { - if (d != -1) { - (void) close(d); - d = -1; - } - errno = error; - goto err; - } - - DEBUG_PTHREAD_EXIT(); - return d; - -err: - DEBUG_PTHREAD_EXIT(); - return -1; -} - -/* - * connect(2) replacement which can both observe a timeout and be interrupted - * via pthread_ring_t events. Internally implemented using - * pthread_poll_ring(). Will internally set the descriptor in non-blocking - * mode if necessary, then reverting it back to the mode it was supplied in. - * Returns 0 on success, or -1, in which case errno is set. errno can be - * EINVAL, ETIMEDOUT, ECANCELED or others. - * Timeout is in milliseconds, like for poll(2) and can be -1. - * For the application to know the actual connection status result, it should - * poll until completion and verify the status using getsockopt(2) with - * SOL_SOCKET level and SO_ERROR option. It can alternatively continue to call - * this function in a loop until completion. Calling the function on an - * already connected socket will result in EISCONN. - */ -int -pthread_connect_ring(int s, const struct sockaddr *name, socklen_t namelen, - int timeout, pthread_ring_t *ring) -{ - int oflags, nflags, error = 0; - struct pollfd fd; - - DEBUG_PTHREAD_ENTRY(); - - if (!pthread_poll_initialized) { - errno = EINVAL; - goto err; - } - - /* - * First get current fcntl status flags, and set descriptor to - * non-blocking mode if necessary. - */ - if ((oflags = nflags = fcntl(s, F_GETFL)) == -1) - goto err; - if ((oflags & O_NONBLOCK) == 0) { - nflags |= O_NONBLOCK; - if (fcntl(s, F_SETFL, nflags) == -1) - goto err; - } - - if ((error = connect(s, name, namelen)) == -1) { - if (errno != EINPROGRESS && errno != EALREADY) { - error = errno; - goto end; - } - } else - goto end; - - /* - * EINPROGRESS or EALREADY, poll until completion, timeout or ring - * event. - */ - fd.fd = s; - fd.events = POLLOUT; - if (pthread_poll_ring(&fd, 1, timeout, ring) == 1 && - (fd.revents & POLLOUT) != 0) { - socklen_t l; - - /* - * connect(2) completed, return result - */ - if (getsockopt(s, SOL_SOCKET, SO_ERROR, &error, &l) == -1) - error = errno; - } - -end: - /* - * Restore supplied descriptor fcntl status flags if necessary - */ - if (nflags != oflags) - (void) fcntl(s, F_SETFL, oflags); - - if (error != 0) { - errno = error; - goto err; - } - - DEBUG_PTHREAD_EXIT(); - return 0; - -err: - DEBUG_PTHREAD_EXIT(); - return -1; -} diff --git a/tests/pthread_utils/mm_pthread_poll.h b/tests/pthread_utils/mm_pthread_poll.h deleted file mode 100644 index 77af18a..0000000 --- a/tests/pthread_utils/mm_pthread_poll.h +++ /dev/null @@ -1,63 +0,0 @@ -/* $Id: mm_pthread_poll.h,v 1.6 2005/11/22 18:03:48 mmondor Exp $ */ - -/* - * Copyright (C) 2005, Matthew Mondor - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Matthew Mondor. - * 4. The name of Matthew Mondor may not be used to endorse or promote - * products derived from this software without specific prior written - * permission. - * 5. Redistribution of source code may not be released under the terms of - * any GNU Public License derivate. - * - * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - - - -#ifndef MM_PTHREAD_POLL_H -#define MM_PTHREAD_POLL_H - - - -#include -#include -#include -#include -#include - -#include - - - -extern int pthread_poll_init(void); -extern int pthread_poll_ring(struct pollfd *, nfds_t, int, - pthread_ring_t *); -extern int pthread_accept_ring(int, struct sockaddr *, socklen_t *, int, - pthread_ring_t *); -extern int pthread_connect_ring(int, const struct sockaddr *, socklen_t, - int, pthread_ring_t *); - - - -#endif diff --git a/tests/pthread_utils/mm_pthread_pool.c b/tests/pthread_utils/mm_pthread_pool.c deleted file mode 100644 index 80ba5fb..0000000 --- a/tests/pthread_utils/mm_pthread_pool.c +++ /dev/null @@ -1,504 +0,0 @@ -/* $Id: mm_pthread_pool.c,v 1.7 2006/02/05 13:00:48 mmondor Exp $ */ - -/* - * Copyright (C) 2004-2005, Matthew Mondor - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Matthew Mondor. - * 4. The name of Matthew Mondor may not be used to endorse or promote - * products derived from this software without specific prior written - * permission. - * 5. Redistribution of source code may not be released under the terms of - * any GNU Public License derivate. - * - * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -/* - * Implementation of a pool of ready threads which adapts with concurrency - * needs. These ready threads can serve requests passed through efficient - * inter-thread messaging. mmpool(3) is used for the pool functionality. - */ - - - -#include -#include -#include - -#include -#include - - - -MMCOPYRIGHT("@(#) Copyright (c) 2004-2005\n\ -\tMatthew Mondor. All rights reserved.\n"); -MMRCSID("$Id: mm_pthread_pool.c,v 1.7 2006/02/05 13:00:48 mmondor Exp $"); - - - -/* - * STATIC FUNCTIONS PROTOTYPES - */ - -inline static pthread_object_t *thread_object_alloc(void); -inline static void thread_object_free(pthread_object_t *); -static bool thread_object_constructor(pnode_t *); -static void thread_object_destructor(pnode_t *); -static void *thread_object_main(void *); - - - -/* - * GLOBALS - */ - -static bool thread_object_initialized = FALSE; -static pthread_attr_t thread_object_attr; -static pool_t thread_object_pool; -static pool_t thread_object_msg_pool; -static pthread_mutex_t thread_object_pool_mutex = - PTHREAD_MUTEX_INITIALIZER; -static pthread_mutex_t thread_object_msg_pool_mutex = - PTHREAD_MUTEX_INITIALIZER; -static pthread_ring_t thread_started_ring; - - - -/* - * EXPORTED PUBLIC FUNCTIONS - */ - -/* - * Must be called to initialize the pthreads pool subsystem, before calling - * any other function of this API. Returns 0 on success, or an error number. - * threads are launched, and more will be launched in increments - * of whenever necessary. These will also only be destroyed in - * decrements of whenever that many threads have not been in use for - * some time, and a minimum of threads will always be kept. - * Setting to high values may actually degrade performance with some - * unefficient threading implementations. It is not recommended to use more - * than 8 using the pth(3) library. Using NetBSD 2.0+ SA threads, a high - * number does not reduce performance. We current do not observe any limit - * whatsoever according to the number of threads launched over time. It is the - * application's responsibility to ensure to observe decent concurrency limits - * before calling pthread_object_call(). - */ -int -pthread_object_init(int initial) -{ - int error = 0; - - DEBUG_PTHREAD_ENTRY(); - - if (thread_object_initialized) { - error = EINVAL; - goto err; - } - - /* - * Create attributes which will be used for threads of the pool. - * We want them to be joinable. - */ - if ((error = pthread_attr_init(&thread_object_attr)) != 0) - goto err; - if ((error = pthread_attr_setdetachstate(&thread_object_attr, 0)) - != 0) - goto err; - - /* - * We use this ring to obtain notification of ready children when - * launching them. This is required for proper synchronization to - * avoid aweful race conditions. - */ - if ((error = pthread_ring_init(&thread_started_ring)) != 0) - goto err; - - /* - * First initialize the message subsystem pool - */ - if (!pool_init(&thread_object_msg_pool, "thread_object_msg_pool", - malloc, free, NULL, NULL, sizeof(pthread_object_msg_t), - 32768 / sizeof(pthread_object_msg_t), 1, 0)) { - error = ENOMEM; - goto err; - } - - /* - * Now initialize the threads pool. This creates threads, uses - * synchronization with thread_started_ring, and uses the message - * subsystem, which all must be initialized and ready. - */ - if (!pool_init(&thread_object_pool, "thread_object_pool", - malloc, free, thread_object_constructor, thread_object_destructor, - sizeof(pthread_object_t), initial, 1, 0)) { - error = ENOMEM; - goto err; - } - - thread_object_initialized = TRUE; - - DEBUG_PTHREAD_EXIT(); - return 0; - -err: - if (POOL_VALID(&thread_object_msg_pool)) - pool_destroy(&thread_object_msg_pool); - if (POOL_VALID(&thread_object_pool)) - pool_destroy(&thread_object_pool); - - DEBUG_PTHREAD_EXIT(); - return error; -} - -/* - * Allows allocation/creation of a message suitable for asynchronous requests - * with the threads via their main message port provided by this system. - * Returns new message, or NULL on error. - */ -inline pthread_object_msg_t * -pthread_object_msg_alloc(void) -{ - pthread_object_msg_t *msg = NULL; - - DEBUG_PTHREAD_ENTRY(); - - if (pthread_mutex_lock(&thread_object_msg_pool_mutex) != 0) - goto err; - msg = (pthread_object_msg_t *)pool_alloc(&thread_object_msg_pool, - FALSE); - (void) pthread_mutex_unlock(&thread_object_msg_pool_mutex); - - (void) pthread_msg_init(&msg->message, NULL); - -err: - DEBUG_PTHREAD_EXIT(); - return msg; -} - -/* - * Permits to free/destroy a message which was allocated using - * pthread_object_msg_alloc() and sent asynchroneously. - */ -inline int -pthread_object_msg_free(pthread_object_msg_t *msg) -{ - int error = 0; - - DEBUG_PTHREAD_ENTRY(); - - (void) pthread_msg_destroy(&msg->message); - - if ((error = pthread_mutex_lock(&thread_object_msg_pool_mutex)) != 0) - goto err; - (void) pool_free((pnode_t *)msg); - (void) pthread_mutex_unlock(&thread_object_msg_pool_mutex); - -err: - DEBUG_PTHREAD_EXIT(); - return error; -} - -/* - * Allows to invoke a thread of the pool to perform execution of the wanted - * function. This is very efficient since the threads are already created and - * are waiting for requests. There is no maximum concurrency limit enforced by - * this system; It is the responsibility of the application to restrict - * concurrency as necessary by keeping internal information on the current - * number of requests. 0 is returned on success, or an error number. - * XXX Add support for synchroneous and asynchroneous operation. Current - * operation is only asynchroneous, but we would like to add a boolean here to - * decide. We also could add back the result value of the thread function - * which would only be useful in synchroneous operation, when we are waiting - * until the task ends... Of course, it's still easy for applications to use - * these in a synchroneous manner, by using a message and/or ring, - * conditionnal variable, etc. - * Also evaluate if a callback function to be called to notify end of - * asynchroneous operation would be useful. - */ -int -pthread_object_call(pthread_port_t **port, - void (*function)(pthread_object_t *, void *), void *args) -{ - pthread_object_t *obj = NULL; - pthread_object_msg_t *msg = NULL; - int error; - - DEBUG_PTHREAD_ENTRY(); - - if (function == NULL) { - error = EINVAL; - goto err; - } - - /* - * Allocate a thread from the pool to reserve it, and tell it to call - * a function via a message. The message cannot be on the stack in - * this case, since it holds arguments to be passed to a thread, and - * also consists of an asynchroneous message for wich we do not expect - * a response back, waiting for it. We just dispatch it and go on. - */ - if ((obj = thread_object_alloc()) == NULL) { - error = ENOMEM; - goto err; - } - if ((msg = pthread_object_msg_alloc()) == NULL) { - error = ENOMEM; - goto err; - } - - msg->command = PTHREAD_OBJ_CALL; - msg->u.call.function = function; - msg->u.call.arguments = args; - if ((error = pthread_msg_put(obj->port, &msg->message)) != 0) - goto err; - - /* - * Everything successful; - * If caller wants the message port of the thread, supply it - */ - if (port != NULL) - *port = obj->port; - - DEBUG_PTHREAD_EXIT(); - return 0; - -err: - if (msg != NULL) - pthread_object_msg_free(msg); - if (obj != NULL) - thread_object_free(obj); - - DEBUG_PTHREAD_EXIT(); - return error; -} - - - -/* - * INTERNAL STATIC FUNCTIONS - */ - -/* - * Internally used to allocate a ready thread from the pool. - */ -inline static pthread_object_t * -thread_object_alloc(void) -{ - pthread_object_t *obj = NULL; - - DEBUG_PTHREAD_ENTRY(); - - if (pthread_mutex_lock(&thread_object_pool_mutex) != 0) - goto err; - obj = (pthread_object_t *)pool_alloc(&thread_object_pool, FALSE); - (void) pthread_mutex_unlock(&thread_object_pool_mutex); - -err: - return obj; -} - -/* - * Internally used to free a no longer needed thread back to the pool of ready - * threads. - */ -inline static void -thread_object_free(pthread_object_t *obj) -{ - - DEBUG_PTHREAD_ENTRY(); - - if (pthread_mutex_lock(&thread_object_pool_mutex) == 0) { - (void) pool_free((pnode_t *)obj); - (void) pthread_mutex_unlock(&thread_object_pool_mutex); - } - - DEBUG_PTHREAD_EXIT(); -} - -/* - * Internally called by mmpool(3) to create a thread object. - */ -static bool -thread_object_constructor(pnode_t *pnode) -{ - pthread_object_t *obj = (pthread_object_t *)pnode; - int success = TRUE; - - DEBUG_PTHREAD_ENTRY(); - - /* - * Note that we leave thread_object_main() initialize the port field - * when it creates its port and ring. - */ - if (pthread_create(&obj->thread, &thread_object_attr, - thread_object_main, obj) != 0) { - success = FALSE; - goto err; - } - - /* - * Wait until new thread ready notification. Without this, at least - * with NetBSD 2.0 SA threads, hell would break loose. Thread creation - * isn't really a bottleneck in our case anyways, since we only need - * to do it when all threads of the pool are already busy. - */ - (void) pthread_ring_wait(&thread_started_ring, NULL); - -err: - DEBUG_PTHREAD_EXIT(); - return success; -} - -/* - * Internally called by mmpool(3) to destroy a thread object. - */ -static void -thread_object_destructor(pnode_t *pnode) -{ - pthread_object_t *obj = (pthread_object_t *)pnode; - pthread_object_msg_t *msg; - - DEBUG_PTHREAD_ENTRY(); - - /* - * To be freed, the thread has to be terminated. We thus send it a - * quit message and then wait for it to exit using pthread_join(). - * Note that we let the thread destroy the port field. Although we - * theoretically could use a message on the stack here, let's be safe. - * Thread destruction is only performed rarely anyways, so this isn't - * a performance problem. - */ - if ((msg = pthread_object_msg_alloc()) != NULL) { - msg->command = PTHREAD_OBJ_QUIT; - (void) pthread_msg_put(obj->port, &msg->message); - } - (void) pthread_join(obj->thread, NULL); - - DEBUG_PTHREAD_EXIT(); -} - -/* - * Actual thread's main loop. We create a message port and listen for command - * messages (quit and call). When we obtain a quit request, we destroy the - * port and exit cleanly. The quit event can never occur during the execution - * of a call command, since it is only called on already freed thread nodes - * (by mmpool(3) pool_free()). It is advized to applications which need to - * obtain and use the port of the thread after thread_object_call() to only - * send proper user messages, not system reserved ones. - */ -static void * -thread_object_main(void *args) -{ - pthread_object_t *obj = (pthread_object_t *)args; - pthread_port_t port; - pthread_ring_t ring; - pthread_msg_t *imsg; - pthread_object_msg_t *msg; - - DEBUG_PTHREAD_ENTRY(); - - /* - * Create our incomming message port as well as its corresponding - * notification ring we can sleep on. Then advertize our port address. - * Ideally, we should somehow panic if any of this initialization - * fails. XXX - */ - (void) pthread_port_init(&port); - (void) pthread_ring_init(&ring); - (void) pthread_port_set_ring(&port, &ring); - obj->port = &port; - - /* - * Notify parent that we are ready, so that it may proceed - */ - (void) pthread_ring_notify(&thread_started_ring); - - /* - * Main loop, which keeps executing until we obtain a PTHREAD_OBJ_QUIT - * message, at which event we cleanly exit. - */ - for (;;) { - /* - * Wait for any message(s) to be available, without taking any - * CPU time. - */ - (void) pthread_ring_wait(&ring, NULL); - - /* - * We were awaken because at least one message is available. - * Process all messages in the queue. - */ - while ((imsg = pthread_msg_get(&port)) != NULL) { - msg = (pthread_object_msg_t *)(&((pnode_t *)imsg)[-1]); - if (msg->command == PTHREAD_OBJ_QUIT) { - /* - * We are ordered to exit by the object - * destructor. - */ - pthread_object_msg_free(msg); - goto end; - } - if (msg->command == PTHREAD_OBJ_CALL) { - /* - * Request to execute a function. This means - * that we were allocated/reserved first. - */ - msg->u.call.function(obj, - msg->u.call.arguments); - pthread_object_msg_free(msg); - /* - * Free/release us back, so that we be - * available again to process further - * requests. It is possible that freeing - * ourselves cause a PTHREAD_OBJ_QUIT message - * to be queued soon on our port by the - * destructor function. This is safe, since - * the destructor does not cause us to be - * destroyed until it waits for us to have - * ended cleanly using pthread_join(). - */ - thread_object_free(obj); - } - } - } - -end: - /* - * Discard messages that are still queued on our port (if any) - */ - while ((imsg = pthread_msg_get(&port)) != NULL) { - msg = (pthread_object_msg_t *)(&((pnode_t *)imsg)[-1]); - pthread_object_msg_free(msg); - } - /* - * Free our resources and exit. - */ - (void) pthread_port_destroy(&port); - (void) pthread_ring_destroy(&ring); - - DEBUG_PTHREAD_EXIT(); - pthread_exit(NULL); - - /* NOTREACHED */ - return NULL; -} diff --git a/tests/pthread_utils/mm_pthread_pool.h b/tests/pthread_utils/mm_pthread_pool.h deleted file mode 100644 index 2a46ebd..0000000 --- a/tests/pthread_utils/mm_pthread_pool.h +++ /dev/null @@ -1,97 +0,0 @@ -/* $Id: mm_pthread_pool.h,v 1.1 2004/12/27 11:16:16 mmondor Exp $ */ - -/* - * Copyright (C) 2004-2005, Matthew Mondor - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Matthew Mondor. - * 4. The name of Matthew Mondor may not be used to endorse or promote - * products derived from this software without specific prior written - * permission. - * 5. Redistribution of source code may not be released under the terms of - * any GNU Public License derivate. - * - * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - - - -#ifndef MM_PTHREAD_POOL_H -#define MM_PTHREAD_POOL_H - - - -#include - -#include -#include - -#include - - - -typedef struct { - pnode_t node; - pthread_t thread; - pthread_port_t *port; -} pthread_object_t; - -typedef struct { - pnode_t node; - pthread_msg_t message; - int command; - union { - /* PTHREAD_OBJ_CALL, sent to thread_object_main() */ - struct { - void (*function)(pthread_object_t *, void *); - void *arguments; - } call; - /* PTHREAD_OBJ_QUIT, sent to thread_oject_reaper() */ - pthread_object_t *quit; - /* PTHREAD_OBJ_USER, custom user messages */ - struct { - int user_command; - void *user_data; - } user; - } u; -} pthread_object_msg_t; - -enum pthread_object_commands { - PTHREAD_OBJ_CALL, - PTHREAD_OBJ_QUIT, - PTHREAD_OBJ_USER, - PTHREAD_OBJ_MAX -}; - - - -extern int pthread_object_init(int); -extern inline pthread_object_msg_t *pthread_object_msg_alloc(void); -extern inline int pthread_object_msg_free( - pthread_object_msg_t *); -extern int pthread_object_call(pthread_port_t **, - void (*)(pthread_object_t *, - void *), void *); - - - -#endif diff --git a/tests/pthread_utils/mm_pthread_sleep.c b/tests/pthread_utils/mm_pthread_sleep.c deleted file mode 100644 index a9f8c58..0000000 --- a/tests/pthread_utils/mm_pthread_sleep.c +++ /dev/null @@ -1,285 +0,0 @@ -/* $Id: mm_pthread_sleep.c,v 1.5 2006/02/05 13:00:48 mmondor Exp $ */ - -/* - * Copyright (C) 2005, Matthew Mondor - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Matthew Mondor. - * 4. The name of Matthew Mondor may not be used to endorse or promote - * products derived from this software without specific prior written - * permission. - * 5. Redistribution of source code may not be released under the terms of - * any GNU Public License derivate. - * - * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - - - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - - - -MMCOPYRIGHT("@(#) Copyright (c) 2005\n\ -\tMatthew Mondor. All rights reserved.\n"); -MMRCSID("$Id: mm_pthread_sleep.c,v 1.5 2006/02/05 13:00:48 mmondor Exp $"); - - - -static int pthread_sleep_proc_init(void); -static void pthread_sleep_proc_init2(void); -static int pthread_sleep_thread_init(pthread_ring_t **); -static void pthread_sleep_thread_exit(void *); - -static pthread_key_t pthread_sleep_proc_key; -static pthread_once_t pthread_sleep_proc_initialized = PTHREAD_ONCE_INIT; - - - -static int -pthread_sleep_proc_init(void) -{ - int error; - - DEBUG_PTHREAD_ENTRY(); - - error = pthread_key_create(&pthread_sleep_proc_key, - pthread_sleep_thread_exit); - - DEBUG_PTHREAD_EXIT(); - return error; -} - -static void -pthread_sleep_proc_init2(void) -{ - int error; - - DEBUG_PTHREAD_ENTRY(); - - if ((error = pthread_sleep_proc_init()) != 0) { - (void) fprintf(stderr, "pthread_sleep_proc_init() - %s\n", - strerror(error)); - DEBUG_PTHREAD_EXIT(); - exit(EXIT_FAILURE); - } - - DEBUG_PTHREAD_EXIT(); -} - -static int -pthread_sleep_thread_init(pthread_ring_t **res) -{ - int error; - pthread_ring_t *ring; - - DEBUG_PTHREAD_ENTRY(); - - if ((ring = malloc(sizeof(pthread_ring_t))) == NULL) { - error = ENOMEM; - goto err; - } - - if ((error = pthread_ring_init(ring)) != 0) - goto err; - - if ((error = pthread_setspecific(pthread_sleep_proc_key, ring)) != 0) - goto err; - - *res = ring; - - DEBUG_PTHREAD_EXIT(); - return 0; - -err: - if (ring != NULL) - free(ring); - - DEBUG_PTHREAD_EXIT(); - return error; -} - -static void -pthread_sleep_thread_exit(void *specific) -{ - pthread_ring_t *ring = (pthread_ring_t *)specific; - - DEBUG_PTHREAD_ENTRY(); - - (void) pthread_ring_destroy(ring); - free(ring); - - /* - * Although NetBSD threads don't need this, some pthread - * implementations do. Some will crash for attempting to reference the - * already freed memory twice calling us again until we NULL the - * pointer for the data. Lame, but the POSIX standard was unclear - * about this. - */ - (void) pthread_setspecific(pthread_sleep_proc_key, NULL); - - DEBUG_PTHREAD_EXIT(); -} - - - -/* - * Suspends the calling thread for duration specified in supplied timespec. - * Returns 0 on success, or an error number. - */ -int -pthread_nanosleep(struct timespec *ts) -{ - int error; - struct timeval tv; - struct timespec its; - pthread_ring_t *ring; - - DEBUG_PTHREAD_ENTRY(); - - /* - * Process specific initialization if needed - */ - if ((error = pthread_once(&pthread_sleep_proc_initialized, - pthread_sleep_proc_init2)) != 0) - goto err; - /* - * Thread specific initialization if needed - * XXX Use pthread_once() here too, or mutex around ring? - */ - if ((ring = pthread_getspecific(pthread_sleep_proc_key)) == NULL) { - if ((error = pthread_sleep_thread_init(&ring)) != 0) - goto err; - } - - /* - * Generate absolute time timespec using current time and supplied - * timespec delay. - */ - if (gettimeofday(&tv, NULL) == -1) { - error = errno; - goto err; - } - TIMEVAL_TO_TIMESPEC(&tv, &its); - timespecadd(&its, ts, &its); - - /* - * We can finally sleep. We expect ETIMEDOUT to be the normal return - * value in this case, which we convert to a no-error. Other errors - * will be returned un changed. - */ - if ((error = pthread_ring_wait(ring, &its)) == ETIMEDOUT) - error = 0; - -err: - DEBUG_PTHREAD_EXIT(); - return error; -} - -/* - * Suspends the current thread for the duration specified into supplied - * timeval. Returns 0 on success or an error number. - */ -int -pthread_microsleep(struct timeval *tv) -{ - struct timespec ts; - int error; - - DEBUG_PTHREAD_ENTRY(); - - TIMEVAL_TO_TIMESPEC(tv, &ts); - error = pthread_nanosleep(&ts); - - DEBUG_PTHREAD_EXIT(); - return error; -} - -/* - * Suspends execution of current thread for duration of specified - * milliseconds. Returns 0 on success or an error number. - */ -int -pthread_millisleep(unsigned int ms) -{ - struct timeval tv; - int error; - - DEBUG_PTHREAD_ENTRY(); - - tv.tv_sec = ms / 1000; - tv.tv_usec = (ms % 1000) * 1000; - error = pthread_microsleep(&tv); - - DEBUG_PTHREAD_EXIT(); - return error; -} - -/* - * Suspends execution of thread for duration of specified number of seconds. - * Returns 0 on success or an error number. - */ -unsigned int -pthread_sleep(unsigned int seconds) -{ - struct timespec ts; - int error; - - DEBUG_PTHREAD_ENTRY(); - - ts.tv_sec = seconds; - ts.tv_nsec = 0; - error = pthread_nanosleep(&ts); - - DEBUG_PTHREAD_EXIT(); - return error; -} - -/* - * Suspends execution of thread for durection of specified number of - * microseconds. Like usleep(3). - */ -int -pthread_usleep(useconds_t ms) -{ - struct timeval tv; - int error; - - DEBUG_PTHREAD_ENTRY(); - - tv.tv_sec = 0; - tv.tv_usec = ms; - error = pthread_microsleep(&tv); - - DEBUG_PTHREAD_EXIT(); - return error; -} diff --git a/tests/pthread_utils/mm_pthread_sleep.h b/tests/pthread_utils/mm_pthread_sleep.h deleted file mode 100644 index 93b6766..0000000 --- a/tests/pthread_utils/mm_pthread_sleep.h +++ /dev/null @@ -1,57 +0,0 @@ -/* $Id: mm_pthread_sleep.h,v 1.2 2005/09/16 08:49:06 mmondor Exp $ */ - -/* - * Copyright (C) 2005, Matthew Mondor - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Matthew Mondor. - * 4. The name of Matthew Mondor may not be used to endorse or promote - * products derived from this software without specific prior written - * permission. - * 5. Redistribution of source code may not be released under the terms of - * any GNU Public License derivate. - * - * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - - - -#ifndef MM_PTHREAD_SLEEP_H -#define MM_PTHREAD_SLEEP_H - - - -#include -#include -#include - - - -extern int pthread_nanosleep(struct timespec *); -extern int pthread_microsleep(struct timeval *); -extern int pthread_millisleep(unsigned int); -extern unsigned int pthread_sleep(unsigned int); -extern int pthread_usleep(useconds_t); - - - -#endif diff --git a/tests/pthread_utils/tests/msg_test.c b/tests/pthread_utils/tests/msg_test.c deleted file mode 100644 index bb7e918..0000000 --- a/tests/pthread_utils/tests/msg_test.c +++ /dev/null @@ -1,269 +0,0 @@ -/* $Id: msg_test.c,v 1.3 2005/11/18 10:54:58 mmondor Exp $ */ - -/* - * Copyright (C) 2005, Matthew Mondor - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Matthew Mondor. - * 4. The name of Matthew Mondor may not be used to endorse or promote - * products derived from this software without specific prior written - * permission. - * 5. Redistribution of source code may not be released under the terms of - * any GNU Public License derivate. - * - * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - - - -#include -#include -#include -#include -#include -#include - -#include -#include -#include - - - -MMCOPYRIGHT("@(#) Copyright (c) 2005\n\ -\tMatthew Mondor. All rights reserved.\n"); -MMRCSID("$Id: msg_test.c,v 1.3 2005/11/18 10:54:58 mmondor Exp $"); - - - -#define THREADS 32 -#define ROUNDS 8 -#define TIMEOUT 1 -/*#define PRINTLOCK*/ -/*#define NOPRINT*/ - - - -struct message { - pthread_msg_t node; - int id, i; -}; - - - -int main(void); -static void threadfunc(pthread_object_t *, void *); -static void printfunc(const char *, ...); - - - -static pthread_port_t main_port; -static pthread_mutex_t print_lock; - - - -int -main(void) -{ - pthread_ring_t ring; - struct message *msg; - int i, err; - int threads_args[THREADS]; - struct timeval tv; - struct timespec ts, ts1; - - if ((err = pthread_mutex_init(&print_lock, NULL)) != 0) { - (void) printf("main() - stdout lock - %s\n", strerror(err)); - exit(EXIT_FAILURE); - } - - if ((err = pthread_port_init(&main_port)) != 0 || - (err = pthread_ring_init(&ring)) != 0 || - (err = pthread_port_set_ring(&main_port, &ring)) != 0) { - printfunc("main() - initialization - %s\n", strerror(err)); - exit(EXIT_FAILURE); - } - - printfunc("Main: launching threads\n"); - - if ((err = pthread_poll_init()) != 0) { - printfunc("main() - pthread_poll_init() - %s\n", - strerror(err)); - exit(EXIT_FAILURE); - } - - /* - * Initializes a poll of ready threads which can be dispatched - * functions to execute. - */ - if ((err = pthread_object_init(THREADS + 1)) != 0) { - printfunc("main() - pthread_object_init() - %s\n", - strerror(err)); - exit(EXIT_FAILURE); - } - - /* - * Now dispatch a main reentrant function to many threads, without - * waiting for them to complete, in an asynchroneous manner. - * XXX Because of the way this works, the parent main thread should - * actually already be listening to messages... We did create a port - * however, which should queue messages until we reach the main loop. - */ - for (i = 0; i < THREADS; i++) { - threads_args[i] = i; - if ((err = pthread_object_call(NULL, threadfunc, - &threads_args[i])) != 0) - printfunc("main() - pthread_object_call() - %s\n", - strerror(errno)); - } - - ts1.tv_sec = TIMEOUT; - ts1.tv_nsec = 0; - for (;;) { - /* - * Read messages as long as there are any, and reply to each - * of them in a synchroneous manner. - */ - while ((msg = (struct message *)pthread_msg_get(&main_port)) - != NULL) { - - printfunc( - "Main: Received message %d from thread #%d\n", - msg->i, msg->id); - - if ((err = pthread_msg_reply((pthread_msg_t *)msg)) - != 0) - printfunc( - "Main: pthread_message_reply() - %s\n", - strerror(err)); - } - - /* - * No more messages to process; Wait for any message(s) to be - * available. - * Note that there is special provision in the event where - * this loop first polling for new messages before processing - * them, which causes waiting for the ring to immediately - * return instead of actually waiting if any messages already - * have been sent. - */ - printfunc("Main: Waiting for messages\n"); - - (void) gettimeofday(&tv, NULL); - TIMEVAL_TO_TIMESPEC(&tv, &ts); - timespecadd(&ts, &ts1, &ts); - if ((err = pthread_ring_wait(&ring, &ts)) != 0) { - printfunc("Main: pthread_ring_wait() - %s\n", - strerror(err)); - break; - } - } - - (void) pthread_mutex_destroy(&print_lock); - (void) pthread_port_destroy(&main_port); - (void) pthread_ring_destroy(&ring); - - return 0; -} - -static void -threadfunc(pthread_object_t *obj, void *args) -{ - int id = *(int *)args; - int i, err; - struct message msg; - pthread_port_t rport; - pthread_ring_t rring; - - if ((err = pthread_port_init(&rport)) != 0 || - (err = pthread_ring_init(&rring)) != 0 || - (err = pthread_port_set_ring(&rport, &rring)) != 0 || - (err = pthread_msg_init((pthread_msg_t *)&msg, &rport)) != 0) { - printfunc("threadfunc() - initialization - %s\n", - strerror(err)); - return; - } - - msg.id = id; - - (void) printfunc("Thread #%d started\n", id); - - for (i = 0; i < ROUNDS; i++) { - /* - * Prepare and send synchronous message. For asynchronous - * operation, we would need to allocate a message and to send - * it, and not expect a reply back immediately, even letting - * the other end free the message as necessary. In synchronous - * mode we can use the same message over and over and share - * its memory area using proper send/reply methods for - * synchronization. - */ - msg.i = i; - if ((err = pthread_msg_put(&main_port, (pthread_msg_t *)&msg)) - != 0) - printfunc("Thread: pthread_message_put() - %s\n", - strerror(err)); - - /* Now wait for synchronous reply and discard it */ - if ((err = pthread_ring_wait(&rring, NULL)) != 0) { - printfunc("Thread: pthread_ring_wait() - %s\n", - strerror(err)); - break; - } - if (pthread_msg_get(&rport) == NULL) - printfunc("Thread: pthread_msg_get() == NULL!?\n"); - printfunc("Thread #%d received reply message for %d\n", - id, i); - } - - printfunc("Thread #%d ending\n", id); - - (void) pthread_port_destroy(&rport); - (void) pthread_ring_destroy(&rring); - (void) pthread_msg_destroy((pthread_msg_t *)&msg); -} - -static void -printfunc(const char *fmt, ...) -{ - char buf[1024]; - va_list arg_ptr; - int len; - -#ifdef NOPRINT - return; -#endif - - *buf = '\0'; - va_start(arg_ptr, fmt); - if ((len = vsnprintf(buf, 1023, fmt, arg_ptr)) < 1) - return; - va_end(arg_ptr); - -#ifdef PRINTLOCK - (void) pthread_mutex_lock(&print_lock); -#endif - (void) fwrite(buf, len, 1, stdout); -#ifdef PRINTLOCK - (void) fflush(stdout); - (void) pthread_mutex_unlock(&print_lock); -#endif -} diff --git a/tests/pthread_utils/tests/poll_test.c b/tests/pthread_utils/tests/poll_test.c deleted file mode 100644 index 7be14bc..0000000 --- a/tests/pthread_utils/tests/poll_test.c +++ /dev/null @@ -1,73 +0,0 @@ -/* $Id: poll_test.c,v 1.1 2005/09/14 23:48:10 mmondor Exp $ */ - -/* - * Copyright (C) 2005, Matthew Mondor - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Matthew Mondor. - * 4. The name of Matthew Mondor may not be used to endorse or promote - * products derived from this software without specific prior written - * permission. - * 5. Redistribution of source code may not be released under the terms of - * any GNU Public License derivate. - * - * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - - - -#include -#include -#include -#include -#include -#include - -#include -#include -#include - - - -MMCOPYRIGHT("@(#) Copyright (c) 2005\n\ -\tMatthew Mondor. All rights reserved.\n"); -MMRCSID("$Id: poll_test.c,v 1.1 2005/09/14 23:48:10 mmondor Exp $"); - - - -int main(void); - - - -int -main(void) -{ - int err; - - if ((err = pthread_poll_init()) != 0) { - (void) fprintf(stderr, "main() - pthread_poll_init() - %s\n", - strerror(err)); - exit(EXIT_FAILURE); - } - - return 0; -} diff --git a/tests/pthread_utils/tests/polltest.c b/tests/pthread_utils/tests/polltest.c deleted file mode 100644 index ceedfd5..0000000 --- a/tests/pthread_utils/tests/polltest.c +++ /dev/null @@ -1,153 +0,0 @@ -/* - * The goal of this program is to verify if it is valid for multiple threads - * to poll(2) on the same filedescriptor, and if so, what happens whenever - * an event is triggered on that descriptor. - * - * XXX Problems: - * - Only one of the polling threads seems to be awaken when an event occurs - * on the descriptor. This probably means that using a signal would be - * better... I sure don't want to need a filedescriptor per ring... - * If I did however, would this really hurt? Are there that many rings? - * But oops, this actually means two filedescriptors for each! - * using a signal is probably better. However, we then need to clobber some - * signal... We could use SIGUSR2. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - - - -#define THREADS 8 - - - -int main(void); -static void *thread_poll(void *); -static void *thread_notify(void *); -static void thread_print(int, const char *); - - - -static int sockets[2]; -static int threadargs[THREADS]; -static pthread_mutex_t print_mutex; -static pthread_mutex_t sockets_mutex; - - - -int -main(void) -{ - pthread_t threadid; - int i; - - /* - * Create socketpair which will be used to trigger events to awaken - * polling threads. - */ - if (socketpair(AF_LOCAL, SOCK_DGRAM, 0, sockets) != 0) { - perror("socketpair()"); - exit(EXIT_FAILURE); - } - if (fcntl(sockets[0], F_SETFL, O_NONBLOCK) != 0 || - fcntl(sockets[1], F_SETFL, O_NONBLOCK) != 0) { - perror("fcntl()"); - exit(EXIT_FAILURE); - } - - pthread_mutex_init(&print_mutex, NULL); - pthread_mutex_init(&sockets_mutex, NULL); - - /* - * First launch THREADS polling threads - */ - for (i = 0; i < THREADS; i++) { - threadargs[i] = i; - pthread_create(&threadid, NULL, thread_poll, &threadargs[i]); - } - sleep(1); - - /* - * And finally launch notifyer thread - */ - pthread_create(&threadid, NULL, thread_notify, NULL); - - /* - * Now just wait - */ - for (;;) - (void) pause(); -} - -static void * -thread_poll(void *args) -{ - struct pollfd fds[1]; - int n; - int id = *(int *)args; - char c; - - fds[0].fd = sockets[1]; - fds[0].events = POLLIN; - for (;;) { - thread_print(id, "Polling"); - if ((n = poll(fds, 1, -1)) == -1) { - perror("poll()"); - return NULL; - } - thread_print(id, "Poll returned"); - if (n == 0) { - thread_print(id, "Woke up! (no data)"); - continue; - } - if ((fds[0].revents & POLLIN) != 0) { - /* Attempt to read event/byte */ - thread_print(id, "Woke up! (with data)"); - pthread_mutex_lock(&sockets_mutex); - while ((n = read(sockets[1], &c, 1)) == 1) - thread_print(id, "Read data!"); - if (n == -1) - thread_print(id, strerror(errno)); - pthread_mutex_unlock(&sockets_mutex); - } - } -} - -/* ARGSUSED */ -static void * -thread_notify(void *args) -{ - char c = '\0'; - struct pollfd fds[1]; - - fds[0].fd = sockets[0]; - fds[0].events = POLLOUT; - for (;;) { - sleep(1); - thread_print(-1, "Notifying"); - pthread_mutex_lock(&sockets_mutex); - if (write(sockets[0], &c, 1) != 1) { - /* Poll until we can send data */ - (void) poll(fds, 1, -1); - } - pthread_mutex_unlock(&sockets_mutex); - } -} - -static void -thread_print(int id, const char *str) -{ - - pthread_mutex_lock(&print_mutex); - printf("%d: %s\n", id, str); - pthread_mutex_unlock(&print_mutex); -} diff --git a/tests/pthread_utils/tests/sigtest.c b/tests/pthread_utils/tests/sigtest.c deleted file mode 100644 index cc4b681..0000000 --- a/tests/pthread_utils/tests/sigtest.c +++ /dev/null @@ -1,146 +0,0 @@ -/* - * The goal of this program is to verify if it is valid for multiple threads - * to be awaken from a poll(2) call by a single process-wide signal. This - * would allow the notifyer of a thread message event to generate this signal - * if needed to cause interested treads to wake up. Threads which do not want - * to receive the signal can simply ignore it using pthread_sigmask(). - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - - - -#define THREADS 8 - - - -int main(void); -static void *thread_poll(void *); -static void *thread_notify(void *); -static void thread_print(int, const char *); - - - -static int sockets[2]; -static int threadargs[THREADS]; -static pthread_mutex_t print_mutex; -static pthread_mutex_t sockets_mutex; - - - -int -main(void) -{ - pthread_t threadid; - int i; - - /* - * Create socketpair which will be used to trigger events to awaken - * polling threads. - */ - if (socketpair(AF_LOCAL, SOCK_DGRAM, 0, sockets) != 0) { - perror("socketpair()"); - exit(EXIT_FAILURE); - } - if (fcntl(sockets[0], F_SETFL, O_NONBLOCK) != 0 || - fcntl(sockets[1], F_SETFL, O_NONBLOCK) != 0) { - perror("fcntl()"); - exit(EXIT_FAILURE); - } - - pthread_mutex_init(&print_mutex, NULL); - pthread_mutex_init(&sockets_mutex, NULL); - - /* - * First launch THREADS polling threads - */ - for (i = 0; i < THREADS; i++) { - threadargs[i] = i; - pthread_create(&threadid, NULL, thread_poll, &threadargs[i]); - } - sleep(1); - - /* - * And finally launch notifyer thread - */ - pthread_create(&threadid, NULL, thread_notify, NULL); - - /* - * Now just wait - */ - for (;;) - (void) pause(); -} - -static void * -thread_poll(void *args) -{ - struct pollfd fds[1]; - int n; - int id = *(int *)args; - char c; - - fds[0].fd = sockets[1]; - fds[0].events = POLLIN; - for (;;) { - thread_print(id, "Polling"); - if ((n = poll(fds, 1, -1)) == -1) { - perror("poll()"); - return NULL; - } - thread_print(id, "Poll returned"); - if (n == 0) { - thread_print(id, "Woke up! (no data)"); - continue; - } - if ((fds[0].revents & POLLIN) != 0) { - /* Attempt to read event/byte */ - thread_print(id, "Woke up! (with data)"); - pthread_mutex_lock(&sockets_mutex); - while ((n = read(sockets[1], &c, 1)) == 1) - thread_print(id, "Read data!"); - if (n == -1) - thread_print(id, strerror(errno)); - pthread_mutex_unlock(&sockets_mutex); - } - } -} - -/* ARGSUSED */ -static void * -thread_notify(void *args) -{ - char c = '\0'; - struct pollfd fds[1]; - - fds[0].fd = sockets[0]; - fds[0].events = POLLOUT; - for (;;) { - sleep(1); - thread_print(-1, "Notifying"); - pthread_mutex_lock(&sockets_mutex); - if (write(sockets[0], &c, 1) != 1) { - /* Poll until we can send data */ - (void) poll(fds, 1, -1); - } - pthread_mutex_unlock(&sockets_mutex); - } -} - -static void -thread_print(int id, const char *str) -{ - - pthread_mutex_lock(&print_mutex); - printf("%d: %s\n", id, str); - pthread_mutex_unlock(&print_mutex); -} diff --git a/tests/rotate/GNUmakefile b/tests/rotate/GNUmakefile deleted file mode 100644 index 41ca002..0000000 --- a/tests/rotate/GNUmakefile +++ /dev/null @@ -1,57 +0,0 @@ -# $Id: GNUmakefile,v 1.2 2006/05/06 12:25:02 mmondor Exp $ - -CC := cc -RM := rm -UNAME := uname - -CFLAGS += -Wall - -# Enable for verbosity/debugging -#CFLAGS += -v -H -g -#LDFLAGS += -v -g - -# And to disable assertions -CFLAGS += -DNDEBUG - -OBJS := main.o -BINS := rotate -CBINS := $(addsuffix .exe,$(BINS)) - -SDL_CFLAGS := $(shell sdl-config --cflags) -SDL_LDFLAGS := $(shell sdl-config --libs) -SDL_LDFLAGS += -lSDL_gfx - -# OS dependent settings follow -OS := $(shell $(UNAME) -s) -ifneq (,$(findstring CYGWIN,$(OS))) - # cygwin-mingw - CFLAGS += -mno-cygwin -I/usr/include/mingw - LDFLAGS += -mwindows -mno-cygwin -L/usr/lib/mingw -L/usr/local/lib -# GL_CFLAGS := -# GL_LDFLAGS := -lopengl32 -lglu32 -else - # unix - CFLAGS += -I/usr/include -I/usr/pkg/include -I/usr/X11R6/include - LDFLAGS += -L/usr/lib -L/usr/pkg/lib -L/usr/X11R6/lib -# GL_CFLAGS := -# GL_LDFLAGS := -lGL -lGLU -endif - -#CFLAGS += $(SDL_CFLAGS) $(GL_CFLAGS) -#LDFLAGS += $(SDL_LDFLAGS) $(GL_LDFLAGS) -CFLAGS += $(SDL_CFLAGS) -LDFLAGS += $(SDL_LDFLAGS) - -all: $(BINS) - -%.o: %.c - $(CC) -c $(CFLAGS) -I. -o $@ $< - -#$(BINS): $(OBJS) -# $(CC) -o $@ $(CFLAGS) $(OBJS) $(LDFLAGS) - -$(BINS): $(OBJS) - $(CC) -o $@ $(OBJS) $(LDFLAGS) - -clean: - $(RM) -f $(BINS) $(CBINS) $(OBJS) stdout.txt stderr.txt diff --git a/tests/rotate/main.c b/tests/rotate/main.c deleted file mode 100644 index b2ed875..0000000 --- a/tests/rotate/main.c +++ /dev/null @@ -1,195 +0,0 @@ -/* $Id: main.c,v 1.1 2006/05/06 12:25:02 mmondor Exp $ */ - -/* - * Copyright (C) 2006, Matthew Mondor - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Matthew Mondor. - * 4. The name of Matthew Mondor may not be used to endorse or promote - * products derived from this software without specific prior written - * permission. - * 5. Redistribution of source code may not be released under the terms of - * any GNU Public License derivate. - * - * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - - - -#include -#include -#include -#include -#include - -#include -#include -#include - - - -int main(int, char **); - - - -static SDL_Surface *screen_surface; - - - -int -main(int argc, char **argv) -{ - SDL_Surface *s, *save; - int a, ad, at; - size_t fnlen; - char *fnbuf, *file; - - if (argc != 3) { - (void) fprintf(stderr, "Usage: rotate \"\" \n"); - exit(EXIT_FAILURE); - } - - if ((file = strdup(argv[1])) != NULL) { - char *cptr; - - if ((cptr = strrchr(file, '.')) != NULL) - *cptr = '\0'; - } else { - (void) fprintf(stderr, "strdup(%s)\n", argv[1]); - exit(EXIT_FAILURE); - } - - fnlen = strlen(file) + 16; - if ((fnbuf = malloc(fnlen)) == NULL) { - (void) fprintf(stderr, "fnbuf = malloc(%d)\n", (int)fnlen + 8); - exit(EXIT_FAILURE); - } - - at = (double)strtol(argv[2], NULL, 10); - ad = (360 / at); - if (at * ad != 360) { - (void) fprintf(stderr, " must be a divisor of 360\n"); - exit(EXIT_FAILURE); - } - - if (SDL_Init(SDL_INIT_VIDEO) == -1) { - (void) fprintf(stderr, "SDL_Init() - %s\n", SDL_GetError()); - exit(EXIT_FAILURE); - } - if ((screen_surface = SDL_SetVideoMode(640, 480, 32, SDL_DOUBLEBUF)) - == NULL) { - (void) fprintf(stderr, "SDL_SetVideoMode() - %s\n", - SDL_GetError()); - goto err; - } - - if ((s = SDL_LoadBMP(argv[1])) == NULL) { - (void) fprintf(stderr, "SDL_LoadBMP(%s) - %s", - argv[1], SDL_GetError()); - goto err; - } - if ((save = SDL_CreateRGBSurface(SDL_SWSURFACE, s->w, s->h, 32, - s->format->Rmask, s->format->Gmask, s->format->Bmask, - s->format->Amask)) == NULL) { - (void) fprintf(stderr, "SDL_CreateRGBSurface() - %s\n", - SDL_GetError()); - goto err; - } - - /* - * XXX Assumes 0x000000 is the trasparent color, should probably be - * part of the command line parameters. - */ - { - Uint32 col; - - col = SDL_MapRGB(s->format, 0x00, 0x00, 0x00); - if (SDL_SetColorKey(s, SDL_SRCCOLORKEY, col) == -1) { - (void) fprintf(stderr, "SDL_SetColorKey() - %s\n", - SDL_GetError()); - goto err; - } - } - - for (a = 0; a < 360; a += ad) { - SDL_Surface *t; - SDL_Rect d; - int cx, cy, c; - - cx = s->w / 2; - cy = s->h / 2; - c = (cx >= cy ? cx : cy); - - /* - * XXX Again assumes that 0x000000 color is transparent. - * Moreover, in a future version, we also could allow to scale - * the bitmap image as wanted... - */ - if (boxRGBA(save, 0, 0, s->w - 1, s->h - 1, - 0x00, 0x00, 0x00, 0xff) != 0 || - boxRGBA(screen_surface, 320 - c, 240 - c, 320 + c, 240 + c, - 0x00, 0x00, 0x00, 0xff) != 0) { - (void) fprintf(stderr, "boxRGBA() - %s\n", - SDL_GetError()); - goto err; - } - - if ((t = rotozoomSurface(s, (double)-a, 1.0, 1)) == NULL) { - (void) fprintf(stderr, "rotozoomSurface() - %s\n", - SDL_GetError()); - goto err; - } - d = (SDL_Rect){320 - (t->w / 2), 240 - (t->h / 2), 0, 0}; - if (SDL_BlitSurface(t, NULL, screen_surface, &d) != 0) { - (void) fprintf(stderr, "SDL_BlitSurface() - %s\n", - SDL_GetError()); - goto err; - } - SDL_FreeSurface(t); - - d = (SDL_Rect){320 - c, 240 - c, 320 + c, 240 + c}; - if (SDL_BlitSurface(screen_surface, &d, save, NULL) != 0) { - (void) fprintf(stderr, "SDL_BlitSurface() - %s\n", - SDL_GetError()); - goto err; - } - (void) snprintf(fnbuf, fnlen - 1, "%s-%03d.bmp", file, (int)a); - if (SDL_SaveBMP(save, fnbuf) != 0) { - (void) fprintf(stderr, "SDL_SaveBMP(%s) - %s\n", - fnbuf, SDL_GetError()); - goto err; - } - - if (SDL_Flip(screen_surface) != 0) { - (void) fprintf(stderr, "SDL_Flip() - %s", - SDL_GetError()); - goto err; - } - } - - SDL_Quit(); - exit(EXIT_SUCCESS); - -err: - SDL_Quit(); - exit(EXIT_FAILURE); -} diff --git a/tests/sdl-client/GNUmakefile b/tests/sdl-client/GNUmakefile deleted file mode 100644 index e316dd3..0000000 --- a/tests/sdl-client/GNUmakefile +++ /dev/null @@ -1,90 +0,0 @@ -# $Id: GNUmakefile,v 1.31 2006/05/23 01:32:50 mmondor Exp $ - -CC := cc -RM := rm -UNAME := uname -TOUCH := touch -OBJDUMP := objdump -OBJCOPY := objcopy -GREP := grep -AWK := awk -DATE := date -STRIP := strip - -TMPDIR := /tmp - -CFLAGS += -Wall - -# Enable for verbosity/debugging -#CFLAGS += -v -H -g -#LDFLAGS += -v -g - -# And to disable assertions -CFLAGS += -DNDEBUG -CFLAGS += -g - -OBJS := main.o debug.o pool.o thread_msg.o thread_net_recv.o \ - thread_net_send.o screen.o decode.o -BINS := client -CBINS := $(addsuffix .exe,$(BINS)) -RAWOBJS := bmp/RomDD.bmp.enc.o bmp/FedCA.bmp.enc.o wav/nt_cloaked.wav.enc.o \ - wav/nt_uncloak.wav.enc.o wav/nt_shield_up.wav.enc.o \ - wav/nt_shield_down.wav.enc.o wav/nt_fire_torp_other.wav.enc.o \ - wav/nt_plasma_hit.wav.enc.o wav/nt_explosion_other.wav.enc.o \ - fnt/7x13.fnt.enc.o - -SDL_CFLAGS := $(shell sdl-config --cflags) -SDL_LDFLAGS := $(shell sdl-config --libs) -SDL_LDFLAGS += -lSDL_image -lSDL_mixer -lSDL_net -lSDL_gfx - -# OS dependent settings follow -OS := $(shell $(UNAME) -s) -ifneq (,$(findstring CYGWIN,$(OS))) - # cygwin-mingw - CFLAGS += -mno-cygwin -I/usr/include/mingw -DWIN32 - LDFLAGS += -mwindows -mno-cygwin -L/usr/lib/mingw -L/usr/local/lib -# GL_CFLAGS := -# GL_LDFLAGS := -lopengl32 -lglu32 -else - # unix - CFLAGS += -I/usr/include -I/usr/pkg/include -I/usr/X11R6/include - LDFLAGS += -L/usr/lib -L/usr/pkg/lib -L/usr/X11R6/lib -# GL_CFLAGS := -# GL_LDFLAGS := -lGL -lGLU -endif - -# Determine target of compiled objects so that we may convert binaries -# to compatible objects using objcopy and then link them like other modules. -OBJTARGET := $(shell $(TOUCH) $(TMPDIR)/obj.c && \ - $(CC) $(CFLAGS) -c -o $(TMPDIR)/obj.o $(TMPDIR)/obj.c && \ - $(OBJDUMP) -t $(TMPDIR)/obj.o | \ - $(GREP) 'file format' | $(AWK) '{print $$4}' \ - && $(RM) $(TMPDIR)/obj.o $(TMPDIR)/obj.c) -OBJARCH := $(shell echo $(OBJTARGET) | $(AWK) -F '-' '{print $$2}') -SEED := $(shell date +%s) - -#CFLAGS += $(SDL_CFLAGS) $(GL_CFLAGS) -#LDFLAGS += $(SDL_LDFLAGS) $(GL_LDFLAGS) -CFLAGS += $(SDL_CFLAGS) -LDFLAGS += $(SDL_LDFLAGS) - -all: $(BINS) - -%.o: %.c - $(CC) -c $(CFLAGS) -I. -o $@ $< - -encode: - $(CC) -o encode encode.c - -$(RAWOBJS): encode - ./encode $(basename $(basename $@)) $(basename $@) $(SEED) - $(OBJCOPY) -I binary -B $(OBJARCH) -O $(OBJTARGET) $(basename $@) $@ - $(RM) -f $(basename $@) - -$(BINS): $(OBJS) $(RAWOBJS) - $(CC) -o $@ $(OBJS) $(RAWOBJS) $(LDFLAGS) - $(STRIP) -s -w -R .comment -R .ident -R .debug* $@* - -clean: - $(RM) -f $(BINS) $(CBINS) $(OBJS) $(RAWOBJS) encode encode.exe \ - stdout.txt stderr.txt diff --git a/tests/sdl-client/README b/tests/sdl-client/README deleted file mode 100644 index 222484f..0000000 --- a/tests/sdl-client/README +++ /dev/null @@ -1,190 +0,0 @@ -$Id: README,v 1.16 2006/05/23 01:32:50 mmondor Exp $ - -An attempt to develop a portable game client using SDL among unix -and windows operating systems. The only officially supported -compiler should be GCC (and mingw under windows, avoiding the need -for cygwin libraries). The compiling/development environment when -under windows will use cygwin to provide a shell, vim and cvs, -while compile options will be specified as needed for it to use -the mingw compiler (which also comes as part of cygwin). - -Initial tests will be using SDL, SDL_mixer and SDL_net. - -If all works well, it should also be easy to use OpenGL portably. -I was already able to get portable SDL/OpenGL code working, a while -ago, but this generated no sound and had no network requirements. -Hence this test. - -Moreover, the test server under kqueue/ needed a client for further -testing to be possible at its development stage. - -Since SDL_net does not provide non-blocking I/O (and it remains -unclear if windows supports this properly), a decision was made to -port to SDL an inter-thread messaging library I had done for use -with POSIX threads, and to use multiple threads. - -One thread will be used to send data to the server, another thread -to receive data form the server, yet another thread to deal with -all user input events, and a main thread to receive all those events -in an asynchroneous manner form the utility threads and run the -main loop. - -04:21 <@lucca> one thread to send, one thread to receive, one thread to poll - for io events, and one thread to bring them all and in the darkness - bind them. - -:) - -Ogg-vorbis will be used for music, using SDL_mixer. Ship rotations -will be performed using SDL_gfx. - -It is very important to avoid having to link this client statically -against GPL or LGPL libraries, because of the viral nature of those -licenses. I do not intend to release my code under those licenses. -If it ever publically is released, it shall be done under a MIT/BSD -derived license. - -I am also thinking about dedicating a thread for the connect state -to the server, or possibly to have the writer or reader thread also -perform that task. - -It is possible that a thread be ideal for rendering as well. It -could be notified when a screen refresh is wanted, when it could -set a flag. When it's time for it to draw a frame (honoring FPS), -it would if the flag is set, or perhaps it simply could when it -wants. I wonder if it would be appropriate for the display thread -to not need a mutex, since it would always be read-only accessing -the data. - -Anticipated design so far: - Main thread - User events thread - Network receive thread - Network Send/Connect thread - Display thread (the Receive thread will draw for now). - -It is possible that states may be desired. For instance, there -would be the connection state, the one where the user and client -have to provide authentication information (this could be part of -connect phase perhaps), and in-game state. - -At first, as a test, the world will all be seen by everyone and -will fit into their screen. A world of 1024x768 could be used for -this :) Then there will probably be addition of a chat system with -messages window, to continue enhancing the protocol. Things will -go on from there... - -Hmm for now I want to simply use a joypad. -- Button 0 fires in direction of the paddle direction. -- Buttons 1 and 3 could act like button 0, to provide secondary weapon (1) - and special weapon (3). -- Button 2 attempts to correct navigation direction in the direction - of the paddle. -- Button 6 would accelerate. -- Button 7 would decelerate. -- Button 4 could toggle shields -- Button 5 could toggle cloak - -Equivalent keyboard layout: -- uiojlm,. would change angle just like the gamepad directions. -- w would toggle cloak -- s would toggle shield -- z would thrust up -- a would thrust down -- d would cause direction change -- space would torp -- f would fire second weapon -- g would fire special weapon - -And we're already out of buttons, we can't beam up/down armies or -bomb. Unless button 3 was special instead of a special weapon, -and allowed to perform various commands depending on the paddle -direction (i.e. up/down to beam up/down, left to bomb). This also -means that orbiting/launching would need to be automatic. Of course -all this is if we're thinking about a game like netrek. But we'll -simply only allow dogfighting at first. - -Now it becomes tricky how I'll minimize bandwidth sent from the -client to the server. Probably that buttons events will be monitored, -and then current paddle direction when required. In the case of -direction change button, the last paddle direction applied would -be remembered, and if the same, the event could be dropped. If -not, send a direction change packet. What happens if a button -remains pressed while a direction change occurs? We probably should -ignore it. - - -Threading limitations under win32 -================================= - -There seem to be bugs when using SDL with multiple threads under windows -which I did not observe on unix systems. The docs specify that the main -thread should perform the drawing, but it wasn't specified that another -thread than the initial one would not be able to obtain all user input -events on windows. Typed keys would not be received, for instance. - -It then appears that most of the processing must be done in the main -initial thread, while only networking related blocking functions will -be done in slave threads. - -There also seem to be other windows-specific problems using SDL threads, -such as instability. I have noticed that when using another thread -for user events reception, part of the application would often lockup, -despite my code properly using mutexes as required, and all sound and -greaphics being performed by the main initial thread nevertheless. -These problems were also not found to occur on unix systems. - -The design was thus changed for now, and threads will be used for SDL_net -functions only, since they are blocking. Let's hope that this will work -stably, however. It remains to be tested. - - -Storing images and sound samples as part of the executable binary -================================================================= - -I was able to include read-only (.rodata) and read-write (.data) into -binaries directly from files using objcopy and linking them on NetBSD, -Linux and cygwin-mingw. The SDL_image library, which I now successfully -built for mingw, includes functions that can use RWops, and the SDL -library allows to easily create RWops from memory buffers. Moreover, the -SDL_mixer library also allows to do this to load sound samples. I should -thus modify the makefiles and code to very easily use these features. - -The SDL_mixer library however does not allow by itself to do this easily -with music files. However, thise generally being considerably larger, -it should not be a problem and they can remain external. - -If this works fine, it would be easy to generate a cryptographic block -cipher key at build time and to include that key within the executable -as well. Copies of the binary files to be included could then be -encrypted using that cipher to a temporary copy which will be linked -in, and an initialization function could be provided to unencrypt the -files prior to use. Of course, since the key is also in the executable, -there is no real security. However, it could prevent computer-illiterate -people from too easily ripping our original content. RC4 could be used -for this for instance, or even a much more simple custom encoding -algorithm :) - - -Using a map -=========== - -We probably want to randomize the ship's positions on the map. -Randomization in this respect could be done on a position in a -circle of varied radius distance to the location of the actual -ship. This would prevent clients from being able to exactly fire -at the actual ship position using the map (which also would make -cloaking useful against an unofficial client). - - -PROGRAMMING STYLE -================= - -The style chosen for this project consists of the (Net)BSD KNF style -(Kernel Normal Form) style borrowed from. All code should conform to it. -Additionally, lint(1)-style comments are used to make the code clearer -for future code auditors. - - - -Matt diff --git a/tests/sdl-client/bmp/FedCA.bmp b/tests/sdl-client/bmp/FedCA.bmp deleted file mode 100644 index be3a56267b517c13484c6601f95d26989608c14b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4854 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 diff --git a/tests/sdl-client/bmp/fedship.bmp b/tests/sdl-client/bmp/fedship.bmp deleted file mode 100755 index 465806fdf2c5fbb1f9b021b287f87554ace7c012..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 103478 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

Uj`^3 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 icQSl8#el))=Ata84~-!jbw1<(uw>O!_$d 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 UWygl@X?bKh5_lD(v)RG^FTEQsqyPW_ diff --git a/tests/sdl-client/bmp/indship.bmp b/tests/sdl-client/bmp/indship.bmp deleted file mode 100755 index 1a7e39b8bcd9a35dbab210a0fb7b8794593e7b44..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 87098 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

    ^m#*$c2-ijG}lrJuhAnT^m}TF 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 diff --git a/tests/sdl-client/bmp/misc-fixed-bold-r-normal--13-120-75-75-c-80-iso8859-1.bmp b/tests/sdl-client/bmp/misc-fixed-bold-r-normal--13-120-75-75-c-80-iso8859-1.bmp deleted file mode 100644 index c55b1721534c44ce6deb1a75cadfdce04c6a0fd6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3390 zcmeHJ-)kH<5T00MZOGULKMCJ<3#ODl_(5esb-9by^u2v4h2$YrWyo#XJD(kv`~aI( z|HS@B={M5dC1+Za#}+botJSwNl4kVH?&yaf!y#n}XurYrC$7J6J-{yu^jD9{wTB0K zsl0plu5>nSh&n}=cr19_V{w(WI}DCGTDT!^R(9^2d75A==rw(uiae0lbmfQT~Kapbu`I#_xZu-oq*y#Z4*uh zj1NhNm}ApWht8Xa`gD2B$E|iqGWLhnVVe!*3p@;yeY5X~XBQ-onPzl&dplU_+&Rf} zCRdH#bkXp^J-(zJ$Ie=WgytlHIx2 z-0WbcUx_izM)=?@UW672G1=3s;NVinhJ^ec8sa~kSNmsSxktYMzi?+A+v+)tcv+gi)eOo5s8Yw3f7wj z7X)86`~&sF(2K)6Waj$u(f}m78Srw8lbZv-Wqg#WgI>n>*yQo^q26C|8iYcgzgrQ_ z*{?!HkqkMCY@)vGoN>FwHsb~@#TxpLirHG=P(Uf-fWYAZ+nd}2?0-bPVX2dFbVjkj zRjC)K4I6A&`C{iP=tsFzEHztK_!|SunbZwCoB5Qe`{0`j19W?Ie&E))SotlMjB6Uh z7&K(Jddb~zXJjH%2&oIiH!l1>76~jl|p_H&SDPw9ZRnQ|b&A@^$sUuLuRWlz(4m}D@ zS5=6U{TM4`bHl_&W|%ofgAD_nR(vhy2-HhYg!RF5NL6YQj+82&6pyl4;ib$0;mjBM z4#z>wWxhn?7tiO2h+hUjR diff --git a/tests/sdl-client/bmp/misc-fixed-bold-r-normal--15-120-100-100-c-90-iso8859-1.bmp b/tests/sdl-client/bmp/misc-fixed-bold-r-normal--15-120-100-100-c-90-iso8859-1.bmp deleted file mode 100644 index 5c2ab05190944fcd96557fcba456757dcc0f0e3e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4862 zcmeH~?~5D98ONXPgq1^9C)z++VOMJ3C9gWlf)W!~XSbn&Lf`aN)4rKSt&YN-NjL~< zx!QZIk>7~KFJ8s3CA@Y2@VTCE*jU@UPzB`pv?!DIj)76Db9P>ye7l|q# zq`ETqR+mcE5yC;LtLi(f12<)#r|iscQnmrRjiZySf&18)WWK*Pc1Z&hD970th7~(( zCzAm}NSYVwYzO_JzeZNCe^YV+r3ZXSBw*<7Nk`k_!ThxQrh}u>dWvPPJRH`-V z1%w%&m)X?J(Sok$a8Xy;vvsRxM3U+%W1TuAWM>+hvfct?Ieq@c0_B{O=Mc*$Fh=X%?eYk@ngBD zH7g)mnst{}n4NwC6ANrvV^RFHni6-z<^|KR2ZrT?v8r*PHV&(8n5~Dhy1-81l2A>z zq)@R7VPLi2Jwqk3{`BYgV}2NxlZ1@ z^}4*=zI;9(6c2x9n6qbnbBr-wdh=d5wti<=F0#4_`g{3}el)gzVi^6%JM6d${Y8pW zJIv7DX$QVi_qvpg3uEE9=e+XO099UNXT{6zW0B1+KC3*-!2c!#igea-c{aeD&E6z5 zXK)zdhjMW#&W(~svf;nC)eE$F)vgy~r*i#9^BDUpD`S2)rb_5Ew@b3Wz;e5x6K^{b zt4rz&JI7*HlD&|$dH1Xr|FK#L^R38}I?C9^rR~A~9+=;qjmz=yg2e^>Rc@r!6YRr` zO;0@T!)arB&hBm6e9{?@Xik6SLWD(8{U=u14tIxRb;-ZoZg>Z4Endtj5BlEOw$gr! z8Y)_AHNfO>)jmyXgP8_H$5duEQGKwLAY;TVKR&@+?ysW;>$dUbJrWiEfN>M``-@A~ zL-p7Dcg-S-)Vf&e6u(L|jQBid@XeT$vU8-stklj-it+gR|HO#Kge|`+{Cs7k6B+rf z!XCM1NLB2t%3eqA`z;5r+LU>Cy+;nTX3tsme!y(IWZMGo0*+G29Rh>aY-|d(VVC93 zV3aZ|%oy(@2rMcU2%>baz&B;AzhY1qEiw47yrb zhK5o`_d=1s1%}U^nAreY+Y(~!L&J93cJXkmFV7i1fs7q%7rtu`VaIvlF_>YD$IQHf zJB@|ny13a#EG4_lF&`o3F!qFr`DyYpecn&_>Ju$)V9HbGlyC2AiSI#d1UIqduo6nP zLvq1Br!m9MKA8N>a?08ENM1)ThX4H$k z>vx?}aS%pgf(L#>xgB^iPbvA0$L+NEMN}4 zakMvX9z&B@97xx2da9zI;HBp5JgR~>ZwjNJAa3j6)MxlKMPo!w&5%U>t(!NntwRb% zgs)S!6Y8y-Z7{nRpsjG&luMd_zlG1$5Vy-=8#_Ny{{nmelSE8-X<^^GR%&LKvfRN1 znYh@hnf^E=+m7nL>KS&t}_r*-p1?O$o#EiDUF)ilarj2LkXBqhaWZ+AA C4jL!` diff --git a/tests/sdl-client/bmp/misc-fixed-medium-r-normal--13-120-75-75-c-80-iso8859-1.bmp b/tests/sdl-client/bmp/misc-fixed-medium-r-normal--13-120-75-75-c-80-iso8859-1.bmp deleted file mode 100644 index e8ac5d4f9acb7587bd21fd2a3fd114d9e6eba41c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3390 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 z 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 diff --git a/tests/sdl-client/bmp/sbexpl.bmp b/tests/sdl-client/bmp/sbexpl.bmp deleted file mode 100755 index b06ffcf99fd8c3a219a6bbd445c4eb1abd60077d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 29050 zcmeI5e~?vIo#5}e_dOuQ9t=h$@%g%GgP0kcNknalO1B9i#`I{*phghwA(>VNb#Z5y zK?Q9R0!}i~)D+HU9Wt~bM!_1Y5_MD27*V1o8^=E=3oGl7CTUS57%e3mYKPt4&*yvY zYvR=S$NcLgRhRer-gD0P_s?_g%cXzv;ty4Y|GlzN6ffcPi(Du8o{CvqMX~ICMe)9~ z^A$I|>3{A1qUgSa&r3EH?bj|Ux(ABlg|97&7jUnq|E!{C$;Cy_tGT$>v$QCBdEU$S z?!mF*`D@0C^H(uP_)+ku;^a%Ka18ody4LN^%OmWJw@;9dW!j%@_8Ab z%La?y%Lj`WEgvi{xPs5u^Z5oYJ{P`mu;^JmQuMr?YYo@CMvC+QY@~SM$Vk!6)w*V+ zXs;bB+ShW8jup@S@K`Z-%UJQe&3ymJSaHsW#)|W{j2Gu~o%azwxmq6`FS@x}-0K

    `Az2-pCb1k2L&gUqf?>e-Gb3dZ3s)cBFVN*YmF9+IpmT{JFz4N$t z{`E!C+6w(#t?T&YI)7VHoOc6%b9Ha)DPFj}r|9Ntf2^nI8RNR1>jp0F^?Yow=(%yQ zc;l^u#fnc27H|2-!Q!o6y9SH5-!@pRx^1Lb^-m+k+qvGs_ceUJbN5Iw!nJCgd*frp zySUzQJD+>FJ~LK~@b}tJ^U2@W+&*4h`)MvdNB4{uBX^7!*L;T0JNe|g_KpL^yS?r_ zP+ZIPo;_Ur{TH9%^6xv36z~7+k>Z-q9Vy<;HM;jmv7YPwT<`hkBd`32`-)fo!&i#M zx87Iuf9k76|84gdFT3?C#ghO2)#4KVe)T`z-?QXX_xHSd*Zn=0{L}qCuet53T=(}Z z{im<>yq4>d@vjXIa1Hvo`@rBOUblapdj|#w_IzV->1Vz%GVqxPMh3?Zj9hyA*GDef z^No?$@p={rJEKfBfSEADWmrvgt=ZKC+q5zd7>bBby$bIPxK`Est?c969pg zi6g}aA3aiR=KAQ7BgK}-9xXmHd4%h+;=@0AwAg&~(Vma|^s%0g9-HhLdvu~_?8uRx z>n0!VxqkAoo~?Y29er$Y>rWpWyzXa{gV!B>ba2~GydE38;ir>>+qt*nXFnOa?&p&u zH+UWY$;kF!938p-@t=%*?AYYUO^+WP`P<`1$8P+^(XpF<@zb%JpEx#l^RJGL-SVrS zjs5*^j*tEF|4fbj>R+eEf6euq(^KRB`~OajKlT5n#-HT-DZZcL`@c?29XQQ3#ift< zeY$2BsXe2Wx~f*AUA0pCzd_$wMQWu*sV~h-=cWs}R;Cr@%G6hNrDmfqb@8;llW};- zRvO^xj+tB1Ha_R^tm0qpq#K#BwCrlOKHeF*k};dox^hEWS8e25e_EK9GJ3e&l$c+x zN~_CxX^44?Qmbs=bY|X`a$UM7-J8~>jf~x14l{FgxsC5j(r{W;4wu_lVLRWKie00< zqce8}_t!J`uC%}02t>y(NvoN^l5av~Eg)~NS6G>@W`(Ag+_<5$&Pv9Prn}RA{&k&U z=C3Z7mb1Yh?(naaEG<`-E7A~m=9OKgTpi%w_Vt}r`dDQHbnIu1EvzvQJZ6h=*)ChN zx*A{(_5+EzjUsh#VIHz*vf}3S74RQo#_YhZH3PX--Kl+6;oPzdIb4=nXSF|==d@B^ zxdkY|P-?0De8#74uo;-yN^^h-q}2O2j79^RV0y^n_s@BLZmd&5ZPu(;fa?hNpDWi{9~zdz z`QX6I3MduRZX$az&zZr8G`nmqX4TYgv{vOOO4*rx@xIVt{)ZtYE8Rju=QvMP8J z>i{oo(KQ$0q1(^p`8!z?Ni*}}0+`ASKXooo-j#6*|Af4!^n>w;s%bqM%BfkmC!4+(#tZM07D8 zwH*8YqUj2Hji9V*&v)EHqTD7TtG=%FEXMuUjzqG35KZ(Ctpb=sWCx6n2(9;P&1U?@ zS{AH@jH>~8iwE?t$R^4qMt`tq68jjxsX+RzCi>fGKTl~BpLxzGqT3QOgdN~QG@QlQ z&7seDq2?@j(AB}L8N6RrE@V!B+Da7dOD)HLBs4+GZ0ehW8*o=GR(^0=8K$Mph2Ya4 z{MiCkUCzBeV1>6@ZJxuZt{iJ;&aQyOwV6B%3Nx7>T>?y%7LyXGpd{;ZA#lJXi8fej@rr`?!d_bazMua9q=-z z;V%*b=Bjj)a;dh#yO0l=+ZH4$w5%)L&isTk;e5ozd0;g}PGpWWOg?mDNj3=QBQIiI zb0sWB40p!7vzS-)aj)4#67uW!DZ~DKr{xVrx?tVEBeUnJz33nLrXC-EdNZS*tMx8I zJ9ebyX+?7g{m9h?@ll`ku{>rDBglDYQYSREB6Nvt$aNwoXO%8i_zJo7m#7Nvjm-RR zD}*K$0`-8oQ>-_Z*K_p!K2M|N{&HXB@zf#su=Ql*RuxOqvXXqDqRv4-1JWWWd*c~= z^iGanm-eMytT9S0vys{B`PXV@Ra(}dlE6*^UwrkzjJA1mP4jTspB4jafBJRWoAy1DFIkOBK#44br_!^o>QqL05qxRRRFnpI%x3ItR$6(c!cq!lWb={Zb^+07 zu&x{pPFi8D=H6^$l3uZ?jq8b61wWwopk@QUe5PV7Tkiv_^EzJLh>`hel{c3$RwP8O z@J&tp`4Ap2*ALrSQI3VQ*0V~)hCbHcQf|V(4VMeE);v4M0{H_>=5G&!GYe@DJdrE-CJD)9^*pD`>yOKW2C0W&Ay zz)?Pzq8sFk<{N*;_?)+M*C-WUmZ+n|(DrkQFHw)^UA2I|Oi{!Bq1n59bynt@^QzS9 zHe(1(@!>u{n{Q zm8}^@@rX-h>urqfb!D@X7!6KFW~!>P1irGNFiU_=QzGV?0*SJ2%6Q zT&pu$dynkOydP}-u%kKBS?k)LForrY_zV3;Wh9xpsbm9s=84j^3Nr6=EO}W|o5;Ca zB~9`gT*~%x#^G%uhB9&>GQ7wr;9lfZ9qN}c^5RU*Wy}tq^ob*U2g22vj>{Oe*t?Mj z3gJTjLVMI^>9zaOXV&yOM!q~UJum{hM0O9h;kl9BYQ}BDYRyR)ouUrwkCo*I-sN0m z+-GjNAj?ZWFJ$~SG;2p_KwqxVs8W@r8HIV6{Ra9h@T>(k_yv53y1$Jz;fYn76$h)? zVH##NbC#9PNmTpX*A~n|?EGwl=kkIYwXTt}9_nqfS0&?c9?&ZAB|9O1itd5XtnH{Y zUGwVjFS$Fx-*y&)bF-mJuIVGGl&$mzVg~YAO+Hv_wR{e~-4uSV?mk%6S!JoU1V*bxft&J_BlNh;h1CP$ErictZe4jr(G0Hy4p(#0 z^Mbza7dcN~nLU&7P=61eJdb+wPwJIp-R;I|y-n!d05}=FYwdgqP~Ie$FNG`Opx^i3 z&=FSo3}z|aW8Nc}~`mbR-zq-Qiadq;Pzq1RAnXBgqv)RskH zA2}0IlvSW$NzO0$)|Xn=*VhDB)hVtuz&CrtGw8LExp8r8+SJJ1&A{yi%(~WDuI#F- z;rLDP<|f9%E&Q-J8nMl7%*5=j>TkNN%$MP(iE{ES^F&LLuXcO2Bl;qGT6%(E7v&}@ zlw7ynh*mbO;jyA#I$-oB=&BN#8!Mq1Z_JD))e)65cf&5KO{F!FQIVDBP0yt}Fn>nI zqK@EU@#YWkylm6?l)BmED`r*Oti;k1R|3&N^Oz6ZP4IbGTeVB^!Hm1! z7uZ?Kfic-)xdkb2EX`%bI`dEa>OPm59}cYf%bGW|*6u=Z&))ZNKyt2m@t5he`~9Ly zIU4=zPDpvd3k!D{n5RwyGu;<+Hq>2pvUs${d|ay$Qrqsx^%-yB86%|1l^tp3Pd*=i ziL-tg`*79`fphplJ$X)~~?mLoo4`?*(hqw<@B4)m39q_TP)s^razu)pBp z&8#{HeSM}n{e7rtsahjSy&HMvUM7}I%|aimdxU%V9X)VFL~K$|8#^QBNR`j#+sh}W z_u^O7Y-|C&39m#26&$T-7IBdQW={&yvGNB5MJpN`|A~?hoD)g~8i>xu6CNwnt}RVHMR3+=AQTTQUVu zVJ1)PMSemgrnl{IRLj$qcx5BxaM_o0YdpO$KS%y?sQEe`ZxZo~ScvkxY zV3qTyI%A04xXkN$wOj%VxX|kk2C(PArH$!}bVR(KrARpzL4FeBs`vV=CQr^aJ|9REw{iF6_z zqhGQgUmd-Yhzwis!g6LPU079VmeQ^xle}NnuwR>ZM*H|QdklQ|RG*ZHkkPoA(gGjr zj5?(6Zb5LOD{ZFFay(6?NBB4THQ+$MrzF~ePxO6^2x&*T93J(PCCx(<+?5T8{2jIm zZV|7s!Q3TU*od9xk{%{BtNG3_6ZgBk@anGdXwvDr8!k+xL+L1ZtgSmoTI#y$UNBfk zC+qHXSNSEmLk4n={07$6GG{|-znYMHw)zgalXp>rPnG+@;b9`14nDw{sD?rxSELW5 z+mXctouhH30S9vLOHSOwU+cl@uC%w@M}O+>^ac84?9_uB_@u>nAotFZ3jD{*fLEs) zm=)R@9uL~JCh}UW%&yVz;0ZSAp7w^cr|zWP1#J3gYKVCVt8WCWyDKrdi*H8y{r1e5 zu@-KvN2|dITtYv|U-HhrT7gD4=p&>)JNw#pts5bg4o|>987)WlUjl}3GDpl_^fC6g zH>WK#mCb(O9#50yG0qUU``_spTGz}Cer!Thv|}x4VbfhjD|2ga}_Aswm_ z@dR4vf$;20=H4XPA6dw`vxe*&LjQ?!3Jv;Yx|^|?U*g*zIy<+bCXgdqP}X0q#BLKD zh?*1Iej0qWGiolfo@%JJ3ALgh3yAI=n9Z)La$Y(I`0LVb=~y`dFRq!^9eJUw^o-ig zT==jRJ(U-$%8n0FL*+$9d1z)5enp5M_pUx>eS$OOw;n$RhepM`T?@s^QL^rIj(AV4n$ekJdwHmI})ylFRxJVvP9JH$t zoux%!v6;Ddqo4b^^iok11sxq)#6^uq582lV-+`3|T(J9csZM82>M!TA$HZ7-a34@3 zm#H}dHORyW6P?DgTP)~WNpGK;haK)kKZc@52N#jTfOe)=!aKE4#hQp0HZiXW?Z!d=ZZ(aV0hJzD-d)r#jTv8H?M-Ct+L}_#GBhd(9ZhFv zN-2}ibBT?1aSzhl%O&+zQ0J8MN+X;7=7k}1*U>{61*7WLf2Q^;p!|m@6NK1=#HYI1X`abTaJ88Hvscf|dQ*%h!mQpgd*-hdr)>_FHTW!Qas zChMWnTxTJ;gL!i`8Hjs)TG$di%`9L=4<9LyQh8-x`DHj3R*AgknajZ^cvbTnD{81| zHLdJtXDJVrTY!homUi<3$KVb;LT{urcMqaIovoh^x!@(Wdn0t@WRW>VPYD>VZEat} zdigZ0&=M>oDlvJMW4+etH5-f<;7PVM+(nhc_+u#VTxQT48`|1p>M4wb;p`hkt+ zeQi0)PQUw2dFMfxMlSmq!pa^c`z7<7yf3s)*=wq!g)8^WHTU0jg2+(oan zLum~?=97~LMbTf=%WAo%o|iN5pfP8k@)et@tL3>^W!5nMEzLf)>2>A)CY}iUGe6Oq zI(i7iOMnw+BeX0}-C4_<>A6z&cAk`5MPx9S*h+j50(27{m7CV1zcoYqRr+Ol0?QwroIKCysyP-pDad7{$Y)D7sa_*=(LXUS<>)2wkLS?xV=gPssn z+zbE7F4JhG)LlK(9??}ET|zBgg)am@`dJbGjHH1Zr$#rTWm2s#l7~jxb>U0akvs1T z3w0OrF0-^a;gI)7sWRYwbnJm)&6_)cZS@8%bqC{|&^Dr1X*SF};;FEu@jiIycfGn? zHCqm>LeBUga22C`wv}okT`9bm2TkE?=WzB9$kwHB-#Qk(0tz)zSep{lI_^!^@Qr=t z&IKh>uZ=jyJa<(i*TLWhF8k7Z$?T!e9R^NXL7R}!QhM4!A2gO3BJ-PYUdu2(Lxp?D z#5eBLJ{GBUdaFboxRbHni09i^{t^%IC8QFkZR)dVLaM1_v=B2?=Ps<_2DP=E{hqOTE&4egzCo>3jybLywU!XqJ3d{C$;CZW0N(kGFno1~hIn#;J#b)VE@yAl z`L0J7tFERLnT5#fTsQ3szqC8}Ew}Y&=3bGfV^2l_`%t--52-h5se!{L^fmUpUU~+L z`M-t7G!9t4eEaq=2UiJ%@^# z4}GcSo|C5ufpSLIGB@ajM<<+__=rxnV#P`qK49+)9l{5JhdvP*;3IOUzGGWa_rQh7 zQbUdl19x%AIYZ0ylAb+1Bl(~i_~@bDBLUvlOJOVM2skT8 zDN+(bF@v`665b51tC?r6sAQ2ong$m7omqTx0s{;jMWhV8m&*(H>EcYVI$#AM9Q7}9 zu*f>)2{u?(*!C=*%ZbcwB#wtevuk{edFr7Zh0KrsraRYW*XH(S|5ji6ofSY~Z?oW( zEoYX)1F&}IJc0bc4^B&?gV7mzxqFz8;~IjYs#h+=^|A zm+~Vz60vf0%F>gDRZy}aY+En06YTba7hc*OPjV}9suCvrOk}17@zlANdbIqu!FP@H)AEV+3A(raa6wg6CSPOz1KXk#BI; zqRduh)oh$Oksc@F*fqd!$_dVF_#VdmGW;d>k?T)SVJQ>62iC+%G_}$UIv_?~r(B|w zLOY@B;cGbHKZZrTTt4ZKayAD9eU;=DAsX2RP*#ifRE2 zausI|2tIV7?SNDJ;}_23)j)1>x_bn!=MAcb2K~ z(83Pv+2&=e(+FNeZJZp?i-k1AGVG(KJjOEKzG3JDE1l_mn1Sb=itPKs$KuWL63Qo} zB2-TK>nXK0D9C#nx$6@e5dJbl@KlEpXu2j2Z7?6m-gqJOs9)5_K#Bb-IABM?U391s zQdCjB^*O&93$&~wS?fV*xru+LIot4Qt|PNeDlNL)=(G`pdMG#o1ou^_KioU1VTSHF zU&{`h+=1b5ZA^ZfA#kY!?A~c{{57*Zz)F{SR>7y7Iw5Q5&Ym+?b_U(+iWBh--^$?G z>zQ#mvhsVq-@rVy2E4r9&hx9nx|rcOJaN8P^|D*m>ptP{!;a}QH2k(4uR_+ox27hl zHSa*qS4O-m)#HvA!(Zd!L$B0|D{@5CcbI8lVLZ(mT*qGZ^lgkp=en6!pS>jdl2e8k z3X8`U&1769PT+9+vkncjg_5Tn}nx+D_Zqmu6`CDQAx|KXN>BSNst^qm$t{r^%(_O|^-!Tm0TzK zL4k)^-kF@!Y}6IDk!R-GeM5RVaO@a}spkegkDX&&-VN#XtR$VBzz}$^FprGhUSzOl zNZvClxl-hm`AI+yc;?yBkqzpRv}<~vGe-z2`M$W=J;K{bL*DN&2h14(Sviu)D@GM# z<_1jek>|UQ=l-TO8=cv5A@S7M*%|~+be9*!zH1#3OQK?FUY!EY5YaU9VDc*EDHR(^ z>7wtDVKnvwL-;;NW6jnhn~8V_$MJ~z*nstSkpaXRpUS`P(yHmz_WJ_E;6Zx&2S~cX(X@C18C(_Z z$wek&JRT;$)VHXWy>?HtjFWpuR=ktx2TeMY>|P#fxc}__@2zp&!e58Eo(Rq=2YGL` zD#hlI@Taqn6V=MoV5491Lb`?CPVwz* zr|+c)tEs>xYb+fZ`y=k?Ascrx?m;R*h@Q6;>%r8P+`aGAHqo7C4@0QS$?x_yl<7Wd zft!K)ZJ?eipG>D`OZiUvnro%2e7uxC_A%K*ur8*@W3~@h(K+}pWI3zd)E#i%`!s8^ zCrLlP=F&K89*4Gr=~Q}>>vVOnak@OnokQmTKsf>RN8vuOfv1e+vGt0zc>JxsRZ^Ed zl9^28jJfk(@0oaBPHS}DuC%j!Bppaoa1%&$whz&FK9nAwHCZ8NxH<`So-q(oh9)fZ zoQMD{Di!^3oh>Oh_DJ9$8M`om0M<}do`SN6!2V|N|3o#>*jY_B9x0{wVW8~E_&*Io zhdQN%1>-N=Z^*JjOEN{VD`M6+3@_;_Y9T;FKe!GA=XSC;;H9i~s=Ltj0QzzWZto5Y zl@s@3tr2TNUj5BZ%_1ayXY8)W-bj|4dU2FBGNgm7dlb6RxzP1&X@37C9R2|~J;K<7 zjD89}Ka9n?FT#ET7NPv45sOr7@C3ho?js=6B~)X4i^*LGq!bh$M0RT8INI?^F5wGN zD}hSjsY~)=0&8%l^_>6mz7V>SYcnl^=!tgL?`0QuC9)N0dqSI1JEQ{}v>t_{vCi&* zrX1CvDI~0R?hgA?x3CK^z8gN{1+8JwNAqf|++BcrcV-2eatFHiF!W3YoC&lhJRK{H zrV*j=?2wui^c+M_ccv2+TEsJa5aZCd=;_z_k-Es4`X)YY19pu6V61pP&=AT_FjKF0 zg-t1;Thez!`&kyO(4SS3TL2CSsOcm?{r} z5jw|}gG3o;`wgvC_Bpi(C=5IPD(&WMCoF5)EUl9`h;m{Hf5t$%c2m#>JdZ z;}ZDRYd;Lv#>1BlO|P6Q!A|dDDH`Jm8TZQe385**crN@1It0{x>0PW5km2uCZDVpm zKejwoVjO+NTv2AN*MhD7=SCirdvC{~)ChJM4ullddt%LZ%0s|W?{~r(Ae2h;R6}SF zp<^e&Z3B2Vb6t*>c-GMxUKnv=D`3HWJPk3p+S$MpV?*C5r+}fRNZUln@?iO3IaL86 zG0s~0{6bD?Pxv8rWA#1X=F($iS=@|H>i-U+ z1v}H(T@1chtZ~;O}lAJkH4X;-k%h=Xma=qXX5d#xSQ&%>$Q&Zb`#lG<7do#BQ+t z!3?}xSe+L230Bqnkz)v#mCbft8UA=0AUctp;=rTQ&thG(DZ72>R|!Svnop^(l_G%U+Z-gWwncLT@%bo_b*kd(Rdk4`2sw<8rC92hn5>`_`poA&U4zcOtiv~bNItv4sfIWcWJgR| z?JAmCDSfaG4~4|TuV-&0)yLr^o~jBDh__Vd@Lpi{akC!z3sk9vtL{LCv^;;SCE3NI zY9k(7y;w2%+W`6<;BvBIvOLNP=7kTW@38K!#wb2keZx+(M=0^5z(u-hPH^}M;KdHB zdyc}xlKMDTlWR^noKNWbwkTCMl_9HoBg7b=BKB_qR`VB zMmS+@u|N998CIvK&l@A~RaktoyF+I>cJrmqedFM&8WTF=NdbRh$UQ_M?naA@hR5N& z-s`)-{LW0N`z7Nxuxe9y#sGHfJS*g`i8;4B`m=y#Ee2I&Wa_=LzXlC@Ir{;5KXp%F zFjDS;pFf9+pQFi0xjM>s_bR@C#CGrwSG=J{1jEPjMw^`vItNf=_fhKH?Ze8jAhJUA zOfO|bS)98Ovo@YCWf4b$4K8hYo6?=y=92F?eDgt*{Hi`q@jkt216% zZJ6a;#2ZWTt5mFG?!UO|=RBnK#O8`xl{nj{ZDk&@Hg96f8Gls)oVUkLRj!Tf4rkmr z_mJz`@*-exW`JrhR~ANd=fUZ^V`<)I4B(;*&{ogLf~0|SCUF`c+nvD~o8U#{@r}sr zfkoATB+L&v?*=3(5JEnS6ZLStR$rWp1y6qqf)j~w;%qKXw^@7br4F+5lXIoIUl233 zY2~E14Jwfpdn8y|rl+%m?|!io%pNoNfHzh*?frYg_MVuLd&qsnzwW2^Q{Vxb=h>Ii zx6Z7SJcVnmZ{Ijh`bGyL?VZoi+3DygpTphj;5kXc~8Exhb9C&G%JZ9oW9~>kI24v*=;xO zGIol2JI#G8&mJ6+5 z`wISQM-{)~O}K?J@98 zEg6a@Xc(G_4dmbBa95Gea@l$dBj|MdE-k%gyliv`=_Kd=aO^;{Qs`gR_RVrTJLlL#_-N*t!+Gtm$h`3L_%=MTHfF99 zZ%j4XS9E4twdB5Nj)o_wfUKWKh<_)@*Tz}VYE2(!*HUkb6;>^+^!2%RHapJoVU$_B zs2=hzuBX<_wI--~%*>2QbRn^2b1bY^e{qFW`}=-&h(@iD%mBmE+ zq#n$5=w3ICvskkpYEGc)OZc&vumJ_ z8;-bOBsG8fE_1$3HbM?rMjtM+j*LU@i*JDPtr78Kf6bN1BYUY!2JlKV;MPn&tvKx~ z+La&|>tfTT-LA@ln};9*vz&uU+q*YuZN>fKm6!8unNrvg4iR%8YH zKtrxkd9bG0CwUr@^SzKS!!b|$<{e^AThuEqg6ChN^S1}Q%v_~7>fOlc4j_R?kU*|y zPO++et;3AX)w_C?Yiw~fLJ*nimF;EzhAfF}rktRo@SqhhtDdBCb(IImhi;CFRfwIn zt@1~xvefy^0k!Q62U^hoRnF2q0cI0O)ZEjG^dxJZR!2%|dR9M0rRp8~w04R*{jS`1 zwfob-vxRIfrrMALP+yuspG3wdS=ANPJaFgRsisdlcg?V^j`!LFMFY%CloysE2Z;Zj z9+qoQu!jBAlT_DKw^XHI2$ZOFS)JZ4W6V9xbv&DGtm7P_ai0B{PJe0-W8l`Tsq!Rv zD(92r!%F)UI&~~0qh{w!0V)TXe`&@=4K#yBi_^)oV+B@$QdW}E2f$z#*5@hH_^p!E zcJ{IY$u0)3<6@njJbS8nlB&j8PubBy<90%!9i4kQ>uv54zlv4Q)w1o090&4);t9Ri?eUX+_Ryy z3Dz;&O4jOO9d^~1ITVn)@L%~ign(Pw!?r6Bu<6~OnXk>|o{bfIaJZhY|2J#&yrX+Q zRu6a-u&2*Nzpk@JwlVGhHZoHBKJvPzK7O;oIDwb9C+nG_TUiATmM62eme7IJ?YkV} zeYy=&F|8wA@E}JJJqtN+WnC8aC3s+u>bv%$?8}og_&YeN&fg&}^-68*FVjOc0zUUt zo}soXRVcM zEbTM<{s}yO?o~g+O#7tJ7#zlB+PmMAS3uYdKkGawQkPR1DXxL$yLWWmNU9{&C{>=ke%YH=OU~*|=E44DgE18l4V-*uXd5kZ_%K#D=|GcB;mym;EK^yHb8D zFV^t)n_ll4dQjx)4m{Q3D&C^;-Oc6IhV(zmuRKVntSpTP)D5F{^sf2sy{IKUtHPZwGHyoB zLI$2ip(e2I%)glGDX3oWUR~s0Fn*?L{Lfi6{zv4#QoSu!1^K=CjjdW<>nbQ@zVhKZ|^6^)i@w8@Ou|HFOpYOLbQ$MyG8w$&@&ZJXfpJj7o%XG7eD-V{3 zW*CES$Ex*|xu$K|gNfQW>I-~_Z!YA$D^D`$>8+-oikL!eh;MMWf;d{wbeD0IEZ;oD zeM9m$^Ihk089`m;P+br1g&QkUm)0Hi_J!c`>2%ooAZ8t)UwnWW>_^k3etNgKe$P2;lxJ(^_H6i*}T1T*zSytho# zCr(crcNY0QTscLCXg0Wf8h0yLvIzJCjp9ZwpY#7c|3(fwkfHdwS4<_D(b^vo5&wVxnvyMg9DxjC>_7y2C)trf0Sw|PtSf~V)lyZmMZWN+@9$^UG5*`Cab zt*|GdP+vjamQVbK7WeVn#m0Ys+e!?qDZ*aBAzSfOaQ$?7h^NXYdL6Y5=XY3h2A$_e zZvuGk3`gH2vgl4tqix~|^&VKWonPOkk$!(0#C$T>Gw3|CUB&(L?;>3@B6EuP#eO`T zv7cTPS*!2P>_(gI)<`At<=b)YScB1Z^8W#V-*vndl>0AsQ4Zlx^`?18=QtXmFWt=P z(Wc%PU(OqbZ!7zkBo_iBivueI}FnT{`fmoQ3wcH_T90 z@Tu*vFGp5P7Tx~Sj&4Ra9oaSSe(>CCmh(jF=NaAQB=Wo}-pYxLBUY)k+xgV==>LZD zVl2cw!wMwt7PWuNPL}hGYNNV?81rdt@wV_y{X|XmzvWo6ANGdn0Oog9Z%@an!{ztz z+~12l#uF{)m@B1DuA~K8FSzz?={P9i zA_H^GmFg~6itc^*9IH*<)52$%gW#o!gE@X2g95vjOY>;5D*2qdP3xdAMiYHIk!d~H z;G4|XVys;)J5%)U7@IpE(&`C>A)W;eG16VKVQ^RrU3sJ(AEV+rJmhb$nY(L7cDgfk zQyeL6UxZ6f$FvK&JjEAXOCxCD`F*%{aq7~4);P|?|66bJ{15V&?=gm4!!f}jPpQ`H zkntSGr(Qn&w3`|>cQ?PAPW`*{yF=?XGAr`1X*?pIsbMHLbz(HjV0z3t@tBrXjx9s* z1+qU!d{pukion5=ziBOy&Cx>rNTMe3B zdg|EoU%SY-Jx9aYBswB19G4=-5Qoq@eu?c|c#+52&G9TrR25brdVn~IKaczF<-|Lu i`Ua|EW8?}WU~0XUJ8$PLBP*WY;c4 diff --git a/tests/sdl-client/bmp/shexpl.bmp b/tests/sdl-client/bmp/shexpl.bmp deleted file mode 100755 index 72a62c41fcfaa119067fe52360690784255384ce..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9832 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 diff --git a/tests/sdl-client/conf.h b/tests/sdl-client/conf.h deleted file mode 100644 index e0a91c9..0000000 --- a/tests/sdl-client/conf.h +++ /dev/null @@ -1,13 +0,0 @@ -/* $Id: conf.h,v 1.6 2006/05/19 11:16:42 mmondor Exp $ */ - -/* - * Copyright (c) 2006, Matthew Mondor - * ALL RIGHTS RESERVED. - */ - -/* - * Various hardcoded configuration parameters. - */ - -#define SERVER_HOST "hal.xisop" -#define SERVER_PORT 7777 diff --git a/tests/sdl-client/debug.c b/tests/sdl-client/debug.c deleted file mode 100644 index 4dd5bae..0000000 --- a/tests/sdl-client/debug.c +++ /dev/null @@ -1,42 +0,0 @@ -/* $Id: debug.c,v 1.2 2006/05/19 09:16:14 mmondor Exp $ */ - -/* - * Copyright (c) 2006, Matthew Mondor - * ALL RIGHTS RESERVED. - */ - - - -#include -#include -#include - - - -#ifndef NDEBUG -void -debug(const char *file, const char *func, int line, const char *fmt, ...) -{ - va_list lst; - char buf[1024]; - - va_start(lst, fmt); - vsnprintf(buf, 1023, fmt, lst); - va_end(lst); - (void) fprintf(stderr, "%s:%s():%d - %s\n", file, func, line, buf); -} - -void -debug2(const char *file, const char *func, int line, const char *fmt, ...) -{ - va_list lst; - char buf[1024]; - - va_start(lst, fmt); - vsnprintf(buf, 1023, fmt, lst); - va_end(lst); - (void) fprintf(stderr, "%s:%s():%d - %s\n", file, func, line, buf); - - exit(EXIT_FAILURE); -} -#endif diff --git a/tests/sdl-client/debug.h b/tests/sdl-client/debug.h deleted file mode 100644 index 30827c0..0000000 --- a/tests/sdl-client/debug.h +++ /dev/null @@ -1,44 +0,0 @@ -/* $Id: debug.h,v 1.2 2006/05/19 09:36:57 mmondor Exp $ */ - -/* - * Copyright (c) 2006, Matthew Mondor - * ALL RIGHTS RESERVED. - */ - - - -#ifndef DEBUG_H -#define DEBUG_H - - - -#ifndef NDEBUG - -/* - * Macro similar to assert(3) but which does not exit the application. Will - * instead log the condition unless DEBUG_ASSERT_ABORT is set. - * Moreover, the aborting one actually simply calls exit(2) after logging the - * error instead of generating a SIGABRT signal. - */ -#ifdef ASSERT_ABORT -#define ASSERT(c) if (!(c)) \ - debug2(__FILE__, __func__, __LINE__, "ASSERT(" #c ") == %d", c); -#else -#define ASSERT(c) if (!(c)) \ - debug(__FILE__, __func__, __LINE__, "ASSERT(" #c ") == %d", c); -#endif - -#else - -#define ASSERT(c) ; - -#endif - - - -void debug(const char *, const char *, int, const char *, ...); -void debug2(const char *, const char *, int, const char *, ...); - - - -#endif diff --git a/tests/sdl-client/decode.c b/tests/sdl-client/decode.c deleted file mode 100644 index f0b0bc4..0000000 --- a/tests/sdl-client/decode.c +++ /dev/null @@ -1,38 +0,0 @@ -/* $Id: decode.c,v 1.2 2006/05/10 01:28:01 mmondor Exp $ */ - -/* - * Copyright (c) 2006, Matthew Mondor - * ALL RIGHTS RESERVED. - */ - -/* - * Function to easily decode data processed by the encode command. - */ - - - -#include -#include - -#include - - - -void -decode(void **ndata, size_t *nsize, void *data, size_t size) -{ - uint8_t *ptr, *tptr, *key; - - ptr = (uint8_t *)data; - *ndata = &ptr[4]; - *nsize = size - 4; - - for (key = ptr, ptr = &ptr[4], tptr = &ptr[*nsize]; - ptr < tptr; ptr++) { - *ptr ^= key[0] ^ key[1] ^ key[2] ^ key[3]; - key[0]--; - key[1]++; - key[2] -= 3; - key[3] += 3; - } -} diff --git a/tests/sdl-client/decode.h b/tests/sdl-client/decode.h deleted file mode 100644 index ab5f3b6..0000000 --- a/tests/sdl-client/decode.h +++ /dev/null @@ -1,23 +0,0 @@ -/* $Id: decode.h,v 1.1 2006/05/10 00:48:40 mmondor Exp $ */ - -/* - * Copyright (c) 2006, Matthew Mondor - * ALL RIGHTS RESERVED. - */ - -/* - * Function to easily decode data processed by the encode command. - */ - - - -#ifndef DECODE_H -#define DECODE_H - - - -void decode(void **, size_t *, void *, size_t); - - - -#endif diff --git a/tests/sdl-client/dlist.h b/tests/sdl-client/dlist.h deleted file mode 100644 index a3bc766..0000000 --- a/tests/sdl-client/dlist.h +++ /dev/null @@ -1,174 +0,0 @@ -/* $Id: dlist.h,v 1.5 2006/04/27 10:59:19 mmondor Exp $ */ - -/* - * Copyright (C) 2001-2006, Matthew Mondor - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Matthew Mondor. - * 4. The name of Matthew Mondor may not be used to endorse or promote - * products derived from this software without specific prior written - * permission. - * 5. Redistribution of source code may not be released under the terms of - * any GNU Public License derivate. - * - * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - - - -#ifndef DLIST_H -#define DLIST_H - - - -typedef struct list list_t; -typedef struct node node_t; - - - -struct node { - node_t *prev, *next; -}; - -struct list { - node_t *top, *bottom; - int nodes; -}; - - - -/* Some macros to optimize operations on doubly linked lists */ -#define DLIST_INITIALIZER {NULL, NULL, 0} - -#define DLIST_INIT(lst) do { \ - (lst)->top = (lst)->bottom = NULL; \ - (lst)->nodes = 0; \ -} while (/* CONSTCOND */0) - -#define DLIST_UNLINK(lst, nod) do { \ - register node_t *prev = (nod)->prev, *next = (nod)->next; \ - \ - if (prev != NULL) \ - prev->next = next; \ - else \ - (lst)->top = next; \ - if (next != NULL) \ - next->prev = prev; \ - else \ - (lst)->bottom = prev; \ - (lst)->nodes--; \ -} while (/* CONSTCOND */0) - -#define DLIST_APPEND(lst, nod) do { \ - register node_t *tmp = (lst)->bottom; \ - \ - if (tmp != NULL) { \ - tmp->next = (nod); \ - (nod)->prev = tmp; \ - (nod)->next = NULL; \ - (lst)->bottom = (nod); \ - } else { \ - (lst)->bottom = (lst)->top = (nod); \ - (nod)->next = (nod)->prev = NULL; \ - } \ - (lst)->nodes++; \ -} while (/* CONSTCOND */0) - -#define DLIST_INSERT(lst, nod) do { \ - register node_t *tmp = (lst)->top; \ - \ - if (tmp != NULL) { \ - tmp->prev = (nod); \ - (nod)->prev = NULL; \ - (nod)->next = tmp; \ - (lst)->top = (nod); \ - } else { \ - (lst)->top = (lst)->bottom = (nod); \ - (nod)->next = (nod)->prev = NULL; \ - } \ - (lst)->nodes++; \ -} while (/* CONSTCOND */0) - -#define DLIST_INSERTAT(lst, atnode, nod) do { \ - register node_t *prev = (atnode)->prev, *next = (atnode); \ - \ - (nod)->next = next; \ - next->prev = (nod); \ - if (prev != NULL) { \ - prev->next = (nod); \ - (nod)->prev = prev; \ - } else { \ - (lst)->top = (nod); \ - (nod)->prev = NULL; \ - } \ - (lst)->nodes++; \ -} while (/* CONSTCOND */0) - -#define DLIST_SWAP(dst, src, nod, ins) do { \ - register node_t *prev = (nod)->prev, *next = (nod)->next; \ - \ - if (prev != NULL) \ - prev->next = next; \ - else \ - (src)->top = next; \ - if (next != NULL) \ - next->prev = prev; \ - else \ - (src)->bottom = prev; \ - (src)->nodes--; \ - if ((ins)) { \ - if ((prev = (dst)->top) != NULL) { \ - prev->prev = (nod); \ - (nod)->prev = NULL; \ - (nod)->next = prev; \ - (dst)->top = (nod); \ - } else { \ - (dst)->top = (dst)->bottom = (nod); \ - (nod)->next = (nod)->prev = NULL; \ - } \ - } else { \ - if ((prev = (dst)->bottom) != NULL) { \ - prev->next = (nod); \ - (nod)->prev = prev; \ - (nod)->next = NULL; \ - (dst)->bottom = (nod); \ - } else { \ - (dst)->bottom = (dst)->top = (nod); \ - (nod)->next = (nod)->prev = NULL; \ - } \ - } \ - (dst)->nodes++; \ -} while (/* CONSTCOND */0) - -#define DLIST_TOP(lst) ((void *)((list_t *)(lst))->top) -#define DLIST_BOTTOM(lst) ((void *)((list_t *)(lst))->bottom) -#define DLIST_NEXT(var) ((void *)((node_t *)(var))->next) -#define DLIST_PREV(var) ((void *)((node_t *)(var))->prev) - -#define DLIST_FOREACH(lst, var) \ - for ((var) = DLIST_TOP((lst)); (var) != NULL; (var) = DLIST_NEXT((var))) - -#define DLIST_NODES(lst) (((list_t *)(lst))->nodes) - - - -#endif diff --git a/tests/sdl-client/encode.c b/tests/sdl-client/encode.c deleted file mode 100644 index 66701da..0000000 --- a/tests/sdl-client/encode.c +++ /dev/null @@ -1,107 +0,0 @@ -/* $Id: encode.c,v 1.4 2006/05/19 03:35:43 mmondor Exp $ */ - -/* - * Copyright (c) 2006, Matthew Mondor - * ALL RIGHT RESERVED. - */ - -/* - * Very simple encoding algorithm. Given a 32-bit key, - * it will encode the supplied file using XOR encoding and changing - * the integer values. - * For even more simplicity, the key is randomly generated and stored as - * part of the output file as the first four bytes :) - * This really shouldn't be considered encryption, it merely is simple - * encoding for computer-illiterates to not too easily rip our images - * and sound samples. - */ - - - -#include -#include -#include - - - -#define BUF_SIZE 65536 - - - -int main(int, char **); -void encode(uint8_t *, size_t, uint8_t *key); - - - -int -main(int argc, char **argv) -{ - FILE *ifh, *ofh; - uint8_t key[4], *buf; - size_t s; - - if (argc != 4) { - (void) fprintf(stderr, - "Usage: encode \n"); - exit(EXIT_FAILURE); - } - - if ((buf = malloc(BUF_SIZE)) == NULL) { - (void) fprintf(stderr, - "Could not allocate %d bytes\n", BUF_SIZE); - exit(EXIT_FAILURE); - } - - if ((ifh = fopen(argv[1], "r")) == NULL) { - (void) fprintf(stderr, - "Cannot open '%s' for reading\n", argv[1]); - exit(EXIT_FAILURE); - } - - if ((ofh = fopen(argv[2], "w")) == NULL) { - (void) fprintf(stderr, - "Cannot open '%s' for writing\n", argv[2]); - exit(EXIT_FAILURE); - } - - /* Generate random key and write it to output file */ - srand((unsigned int)strtoul(argv[3], NULL, 10)); - key[0] = rand() & 0xff; - key[1] = rand() & 0xff; - key[2] = rand() & 0xff; - key[3] = rand() & 0xff; - if (fwrite(key, 1, 4, ofh) != 4) { - (void) fprintf(stderr, - "Error writing key to '%s'\n", argv[2]); - exit(EXIT_FAILURE); - } - - while ((s = fread(buf, 1, BUF_SIZE, ifh)) > 0) { - encode(buf, s, key); - if (fwrite(buf, 1, s, ofh) != s) { - (void) fprintf(stderr, - "Error writing to '%s'\n", argv[2]); - exit(EXIT_FAILURE); - } - } - - (void) fclose(ofh); - (void) fclose(ifh); - free(buf); - - exit(EXIT_SUCCESS); -} - -void -encode(uint8_t *data, size_t size, uint8_t *key) -{ - uint8_t *tdata; - - for (tdata = data + size; data < tdata; data++) { - *data ^= key[0] ^ key[1] ^ key[2] ^ key[3]; - key[0]--; - key[1]++; - key[2] -= 3; - key[3] += 3; - } -} diff --git a/tests/sdl-client/fnt/10x20.fnt b/tests/sdl-client/fnt/10x20.fnt deleted file mode 100644 index 99b34864883761ad0f44e1733af72d539ade0f91..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10240 zcmeHNPj6f`5T7MgLPeUDfJ35fH|YV+TdlMNHDpr>p@*7OBmf z&%>wKkHMu9AETVg{5(4u+s`jfk+{WH_W94xW6yXz<2NrvNauVY86`ZubROE!9;bC0 z(v%u*cq*m!xQl)`_>-^mhe1anUo4+Q+q_DaW1=rGHjSa6n-?T=8Qakk*`3&WHf6*RGg*o09>AfwEE>X0$cOCJr zc%uI@qAi>2%CEd1ZF<*TRawq$dgT_AGM+2ELA&&~{B1}XEpNjwN^vrTqR~^vp{HS- zC(mfh(+aJK9nf>s@MbuzIi?>@H_pdJI3=6(lAbKe_6lP+Le8nYYu!(*m-Hm3l$5^@ z)5c=$4kJ$&1F=#A;dw&Hd0H2Hd&(z-JS81-D%Ll(e;D;SpYj)n(6 zQjYyMUn?XAN;><7NPQGMV>fOEXPkOren>q{J?UE$pF0q`hmxDX6ZlF~!?Q5YOnltA zP(z+`n_gI7KmzAH=kqp!LS25wug;~scg`DllwQ6UsNXNF&$+^`T8hioX3w|}_OK!(Jr)Qezxlivc`mtG>cOehHK+9E{7mv6lA$fflEr@nWQueBGdg&-?LoZ1*i~O?jUHwn z%U!?6F*2vuB^TGfk?b|>U$3~nu6@hTQOu>6+x%Le-V=s0H{LMX=hATV%l*Lapf%=J z^*}ua=ztn(HJoW*dtS4<@Y3ql%l>03cBqu6>GGro6mABX3sj-okah~3hW1$RwVcya zJCsWyrFC_!S;YNnOXHSWd7B={>Zf5Z=Dg1-es-zBv41-kuh^A9&qlTFu6k1Z-V?uN zS^*E%MNc(b?OdB)`}S=2wk~@6IN^l6FT72IZq4Nes>xQae&5#D)<2+C+xEiKG%&YA zz^Qhu3D0qUz{uv8d|r5(6NwS1=gBoAoy}&&oe?vY(vQ-ozEFQl7_`OvTjp0s#+I;c z51e{T5Bg#pIzmgi9+-@B+?l))#3wZGw>owd`wBX~uhxsFYKXmUp|-{S7?6}>9k@zQ zfpg21+fyg0^mH%q4%wB|yI_Y^;@vo3q22^beu-!*^6>lBlv8?Z?7H+K(&)7e=cv4t z(o1PL_C*Q{Z+XXA+fDUG0yh%4k-&`v{yz!8BBzmiv(fpSXb&DJc<{O0#`T7um%Yi`0FOd>G!nwfKIs2>E6iZ3*X8kW~*t>$m@y3qv(hjMermz8nw_bYNwZQozu z!LOUtFF)n)_*?mVx?F8KdYkTx-__eS?$+J+G@@G)KOM-s@d^DUe-nvO-Qim@rkwEA z3U{1xZ`^8k+qf+*J0DN^dwx>j-trVA+Tk1w@ubzz$LIK%G<|2ur2exqA-n?9mX=o^XswWEJA?gRRm zclZ&#NBM1%=vVo^HsaecJucCMo|wHt>{0K$>G{=2qUoR_Jci|r=qisNr>((;-@7- z^Haz_Gx9SYi|s$SM4DVo8vZQH#rv(4;!G~q>(3OY{CcmKQk?Av`Z301qH4u18IgiU zG^TPFv22mZ7=^FCgMb;!e_l+4rlQAE&XY+8aS*$CwBvfT^r{jUsNhUGHJoWp4M(zi z+_A34L90}%SL2-fhxE1h-+uV2c*37~M^?(?lc`_DjzEI;#Yt_F$LXAyc@fqBZk)-l zhBLX;aJIfQ5*XWOU88AX4QK1C;cR_Z)QU~2yqY3E!yUGiIq uqnli6IFm~aXZ+T1rhhrE7p$UvACDut`*PyVuI(=^WBmUGZTlO?JO2gelED=K diff --git a/tests/sdl-client/fnt/5x7.fnt b/tests/sdl-client/fnt/5x7.fnt deleted file mode 100644 index 361216556ee69d3fac1610c06a01b05ba008080d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1792 zcmd^Ephu{zd7aBHn2!;(Sgn|Pr1jB`f4fngQnmyC!ybPXt zrmiK+lJ#eM+QXh^(O(|znb)t6?lIb^uf9bqb>xz*)*0bqjt0f};?IuWd;GB8f9DVo z#-UBK!4miYpbwS+CnS8Q*E|4BEC$Qkz;ra>Z3D8n_@s zv2f0bSs9x#SM*%V+EBasW@P7YVpI10Goz`j?s zTAQueXxJQZwPK95IQ&t&3##_^=C<^kQD)sUKpbfOIC`Vf(6lx&*{q%CzBhT$1!cd= zN^55c;y6%RitH_8(WEG2`Yyl@Tqzh+a+CrkYnm&`ZdO-#u3I>P5#vphVDF{3T|GDy zk&!%tMw@hr+Yhpvse6L+R;n+}rUb3{6k(3FcEyKCLbE=(63xm@0q$2WZd6&J651nL zxMLfZEiv=UU8gZS(cVpRccns|3*d^HgFg2uq0q)nyU& z8`aN~0>YPaNu@x+9FC{plC{r9dooR%2Vf6zJC1b1aoNP6(=?Or`A=Y^*WLx*_tAGW zHJj<@P~Nlo57r#{_iVm%BXjFvd;2+*cvtVfgC3OC(5o?h-_LWH6z|#m0TP$**<{4V zn(*FQr)k&V@yu%eVqTE@1q=zoQWj8&y(@VeL!wNsp-c->U(l~fRhCIJwONoCU`Jfq xxHfm`gFE4UacD}oBGlfcqIioTo&3u?@$n96HhItm#TMiXB-CsYAQ4Ik_ZLi~-Ut8y diff --git a/tests/sdl-client/fnt/5x8.fnt b/tests/sdl-client/fnt/5x8.fnt deleted file mode 100644 index 01add720b2f68bf43371056c40dcc43d7fb2542d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2048 zcmeH|EuJGe48?T>A3-p%U_--(K7!#u!-0kk4F?()EV!`Yz`%h80}BT3|HuerI`-s^VTZoT*O%{MnMjuFGLYBui2 zaQC-4XMRg5@qWg%*~z=}{w8F+i3gt&^!{x;zsVt@`elF0j`5`zeintlq&&M7ej~38+$f#lD%ZF~Q*1-QMzr8CCQpm*bk(aQ?{Gs_U z!>*CO@)ynwKR@S$M>*`qg?r?2zm5YWUgWK4%O6LmCdK2c`Rwu^eqC{ven7;PNAa~v z|Kp<&IqH&?XVbnk#n+0FIg~mVUhH4vq8?vjqk_^W{3&kBl!bj-JQ*FIRrdK*-IbvV z9{K~`YOFtDk=RhGha2@B1!ddY%80!MHRIPjkT>{2#-IHKfJw^h~uIa*CBN+HO1a*_o1_% z8XTLm+t0`2$L$t`$5Ri_Y9F$byO-;!t?s?gdg(aMyFB;Y)lg8x2we>0-g{oCc5-n( z%Un3@zr0bMYaTki>JK@)8pDONg=mL7)Ue<)%Ti{<4)n6H*g|XXkN5XRJpF@=$Vc}= z`3RMdQ27Xzk5Ge-OdXHoem}8;hWETsT1$Un@>hR|>)n+f+7CwLh>ojBpTS3B%?zUtcxk**iMEBeh{wV!SO zd?ut!&%XP+>pQpK#|ia($it+a*ZaHgXCN%m{llgX zRb0+5r$cYWIG>;HtRZ@0+8#5yy2c9i}O-tFq#+D!j8nC4Jq+Y z(dE^;5eUhN{;Nn?k$^Z$E$A_pW<)ViNazKvRY;dINBSevEiuitXM3$`haS^tkYQ1_ zq3dF0uP8RVi`pSuIH8w-Y(?n#nZ1`spp>t-Yo-={vB)c>U!|T%X(^X_7J{1Pni8T# z*dgtdNm^-Yn{3mHqn}rVQ`wZ##7y+8@)I+* z`dM?b*7kh|ZOV78A9c05#=MnMDl^2@v$a~}Y>aZz3MvE5Xd zn6iMvaPB zI5ATg0`HCA01Po`xhX&w+;jhIgQ-^xD|S#>ir(&X|NU&(*jV5C+<&WZvh`_pT}Y2* z;R&`vTc7+gb-k17E!Yk*xt!>uZs@;dj&S>Uyq?eJDwO(YJI2?zw?DE(<2HU^*Jrfz z&HKNQCK*NC$7nSF9MBIkpxgkVrh_m>ibN4*#(@{AmvRBTEl$D}0*(*}c zioj6E9+_S)Wmz8~RCtB77ILfjFgz!bk{I~sNbyC%c`C|C~IflHf?%7OG7xjBPTSo6GCJZKmgSTJB1Xs|yZ@StH}zyZU6fkJ}=fdvDFg@p$P3kw4S2KFS& zPHy{+4@`G$UgYOzS^iLoXrYL|g~V*wf(2T>?thfhBV7%QDUa!PO`T=ZFilfvyS4=e ze9*~u?C<)%!$Zy54eX8I=8wyKvGxR*QgUO~v|h$XQ4P`beUfnLq=TuWvq41S!;m#Vc5Y9 z78+r(47mp{(r&|W(-&E|+As_P8>l!p3C3!}@^tKAVL|*MHX!BhTVuK=X20qDyyd+_ z+q5>t-??dx$^jo?TGff6_yNYo4C2TB8~8t);G1BKDzlZBt*#1Yj}~!Kh>#~MFCku+ z>r`TnTZ~csYu|^yUzLOXt+Bxm%l>Ve)@jl?3Z5$etbFKI&A{o={G53A&x7#eoNj5( z<6r2&sb=-JnlhV2xC7qMDEiUW_egEZVJs9N8uoyh-g(lW}x$J zz`!=B{#Dmb&X=Aw=rE_@fwN8?+|q#fB}{7QrGd%L#p2En%|9~=hv;L0=~$OLMC-UE z|HPT*C6E@0t!V^b?qywwMl3*{iySrkqB`h$e%-O=p9t%y`3L3&ICAvlZRLFNS#kIn zknqR`?0}>#ckY*2RsKKz%?#j9V#~qtO|SR-=i0IO=ojHckNTo~?x%fE4wrH#yPy1V zrsLbU_it}}b~@itI5f3B`L?`ox`Fw4u;Xve`Of2)h2j>s)QRYZ^YNj6 z@HtbC?-K`^azC2`wtn}&UUrOhRz6~=7@A{BYf4v%2DcU03o}5!)(GfV4E>6sSuw|h zMU;thJOy(+1#=7)6QM{wQL2a#XRQ%=UPvzdd0rW_4v_fsyfS9Z;^!IcS56boxB>5B z;#>(uH7kZ@#n7o3>YdK>OeX2Nb+HUKn$v<<93w3+6}*=12?XNDJl_C>TzozW}QyHmd*t diff --git a/tests/sdl-client/fnt/6x13.fnt b/tests/sdl-client/fnt/6x13.fnt deleted file mode 100644 index e259f1a344526247500ba29a2a80162ef197a3da..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3328 zcmeHJA++N-3^WW3EEEh3ELad2Xs}>m;D?5VhJk^FhK7X;3j-Gx78Vu;EVvoTPP$7D z-+QP3e&VsDv1Hj%oy%_4zwA^bY(Y56-#`AGOX-=?xounOp79=kN-sh%#W?j{-*p5r z#^HAhch68h-z#u#^OO=zv&j~9+>gWX9ER~$H11>L z>J;8(B`2IacnobGD z@)ou%973#)HnwMPW&l>*LnnHcng&$E5F-`hXNngEQxb~$g?VmZ7Zq$6hNhVV&Dys2 ze%b8sZ86@~stI$9Z_ue3d@K$6fNj?Q3NFXRb>0{-ocN*1fjgp_O3i%s`ru-l z&_}5%8v)L#9W;Ur@n(i;?=1ovDFZcHMjRJL9rRH0!Bk_!0d3oc(F9GJ4{EUSc>XP5 z;Bo##LUWm~Uz|dYOlf`>JyWv!G!2X(mtC_J4e#lj;p`e%HS_}8ZKacH#7)eHq2c(t zo^dX$5X`JVRH)5v=G-stPEF*4O^;lu58P^rNZ}YLj&`Hqwt_$EkUmpu`8W%D{~9w) z+XI+ljLk1TS|DEmYO?REx@e}*ZuOp?nB|?16Q=oA!T8RBse#7w`N8mi`S0$4I93(H z6eQ%7vVP&6`>TP?!TVq*na2x#uK3Wm5Wt1M5E?y}dru^DnDb+O|Ezl z@*T}owsQ0p*nK0x>+yI!3IRVPyKu=JAQT3s21AnJSv5l%(gOsRMv*y&Dd6^ZoUDKWk5KbD4#&HLSXy1bY03T6w z2AE-~1N7qu===x3ULR(nm%I?TANVnLd#3agY)8*Rf5$p#RA>y`E39(>taAWR9{@gq zwbsudd=3Dg1Hk71+BILLHzMkJUbD3+vT@_JsASs#*>(V*1CV;xFeUu%urFE@u4vnN zT|>n7O6giWKcH3@T?f<$0NVl7%riUqIPD~Cn*`IcB=*T00mI%~4#VC7!`=bI-T}j| zpvQ6D0SQAFQuZ7{tY`7}mu^pOQ@TAB==M~gJ5m9BCT762nfC$Ua{&14z{}L?mZ}e; zKH8K(1+q6*4o``~wF5nmYgh diff --git a/tests/sdl-client/fnt/6x13B.fnt b/tests/sdl-client/fnt/6x13B.fnt deleted file mode 100644 index 503b773cc2eafd52f47477fc7cd3bb1287976587..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3328 zcmeHJuY2S+5ETeCI8a~^P*`w4V1a@EfcyakfdU5t3=9ec76b+q6c!c+957f=5FmKK zAmD((0)s*Wg8&18zBePuP6~?qeO0nvJ!Y`vJpX+7Gt;z9 zQ(I#|w1zo^);5-k{#*FwedixKfPk@PiSgs;@luDiF~T55fzt*)i_wC5>VHhsd}Hmp zW+QyBtGv@UZ7V<@Z7VMokQ^h?7yEy~{}e0RyLFNWDtTv-7F9`lV(HsH^tVO>HFHxn za|Xu5kt6w6MH_?6o6fn-xi4FiNNUsk@Riq)3L$X^kw@X8Px-cf^{v*?bkWz=`Iwa5 zy1F=>X$iJMP+RLbtgVgVlLPNWVFYvb8CI<0aWqK{(H&o7ZpDYH0+n+iP-@W?80wtu zR|!e%AgG`h1Vl`IRcQm&b>r`+D9vYL2-}h+4Oq7&^%-n546egv%Mcm+?Z#HR5eke=a)4cfM$pHNjbDG&)nC#wN(B|8B0P1yjDr&J%6EECql)EGE= z?clJ0X~F9tQ`+>k#$)7gab1%nBrCy@I2{!%1YEewYN%VutC&=YFXnyPpfYym(Lrv8 zECcfy6W8D*u`|%a_8Xr6kNHK(E{#q{DZCe$8jpb@NJ-q($`f!p(@rd!}CeLA)r+$3-`f~Z2 z2)6th-(0f5!~N$UKHm@2rP`Nxduq9v3H*XPrv;!pmnBK03NC0IINx~b;9!SZeFRN+ zkeu!h0J^)4y~8n_3CwvIhCkPJcmW#=#DTjSA>gN*+*3T2vCa;#&JIAm1K8jM!C=B+ zvx9y*0NCsjxiyz`H^OUPRJf-=KsHHyW-9<5>=M}QfYe0|90sxE6Ya)>cUkYsy z1{~c1sCUpE0o9N?4DyBzYP>cHxTfGo%;|Qya+rhy=ym~gy8tXyK=V_x10g^8;qt&4 zhTd6hP)QGRFU`$$4tLf8kh(}<(=&o>vjY@p2l$;?z|^n5OG?ECUhmgbE>LFM)pCKd y%`V9W5(y3$Z1cQ@Zv2S)jI0lXV;S_$N(EAo3ZwvRAJuR-N>$~qPq(J3iR5qOSDa!1 diff --git a/tests/sdl-client/fnt/6x13O.fnt b/tests/sdl-client/fnt/6x13O.fnt deleted file mode 100644 index cb5a27f030eed956cbb25a4c2fb9683718e14196..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3328 zcmeHJEpy~J5EU>mSRgRKAV9#t;DDh(fPsKPfPull0RsX81_T%w7#tWl;6T8Efrfzx z1NSE^3=9k$c<*&fR&J~A_*R`#HPLf-KlPW@HfA&de`m9r|6IdxxL)r{YpHeW4$Ggm za*m+wmP56xnueh3miN0&()TSN>auo%!6cyXUoYTto(R;3E>WxQul@V2?b_1ZG>Ai0 z__qX%nVV)9h8SNlyW1lBs&?+=y8bmvOqDkO_;0Zsvgr79x094dg{GOCTV%HP!{F=Y zQ`G9QDXkmUizwQrX@@^v9tq0Q$}`!6x5IJ{&z!X-7S8G@v*YaN)p3iZb=1B7wyFl8 z)y*=*1W*)0wJa3aCWLT44^BWkNI;S+9|9ch76CJCd{LfHR4YrgSAWyyo4am*OcEcu z?sPt@8f-v-FlYm|FeM3rFC!a#@C;IXi$FubnT{DKNq{w*RJaZgixREQ$6%wTRsmqY zu9rnZm*8$Vm1)+}I1-ggxC^$i}=uvYwa`kz67*6W^wc z&e0u58=7ov-1q&U)h1Z6232bs+xF@~2msRA5NR2;4?MbEV|a&z?J2#9J<5r$q75ZT zJbr&(=%lTdN#=|BtnClpiH=HrAod~ErznbhN~ESDnPcVno=)^A(jh_TN%{(o?2j}O zi8U>KlZ=|uRP^=23PDW^L^XN5sTuP)j_axf#(ZIuLd5#O423v<#tNDH@H;%vR|7U( zdQKZ>wWj*ccH3>t`e+Wo#tLbD1i%BS4?%-_Be?^cm&a0@%*>@dNLOJ7IQPbcv@l>B zup$oo1NYyE-hk?{$M65^7terYiDQ9ri;lx}jkoAe11$JvIBDzf-Nu}grjQOXkpL!D zU00>FA(dF4c{ zxNAHk5>W0i1p&mJBr&Xl>-0XrxaQEo%z+vl&7i8k0DLNB3B1h!VP{}|LvHu)*Xv(k zgQIw_c_(7N;~Wes42GMYlL5}j0Mr@4hJ#9Dhi@6cCIi@HAXD=h!|@a6!0Ql>d%aZ2 zhPz6`Cff$cwgK2=KJ{rEU7xRfn#K^(*SgE|9HX8^VgL=7#Y zeT_*H`gA9w#fAZM@|6eC?QP`H?G4cF4bbfk(Cq}Oy2^lrem7mWD;g!z`p7vUjVVb_ z0+OBtBqIsHw>H&>ac2OV3}CYXSG6Tls;Odu@|e;BMTvNj)Wm}Xv_Mf!+({C&H9omC tp$N3_599is^+^SapRHOT1)ye46o4%Qcpw6uS^Q`W=kKq4X+=$^+23ZW$7}!q diff --git a/tests/sdl-client/fnt/6x9.fnt b/tests/sdl-client/fnt/6x9.fnt deleted file mode 100644 index cca4689668968d2fdb00062d2cbb500d9bd23fa6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2304 zcmeHIE0o+g5Nv2DxFDdRprE0kpg_PtLqkKu0SgQV1Oyr^FkBE2I503UFmR!v!GctE z%aeDv==f>glS}x~XWtim&*?%3LMDD3eKhm8Mj#?|f;m{-Fn@ZkuMim%p^J79yYO>}cQtN?V6`i(q zirb`lD?!WWPGq>y?{oU52&Xy`ccAJaYlrqBzk_ly5UA#ND z0g_UOYAU$6`|@ksY_Bq2n%9ECEwC3t1E8+)k~)`|dny`iFWgiN*f6iQdzr2FW!bi0 ztYWE(iU^2kJo5;cJX6oyD`yQIc94l@N{PDwv~l3LPb@t;jRkzY8zMbk2qazCRLJ@= zP!gh|4**sN&&Rw9)_8^9pTN5kszSvEK?Q}YM1G<{^kzS(vN*}WqeI-((yya+4EYRC zRs}YXo;SRi4_oDTL(rh=l9W6$ZLEv4-NT@o?WPOU!H|q4g!GE;YXSbB{!0h&+}KNu z(AV1Ic>a>rR+qX#14aQ21JKf}`;3Q9QaJlQKYw}o@;t*P)g(}0#JbjK2$#HTfS>vK z+slg?)4|6F!q*z?y~+?Y3^Z~EGxJg|V@9C2w=uRY)D%9!l00TSgKX-GhjPz# zN^>wUcA=NM=Co6SUovjVc%>7m02j;w!Z?U007L&vCG)zk>v!bf90lh!3;lcyDj^x??qN!chb;5uZoU6T}8$JpuA?A|mgQcDnv@8OJHZ z9U6B(L2Muze_7;{ql{Xz)m3Y$+`Z8bI_)GAVgq=Jjdo5+h~Ys*)As6TPL2xbPopNp zagIWqMph@tCpE!lutsS;$?618iCl-AvHOHNEJGA;Rj>;L2zc5!kW&J4{1GV%1X*3i c$=ng9XM5QW0_zI^y#Ap=Z0BCEovTXv15=`0umAu6 diff --git a/tests/sdl-client/fnt/7x13.fnt b/tests/sdl-client/fnt/7x13.fnt deleted file mode 100644 index d20852efc8f7f3b29753079502d1064fd7e56084..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3328 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 X0Cfali-6T&;$~Ku^;98NbCQ1n*yqe< diff --git a/tests/sdl-client/fnt/7x14.fnt b/tests/sdl-client/fnt/7x14.fnt deleted file mode 100644 index fdcd7522218b5be752214150d92c37808d974a6c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3584 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 diff --git a/tests/sdl-client/fnt/8x13.fnt b/tests/sdl-client/fnt/8x13.fnt deleted file mode 100644 index 9de72b4606003e78fdc7b4c28f7cbbe8bf7d0413..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3328 zcmeHJF>B;T6dr^)gjtL*E<`Y~Fe;W5L4*{G5r+`Mh{YVs;-m;J%o46L!G#MKLM$r6 zks>Urt_WedDr}J=zrdu(uSj81qzb|02l(=R?~OFNgcKxNP#6Y*&#ss) z;8^uZ)I`HvnnzXWbC%{7YIdD2l&$1>eh|j_dQC-?6cpVVVY-kdiCtC{o*%_|!(k0< zjy$i(2>i)mQkIP`zBPG%x#~4XF;StzXhWxC+JLQ)zXfNfaAQWmU5DRl^IRO2#Wk3j!|uwWl)u{K6m=B6l=6-mtS7(2 zUzcq`KG?h{&LmkhAhS^8B)Pa9^X&M3DT^!0YH_uwN*M++5ut9|pLSi>wk@fPVzr!~ zSvH1=B1@>zsA;7huzxwvJRs5Z)LD_|S)YX`>yB(ET2O?oZL!;@)2V1MciVN{t|T=y z({NgKW*OziuMJR!T1q)~t7J+~3X8o6~= zmsZOqsn?$8ogz3Icxbbtu^ODyxTZhW=Cmwlc}6SCruo4EO}U&}hWTb931WsYtgLQq zeq_ur%$VO0olf!tJNjnXbT_MSs##rvNXX$Rx3+}DT*AGehuur7!R3M?m+K9S`;A2hl>x)xr~|0j4t#Vz0*JNKL~_V2f%#&y z*?jZ3*?bE&Tzj}=;8F%&%!5LOI>lfDJAio{0JQ@#KKJB0VD4b!0Bjt<=a2)en#C`+ zHh%uvv_HP-zWTBS0ohnV+MaCyWE%i%93b_e0f)QMyzFc~UoMxcEEj5~R3i#U9RRfh zSeyeGhRP=U8N_*CHqkb-5=HpDC@u*QI9GD~F#xyIAVCgbq5&X34v_Hrpx$)XY(bbJ z$&bu4lqoppY8K}iG^8E?LYi^~jHtoKo&#w$0@Vm4(bSjkg^V`v+LbA=IgNN=BZ}QN z(r~2=qY#KYJ;1g$H6JQWk>-cFcQs2Q-xn}YJI!=D0vHD3zGn5cKF?8%B#)x`6M%n? A8UO$Q diff --git a/tests/sdl-client/fnt/8x13B.fnt b/tests/sdl-client/fnt/8x13B.fnt deleted file mode 100644 index 96179a0a7d876cddcd27486e171639993c96573e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3328 zcmeHKJ8R@t6uuav!U!`}xR}9i_6Mj4Mg(C}xY#0Xz@+ZuiMJnnhjd&Xw5FmK=T7cZbu z>>3x3@V5AM@MFrdE=x1FAY*pm0i7`27*K(4{B%w`D)G zp!D|AA0M%J;XWIVR$10kty&$0;x_ksY{(>%Wdh_gZ{ivesr z(~zj=ibCa>w+63C5Cnw-2**iQmJK{2C(rAuTggI!5$1^>i=TNcG~ z4)zQjY)iM)H~nlwko}Q?K*S82)klPkgu(?!9ksqju32tAe%#zsFy@X8cLk^?6CR4m zq^3?)@NB4~25_Su6>J-$0>kN)Y#WGDNnl)<^fI}n$)QXks?RH&?2eeB1 zho+0-TeG4S8bHRX$ITM90VGXiaG#nlHMItujSG}h?@BdI-(0(?Tnpk7bf_=XdVQW{ zvD_%SyjnHv(W2mThHd~rG7^mmK+4E=q1O&QsRVOZc`kJb_Q*jyD1kFVTVd@b!GiSj zVBH0C^ui@p5aL}0pQ<}$y9z7PNXX_TQlnyEBG@LE?C_k*K}}U>Ug9)u9eAxZiHEHb zGa(7Zf2sPOsBA6t9^; z-Y)HRj6M3`_>NLgU5e-HBOv2x6^4{`c^Qa3)5v*deU5S3?39P@7)PliaAqiuJ>)gQ zL-$AXr`q}d`5(>z+S-?xL5$p|+uKi^G2acaqRGy)Mp{SC_FQJac1Zi=(BoHxK-zLR z%h0;lU%!04)@GZ9?iYN);4Y-oZ2fMXohrbsRPXQax1f`PfyMRvg&3H3T)uF}!NTB6 z$@Bt4N(Y27(~HPnOuZnweW2p6xNo>5&Dooqo0~U39UL4Ss>HL&C`5-MF~`XHy62WFQ01<~ zHm^aQfUw;I*zN($^#JiqUP*Ygme=5LY~md)mOW$$fpd;-cO!>xcYtnpfNpnyZnuE- z=>Y7R?DLUZf2L_)p{_m%%#SHej|Z9_4>Thluy^Dwy>ZGpn2RsE`*E F&c6;bfy4j+ diff --git a/tests/sdl-client/fnt/8x13O.fnt b/tests/sdl-client/fnt/8x13O.fnt deleted file mode 100644 index a38a37105608b49ab60ff7dd13d2358823648407..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3328 zcmeHKO=~1Y7_M!bwG2aOhdI=t31w4UPZ+>~tSYiAz z)H|*aOx(6&49-`^xl&g+sq2+dlQh5xKNy=#J~`f7Vpz7bqy6X3Pzn{yv9`@=ADB~< z8cCYw>T(tX7>TDQf5tvPx2Plp-j;U0hsi3$lS2YHQANsfUJv zBsPt%4sz#$39rcwkE4jxa_crXl4NK|X}&XVk!4fQow+=kyNDt%Cue2z>PIKdOP`-8 zinev*)F90f5$jKyNjayGx;5K+HLYhPL!gPxrk-&osIX0bSj6r|Q5=`2K&qh7sV@fqa5_dT)66#N6=zXkcZos+lbvI@|N)PkT8~fl%K`;Ji>$sA|Mb zEBIS4!wtK4{Pp#PE!Z^5b?!bK80)KQy{=RnJlqy>3v&OKW*;73e}8QW%=PCb|N2oP zR6H(lkj)s!!rDcMleOJJx8hGf)R@{iKR{fB0ChkIy2%5vx?6 zTq5;kl*sJa#$byak~mcdf8d;vPh`&iLnp!If+Wb&IEVX`;LUq7{gAPEf zyJ{i;_Nc0=$J6!tOR$BWh3^3DgGM#AZ3_DYfPDf0bpTj{MulY4H5hk6uiuemSqD(p zEPvIuH^S=4#~)mL{u$kg#5S3re|feR5L*kdCIF&tCEy4*T9ohZ?;l-Vy_(h3-Ko9e z?xw83xd7l?0B|k<`c9*L+BOB_0-$|b2~oEIYeJjCngFmS0IX?&62ll@1+E#v>d>YT z#UL>jcr{~z2jpo>V9fCnlr=uN+K*DC=p&mSaxU~N1Tg^QJ^=g=5&$f~Hoj;1USHRY IVN);p3)sb=q5uE@ diff --git a/tests/sdl-client/fnt/9x15.fnt b/tests/sdl-client/fnt/9x15.fnt deleted file mode 100644 index 349e5bd9086bd4b6cf40d95b543e7beae0c57636..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7680 zcmeHMUu#@N6hE8BbR#iImpnuW=_aft6p<9c21(N0bd8&!O{)e9q8RFfFG4_&3S~E6 zqmMrLpx>Y$$8XWM`1{Sd=bo9_xe3_U%&>Rvo!^`@=ii(=b5}&y>tCbz}v+kf{QRc&M z-oOg$gS*HQsFb>fWkEWq3*M33=}eP)#z}+@=)l%iwNNMvY z)94mr{3rZwp(BnB#TDU*63V{Ks49(Jxds?_6GRYW$jhSK34O1^bz7dvCl1zk3ym>d z56Ny)9SO3>3)e}+vS}!WKKgV~ofg|l_m7`~?AKth^IkRHzB*1wyeSfqoAkB?&yA?Q{Kyg zZ!FyeIiJ`fTGjzP=w(Lc;MWdjKS0D%!WjqA(?|VL&Wg0_VJ^j0_L#HI%b88**h8u> zwN$-ReO04@JeIvM^Nj39Qt7Q**YA9g_wmj>=vL|?Vi;4gzDBS1i>*k+DV&Q@2&_sQ zsIBQEu0k)0o?y*?D_;kz#ZANcnnYCBnWGjfkl_X+~hL&PIMTB}Gx)~@ucFU+q z=BQ;P=Im~;m-}eF&iI!3*{;{beNgB+)U`VUn^@Tm>zr#nuAVteQX7ALdsz+9OmW@# zeaO<1N3aa@mKTjhW>K~288T{Kd=;m0MUE|Svv^E7D;JGSU9GWyx!;I)9JFL!_OO0f zci{)+qa1RXDY=)`aJv(&;dtJ;fG&!y-cztLJEA&M{AooS1W9EWcL~ zscTsK{OWd2W!4d@qxxZ*v(wT?Dyef$y`Y|P^a*x18;DJ<-tY6$!Yyc<&P%V1>!`nj z_jF#=Lwb$lO2<(buHk(BI$B!4hUyyDw@>rDhw?eRndV{c7Wii88us;jyNZcf>{wO< zPA}fBSX(a0TZPXn+U2lw(p_E~dEUwc`2ElxZrU3Q+*sho0C+ZTX2_qRr&v4^90`)MzH-h-q-wAm=~gam+(78UiAHy_vX*KTj2GD{1A2pI^q$| z39NhaUR`tA{42mT?mY`34*1-~&PewW{`RwRJ2>+U!#C6TBxfjF^Q-we#*FiZ{05ol z<%9YZ!cm6alRu<|^G5t#RP9Vl9?7Q#T0QaFo}SjS7}5Q@)MtG=6RKRgi)cc1&)$Rz z_FPZE#=at?oF%N=W|UNG*f;4Hh@4jBAuOqNT(u`Ea=0=d^rK3B)q|$*B>0fohSl~+ zrf(TFeaon6R7P!H%S4)g%nLSe8MS%KsLk7iO8sr(Us#1T{?4F2va_|FuGN$-v(o8x zTILe!GM7+`jd;iJG3gqWX;nHqIKlA<-@1Zr{R(n5<6V%$y!yG@G;-`9`r2a} bl~MCT8MWPrqxrd8-#6xm^c#ixw?z9N5ddW# diff --git a/tests/sdl-client/fnt/9x15B.fnt b/tests/sdl-client/fnt/9x15B.fnt deleted file mode 100644 index 23bc97e3f27224b5d3113300feed860271207883..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7680 zcmeHM&x>3|5H4NV1YEM$m0dO>;(19R3}hXN0Z+MlpD-{97{Wq8SF)L%qX+*0!IK9M z3SPYhHyKC{F4=>Zm52rc@w$+M5toeL*Y&=s?l(O#M+ZuGmP76d%6^{C;BYw zcGB~i{ha#GTNolQqOU~ezqyaclYSlPoRQ`yqPwY$Kl7F+F&lhpS8W^lvb}|JcRDwi zU9=~lODT%Z*cH&-jJadiK-20y#KPk^q3OU+lp;ME~SMeuk`9!5B?X4Z3k(F<}5uZ zI60af*wLl-=P+0sPWJD1=oS9F1IrUz!*2m}58fAP5njFy8x2rDK?_o(=a6yFe#aP- z)8z`365nlbNge1U@lYn$nQ}*RZK z@T%Zd#J9&dIHH%}>RKP9QSg-|P}k@nt;7#tvp(V|Qa<{V#tQ;H#@LMU^V~~;^1FA^ zIB~4*w0af~$A`pTBJIKpNy8@ZAz~s;nqH5mz3&AG`TI@%{nT@*k-jHw_#SCk_%b~s z_viKGg^KQvF^6=5HR-UA_4x>NX#2@F0lPl=GuLQrf5FSS_j#CPE~OgVV|xlBdSLfK zxy>kV2sQO`^0M!NeUPES`>fF?|Lzbx>}_a_!3DhKw$dDy&kfPF zWK~|u?6tmK}LdE0On!s|oR7BYud1RULyFw*dpu0(rh@sr;x@o@GystcBWzTw` zSP?j2dIWtPO9>%W+-{H<+8_xldNO_;LJ$t*KQiQr+J`C2T6m~9Uc38gJUdtQ}UXGF5 zAU;rzf@>G(A9f>Y=K4jwmpqCY!?Lhil;cx7(NGVk+UOHu40{BtSLH+%!2?xJbH%V* zWm?@7^wlxEkLz%`a~LApwBLx)KQ;9U>g6N2f<1wtr$x__#BaRxCY*C_FYTRt_FhbLxTCC@pZLA8-L~)STl{XLJVZZ{HZe1I?S1?# z{Q~jQDs;J)?zpa@wAzO8z6)xvZ)LNX@l_vE-L~)T$Cgw>$oQi4>bH3v+&^d?+L`%; z&?U5rl|;>)PjB;b-@Wkpx!r2kc((lzR=Sz!Jgn$6+sDh_=PY70*3iKQ?xnbTlD20$ z|ps z8`z=E>+#1Y+w{=C_Irjm?gVUlPSBaqyY^kgJk^UyQ!s!MZwHbfEQNPE8WyDRn{ zC{8aWO4&|>lQ&rVI5X}iI$(8^YUl1HwC*Hr8S{R+*4bL;Q0ZGomv9?%b}p}_JMVct z%g&mKd_Gd#$=B|94>$$6+@9Qt;7bb0g-hdz zShlL8lDUrRyLD6=%|reB7oZhmjWO4^3ysWXZy@#uI63_S`W5sTZ^t%DxHO90bAHy4 zMs-vg)ltb@N2BNRI33dil_YaSQqEi2ihMaL znd`jzZXK0I^HAD|iT?pI={>!qhet8*WS?g78>1g_mY=(&Q5}^=byPCf(R$DMxqB-` StzuYFyp^=^wXW>hdH(~RDjH4z diff --git a/tests/sdl-client/fnt/9x18.fnt b/tests/sdl-client/fnt/9x18.fnt deleted file mode 100644 index 0651c97276b494c6c888382a2721074b5f417ea0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9216 zcmeHN&re)85S}G8St<$%;(#hbSVCJARTWXChy##ak~9ly(7r=^>X&kt$V* zgjoKK9DC@Y{SSKR|CJm4KF^!ypU-f}slwJi+s~iJGvo2tZ$L!TuhNsDjDqKnO;1?# zWm$G*A}7Zal*`g@JO4g@ET{bxJeIM%#&3+~HJNU5OQ@;51~;$p`xdx4%)=a~E%Y*P z;q{!%xZg|+X%IuqCo&HTN+|)!tSlhUrmv&6axiH*nMA#8+?8MCd-)orG^jr2jMH88 zZXjRA>s)r7BYeJ#hZkG#+SzPm4x2x=zV34VI31T`Pck~`nD^H{$@qC+v~#)6qMuyP zIi0icoa->BlfMaNzkr?h48IF_ncJZvwV$|d413mstRv$m_b07>0-L4PrfE*DxSm7z z+?0p%MG9%#v#0E_7FevrIw6E_;>3N-Tuv^<@d` zId*z6`BIG1;f$Q`GUm#8pWu{a|16$8}CO6?STUMpUNR`c?9^bVlF9T5cO&!?fJ# zf_{vmu0XyH+P{ib(($%pHqOd;O{tr*e!h{%O@08auh{Um?8~CdYw!hTZEa^$ImCL8 z@Y7OjDP~0m&B_m5IVj9!9@GcnI`YVFHCxzpg% z$##SBs)t-ITQ9DY@s!fB{iQVS!G^Ai9LK1T&bX02MhL8Loa zRENn#`G4^tm=z((oqjZVs_CJPEAEX|r%+COl>sJKtEq$Crn;DQdI=gM8{^wF_ zf17RXQbNJJTUv&&SMBJFZS7SEaP^yz`K#>h`HOQ6oNM4*1OF!t&{s0WKC>j@H0}@w z?)%{pYO5LKY%wdIYCLmz&Xtp%N{c`+TBLlXNY8D^d5l^@3~3dy35`oF;bd7y%M#DL zv!!j^RNl&;@&|rz-AyVV$5{6qevC|C%e$L5GeE3EgJV1w5dG>-M>q*fv5j~{*L_8> zK6~;H`2AP@!m|`f5jl#96BezE7#h>*6~joDGy zr*)faAiqJb8TrUXvtyiLK9x7pLp&}2dx~F+efhGJ4*drAA)|TxN(t|)_VrbZ?dVO9 zG`izI`W;xuhbXcUoRo ztjWt|-oVO|T-Il$$9TD{&q|LmI}E=l6%}Ii+^J){DOq%<{AJa{`FO|qF@B*vM1GDp z!)Cb2Zk$>fewEvrTA!66#$>lXD?^N}sr6YIV#2vBW@UEfCDz&yQXDn@`^b53lgc9* z_glm6dx;-wYt9S!e!t6Vu^r>8hA|D)Fn;wJ;pvJa+3I&;IFo*h*XL3xZ^6UQV6HJ% z|5hk?P^YC)n;J)o&nB}FwMP1f(1$A#uOF^y8RPZSjWG-9o=f9~oHdLBg?lHw1$1MK zn;ISCrjFr#aRWDx71)+qbZF0}Wc5O|SW~QLr4-BMB~$&BF(z3JV-r OYQag)zlfLPTK@s#?p|U5 diff --git a/tests/sdl-client/fnt/9x18B.fnt b/tests/sdl-client/fnt/9x18B.fnt deleted file mode 100644 index d93d09d0d0e712303299913ff73da7802bf8b304..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9216 zcmeHM&1xJ+5H3X^bP!gK8zdx?ClCnMh8Q_{h(I8h4GU608+k!s3nX^;F#=pEDBssLHQiI))0TrS0d=!8J=0%Tcl~sAO|6KQZ)8b6mCusv zmrYN4Ta``Oleye1=D2UlYTNnK;&XY|&%l>*DR1$+lyzBYs`c6Zja#4c<+56tg~<(05!{yZvsc;N*dHawlfs_ zFR|i>1>VVHTCe3g8}DBbla68Am-WT^!ejh1F@BuK+$?S~I)2PuJ#((RqUGs$zHTF* zlAJxA+pM`HLrfY$0)tI43lbebnh$J@1!%wjHR+A zyXe`Gvv7SYzhK{%%O*U=e=oWN?MgaqcZ+e;ee0hTrM#O2JcfJyHFoNWJPDrVb=dn| z)Uo`DItl9-$|>saT>ze@VZU?76a6cG#}1QnwAQAOC^x=rF5f~Vk@7~c6-@2Ouo@uc?$Nd8p!pa z{c*hNYh=UPyYgikrEGHf>_NJ}E&gcgE69AW9d8XrKdeLanYCG;-Diyr+I@^&<_kTh zYF@9quoPd;U^l5f@54%biS}dEav9~AktfsG1Zf=f+L*0no~+6fvgj!8ublT7@o`6R zcjPuU#@h*Y>J-08BL_d1aUAbC?_+s^y?2yI(N zp^VNUWPJ#UzmSiutGyFL`m3lWhqkk6yp)&09-{r^Yo@QWovfbG>wPadCF6PNC3F!a zXlEvEaoH{BN${ufG}V^3)&cByAM1YKa?=jbIIewsXG69A6!IL)(+oWdd_^lN`5zr* zRx~Qwk6M<_qWaC}igd=%dYKM&Jq#K-Y4wsVI=pxaIjTRPWpmC{XE|TvjEtUpC&~Ff z%P@KlKY+etfyKPz~SaVF2b+Z5(=Udp-rAm8J6jy7{V zu{?(Cp5S|m@6O`uVrv6)PD18tf>xeYw?T8rMk7UgQ@;B%(ok{D=klZcoUNI-8EU=U zT@GK6A(}KNd%ui`VWzG8zk$XD^%bYR7XKXJ8}t-*FiJZS;WMvzig;|JtsOw}zZR~!F%s4<@>)J>J7gh;@(8;~zpoCQ3*m}(0M*kYUgsrEBvGvq2ww^jh#b94vSHZsY`>PdDqjy?fU#`jc(l1~030+$? zzG@ibtA;TzK%)Wp2vZkY)I>1Jq~1$DKk7$FRpyGu(Jjxn?{4DmUJ17~{Q$ zF`d>h=E3cJk2dB{ZV6s&*65rZvHHUO#rnLt?}iS}0hA|1$Fj4zFx%=`+ zru}{ly%5iv`drH8^Kcf-5mCH`6v~B`rt+hIHvgm8C?D#L{vq|@iiz^+$3*$`W6VO- z^2>ASe-48DbhM4=f4b|bVLlA;MxBnWr$$F}6+`&!n6cZ-#H^=JE!SjnuH~9c&NYn5 zxrQ-2tYg#;>liXhJqZ4;r}19H81FTV>9mGP a=b{u;$9X=Ad&W+mnD -#include -#include -#include -#include - -/* THIRD PARTY LIBRARY HEADERS */ -#include -#include -#include -#include -#include -#include -#include -#include - -/* APPLICATION HEADERS */ -#include -#include -#include -#include -#include -#include -#include -#include -#include - - - -/* DEFINITIONS */ - -enum userevents { - UE_FPS -}; - -struct font { - int w, h; - void *data; - size_t size; -}; - -#define VECTOR_X(x, a, r) ((int)(x) + (cos_table[(a)] * (r))) -#define VECTOR_Y(y, a, r) ((int)(y) + (sin_table[(a)] * (r))) - - - -/* PRIVATE PROTOTYPES */ - -int main(int, char **); - -static void axis_angle_update(void); -static void handle_uevent(SDL_Event *); -static void handle_recvmsg(thread_amsg_t *); -static void objects_update(void); -static void frame_clear(void); -static void frame_draw(void); - -static int surface_blit_angle(SDL_Surface *, int, int, double, - int); -static SDL_Surface *bmp_load_key(void *, size_t); - -static Mix_Chunk *sample_load(void *, size_t); - -static struct font *font_load(void *, size_t, int, int); -static void font_blit_string(struct font *, int, int, - const char *, uint8_t, uint8_t, uint8_t, uint8_t); - -static Uint32 fpscnt_callback(Uint32, void *); - -static void trig_init(void); - - - -/* PUBLIC GLOBALS */ - -thread_port_t main_port; - - - -/* PRIVATE GLOBALS */ - -static int joy_angle = 0; - -static int shields = 0, cloaked = 0, nav_thrust = 0, - cur_nav_thrust = 0, nav_angle = 0, - cur_nav_angle = 0, nav_pos_x = 512, nav_pos_y = 384, - max_thrust = 12; -static SDL_Surface *rship, *fship; - -static Mix_Chunk *snd_cloak, *snd_uncloak, *snd_shield, *snd_unshield, - *snd_torp, *snd_hit, *snd_explode; - -static FPSmanager fpsh; -static SDL_TimerID fpst; -static char fpsstr[8]; -static int fpscnt; - -static struct font *font; - -static double cos_table[360], sin_table[360]; -static int fired = 0; - - - -/* PRIVATE FUNCTIONS */ - -/* ARGSUSED */ -int -main(int argc, char **argv) -{ - thread_ring_t main_ring; - SDL_Thread *recv_threadid, *send_threadid; - Mix_Music *mus; - - /* Initialization */ - screen_init(); - trig_init(); - (void) SDL_ShowCursor(0); - (void) SDL_EnableKeyRepeat(0, 0); - - /* - * Ignore a few events with potentially high frequency but which - * we don't need - */ - (void) SDL_EventState(SDL_MOUSEMOTION, SDL_IGNORE); - (void) SDL_EventState(SDL_JOYAXISMOTION, SDL_IGNORE); - (void) SDL_EventState(SDL_JOYBALLMOTION, SDL_IGNORE); - (void) SDL_EventState(SDL_JOYHATMOTION, SDL_IGNORE); - (void) SDL_EventState(SDL_KEYUP, SDL_IGNORE); - - /* - * Network - */ - if (SDLNet_Init() != 0) { - (void) fprintf(stderr, "main() - SDLNet_Init() - %s\n", - SDLNet_GetError()); - exit(EXIT_FAILURE); - } - - /* - * Audio - */ - if (Mix_OpenAudio(44100, MIX_DEFAULT_FORMAT, 2, 1024) != 0) { - (void) fprintf(stderr, "main() - Mix_OpenAudio() - %s\n", - Mix_GetError()); - exit(EXIT_FAILURE); - } - snd_cloak = sample_load((void *)&_binary_wav_nt_cloaked_wav_enc_start, - (size_t)&_binary_wav_nt_cloaked_wav_enc_size); - snd_uncloak = sample_load( - (void *)&_binary_wav_nt_uncloak_wav_enc_start, - (size_t)&_binary_wav_nt_uncloak_wav_enc_size); - snd_shield = sample_load( - (void *)&_binary_wav_nt_shield_up_wav_enc_start, - (size_t)&_binary_wav_nt_shield_up_wav_enc_size); - snd_unshield = sample_load( - (void *)&_binary_wav_nt_shield_down_wav_enc_start, - (size_t)&_binary_wav_nt_shield_down_wav_enc_size); - snd_torp = sample_load( - (void *)&_binary_wav_nt_fire_torp_other_wav_enc_start, - (size_t)&_binary_wav_nt_fire_torp_other_wav_enc_size); - snd_hit = sample_load((void *)&_binary_wav_nt_plasma_hit_wav_enc_start, - (size_t)&_binary_wav_nt_plasma_hit_wav_enc_size); - snd_explode = sample_load( - (void *)&_binary_wav_nt_explosion_other_wav_enc_start, - (size_t)&_binary_wav_nt_explosion_other_wav_enc_size); - (void) Mix_AllocateChannels(16); - (void) Mix_ReserveChannels(2); - if ((mus = Mix_LoadMUS("ogg/1.ogg")) == NULL) { - (void) fprintf(stderr, "main() - Mix_LoadMUS() - %s\n", - Mix_GetError()); - exit(EXIT_FAILURE); - } - if (Mix_PlayMusic(mus, -1) != 0) { - (void) fprintf(stderr, "main() - Mix_PlayMusic() - %s\n", - Mix_GetError()); - exit(EXIT_FAILURE); - } - - /* - * Bitmap graphics - */ - rship = bmp_load_key((void *)&_binary_bmp_RomDD_bmp_enc_start, - (size_t)&_binary_bmp_RomDD_bmp_enc_size); - fship = bmp_load_key((void *)&_binary_bmp_FedCA_bmp_enc_start, - (size_t)&_binary_bmp_FedCA_bmp_enc_size); - - /* - * Bitmap fonts - */ - font = font_load((void *)&_binary_fnt_7x13_fnt_enc_start, - (size_t)&_binary_fnt_7x13_fnt_enc_size, 7, 13); - - /* - * We're already the main thread. - * Initialize our message port and notification ring. - */ - if (thread_ring_init(&main_ring) == -1) { - (void) fprintf(stderr, - "main() - thread_ring_init(main_port) - %s\n", - SDL_GetError()); - exit(EXIT_FAILURE); - } - if (thread_port_init(&main_port) == -1) { - (void) fprintf(stderr, - "main() - thread_port_init(main_port) - %s\n", - SDL_GetError()); - exit(EXIT_FAILURE); - } - thread_port_set_ring(&main_port, &main_ring); - - /* XXX We should obtain user login information */ - - if (thread_amsg_pool_init() != 0) { - (void) fprintf(stderr, "main() - thread_amsg_init()\n"); - exit(EXIT_FAILURE); - } - - /* Launch network utility threads */ - if ((recv_threadid = SDL_CreateThread(thread_net_recv, NULL)) - == NULL) { - (void) fprintf(stderr, - "main() - SDL_CreateThread(thread_net_recv) - %s\n", - SDL_GetError()); - exit(EXIT_FAILURE); - } - if ((send_threadid = SDL_CreateThread(thread_net_send, NULL)) - == NULL) { - (void) fprintf(stderr, - "main() - SDL_CreateThread(thread_net_send) - %s\n", - SDL_GetError()); - exit(EXIT_FAILURE); - } - - /* - * XXX Wait until we receive the server's connection status. - * We should display a "connecting to server" or such message to the - * user during this time. - */ - { - struct msg_connect *cmsg; - - while ((cmsg = (struct msg_connect *)thread_msg_get( - &main_port)) == NULL) - (void) thread_ring_wait(&main_ring, -1); - - if (cmsg->status == -1) { - (void) fprintf(stderr, "%s\n", cmsg->error); - exit(EXIT_FAILURE); - } - - (void) thread_msg_reply(&cmsg->msg); - } - - /* FPS counter initialization */ - (void) strcpy(fpsstr, "000 fps"); - fpscnt = 0; - fpst = SDL_AddTimer(5000, fpscnt_callback, NULL); - - /* FPS management initialization */ - SDL_initFramerate(&fpsh); - SDL_setFramerate(&fpsh, 10); - - /* - * Main loop. - * Listen for messages and events and process them, while drawing - * frames. - * - * XXX There remain design choices to make here. - * We could only draw a frame once we received all data for a frame - * from the server, thus waiting for the EndOfFrame message to draw, - * in which case we only need to bother drawing the current new - * received data. If we did this, we potentially could reduce the - * incomming asynchroneous messages rate so that a single one is sent - * once every messages for a frame were obtained. This method would - * probably be the most efficient, while allowing the client to update - * its display as fast as it is able to obtain the server information - * for a frame. However, this also means that for a very large world, - * if a map exists or such, there could be more data needing to be - * sent per frame, unless there also were in a frame the information - * to update the existing world information, which would be similar to - * the second method. Hmm since we need to send a difference packet - * for every object on the map, or to possibly resend their position, - * what could be done is having the server sending updates less - * frequently for that data. Although, we would need to make sure to - * avoid causing lag to the normal 10fps display when - * sending/processing large map packets. We possibly could - * intermingle some map information data per normal frame, having the - * server round-robin the map information among the clients in a - * distributed way? Say we have 50 connected clients, and that we - * want a map update rate of approximately 1 second interval, we could - * send the data among them at 50 / 10, meaning that we send each - * client update of 5 clients. With round robin this means that every - * client, 10 times per second are receiving enough update information - * so that within a second the whole map be updated. Of course, we - * would need not to break this when clients connect/disconnect. - * - * Or, we could instead maintain our own known world and display - * states and only update them via the messages received from the - * server, allowing us to maintain a steady frame rate (although this - * would also mean that no change could be made between certain - * frames). - */ - for (;;) { - thread_amsg_t *amsg; - SDL_Event ev; - - /* Process incomming server messages. */ - while ((amsg = (thread_amsg_t *)thread_msg_get(&main_port)) - != NULL) { - handle_recvmsg(amsg); - thread_amsg_destroy(amsg); - } - - /* Update gamepad current angle. */ - if (gamepad != NULL) - axis_angle_update(); - - /* - * Process incomming user events, sending corresponding - * messages to the server when appropriate. - */ - while (SDL_PollEvent(&ev)) - handle_uevent(&ev); - - /* - * XXX Probably to be done on the server, but such a step - * might still be necessary perhaps. - * Update objects position in preparation to draw the current - * frame. - */ - objects_update(); - - /* - * Draw current frame and wait until it's time to draw another - * frame. - */ - frame_draw(); - SDL_framerateDelay(&fpsh); - } - /* NOTREACHED */ - - thread_port_set_ring(&main_port, NULL); - thread_port_destroy(&main_port); - thread_ring_destroy(&main_ring); - - exit(EXIT_SUCCESS); -} - -static void -axis_angle_update(void) -{ - int angle, x, y; - - angle = joy_angle; - - /* On win32 -1 is reported for dead state instead of 0! */ - if ((x = SDL_JoystickGetAxis(gamepad, 0)) > 127) - x = 1; - else if (x < -128) - x = -1; - else - x = 0; - - if ((y = SDL_JoystickGetAxis(gamepad, 1)) > 127) - y = 1; - else if (y < -128) - y = -1; - else - y = 0; - - if (x == 0 && y == 0) - return; - if (x == 0 && y == -1) - angle = 0; - else if (x == 1 && y == -1) - angle = 45; - else if (x == 1 && y == 0) - angle = 90; - else if (x == 1 && y == 1) - angle = 135; - else if (x == 0 && y == 1) - angle = 180; - else if (x == -1 && y == 1) - angle = 225; - else if (x == -1 && y == 0) - angle = 270; - else if (x == -1 && y == -1) - angle = 315; - - if (joy_angle != angle) - joy_angle = angle; - - return; -} - -static void -handle_uevent(SDL_Event *ev) -{ - - if (ev->type == SDL_MOUSEMOTION) - return; - - /* - * Quit commands events. - */ - if ((ev->type == SDL_KEYDOWN && ev->key.keysym.sym == SDLK_ESCAPE) || - ev->type == SDL_QUIT) - exit(EXIT_FAILURE); - - /* - * Button events. joy_angle affects several of them. - * We currently ignore release events. - */ - if (ev->type == SDL_JOYBUTTONDOWN) { - int ch; - - if (ev->jbutton.state != 1) - return; - - switch (ev->jbutton.button) { - case 0: - /* Torp */ - if ((ch = Mix_PlayChannel(-1, snd_torp, 0)) != -1) - Mix_SetPosition(ch, joy_angle, 50); - fired = 1; - break; - case 1: - /* Secondary weapon */ - if ((ch = Mix_PlayChannel(-1, snd_hit, 0)) != -1) - Mix_SetPosition(ch, joy_angle, 50); - break; - case 2: - /* Navigation direction change */ - if (nav_angle != joy_angle) - nav_angle = joy_angle; - break; - case 3: - /* Special weapon */ - if ((ch = Mix_PlayChannel(-1, snd_explode, 0)) != -1) - Mix_SetPosition(ch, joy_angle, 50); - break; - case 4: - /* Toggle shields */ - shields = (shields == 0 ? 1 : 0); - Mix_PlayChannel(1, - (shields == 1 ? snd_shield : snd_unshield), 0); - break; - case 5: - /* Toggle cloak */ - cloaked = (cloaked == 0 ? 1 : 0); - Mix_PlayChannel(2, - (cloaked == 1 ? snd_cloak : snd_uncloak), 0); - break; - case 6: - /* Thrust accelerate */ - if (nav_thrust < max_thrust) - nav_thrust++; - break; - case 7: - /* Thrust decelerate */ - if (nav_thrust > 0) - nav_thrust--; - break; - } - - return; - } - - /* - * Keyboard events - */ - if (ev->type == SDL_KEYDOWN) { - int angle = joy_angle, ch; - - switch (ev->key.keysym.sym) { - /* Angle changes */ - case SDLK_i: - angle = 0; - break; - case SDLK_o: - angle = 45; - break; - case SDLK_l: - angle = 90; - break; - case SDLK_PERIOD: - angle = 135; - break; - case SDLK_COMMA: - angle = 180; - break; - case SDLK_m: - angle = 225; - break; - case SDLK_j: - angle = 270; - break; - case SDLK_u: - angle = 315; - break; - /* Navigation change */ - case SDLK_d: - /* Direction */ - if (nav_angle != joy_angle) - nav_angle = joy_angle; - break; - case SDLK_z: - /* Thrust up */ - if (nav_thrust < max_thrust) - nav_thrust++; - break; - case SDLK_a: - /* Thrust down */ - if (nav_thrust > 0) - nav_thrust--; - break; - /* Weapons */ - case SDLK_SPACE: - /* Torp */ - if ((ch = Mix_PlayChannel(-1, snd_torp, 0)) != -1) - Mix_SetPosition(ch, joy_angle, 50); - fired = 1; - break; - case SDLK_f: - /* Secondary weapon */ - if ((ch = Mix_PlayChannel(-1, snd_hit, 0)) != -1) - Mix_SetPosition(ch, joy_angle, 50); - break; - case SDLK_g: - /* Special weapon */ - if ((ch = Mix_PlayChannel(-1, snd_explode, 0)) != -1) - Mix_SetPosition(ch, joy_angle, 50); - break; - /* Toggles */ - case SDLK_s: - /* Shield */ - shields = (shields == 0 ? 1 : 0); - Mix_PlayChannel(1, - (shields == 1 ? snd_shield : snd_unshield), 0); - break; - case SDLK_w: - /* Cloak */ - cloaked = (cloaked == 0 ? 1 : 0); - Mix_PlayChannel(2, - (cloaked == 1 ? snd_cloak : snd_uncloak), 0); - break; - default: - break; - } - - if (joy_angle != angle) - joy_angle = angle; - return; - } - -} - -static void -handle_recvmsg(thread_amsg_t *amsg) -{ - - /* XXX */ - if (amsg->size != -1) - (void) fwrite(amsg->data, amsg->size, 1, stdout); - else { - (void) fprintf(stderr, "Error reading from server socket\n"); - exit(EXIT_FAILURE); - } -} - -/* - * These will normally occur on the server. - * However, it's nice for temporary testing. - */ -static void -objects_update(void) -{ - - /* - * XXX - * Adjust current thrust according to ship's acceleration/deceleration - * speeds in order to eventually reach nav_thrust. It seems that - * we need floating point variables to perform this. - */ - if (cur_nav_thrust != nav_thrust) { - if (cur_nav_thrust < nav_thrust) - cur_nav_thrust++; - else - cur_nav_thrust--; - } - - /* - * XXX - * For now we also should use cos/sin and allow the ship to move - * around in its current direction at its current thrust. - * We should also bounce when reaching the borders for now, - * by reversing its angle. - */ - if (cur_nav_thrust != 0) { - nav_pos_x = VECTOR_X(nav_pos_x, cur_nav_angle, - cur_nav_thrust + 1); - nav_pos_y = VECTOR_Y(nav_pos_y, cur_nav_angle, - cur_nav_thrust + 1); - } - - /* - * XXX - * We need to rotate cur_nav_angle in the shortest direction to - * nav_angle, while making sure to always have angles in the range - * 0 - 359 only. Moreover, if the degrees stepped are too large - * to exactly reach nav_angle, we want to just reach it at the last - * step. To properly test the later condition, we step relatively - * to the current thrust. - */ - if (cur_nav_angle != nav_angle) { - if (cur_nav_angle < nav_angle) { - if ((cur_nav_angle += (max_thrust + 10) - - cur_nav_thrust) > nav_angle) - cur_nav_angle = nav_angle; - } else { - if ((cur_nav_angle -= (max_thrust + 10) - - cur_nav_thrust) < nav_angle) - cur_nav_angle = nav_angle; - } - } -} - -static void -frame_clear(void) -{ - uint32_t *ptr, *tptr; - - /* - for (ptr = screen_surface->pixels, - tptr = &ptr[screen_width * screen_height]; - ptr < tptr; ) { - *ptr++ = 0x00000000; - *ptr++ = 0x00000000; - *ptr++ = 0x00000000; - *ptr++ = 0x00000000; - *ptr++ = 0x00000000; - *ptr++ = 0x00000000; - *ptr++ = 0x00000000; - *ptr++ = 0x00000000; - } - */ - - for (ptr = screen_surface->pixels, - tptr = &ptr[screen_width * screen_height]; - ptr < tptr; ptr = &ptr[8]) { - ptr[0] = 0x00000000; - ptr[1] = 0x00000000; - ptr[2] = 0x00000000; - ptr[3] = 0x00000000; - ptr[4] = 0x00000000; - ptr[5] = 0x00000000; - ptr[6] = 0x00000000; - ptr[7] = 0x00000000; - } -} - -static void -frame_draw(void) -{ - - frame_clear(); - /* SDL_FillRect(screen_surface, NULL, 0); */ - - surface_blit_angle(rship, nav_pos_x, nav_pos_y, cur_nav_angle, 0); - if (cloaked) - filledCircleRGBA(screen_surface, nav_pos_x, nav_pos_y, 25, - 0x00, 0x00, 0x00, 0x80); - if (shields) { - aacircleRGBA(screen_surface, nav_pos_x, nav_pos_y, 25, - 0xe0, 0xe0, 0x20, 0x60); - filledCircleRGBA(screen_surface, nav_pos_x, nav_pos_y, 25, - 0xf0, 0xf0, 0x30, 0x40); - } - aalineRGBA(screen_surface, - VECTOR_X(nav_pos_x, cur_nav_angle, 40), - VECTOR_Y(nav_pos_y, cur_nav_angle, 40), - VECTOR_X(nav_pos_x, cur_nav_angle, 60), - VECTOR_Y(nav_pos_y, cur_nav_angle, 60), - 0xff, 0xff, 0xff, 0x80); - - if (fired) { - int i; - - fired = 0; - for (i = 0; i < 255; i++) { - pixelRGBA(screen_surface, - VECTOR_X(nav_pos_x, joy_angle, i), - VECTOR_Y(nav_pos_y, joy_angle, i), - 0xff, 0xff, 0x00, 0xff - i); - } - } - - { - char str[256]; - - (void) snprintf(str, 255, - "Thrust: %02d/%02d (%02d), " - "Angle: %03d (%03d), " - "Position: (%03d,%03d), " - "Shields: %3s, " - "Cloak: %3s", - cur_nav_thrust, max_thrust, nav_thrust, - cur_nav_angle, nav_angle, - nav_pos_x, nav_pos_y, - (shields == 1 ? "On" : "Off"), - (cloaked == 1 ? "On" : "Off")); - font_blit_string(font, 17, 17, str, - 0x00, 0x00, 0x00, 0xff); - font_blit_string(font, 16, 16, str, - 0xff, 0xff, 0xff, 0xff); - } - - font_blit_string(font, 17, 741, fpsstr, 0x00, 0x00, 0x00, 0xff); - font_blit_string(font, 16, 740, fpsstr, 0xff, 0xff, 0xff, 0xff); - - (void) SDL_Flip(screen_surface); - fpscnt++; -} - -static SDL_Surface * -bmp_load_key(void *data, size_t size) -{ - SDL_RWops *rwo; - SDL_Surface *s; - Uint32 c; - void *ndata; - size_t nsize; - - decode(&ndata, &nsize, data, size); - - if ((rwo = SDL_RWFromMem(ndata, nsize)) == NULL) - goto err; - if ((s = IMG_LoadBMP_RW(rwo)) == NULL) - goto err; - SDL_FreeRW(rwo); - - c = SDL_MapRGB(s->format, 0, 0, 0); - if (SDL_SetColorKey(s, SDL_SRCCOLORKEY | SDL_RLEACCEL, c) == -1) - goto err; - - return s; - -err: - (void) fprintf(stderr, "bmp_load_key() - %s\n", SDL_GetError()); - exit(EXIT_FAILURE); -} - -static int -surface_blit_angle(SDL_Surface *s, int x, int y, double a, int clean) -{ - SDL_Surface *t; - SDL_Rect d; - int r; - - ASSERT(a > -1 && a < 360); - - if (clean) { - int cx, cy, c; - - cx = s->w / 2 + 2; - cy = s->h / 2 + 2; - c = (cx >= cy ? cx : cy); - (void) boxRGBA(screen_surface, x - c, y - c, - x + c, y + c, 0x00, 0x00, 0x00, 0xff); - } - - if ((t = rotozoomSurface(s, -a, 1.0, 1)) != NULL) { - d = (SDL_Rect){x - (t->w / 2), y - (t->h / 2), 0, 0}; - r = SDL_BlitSurface(t, NULL, screen_surface, &d); - SDL_FreeSurface(t); - } else - r = -1; - - return r; -} - -static Mix_Chunk * -sample_load(void *data, size_t size) -{ - SDL_RWops *rwo; - Mix_Chunk *c; - void *ndata; - size_t nsize; - - decode(&ndata, &nsize, data, size); - - if ((rwo = SDL_RWFromMem(ndata, nsize)) == NULL) - goto err; - - if ((c = Mix_LoadWAV_RW(rwo, 0)) == NULL) - goto err; - - SDL_FreeRW(rwo); - return c; - -err: - (void) fprintf(stderr, "sample_load() - %s\n", SDL_GetError()); - exit(EXIT_FAILURE); -} - -static struct font * -font_load(void *data, size_t size, int width, int height) -{ - struct font *font; - void *ndata; - size_t nsize; - - decode(&ndata, &nsize, data, size); - - if ((font = malloc(sizeof(struct font))) == NULL) - goto err; - - font->data = ndata; - font->w = width; - font->h = height; - font->size = nsize; - return font; - -err: - (void) fprintf(stderr, "font_load()\n"); - exit(EXIT_FAILURE); -} - -static void -font_blit_string(struct font *font, int x, int y, const char *str, - uint8_t r, uint8_t g, uint8_t b, uint8_t a) -{ - - (void) gfxPrimitivesSetFont(font->data, font->w, font->h); - (void) stringRGBA(screen_surface, x, y, str, r, g, b, a); -} - -/* ARGSUSED */ -static Uint32 -fpscnt_callback(Uint32 interval, void *args) -{ - - (void) sprintf(fpsstr, "%03d fps", fpscnt / 5); - fpscnt = 0; - - return interval; -} - -/* Initialize trigonometric tables */ -static void -trig_init(void) -{ - int i; - double f; - - for (i = 0; i < 360; i++) { - f = ((2 * M_PI) / 360) * (i - 90); - sin_table[i] = sin(f); - cos_table[i] = cos(f); - } -} diff --git a/tests/sdl-client/main.h b/tests/sdl-client/main.h deleted file mode 100644 index 12b8cb2..0000000 --- a/tests/sdl-client/main.h +++ /dev/null @@ -1,29 +0,0 @@ -/* $Id: main.h,v 1.3 2006/05/19 09:13:42 mmondor Exp $ */ - -/* - * Copyright (c) 2006, Matthew Mondor - * ALL RIGHTS RESERVED. - */ - -/* - * Exported resources by the main program, mainly for thread modules. - */ - - - -#ifndef MAIN_H -#define MAIN_H - - - -#include - -#include - - - -extern thread_port_t main_port; - - - -#endif diff --git a/tests/sdl-client/ogg/README b/tests/sdl-client/ogg/README deleted file mode 100644 index 90d493c..0000000 --- a/tests/sdl-client/ogg/README +++ /dev/null @@ -1,5 +0,0 @@ -For how, place any ogg track that you like as 1.ogg under this directory. -Eventually there shall be some of my own composed music, when I have time -and care enough to add it. - -Matt diff --git a/tests/sdl-client/packets.c b/tests/sdl-client/packets.c deleted file mode 100644 index 0a46921..0000000 --- a/tests/sdl-client/packets.c +++ /dev/null @@ -1,327 +0,0 @@ -/* $Id: packets.c,v 1.1 2006/05/19 09:13:42 mmondor Exp $ */ - -/* - * Copyright (c) 2006, Matthew Mondor - * ALL RIGHTS RESERVED. - */ - -/* - * To validate a received packet, we'll make sure that it's larger than - * sizeof(int), and that the first integer consists of a valid expected packet - * type ID within range. If so, we verify if packet length really corresponds - * to the packet type, and then can interpret the packet information. - * An actual network packet may contain a number of these packets. - * We thus must be able to efficiently determine each packet's length. - * Since we're using TCP, it should be enough. However, on the server side - * especially, proper sanity checking on input must be done to avoid crashing - * the server because of unexpected data processing. - */ - - - -#include - -#include -#include - -#include "packets.h" -#include "client.h" -#include "sendq.h" -#include "conf.h" - - - -static int cpacket_auth_handler(client_t *, uint16_t *); -static int cpacket_ping_handler(client_t *, uint16_t *); -static int cpacket_pong_handler(client_t *, uint16_t *); -static int cpacket_direction_handler(client_t *, uint16_t *); -static int cpacket_thrust_handler(client_t *, uint16_t *); -static int cpacket_torp_handler(client_t *, uint16_t *); -static int cpacket_quit_handler(client_t *, uint16_t *); - - - -static struct packet_index cpacket_index[CPACKET_MAX] = { - {sizeof(struct cpacket_auth), cpacket_auth_handler}, - {sizeof(struct cpacket_ping), cpacket_ping_handler}, - {sizeof(struct cpacket_pong), cpacket_pong_handler}, - {sizeof(struct cpacket_direction), cpacket_direction_handler}, - {sizeof(struct cpacket_thrust), cpacket_thrust_handler}, - {sizeof(struct cpacket_torp), cpacket_torp_handler}, - {sizeof(struct cpacket_quit), cpacket_quit_handler} -}; - - - -void -update(void) -{ - node_t *nod, *next; - client_t *c; - uint8_t *data; - size_t size, off; - - for (nod = DLIST_TOP(&clients_list); nod != NULL; nod = next) { - next = DLIST_NEXT(nod); - c = (client_t *)nod; - - if (c->todestroy || c->toclose) - continue; - - /* Reset ping request throttling flag */ - c->askedping = 0; - - /* First process incomming packets */ - recvq_content(&c->recvq, &data, &size); - for (off = 0; off < size; ) { - int16_t *id; - - /* - * Verify packet type ID. It already has been - * converted to host endian byte order, unlike other - * packet fields. - */ - id = (int16_t *)&data[off]; - *id = BYTEORDER_HOST16(*id); - if (*id < 0 || *id > CPACKET_MAX) { - client_destroy_mark(c); - break; - } - - /* There must be enough data for packet size */ - if (size - off < cpacket_index[*id].size) { - /* - * Kill authentucated client if other packets - * than CPACKET_AUTH - */ - if (!c->authenticated && *id != CPACKET_AUTH) - goto k; - - /* - * XXX We'll need to also do special - * processing for chat packets, since they'll - * have arbitrary length. They'll still need - * to be 16-bit aligned, too. - */ - - /* Kill client on packet processing error */ - if (cpacket_index[*id].handler(c, - (uint16_t *)off) == -1) - goto k; - } else - goto k; - - /* Process next packet if any */ - off += cpacket_index[*id].size; - continue; - -k: - client_destroy_mark(c); - break; - } - - /* - * XXX I don't like having to run the clients list all over, - * to then run through all objects (including clients) to - * update them, and then yet again through all objects to send - * updates to all clients. - */ - - /* Run game frame on all objects */ - DLIST_FOREACH(&clients_list, nod) { - client_t *c = (client_t *)nod; - - /* - if (!c->authenticated) - continue; - */ - - /* - * First update direction and thrust. - * XXX I could do a small function or macro to deal - * with similar situations, i.e. - * change_update(int goal, int *current, int steps); - */ - if (c->object.direction < c->object.i_direction) - c->object.direction++; - else if (c->object.direction > c->object.i_direction) - c->object.direction--; - if (c->object.thrust < c->object.i_thrust) - c->object.thrust++; - else if (c->object.thrust > c->object.i_thrust) - c->object.thrust--; - - /* - * Translate object x/y position according to - * trust/direction. When reaching borders we simply - * bounce for now. - * (I need to review my trigonometry a bit again :) - */ - /* XXX */ - c->object.x += 1 - (random() & 2); - if (c->object.x < 0) - c->object.x = 0; - else if (c->object.x > WORLD_X_MAX - 1) - c->object.x = WORLD_X_MAX - 1; - c->object.y += 1 - (random() & 2); - if (c->object.y < 0) - c->object.y = 0; - else if (c->object.y > WORLD_Y_MAX - 1) - c->object.y = WORLD_Y_MAX - 1; - } - - /* Send update frame information packets */ - DLIST_FOREACH(&clients_list, nod) { - client_t *c = (client_t *)nod; - node_t *nod2; - - /* - if (!c->authenticated) - continue; - */ - - DLIST_FOREACH(&clients_list, nod2) { - if (spacket_position_send(c, - &((client_t *)nod2)->object) == -1) - break; - } - (void) sendq_flush(&c->sendq, -1); - } - } -} - - -int -spacket_auth_send(client_t *c) -{ - struct spacket_auth p; - - p.packet_type = BYTEORDER_NETWORK16(SPACKET_AUTH); - mm_memclr(p.string, 32); - mm_strncpy(p.string, SERVER_STRING, 31); - p.protocol_version = BYTEORDER_NETWORK16(SERVER_VERSION); - /* XXX */ - - return client_write(c, (uint8_t *)&p, sizeof(p), 0); -} - -int -spacket_pong_send(client_t *c) -{ - struct spacket_pong p; - - p.packet_type = BYTEORDER_NETWORK16(SPACKET_PONG); - - return client_write(c, (uint8_t *)&p, sizeof(p), 0); -} - -int -spacket_position_send(client_t *c, object_t *o) -{ - struct spacket_position p; - - p.packet_type = BYTEORDER_NETWORK16(SPACKET_POSITION); - p.object_id = p.object_type = BYTEORDER_NETWORK16(0); /* XXX */ - p.x = BYTEORDER_NETWORK16(o->x); - p.y = BYTEORDER_NETWORK16(o->y); - p.direction - BYTEORDER_NETWORK16(o->direction); - - return client_write(c, (uint8_t *)&p, sizeof(p), 1); -} - - -/* Note that only the packet_type field is endian converted yet. */ - -static int -cpacket_auth_handler(client_t *c, uint16_t *ptr) -{ - struct cpacket_auth *p = (struct cpacket_auth *)ptr; - - p->protocol_version = BYTEORDER_HOST16(p->protocol_version); - - if (c->authenticated || p->protocol_version < SERVER_VERSION) - return -1; - - /* - * XXX For now. Eventually also check user/password - * We could use APOP-like authentication where server sends random - * data as part of the authentication greeting/request, and expect - * client to append password to random data and send hashed result. - * We also should do user concurrency checking here. - */ - c->authenticated = 1; - - return 0; -} - -/* - * Note: Following function is never used, since pings are handled in the - * kqueue main loop code. - */ -/* ARGSUSED */ -static int -cpacket_ping_handler(client_t *c, uint16_t *ptr) -{ - /* NOOP */ - - return 0; -} - -static int -cpacket_pong_handler(client_t *c, uint16_t *ptr) -{ -/* struct cpacket_pong *p = (struct cpacket_pong *)ptr;*/ - - /* XXX */ - - return 0; -} - -static int -cpacket_direction_handler(client_t *c, uint16_t *ptr) -{ - struct cpacket_direction *p = (struct cpacket_direction *)ptr; - - p->direction = BYTEORDER_HOST16(p->direction); - - /* Radians */ - if (p->direction < 0 || p->direction > 1000) - return -1; - - c->object.i_direction = p->direction; - - return 0; -} - -static int -cpacket_thrust_handler(client_t *c, uint16_t *ptr) -{ - struct cpacket_thrust *p = (struct cpacket_thrust *)ptr; - - p->thrust = BYTEORDER_HOST16(p->thrust); - - if (p->thrust < 0 || p->thrust > 20) - return -1; - - c->object.i_thrust = p->thrust; - - return 0; -} - -static int -cpacket_torp_handler(client_t *c, uint16_t *ptr) -{ -/* struct cpacket_torp *p = (struct cpacket_torp *)ptr;*/ - - /* XXX */ - - return 0; -} - -/* ARGSUSED */ -static int -cpacket_quit_handler(client_t *c, uint16_t *ptr) -{ - - return -1; -} diff --git a/tests/sdl-client/packets.h b/tests/sdl-client/packets.h deleted file mode 100644 index 4db2425..0000000 --- a/tests/sdl-client/packets.h +++ /dev/null @@ -1,151 +0,0 @@ -/* $Id: packets.h,v 1.1 2006/05/19 09:13:42 mmondor Exp $ */ - -/* - * Copyright (c) 2006, Matthew Mondor - * ALL RIGHTS RESERVED. - */ - -/* - * All packets must begin with an int16_t packet_type. All fields of a packet - * must either be [u]int8_t, [u]int16_t, [u]int32+t or [u]int64_t. Moreover, - * all fields will be passed in network/big endian byte order when sent over - * the network. We could have used a union, but this would have resulted in - * larger, fixed sized packets wasting bandwidth. Since a server to client - * update will generally involve sending more than one such packet in a row, - * it is important to minimize their size. - * Note: If using 32-bit fields within a packet, make sure that they are - * 32-bit aligned. - */ - - - -#ifndef _PACKETS_H_ -#define _PACKETS_H_ - - - -#include - - - -/* - * For every server packet type we support, an index array will be used - * with this structure to call the associated handler function, which will - * perform sanity checking and process the packet, returning 0 on success or - * -1 on error. The size field will allow to perform sanity checking on - * data size prior to calling the handler function, as well as to increase the - * buffer pointer. - */ -struct packet_index { - size_t size; - int (*handler)(uint16_t *); -}; - - - -/* - * Server to client packets - */ - -enum spacket_types { - SPACKET_AUTH = 0, - SPACKET_PING, - SPACKET_PONG, - SPACKET_POSITION, - SPACKET_COLLISION, - SPACKET_MAX -}; - -/* Used to request client authentication and respond success/failure */ -struct spacket_auth { - int16_t packet_type; - int16_t protocol_version; - char string[32]; - /* XXX */ -} __attribute__((__packed__)); - -/* And for server to test idle connections */ -struct spacket_ping { - int16_t packet_type; -} __attribute__((__packed__)); - -/* And clients to test latency */ -struct spacket_pong { - int16_t packet_type; -} __attribute__((__packed__)); - -/* Object position/direction update to client */ -struct spacket_position { - int16_t packet_type; - int16_t object_id, object_type; - int16_t x, y, direction; -} __attribute__((__packed__)); - -/* Collision/detonation update to client */ -struct spacket_collision { - int16_t packet_type; - int16_t collision_type; - int16_t object1_id, object2_id; -} __attribute__((__packed__)); - - - -/* - * Client to server packets - */ - -enum cpacket_tyoes { - CPACKET_AUTH = 0, - CPACKET_PING, - CPACKET_PONG, - CPACKET_DIRECTION, - CPACKET_THRUST, - CPACKET_TORP, - CPACKET_QUIT, - CPACKET_MAX -}; - -/* Used to respond to server authentication request */ -struct cpacket_auth { - int16_t packet_type; - int16_t protocol_version; - char string[32]; - /* XXX */ -} __attribute__((__packed__)); - -/* To ping server for latency mesurement */ -struct cpacket_ping { - int16_t packet_type; -} __attribute__((__packed__)); - -/* To respond to server ping requests */ -struct cpacket_pong { - int16_t packet_type; -} __attribute__((__packed__)); - -/* Angle/direction change request */ -struct cpacket_direction { - int16_t packet_type; - int16_t direction; -} __attribute__((__packed__)); - -/* Speed change request */ -struct cpacket_thrust { - int16_t packet_type; - int16_t thrust; -} __attribute__((__packed__)); - -/* Torpedo fire request */ -struct cpacket_torp { - int16_t packet_type; - int16_t direction; -} __attribute__((__packed__)); - -/* Game quit request */ -struct cpacket_quit { - int16_t packet_type; -} __attribute__((__packed__)); - - - -#endif diff --git a/tests/sdl-client/pool.c b/tests/sdl-client/pool.c deleted file mode 100644 index 23d7d25..0000000 --- a/tests/sdl-client/pool.c +++ /dev/null @@ -1,417 +0,0 @@ -/* $Id: pool.c,v 1.3 2006/05/19 09:36:57 mmondor Exp $ */ - -/* - * Copyright (C) 2001-2006, Matthew Mondor - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Matthew Mondor. - * 4. The name of Matthew Mondor may not be used to endorse or promote - * products derived from this software without specific prior written - * permission. - * 5. Redistribution of source code may not be released under the terms of - * any GNU Public License derivate. - * - * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - - - -#include -#include -#include - -#include -#include -#include - - - -/* DEFINITIONS */ - -#define BPAGE_SIZE ((size_t)OALIGN_CEIL(sizeof(bpage_t), long)) - - - -/* STATIC FUNCTION PROTOTYPES */ - -static bpage_t * pool_page_create(const char *, pool_t *); -static bpage_t * pool_page_destroy(bpage_t *); - - - -/* STATIC FUNCTIONS */ - -/* - * Creates a new page of objects, and calls the constructor function for each - * object of the new page if needed. Returns NULL on failure, or the new - * bpage_t pointer on success. - */ -static bpage_t * -pool_page_create(const char *func, pool_t *pool) -{ - register size_t nodesize = pool->nodesize; - register uint8_t *ptr, *toptr; - register bpage_t *page; - register list_t *list; - - if ((page = pool->malloc(pool->pagesize)) == NULL) - return NULL; - - /* Initialize bpage_t */ - page->magic = MAGIC_PAGE; - page->pool = pool; - DLIST_INIT(&page->objects); - - /* - * Create all objects of that bpage_t, inserting them into it's list_t. - * If any object creation fails (it's optional construtor function can - * fail), destroy the page and return NULL. - */ - if (pool->create != NULL) { - for (ptr = toptr = (uint8_t *)page, ptr += BPAGE_SIZE, - toptr += pool->pagesize, list = &page->objects; - ptr + nodesize < toptr; ptr += nodesize) { - ((pnode_t *)ptr)->magic = 0; - ((pnode_t *)ptr)->page = page; - if (pool->create((pnode_t *)ptr) != 0) - return pool_page_destroy(page); - DLIST_APPEND(list, (node_t *)ptr); - } - } else { - for (ptr = toptr = (uint8_t *)page, ptr += BPAGE_SIZE, - toptr += pool->pagesize, list = &page->objects; - ptr + nodesize < toptr; ptr += nodesize) { - ((pnode_t *)ptr)->magic = 0; - ((pnode_t *)ptr)->page = page; - DLIST_APPEND(list, (node_t *)ptr); - } - } - - return page; -} - -/* - * Destroys a page previously created using pool_page_create(), calling the - * destructor function for each object of the page if necessary. - * Returns NULL. - */ -static bpage_t * -pool_page_destroy(bpage_t *page) -{ - register pnode_t *pnode; - register void (*destroy)(pnode_t *); - - if ((destroy = (page->pool->destroy)) != NULL) { - /* We need to destroy all objects */ - DLIST_FOREACH(&page->objects, pnode) - destroy(pnode); - } - - page->pool->free(page); - - return NULL; -} - - - -/* PUBLIC FUNCTIONS */ - -/* - * Initializes a memory pool for efficient management of fixed sized nodes. - * Returns 0 on success or -1 on failure. - */ -int -pool_init(pool_t *pool, const char *label, void *(*mallocfunc)(size_t), - void (*freefunc)(void *), int (*create)(pnode_t *), - void (*destroy)(pnode_t *), size_t nodesize, uint32_t nodesperpage, - uint32_t minpages, uint32_t maxpages) -{ - int ok = -1; - register size_t ns, ps; - - ASSERT(!POOL_VALID(pool)); - ASSERT(pool != NULL && mallocfunc != NULL && freefunc != NULL && - ((create != NULL && destroy != NULL) || - (void *)create == (void *)destroy) && - nodesize != 0 && nodesperpage > 0); - - ns = (size_t)OALIGN_CEIL(nodesize, long); - ps = (BPAGE_SIZE + ((nodesperpage + 1) * ns)); - - pool->magic = 0; - pool->label = label; - pool->malloc = mallocfunc; - pool->free = freefunc; - pool->create = create; - pool->destroy = destroy; - pool->nodesize = ns; - pool->minpages = minpages; - pool->maxpages = maxpages; - pool->nodesperpage = nodesperpage; - pool->pagesize = ps; - pool->avgtotal = pool->avgcnt = minpages; - DLIST_INIT(&pool->pages); - DLIST_INIT(&pool->fpages); - DLIST_INIT(&pool->epages); - - /* - * Allocate minimum number of pages, if needed. We insert them into - * the empty pages list, but minpages will be honored as such to - * never free pages below it. - */ - for (; minpages > 0; minpages--) { - register bpage_t *p; - - if ((p = pool_page_create("pool_init", pool)) == NULL) - break; - DLIST_APPEND(&pool->epages, (node_t *)p); - } - - /* Validate this pool */ - pool->magic = MAGIC_POOL; - - /* - * Have we failed to allocate any needed pages? If so, destroy pool - * and return -1. - */ - if (minpages == 0) - ok = 0; - else if (minpages < pool->minpages) - (void) pool_destroy(pool); - - return ok; -} - - -/* - * Destroys a pool which previously has been created by pool_init(), and frees - * any resources it uses (all the nodes it created become invalid). - */ -void -pool_destroy(pool_t *pool) -{ - register bpage_t *p, *t; - - ASSERT(POOL_VALID(pool)); - - /* Destroy all pages of all lists */ - for (p = DLIST_TOP(&pool->pages); p != NULL; p = t) { - t = DLIST_NEXT(p); - (void) pool_page_destroy(p); - } - for (p = DLIST_TOP(&pool->fpages); p != NULL; p = t) { - t = DLIST_NEXT(p); - (void) pool_page_destroy(p); - } - for (p = DLIST_TOP(&pool->epages); p != NULL; p = t) { - t = DLIST_NEXT(p); - (void) pool_page_destroy(p); - } - - /* Invalidate pool_t */ - pool->magic = 0; -} - - -/* - * Allows to very efficiently allocate a single node from the specified pool, - * optionally initializing it's memory to zeros. Returns the newly allocated - * node pointer on success, or NULL on error. - */ -pnode_t * -pool_alloc(pool_t *pool, int zero) -{ - pnode_t *pnode = NULL; - register bpage_t *page; - - ASSERT(POOL_VALID(pool)); - ASSERT(!zero || pool->create == NULL); - - /* Are there any partially used pages? */ - if ((page = DLIST_TOP(&pool->pages)) == NULL) { - /* - * No, we thus attempt to move a page from our empty pages - * cache back into our usable pages list. The order of the - * usable page list becomes irrelevant at DLIST_SWAP() since - * it will be the only node. - */ - if ((page = DLIST_TOP(&pool->epages)) == NULL) { - /* - * No more free pages in our cache neither. - * If maxpages is set and not yet reached, we need - * to create a new page. If we can't, return NULL - * for error. - */ - if (pool->maxpages == 0 || - DLIST_NODES(&pool->fpages) < pool->maxpages) { - if ((page = pool_page_create("pool_alloc", - pool)) == NULL) - return NULL; - DLIST_APPEND(&pool->pages, (node_t *)page); - } else - return NULL; - } else - DLIST_SWAP(&pool->pages, &pool->epages, (node_t *)page, - 0); - } - - /* - * now points to a page we know we can at least get a free - * object node from. Obtain one and unlink it. - */ - pnode = DLIST_TOP(&page->objects); - DLIST_UNLINK(&page->objects, (node_t *)pnode); - - /* - * Have we obtained the last available object from this page? - * If so, move the page to the full pages list. The order of the - * full pages list nodes is irrelevant, since we don't know which - * of those pages are more likely to be swapped to the usable - * pages list first, and we won't run through that list, except at - * pool_destroy(). - */ - if (DLIST_NODES(&page->objects) == 0) - DLIST_SWAP(&pool->fpages, &pool->pages, (node_t *)page, 0); - - /* - * If requested, zero object. This is stupid if a constructor and - * destructor are used, but can be useful otherwise. - */ - if (zero) { - page = pnode->page; - (void) memset(pnode, 0, pool->nodesize); - pnode->page = page; - } - - /* Mark this node as a valid allocated object */ - pnode->magic = MAGIC_PNODE; - - return pnode; -} - - -/* - * Efficiently frees the specified node back to the pool it was allocated from. - * Returns NULL. Uses heuristics keeping statistics to determine when to - * actually shrink the memory blocks internally used by the pool, so that - * frequently growing and shrinking pools will remain large for scalability. - * This also makes a big difference when constructors and destructors are used - * and need to execute a significant amount of code. - */ -pnode_t * -pool_free(pnode_t *pnode) -{ - register bpage_t *page = pnode->page; - register pool_t *pool = page->pool; - register uint32_t count; - - ASSERT(PNODE_VALID(pnode)); - - /* Invalidate object */ - pnode->magic = 0; - - /* - * Record how many nodes there currently are in our page's object - * list, to be used later on - */ - count = DLIST_NODES(&page->objects); - - /* - * Add node back to it's page's object list. Insert it so that we - * favor reuse of recently used objects soon. - */ - DLIST_INSERT(&page->objects, (node_t *)pnode); - - /* - * Was this page full before we inserted our node? If so, page is in - * full pages list. Move it to the usable pages list. Insert it to - * favor reuse of recently used pages soon (this page will soon return - * back to the full pages list). - */ - if (count == 0) - DLIST_SWAP(&pool->pages, &pool->fpages, (node_t *)page, 1); - else { - /* - * Did we cause our node insertion to totally free back the - * page? If so, the page is on the usable free list, but move - * it back to the empty pages list. Insert it to favor reuse - * of recently used pages soon (this page will be the first to - * get used again when the usable pages list needs pages). - */ - if (++count == pool->nodesperpage) { - DLIST_SWAP(&pool->epages, &pool->pages, (node_t *)page, - 1); - } - } - - if ((pool->minpages < pool->maxpages) || - (pool->minpages == 0 && pool->maxpages == 0)) { - register int exceeding; - - /* - * This pool_t is allowed to shrink. Maintain average pages - * usage to prevent destroying our pages in the empty pages - * list unless we should. - */ - count = DLIST_NODES(&pool->pages) + DLIST_NODES(&pool->fpages); - pool->avgtotal += count; - pool->avgcnt++; - - /* - * Using * 8 here means that pool_free() needs to at least be - * called to release as much objects as can fit into eight full - * pages in order to execute the following block. But of - * course, this does not mean that eight pages will recently - * have been freed, since allocations probably also occured - * meanwhile. - */ - if (pool->avgcnt > pool->nodesperpage * 8) { - /* - * Do statistics suggest that we should shrink the - * pool? If so, free pages from our empty pages - * cache back to the system, destroying their objects - * if necessary. We'll make sure to at least leave a - * one page hysterisis for better performance. - */ - if ((exceeding = (count + DLIST_NODES(&pool->epages) - - 1) - (pool->avgtotal / pool->avgcnt)) > 0) { - register list_t *epages = &pool->epages; - - /* - * Preferably free pages which haven't been - * used recently. - */ - for (; exceeding > 0 && - (page = DLIST_BOTTOM(epages)) != NULL; - exceeding--) { - DLIST_UNLINK(epages, (node_t *)page); - (void) pool_page_destroy(page); - } - } - /* Reset statistics */ - pool->avgcnt = 1; - pool->avgtotal = count; - } - } - - return NULL; -} diff --git a/tests/sdl-client/pool.h b/tests/sdl-client/pool.h deleted file mode 100644 index f132c3c..0000000 --- a/tests/sdl-client/pool.h +++ /dev/null @@ -1,122 +0,0 @@ -/* $Id: pool.h,v 1.1 2006/04/27 10:59:19 mmondor Exp $ */ - -/* - * Copyright (C) 2001-2006, Matthew Mondor - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Matthew Mondor. - * 4. The name of Matthew Mondor may not be used to endorse or promote - * products derived from this software without specific prior written - * permission. - * 5. Redistribution of source code may not be released under the terms of - * any GNU Public License derivate. - * - * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - - - - -#ifndef MPOOL_H -#define MPOOL_H - - - -#include - -#include - - - -/* Useful to o-align a value relative to v (sizes and pointers) */ -#define OALIGN_CEIL(v, o) \ - ((((size_t)(v)) + (sizeof(o)) - 1) / (sizeof(o)) * (sizeof(o))) -#define OALIGN_FLOOR(v, o) ((size_t)(v) - (((size_t)(v) % sizeof(o)))) - -/* Useful to byte align a value with supplied size */ -#define BALIGN_CEIL(v, s) ((((size_t)(v)) + (s) - 1) / (s) * (s)) -#define BALIGN_FLOOR(v, s) ((size_t)(v) - (((size_t)(v) % (s)))) - - - -typedef struct pnode pnode_t; -typedef struct bpage bpage_t; -typedef struct pool pool_t; - - - -#define MAGIC_POOL 0x504f4f4c /* POOL */ -#define MAGIC_PAGE 0x50414745 /* PAGE */ -#define MAGIC_PNODE 0x504e4f44 /* PNOD */ - -#define POOL_VALID(p) ((p) != NULL && (p)->magic == MAGIC_POOL) -#define PNODE_VALID(p) ((p) != NULL && (p)->magic == MAGIC_PNODE && \ - (p)->page != NULL && (p)->page->magic == MAGIC_PAGE && \ - (p)->page->pool != NULL && (p)->page->pool->magic == MAGIC_POOL) - - - -/* Header structure of internally allocated/prepared page/blocks */ -struct bpage { - node_t node; - uint32_t magic; - pool_t *pool; - list_t objects; -}; - -/* Header structure of individual pool objects */ -struct pnode { - node_t node; - uint32_t magic; - bpage_t *page; -}; - -/* Pool control structure */ -struct pool { - pnode_t node; /* Hey, we never know, a pool_t of pool_t */ - uint32_t magic; - const char *label; - void *(*malloc)(size_t); - void (*free)(void *); - int (*create)(pnode_t *); - void (*destroy)(pnode_t *); - size_t nodesize, pagesize; - uint32_t minpages, maxpages, nodesperpage; - uint32_t avgtotal, avgcnt; - /* Usable pages, totally full/busy pages, totally empty/free pages */ - list_t pages, fpages, epages; -}; - - - -/* Public API prototypes */ -extern int pool_init(pool_t *, const char *, void *(*)(size_t), - void (*)(void *), int (*)(pnode_t *), - void (*)(pnode_t *), size_t, - uint32_t, uint32_t, uint32_t); -extern void pool_destroy(pool_t *); -extern pnode_t * pool_alloc(pool_t *, int); -extern pnode_t * pool_free(pnode_t *); - - - -#endif diff --git a/tests/sdl-client/rawobjs.h b/tests/sdl-client/rawobjs.h deleted file mode 100644 index 89d59bc..0000000 --- a/tests/sdl-client/rawobjs.h +++ /dev/null @@ -1,79 +0,0 @@ -/* $Id: rawobjs.h,v 1.4 2006/05/10 00:48:40 mmondor Exp $ */ - -/* - * Copyright (c) 2006, Matthew Mondor - * ALL RIGHTS RESERVED. - */ - -/* XXX This file should probably be auto-generated */ - - - -#ifndef RAWOBJS_H -#define RAWOBJS_H - - - -/* - * On win32, binary objects must be referenced without the underscore prefix. - * Let's then define a few aliases. - */ -#ifdef WIN32 - -#define _binary_bmp_FedCA_bmp_enc_size binary_bmp_FedCA_bmp_enc_size -#define _binary_bmp_FedCA_bmp_enc_start binary_bmp_FedCA_bmp_enc_start -#define _binary_bmp_RomDD_bmp_enc_size binary_bmp_RomDD_bmp_enc_size -#define _binary_bmp_RomDD_bmp_enc_start binary_bmp_RomDD_bmp_enc_start - -#define _binary_wav_nt_cloaked_wav_enc_size binary_wav_nt_cloaked_wav_enc_size -#define _binary_wav_nt_cloaked_wav_enc_start binary_wav_nt_cloaked_wav_enc_start -#define _binary_wav_nt_explosion_other_wav_enc_size binary_wav_nt_explosion_other_wav_enc_size -#define _binary_wav_nt_explosion_other_wav_enc_start binary_wav_nt_explosion_other_wav_enc_start -#define _binary_wav_nt_fire_torp_other_wav_enc_size binary_wav_nt_fire_torp_other_wav_enc_size -#define _binary_wav_nt_fire_torp_other_wav_enc_start binary_wav_nt_fire_torp_other_wav_enc_start -#define _binary_wav_nt_plasma_hit_wav_enc_size binary_wav_nt_plasma_hit_wav_enc_size -#define _binary_wav_nt_plasma_hit_wav_enc_start binary_wav_nt_plasma_hit_wav_enc_start -#define _binary_wav_nt_shield_down_wav_enc_size binary_wav_nt_shield_down_wav_enc_size -#define _binary_wav_nt_shield_down_wav_enc_start binary_wav_nt_shield_down_wav_enc_start -#define _binary_wav_nt_shield_up_wav_enc_size binary_wav_nt_shield_up_wav_enc_size -#define _binary_wav_nt_shield_up_wav_enc_start binary_wav_nt_shield_up_wav_enc_start -#define _binary_wav_nt_uncloak_wav_enc_size binary_wav_nt_uncloak_wav_enc_size -#define _binary_wav_nt_uncloak_wav_enc_start binary_wav_nt_uncloak_wav_enc_start - -#define _binary_fnt_7x13_fnt_enc_start binary_fnt_7x13_fnt_enc_start -#define _binary_fnt_7x13_fnt_enc_size binary_fnt_7x13_fnt_enc_size - -#endif - - - -/* - * And export symbols to other modules - */ - -extern void *_binary_bmp_FedCA_bmp_enc_size; -extern void *_binary_bmp_FedCA_bmp_enc_start; -extern void *_binary_bmp_RomDD_bmp_enc_size; -extern void *_binary_bmp_RomDD_bmp_enc_start; - -extern void *_binary_wav_nt_cloaked_wav_enc_size; -extern void *_binary_wav_nt_cloaked_wav_enc_start; -extern void *_binary_wav_nt_explosion_other_wav_enc_size; -extern void *_binary_wav_nt_explosion_other_wav_enc_start; -extern void *_binary_wav_nt_fire_torp_other_wav_enc_size; -extern void *_binary_wav_nt_fire_torp_other_wav_enc_start; -extern void *_binary_wav_nt_plasma_hit_wav_enc_size; -extern void *_binary_wav_nt_plasma_hit_wav_enc_start; -extern void *_binary_wav_nt_shield_down_wav_enc_size; -extern void *_binary_wav_nt_shield_down_wav_enc_start; -extern void *_binary_wav_nt_shield_up_wav_enc_size; -extern void *_binary_wav_nt_shield_up_wav_enc_start; -extern void *_binary_wav_nt_uncloak_wav_enc_size; -extern void *_binary_wav_nt_uncloak_wav_enc_start; - -extern void *_binary_fnt_7x13_fnt_enc_start; -extern void *_binary_fnt_7x13_fnt_enc_size; - - - -#endif diff --git a/tests/sdl-client/screen.c b/tests/sdl-client/screen.c deleted file mode 100644 index 4cf25f2..0000000 --- a/tests/sdl-client/screen.c +++ /dev/null @@ -1,65 +0,0 @@ -/* $Id: screen.c,v 1.11 2006/05/08 21:05:17 mmondor Exp $ */ - -/* - * Copyright (c) 2006, Matthew Mondor - * ALL RIGHTS RESERVED. - */ - -/* - * Display related initialization. - */ - - - -#include -#include - -#include - -#include -#include - - - -#define SCREEN_WIDTH 1024 -#define SCREEN_HEIGHT 768 - - - -SDL_Surface *screen_surface; -int screen_width, screen_height; -SDL_Joystick *gamepad; - - - -void -screen_init(void) -{ - - if (SDL_Init(SDL_INIT_EVERYTHING) == -1) { - (void) fprintf(stderr, "main() - SDL_Init() - %s\n", - SDL_GetError()); - exit(EXIT_FAILURE); - } - if ((screen_surface = SDL_SetVideoMode(SCREEN_WIDTH, SCREEN_HEIGHT, 32, - SDL_DOUBLEBUF | SDL_FULLSCREEN)) == NULL) { - (void) fprintf(stderr, "main() - SDL_SetVideoMode() - %s\n", - SDL_GetError()); - exit(EXIT_FAILURE); - } - - if (SDL_NumJoysticks > 0) - gamepad = SDL_JoystickOpen(0); - - screen_width = SCREEN_WIDTH; - screen_height = SCREEN_HEIGHT; - - (void) atexit(screen_destroy); -} - -void -screen_destroy(void) -{ - - SDL_Quit(); -} diff --git a/tests/sdl-client/screen.h b/tests/sdl-client/screen.h deleted file mode 100644 index d98322f..0000000 --- a/tests/sdl-client/screen.h +++ /dev/null @@ -1,34 +0,0 @@ -/* $Id: screen.h,v 1.3 2006/05/08 08:17:00 mmondor Exp $ */ - -/* - * Copyright (c) 2006, Matthew Mondor - * ALL RIGHTS RESERVED. - */ - -/* - * Screen related initialization. - */ - - - -#ifndef SCREEN_H -#define SCREEN_H - - - -#include - - - -extern SDL_Surface *screen_surface; -extern int screen_width, screen_height; -extern SDL_Joystick *gamepad; - - - -void screen_init(void); -void screen_destroy(void); - - - -#endif diff --git a/tests/sdl-client/tests/draw.c b/tests/sdl-client/tests/draw.c deleted file mode 100644 index da2c393..0000000 --- a/tests/sdl-client/tests/draw.c +++ /dev/null @@ -1,948 +0,0 @@ -/* $Id: draw.c,v 1.1 2006/05/03 14:20:47 mmondor Exp $ */ - -/* - * Copyright (c) 2004-2006, Matthew Mondor - * ALL RIGHTS RESERVED. - */ - -/* - * XXX TODO XXX - * - Fix most all code using multiplication to locate x,y pixel positions into - * the display to use the screen_lines array for optimization. - * This however could cause a problem for SDL surfaces other than the screen - * which are involved. A possibility to fix this would be to wrap the - * SDL_Surface into a custom structure, which could also hold the optimized - * line position array for each... - * - Implement a diff function, to fill a surface with the difference between - * two other surfaces... This might be good for some type of effects and - * animations. Hmm there possibly would be a problem with the 0 transparent - * color though... Consider following situation: - * - Diff detects new 0 color pixels in second image, how would we effect - * transformation of first image to second one, since we can't blit zero - * color images? If we did blit zero color, how would we detect what - * pixels consist in the difference? Perhaps that we would need a special - * diff format rather than just blitting differences into a zero color - * surface. Diff format could be very similar to runlength-style encoding - * format, if not the same, perhaps. I would only need to add section - * support, other than just compression... I.E. section starting at x,y, - * of total length l, followed by runlength compressed section... - * Such diffs might also be nice to send over the network when images change - * or such... - * - Implement simple runlength compression and decompression support, with - * possibly load/save image support. - * - Maybe implement simple animation format based on above diff and runlength - * support. It would be enough to implement small animated sets with limited - * motion. - * - Implement ellipse and ellipse arc support - * - Splines support would be great but I would need to read about it. - * If really required, SDL_gfx might be worth using. - * - Once all required primitives have been implemented, allow support for - * parallel postscript commands output, so that it would be possible to - * print results. This would allow to have consistent on-screen and printer - * results (this is not game related, it could be used for applications). - * * Postscript has all of the above. It actually would be nice if I could - * have my application draw postscript in real time while internally keeping - * the necessary information in memory of the screen state to be able to - * also print to a postscript printer. It seems that ghostscript has a - * graphics library. Can it be used by a third party application easily? - * Also, if I used postscript, how would I allow bitmap images to be - * supported? Especially as the relative scaling of everything would have - * to be respected? - * - It also would be possible to simply use X11R6. However, this would - * limit client applications to run on unix systems in most cases. - * Moreover, I probably would still require some kind of hack to - * internally keep track of postscript equivalents for printing, or the - * contrary, to process using postscript and draw using X11, etc... - * I of course still would need to provide my own toolkit. - * - The main problem I would have with both above techniques is dealing - * with fonts. Obviously, fonts would need to be vectorial, and in a way - * which is useful for postscript output. I would need to have an onscreen - * WYSIWYG renderer for the same type of fonts. X11 can supposedly output - * GS fonts to screen, using the X Font Server (XFS)... But this requires - * clients to have a special configuration, once more. - * NOTE: This is fixed, SDL_ttf can be used. - * - It would be possible to use bitmap fonts, as long as they are of the - * right size to be equivalent to the postscript output, and that the - * screen has proper visual to represent the page... If we support - * proportional fonts, some new code would need to be added. Moreover, we - * probably would not want the proportional fonts to be used where the - * user types text, to ease the editor's tasks... We would need to at - * least support fixed/courrier, times and helvetica. We would need a font - * format which can specify the size of each character of proportional - * fonts. Maybe an aditionnal data file along with the bitmap file... - */ - - - -/* HEADERFILES */ - -#include -#include -#include - -#include - -#include -#include -#include - - - -/* DEFINITIONS */ - -#ifdef ANTIALIASING /* conf.h */ -#define DRAW_PIXEL draw_pixel_antialias -#else -#define DRAW_PIXEL draw_pixel -#endif - - - -/* STATIC FUNCTIONS PROTOTYPES */ - - - -/* GLOBALS */ - -static draw_point_t *stack_start, *stack_pos, pen; -static double sin_table[1000], cos_table[1000]; -static uint32_t **screen_lines; - - - -/* PRIVATE FUNCTIONS */ - - - -/* PUBLIC FUNCTIONS */ - -int -draw_init(void) -{ - - /* Allocate memory for draw_fill_*() stack */ - if ((stack_start = malloc(sizeof(draw_point_t) * - (screen_width * screen_height) * 4)) == NULL) - return -1; - - /* - * Allocate and initialize fast screen display line index lookup table, - * which can be used by pixel drawing routines without the need for - * multiplications. - */ - { - uint32_t *lptr; - int i; - - if ((screen_lines = malloc(sizeof(uint32_t *) * - screen_height)) == NULL) - return -1; - - for (lptr = (uint32_t *)(screen_surface->pixels), i = 0; - i < screen_height; i++, lptr += screen_width) - screen_lines[i] = lptr; - } - - /* - * Initialize our sin/cos trigonometric functions arrays for - * performance when mesuring rotations in radians. - * XXX We should add pixel ratio modifyer too here. - */ - { - int i; - double f; - - for (i = 0; i < 1000; i++) { - f = ((2 * M_PI) / 1000) * (i - 250); - sin_table[i] = sin(f); - cos_table[i] = cos(f); - } - } - - return 0; -} - -/* The following functions require that screen_surface be locked. */ - -/* Draws a vertical line at specified position and of specified height */ -inline void -draw_vline(draw_point_t *p, int height, uint32_t col) -{ - register uint32_t *ptr; - register int i; - - assert(p != NULL); - assert(!(p->x < 0 || p->x >= screen_width || - p->y < 0 || p->y + height > screen_height)); - - /* XXX Should use lines array */ - for (ptr = &((uint32_t *)screen_surface->pixels)[ - (p->y * screen_width) + p->x], i = height; i > 0; - i--, ptr = &ptr[screen_width]) - *ptr = col; -} - -/* Draws an horizontal line at specified position and of specified width */ -inline void -draw_hline(draw_point_t *p, int width, uint32_t col) -{ - register uint32_t *ptr, *tptr; - - assert(p != NULL); - assert(!(p->x < 0 || p->x + width > screen_width || - p->y < 0 || p->y >= screen_height)); - - /* XXX Should use lines array */ - for (ptr = &((uint32_t *)screen_surface->pixels)[ - (p->y * screen_width + p->x)], tptr = &ptr[width]; ptr < tptr; ) - *ptr++ = col; -} - -/* - * Draws a rectangle of specified colors. Two colors can be specified for a - * special 3d-like effect (although both can be the same if wanted). - */ -inline void -draw_rect(draw_rect_t *r, uint32_t hicol, uint32_t locol) -{ - draw_point_t p; - - assert(r != NULL); - assert(!(r->x < 0 || r->x + r->w > screen_width || - r->y < 0 || r->y + r->h > screen_height)); - - p.x = r->x; - p.y = r->y; - draw_hline(&p, r->w, hicol); - p.y++; - draw_vline(&p, r->h - 1, hicol); - p.x = r->x + r->w - 1; - p.y--; - draw_vline(&p, r->h, locol); - p.x = r->x + 1; - p.y = r->y + r->h - 1; - draw_hline(&p, r->w - 1, locol); -} - -/* - * Draws rectangular box at (x,y) to (x+w-1,y+h-1), using lcol for the 3d - * border light color, dcol for 3d border dark color, filled with fcol (or 0 - * for transparent interior) and of specified 3d depth. - */ -void -draw_rect_3d(draw_rect_t *r, uint32_t hicol, uint32_t locol, uint32_t fillcol, - int depth) -{ - draw_rect_t rect; - - assert(r != NULL); - assert(!(r->x < 0 || r->x + r->w > screen_width || - r->y < 0 || r->y + r->h > screen_height)); - - if (fillcol != 0) { - rect.x = r->x + 1; - rect.y = r->y + 1; - rect.w = r->w - 2; - rect.h = r->h - 2; - draw_rect_fill(&rect, fillcol); - } - rect = *r; - for (; depth > 0; depth--, rect.x += 1, rect.y += 1, - rect.w -= 2, rect.h -= 2) - draw_rect(&rect, hicol, locol); -} - -/* Allows to fill specified rectangle with specified color */ -inline void -draw_rect_fill(draw_rect_t *r, uint32_t col) -{ - register uint32_t *ptr, *tptr = NULL, *lptr; - - assert(r != NULL); - assert(!(r->x < 0 || r->x + r->w > screen_width || - r->y < 0 || r->y + r->h > screen_height)); - - if (r->w == screen_width) { - /* - * Even better optimization, setting an entire single block of - * multiple lines together. - * XXX Should use lines array - */ - for (ptr = &((uint32_t *)screen_surface->pixels)[ - (r->y * screen_width) + r->x], - tptr = &ptr[screen_width * r->h]; - ptr < tptr; ) - *ptr++ = col; - } else { - /* Use line-based optimizations */ - /* XXX Should use lines array */ - for (ptr = &((uint32_t *)screen_surface->pixels)[ - (r->y * screen_width) + r->x], - tptr = &((uint32_t *)screen_surface->pixels)[ - ((r->y + r->h - 1) * screen_width) + (r->x + r->w)]; - ptr < tptr; ptr = &ptr[screen_width - r->w]) { - for (lptr = &ptr[r->w]; ptr < lptr; ) - *ptr++ = col; - } - } -} - -/* - * Similar to draw_rect_fill(), but causes pixels to be XORed against m, - * to toggle their bits. This is reversable by another call to this function - * on the same area again. This can be useful for the implementation of a - * block cursor. However, because of the disposition of the current 256 color - * palette, the effect might not be what is expected (some colors will render - * too bright or too dark to be considered an inversing effect). - */ -void -draw_rect_fill_xor(draw_rect_t *r, uint32_t mask) -{ - register uint32_t *ptr, *toptr, *lptr; - - assert(r != NULL); - assert(!(r->x < 0 || r->x + r->w > screen_width || - r->y < 0 || r->y + r->h > screen_height)); - - /* XXX Should use lines array */ - for (ptr = &((uint32_t *)screen_surface->pixels)[ - (r->y * screen_width) + r->x], - toptr = &((uint32_t *)screen_surface->pixels)[ - ((r->y + r->h - 1) * screen_width) + (r->x + r->w)]; - ptr < toptr; ptr = &ptr[screen_width - r->w]) { - for (lptr = &ptr[r->w]; ptr < lptr; ) - *ptr++ ^= mask; - } -} - -/* - * Similar to draw_rect_fill(), but causes pixels which are not of the bgcol - * to be reversed to the bgcol. Other pixels, which already were of bgcol are - * set to col. This function is useful for the implementation of a block - * cursor. - */ -void -draw_rect_fill_inverse(draw_rect_t *r, uint32_t col, uint32_t bgcol) -{ - register uint32_t *ptr, *toptr, *lptr; - - assert(r != NULL); - assert(!(r->x < 0 || r->x + r->w > screen_width || - r->y < 0 || r->y + r->h > screen_height)); - - /* XXX Should use lines array */ - for (ptr = &((uint32_t *)screen_surface->pixels)[ - (r->y * screen_width) + r->x], - toptr = &((uint32_t *)screen_surface->pixels)[ - ((r->y + r->h - 1) * screen_width) + (r->x + r->w)]; - ptr < toptr; ptr = &ptr[screen_width - r->w]) { - for (lptr = &ptr[r->w]; ptr < lptr; ptr++) - *ptr = (*ptr == bgcol ? col : bgcol); - } -} - -/* - * Very similar to draw_rect_fill(), but causes empty/transparent pixels to be - * left at 50%. Can be used to simulate transparency without the need for - * alpha values. Especially useful with restricted palettes, or simply to - * create an effect. - */ -void -draw_rect_fill_transparent(draw_rect_t *r, uint32_t col) -{ - register uint32_t *ptr, *toptr, *lptr; - int even; - - assert(r != NULL); - assert(!(r->x < 0 || r->x + r->w > screen_width || - r->y < 0 || r->y + r->h > screen_height)); - - /* XXX Should use lines array */ - for (ptr = &((uint32_t *)screen_surface->pixels)[ - (r->y * screen_width) + r->x], - toptr = &((uint32_t *)screen_surface->pixels)[ - ((r->y + r->h - 1) * screen_width) + (r->x + r->w)], even = 0; - ptr < toptr; - ptr = &ptr[screen_width - r->w], even = (even == 0 ? 1 : 0)) { - ptr += even; - for (lptr = &ptr[r->w]; ptr < lptr; ptr += 2) - *ptr = col; - ptr -= even; - } -} - -/* - * Similar to SDL_BlitSurface(), but less efficient, it allows to blit the - * specified image in one color. All non-zero pixels are copied, while others - * are output as fg. In the case where bg is non-zero, zero pixels output bg. - * The screen surface should be locked when calling this function. - */ -void -draw_surface_color(SDL_Surface *surface, draw_rect_t *r, draw_point_t *p, - uint32_t fg, uint32_t bg) -{ - register uint32_t *dstptr, *tdstptr, *srcptr, *tptr; - - assert(surface != NULL); - assert(r != NULL); - assert(p != NULL); - assert(!(r->x < 0 || r->x + r->w > surface->w || - r->y < 0 || r->y + r->h > surface->h)); - assert(!(p->x < 0 || p->x + r->w > surface->w)); - - /* - * For performance we want to avoid having to include the bg color - * check every loop iteration. We prefer to have two loops instead. - * Moreover, we optimize the loop as such to avoid multiplications - * and divisions, using a line-based algorithm with additions only. - */ - if (bg == 0) { - /* XXX Should use lines array */ - for (dstptr = &((uint32_t *)screen_surface->pixels)[ - (p->y * screen_width) + p->x], - tdstptr = &((uint32_t *)screen_surface->pixels)[ - ((p->y + r->h - 1) * screen_width) + (p->x + r->w)], - srcptr = &((uint32_t *)surface->pixels)[ - (r->y * surface->w) + r->x]; - dstptr < tdstptr; - dstptr = &dstptr[screen_width - r->w], - srcptr = &srcptr[surface->w - r->w]) { - for (tptr = &dstptr[r->w]; dstptr < tptr; - dstptr++, srcptr++) { - if ((*srcptr & 0xff000000) != 0) - *dstptr = fg; - } - } - } else { - /* XXX Should use lines array */ - for (dstptr = &((uint32_t *)screen_surface->pixels)[ - (p->y * screen_width) + p->x], - tdstptr = &((uint32_t *)screen_surface->pixels)[ - ((p->y + r->h - 1) * screen_width) + (p->x + r->w)], - srcptr = &((uint32_t *)surface->pixels)[ - (r->y * surface->w) + r->x]; - dstptr < tdstptr; - dstptr = &dstptr[screen_width - r->w], - srcptr = &srcptr[surface->w - r->w]) { - for (tptr = &dstptr[r->w]; dstptr < tptr; ) - *dstptr++ = ((*srcptr++ & 0xff000000) == 0 ? - bg : fg); - } - } -} - -void -draw_surface_color_antialias(SDL_Surface *surface, draw_rect_t *r, - draw_point_t *p, uint32_t fg, uint32_t bg) -{ - register uint32_t *srcptr, *tsrcptr, *tptr; - register int x, y; - - assert(surface != NULL); - assert(r != NULL); - assert(p != NULL); - assert(!(r->x < 0 || r->x + r->w > surface->w || - r->y < 0 || r->y + r->h > surface->h)); - assert(!(p->x < 0 || p->x + r->w > screen_width || - p->y < 0 || p->y + r->h > screen_height)); - - /* XXX Should use lines array */ - for (tsrcptr = &((uint32_t *)surface->pixels)[ - ((r->y + r->h - 1) * surface->w) + (r->x + r->w)], - srcptr = &((uint32_t *)surface->pixels)[ - (r->y * surface->w) + r->x], - y = p->y; - srcptr < tsrcptr; - srcptr = &srcptr[surface->w - r->w], y++) { - for (x = p->x, tptr = &srcptr[r->w]; srcptr < tptr; - srcptr++, x++) { - if (bg == 0) { - if ((*srcptr & 0xff000000) != 0) - draw_pixel_antialias(x, y, fg); - } else - draw_pixel_antialias(x, y, - ((*srcptr & 0xff000000) == 0 ? bg : fg)); - } - } -} - -/* - * Similar to SDL_BlitSurface(), but less efficient, it allows to blit a - * perfect copy of a surface area to another surface area. This is most useful - * when colors have been setup as transparent and that an opaque identical - * copy is wanted. - */ -void -draw_surface_copy(SDL_Surface *dstsurface, draw_point_t *p, - SDL_Surface *srcsurface, draw_rect_t *r) -{ - uint32_t *dstptr, *tdstptr, *srcptr, *tptr; - - assert(dstsurface != NULL); - assert(p != NULL); - assert(srcsurface != NULL); - assert(r != NULL); - assert(!(r->x < 0 || r->x + r->w > srcsurface->w || - r->y < 0 || r->y + r->h > srcsurface->h)); - assert(!(p->x < 0 || p->x + r->w > dstsurface->w || - p->y < 0 || p->y + r->h > dstsurface->h)); - - /* XXX Should use lines array */ - for (dstptr = &((uint32_t *)dstsurface->pixels)[ - (p->y * dstsurface->w) + p->x], - tdstptr = &((uint32_t *)dstsurface->pixels)[ - ((p->y + r->h - 1) * dstsurface->w) + (p->x + r->w)], - srcptr = &((uint32_t *)srcsurface->pixels)[ - (r->y * srcsurface->w) + r->x]; - dstptr < tdstptr; - dstptr = &dstptr[dstsurface->w - r->w], - srcptr = &srcptr[srcsurface->w - r->w]) { - for (tptr = &dstptr[r->w]; dstptr < tptr; ) - *dstptr++ = *srcptr++; - } -} - -/* - * Blits a single pixel of specified color to screen at specified position. - * Note that this function does not consider color 0 to be transparent. - * This function, by exception, does not require the use of a draw_point_t. - * This allows to use register based optimizations in functions calling us - * (They do not need to keep counters in a memory structure when looping). - */ -inline void -draw_pixel(int x, int y, uint32_t col) -{ - - /* - * Instead of using this alternative, which requires multiplication: - * ((uint32_t *)screen_surface->pixels)[(y * screen_width) + x] = col; - * Use the advantage of our screen_lines array for indexing - * optimization. - */ - screen_lines[y][x] = col; -} - -inline void -draw_pixel_transparent(int x, int y, uint32_t col, int alpha) -{ - uint32_t ccol; - - /* This condition can happen because we are called for antialiasing */ - if (x >= screen_width || x < 0 || y >= screen_height || y < 0) - return; - - alpha &= 0xff; - - /* First optimize possible cases */ - if (alpha == 0x00) - return; - if (alpha == 0xff) { - draw_pixel(x, y, col); - return; - } - - /* Transparently blit pixel if necessary */ - if ((ccol = draw_getpixel2(x, y)) != col) { - int r, g, b, nr, ng, nb; - - /* - * Decompose into r, g, b levels, first current screen pixel - * color - */ - r = ((ccol & 0x00ff0000) >> 16); - g = ((ccol & 0x0000ff00) >> 8); - b = (ccol & 0x000000ff); - /* Then supplied color */ - nr = ((col & 0x00ff0000) >> 16); - ng = ((col & 0x0000ff00) >> 8); - nb = (col & 0x000000ff); - - /* - * Apply opacity modification on supplied color according to - * alpha - */ - nr = nr * alpha / 0xff; - ng = ng * alpha / 0xff; - nb = nb * alpha / 0xff; - - /* - * XXX bug here! Works fine when blitting lighter colors on - * darker ones, but not for dark colors over brighter - * background. I probably need something as simple as reverse - * proportional function or such... - */ - /* Mix supplied color with pixel color */ - if ((r = (r + nr) / 2) > 0xff) - r = 0xff; - if ((g = (g + ng) / 2) > 0xff) - g = 0xff; - if ((b = (b + nb) / 2) > 0xff) - b = 0xff; - - /* Recompose into a 32-bit color */ - ccol = (((r << 16) & 0x00ff0000) | ((g << 8) & 0x0000ff00) | - (b & 0x000000ff)); - - /* Finally blit resulting pixel back to screen */ - draw_pixel(x, y, ccol); - } -} - -/* - * XXX Test. - */ -inline void -draw_pixel_antialias(int x, int y, uint32_t col) -{ -#define AA_T 0xd0 /*0x60*/ - - assert(!(x >= screen_width || x < 0 || y >= screen_height || y < 0)); - - draw_pixel(x, y, col); - draw_pixel_transparent(x, y - 1, col, AA_T); - draw_pixel_transparent(x + 1, y, col, AA_T); - draw_pixel_transparent(x, y + 1, col, AA_T); - draw_pixel_transparent(x - 1, y, col, AA_T); - -#undef AA_T -} - -/* Returns the color of pixel at specified location on screen. */ -inline uint32_t -draw_getpixel(draw_point_t *p) -{ - - assert(p != NULL); - - return screen_lines[p->y][p->x]; -} - -inline uint32_t -draw_getpixel2(int x, int y) -{ - - return screen_lines[y][x]; -} - -/* - * Blits a line on screen of specified color (x,y)->(x2,y2). Note that color 0 - * is not considered transparent. Probably does not use the best algorithm, - * but SDL doesn't provide hardware line blitting support, and I at least - * needed such a function... This seems to be fast enough, using this - * home-rolled algorithm for now. - */ -/* XXX Modify so that it performs a gradient using draw_pixel_transparent() - * with various alpha values when lines are nor vertical, horizontal or - * diagonal. I.E. - * --++-- - * --++-- - * This is apparently a known antialiasing algorithm for lines and would - * probably be faster than using my current method. - */ -void -draw_line(draw_point_t *p1, draw_point_t *p2, uint32_t col) -{ - int ix, iy; - register int x, y, tx, ty; - double f, fa; - - assert(p1 != NULL); - assert(p2 != NULL); - assert(!(p1->x < 0 || p1->x >= screen_width || - p1->y < 0 || p1->y >= screen_height)); - assert(!(p2->x < 0 || p2->x >= screen_width || - p2->y < 0 || p2->y >= screen_height)); - - /* - * Convert quadrant 1 to 3, and quadrant 2 to 4. This ensures that we - * only need to blit the line from top to bottom. - */ - if (p1->y > p2->y) { - draw_point_t *p; - - p = p1; - p1 = p2; - p2 = p; - } - - /* Test for cases which can easily be optimized */ - if (p1->x == p2->x) { - if (p1->y == p2->y) { - /* Only a single pixel to blit */ - draw_pixel(p1->x, p1->y, col); - return; - } - /* We can use draw_vline() */ - draw_vline(p1, (p2->y - p1->y), col); - return; - } - if (p1->y == p2->y) { - /* We can use draw_hline() */ - if (p1->x < p2->x) - draw_hline(p1, (p2->x - p1->x), col); - else - draw_hline(p2, (p1->x - p2->x), col); - return; - } - - if (p1->x < p2->x) { - /* Left to right blitting, quadrant 4 */ - - /* Obtain width/height ratio */ - ix = p2->x - p1->x; - iy = p2->y - p1->y; - if (ix == iy) { - /* - * Equal ratio, no need to use floating point - * arithmetic - */ - for (x = p1->x, y = p1->y, tx = p2->x, ty = p2->y; - x <= tx && y <= ty; x++, y++) - DRAW_PIXEL(x, y, col); - } else if (ix > iy) { - /* - * XXX Use tf double instead of converting f to int - * in comparision and test if speed is gained - */ - /* X ratio larger, use floating point for Y count */ - for (fa = (double)iy / (double)ix, - f = (double)p1->y, x = p1->x, tx = p2->x; - (int)f <= p2->y && x <= tx; x++, f += fa) - DRAW_PIXEL(x, (int)f, col); - } else { - /* Y ratio larger, use floating point for X count */ - for (fa = (double)ix / (double)iy, - f = (double)p1->x, y = p1->y, ty = p2->y; - (int)f <= p2->x && y <= ty; y++, f += fa) - DRAW_PIXEL((int)f, y, col); - } - } else { - /* Right to left blitting, quadrant 3 */ - - /* Obtain width/height ratio */ - ix = p1->x - p2->x; - iy = p2->y - p1->y; - if (ix == iy) { - /* - * Equal ratio, no need to use floating point - * arithmetic - */ - for (x = p1->x, y = p1->y, tx = p2->x, ty = p2->y; - x >= tx && y <= ty; x--, y++) - DRAW_PIXEL(x, y, col); - } else if (ix > iy) { - /* X ratio larger, use floating point for Y count */ - for (fa = (double)iy / (double)ix, - f = (double)p1->y, x = p1->x, tx = p2->x; - (int)f <= p2->y && x >= tx; x--, f += fa) - DRAW_PIXEL(x, (int)f, col); - } else { - /* Y ratio larger, use floating point for X count */ - for (fa = (double)ix / (double)iy, - f = (double)p1->x, y = p1->y, ty = p2->y; - (int)f >= p2->x && y <= ty; y++, f -= fa) - DRAW_PIXEL((int)f, y, col); - } - } -} - -inline void -draw_moveto(draw_point_t *p) -{ - - assert(p != NULL); - assert(!(p->x < 0 || p->x >= screen_width || - p->y < 0 || p->y >= screen_height)); - - pen = *p; -} - -inline void -draw_lineto(draw_point_t *p, uint32_t col) -{ - - assert(p != NULL); - assert(!(p->x < 0 || p->x >= screen_width || - p->y < 0 || p->y >= screen_height)); - - draw_line(&pen, p, col); - pen = *p; -} - -/* - * Fills the specified region, as long as pixels are bgcol, in all directions. - * This implementation uses a custom stack with iteration to avoid overflowing - * the stack. SDL can internally be compiled against various threading - * libraries, several of which have a fixed stack size (I.E. Pth). Thus, - * filling rather large areas crashes using the previous implementation. - * With this one, we don't need to worry about stack room, since we are using - * the process heap instead. It however may be slower on some architectures - * than implementations using the actual stack. - */ -void -draw_fill_bg(draw_point_t *p, uint32_t col, uint32_t bgcol) -{ - draw_point_t node; - - assert(p != NULL); - assert(!(p->x < 0 || p->x >= screen_width || - p->y < 0 || p->y >= screen_height)); - - stack_pos = stack_start; - *stack_pos++ = *p; - - while (stack_pos > stack_start) { - node = *--stack_pos; - if (!(node.x < 0 || node.y > screen_width - 1 || - node.y < 0 || node.y > screen_height - 1)) { - if (draw_getpixel(&node) == bgcol) { - draw_pixel(node.x, node.y, col); - *stack_pos++ = - (draw_point_t){node.x + 1, node.y}; - *stack_pos++ = - (draw_point_t){node.x, node.y + 1}; - *stack_pos++ = - (draw_point_t){node.x - 1, node.y}; - *stack_pos++ = - (draw_point_t){node.x, node.y - 1}; - } - } - } -} - -/* - * This implementation varies in that all pixels which are not of specified - * colors are filled. This is only useful if you know the borders of your - * surface to fill has the same color. It then allows to perform opaque - * filling over a variety of colors, contrary to the last function. - */ -void -draw_fill_fg(draw_point_t *p, uint32_t col) -{ - draw_point_t node; - - assert(p != NULL); - assert(!(p->x < 0 || p->x >= screen_width || - p->y < 0 || p->y >= screen_height)); - - stack_pos = stack_start; - *stack_pos++ = *p; - - while (stack_pos > stack_start) { - node = *--stack_pos; - if (!(node.x < 0 || node.y > screen_width - 1 || - node.y < 0 || node.y > screen_height - 1)) { - if (draw_getpixel(&node) != col) { - draw_pixel(node.x, node.y, col); - *stack_pos++ = - (draw_point_t){node.x + 1, node.y}; - *stack_pos++ = - (draw_point_t){node.x, node.y + 1}; - *stack_pos++ = - (draw_point_t){node.x - 1, node.y}; - *stack_pos++ = - (draw_point_t){node.x, node.y - 1}; - } - } - } -} - -/* - * Draws a perfect circle pixel-wise, radius , expressed in pixels. - * What it does is draw in four sections, adding pixels at both sides of each - * section until the sections close the circle. - */ -void -draw_circle(draw_point_t *p, int radius, uint32_t col) -{ - int x = 0, y = radius, u = 1, v = 2 * radius - 1, e = 0; - - assert(p != NULL); - assert(!(p->x < 0 || p->x + radius > screen_width || - p->y < 0 || p->y + radius > screen_height || - p->x - radius < 0 || p->y - radius < 0)); - - while (x < y) { - DRAW_PIXEL(p->x + x, p->y + y, col); - DRAW_PIXEL(p->x + y, p->y - x, col); - DRAW_PIXEL(p->x - x, p->y - y, col); - DRAW_PIXEL(p->x - y, p->y + x, col); - x++; - e += u; - u += 2; - if (v < (2 * e)) { - y--; - e -= v; - v -= 2; - } - if (x > y) - break; - DRAW_PIXEL(p->x + y, p->y + x, col); - DRAW_PIXEL(p->x + x, p->y - y, col); - DRAW_PIXEL(p->x - y, p->y - x, col); - DRAW_PIXEL(p->x - x, p->y + y, col); - } -} - -/* - * Returns x/y position in (xp,yp), for point at (x,y) of radius at - * angle (in radians). Uses an array suitable for onscreen resolution - * for efficiency instead of having to always call sin()/cos() functions. - */ -inline void -draw_get_vector(draw_point_t *tp, draw_point_t *p, draw_vect_t *v) -{ - int angle; - - assert(tp != NULL); - assert(p != NULL); - assert(v != NULL); - assert(!(p->x < 0 || p->x + v->r > screen_width || - p->y < 0 || p->y + v->r > screen_height || - p->x - v->r < 0 || p->y - v->r < 0)); - - if ((angle = v->a) < 0) - angle = 1000 - (-angle); - else if (angle > 999) - angle %= 1000; - - tp->x = (int)(p->x + (cos_table[angle] * v->r)); - tp->y = (int)(p->y + (sin_table[angle] * v->r)); -} - -/* - * Slower implementation of draw_circle() using floating point, but which - * can draw arcs. - */ -void draw_circle_arc(draw_point_t *p, int radius, int a1, int a2, int step, - uint32_t col) -{ - register int i; - draw_point_t tp; - draw_vect_t v; - - assert(p != NULL); - assert(!(p->x < 0 || p->x + radius > screen_width || - p->y < 0 || p->y + radius > screen_height || - p->x - radius < 0 || p->y - radius < 0)); - - if (a2 < a1) { - i = a1; - a1 = a2; - a2 = i; - } - v.r = radius; - v.a = a1; - draw_get_vector(&tp, p, &v); - draw_moveto(&tp); - for (i = a1; i <= a2; i += step) { - v.a = i; - draw_get_vector(&tp, p, &v); - draw_lineto(&tp, col); - } - if (i > a2) { - v.a = a2; - draw_get_vector(&tp, p, &v); - draw_lineto(&tp, col); - } -} diff --git a/tests/sdl-client/tests/draw.h b/tests/sdl-client/tests/draw.h deleted file mode 100644 index 2cb3647..0000000 --- a/tests/sdl-client/tests/draw.h +++ /dev/null @@ -1,75 +0,0 @@ -/* $Id: draw.h,v 1.1 2006/05/03 14:22:06 mmondor Exp $ */ - -/* - * Copyright (c) 2004-2006, Matthew Mondor - * ALL RIGHTS RESERVED. - */ - - -#ifndef DRAW_H -#define DRAW_H - - - -#include - - - -#define SCREEN_LOCK() if (SDL_LockSurface(screen_surface) != 0) \ - abort(); -#define SCREEN_UNLOCK() SDL_UnlockSurface(screen_surface) - - - -typedef struct draw_point { - int x, y; -} draw_point_t; - -typedef struct draw_vect { - int a, r; -} draw_vect_t; - -typedef struct draw_rect { - int x, y; - int w, h; -} draw_rect_t; - - - -extern int draw_init(void); -extern inline void draw_vline(draw_point_t *, int, uint32_t); -extern inline void draw_hline(draw_point_t *, int, uint32_t); -extern inline void draw_rect(draw_rect_t *, uint32_t, uint32_t); -extern void draw_rect_3d(draw_rect_t *, uint32_t, uint32_t, - uint32_t, int); -extern inline void draw_rect_fill(draw_rect_t *, uint32_t); -extern void draw_rect_fill_xor(draw_rect_t *, uint32_t); -extern void draw_rect_fill_inverse(draw_rect_t *, uint32_t, - uint32_t); -extern void draw_rect_fill_transparent(draw_rect_t *, uint32_t); -extern void draw_surface_color(SDL_Surface *, draw_rect_t *, - draw_point_t *, uint32_t, uint32_t); -extern void draw_surface_color_antialias(SDL_Surface *, - draw_rect_t *, draw_point_t *, uint32_t, - uint32_t); -extern void draw_surface_copy(SDL_Surface *, draw_point_t *, - SDL_Surface *, draw_rect_t *); -extern inline void draw_pixel(int, int, uint32_t); -extern inline void draw_pixel_transparent(int, int, uint32_t, int); -extern inline void draw_pixel_antialias(int, int, uint32_t); -extern inline uint32_t draw_getpixel(draw_point_t *); -extern inline uint32_t draw_getpixel2(int, int); -extern void draw_line(draw_point_t *, draw_point_t *, uint32_t); -extern void draw_moveto(draw_point_t *); -extern void draw_lineto(draw_point_t *, uint32_t); -extern void draw_fill_bg(draw_point_t *, uint32_t, uint32_t); -extern void draw_fill_fg(draw_point_t *, uint32_t); -extern void draw_circle(draw_point_t *, int, uint32_t); -extern inline void draw_get_vector(draw_point_t *, draw_point_t *, - draw_vect_t *); -extern void draw_circle_arc(draw_point_t *, int, int, int, int, - uint32_t); - - - -#endif diff --git a/tests/sdl-client/tests/fonts.c b/tests/sdl-client/tests/fonts.c deleted file mode 100644 index fc6b96f..0000000 --- a/tests/sdl-client/tests/fonts.c +++ /dev/null @@ -1,318 +0,0 @@ -/* $Id: fonts.c,v 1.1 2006/05/03 14:20:47 mmondor Exp $ */ - -/* - * Copyright (c) 2004-2006, Matthew Mondor - * ALL RIGHTS RESERVED. - */ - - - -/* HEADERFILES */ - -#include -#include -#include -#include - -#include - -#include -#include -#include -#include - - - -/* DEFINITIONS */ - -#ifdef ANTIALIASING /* conf,h */ -#define DRAW_SURFACE_COLOR draw_surface_color_antialias -#else -#define DRAW_SURFACE_COLOR draw_surface_color -#endif - - - -/* STATIC FUNCTIONS PROTOTYPES */ - -inline static void font_rectangle(font_t *, draw_rect_t *, int); - - - -/* GLOBALS */ - -font_t *font_small, *font_small_bold, - *font_large, *font_large_bold; - - - -/* PRIVATE FUNCTIONS */ - -/* - * Fills the rectangle coordinates for the specified character of specified - * font within the font's surface - */ -inline static void -font_rectangle(font_t *font, draw_rect_t *rect, int c) -{ - assert(font != NULL); - assert(rect != NULL); - - /* Make sure that c is within 0-255 */ - c &= 0xff; - - /* Map c (0-255) to 0-15 x/y coordinates */ - rect->x = font->x * (c & 0x0f); - rect->y = font->y * (c / 16); - rect->w = font->x; - rect->h = font->y; -} - - - -/* PUBLIC FUNCTIONS */ - -/* Load fonts */ -int -font_init(void) -{ - - font_small = font_load( - "bmp/misc-fixed-medium-r-normal--13-120-75-75-c-80-iso8859-1.bmp"); - font_small_bold = font_load( - "bmp/misc-fixed-bold-r-normal--13-120-75-75-c-80-iso8859-1.bmp"); - font_large = font_load( - "bmp/misc-fixed-medium-r-normal--15-120-100-100-c-90-iso8859-1.bmp"); - font_large_bold = font_load( - "bmp/misc-fixed-bold-r-normal--15-120-100-100-c-90-iso8859-1.bmp"); - - /* - * On error, we don't free any successfully loaded fonts, and expect - * the application to exit. - */ - if (font_small == NULL || font_small_bold == NULL || - font_large == NULL || font_large_bold == NULL) - return -1; - - return 0; -} - -/* - * Allows to load a 256 characters (16x16 tiled) font map from an image. - * This image usually is in 1 bit depth, but it internally gets converted to - * 32-bit depth, and we then create a per-pixel alpha blending enabled surface - * for it. - */ -font_t * -font_load(const char *file) -{ - font_t *font; - SDL_Surface *surface1 = NULL, *surface2 = NULL, *surface3 = NULL; - int ok; - - assert(file != NULL); - - /* Load 1 bit depth bitmap */ - if ((surface1 = SDL_LoadBMP(file)) == NULL) - goto err; - - /* - * Convert loaded surface to screen_surface's depth, into new surface. - * bits 0 become 0x00000000 and 1 0x00ffffff. Remember that surface2 - * is the one we'll copy to the final destination one. - */ - if ((surface2 = SDL_ConvertSurface(surface1, screen_surface->format, - SDL_SWSURFACE)) == NULL) - goto err; - SDL_FreeSurface(surface1); - surface1 = NULL; - - /* - * Create new surface suitable for source alpha blending blitting. - * I previously attempted to use the converted surface and create an - * alpha blending enabled copy, but this seemed to fail. I thus must - * explicitely create a new surface myself (which proved to work in a - * test program). - */ - if ((surface1 = SDL_CreateRGBSurface(SDL_SWSURFACE | SDL_SRCALPHA, - surface2->w, surface2->h, 32, 0, 0, 0, 0)) == NULL) - goto err; - - /* - * Convert newly created surface to support per-pixel alpha blending - * with hardware support. Resulting surface3 is the one we'll copy - * surface2 to. - */ - (void) SDL_SetAlpha(surface1, SDL_SRCALPHA | SDL_RLEACCEL, 128); - if ((surface3 = SDL_DisplayFormatAlpha(surface1)) == NULL) - goto err; - SDL_FreeSurface(surface1); - surface1 = NULL; - - /* - * We now have both our 32-bit depth font surface (surface2) and our - * destination alpha surface (surface3). Copy surface2 data to - * surface3, making sure to set the alpha color to 0x00 for pixels - * which originally were 0 bits, that is, now 0x00000000, and to 0xff - * for pixels which were 1 bits, now being 0x00ffffff. - */ - ok = -1; - if (SDL_LockSurface(surface2) == 0) { - if (SDL_LockSurface(surface3) == 0) { - uint32_t *sptr, *dptr, *dtptr; - - for (sptr = (uint32_t *)surface2->pixels, - dptr = (uint32_t *)surface3->pixels, - dtptr = &dptr[surface3->w * surface3->h]; - dptr < dtptr; - sptr++, dptr++) - *dptr = (*sptr == 0 ? 0x00000000 : 0xffffffff); - ok = 0; - SDL_UnlockSurface(surface3); - } - SDL_UnlockSurface(surface2); - } - SDL_FreeSurface(surface2); - surface2 = NULL; - if (ok == -1) - goto err; - - /* Allocate font structure */ - if ((font = malloc(sizeof(font_t))) == NULL) - goto err; - - /* Everything successful, fill font structure and return it. */ - font->x = surface3->w / 16; - font->y = surface3->h / 16; - font->surface = surface3; - - return font; - -err: - if (surface1 != NULL) - SDL_FreeSurface(surface1); - if (surface2 != NULL) - SDL_FreeSurface(surface2); - if (surface3 != NULL) - SDL_FreeSurface(surface3); - - return NULL; -} - -/* Frees previously loaded font using font_load() */ -void -font_free(font_t *font) -{ - - assert(font != NULL); - assert(font->surface != NULL); - - SDL_FreeSurface(font->surface); - free(font); -} - -/* - * Efficiently draws a character to screen at specified position. The output - * color however can only be white using this function. - */ -void -font_draw_char(font_t *font, draw_point_t *p, int c) -{ - SDL_Rect screen_rect, font_rect; - draw_rect_t r; - - assert(font != NULL); - assert(p != NULL); - - screen_rect.x = p->x; - screen_rect.y = p->y; - font_rectangle(font, &r, c); - font_rect = (SDL_Rect){r.x, r.y, r.w, r.h}; - (void) SDL_BlitSurface(font->surface, &font_rect, - screen_surface, &screen_rect); -} - -/* - * Efficiently draws a character string to screen at specified position. - * The output color however can only be white using this function. - */ -void -font_draw_string(font_t *font, draw_point_t *p, const char *string) -{ - register const char *ptr; - SDL_Rect screen_rect, font_rect; - draw_rect_t r; - - assert(font != NULL); - assert(p != NULL); - assert(string != NULL); - - screen_rect.x = p->x; - screen_rect.y = p->y; - for (ptr = string; *ptr != '\0'; ptr++, screen_rect.x += font->x) { - font_rectangle(font, &r, *ptr); - font_rect = (SDL_Rect){r.x, r.y, r.w, r.h}; - (void) SDL_BlitSurface(font->surface, &font_rect, - screen_surface, &screen_rect); - } -} - -/* - * Returns the necessary number of horizontal pixels required to blit the - * specified string. - */ -int -font_string_width(font_t *font, const char *string) -{ - - assert(font != NULL); - assert(string != NULL); - - return (strlen(string) * font->x); -} - - -/* The following functions require that the screen surface be locked. */ - -/* - * font_draw_char() variant which is a slightly less efficient but allows to - * blit a character on screen at specified position with specified color. - * If bg color is 0, font is drawn in overlay mode. Otherwise, it is drawn - * overstrike with specified color. Note that if calling this function, the - * screen_surface SDL_Surface should be locked using SDL_LockSurface(). - */ -void -font_draw_char_color(font_t *font, uint32_t fg, uint32_t bg, draw_point_t *p, - int c) -{ - draw_rect_t rect; - - assert(font != NULL); - assert(p != NULL); - - font_rectangle(font, &rect, c); - DRAW_SURFACE_COLOR(font->surface, &rect, p, fg, bg); -} - -/* - * font_draw_string() variant which is a little less efficient but allows to - * blit a character string on screen at specified position with specified - * color. If bg is 0, string is drawn in overlay mode. Otherwise, it is drawn - * in overstrike mode with specified background color. - */ -void -font_draw_string_color(font_t *font, uint32_t fg, uint32_t bg, draw_point_t *p, - const char *string) -{ - register const char *ptr; - draw_rect_t rect; - - assert(font != NULL); - assert(p != NULL); - assert(string != NULL); - - for (ptr = string; *ptr != '\0'; ptr++, p->x += font->x) { - font_rectangle(font, &rect, *ptr); - DRAW_SURFACE_COLOR(font->surface, &rect, p, fg, bg); - } -} diff --git a/tests/sdl-client/tests/fonts.h b/tests/sdl-client/tests/fonts.h deleted file mode 100644 index 2edbccd..0000000 --- a/tests/sdl-client/tests/fonts.h +++ /dev/null @@ -1,46 +0,0 @@ -/* $id$ */ - -/* - * Copyright (c) 2004-2006, Matthew Mondor - * ALL RIGHTS RESERVED. - */ - - - -#ifndef FONTS_H -#define FONTS_H - - - -#include - -#include - - - -typedef struct font { - int x, y; - SDL_Surface *surface; -} font_t; - - - -extern int font_init(void); -extern font_t *font_load(const char *); -extern void font_free(font_t *); -extern void font_draw_char(font_t *, draw_point_t *, int); -extern void font_draw_string(font_t *, draw_point_t *, - const char *); -extern int font_string_width(font_t *, const char *); -extern void font_draw_char_color(font_t *, uint32_t, uint32_t, - draw_point_t *, int); -extern void font_draw_string_color(font_t *, uint32_t, uint32_t, - draw_point_t *, const char *); - - -extern font_t *font_small, *font_small_bold, - *font_large, *font_large_bold; - - - -#endif diff --git a/tests/sdl-client/tests/msg.c b/tests/sdl-client/tests/msg.c deleted file mode 100644 index dc4a14a..0000000 --- a/tests/sdl-client/tests/msg.c +++ /dev/null @@ -1,189 +0,0 @@ -/* $Id: msg.c,v 1.1 2006/04/27 13:26:51 mmondor Exp $ */ - -/* - * Copyright (c) 2006, Matthew Mondor - * ALL RIGHTS RESERVED. - */ - - - -#include -#include - -#include -#include - -#include - - - -#define CLIENTS 16 -#define ROUNDS 32 - - - -struct client { - SDL_Thread *thread; - int id; -}; - -struct client_msg { - thread_msg_t msg; - struct client *c; - int rounds; -}; - - - -int main(int, char **); - -static void done(int); -static int thread_client(void *); - - - -thread_port_t server_port; - - - -/* ARGSUSED */ -int -main(int argc, char **argv) -{ - struct client clients[CLIENTS]; - int i; - thread_ring_t server_ring; - - if (SDL_Init(SDL_INIT_EVERYTHING) == -1) { - (void) fprintf(stderr, "main() - SDL_Init() - %s\n", - SDL_GetError()); - exit(EXIT_FAILURE); - } - - /* - * We're already the server thread. - * Initialize our message port and notification ring. - */ - if (thread_ring_init(&server_ring) == -1) { - (void) fprintf(stderr, - "main() - thread_ring_init(server_port) - %s\n", - SDL_GetError()); - done(EXIT_FAILURE); - } - if (thread_port_init(&server_port) == -1) { - (void) fprintf(stderr, - "main() - thread_port_init(server_port) - %s\n", - SDL_GetError()); - done(EXIT_FAILURE); - } - thread_port_set_ring(&server_port, &server_ring); - - /* Launch client threads */ - for (i = 0; i < CLIENTS; i++) { - clients[i].id = i; - if ((clients[i].thread = SDL_CreateThread(thread_client, - &clients[i])) == NULL) { - (void) fprintf(stderr, - "main() - SDL_CreateThread(%d) - %s\n", - i, SDL_GetError()); - done(EXIT_FAILURE); - } - } - - /* - * Listen for messages from our client threads with a one second - * timeout delay and display them. - */ - for (;;) { - struct client_msg *msg; - - while ((msg = (struct client_msg *)thread_msg_get( - &server_port)) != NULL) { - (void) printf("Message from client %d: %d\n", - msg->c->id, msg->rounds); - if (thread_msg_reply(&msg->msg) != 0) { - (void) fprintf(stderr, - "main() - thread_msg_reply() - %s\n", - SDL_GetError()); - break; - } - } - (void) printf("Server polling\n"); - if (thread_ring_wait(&server_ring, 1000) != 0) { - (void) fprintf(stderr, - "main() - thread_ring_wait() - %s\n", - SDL_GetError()); - break; - } - } - - thread_port_set_ring(&server_port, NULL); - thread_port_destroy(&server_port); - thread_ring_destroy(&server_ring); - - exit(EXIT_SUCCESS); -} - -static void -done(code) -{ - - SDL_Quit(); - exit(code); -} - -static int -thread_client(void *data) -{ - struct client *c = (struct client *)data; - struct client_msg msg, *rmsg; - thread_ring_t client_ring; - thread_port_t client_port; - int i; - - /* Initialize reply port and ring */ - if (thread_ring_init(&client_ring) == -1) { - (void) fprintf(stderr, - "thread_client(%d) - thread_ring_init(server_port) - %s\n", - c->id, SDL_GetError()); - done(EXIT_FAILURE); - } - if (thread_port_init(&client_port) == -1) { - (void) fprintf(stderr, - "thread_client(%d) - thread_port_init(server_port) - %s\n", - c->id, SDL_GetError()); - done(EXIT_FAILURE); - } - thread_port_set_ring(&client_port, &client_ring); - - /* Initialize message with our reply port */ - thread_msg_init(&msg.msg, &client_port); - msg.c = c; - - for (i = 0; i < ROUNDS; i++) { - msg.rounds = i; - if (thread_msg_put(&server_port, &msg.msg) == -1) { - (void) fprintf(stderr, - "thread_client(%d) - thread_msg_put(%d) - %s\n", - c->id, i, SDL_GetError()); - break; - } - while ((rmsg = (struct client_msg *)thread_msg_get( - &client_port)) == NULL) { - (void) printf("Client polling\n"); - if (thread_ring_wait(&client_ring, 1000) != 0) { - (void) fprintf(stderr, - "thread_client(%d) - thread_ring_wait(%d)" - "- %s\n", c->id, i, SDL_GetError()); - break; - } - } - if (rmsg != NULL) - (void) printf("Received reply for %d: %d\n", - rmsg->c->id, rmsg->rounds); - else - break; - } - - return 0; -} diff --git a/tests/sdl-client/tests/netrek-like.c b/tests/sdl-client/tests/netrek-like.c deleted file mode 100644 index dbf9e11..0000000 --- a/tests/sdl-client/tests/netrek-like.c +++ /dev/null @@ -1,124 +0,0 @@ -/* $Id: netrek-like.c,v 1.1 2006/05/03 08:58:01 mmondor Exp $ */ - -/* - * Copyright (c) 2006, Matthew Mondor - * ALL RIGHTS RESERVED. - */ - - - -#include -#include -#include - -#include -#include -#include - -#include -#include - - - -int main(int, char **); - -static int surface_blit_angle(SDL_Surface *, int, int, - double, int); -static SDL_Surface *bmp_load_key(const char *); - - - -/* ARGSUSED */ -int -main(int argc, char **argv) -{ - int i; - SDL_Surface *rship, *fship; - - screen_init(); - draw_init(); - if (font_init() != 0) { - (void) fprintf(stderr, "Could not load font bitmaps\n"); - screen_destroy(); - } - - rship = bmp_load_key("bmp/RomDD.bmp"); - fship = bmp_load_key("bmp/FedCA.bmp"); - - (void) rectangleRGBA(screen_surface, 0, 0, 750, 750, - 0xff, 0xff, 0xff, 0xff); - (void) rectangleRGBA(screen_surface, 750, 0, 1023, 274, - 0xff, 0xff, 0xff, 0xff); - (void) rectangleRGBA(screen_surface, 750, 274, 1023, 506, - 0xff, 0xff, 0xff, 0xff); - (void) rectangleRGBA(screen_surface, 750, 274, 1023, 767, - 0xff, 0xff, 0xff, 0xff); - (void) rectangleRGBA(screen_surface, 0, 750, 1023, 767, - 0xff, 0xff, 0xff, 0xff); - - for (i = 0; i < 1440; i += 2) { - if (surface_blit_angle(rship, 100, 100, (double)i, 1) == -1 || - surface_blit_angle(fship, 200, 100, (double)i, 1) == -1) - (void) fprintf(stderr, "surface_blit_angle()\n"); - SDL_Flip(screen_surface); - SDL_Delay(10); - } - - SDL_Delay(1000); - - /* Attempt to connect to server */ - - screen_destroy(); - /* NOTREACHED */ - - exit(EXIT_SUCCESS); -} - -static SDL_Surface * -bmp_load_key(const char *file) -{ - SDL_Surface *s; - Uint32 c; - - if ((s = SDL_LoadBMP(file)) == NULL) - goto err; - - c = SDL_MapRGB(s->format, 0, 0, 0); - if (SDL_SetColorKey(s, SDL_SRCCOLORKEY | SDL_RLEACCEL, c) == -1) - goto err; - - return s; - -err: - (void) fprintf(stderr, "bmp_load_key(%s) - %s\n", file, SDL_GetError()); - screen_destroy(); - /* NOTREACHED */ - return NULL; -} - -static int -surface_blit_angle(SDL_Surface *s, int x, int y, double a, int clean) -{ - SDL_Surface *t; - SDL_Rect d; - int r; - - if (clean) { - int cx, cy, c; - - cx = s->w / 2 + 2; - cy = s->h / 2 + 2; - c = (cx >= cy ? cx : cy); - (void) boxRGBA(screen_surface, x - c, y - c, - x + c, y + c, 0x00, 0x00, 0x00, 0xff); - } - - if ((t = rotozoomSurface(s, a, 0.75, 1)) == NULL) - return -1; - - d = (SDL_Rect){x - (t->w / 2), y - (t->h / 2), 0, 0}; - r = SDL_BlitSurface(t, NULL, screen_surface, &d); - SDL_FreeSurface(t); - - return r; -} diff --git a/tests/sdl-client/tests/rot.c b/tests/sdl-client/tests/rot.c deleted file mode 100644 index 18448e8..0000000 --- a/tests/sdl-client/tests/rot.c +++ /dev/null @@ -1,182 +0,0 @@ -/* $Id: rot.c,v 1.3 2006/05/03 08:09:37 mmondor Exp $ */ - -/* - * Copyright (c) 2006, Matthew Mondor - * ALL RIGHTS RESERVED. - */ - - - -#include -#include -#include -#include - -#include -#include -#include - -#include -#include - - - -typedef struct angle_cache { - SDL_Surface *source; - SDL_Surface *angles[360]; -} angle_cache_t; - - - -int main(int, char **); - -static angle_cache_t *angle_cache_init(SDL_Surface *); -static void angle_cache_destroy(angle_cache_t *); -static int surface_blit_angle(struct angle_cache *, int, int, - double, int); -static SDL_Surface *bmp_load_key(const char *); - - - -/* ARGSUSED */ -int -main(int argc, char **argv) -{ - int i, i2; - SDL_Surface *rship, *fship; - angle_cache_t *rshipc, *fshipc; - - screen_init(); - draw_init(); - if (font_init() != 0) { - (void) fprintf(stderr, "Could not load font bitmaps\n"); - screen_destroy(); - } - - rship = bmp_load_key("bmp/RomDD.bmp"); - fship = bmp_load_key("bmp/FedCA.bmp"); - if ((rshipc = angle_cache_init(rship)) == NULL || - (fshipc = angle_cache_init(fship)) == NULL) { - (void) fprintf(stderr, "Could not initialize angle caches\n"); - screen_destroy(); - } - - for (i2 = 0; i2 < 2; i2++) { - for (i = 0; i < 360; i += 5) { - if (surface_blit_angle(rshipc, 100, 100, (double)i, 1) - == -1 || surface_blit_angle(fshipc, 200, 100, - (double)i, 1) == -1) - (void) fprintf(stderr, - "surface_blit_angle()\n"); - SDL_Flip(screen_surface); - SDL_Delay(33); - } - } - - SDL_Delay(1000); - - /* Attempt to connect to server */ - - screen_destroy(); - /* NOTREACHED */ - - exit(EXIT_SUCCESS); -} - -static SDL_Surface * -bmp_load_key(const char *file) -{ - SDL_Surface *s; - Uint32 c; - - if ((s = SDL_LoadBMP(file)) == NULL) - goto err; - - c = SDL_MapRGB(s->format, 0, 0, 0); - if (SDL_SetColorKey(s, SDL_SRCCOLORKEY | SDL_RLEACCEL, c) == -1) - goto err; - - return s; - -err: - (void) fprintf(stderr, "bmp_load_key(%s) - %s\n", file, SDL_GetError()); - screen_destroy(); - /* NOTREACHED */ - return NULL; -} - -static angle_cache_t * -angle_cache_init(SDL_Surface *s) -{ - angle_cache_t *ac; - int i; - - assert(s != NULL); - - if ((ac = malloc(sizeof(angle_cache_t))) != NULL) { - ac->source = s; - for (i = 0; i < 360; i++) - ac->angles[i] = NULL; - - return ac; - } - - return NULL; -} - -static void -angle_cache_destroy(angle_cache_t *ac) -{ - int i; - - assert(ac != NULL); - - for (i = 0; i < 360; i++) { - if (ac->angles[i] != NULL) - SDL_FreeSurface(ac->angles[i]); - } - - free(ac); -} - -static int -surface_blit_angle(angle_cache_t *ac, int x, int y, double a, int clean) -{ - SDL_Surface *t, *s = ac->source; - SDL_Rect d; - int r, i; - - assert(a > -1 && a < 360); - assert(((int)a) == a); - - if (clean) { - int cx, cy, c; - - cx = s->w / 2 + 2; - cy = s->h / 2 + 2; - c = (cx >= cy ? cx : cy); - (void) boxRGBA(screen_surface, x - c, y - c, - x + c, y + c, 0x00, 0x00, 0x00, 0xff); - } - - if ((t = ac->angles[(int)a]) == NULL) { - for (r = 0; r < 2; r++) { - if ((t = rotozoomSurface(s, a, 1.0, 1)) != NULL) { - ac->angles[(int)a] = t; - break; - } - /* Free some room and retry */ - for (i = 0; i < 360; i++) { - if (ac->angles[i] != NULL) { - SDL_FreeSurface(ac->angles[i]); - ac->angles[i] = NULL; - } - } - } - if (t == NULL) - return -1; - } - - d = (SDL_Rect){x - (t->w / 2), y - (t->h / 2), 0, 0}; - return SDL_BlitSurface(t, NULL, screen_surface, &d); -} diff --git a/tests/sdl-client/tests/snd.c b/tests/sdl-client/tests/snd.c deleted file mode 100644 index eceaa3c..0000000 --- a/tests/sdl-client/tests/snd.c +++ /dev/null @@ -1,159 +0,0 @@ -/* $Id: snd.c,v 1.1 2006/05/01 03:52:45 mmondor Exp $ */ - -/* - * Copyright (c) 2006, Matthew Mondor - * ALL RIGHTS RESERVED. - */ - - - -#include -#include -#include -#include - -#include -#include - - - -int main(int, char **); - -static void done(int); -static Mix_Chunk *sample_load(const char *); - - - -static SDL_Surface *surface_screen = NULL; -static Mix_Chunk *cloak, *uncloak, *shield, *unshield, *torp, *hit, - *explode; - -static int iscloaked, isshielded; - - - -/* ARGSUSED */ -int -main(int argc, char **argv) -{ - Mix_Music *mus; - SDL_Event ev; - - if (SDL_Init(SDL_INIT_EVERYTHING) == -1) { - (void) fprintf(stderr, "main() - SDL_Init() - %s\n", - SDL_GetError()); - exit(EXIT_FAILURE); - } - if ((surface_screen = SDL_SetVideoMode(640, 480, 32, - SDL_HWSURFACE | SDL_HWPALETTE | SDL_DOUBLEBUF/* | SDL_FULLSCREEN*/)) - == NULL) { - (void) fprintf(stderr, "main() - SDL_SetVideoMode() - %s\n", - SDL_GetError()); - exit(EXIT_FAILURE); - } - - if (Mix_OpenAudio(44100, MIX_DEFAULT_FORMAT, 2, 1024) != 0) { - (void) fprintf(stderr, "main() - Mix_OpenAudio() - %s\n", - Mix_GetError()); - done(EXIT_FAILURE); - } - - cloak = sample_load("wav/nt_cloaked.wav"); - uncloak = sample_load("wav/nt_uncloak.wav"); - shield = sample_load("wav/nt_shield_up.wav"); - unshield = sample_load("wav/nt_shield_down.wav"); - torp = sample_load("wav/nt_fire_torp_other.wav"); - hit = sample_load("wav/nt_plasma_hit.wav"); - explode = sample_load("wav/nt_explosion_other.wav"); - - (void) Mix_AllocateChannels(16); - (void) Mix_ReserveChannels(2); - - if ((mus = Mix_LoadMUS("ogg/1.ogg")) == NULL) { - (void) fprintf(stderr, "main() - Mix_LoadMUS() - %s\n", - Mix_GetError()); - done(EXIT_FAILURE); - } - - if (Mix_PlayMusic(mus, -1) != 0) { - (void) fprintf(stderr, "main() - Mix_PlayMusic() - %s\n", - Mix_GetError()); - done(EXIT_FAILURE); - } - - for (iscloaked = isshielded = 0; SDL_WaitEvent(&ev) != 0; ) { - int ch; - - if (ev.type == SDL_KEYUP && ev.key.keysym.sym == SDLK_ESCAPE) - break; - if (ev.type == SDL_KEYDOWN) { - switch (ev.key.keysym.sym) { - case SDLK_SPACE: - /* Torp */ - if ((ch = Mix_PlayChannel(-1, torp, 0)) == -1) - (void) fprintf(stderr, "main() - " - "Mix_PlayChannel(torp) - %s\n", - SDL_GetError()); - else - Mix_SetPosition(ch, rand() % 359, - 255 - (200 + (rand() % 54))); - break; - case SDLK_w: - /* Cloak toggle */ - iscloaked = (iscloaked == 0 ? 1 : 0); - if (Mix_PlayChannel(0, - (iscloaked == 1 ? cloak : uncloak), 0) - == -1) - (void) fprintf(stderr, "main() - " - "Mix_PlayChannel(cloack) - %s\n", - SDL_GetError()); - break; - case SDLK_s: - /* Shield toggle */ - isshielded = (isshielded == 0 ? 1 : 0); - if (Mix_PlayChannel(1, - (isshielded == 1 ? shield : unshield), 0) - == -1) - (void) fprintf(stderr, "main() - " - "Mix_PlayChannel(shield) - %s\n", - SDL_GetError()); - break; - default: - break; - } - } - } - - if (Mix_FadeOutMusic(2000)) - SDL_Delay(2000); - - Mix_HaltMusic(); - Mix_CloseAudio(); - - done(EXIT_SUCCESS); - /* NOTREACHED */ - exit(EXIT_SUCCESS); -} - -static void -done(code) -{ - - SDL_Quit(); - exit(code); -} - -static Mix_Chunk * -sample_load(const char *file) -{ - Mix_Chunk *c; - - if ((c = Mix_LoadWAV(file)) == NULL) { - (void) fprintf(stderr, - "sample_load() - Mix_LoadWAV(%s) - %s\n", - file, SDL_GetError()); - done(EXIT_FAILURE); - } - - return c; -} diff --git a/tests/sdl-client/thread_msg.c b/tests/sdl-client/thread_msg.c deleted file mode 100644 index 3652617..0000000 --- a/tests/sdl-client/thread_msg.c +++ /dev/null @@ -1,432 +0,0 @@ -/* $Id: thread_msg.c,v 1.12 2006/05/19 10:14:35 mmondor Exp $ */ - -/* - * Copyright (C) 2006, Matthew Mondor - * All rights reserved. - * - * Adapted from older code intended for use with POSIX threads, - * for use within SDL. - */ - - - -#include - -#include -#include -#include - - - -static int amsg_create(pnode_t *); -static void amsg_destroy(pnode_t *); - - - -static pool_t amsg_pool; -static SDL_mutex *amsg_mutex; -static int amsg_initialized = 0; - - - -/* - * Allows to initialize a polling notification handle. When attached to a - * port, a message arriving on an empty port causes the associated ring to - * wake the thread from thread_ring_wait(). - */ -int -thread_ring_init(thread_ring_t *ring) -{ - - ASSERT(ring != NULL && ring->magic != PRING_MAGIC); - - if ((ring->cond = SDL_CreateCond()) != NULL) { - if ((ring->mutex = SDL_CreateMutex()) != NULL) { - ring->event = 0; - ring->magic = PRING_MAGIC; - - return 0; - } - SDL_DestroyCond(ring->cond); - } - - return -1; -} - -/* - * Returns TRUE if the supplied ring is a valid/usable one, or FALSE - * otherwise. Useful to conditionally destroy it. - */ -int -thread_ring_valid(thread_ring_t *ring) -{ - - ASSERT(ring != NULL); - - return (ring != NULL && ring->magic == PRING_MAGIC); -} - -/* - * Destroys a ring. Note that all message ports attached to this ring should - * first be detached or destroyed. - */ -void -thread_ring_destroy(thread_ring_t *ring) -{ - - ASSERT(ring != NULL && ring->magic == PRING_MAGIC); - - SDL_DestroyMutex(ring->mutex); - SDL_DestroyCond(ring->cond); - ring->magic = 0; -} - -/* - * Causes the current thread to sleep until a message arrives on an empty port - * associated with this ring. In normal operation, a thread only goes in wait - * mode after it processed all queued messages on all interesting ports. - * The ring is only notified when the port is empty and that a message is - * queued into it. - */ -int -thread_ring_wait(thread_ring_t *ring, Uint32 ms) -{ - int error = 0; - - ASSERT(ring != NULL && ring->magic == PRING_MAGIC); - - /* We must hold the condition variable's mutex */ - if (SDL_LockMutex(ring->mutex) != 0) { - error = -1; - goto err; - } - - /* As long as we don't have confirmation that we must stop waiting */ - for (ring->event = 0; !ring->event && error == 0; ) { - /* - * Wait on conditional variable, which will automatically - * and atomically release the mutex and return with the mutex - * locked again, as soon as the conditional variable gets - * signaled. - */ - if (ms != 0) - error = SDL_CondWaitTimeout(ring->cond, ring->mutex, - ms); - else - error = SDL_CondWait(ring->cond, ring->mutex); - } - - /* - * And we know that conditional waiting functions returned with mutex - * locked, so now release it back. - */ - (void) SDL_UnlockMutex(ring->mutex); - -err: - return error; -} - -/* - * Allows to wake up waiter(s) on the specified ring, which are sleeping - * threads within thread_ring_wait(). This can be used to simulate the - * arrival of a message on an empty port. Also useful to use rings as a - * notification system only when no message passing is needed. - */ -int -thread_ring_notify(thread_ring_t *ring) -{ - - ASSERT(ring != NULL && ring->magic == PRING_MAGIC); - - if (SDL_LockMutex(ring->mutex) != 0) - return -1; - - ring->event = 1; - (void) SDL_CondSignal(ring->cond); - - (void) SDL_UnlockMutex(ring->mutex); - - return 0; -} - -/* - * Allows to initialize/create a message port. - */ -int -thread_port_init(thread_port_t *port) -{ - - ASSERT(port != NULL && port->magic != PPORT_MAGIC); - - if ((port->lock = SDL_CreateMutex()) == NULL) - return -1; - - port->magic = PPORT_MAGIC; - port->ring = NULL; - DLIST_INIT(&port->messages); - - return 0; -} - -/* - * Returns TRUE if the supplied port is valid/usable, or FALSE otherwise. - * Useful to conditionally destroy a port, for instance. - */ -int -thread_port_valid(thread_port_t *port) -{ - - return (port != NULL && port->magic == PPORT_MAGIC); -} - -/* - * Destroys the specified port, previously created using thread_port_init(). - */ -void -thread_port_destroy(thread_port_t *port) -{ - - ASSERT(port != NULL && port->magic == PPORT_MAGIC); - - port->magic = 0; - SDL_DestroyMutex(port->lock); -} - -/* - * Attaches a port to a ring. Multiple ports may be attached to a ring. A - * message arriving on an empty port will cause the attached ring to be - * notified, if any, and as such to cause a thread waiting on the ring to - * be awakened. - */ -void -thread_port_set_ring(thread_port_t *port, thread_ring_t *ring) -{ - - ASSERT(port != NULL && port->magic == PPORT_MAGIC && - (ring == NULL || ring->magic == PRING_MAGIC)); - - port->ring = ring; -} - -/* - * Allows to initialize a message before it can be sent over a port. The - * message only needs to be initialized once in general, even if it will be - * used for bidirectional transmission for synchronous operation. If the - * reply port needs to be changed, however, this function should be used again - * to set the new reply port. - */ -void -thread_msg_init(thread_msg_t *msg, thread_port_t *rport) -{ - - ASSERT(msg != NULL && msg->magic != PMESG_MAGIC && - (rport == NULL || rport->magic == PPORT_MAGIC)); - - msg->magic = PMESG_MAGIC; - msg->reply = rport; -} - -/* - * Returns TRUE if supplied message is valid/usable or FALSE otherwise. - */ -int -thread_msg_valid(thread_msg_t *msg) -{ - - return (msg != NULL && msg->magic == PMESG_MAGIC); -} - -/* - * Invalidates a message, so that it can no longer be sent over ports. - */ -void -thread_msg_destroy(thread_msg_t *msg) -{ - - ASSERT(msg != NULL && msg->magic == PMESG_MAGIC); - - msg->magic = 0; -} - -/* - * If any message exists in the queue of the specified port, unqueues it and - * returns it. Otherwise, NULL is returned. In normal operation, all messages - * queued to a port are processed before putting the thread back into sleep, - * mainly for efficiency, but also because it eases synchronization. - */ -thread_msg_t * -thread_msg_get(thread_port_t *port) -{ - thread_msg_t *msg = NULL; - - ASSERT(port != NULL && port->magic == PPORT_MAGIC); - - if (SDL_LockMutex(port->lock) != 0) - goto err; - - if ((msg = DLIST_TOP(&port->messages)) != NULL) { - ASSERT(msg->magic == PMESG_MAGIC); - DLIST_UNLINK(&port->messages, (node_t *)msg); - } - - (void) SDL_UnlockMutex(port->lock); - -err: - return (thread_msg_t *)msg; -} - -/* - * Queues the specified message to the specified port, returning 0 on success. - * Note that the message data is not copied or moved, but that a pointer - * system is used to queue the message. Thus, the message's shared memory - * region is leased temporarily to the other end. One has to be careful to - * not allocate this message space on the stack when asynchroneous operation - * is needed. In synchroneous operation mode, it is not a problem, since the - * sender does not have to modify the data until the other end replies back - * with the same message after modifying the message if necessary. In - * synchroneous mode, we simply delegate that message memory region to the - * other end until it notifies us with a reply that it is done working with - * it. Returns 0 on success, or -1 on error. - */ -int -thread_msg_put(thread_port_t *port, thread_msg_t *msg) -{ - - ASSERT(port != NULL && port->magic == PPORT_MAGIC && - msg != NULL && msg->magic == PMESG_MAGIC); - - if (SDL_LockMutex(port->lock) != 0) - return -1; - - DLIST_APPEND(&port->messages, (node_t *)msg); - if (port->ring != NULL) { - if (DLIST_NODES(&port->messages) == 1) { - /* - * We know that there previously were no messages, - * and that the reading thread then waits for any - * message to be available. Signal it that there at - * least is one message ready. The other end should - * normally process all available messages before - * going back into waiting. - */ - if (SDL_LockMutex(port->ring->mutex) == 0) { - port->ring->event = 1; - (void) SDL_CondSignal(port->ring->cond); - (void) SDL_UnlockMutex(port->ring->mutex); - } - } - } - - (void) SDL_UnlockMutex(port->lock); - - return 0; -} - -/* - * Meant to be used in synchroneous message transfer mode. The initial sender - * sends a message to the other end, which then uses this function to notify - * back the initial sender that it is done, often with a success/failure - * result as part of the message. Returns 0 on success, or -1 on error. - */ -int -thread_msg_reply(thread_msg_t *msg) -{ - - ASSERT(msg != NULL && msg->magic == PMESG_MAGIC && msg->reply != NULL); - - return thread_msg_put(msg->reply, msg); -} - -/* - * Returns the number of pending messages tied to the port, if any, or -1 - * on error. - */ -int -thread_port_pending(thread_port_t *port) -{ - int pending = -1; - - ASSERT(port != NULL && port->magic == PPORT_MAGIC); - - if (SDL_LockMutex(port->lock) == 0) { - pending = (int)DLIST_NODES(&port->messages); - (void) SDL_UnlockMutex(port->lock); - } - - return pending; -} - -/* - * Initializes the asynchroneous messages pool and mutex. Must be called - * before thread_amsg_create() and thread_amsg_destroy() can be used. - * Returns 0 on success or -1 on error. - */ -int -thread_amsg_pool_init(void) -{ - - if (pool_init(&amsg_pool, "amsg_pool", malloc, free, amsg_create, - amsg_destroy, sizeof(thread_amsg_t), 64, 1, 0) == -1) - return -1; - - if ((amsg_mutex = SDL_CreateMutex()) == NULL) { - pool_destroy(&amsg_pool); - return -1; - } - - amsg_initialized = 1; - - return 0; -} - -static int -amsg_create(pnode_t *p) -{ - - thread_msg_init((thread_msg_t *)p, NULL); - - return 0; -} - -static void -amsg_destroy(pnode_t *p) -{ - - thread_msg_destroy((thread_msg_t *)p); -} - -/* - * Creates an amsg and returns a pointer to it, or NULL on failure. - * This is useful to efficiently create/send asynchroneous messages to - * another thread which is expected to receive/destroy it asynchroneously - * without replying, in which case a pool of distinct messages must be used. - */ -thread_amsg_t * -thread_amsg_create(void) -{ - thread_amsg_t *amsg = NULL; - - ASSERT(amsg_initialized); - - if (SDL_LockMutex(amsg_mutex) == 0) { - amsg = (thread_amsg_t *)pool_alloc(&amsg_pool, 0); - SDL_UnlockMutex(amsg_mutex); - } - - return amsg; -} - -/* - * Destroys an amsg previously created with thread_amsg_create(). - */ -void -thread_amsg_destroy(thread_amsg_t *amsg) -{ - - ASSERT(amsg_initialized && amsg != NULL); - - if (SDL_LockMutex(amsg_mutex) == 0) { - (void) pool_free((pnode_t *)amsg); - SDL_UnlockMutex(amsg_mutex); - } -} diff --git a/tests/sdl-client/thread_msg.h b/tests/sdl-client/thread_msg.h deleted file mode 100644 index 157cad8..0000000 --- a/tests/sdl-client/thread_msg.h +++ /dev/null @@ -1,86 +0,0 @@ -/* $Id: thread_msg.h,v 1.9 2006/05/19 10:14:35 mmondor Exp $ */ - -/* - * Copyright (C) 2006, Matthew Mondor - * All rights reserved. - * - * Adapted from older code intended for use with POSIX threads, - * for use within SDL. - */ - - - -#ifndef THREAD_MSG_H -#define THREAD_MSG_H - - - -#include - -#include -#include - -#include -#include - - - -#define PRING_MAGIC 0x50524e47 -#define PPORT_MAGIC 0x50505254 -#define PMESG_MAGIC 0x504d5347 - -#define AMSG_MAXSIZE 256 - -typedef struct { - uint32_t magic; - SDL_cond *cond; - SDL_mutex *mutex; - int event; -} thread_ring_t; - -typedef struct { - uint32_t magic; - thread_ring_t *ring; - SDL_mutex *lock; - list_t messages; -} thread_port_t; - -typedef struct { - pnode_t node; - uint32_t magic; - thread_port_t *reply; -} thread_msg_t; - -typedef struct { - thread_msg_t msg; - size_t size; - uint32_t data[AMSG_MAXSIZE / 4]; -} thread_amsg_t; - - - -extern int thread_ring_init(thread_ring_t *); -extern int thread_ring_valid(thread_ring_t *); -extern void thread_ring_destroy(thread_ring_t *); -extern int thread_ring_wait(thread_ring_t *, Uint32); -extern int thread_ring_notify(thread_ring_t *); - -extern int thread_port_init(thread_port_t *); -extern int thread_port_valid(thread_port_t *); -extern void thread_port_destroy(thread_port_t *); -extern void thread_port_set_ring(thread_port_t *, thread_ring_t *); -extern void thread_msg_init(thread_msg_t *, thread_port_t *); -extern int thread_msg_valid(thread_msg_t *); -extern void thread_msg_destroy(thread_msg_t *); -extern thread_msg_t *thread_msg_get(thread_port_t *); -extern int thread_msg_put(thread_port_t *, thread_msg_t *); -extern int thread_msg_reply(thread_msg_t *); -extern int thread_port_pending(thread_port_t *); - -extern int thread_amsg_pool_init(void); -extern thread_amsg_t *thread_amsg_create(void); -extern void thread_amsg_destroy(thread_amsg_t *); - - - -#endif diff --git a/tests/sdl-client/thread_net_recv.c b/tests/sdl-client/thread_net_recv.c deleted file mode 100644 index efa0da0..0000000 --- a/tests/sdl-client/thread_net_recv.c +++ /dev/null @@ -1,191 +0,0 @@ -/* $Id: thread_net_recv.c,v 1.4 2006/05/19 14:54:38 mmondor Exp $ */ - -/* - * Copyright (c) 2006, Matthew Mondor - * ALL RIGHTS RESERVED. - */ - -/* - * User events polling thread. Reports all events to the master thread - * via efficient inter-thread messages. - */ - - - -#include -#include - -#include -#include -#include - - - -static void ssend(thread_msg_t *); -static int state_connect(void); -static void state_recv(void); -static ssize_t server_recv(void *, size_t); - - - -static thread_ring_t recv_ring; -static thread_port_t recv_port; - - - -TCPsocket server_socket = NULL; - - - -int -thread_net_recv(void *data) -{ - - /* Initialize reply port and ring */ - if (thread_ring_init(&recv_ring) == -1) { - (void) fprintf(stderr, - "thread_net_recv() - thread_ring_init(recv_port) - %s\n", - SDL_GetError()); - exit(EXIT_FAILURE); - } - if (thread_port_init(&recv_port) == -1) { - (void) fprintf(stderr, - "thread_net_recv() - thread_port_init(recv_port) - %s\n", - SDL_GetError()); - exit(EXIT_FAILURE); - } - thread_port_set_ring(&recv_port, &recv_ring); - - if (state_connect() == 0) - state_recv(); - - /* Wait peacefully until our process exits */ - for (;;) - (void) thread_ring_wait(&recv_ring, -1); - - /* NOTREACHED */ - - return 0; -} - -/* - * Sends a message synchroneously, that is, sends and waits for a reply. - */ -static void -ssend(thread_msg_t *msg) -{ - thread_msg_t *rmsg; - - if (thread_msg_put(&main_port, msg) == -1) - (void) fprintf(stderr, - "ssend() - thread_msg_put()\n"); - while ((rmsg = thread_msg_get(&recv_port)) == NULL) { - if (thread_ring_wait(&recv_ring, -1) != 0) - (void) fprintf(stderr, - "ssend() - thread_ring_wait()\n"); - } -} - -/* - * Attempts to connect to server. On success, 0 is returned and a success - * reply packet is sent synchroneously to the main thread. On error, a - * failure packet is sent instead and we return -1. - */ -static int -state_connect(void) -{ - struct msg_connect msg; - IPaddress addr; - - thread_msg_init(&msg.msg, &recv_port); - msg.status = 0; - msg.error = NULL; - - if (SDLNet_ResolveHost(&addr, SERVER_HOST, SERVER_PORT) != 0) { - msg.status = -1; - msg.error = "Could not resolve server hostname"; - goto end; - } - - if ((server_socket = SDLNet_TCP_Open(&addr)) == NULL) { - msg.status = -1; - msg.error = "Could not connect to server"; - } - -end: - ssend(&msg.msg); - - return 0; -} - -/* - * We endlessly read packets from the server and queue them asynchroneously to - * the main thread's port, unless a read error occurs, in which case we queue - * an empty/error special packet and return. - */ -static void -state_recv(void) -{ - thread_amsg_t *amsg = NULL; - - for (;;) { - uint8_t type, length; - - /* - * We must already have an allocated message since we'll read - * data directly into its buffer. If this operation fails, - * it's fatal since the receiving main thread will never be - * notified. - */ - if ((amsg = thread_amsg_create()) == NULL) { - (void) fprintf(stderr, - "state_recv() - thread_amsg_create()\n"); - exit(EXIT_FAILURE); - } - - /* - * Start reading a single byte, the message type, and evaluate - * the message size. For variable packets, we also must read - * another byte for the legth. Then read the remaining of the - * message. On any invalid message, we error and stop as if - * the connection had problems, but we log it. Upon - * successful reception of a complete message, directly - * transmit it to the main thread asynchroneously and - * continue for another packet. These read operations are - * blocking automatically when no data is available to read. - */ - if (server_recv(amsg->data, 16) == -1) - break; - type = *(amsg->data); - - /* XXX */ - amsg->size = 16; - - /* Valid fixed length packet type? If so, set length */ - /* Valid variable length packet type? Obtain length */ - - /* Read remaining of packet */ - - /* Asynchroneously send packet to the main thread */ - (void) thread_msg_put(&main_port, &amsg->msg); - } - - /* Send the end of stream message to the main thread */ - amsg->size = -1; - (void) thread_msg_put(&main_port, &amsg->msg); -} - -static ssize_t -server_recv(void *data, size_t size) -{ - size_t len; - int res; - - for (len = 0; len < size; len += res) { - if ((res = SDLNet_TCP_Recv(server_socket, data + len, - size - len)) < 1) - return -1; - } - - return len; -} diff --git a/tests/sdl-client/thread_net_recv.h b/tests/sdl-client/thread_net_recv.h deleted file mode 100644 index 7a977e2..0000000 --- a/tests/sdl-client/thread_net_recv.h +++ /dev/null @@ -1,65 +0,0 @@ -/* $Id: thread_net_recv.h,v 1.1 2006/05/19 09:13:42 mmondor Exp $ */ - -/* - * Copyright (c) 2006, Matthew Mondor - * ALL RIGHTS RESERVED. - */ - -/* - * User events polling thread. Reports all events to the master thread - * via efficient inter-thread messages. - */ - - - -#ifndef THREAD_NET_RECV_H -#define THREAD_NET_RECV_H - - - -#include -#include - -#include - - - -/* - * The initial message we're waiting for from the read thread. The read - * thread will attempt to connect to the server, initiate authentication and - * then reply using this message with the connection status. Afterwards, we - * only expect msg_read packets from that thread. - * will be 0 on success or -1 on failure, in which case the error - * message string will be found into . - */ -struct msg_connect { - thread_msg_t msg; - int status; - const char *error; -}; - -/* - * We receive such a message when a packet was successfully read from the - * server. We reply in a synchroneous manner, however the reply delay should - * be very short compared to the possible delay of the read thread during a - * blocking read (SDL_net does not support non-blocking I/O operations). - * If -1 is received for the size, with a NULL pointer, it means - * that the connection status was lost or that a timeout occurred. - */ -struct msg_recv { - thread_msg_t msg; - void *data; - size_t size; -}; - - - -int thread_net_recv(void *); - - - -extern TCPsocket server_socket; - - - -#endif diff --git a/tests/sdl-client/thread_net_send.c b/tests/sdl-client/thread_net_send.c deleted file mode 100644 index 4fe7e21..0000000 --- a/tests/sdl-client/thread_net_send.c +++ /dev/null @@ -1,71 +0,0 @@ -/* $Id: thread_net_send.c,v 1.1 2006/05/19 09:13:42 mmondor Exp $ */ - -/* - * Copyright (c) 2006, Matthew Mondor - * ALL RIGHTS RESERVED. - */ - -/* - * Server socket writing thread. We post all asynchroneous messages - * (thread_amsg_t) we receive from the main thread to the server socket. - */ - - - -#include -#include - -#include -#include - - - -static thread_ring_t send_ring; - - - -thread_port_t send_port; - - - -int -thread_net_send(void *data) -{ - thread_amsg_t *amsg; - - /* Initialize reply port and ring */ - if (thread_ring_init(&send_ring) == -1) { - (void) fprintf(stderr, - "thread_net_recv() - thread_ring_init(send_port) - %s\n", - SDL_GetError()); - exit(EXIT_FAILURE); - } - if (thread_port_init(&send_port) == -1) { - (void) fprintf(stderr, - "thread_net_send() - thread_port_init(send_port) - %s\n", - SDL_GetError()); - exit(EXIT_FAILURE); - } - thread_port_set_ring(&send_port, &send_ring); - - /* - * Simply process all incomming messages, sending their data to - * the server port, and discarding the messages. When the port is - * empty, wait polling until messages become available. - */ - for (;;) { - while ((amsg = (thread_amsg_t *)thread_msg_get(&send_port)) - != NULL) { - if (SDLNet_TCP_Send(server_socket, - amsg->data, amsg->size) != amsg->size) - (void) fprintf(stderr, - "Error writing to server socket\n"); - thread_amsg_destroy(amsg); - } - (void) thread_ring_wait(&send_ring, -1); - } - - /* NOTREACHED */ - - return 0; -} diff --git a/tests/sdl-client/thread_net_send.h b/tests/sdl-client/thread_net_send.h deleted file mode 100644 index f1ae02f..0000000 --- a/tests/sdl-client/thread_net_send.h +++ /dev/null @@ -1,35 +0,0 @@ -/* $Id: thread_net_send.h,v 1.1 2006/05/19 09:13:42 mmondor Exp $ */ - -/* - * Copyright (c) 2006, Matthew Mondor - * ALL RIGHTS RESERVED. - */ - -/* - * Server socket writing thread. We post all asynchroneous messages - * (thread_amsg_t) we receive from the main thread to the server socket. - */ - - - -#ifndef THREAD_NET_SEND_H -#define THREAD_NET_SEND_H - - - -#include -#include - -#include - - - -int thread_net_send(void *); - - - -extern thread_port_t send_port; - - - -#endif diff --git a/tests/sdl-client/wav/README b/tests/sdl-client/wav/README deleted file mode 100644 index 8c7a92a..0000000 --- a/tests/sdl-client/wav/README +++ /dev/null @@ -1 +0,0 @@ -For now these have been borrowed from the netrekxp client. diff --git a/tests/sdl-client/wav/nt_cloaked.wav b/tests/sdl-client/wav/nt_cloaked.wav deleted file mode 100755 index 3e010dd162f08a4ea14a8ce1e0bf36078eb7cd9b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 198704 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

    U$dpU>Of~^!vM- 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

    LFI>V(w-MTRW3BvH)Di_MPW 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 -#include - -#include - - - -#define RBUF_SIZE 4096 -#define DBUF_SIZE (RBUF_SIZE * 2) - - - -int main(void); - - - -int -main(void) -{ - z_stream s; - char rbuf[RBUF_SIZE], dbuf[DBUF_SIZE]; - size_t size; - - if (setvbuf(stdin, NULL, _IONBF, 0) == EOF) - perror("setvbuf(stdin)"); - if (setvbuf(stdout, NULL, _IONBF, 0) == EOF) - perror("setvbuf(stdout)"); - - s.next_in = s.next_out = NULL; - s.avail_in = s.avail_out = 0; - s.zalloc = NULL; - s.zfree = NULL; - s.opaque = NULL; - - if (deflateInit(&s, Z_DEFAULT_COMPRESSION) != Z_OK) { - perror("deflateInit()"); - exit(EXIT_FAILURE); - } - - while (!feof(stdin) && !ferror(stdin)) { - if ((size = fread(rbuf, 1, RBUF_SIZE, stdin)) < 1) - continue; - s.next_in = rbuf; - s.avail_in = size; - s.next_out = dbuf; - s.avail_out = DBUF_SIZE; - if (deflate(&s, Z_NO_FLUSH/*Z_SYNC_FLUSH*/) != Z_OK) { - perror("deflate()"); - exit(EXIT_FAILURE); - } - if (fwrite(dbuf, 1, DBUF_SIZE - s.avail_out, stdout) - != DBUF_SIZE - s.avail_out) { - perror("fwrite()"); - exit(EXIT_FAILURE); - } - } - - s.next_in = rbuf; - s.avail_in = 0; - s.next_out = dbuf; - s.avail_out = DBUF_SIZE; - if (deflate(&s, Z_FINISH) != Z_STREAM_END) { - perror("deflate()"); - exit(EXIT_FAILURE); - } - if (fwrite(dbuf, 1, DBUF_SIZE - s.avail_out, stdout) - != DBUF_SIZE - s.avail_out) { - perror("fwrite()"); - exit(EXIT_FAILURE); - } - - if (deflateEnd(&s) != Z_OK) { - perror("deflateEnd()"); - exit(EXIT_FAILURE); - } - - exit(feof(stdin) ? EXIT_SUCCESS : EXIT_FAILURE); -} diff --git a/tests/zlib/inflate.c b/tests/zlib/inflate.c deleted file mode 100644 index 59b807b..0000000 --- a/tests/zlib/inflate.c +++ /dev/null @@ -1,91 +0,0 @@ -/* $Id: inflate.c,v 1.1 2006/06/15 14:23:24 mmondor Exp $ */ - -/* - * Copyright (c) 2006, Matthew Mondor - * ALL RIGHTS RESERVED. - */ - - - -#include -#include - -#include - - - -#define RBUF_SIZE 4096 -#define IBUF_SIZE (RBUF_SIZE * 64) - - - -int main(void); - - - -int -main(void) -{ - z_stream s; - char rbuf[RBUF_SIZE], ibuf[IBUF_SIZE]; - size_t size; - int ret; - - if (setvbuf(stdin, NULL, _IONBF, 0) == EOF) - perror("setvbuf(stdin)"); - if (setvbuf(stdout, NULL, _IONBF, 0) == EOF) - perror("setvbuf(stdout)"); - - s.next_in = s.next_out = NULL; - s.avail_in = s.avail_out = 0; - s.zalloc = NULL; - s.zfree = NULL; - s.opaque = NULL; - - if (inflateInit(&s) != Z_OK) { - perror("inflateInit()"); - exit(EXIT_FAILURE); - } - - while (!feof(stdin) && !ferror(stdin)) { - if ((size = fread(rbuf, 1, RBUF_SIZE, stdin)) < 1) - continue; - s.next_in = rbuf; - s.avail_in = size; - s.next_out = ibuf; - s.avail_out = IBUF_SIZE; - if ((ret = inflate(&s, Z_SYNC_FLUSH)) != Z_OK && - ret != Z_STREAM_END) { - perror("inflate()"); - exit(EXIT_FAILURE); - } - if (fwrite(ibuf, 1, IBUF_SIZE - s.avail_out, stdout) - != IBUF_SIZE - s.avail_out) { - perror("fwrite()"); - exit(EXIT_FAILURE); - } - if (ret == Z_STREAM_END) - break; - } - - s.next_in = rbuf; - s.avail_in = 0; - s.next_out = ibuf; - s.avail_out = IBUF_SIZE; - if (inflate(&s, Z_FINISH) != Z_STREAM_END) { - perror("inflate()"); - exit(EXIT_FAILURE); - } - if (fwrite(ibuf, 1, IBUF_SIZE - s.avail_out, stdout) - != IBUF_SIZE - s.avail_out) { - perror("fwrite()"); - exit(EXIT_FAILURE); - } - - if (inflateEnd(&s) != Z_OK) { - perror("inflateEnd()"); - exit(EXIT_FAILURE); - } - - exit(feof(stdin) ? EXIT_SUCCESS : EXIT_FAILURE); -} -- 2.9.0