From: Matthew Mondor Date: Tue, 13 Mar 2007 20:28:24 +0000 (+0000) Subject: This commit was manufactured by cvs2svn to create tag 'pthread-branch- X-Git-Tag: pthread-branch-merge X-Git-Url: http://git.pulsar-zone.net/?a=commitdiff_plain;h=36be6cdbf46a395e4c03445c206dd8c50a295fc4;p=mmondor.git This commit was manufactured by cvs2svn to create tag 'pthread-branch- merge'. --- 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/TODO.txt b/mmsoftware/js/classes/TODO.txt deleted file mode 100644 index f323174..0000000 --- a/mmsoftware/js/classes/TODO.txt +++ /dev/null @@ -1,8 +0,0 @@ -- Implement security policy module -- Add berkeley db4 support -- Add a Timer module - - Should have sleep, usleep, nanosleep etc, as well as setitimer support - and/or possibly support mmalarm(3) -- Add a Signal module to install custom JS handlers. Ideally, signal would - get queued then user handler called when exiting the signal handler... - With automatic signal blocking during processing. diff --git a/mmsoftware/js/classes/js_cgi.c b/mmsoftware/js/classes/js_cgi.c deleted file mode 100644 index abfd774..0000000 --- a/mmsoftware/js/classes/js_cgi.c +++ /dev/null @@ -1,191 +0,0 @@ -/* $Id: js_cgi.c,v 1.1 2006/09/08 08:04:58 mmondor Exp $ */ - -/* - * Copyright (c) 2006, Matthew Mondor - * ALL RIGHTS RESERVED. - */ - - - -#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) - - -struct property_spec { - const char *name; - int value; -}; - - - -/* - * Static prototypes - */ -static JSBool cgi_constructor(JSContext *, JSObject *, uintN, jsval *, - jsval *); - -static JSBool cgi_sm_JSON(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 cgi_class = { - "CGI", 0, JS_PropertyStub, JS_PropertyStub, - JS_PropertyStub, JS_PropertyStub, JS_EnumerateStub, JS_ResolveStub, - JS_ConvertStub, JS_FinalizeStub -}; - -/* Provided static methods */ -static JSFunctionSpec cgi_smethods[] = { - { "JSON", cgi_sm_json, 2, 0, 0 }, /* XXX Check naming */ - { NULL, NULL, 0, 0, 0 } -}; - -/* Provided static properties */ - -#define SP(n) \ - { #n, n } - -static struct property_spec cgi_sprops[] = { - { NULL, 0 } -}; - -#undef SP - - -/* - * CGI object control - */ - -JSObject * -js_InitCGIClass(JSContext *cx, JSObject *obj) -{ - JSObject *proto, *ctor; - struct property_spec *sp; - - if ((proto = JS_InitClass(cx, obj, NULL, &cgi_class, cgi_constructor, 0, - NULL, NULL, NULL, cgi_smethods)) == NULL) { - (void) fprintf(stderr, "Error initializing CGI class\n"); - goto err; - } - - /* Create static properties */ - if ((ctor = JS_GetConstructor(cx, proto)) == NULL) { - (void) fprintf(stderr, "CGI: JS_GetConstructor == NULL\n"); - goto err; - } - for (sp = cgi_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, - "CGI: Error defining property %s\n", sp->name); - goto err; - } - } - - return proto; - -err: - return NULL; -} - -/* Non instanciable */ -static JSBool -cgi_constructor(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - - QUEUE_EXCEPTION("CGI class uninstanciable"); - - return JS_FALSE; -} - - -/* - * CGI object static methods - */ - -/* - * Secure replacement for eval('o = ' + user_defined_data); which is unsafe. - * We only support Strings, (using ""), Arrays (using []) objects (using {}) - * and numbers (using no special marker). This function allows recursiveness - * up to a certain level allowed by the function. We return an object with - * the unserialized data, or null on error. We don't allow functions. - */ -static JSBool -cgi_sm_json(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - - if (argc != 2) { - QUEUE_EXCEPTION("Wrong number of arguments"); - return JS_FALSE; - } - if (!JS_IS_STRING(argv[0])) { - QUEUE_EXCEPTION("Argument 1 not a String"); - return JS_FALSE; - } - if (!JS_IS_INT(argv[1])) { - QUEUE_EXCEPTION("Argument 2 not an Integer"); - return JS_FALSE; - } - - /* XXX */ - json_level = 0; -} - -static int json_level; - -static JSObject * -json_object(const char *s) -{ -} - -static JSArray * -json_array(const char *s) -{ -} - -static JSString * -json_string(const char *) -{ -} - -static jsval -json_number(const char *) -{ -} diff --git a/mmsoftware/js/classes/js_cgi.h b/mmsoftware/js/classes/js_cgi.h deleted file mode 100644 index 6fa33ba..0000000 --- a/mmsoftware/js/classes/js_cgi.h +++ /dev/null @@ -1,15 +0,0 @@ -/* $Id: js_cgi.h,v 1.1 2006/09/08 08:04:58 mmondor Exp $ */ - -/* - * Copyright (c) 2006, Matthew Mondor - * ALL RIGHTS RESERVED. - */ - -#ifndef JSCGI_H -#define JSCGI_H - -#include - -extern JSObject *js_InitCGIClass(JSContext *, JSObject *); - -#endif diff --git a/mmsoftware/js/classes/js_dir.c b/mmsoftware/js/classes/js_dir.c deleted file mode 100644 index 8e80cc3..0000000 --- a/mmsoftware/js/classes/js_dir.c +++ /dev/null @@ -1,368 +0,0 @@ -/* $Id: js_dir.c,v 1.7 2006/10/27 05:38:50 mmondor Exp $ */ - -/* - * Copyright (c) 2006, Matthew Mondor - * ALL RIGHTS RESERVED. - */ - - - -#include - -#include - -#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) - - -struct property_spec { - const char *name; - int value; -}; - -#define SP(n) \ - { #n, n } - - - -/* - * Static prototypes - */ -static JSBool dir_constructor(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static void dir_finalize(JSContext *, JSObject *); - -static JSBool dir_m_read(JSContext *, JSObject *, uintN, jsval *, jsval *); -static JSBool dir_m_tell(JSContext *, JSObject *, uintN, jsval *, jsval *); -static JSBool dir_m_seek(JSContext *, JSObject *, uintN, jsval *, jsval *); -static JSBool dir_m_rewind(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool dir_m_close(JSContext *, JSObject *, uintN, jsval *, jsval *); - -static int dir_path_allow(const char *); - - - -/* - * Static globals - */ - -/* Dir class */ -static JSClass dir_class = { - "Dir", JSCLASS_HAS_PRIVATE, JS_PropertyStub, JS_PropertyStub, - JS_PropertyStub, JS_PropertyStub, JS_EnumerateStub, JS_ResolveStub, - JS_ConvertStub, dir_finalize -}; - -/* Provided methods */ -static JSFunctionSpec dir_methods[] = { - { "read", dir_m_read, 0, 0, 0 }, - { "tell", dir_m_tell, 0, 0, 0 }, - { "seek", dir_m_seek, 1, 0, 0 }, - { "rewind", dir_m_rewind, 0, 0, 0 }, - { "close", dir_m_close, 0, 0, 0 }, - { NULL, NULL, 0, 0, 0 }, -}; - -/* Static properties */ - -static struct property_spec dir_sprops[] = { - SP(DT_UNKNOWN), - SP(DT_FIFO), - SP(DT_CHR), - SP(DT_DIR), - SP(DT_BLK), - SP(DT_REG), - SP(DT_LNK), - SP(DT_SOCK), -#ifdef DT_WHT - SP(DT_WHT), -#endif - { NULL, 0 } -}; - - - -/* - * Dir object control - */ - -JSObject * -js_InitDirClass(JSContext *cx, JSObject *obj) -{ - JSObject *proto, *ctor; - struct property_spec *sp; - - if ((proto = JS_InitClass(cx, obj, NULL, &dir_class, dir_constructor, - 0, NULL, dir_methods, NULL, NULL)) == NULL) { - (void) fprintf(stderr, "Error initializing Dir class\n"); - goto err; - } - - /* Create static properties. */ - if ((ctor = JS_GetConstructor(cx, proto)) == NULL) { - (void) fprintf(stderr, "Dir: JS_GetConstructor == NULL\n"); - return NULL; - } - for (sp = dir_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, - "Dir: Error defining property %s\n", sp->name); - return NULL; - } - } - - return proto; - -err: - return NULL; -} - -static JSBool -dir_constructor(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - DIR *dir = NULL; - char *path; - - if (!JS_IsConstructing(cx)) { - QUEUE_EXCEPTION("Constructor called as a function"); - goto err; - } - - if (argc != 1) { - QUEUE_EXCEPTION("Wrong number of arguments"); - goto err; - } - if (!JSVAL_IS_STRING(argv[0]) || - ((path = JS_GetStringBytes(JSVAL_TO_STRING(argv[0])))) == NULL) { - QUEUE_EXCEPTION("Argument not a string"); - goto err; - } - - if (dir_path_allow(path) == -1) { - char str[1024]; - - (void) snprintf(str, sizeof(str), "opendir('%s'): %s", - path, "Denied by security hook dir_path_allow()"); - QUEUE_EXCEPTION(str); - goto err; - } - - if ((dir = opendir(path)) == NULL) { - char str[1024]; - - (void) snprintf(str, sizeof(str), "opendir('%s'): %s", - path, strerror(errno)); - QUEUE_EXCEPTION(str); - goto err; - } - - if (!JS_SetPrivate(cx, obj, dir)) { - QUEUE_EXCEPTION("Internal error!"); - goto err; - } - - return JS_TRUE; - -err: - *rval = OBJECT_TO_JSVAL(NULL); - if (dir != NULL) - (void) closedir(dir); - - return JS_FALSE; -} - -static void -dir_finalize(JSContext *cx, JSObject *obj) -{ - DIR *dir; - - if ((dir = JS_GetInstancePrivate(cx, obj, &dir_class, NULL)) != NULL) { - (void) closedir(dir); - (void) JS_SetPrivate(cx, obj, NULL); - } -} - - - -/* Methods */ -static JSBool -dir_m_read(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - DIR *dir; - struct dirent *de; - JSObject *o; - jsval val; - JSString *str; - - if (argc != 0) { - QUEUE_EXCEPTION("Function allows no arguments"); - *rval = OBJECT_TO_JSVAL(NULL); - return JS_FALSE; - } - - dir = JS_GetInstancePrivate(cx, obj, &dir_class, NULL); - assert(dir != NULL); - - if ((de = readdir(dir)) == NULL) { - *rval = OBJECT_TO_JSVAL(NULL); - return JS_TRUE; - } - - if ((o = JS_NewObject(cx, NULL, NULL, NULL)) == NULL) { - QUEUE_EXCEPTION("Internal error!"); - *rval = OBJECT_TO_JSVAL(NULL); - return JS_FALSE; - } - *rval = OBJECT_TO_JSVAL(o); - - /* d_fileno is u_int32_t and so could exceed a JS int, so use this */ - if (!JS_NewNumberValue(cx, (jsdouble)de->d_fileno, &val) || - !JS_DefineProperty(cx, o, "fileno", val, NULL, NULL, - JSPROP_ENUMERATE)) - goto err; - - if (!JS_DefineProperty(cx, o, "type", INT_TO_JSVAL(de->d_type), NULL, - NULL, JSPROP_ENUMERATE)) - goto err; - - /* We can't use d_namlen on Linux - if ((str = JS_NewStringCopyN(cx, de->d_name, de->d_namlen)) == NULL || - !JS_DefineProperty(cx, o, "name", STRING_TO_JSVAL(str), NULL, - NULL, JSPROP_ENUMERATE)) - goto err; - */ - if ((str = JS_NewStringCopyZ(cx, de->d_name)) == NULL || - !JS_DefineProperty(cx, o, "name", STRING_TO_JSVAL(str), NULL, - NULL, JSPROP_ENUMERATE)) - goto err; - - return JS_TRUE; - -err: - QUEUE_EXCEPTION("Internal error!"); - *rval = OBJECT_TO_JSVAL(NULL); - return JS_FALSE; -} - -static JSBool -dir_m_tell(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - DIR *dir; - long v; - - if (argc != 0) { - QUEUE_EXCEPTION("Function allows no arguments"); - *rval = OBJECT_TO_JSVAL(NULL); - return JS_FALSE; - } - - dir = JS_GetInstancePrivate(cx, obj, &dir_class, NULL); - assert(dir != NULL); - - v = telldir(dir); - - if (!JS_NewNumberValue(cx, (jsdouble)v, rval)) { - QUEUE_EXCEPTION("Internal error!"); - *rval = OBJECT_TO_JSVAL(NULL); - return JS_FALSE; - } - - return JS_TRUE; -} - -static JSBool -dir_m_seek(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - DIR *dir; - jsdouble v; - - *rval = OBJECT_TO_JSVAL(NULL); - - if (argc != 1) { - QUEUE_EXCEPTION("Wrong number of arguments"); - return JS_FALSE; - } - if (!JSVAL_IS_NUMBER(argv[0]) || - !JS_ValueToNumber(cx, argv[0], &v)) { - QUEUE_EXCEPTION("Argument 1 not a number"); - return JS_FALSE; - } - - dir = JS_GetInstancePrivate(cx, obj, &dir_class, NULL); - assert(dir != NULL); - - seekdir(dir, (long)v); - - return JS_TRUE; -} - -static JSBool -dir_m_rewind(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - DIR *dir; - - *rval = OBJECT_TO_JSVAL(NULL); - - if (argc != 0) { - QUEUE_EXCEPTION("Function allows no arguments"); - return JS_FALSE; - } - - dir = JS_GetInstancePrivate(cx, obj, &dir_class, NULL); - assert(dir != NULL); - - rewinddir(dir); - - return JS_TRUE; -} - -static JSBool -dir_m_close(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - DIR *dir; - int v; - - if (argc != 0) { - QUEUE_EXCEPTION("Function allows no arguments"); - *rval = OBJECT_TO_JSVAL(NULL); - return JS_FALSE; - } - - dir = JS_GetInstancePrivate(cx, obj, &dir_class, NULL); - assert(dir != NULL); - - v = closedir(dir); - *rval = INT_TO_JSVAL(v); - (void) JS_SetPrivate(cx, obj, NULL); - - return JS_TRUE; -} - - -/* Security hook could be added here */ -static int -dir_path_allow(const char *path) -{ - - return 0; -} diff --git a/mmsoftware/js/classes/js_dir.h b/mmsoftware/js/classes/js_dir.h deleted file mode 100644 index 9bd504b..0000000 --- a/mmsoftware/js/classes/js_dir.h +++ /dev/null @@ -1,15 +0,0 @@ -/* $Id: js_dir.h,v 1.1 2006/09/08 12:50:43 mmondor Exp $ */ - -/* - * Copyright (c) 2006, Matthew Mondor - * ALL RIGHTS RESERVED. - */ - -#ifndef JSDIR_H -#define JSDIR_H - -#include - -extern JSObject *js_InitDirClass(JSContext *, JSObject *); - -#endif diff --git a/mmsoftware/js/classes/js_errno.c b/mmsoftware/js/classes/js_errno.c deleted file mode 100644 index 829b0b6..0000000 --- a/mmsoftware/js/classes/js_errno.c +++ /dev/null @@ -1,235 +0,0 @@ -/* $Id: js_errno.c,v 1.2 2006/07/28 02:50: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_constructor(JSContext *, JSObject *, uintN, jsval *, - jsval *); - -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, - errno_constructor, 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; -} - -/* ARGSUSED */ -static JSBool -errno_constructor(JSContext *cx, JSObject *org, uintN argc, jsval *argv, - jsval *rval) -{ - - QUEUE_EXCEPTION("Errno object not user instanciable"); - - return JS_FALSE; -} - - -/* - * 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 fef8fb7..0000000 --- a/mmsoftware/js/classes/js_fd.c +++ /dev/null @@ -1,2120 +0,0 @@ -/* $Id: js_fd.c,v 1.10 2006/09/15 21:04:48 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... - * - fchdir(2) XXX - */ - - - -#include -#include -#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 << 1), - JSAT_DOUBLE = (1 << 2), - JSAT_STRING = (1 << 3), - JSAT_OBJECT = (1 << 4) -}; - - -/* 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_ftruncate(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_fchown(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_FCHOWN, - FDMA_FCHMOD, - FDMA_FLOCK, - FDMA_FSTAT, - FDMA_FTRUNCATE, - FDMA_MAX -}; - -static int fd_methods_args_array[FDMA_MAX][6] = { - { 1, JSAT_INTEGER }, /* SET */ - { 0 }, /* CLOSE */ - { 1, JSAT_DOUBLE | JSAT_INTEGER }, /* 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, JSAT_INTEGER },/* LSEEK */ - { 2, JSAT_STRING | JSAT_INTEGER, - JSAT_STRING | JSAT_INTEGER }, /* FCHOWN */ - { 1, JSAT_INTEGER }, /* FCHMOD */ - { 1, JSAT_INTEGER }, /* FLOCK */ - { 0 }, /* FSTAT */ - { 1, JSAT_DOUBLE | JSAT_INTEGER }, /* FTRUNCATE */ -}; - -/* 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 }, - { "ftruncate", fd_m_ftruncate, 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 }, - { "fchown", fd_m_fchown, 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), -#ifdef POLLRDNORM - SP(POLLRDNORM), -#endif -#ifdef POLLRDBAND - SP(POLLRDBAND), -#endif - SP(POLLPRI), - SP(POLLOUT), -#ifdef POLLWRNORM - SP(POLLWRNORM), -#endif -#ifdef POLLWRBAND - SP(POLLWRBAND), -#endif - 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), -#ifdef SO_REUSEPORT - SP(SO_REUSEPORT), -#endif - 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), -#ifdef S_IFWHT - SP(S_IFWHT), -#endif -#ifdef UF_NODUMP - SP(UF_NODUMP), -#endif -#ifdef UF_IMMUTABLE - SP(UF_IMMUTABLE), -#endif -#ifdef UF_APPEND - SP(UF_APPEND), -#endif -#ifdef UF_OPAQUE - SP(UF_OPAQUE), -#endif -#ifdef SF_ARCHIVED - SP(SF_ARCHIVED), -#endif -#ifdef SF_IMMUTABLE - SP(SF_IMMUTABLE), -#endif -#ifdef SF_APPEND - SP(SF_APPEND), -#endif - - SP(LOCK_SH), - SP(LOCK_EX), - SP(LOCK_NB), - SP(LOCK_UN), - - { NULL, 0 } -}; - -#undef SP - - - -/* - * Miscelaneous static globals - */ - -static int tcp_proto = 4; - -/* - * These would cause problems with reentrancy. These buffers should be FD - * specific to be thread-safe. - */ - -/* Read buffer */ -static char *read_charbuf = NULL; -static size_t read_charbuf_size = 0; - -/* Poll buffers */ -static struct poll_fds fds = { NULL, NULL, 0, 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; - } - } - - /* Initialize useful globals for performance */ - { - struct protoent *pent; - - if ((pent = getprotobyname("TCP")) != NULL) - tcp_proto = pent->p_proto; - } - - 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); - /* XXX We now need to allow arbitrary descriptors! - if (fd < STDIN_FILENO || fd > STDERR_FILENO) { - QUEUE_EXCEPTION("Unknown standard descriptor"); - return JS_FALSE; - } - */ - if (fd == -1) { - QUEUE_EXCEPTION("Invalid descriptor (-1)"); - 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_ftruncate(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])); - sinaddr.sin_family = AF_INET; - - 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; - } - *rval = OBJECT_TO_JSVAL(nobj); - - 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)); - - 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; -} - -/* - * 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) - 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; -} - -/* - * 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) - 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; -} - -/* - * XXX Not thread-safe as it uses a global read buffer. To avoid this, read - * buffers could be FD object specific instead, although this would not be as - * efficient (in case new FD objects are frequently created), and would - * require more redundant buffer memory. - */ -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_fchown(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - jsfd_t *jsfd; - struct passwd *pwd; - struct group *grp; - char *str, e[1024]; - uid_t uid; - gid_t gid; - - *rval = OBJECT_TO_JSVAL(NULL); - - if ((jsfd = fd_methods_args_check(cx, obj, "fchown", FDMA_FCHOWN, - argc, argv, JSFD_FILE)) == NULL) - return JS_FALSE; - - if (JSVAL_IS_STRING(argv[0])) { - if ((str = JS_GetStringBytes(JSVAL_TO_STRING(argv[0]))) - == NULL) { - QUEUE_EXCEPTION("Internal error!"); - return JS_FALSE; - } - if ((pwd = getpwnam(str)) == NULL) { - (void) snprintf(e, 1023, "Unknown user '%s'", str); - QUEUE_EXCEPTION(e); - return JS_FALSE; - } - uid = pwd->pw_uid; - } else - uid = JSVAL_TO_INT(argv[0]); - - if (JSVAL_IS_STRING(argv[1])) { - if ((str = JS_GetStringBytes(JSVAL_TO_STRING(argv[1]))) - == NULL) { - QUEUE_EXCEPTION("Internal error!"); - return JS_FALSE; - } - if ((grp = getgrnam(str)) == NULL) { - (void) snprintf(e, 1023, "Unknown group '%s'", str); - QUEUE_EXCEPTION(e); - return JS_FALSE; - } - gid = grp->gr_gid; - } else - gid = JSVAL_TO_INT(argv[1]); - - if (fchown(jsfd->fd, uid, gid) == -1) { - jsfd->error = errno; - QUEUE_EXCEPTION(strerror(errno)); - 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 - */ - -/* - * XXX Not thread-safe as we are using global buffers. To avoid this, these - * buffers would need to be PollSet object specific and we would need to - * provide such an object with FD add/remove methods. - */ -static JSBool -fd_sm_poll(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - 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 argument really consists of an - * object and/or 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. - * XXX We ideally should use a buffer which we only need to grow as - * necessary, instead of having to reallocate memory every time. - * This however would introduce reentrency problems unless we used - * this buffer as part of a set object. We potentially then could - * alternatively provide a PollSet object, along with add/remove - * methods... - */ - if (fds.entries == NULL && fds.info == NULL) { - 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.size = 16; - } - fds.count = 0; - 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; - } - } - } - } - - /* We now use global buffers for performance - free(fds.entries); - free(fds.info); - */ - - return JS_TRUE; - -err: - *rval = OBJECT_TO_JSVAL(NULL); - /* We now use global buffers for performance - 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++) { - int cur; - - cur = 0; - if (JSVAL_IS_DOUBLE(argv[i])) - cur |= JSAT_DOUBLE; - else if (JSVAL_IS_INT(argv[i])) - cur |= JSAT_INTEGER; - else if (JSVAL_IS_STRING(argv[i])) - cur |= JSAT_STRING; - if ((p[i] & cur) == 0) { - char types[256]; - - *types = '\0'; - if ((p[i] & JSAT_INTEGER) != 0) - (void) strcat(types, "INTEGER | "); - if ((p[i] & JSAT_DOUBLE) != 0) - (void) strcat(types, "DOUBLE | "); - if ((p[i] & JSAT_STRING) != 0) - (void) strcat(types, "STRING | "); - if ((p[i] & JSAT_OBJECT) != 0) - (void) strcat(types, "OBJECT | "); - types[strlen(types) - 3] = '\0'; - (void) snprintf(line, 1023, - "%s() - argument #%d not of expected type(s) (%s)", - fun, i + 1, types); - 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_file.c b/mmsoftware/js/classes/js_file.c deleted file mode 100644 index 1ca4ac5..0000000 --- a/mmsoftware/js/classes/js_file.c +++ /dev/null @@ -1,1324 +0,0 @@ -/* $Id: js_file.c,v 1.9 2006/11/26 14:53:00 mmondor Exp $ */ - -/* - * Copyright (c) 2006, Matthew Mondor - * ALL RIGHTS RESERVED. - */ - -/* - * Since many libraries support working with FILE * rather than file - * desdcriptors, it was necessary to provide stdio functionality. - * The file_new() function is exported so that other modules may create file - * handles. - * - * XXX - * - add path validity checking hook function - * - implement formatted printing functions sprintf, fprintf, printf - * - XXX There is a problem with file_fh(). We probably should provide - * a corresponding function to free/unlock the fh. Because otherwise the - * object could be GCed while the FILE * is still being used, causing it - * to be unexpectedly closed. We thus need to root the File object during - * the time it's being used by the third party module. Optionally we could - * perhaps leave the responsibility to root the File object to third - * parties, but then this isn't always easy obvious since generally it's to - * support libraries which deal with FILE *. - */ - - - -#include - -#include - -#include -#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) - -#define SP(n) \ - { #n, n } - -struct property_spec { - const char *name; - int value; -}; - -enum file_flags { - FF_NONE = 0, - FF_CLOSE = (1 << 1), - FF_PCLOSE = (1 << 2) -}; - -/* - * Although we could simply wrap around FILE * directly, we need to know if - * we should close the file or not upon finalization, and how to close it. - * We use the flags field for that, since stdio does not allow to associate - * user data pointers with FILE objects portably (which would have been better - * for performance). - */ -typedef struct file { - FILE *fh; - int flags; - char *vbuf; - size_t vbufsize; -} file_t; - - - -/* - * Static prototypes - */ - -static JSBool file_constructor(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static void file_finalize(JSContext *, JSObject *); - -static JSBool file_sm_popen(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool file_sm_tmpfile(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool file_sm_remove(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool file_sm_strerror(JSContext *, JSObject *, uintN, jsval *, - jsval *); - -static JSBool file_m_close(JSContext *, JSObject *, uintN, jsval *, jsval *); -static JSBool file_m_reopen(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool file_m_seek(JSContext *, JSObject *, uintN, jsval *, jsval *); -static JSBool file_m_tell(JSContext *, JSObject *, uintN, jsval *, jsval *); -static JSBool file_m_rewind(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool file_m_flush(JSContext *, JSObject *, uintN, jsval *, jsval *); -static JSBool file_m_purge(JSContext *, JSObject *, uintN, jsval *, jsval *); -static JSBool file_m_setvbuf(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool file_m_clearerr(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool file_m_eof(JSContext *, JSObject *, uintN, jsval *, jsval *); -static JSBool file_m_error(JSContext *, JSObject *, uintN, jsval *, jsval *); -static JSBool file_m_fileno(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool file_m_read(JSContext *, JSObject *, uintN, jsval *, jsval *); -static JSBool file_m_write(JSContext *, JSObject *, uintN, jsval *, jsval *); -static JSBool file_m_gets(JSContext *, JSObject *, uintN, jsval *, jsval *); -static JSBool file_m_getc(JSContext *, JSObject *, uintN, jsval *, jsval *); -static JSBool file_m_ungetc(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool file_m_putc(JSContext *, JSObject *, uintN, jsval *, jsval *); - - - -/* - * Static globals - */ - -/* - * File - */ - -static JSClass file_class = { - "File", JSCLASS_HAS_PRIVATE, JS_PropertyStub, JS_PropertyStub, - JS_PropertyStub, JS_PropertyStub, JS_EnumerateStub, JS_ResolveStub, - JS_ConvertStub, file_finalize -}; - -/* Static properties */ -static struct property_spec file_sprops[] = { - SP(SEEK_SET), - SP(SEEK_CUR), - SP(SEEK_END), - SP(_IONBF), - SP(_IOLBF), - SP(_IOFBF), - SP(STDIN_FILENO), - SP(STDOUT_FILENO), - SP(STDERR_FILENO), - { NULL, 0 } -}; - -/* Static methods */ -static JSFunctionSpec file_smethods[] = { - { "popen", file_sm_popen, 2, 0, 0 }, - { "tmpfile", file_sm_tmpfile, 0, 0, 0 }, - { "remove", file_sm_remove, 1, 0, 0 }, - { "strerror", file_sm_strerror, 1, 0, 0 }, - { NULL, NULL, 0, 0, 0 } -}; - -/* Methods */ -static JSFunctionSpec file_methods[] = { - { "close", file_m_close, 0, 0, 0 }, - { "reopen", file_m_reopen, 2, 0, 0 }, - { "seek", file_m_seek, 2, 0, 0 }, - { "tell", file_m_tell, 0, 0, 0 }, - { "rewind", file_m_rewind, 0, 0, 0 }, - { "flush", file_m_flush, 0, 0, 0 }, - { "purge", file_m_purge, 0, 0, 0 }, - { "setvbuf", file_m_setvbuf, 2, 0, 0 }, - { "clearerr", file_m_clearerr, 0, 0, 0 }, - { "eof", file_m_eof, 0, 0, 0 }, - { "error", file_m_error, 0, 0, 0 }, - { "fileno", file_m_fileno, 0, 0, 0 }, - { "read", file_m_read, 2, 0, 0 }, - { "write", file_m_write, 1, 0, 0 }, - { "gets", file_m_gets, 2, 0, 0 }, - { "getc", file_m_getc, 1, 0, 0 }, - { "ungetc", file_m_ungetc, 1, 0, 0 }, - { "putc", file_m_putc, 1, 0, 0 }, - { NULL, NULL, 0, 0, 0 } -}; - -/* - * Static globals. These could cause reentrancy problems unless they were - * moved to be File specific, which would however require more RAM and reduce - * performance. - */ -static char *read_charbuf = NULL; -static size_t read_charbuf_size = 0; - - - -/* - * Publically exported constructor function. Returns the new File object on - * success or NULL on failure. - */ -JSObject * -file_new(JSContext *cx, FILE *fh, int close) -{ - file_t *f; - JSObject *o; - - assert(fh != NULL); - - if ((f = JS_malloc(cx, sizeof(file_t))) != NULL) { - f->fh = fh; - f->flags = (close != 0 ? FF_CLOSE : 0); - f->vbuf = NULL; - f->vbufsize = 0; - } - - if ((o = JS_NewObject(cx, &file_class, NULL, NULL)) == NULL) - goto err; - if (!JS_SetPrivate(cx, o, f)) - goto err; - - return o; - -err: - if (f != NULL) { - if ((f->flags & FF_CLOSE) != 0 && f->fh != NULL) - (void) fclose(f->fh); - JS_free(cx, f); - } - - return NULL; -} - -/* - * Allows external modules to obtain FILE * associated with a File. - * Returns NULL if not a valid File. - */ -FILE * -file_fh(JSContext *cx, jsval v) -{ - file_t *f = NULL; - JSObject *o; - - assert(cx != NULL); - - if (!JSVAL_IS_OBJECT(v)) - return NULL; - o = JSVAL_TO_OBJECT(v); - - if (!JS_InstanceOf(cx, o, &file_class, NULL)) - return NULL; - - f = JS_GetInstancePrivate(cx, o, &file_class, NULL); - assert(f != NULL); - - return f->fh; -} - - - -/* - * File object control - */ - -JSObject * -js_InitFileClass(JSContext *cx, JSObject *obj) -{ - JSObject *proto, *ctor; - struct property_spec *sp; - - if ((proto = JS_InitClass(cx, obj, NULL, &file_class, file_constructor, - 0, NULL, file_methods, NULL, file_smethods)) == NULL) { - (void) fprintf(stderr, "Error initializing File class\n"); - return NULL; - } - - /* Create static properties. */ - if ((ctor = JS_GetConstructor(cx, proto)) == NULL) { - (void) fprintf(stderr, "File: JS_GetConstructor == NULL\n"); - return NULL; - } - for (sp = file_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, - "File: Error defining property %s\n", sp->name); - return NULL; - } - } - - /* Create stdin, stdout, stderr File objects */ - { - JSObject *o; - - if ((o = file_new(cx, stdin, 0)) != NULL) { - if (!JS_DefineProperty(cx, obj, "stdin", - OBJECT_TO_JSVAL(o), NULL, NULL, - JSPROP_READONLY | JSPROP_PERMANENT)) { - (void) fprintf(stderr, - "File: Error defining stdin property\n"); - return NULL; - } - } else { - (void) fprintf(stderr, - "File: Error creating stdin object\n"); - return NULL; - } - - if ((o = file_new(cx, stdout, 0)) != NULL) { - if (!JS_DefineProperty(cx, obj, "stdout", - OBJECT_TO_JSVAL(o), NULL, NULL, - JSPROP_READONLY | JSPROP_PERMANENT)) { - (void) fprintf(stderr, - "File: Error defining stdout property\n"); - return NULL; - } - } else { - (void) fprintf(stderr, - "File: Error creating stdout object\n"); - return NULL; - } - - if ((o = file_new(cx, stderr, 0)) != NULL) { - if (!JS_DefineProperty(cx, obj, "stderr", - OBJECT_TO_JSVAL(o), NULL, NULL, - JSPROP_READONLY | JSPROP_PERMANENT)) { - (void) fprintf(stderr, - "File: Error defining stderr property\n"); - return NULL; - } - } else { - (void) fprintf(stderr, - "File: Error creating stderr object\n"); - return NULL; - } - } - - return proto; -} - -/* - * We support a few forms: - * File(filename:String, mode:String); fopen(3) - * File(fdnum:Integer, mode:String); fdopen(3) - */ -static JSBool -file_constructor(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - file_t *f = NULL; - JSObject *o; - - if (!JS_IsConstructing(cx)) { - QUEUE_EXCEPTION("Constructor called as a function"); - goto err; - } - - if (argc != 2) { - QUEUE_EXCEPTION("Wrong number of arguments"); - goto err; - } - - if ((f = JS_malloc(cx, sizeof(file_t))) == NULL) { - QUEUE_EXCEPTION("Out of memory"); - goto err; - } - f->flags = 0; - f->vbuf = NULL; - f->vbufsize = 0; - - if (JSVAL_IS_STRING(argv[0]) && JSVAL_IS_STRING(argv[1])) { - char *path, *mode; - - /* File(filename:String, mode:String); */ - if ((path = JS_GetStringBytes(JSVAL_TO_STRING(argv[0]))) - == NULL || - (mode = JS_GetStringBytes(JSVAL_TO_STRING(argv[1]))) - == NULL) { - QUEUE_EXCEPTION("Internal error!"); - goto err; - } - f->flags = FF_CLOSE; - if ((f->fh = fopen(path, mode)) == NULL) { - QUEUE_EXCEPTION(strerror(errno)); - goto err; - } - } else if (JSVAL_IS_INT(argv[0]) && JSVAL_IS_STRING(argv[1])) { - char *mode; - - /* File(fdnum:Integer, mode:String); */ - if ((mode = JS_GetStringBytes(JSVAL_TO_STRING(argv[1]))) - == NULL) { - QUEUE_EXCEPTION("Internal error!"); - goto err; - } - f->flags = FF_CLOSE; - if ((f->fh = fdopen(JSVAL_TO_INT(argv[0]), mode)) == NULL) { - QUEUE_EXCEPTION(strerror(errno)); - goto err; - } - } else { - QUEUE_EXCEPTION("Unknown method (invalid argument types)"); - goto err; - } - - if ((o = JS_NewObject(cx, &file_class, NULL, NULL)) == NULL) { - QUEUE_EXCEPTION("Internal error!"); - goto err; - } - *rval = OBJECT_TO_JSVAL(o); - - if (!JS_SetPrivate(cx, o, f)) { - QUEUE_EXCEPTION("Internal error!"); - goto err; - } - - return JS_TRUE; - -err: - *rval = OBJECT_TO_JSVAL(NULL); - if (f != NULL) { - if ((f->flags & FF_CLOSE) != 0 && f->fh != NULL) - (void) fclose(f->fh); - JS_free(cx, f); - } - - return JS_FALSE; -} - -/* ARGSUSED */ -static void -file_finalize(JSContext *cx, JSObject *obj) -{ - file_t *f; - - if ((f = JS_GetInstancePrivate(cx, obj, &file_class, NULL)) != NULL) { - if ((f->flags & FF_CLOSE) != 0 && f->fh != NULL) { - if ((f->flags & FF_PCLOSE) != 0) - (void) pclose(f->fh); - else - (void) fclose(f->fh); - if (f->vbuf != NULL) - JS_free(cx, f->vbuf); - } - (void) JS_SetPrivate(cx, obj, NULL); - JS_free(cx, f); - } -} - - -/* - * File object static methods - */ - -static JSBool -file_sm_popen(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - file_t *f = NULL; - JSObject *o; - char *cmd, *mode; - - if (argc != 2) { - 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 ((cmd = JS_GetStringBytes(JSVAL_TO_STRING(argv[0]))) == NULL || - (mode = JS_GetStringBytes(JSVAL_TO_STRING(argv[1]))) == NULL) { - QUEUE_EXCEPTION("Internal error!"); - goto err; - } - - if ((f = JS_malloc(cx, sizeof(file_t))) == NULL) { - QUEUE_EXCEPTION("Out of memory"); - goto err; - } - f->flags = FF_CLOSE | FF_PCLOSE; - f->vbuf = NULL; - f->vbufsize = 0; - - if ((f->fh = popen(cmd, mode)) == NULL) { - QUEUE_EXCEPTION(strerror(errno)); - goto err; - } - - if ((o = JS_NewObject(cx, &file_class, NULL, NULL)) == NULL) { - QUEUE_EXCEPTION("Internal error!"); - goto err; - } - *rval = OBJECT_TO_JSVAL(o); - - if (!JS_SetPrivate(cx, o, f)) { - QUEUE_EXCEPTION("Internal error!"); - goto err; - } - - return JS_TRUE; - -err: - *rval = OBJECT_TO_JSVAL(NULL); - if (f != NULL) { - if (f->fh != NULL) - (void) pclose(f->fh); - JS_free(cx, f); - } - - return JS_FALSE; -} - -static JSBool -file_sm_tmpfile(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - file_t *f = NULL; - JSObject *o; - - if (argc != 0) { - QUEUE_EXCEPTION("Wrong number of arguments"); - goto err; - } - - if ((f = JS_malloc(cx, sizeof(file_t))) == NULL) { - QUEUE_EXCEPTION("Out of memory"); - goto err; - } - f->flags = FF_CLOSE | FF_PCLOSE; - f->vbuf = NULL; - f->vbufsize = 0; - - if ((f->fh = tmpfile()) == NULL) { - QUEUE_EXCEPTION(strerror(errno)); - goto err; - } - - if ((o = JS_NewObject(cx, &file_class, NULL, NULL)) == NULL) { - QUEUE_EXCEPTION("Internal error!"); - goto err; - } - *rval = OBJECT_TO_JSVAL(o); - - if (!JS_SetPrivate(cx, o, f)) { - QUEUE_EXCEPTION("Internal error!"); - goto err; - } - - return JS_TRUE; - -err: - *rval = OBJECT_TO_JSVAL(NULL); - if (f != NULL) { - if (f->fh != NULL) - (void) fclose(f->fh); - JS_free(cx, f); - } - - return JS_FALSE; -} - -static JSBool -file_sm_remove(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - char *path; - - *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 1 not a String"); - return JS_FALSE; - } - if ((path = JS_GetStringBytes(JSVAL_TO_STRING(argv[0]))) == NULL) { - QUEUE_EXCEPTION("Internal error!"); - return JS_FALSE; - } - - if (remove(path) != 0) { - QUEUE_EXCEPTION(strerror(errno)); - return JS_FALSE; - } - - return JS_TRUE; -} - -/* - * We allow two forms; If no arguments are provided, we return the error - * message associated with errno. If one is specified, we return the error - * message associated with the provided error number. - */ -static JSBool -file_sm_strerror(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - int error; - JSString *str; - - if (argc == 0) - error = errno; - else { - if (!JSVAL_IS_INT(argv[0])) { - QUEUE_EXCEPTION("Argument 1 not an Integer"); - *rval = OBJECT_TO_JSVAL(NULL); - return JS_FALSE; - } - error = JSVAL_TO_INT(argv[0]); - } - - if ((str = JS_NewStringCopyZ(cx, strerror(error))) == NULL) { - QUEUE_EXCEPTION("Internal error!"); - *rval = OBJECT_TO_JSVAL(NULL); - return JS_FALSE; - } - *rval = STRING_TO_JSVAL(str); - - return JS_TRUE; -} - - -/* - * File object methods - */ - -static JSBool -file_m_close(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - file_t *f; - int ret; - - *rval = OBJECT_TO_JSVAL(NULL); - - if (argc != 0) { - QUEUE_EXCEPTION("Wrong number of arguments"); - return JS_FALSE; - } - - f = JS_GetInstancePrivate(cx, obj, &file_class, NULL); - assert(f != NULL); - - if ((f->flags & FF_CLOSE) != 0 && f->fh != NULL) { - if ((f->flags & FF_PCLOSE) != 0) - ret = pclose(f->fh); - else - ret = fclose(f->fh); - f->fh = NULL; - } else - ret = 0; - if (ret == -1) { - QUEUE_EXCEPTION(strerror(errno)); - return JS_FALSE; - } - - return JS_TRUE; -} - -static JSBool -file_m_reopen(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - file_t *f; - char *path, *mode; - - *rval = OBJECT_TO_JSVAL(NULL); - - if (argc != 2) { - QUEUE_EXCEPTION("Wrong number of arguments"); - return JS_FALSE; - } - - if (!JSVAL_IS_STRING(argv[0])) { - QUEUE_EXCEPTION("Argument 1 not a String"); - return JS_FALSE; - } - if (!JSVAL_IS_STRING(argv[1])) { - QUEUE_EXCEPTION("Argument 2 not a String"); - return JS_FALSE; - } - - if ((path = JS_GetStringBytes(JSVAL_TO_STRING(argv[0]))) == NULL || - (mode = JS_GetStringBytes(JSVAL_TO_STRING(argv[1]))) == NULL) { - QUEUE_EXCEPTION("Internal error!"); - return JS_FALSE; - } - - f = JS_GetInstancePrivate(cx, obj, &file_class, NULL); - assert(f != NULL); - if (f->fh == NULL) { - QUEUE_EXCEPTION("File already closed"); - return JS_FALSE; - } - - if ((f->fh = freopen(path, mode, f->fh)) == NULL) { - QUEUE_EXCEPTION(strerror(errno)); - return JS_FALSE; - } - - return JS_TRUE; -} - -static JSBool -file_m_seek(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - file_t *f; - jsdouble v; - - *rval = OBJECT_TO_JSVAL(NULL); - - if (argc != 2) { - QUEUE_EXCEPTION("Wrong number of arguments"); - return JS_FALSE; - } - if (!JSVAL_IS_NUMBER(argv[0]) || !JS_ValueToNumber(cx, argv[0], &v)) { - QUEUE_EXCEPTION("Argument 1 not a number"); - return JS_FALSE; - } - if (!JSVAL_IS_INT(argv[1])) { - QUEUE_EXCEPTION("Argument 2 not an Integer"); - } - - f = JS_GetInstancePrivate(cx, obj, &file_class, NULL); - assert(f != NULL); - if (f->fh == NULL) { - QUEUE_EXCEPTION("File already closed"); - return JS_FALSE; - } - - if (fseeko(f->fh, (off_t)v, JSVAL_TO_INT(argv[1])) != 0) { - QUEUE_EXCEPTION(strerror(errno)); - return JS_FALSE; - } - - return JS_TRUE; -} - -static JSBool -file_m_tell(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - file_t *f; - off_t v; - - *rval = OBJECT_TO_JSVAL(NULL); - - if (argc != 0) { - QUEUE_EXCEPTION("Wrong number of arguments"); - return JS_FALSE; - } - - f = JS_GetInstancePrivate(cx, obj, &file_class, NULL); - assert(f != NULL); - if (f->fh == NULL) { - QUEUE_EXCEPTION("File already closed"); - return JS_FALSE; - } - - if ((v = ftello(f->fh)) == -1) { - QUEUE_EXCEPTION(strerror(errno)); - return JS_FALSE; - } - - if (!JS_NewNumberValue(cx, (jsdouble)v, rval)) { - QUEUE_EXCEPTION("Internal error!"); - return JS_FALSE; - } - - return JS_TRUE; -} - -static JSBool -file_m_rewind(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - file_t *f; - - *rval = OBJECT_TO_JSVAL(NULL); - - if (argc != 0) { - QUEUE_EXCEPTION("Wrong number of arguments"); - return JS_FALSE; - } - - f = JS_GetInstancePrivate(cx, obj, &file_class, NULL); - assert(f != NULL); - if (f->fh == NULL) { - QUEUE_EXCEPTION("File already closed"); - return JS_FALSE; - } - - rewind(f->fh); - - return JS_TRUE; -} - -static JSBool -file_m_flush(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - file_t *f; - - *rval = OBJECT_TO_JSVAL(NULL); - - if (argc != 0) { - QUEUE_EXCEPTION("Wrong number of arguments"); - return JS_FALSE; - } - - f = JS_GetInstancePrivate(cx, obj, &file_class, NULL); - assert(f != NULL); - if (f->fh == NULL) { - QUEUE_EXCEPTION("File already closed"); - return JS_FALSE; - } - - if (fflush(f->fh) != 0) { - QUEUE_EXCEPTION(strerror(errno)); - return JS_FALSE; - } - - return JS_TRUE; -} - -static JSBool -file_m_purge(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - file_t *f; - - *rval = OBJECT_TO_JSVAL(NULL); - - if (argc != 0) { - QUEUE_EXCEPTION("Wrong number of arguments"); - return JS_FALSE; - } - - f = JS_GetInstancePrivate(cx, obj, &file_class, NULL); - assert(f != NULL); - if (f->fh == NULL) { - QUEUE_EXCEPTION("File already closed"); - return JS_FALSE; - } - - if (fpurge(f->fh) != 0) { - QUEUE_EXCEPTION(strerror(errno)); - return JS_FALSE; - } - - return JS_TRUE; -} - -/* - * Unlike stdio setvbuf(3), requires two arguments. One setting the buffering - * type, and the other the size of the wanted buffer, or 0 to use the default - * internal buffer. - * So our syntax is as follows: file.setvbuf(type:Integer, bufsize:Integer); - */ -static JSBool -file_m_setvbuf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - file_t *f; - size_t size; - char *vbuf = NULL; - - *rval = OBJECT_TO_JSVAL(NULL); - - if (argc != 2) { - QUEUE_EXCEPTION("Wrong number of arguments"); - return JS_FALSE; - } - if (!JSVAL_IS_INT(argv[0])) { - QUEUE_EXCEPTION("Argument 1 not an Integer"); - return JS_FALSE; - } - if (!JSVAL_IS_INT(argv[1])) { - QUEUE_EXCEPTION("Argument 2 not an Integer"); - return JS_FALSE; - } - - f = JS_GetInstancePrivate(cx, obj, &file_class, NULL); - assert(f != NULL); - if (f->fh == NULL) { - QUEUE_EXCEPTION("File already closed"); - return JS_FALSE; - } - - if ((size = JSVAL_TO_INT(argv[1])) > 0) { - if (f->vbuf == NULL) { - if ((f->vbuf = JS_malloc(cx, size)) == NULL) { - QUEUE_EXCEPTION("Out of memory"); - return JS_FALSE; - } - f->vbufsize = size; - vbuf = f->vbuf; - } else if (size != f->vbufsize) { - if ((vbuf = JS_realloc(cx, f->vbuf, size)) == NULL) { - QUEUE_EXCEPTION("Out of memory"); - return JS_FALSE; - } - f->vbuf = vbuf; - f->vbufsize = size; - } - } - - if (setvbuf(f->fh, vbuf, JSVAL_TO_INT(argv[0]), size) != 0) { - QUEUE_EXCEPTION("setvbuf() == EOF"); - return JS_FALSE; - } - - return JS_TRUE; -} - -static JSBool -file_m_clearerr(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - file_t *f; - - *rval = OBJECT_TO_JSVAL(NULL); - - if (argc != 0) { - QUEUE_EXCEPTION("Wrong number of arguments"); - return JS_FALSE; - } - - f = JS_GetInstancePrivate(cx, obj, &file_class, NULL); - assert(f != NULL); - if (f->fh == NULL) { - QUEUE_EXCEPTION("File already closed"); - return JS_FALSE; - } - - clearerr(f->fh); - - return JS_TRUE; -} - -static JSBool -file_m_eof(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - file_t *f; - int v; - - *rval = OBJECT_TO_JSVAL(NULL); - - if (argc != 0) { - QUEUE_EXCEPTION("Wrong number of arguments"); - return JS_FALSE; - } - - f = JS_GetInstancePrivate(cx, obj, &file_class, NULL); - assert(f != NULL); - if (f->fh == NULL) { - QUEUE_EXCEPTION("File already closed"); - return JS_FALSE; - } - - v = feof(f->fh); - *rval = BOOLEAN_TO_JSVAL((JSBool)v); - - return JS_TRUE; -} - -static JSBool -file_m_error(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - file_t *f; - int v; - - *rval = OBJECT_TO_JSVAL(NULL); - - if (argc != 0) { - QUEUE_EXCEPTION("Wrong number of arguments"); - return JS_FALSE; - } - - f = JS_GetInstancePrivate(cx, obj, &file_class, NULL); - assert(f != NULL); - if (f->fh == NULL) { - QUEUE_EXCEPTION("File already closed"); - return JS_FALSE; - } - - v = ferror(f->fh); - *rval = BOOLEAN_TO_JSVAL((JSBool)v); - - return JS_TRUE; -} - -static JSBool -file_m_fileno(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - file_t *f; - int v; - - *rval = OBJECT_TO_JSVAL(NULL); - - if (argc != 0) { - QUEUE_EXCEPTION("Wrong number of arguments"); - return JS_FALSE; - } - - f = JS_GetInstancePrivate(cx, obj, &file_class, NULL); - assert(f != NULL); - if (f->fh == NULL) { - QUEUE_EXCEPTION("File already closed"); - return JS_FALSE; - } - - v = fileno(f->fh); - *rval = INT_TO_JSVAL(v); - - return JS_TRUE; -} - -/* - * Unlike fread(3), returns a string holding the read buffer (which could also - * be shorter than requested), or throws an exception on error. - */ -static JSBool -file_m_read(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - file_t *f; - size_t size, nmemb, tsize, rsize; - JSString *str; - - *rval = OBJECT_TO_JSVAL(NULL); - - if (argc != 2) { - QUEUE_EXCEPTION("Wrong number of arguments"); - return JS_FALSE; - } - if (!JSVAL_IS_INT(argv[0])) { - QUEUE_EXCEPTION("Argument 1 not an Integer"); - return JS_FALSE; - } - if (!JSVAL_IS_INT(argv[1])) { - QUEUE_EXCEPTION("Argument 2 not an Integer"); - return JS_FALSE; - } - size = JSVAL_TO_INT(argv[0]); - nmemb = JSVAL_TO_INT(argv[1]); - tsize = size * nmemb; - - f = JS_GetInstancePrivate(cx, obj, &file_class, NULL); - assert(f != NULL); - if (f->fh == NULL) { - QUEUE_EXCEPTION("File already closed"); - return JS_FALSE; - } - - /* - * Ensure that read buffer is ready and large enough - */ - if (read_charbuf_size < tsize) { - if (read_charbuf == NULL) { - if ((read_charbuf = malloc(tsize)) == NULL) { - QUEUE_EXCEPTION("Cannot allocate read buffer"); - return JS_FALSE; - } - } else { - char *ptr; - - if ((ptr = realloc(read_charbuf, tsize)) == NULL) { - QUEUE_EXCEPTION( - "Cannot reallocate read bufer"); - return JS_FALSE; - } - read_charbuf = ptr; - } - read_charbuf_size = tsize; - } - - if ((rsize = fread(read_charbuf, size, nmemb, f->fh)) != nmemb) { - if (rsize == 0) { - QUEUE_EXCEPTION(strerror(errno)); - return JS_FALSE; - } - } - - if ((str = JS_NewStringCopyN(cx, read_charbuf, rsize * size)) - == NULL) { - QUEUE_EXCEPTION("Internal error!"); - return JS_FALSE; - } - *rval = STRING_TO_JSVAL(str); - - return JS_TRUE; -} - -/* - * Unlike fwrite(2), is simply provided the string to write rather than having - * to specify sizes. - */ -static JSBool -file_m_write(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - file_t *f; - JSString *str; - char *bytes; - size_t size; - - *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 1 not a String"); - return JS_FALSE; - } - str = JSVAL_TO_STRING(argv[0]); - if ((bytes = JS_GetStringBytes(str)) == NULL) { - QUEUE_EXCEPTION("Internal error!"); - return JS_FALSE; - } - size = JS_GetStringLength(str); - - f = JS_GetInstancePrivate(cx, obj, &file_class, NULL); - assert(f != NULL); - if (f->fh == NULL) { - QUEUE_EXCEPTION("File already closed"); - return JS_FALSE; - } - - if (fwrite(bytes, size, 1, f->fh) != 1) { - QUEUE_EXCEPTION(strerror(errno)); - return JS_FALSE; - } - - return JS_TRUE; -} - -/* - * Like fgets(3), is provided a maximum buffer size (which although is - * internal). Automatically strips the '\r', '\n' or '\r\n' if the second - * argument is false, or keep them otherwise. On error, we throw an - * exception. - */ -static JSBool -file_m_gets(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - file_t *f; - size_t tsize; - JSBool nostrip; - JSString *str; - char *rstr; - - *rval = OBJECT_TO_JSVAL(NULL); - - if (argc != 2) { - QUEUE_EXCEPTION("Wrong number of arguments"); - return JS_FALSE; - } - if (!JSVAL_IS_INT(argv[0])) { - QUEUE_EXCEPTION("Argument 1 not an Integer"); - return JS_FALSE; - } - if (!JSVAL_IS_BOOLEAN(argv[1])) { - QUEUE_EXCEPTION("Argument 2 not a Boolean"); - return JS_FALSE; - } - tsize = JSVAL_TO_INT(argv[0]); - nostrip = JSVAL_TO_BOOLEAN(argv[1]); - - f = JS_GetInstancePrivate(cx, obj, &file_class, NULL); - assert(f != NULL); - if (f->fh == NULL) { - QUEUE_EXCEPTION("File already closed"); - return JS_FALSE; - } - - /* - * Ensure that read buffer is ready and large enough - */ - if (read_charbuf_size < tsize) { - if (read_charbuf == NULL) { - if ((read_charbuf = malloc(tsize)) == NULL) { - QUEUE_EXCEPTION("Cannot allocate read buffer"); - return JS_FALSE; - } - } else { - char *ptr; - - if ((ptr = realloc(read_charbuf, tsize)) == NULL) { - QUEUE_EXCEPTION( - "Cannot reallocate read bufer"); - return JS_FALSE; - } - read_charbuf = ptr; - } - read_charbuf_size = tsize; - } - - if ((rstr = fgets(read_charbuf, tsize, f->fh)) == NULL) { - QUEUE_EXCEPTION(strerror(errno)); - return JS_FALSE; - } - tsize = strlen(rstr); - - if ((str = JS_NewStringCopyN(cx, rstr, tsize)) == NULL) { - QUEUE_EXCEPTION("Internal error!"); - return JS_FALSE; - } - *rval = STRING_TO_JSVAL(str); - - return JS_TRUE; -} - -/* - * Returns an int just like fgetc(3) but throws an exception on error. - */ -static JSBool -file_m_getc(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - file_t *f; - int c; - - *rval = OBJECT_TO_JSVAL(NULL); - - if (argc != 0) { - QUEUE_EXCEPTION("Wrong number of arguments"); - return JS_FALSE; - } - - f = JS_GetInstancePrivate(cx, obj, &file_class, NULL); - assert(f != NULL); - if (f->fh == NULL) { - QUEUE_EXCEPTION("File already closed"); - return JS_FALSE; - } - - if ((c = fgetc(f->fh)) == EOF) { - QUEUE_EXCEPTION(strerror(errno)); - return JS_FALSE; - } - *rval = INT_TO_JSVAL(c); - - return JS_TRUE; -} - -/* - * Like ungetc(3) but throws an exception on error. - */ -static JSBool -file_m_ungetc(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - file_t *f; - int c; - - *rval = OBJECT_TO_JSVAL(NULL); - - if (argc != 1) { - QUEUE_EXCEPTION("Wrong number of arguments"); - return JS_FALSE; - } - if (!JSVAL_IS_INT(argv[0])) { - QUEUE_EXCEPTION("Argument 1 not an Integer"); - return JS_FALSE; - } - - f = JS_GetInstancePrivate(cx, obj, &file_class, NULL); - assert(f != NULL); - if (f->fh == NULL) { - QUEUE_EXCEPTION("File already closed"); - return JS_FALSE; - } - - if ((c = ungetc(JSVAL_TO_INT(argv[0]), f->fh)) == EOF) { - QUEUE_EXCEPTION(strerror(errno)); - return JS_FALSE; - } - *rval = INT_TO_JSVAL(c); - - return JS_TRUE; -} - -/* - * Works with an int just like fputc(3). Throws an exception on error. - */ -static JSBool -file_m_putc(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - file_t *f; - - *rval = OBJECT_TO_JSVAL(NULL); - - if (argc != 1) { - QUEUE_EXCEPTION("Wrong number of arguments"); - return JS_FALSE; - } - if (!JSVAL_IS_INT(argv[0])) { - QUEUE_EXCEPTION("Argument 1 not an Integer"); - return JS_FALSE; - } - - f = JS_GetInstancePrivate(cx, obj, &file_class, NULL); - assert(f != NULL); - if (f->fh == NULL) { - QUEUE_EXCEPTION("File already closed"); - return JS_FALSE; - } - - if (fputc(JSVAL_TO_INT(argv[0]), f->fh) != 0) { - QUEUE_EXCEPTION(strerror(errno)); - return JS_FALSE; - } - - return JS_TRUE; -} diff --git a/mmsoftware/js/classes/js_file.h b/mmsoftware/js/classes/js_file.h deleted file mode 100644 index c6d18c2..0000000 --- a/mmsoftware/js/classes/js_file.h +++ /dev/null @@ -1,18 +0,0 @@ -/* $Id: js_file.h,v 1.3 2006/09/26 11:49:49 mmondor Exp $ */ - -/* - * Copyright (c) 2006, Matthew Mondor - * ALL RIGHTS RESERVED. - */ - -#ifndef JSFILE_H -#define JSFILE_H - -#include - -extern JSObject *js_InitFileClass(JSContext *, JSObject *); - -extern JSObject *file_new(JSContext *, FILE *, int); -extern FILE *file_fh(JSContext *, jsval); - -#endif diff --git a/mmsoftware/js/classes/js_fs.c b/mmsoftware/js/classes/js_fs.c deleted file mode 100644 index 8e46534..0000000 --- a/mmsoftware/js/classes/js_fs.c +++ /dev/null @@ -1,873 +0,0 @@ -/* $Id: js_fs.c,v 1.3 2006/10/27 05:41:30 mmondor Exp $ */ - -/* - * Copyright (c) 2006, Matthew Mondor - * ALL RIGHTS RESERVED. - */ - - - -#include -#include -#include - -#include - -#include -#include -#include -#include -#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 fs_constructor(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static void fs_finalize(JSContext *, JSObject *); - -static JSBool fs_sm_chdir(JSContext *, JSObject *, uintN, jsval *, jsval *); -static JSBool fs_sm_getcwd(JSContext *, JSObject *, uintN, jsval *, jsval *); -static JSBool fs_sm_mkdir(JSContext *, JSObject *, uintN, jsval *, jsval *); -static JSBool fs_sm_rmdir(JSContext *, JSObject *, uintN, jsval *, jsval *); -static JSBool fs_sm_stat(JSContext *, JSObject *, uintN, jsval *, jsval *); -static JSBool fs_sm_lstat(JSContext *, JSObject *, uintN, jsval *, jsval *); -static JSBool fs_sm_chown(JSContext *, JSObject *, uintN, jsval *, jsval *); -static JSBool fs_sm_lchown(JSContext *, JSObject *, uintN, jsval *, jsval *); -static JSBool fs_sm_chmod(JSContext *, JSObject *, uintN, jsval *, jsval *); -static JSBool fs_sm_rename(JSContext *, JSObject *, uintN, jsval *, jsval *); -static JSBool fs_sm_unlink(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool fs_sm_truncate(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool fs_sm_creat(JSContext *, JSObject *, uintN, jsval *, jsval *); -static JSBool fs_sm_mknod(JSContext *, JSObject *, uintN, jsval *, jsval *); -static JSBool fs_sm_mkfifo(JSContext *, JSObject *, uintN, jsval *, jsval *); -static JSBool fs_sm_symlink(JSContext *, JSObject *, uintN, jsval *, - jsval *); - -static JSBool fs_sm_stat_i(JSContext *, JSObject *, uintN, jsval *, jsval *, - int (*)(const char *, struct stat *)); - -static int chown_resolve(JSContext *, jsval, jsval, uid_t *, gid_t *); - -static mode_t fs_mode_allow(mode_t); -static int fs_path_allow(const char *); - - - -/* - * Static globals - */ - -/* FS class */ -static JSClass fs_class = { - "FS", 0, JS_PropertyStub, JS_PropertyStub, - JS_PropertyStub, JS_PropertyStub, JS_EnumerateStub, JS_ResolveStub, - JS_ConvertStub, fs_finalize -}; - -/* Provided static methods */ -static JSFunctionSpec fs_smethods[] = { - { "chdir", fs_sm_chdir, 1, 0, 0 }, - { "getcwd", fs_sm_getcwd, 0, 0, 0 }, - { "mkdir", fs_sm_mkdir, 2, 0, 0 }, - { "rmdir", fs_sm_rmdir, 1, 0, 0 }, - { "stat", fs_sm_stat, 1, 0, 0 }, - { "lstat", fs_sm_lstat, 1, 0, 0 }, - { "chown", fs_sm_chown, 3, 0, 0 }, - { "lchown", fs_sm_lchown, 3, 0, 0 }, - { "chmod", fs_sm_chmod, 2, 0, 0 }, - { "rename", fs_sm_rename, 2, 0, 0 }, - { "unlink", fs_sm_unlink, 1, 0, 0 }, - { "truncate", fs_sm_truncate, 2, 0, 0 }, - { "creat", fs_sm_creat, 2, 0, 0 }, - { "mknod", fs_sm_mknod, 3, 0, 0 }, - { "mkfifo", fs_sm_mkfifo, 2, 0, 0 }, - { "symlink", fs_sm_symlink, 2, 0, 0 }, - { NULL, NULL, 0, 0, 0 } -}; - - - -/* - * FS object control - */ - -JSObject * -js_InitFSClass(JSContext *cx, JSObject *obj) -{ - JSObject *proto; - - if ((proto = JS_InitClass(cx, obj, NULL, &fs_class, fs_constructor, - 0, NULL, NULL, NULL, fs_smethods)) == NULL) - (void) fprintf(stderr, "Error initializing FS class\n"); - - return proto; -} - -static JSBool -fs_constructor(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - - QUEUE_EXCEPTION("FS class non-instanciable"); - - return JS_FALSE; -} - -static void -fs_finalize(JSContext *cx, JSObject *obj) -{ - - /* NOOP */ -} - - - -/* Static methods */ -static JSBool -fs_sm_chdir(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - char *path; - - *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 1 not a String"); - return JS_FALSE; - } - - if ((path = JS_GetStringBytes(JSVAL_TO_STRING(argv[0]))) == NULL) { - QUEUE_EXCEPTION("Internal error!"); - return JS_FALSE; - } - - if (fs_path_allow(path) == -1) { - QUEUE_EXCEPTION("Path not allowed"); - return JS_FALSE; - } - - if (chdir(path) != 0) { - QUEUE_EXCEPTION(strerror(errno)); - return JS_FALSE; - } - - return JS_TRUE; -} - -static JSBool -fs_sm_getcwd(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - static char cwd[MAXPATHLEN]; - JSString *str; - - *rval = OBJECT_TO_JSVAL(NULL); - - if (argc != 0) { - QUEUE_EXCEPTION("Wrong number of arguments"); - return JS_FALSE; - } - - if (getcwd(cwd, MAXPATHLEN - 1) == NULL) { - QUEUE_EXCEPTION(strerror(errno)); - return JS_FALSE; - } - - if ((str = JS_NewStringCopyZ(cx, cwd)) == NULL) { - QUEUE_EXCEPTION("Internal error!"); - return JS_FALSE; - } - *rval = STRING_TO_JSVAL(str); - - return JS_TRUE; -} - -static JSBool -fs_sm_mkdir(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - char *path; - mode_t mode; - - *rval = OBJECT_TO_JSVAL(NULL); - - if (argc != 2) { - QUEUE_EXCEPTION("Wrong number of arguments"); - return JS_FALSE; - } - if (!JSVAL_IS_STRING(argv[0])) { - QUEUE_EXCEPTION("Argument 1 not a String"); - return JS_FALSE; - } - if (!JSVAL_IS_INT(argv[0])) { - QUEUE_EXCEPTION("Argument 2 not an Integer"); - return JS_FALSE; - } - - if ((path = JS_GetStringBytes(JSVAL_TO_STRING(argv[0]))) == NULL) { - QUEUE_EXCEPTION("Internal error!"); - return JS_FALSE; - } - if (fs_path_allow(path) != 0) { - QUEUE_EXCEPTION("Path not allowed"); - return JS_FALSE; - } - - mode = fs_mode_allow((mode_t)JSVAL_TO_INT(argv[1])); - - if (mkdir(path, mode) == -1) { - QUEUE_EXCEPTION(strerror(errno)); - return JS_FALSE; - } - - return JS_TRUE; -} - -static JSBool -fs_sm_rmdir(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - char *path; - - *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 1 not a String"); - return JS_FALSE; - } - - if ((path = JS_GetStringBytes(JSVAL_TO_STRING(argv[0]))) == NULL) { - QUEUE_EXCEPTION("Internal error!"); - return JS_FALSE; - } - if (fs_path_allow(path) != 0) { - QUEUE_EXCEPTION("Path not allowed"); - return JS_FALSE; - } - - if (rmdir(path) == -1) { - QUEUE_EXCEPTION(strerror(errno)); - return JS_FALSE; - } - - return JS_TRUE; -} - -static JSBool -fs_sm_stat(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - - return fs_sm_stat_i(cx, obj, argc, argv, rval, stat); -} - -static JSBool -fs_sm_lstat(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - - return fs_sm_stat_i(cx, obj, argc, argv, rval, lstat); -} - -static JSBool -fs_sm_chown(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - char *path; - uid_t uid; - gid_t gid; - - *rval = OBJECT_TO_JSVAL(NULL); - - if (argc != 3) { - QUEUE_EXCEPTION("Wrong number of arguments"); - return JS_FALSE; - } - if (!JSVAL_IS_STRING(argv[0])) { - QUEUE_EXCEPTION("Argument 1 not a String"); - return JS_FALSE; - } - - if ((path = JS_GetStringBytes(JSVAL_TO_STRING(argv[0]))) == NULL) { - QUEUE_EXCEPTION("Internal error!"); - return JS_FALSE; - } - if (fs_path_allow(path) != 0) { - QUEUE_EXCEPTION("Path not allowed"); - return JS_FALSE; - } - - if (chown_resolve(cx, argv[1], argv[2], &uid, &gid) == -1) - return JS_FALSE; - - if (chown(path, uid, gid) == -1) { - QUEUE_EXCEPTION(strerror(errno)); - return JS_FALSE; - } - - return JS_TRUE; -} - -static JSBool -fs_sm_lchown(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - char *path; - uid_t uid; - gid_t gid; - - *rval = OBJECT_TO_JSVAL(NULL); - - if (argc != 3) { - QUEUE_EXCEPTION("Wrong number of arguments"); - return JS_FALSE; - } - if (!JSVAL_IS_STRING(argv[0])) { - QUEUE_EXCEPTION("Argument 1 not a String"); - return JS_FALSE; - } - - if ((path = JS_GetStringBytes(JSVAL_TO_STRING(argv[0]))) == NULL) { - QUEUE_EXCEPTION("Internal error!"); - return JS_FALSE; - } - if (fs_path_allow(path) != 0) { - QUEUE_EXCEPTION("Path not allowed"); - return JS_FALSE; - } - - if (chown_resolve(cx, argv[1], argv[2], &uid, &gid) == -1) - return JS_FALSE; - - if (lchown(path, uid, gid) == -1) { - QUEUE_EXCEPTION(strerror(errno)); - return JS_FALSE; - } - - return JS_TRUE; -} - -static JSBool -fs_sm_chmod(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - char *path; - mode_t mode; - - *rval = OBJECT_TO_JSVAL(NULL); - - if (argc != 2) { - QUEUE_EXCEPTION("Wrong number of arguments"); - return JS_FALSE; - } - if (!JSVAL_IS_STRING(argv[0])) { - QUEUE_EXCEPTION("Argument 1 not a String"); - return JS_FALSE; - } - if (!JSVAL_IS_INT(argv[1])) { - QUEUE_EXCEPTION("Argument 2 not an Integer"); - return JS_FALSE; - } - - if ((path = JS_GetStringBytes(JSVAL_TO_STRING(argv[0]))) == NULL) { - QUEUE_EXCEPTION("Internal error!"); - return JS_FALSE; - } - if (fs_path_allow(path) != 0) { - QUEUE_EXCEPTION("Path not allowed"); - return JS_FALSE; - } - - mode = fs_mode_allow((mode_t)JSVAL_TO_INT(argv[1])); - - if (chmod(path, mode) == -1) { - QUEUE_EXCEPTION(strerror(errno)); - return JS_FALSE; - } - - return JS_TRUE; -} - -static JSBool -fs_sm_rename(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - char *path1, *path2; - - *rval = OBJECT_TO_JSVAL(NULL); - - if (argc != 2) { - QUEUE_EXCEPTION("Wrong number of arguments"); - return JS_FALSE; - } - if (!JSVAL_IS_STRING(argv[0])) { - QUEUE_EXCEPTION("Argument 1 not a String"); - return JS_FALSE; - } - if (!JSVAL_IS_STRING(argv[1])) { - QUEUE_EXCEPTION("Argument 2 not a String"); - return JS_FALSE; - } - - if ((path1 = JS_GetStringBytes(JSVAL_TO_STRING(argv[0]))) == NULL) { - QUEUE_EXCEPTION("Internal error!"); - return JS_FALSE; - } - if (fs_path_allow(path1) != 0) { - QUEUE_EXCEPTION("Path 1 not allowed"); - return JS_FALSE; - } - - if ((path2 = JS_GetStringBytes(JSVAL_TO_STRING(argv[1]))) == NULL) { - QUEUE_EXCEPTION("Internal error!"); - return JS_FALSE; - } - if (fs_path_allow(path2) != 0) { - QUEUE_EXCEPTION("Path 2 not allowed"); - return JS_FALSE; - } - - if (rename(path1, path2) == -1) { - QUEUE_EXCEPTION(strerror(errno)); - return JS_FALSE; - } - - return JS_TRUE; -} - -static JSBool -fs_sm_unlink(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - char *path; - - *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 1 not a String"); - return JS_FALSE; - } - - if ((path = JS_GetStringBytes(JSVAL_TO_STRING(argv[0]))) == NULL) { - QUEUE_EXCEPTION("Internal error!"); - return JS_FALSE; - } - if (fs_path_allow(path) != 0) { - QUEUE_EXCEPTION("Path not allowed"); - return JS_FALSE; - } - - if (unlink(path) == -1) { - QUEUE_EXCEPTION(strerror(errno)); - return JS_FALSE; - } - - return JS_TRUE; -} - -static JSBool -fs_sm_truncate(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - char *path; - off_t size; - jsdouble dsize; - - *rval = OBJECT_TO_JSVAL(NULL); - - if (argc != 2) { - QUEUE_EXCEPTION("Wrong number of arguments"); - return JS_FALSE; - } - if (!JSVAL_IS_STRING(argv[0])) { - QUEUE_EXCEPTION("Argument 1 not a String"); - return JS_FALSE; - } - if (!JSVAL_IS_NUMBER(argv[1])) { - QUEUE_EXCEPTION("Argument 2 not a number"); - return JS_FALSE; - } - - if ((path = JS_GetStringBytes(JSVAL_TO_STRING(argv[0]))) == NULL) { - QUEUE_EXCEPTION("Internal error!"); - return JS_FALSE; - } - if (fs_path_allow(path) != 0) { - QUEUE_EXCEPTION("Path not allowed"); - return JS_FALSE; - } - - if (!JS_ValueToNumber(cx, argv[1], &dsize)) { - QUEUE_EXCEPTION("Internal error!"); - return JS_FALSE; - } - size = (off_t)dsize; - - if (truncate(path, size) == -1) { - QUEUE_EXCEPTION(strerror(errno)); - return JS_FALSE; - } - - return JS_TRUE; -} - -static JSBool -fs_sm_creat(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - char *path; - mode_t mode; - - *rval = OBJECT_TO_JSVAL(NULL); - - if (argc != 2) { - QUEUE_EXCEPTION("Wrong number of arguments"); - return JS_FALSE; - } - if (!JSVAL_IS_STRING(argv[0])) { - QUEUE_EXCEPTION("Argument 1 not a String"); - return JS_FALSE; - } - if (!JSVAL_IS_INT(argv[1])) { - QUEUE_EXCEPTION("Argument 2 not an Integer"); - return JS_FALSE; - } - - if ((path = JS_GetStringBytes(JSVAL_TO_STRING(argv[0]))) == NULL) { - QUEUE_EXCEPTION("Internal error!"); - return JS_FALSE; - } - if (fs_path_allow(path) != 0) { - QUEUE_EXCEPTION("Path not allowed"); - return JS_FALSE; - } - - mode = fs_mode_allow((mode_t)JSVAL_TO_INT(argv[1])); - - if (creat(path, mode) == -1) { - QUEUE_EXCEPTION(strerror(errno)); - return JS_FALSE; - } - - return JS_TRUE; -} - -static JSBool -fs_sm_mknod(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - char *path; - mode_t mode; - dev_t dev; - - *rval = OBJECT_TO_JSVAL(NULL); - - if (argc != 3) { - QUEUE_EXCEPTION("Wrong number of arguments"); - return JS_FALSE; - } - if (!JSVAL_IS_STRING(argv[0])) { - QUEUE_EXCEPTION("Argument 1 not a String"); - return JS_FALSE; - } - if (!JSVAL_IS_INT(argv[1])) { - QUEUE_EXCEPTION("Argument 2 not an Integer"); - return JS_FALSE; - } - if (!JSVAL_IS_INT(argv[2])) { - QUEUE_EXCEPTION("Argument 3 not an Integer"); - return JS_FALSE; - } - - if ((path = JS_GetStringBytes(JSVAL_TO_STRING(argv[0]))) == NULL) { - QUEUE_EXCEPTION("Internal error!"); - return JS_FALSE; - } - if (fs_path_allow(path) != 0) { - QUEUE_EXCEPTION("Path not allowed"); - return JS_FALSE; - } - - mode = fs_mode_allow((mode_t)JSVAL_TO_INT(argv[1])); - - dev = (dev_t)JSVAL_TO_INT(argv[2]); - - if (mknod(path, mode, dev) == -1) { - QUEUE_EXCEPTION(strerror(errno)); - return JS_FALSE; - } - - return JS_TRUE; -} - -static JSBool -fs_sm_mkfifo(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - char *path; - mode_t mode; - - *rval = OBJECT_TO_JSVAL(NULL); - - if (argc != 2) { - QUEUE_EXCEPTION("Wrong number of arguments"); - return JS_FALSE; - } - if (!JSVAL_IS_STRING(argv[0])) { - QUEUE_EXCEPTION("Argument 1 not a String"); - return JS_FALSE; - } - if (!JSVAL_IS_INT(argv[1])) { - QUEUE_EXCEPTION("Argument 2 not an Integer"); - return JS_FALSE; - } - - if ((path = JS_GetStringBytes(JSVAL_TO_STRING(argv[0]))) == NULL) { - QUEUE_EXCEPTION("Internal error!"); - return JS_FALSE; - } - if (fs_path_allow(path) != 0) { - QUEUE_EXCEPTION("Path not allowed"); - return JS_FALSE; - } - - mode = fs_mode_allow((mode_t)JSVAL_TO_INT(argv[1])); - - if (mkfifo(path, mode) == -1) { - QUEUE_EXCEPTION(strerror(errno)); - return JS_FALSE; - } - - return JS_TRUE; -} - -static JSBool -fs_sm_symlink(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - char *path1, *path2; - - *rval = OBJECT_TO_JSVAL(NULL); - - if (argc != 2) { - QUEUE_EXCEPTION("Wrong number of arguments"); - return JS_FALSE; - } - if (!JSVAL_IS_STRING(argv[0])) { - QUEUE_EXCEPTION("Argument 1 not a String"); - return JS_FALSE; - } - if (!JSVAL_IS_STRING(argv[1])) { - QUEUE_EXCEPTION("Argument 2 not a String"); - return JS_FALSE; - } - - if ((path1 = JS_GetStringBytes(JSVAL_TO_STRING(argv[0]))) == NULL) { - QUEUE_EXCEPTION("Internal error!"); - return JS_FALSE; - } - if (fs_path_allow(path1) != 0) { - QUEUE_EXCEPTION("Path 1 not allowed"); - return JS_FALSE; - } - - if ((path2 = JS_GetStringBytes(JSVAL_TO_STRING(argv[1]))) == NULL) { - QUEUE_EXCEPTION("Internal error!"); - return JS_FALSE; - } - if (fs_path_allow(path2) != 0) { - QUEUE_EXCEPTION("Path 2 not allowed"); - return JS_FALSE; - } - - if (symlink(path1, path2) == -1) { - QUEUE_EXCEPTION(strerror(errno)); - return JS_FALSE; - } - - return JS_TRUE; -} - - -static JSBool -fs_sm_stat_i(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval, int (*stat_i)(const char *, struct stat *)) -{ - char *path; - struct stat st; - JSObject *o = NULL; - jsval val; - - *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 1 not a String"); - return JS_FALSE; - } - - if ((path = JS_GetStringBytes(JSVAL_TO_STRING(argv[0]))) == NULL) { - QUEUE_EXCEPTION("Internal error!"); - return JS_FALSE; - } - - if (fs_path_allow(path) == -1) { - QUEUE_EXCEPTION("Path now allowed"); - return JS_FALSE; - } - - if (stat_i(path, &st) == -1) { - 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 ((o = JS_NewObject(cx, NULL, NULL, NULL)) == NULL) { - QUEUE_EXCEPTION("Out of memory"); - goto err; - } - *rval = OBJECT_TO_JSVAL(o); - -#define DEFINE_INT_PROP(n, i) do { \ - val = INT_TO_JSVAL((int)(i)); \ - if (!JS_DefineProperty(cx, o, (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, o, (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 int -chown_resolve(JSContext *cx, jsval vuid, jsval vgid, uid_t *uid, gid_t *gid) -{ - char *str, e[1024]; - struct passwd *pwd; - struct group *grp; - - if (JSVAL_IS_STRING(vuid)) { - if ((str = JS_GetStringBytes(JSVAL_TO_STRING(vuid))) == NULL) { - QUEUE_EXCEPTION("Internal error!"); - return -1; - } - if ((pwd = getpwnam(str)) == NULL) { - (void) snprintf(e, 1023, "Unknown user '%s'", str); - QUEUE_EXCEPTION(e); - return -1; - } - *uid = pwd->pw_uid; - } else if (JSVAL_IS_INT(vuid)) - *uid = JSVAL_TO_INT(vuid); - else { - QUEUE_EXCEPTION("uid argument not String or Integer"); - return -1; - } - - if (JSVAL_IS_STRING(vgid)) { - if ((str = JS_GetStringBytes(JSVAL_TO_STRING(vgid))) == NULL) { - QUEUE_EXCEPTION("Internal error!"); - return -1; - } - if ((grp = getgrnam(str)) == NULL) { - (void) snprintf(e, 1023, "Unknown group '%s'", str); - QUEUE_EXCEPTION(e); - return -1; - } - *gid = grp->gr_gid; - } else if (JSVAL_IS_INT(vgid)) - *gid = JSVAL_TO_INT(vgid); - else { - QUEUE_EXCEPTION("gid argument not String or Integer"); - return -1; - } - - return 0; -} - -static mode_t -fs_mode_allow(mode_t mode) -{ - /* XXX */ - - return mode; -} - -/* ARGSUSED */ -static int -fs_path_allow(const char *path) -{ - /* XXX */ - - return 0; -} diff --git a/mmsoftware/js/classes/js_fs.h b/mmsoftware/js/classes/js_fs.h deleted file mode 100644 index 3785dc2..0000000 --- a/mmsoftware/js/classes/js_fs.h +++ /dev/null @@ -1,13 +0,0 @@ -/* $Id: js_fs.h,v 1.1 2006/09/15 21:04:48 mmondor Exp $ */ - -/* - * Copyright (c) 2006, Matthew Mondor - * ALL RIGHTS RESERVED. - */ - -#ifndef JSFS_H -#define JSFS_H - -extern JSObject *js_InitFSClass(JSContext *, JSObject *); - -#endif diff --git a/mmsoftware/js/classes/js_gcroot.c b/mmsoftware/js/classes/js_gcroot.c deleted file mode 100644 index 194d550..0000000 --- a/mmsoftware/js/classes/js_gcroot.c +++ /dev/null @@ -1,311 +0,0 @@ -/* $Id: js_gcroot.c,v 1.3 2006/10/18 05:07:42 mmondor Exp $ */ - -/* - * Copyright (c) 2006, Matthew Mondor - * ALL RIGHTS RESERVED. - */ - -/* - * This module provides APIs to several tricks which may be required to - * support the functionality of a few C libraries when writing their JS stubs. - * Here are cases where it can be useful: - * - * - An object must be provided to a C library for an arbitrary amount of time - * and we must ensure that it does not get freed by the potential - * auto-destruction of the object by the garbage collector. - * An example of this is in js_pgsql.c where PQtrace() can be provided a - * FILE *. We thus can store the wrapping File object as a property into - * the context-global rooted GCRoot object, and delete it from the object - * when PQuntrace() is called. - * An application using this functionality must call js_InitGCRoot() when - * creating contexts and js_DestroyGCRoot() before destroying the contexts. - * It may then call js_GCRoot() to obtain the JSObject pointer of the rooted - * object to assiciate properties with. Since this clobbers the private - * data which can be set for a context using JS_SetContextPrivate(), - * alternate functions are provided to store arbitrary context-specific - * data: js_GCRoot_udata_set()/js_GCRoot_udata_get(). - * - A C API function must be provided an object which is internally wrapped - * by a JSObject, but to which it is impossibe to pass the JSContext * and - * JSObject *. There must then be a way to map internal arbitrary C - * pointers to their corresponding JSContext/JSObject pointers, in which - * case we may use the js_map_add() function at object construction and - * js_map_remove() at its destruction, and js_map_lookup() to obtain the - * necessary information from within the C function. This case most often - * occurs for callback functions, like for the notice receiver callback - * in js_pgsql.c. - * To use this functionality, the application must call js_map_init() after - * creating the runtime, and js_map_destroy() before destroying the runtime. - * Provision is also made to store an additional user data pointer. - * - In cases where the private data set on an object also must hold the - * JSContext * and JSObject * associated with it, it is possible to use - * the js_udata_alloc() and js_udata_free() to create/destroy private - * data objects. Additionally, udata_t also allows to store more than one - * void *, which may be useful. - */ - - - -#include - -#include - -#include -#include -#include -#include -#include -#include - -#include - -#include - - - -/* - * We define a structure allowing to continue storing context-specific - * user-data, while also storing the gcroot which requires a unique address in - * the process space (and can't be on the stack) to be rooted. - */ -typedef struct cxspec { - JSObject *gcroot; - udata_t *udata; -} cxspec_t; - - - -/* - * Static functions prototypes - */ -static int omap_keycmp(const void *, const void *, size_t); -static u_int32_t omap_keyhash(const void *, size_t); - - - -/* - * Static globals - */ - -/* Dir class */ -static JSClass gcroot_class = { - "GCRoot", 0, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, - JS_PropertyStub, JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, - JS_FinalizeStub -}; - -/* - * XXX since these are runtime-global, they should be lock-protected if we - * wanted thread-safe code. - */ -static pool_t udata_pool; -static pool_t omap_pool; -static hashtable_t omap_table; - - - -/* - * GCRoot object control - */ - -JSBool -js_InitGCRoot(JSContext *cx) -{ - cxspec_t *d = NULL; - - if ((d = malloc(sizeof(cxspec_t))) == NULL) { - (void) fprintf(stderr, "GCRoot: malloc()\n"); - return JS_FALSE; - } - d->udata = NULL; - - /* Instantiate new object */ - if ((d->gcroot = JS_NewObject(cx, &gcroot_class, NULL, NULL)) - == NULL) { - (void) fprintf(stderr, "GCRoot: JS_NewObject(gcroot)\n"); - goto err; - } - if (!JS_AddRoot(cx, &d->gcroot)) { - (void) fprintf(stderr, "GCRoot: JS_AddRoot(gcroot)\n"); - goto err; - } - JS_SetContextPrivate(cx, d); - - return JS_TRUE; - -err: - if (d != NULL) - free(d); - - return JS_FALSE; -} - -void -js_DestroyGCRoot(JSContext *cx) -{ - cxspec_t *d; - - if ((d = JS_GetContextPrivate(cx)) != NULL) { - assert(d->udata == NULL); - if (d->gcroot != NULL) { - if (!JS_RemoveRoot(cx, &d->gcroot)) - (void) fprintf(stderr, - "GCRoot: JS_RemoveRoot(gcroot)\n"); - } - free(d); - JS_SetContextPrivate(cx, NULL); - } -} - -JSObject * -js_GCRoot(JSContext *cx) -{ - cxspec_t *d; - - d = JS_GetContextPrivate(cx); - assert(d != NULL); - - return d->gcroot; -} - -void -js_GCRoot_udata_set(JSContext *cx, udata_t *udata) -{ - cxspec_t *d; - - d = JS_GetContextPrivate(cx); - assert(d != NULL); - - d->udata = udata; -} - -udata_t * -js_GCRoot_udata_get(JSContext *cx) -{ - cxspec_t *d; - - d = JS_GetContextPrivate(cx); - assert(d != NULL); - - return d->udata; -} - -udata_t * -js_udata_alloc(JSContext *cx, JSObject *obj) -{ - udata_t *udata; - - if ((udata = (udata_t *)pool_alloc(&udata_pool, FALSE)) != NULL) { - int i; - - udata->cx = cx; - udata->obj = obj; - for (i = 0; i < UDATA_MAX; ) - udata->udata[i++] = NULL; - } - - return udata; -} - -void -js_udata_free(udata_t *udata) -{ - - (void) pool_free((pnode_t *)udata); -} - - -JSBool -js_map_init(void) -{ - - if (!pool_init(&udata_pool, "udata_pool", malloc, free, NULL, NULL, - sizeof(udata_t), 64, 1, 0)) - goto err; - - if (!pool_init(&omap_pool, "omap_pool", malloc, free, NULL, NULL, - sizeof(omap_t), 64, 1, 0)) - goto err; - - if (!hashtable_init(&omap_table, "omap_table", HT_DEFAULT_CAPACITY, - HT_DEFAULT_FACTOR, malloc, free, omap_keycmp, omap_keyhash, TRUE)) - goto err; - - return JS_TRUE; - -err: - if (HASHTABLE_VALID(&omap_table)) - hashtable_destroy(&omap_table, FALSE); - if (POOL_VALID(&omap_pool)) - (void) pool_destroy(&omap_pool); - if (POOL_VALID(&udata_pool)) - (void) pool_destroy(&udata_pool); - - return JS_FALSE; -} - -void -js_map_destroy(void) -{ - - if (HASHTABLE_VALID(&omap_table)) - hashtable_destroy(&omap_table, FALSE); - if (POOL_VALID(&omap_pool)) - (void) pool_destroy(&omap_pool); - if (POOL_VALID(&udata_pool)) - (void) pool_destroy(&udata_pool); -} - -JSBool -js_map_add(void *o, JSContext *cx, JSObject *obj, void *udata) -{ - omap_t *omap; - - assert(cx != NULL && obj != NULL && o != NULL); - if ((omap = (omap_t *)pool_alloc(&omap_pool, FALSE)) == NULL) - return FALSE; - - omap->mem = o; - omap->cx = cx; - omap->obj = obj; - omap->udata = udata; - - if (!hashtable_link(&omap_table, &omap->node, &omap->mem, - sizeof(void *), TRUE)) { - (void) pool_free((pnode_t *)omap); - return JS_FALSE; - } - - return JS_TRUE; -} - -omap_t * -js_map_lookup(void *o) -{ - - return (omap_t *)hashtable_lookup(&omap_table, &o, sizeof(void *)); -} - -void -js_map_remove(omap_t *omap) -{ - - hashtable_unlink(&omap_table, &omap->node); - (void) pool_free((pnode_t *)omap); -} - -static int -omap_keycmp(const void *a, const void *b, size_t s) -{ - long da = (long)*(long *)a; - long db = (long)*(long *)b; - - return (int)(da - db); -} - -static u_int32_t -omap_keyhash(const void *a, size_t s) -{ - long d = (long)*(long *)a; - - return (u_int32_t)(d & 0xffffffff); -} diff --git a/mmsoftware/js/classes/js_gcroot.h b/mmsoftware/js/classes/js_gcroot.h deleted file mode 100644 index c941601..0000000 --- a/mmsoftware/js/classes/js_gcroot.h +++ /dev/null @@ -1,72 +0,0 @@ -/* $Id: js_gcroot.h,v 1.3 2006/10/18 05:07:42 mmondor Exp $ */ - -/* - * Copyright (c) 2006, Matthew Mondor - * ALL RIGHTS RESERVED. - */ - -#ifndef JSGCROOT_H -#define JSGCROOT_H - - - -#include - -#include -#include -#include - - - -#define UDATA_MAX 4 - -/* - * This structure allows to store/retreive user data from an object or - * context, in a way that passing the udata_t * alone permits to retreive the - * JSContext *, JSObject * and room for various user data pointers. - * This is useful when implementing various classes. - * We are using a pool_t in order to efficiently allocate and free these. - */ -typedef struct udata { - pnode_t node; - JSContext *cx; - JSObject *obj; - void *udata[UDATA_MAX]; -} udata_t; - -/* - * Used to implement a lookup hash table mapping arbitrary memory addresses to - * their JSObject *. This ideally would be on a per-context basis, but since - * we must be able to only rely on the address, we must make this table - * process-global. In fact, we also need to obtain the JSContext pointer. - * Thus, js_map_init() and js_map_destroy() should be called after runtime - * creation and before runtime finalization, respectively. - */ -typedef struct omap { - hashnode_t node; - void *mem; /* Key */ - JSContext *cx; - JSObject *obj; - void *udata; -} omap_t; - - - -extern JSBool js_InitGCRoot(JSContext *); -extern void js_DestroyGCRoot(JSContext *); -extern JSObject *js_GCRoot(JSContext *); -extern void js_GCRoot_udata_set(JSContext *, udata_t *); -extern udata_t *js_GCRoot_udata_get(JSContext *); - -extern udata_t *js_udata_alloc(JSContext *, JSObject *); -extern void js_udata_free(udata_t *); - -extern JSBool js_map_init(void); -extern void js_map_destroy(void); -extern JSBool js_map_add(void *, JSContext *, JSObject *, void *); -extern omap_t *js_map_lookup(void *); -extern void js_map_remove(omap_t *); - - - -#endif diff --git a/mmsoftware/js/classes/js_gd.c b/mmsoftware/js/classes/js_gd.c deleted file mode 100644 index 7dcfcb0..0000000 --- a/mmsoftware/js/classes/js_gd.c +++ /dev/null @@ -1,4357 +0,0 @@ -/* $Id: js_gd.c,v 1.6 2006/10/28 09:30:09 mmondor Exp $ */ - -/* - * Copyright (c) 2006, Matthew Mondor - * ALL RIGHTS RESERVED. - */ - -/* - * Refer to http://www.boutell.com/gd/manual2.0.33.html for more information. - * - * XXX TODO XXX - * - Implement GDIOCtx and related functions - * - gdImageSetBrush() and gdImageSetTile() functions documentation specify - * that one should not use the special gdBrushed or gdTiled if no image was - * set for them or if their image were destroyed. It would be lame to have - * to do this checking in this code, but we might have to. Will have to try - * or to check the code to see if a crash can occur. - */ - - - -#include - -#include - -#include -#include -#include -#include -#include -#include - -#include - -#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) - -struct property_spec { - const char *name; - int value; -}; - -#define SP(n) \ - { #n, n } - - - -/* - * Static prototypes - */ - -/* GD */ -static JSBool gd_constructor(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static void gd_finalize(JSContext *, JSObject *); - -static JSBool gd_sm_create(JSContext *, JSObject *, uintN, jsval *, jsval *); -static JSBool gd_sm_create_truecolor(JSContext *, JSObject *, uintN, jsval *, - jsval *); - -static JSBool gd_sm_create_from_file_i(JSContext *, JSObject *, uintN, - jsval *, jsval *, gdImagePtr (*)(FILE *), const char *); -static JSBool gd_sm_create_from_str_i(JSContext *, JSObject *, uintN, - jsval *, jsval *, gdImagePtr (*)(int, void *), - const char *); - -static JSBool gd_sm_create_from_jpeg(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool gd_sm_create_from_jpeg_str(JSContext *, JSObject *, uintN, - jsval *, jsval *); -static JSBool gd_sm_create_from_png(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool gd_sm_create_from_png_str(JSContext *, JSObject *, uintN, - jsval *, jsval *); -static JSBool gd_sm_create_from_gif(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool gd_sm_create_from_gif_str(JSContext *, JSObject *, uintN, - jsval *, jsval *); -static JSBool gd_sm_create_from_gd(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool gd_sm_create_from_gd_str(JSContext *, JSObject *, uintN, - jsval *, jsval *); -static JSBool gd_sm_create_from_gd2(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool gd_sm_create_from_gd2_str(JSContext *, JSObject *, uintN, - jsval *, jsval *); -static JSBool gd_sm_create_from_gd2_part(JSContext *, JSObject *, uintN, - jsval *, jsval *); -static JSBool gd_sm_create_from_gd2_part_str(JSContext *, JSObject *, uintN, - jsval *, jsval *); -static JSBool gd_sm_create_from_wbmp(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool gd_sm_create_from_wbmp_str(JSContext *, JSObject *, uintN, - jsval *, jsval *); -static JSBool gd_sm_create_from_xbm(JSContext *, JSObject *, uintN, jsval *, - jsval *); -#ifdef HAVE_XPM -static JSBool gd_sm_create_from_xpm(JSContext *, JSObject *, uintN, jsval *, - jsval *); -#endif -static JSBool gd_sm_true_color(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool gd_sm_true_color_alpha(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool gd_sm_ft_use_fontconfig(JSContext *, JSObject *, uintN, - jsval *, jsval *); - -/* GDImage */ -static JSObject *js_InitGDImageClass(JSContext *, JSObject *); -static JSBool gdimage_constructor(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static void gdimage_finalize(JSContext *, JSObject *); - -static JSBool gdimage_get_property(JSContext *, JSObject *, jsval id, - jsval *); -static JSBool gdimage_set_property(JSContext *, JSObject *, jsval id, - jsval *); - -static JSBool gdimage_m_image_destroy(JSContext *, JSObject *, uintN, - jsval *, jsval *); -static JSBool gdimage_m_save_file_i0(JSContext *, JSObject *, uintN, jsval *, - jsval *, void (*)(gdImagePtr, FILE *)); -static JSBool gdimage_m_save_file_i1(JSContext *, JSObject *, uintN, jsval *, - jsval *, void (*)(gdImagePtr, FILE *, int)); -static JSBool gdimage_m_save_str_i0(JSContext *, JSObject *, uintN, jsval *, - jsval *, void *(*)(gdImagePtr, int *), const char *); -static JSBool gdimage_m_save_str_i1(JSContext *, JSObject *, uintN, jsval *, - jsval *, void *(*)(gdImagePtr, int *, int), const char *); -static JSBool gdimage_m_jpeg(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool gdimage_m_jpeg_str(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool gdimage_m_gif(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool gdimage_m_gif_str(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool gdimage_m_gif_anim_begin(JSContext *, JSObject *, uintN, - jsval *, jsval *); -static JSBool gdimage_m_gif_anim_begin_str(JSContext *, JSObject *, uintN, - jsval *, jsval *); -static JSBool gdimage_m_gif_anim_add(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool gdimage_m_gif_anim_add_str(JSContext *, JSObject *, uintN, - jsval *, jsval *); -static JSBool gdimage_m_gif_anim_end(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool gdimage_m_gif_anim_end_str(JSContext *, JSObject *, uintN, - jsval *, jsval *); -static JSBool gdimage_m_png(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool gdimage_m_png_str(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool gdimage_m_png_ex(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool gdimage_m_png_ex_str(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool gdimage_m_wbmp(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool gdimage_m_wbmp_str(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool gdimage_m_gd(JSContext *, JSObject *, uintN, jsval *, jsval *); -static JSBool gdimage_m_gd_str(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool gdimage_m_gd2(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool gdimage_m_gd2_str(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool gdimage_m_truecolor_to_palette(JSContext *, JSObject *, uintN, - jsval *, jsval *); -static JSBool gdimage_m_create_palette_from_truecolor(JSContext *, - JSObject *, uintN, jsval *, jsval *); - -static JSBool gdimage_m_set_pixel(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool gdimage_m_primitive5_i(JSContext *, JSObject *, uintN, jsval *, - jsval *, void (*)(gdImagePtr, int, int, int, int, int)); -static JSBool gdimage_m_line(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool gdimage_m_dashed_line(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static gdPointPtr array_to_gdpoints_i(JSContext *, jsval, int *); -static int *array_to_ints_i(JSContext *, jsval, int *); -static JSBool gdimage_m_polygon_i(JSContext *, JSObject *, uintN, jsval *, - jsval *, void (*)(gdImagePtr, gdPointPtr, int, int)); -static JSBool gdimage_m_polygon(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool gdimage_m_open_polygon(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool gdimage_m_rectangle(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool gdimage_m_filled_polygon(JSContext *, JSObject *, uintN, - jsval *, jsval *); -static JSBool gdimage_m_filled_rectangle(JSContext *, JSObject *, uintN, - jsval *, jsval *); -static JSBool gdimage_m_arc(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool gdimage_m_filled_arc(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool gdimage_m_filled_ellipse(JSContext *, JSObject *, uintN, - jsval *, jsval *); -static JSBool gdimage_m_fill_to_border(JSContext *, JSObject *, uintN, - jsval *, jsval *); -static JSBool gdimage_m_fill(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool gdimage_m_set_antialiased(JSContext *, JSObject *, uintN, - jsval *, jsval *); -static JSBool gdimage_m_set_antialiaseddontblend(JSContext *, JSObject *, - uintN, jsval *, jsval *); -static JSBool gdimage_m_set_brush(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool gdimage_m_set_tile(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool gdimage_m_set_style(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool gdimage_m_set_thickness(JSContext *, JSObject *, uintN, - jsval *, jsval *); -static JSBool gdimage_m_alpha_blending(JSContext *, JSObject *, uintN, - jsval *, jsval *); -static JSBool gdimage_m_save_alpha(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool gdimage_m_set_clip(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool gdimage_m_get_clip(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool gdimage_m_alpha(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool gdimage_m_get_pixel(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool gdimage_m_bounds_safe(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool gdimage_m_image_sx(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool gdimage_m_image_sy(JSContext *, JSObject *, uintN, jsval *, - jsval *); - -static JSBool gdimage_m_int_int3_i(JSContext *, JSObject *, uintN, jsval *, - jsval *, int (*)(gdImagePtr, int, int, int)); -static JSBool gdimage_m_int_int4_i(JSContext *, JSObject *, uintN, jsval *, - jsval *, int (*)(gdImagePtr, int, int, int, int)); -static JSBool gdimage_m_color_allocate(JSContext *, JSObject *, uintN, - jsval *, jsval *); -static JSBool gdimage_m_color_allocate_alpha(JSContext *, JSObject *, uintN, - jsval *, jsval *); -static JSBool gdimage_m_color_closest(JSContext *, JSObject *, uintN, - jsval *, jsval *); -static JSBool gdimage_m_color_closest_alpha(JSContext *, JSObject *, uintN, - jsval *, jsval *); -static JSBool gdimage_m_color_closest_hwb(JSContext *, JSObject *, uintN, - jsval *, jsval *); -static JSBool gdimage_m_color_exact(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool gdimage_m_color_resolve(JSContext *, JSObject *, uintN, - jsval *, jsval *); -static JSBool gdimage_m_color_resolve_alpha(JSContext *, JSObject *, uintN, - jsval *, jsval *); -static JSBool gdimage_m_colors_total(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool gdimage_m_red(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool gdimage_m_green(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool gdimage_m_blue(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool gdimage_m_get_interlaced(JSContext *, JSObject *, uintN, - jsval *, jsval *); -static JSBool gdimage_m_get_transparent(JSContext *, JSObject *, uintN, - jsval *, jsval *); -static JSBool gdimage_m_color_deallocate(JSContext *, JSObject *, uintN, - jsval *, jsval *); -static JSBool gdimage_m_color_transparent(JSContext *, JSObject *, uintN, - jsval *, jsval *); - -static JSBool gdimage_m_char_i(JSContext *, JSObject *, uintN, jsval *, - jsval *, - void (*)(gdImagePtr, gdFontPtr, int, int, int, int)); -static JSBool gdimage_m_string_i(JSContext *, JSObject *, uintN, jsval *, - jsval *, void (*)(gdImagePtr, gdFontPtr, int, int, - unsigned char *, int)); -static JSBool gdimage_m_char(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool gdimage_m_char_up(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool gdimage_m_string(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool gdimage_m_string_up(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool gdimage_m_string_ft(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool gdimage_m_string_ft_ex(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool gdimage_m_string_ft_circle(JSContext *, JSObject *, uintN, - jsval *, jsval *); - -static JSBool gdimage_m_copy(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool gdimage_m_copy_resized(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool gdimage_m_copy_resampled(JSContext *, JSObject *, uintN, - jsval *, jsval *); -static JSBool gdimage_m_copy_rotated(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool gdimage_m_copy_merge(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool gdimage_m_copy_merge_gray(JSContext *, JSObject *, uintN, - jsval *, jsval *); -static JSBool gdimage_m_palette_copy(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool gdimage_m_square_to_circle(JSContext *, JSObject *, uintN, - jsval *, jsval *); - -static JSBool gdimage_m_compare(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool gdimage_m_interlace(JSContext *, JSObject *, uintN, jsval *, - jsval *); - -/* GDFont */ -static JSObject *js_InitGDFontClass(JSContext *, JSObject *, JSObject *); -static JSBool gdfont_constructor(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static void gdfont_finalize(JSContext *, JSObject *); -static JSObject *gdfont_new(JSContext *, gdFontPtr); -static gdFontPtr gdfont_get(JSContext *, jsval); - - - -/* - * Static globals - */ - -/* - * GD - */ - -static JSClass gd_class = { - "GD", 0, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, - JS_PropertyStub, JS_EnumerateStub, JS_ResolveStub, - JS_ConvertStub, gd_finalize -}; - -/* Static methods */ -static JSFunctionSpec gd_smethods[] = { - { "create", gd_sm_create, 2, 0, 0 }, - { "createTrueColor", gd_sm_create_truecolor, 2, 0, 0 }, - { "createFromJpeg", gd_sm_create_from_jpeg, 1, 0, 0 }, - { "createFromJpegStr", gd_sm_create_from_jpeg_str, 1, 0, 0 }, - { "createFromPng", gd_sm_create_from_png, 1, 0, 0 }, - { "createFromPngStr", gd_sm_create_from_png_str, 1, 0, 0 }, - { "createFromGif", gd_sm_create_from_gif, 1, 0, 0 }, - { "createFromGifStr", gd_sm_create_from_gif_str, 1, 0, 0 }, - { "createFromGd", gd_sm_create_from_gd, 1, 0, 0 }, - { "createFromGdStr", gd_sm_create_from_gd_str, 1, 0, 0 }, - { "createFromGd2", gd_sm_create_from_gd2, 1, 0, 0 }, - { "createFromGd2Str", gd_sm_create_from_gd2_str, 1, 0, 0 }, - { "createFromGd2Part", gd_sm_create_from_gd2_part, 5, 0, 0 }, - { "createFromGd2PartStr", gd_sm_create_from_gd2_part_str, 5, 0, 0 }, - { "createFromWBMP", gd_sm_create_from_wbmp, 1, 0, 0 }, - { "createFromWBMPStr", gd_sm_create_from_wbmp_str, 1, 0, 0 }, - { "createFromXbm", gd_sm_create_from_xbm, 1, 0, 0 }, -#ifdef HAVE_XPM - { "createFromXpm", gd_sm_create_from_xpm, 1, 0, 0 }, -#endif - { "trueColor", gd_sm_true_color, 3, 0, 0 }, - { "trueColorAlpha", gd_sm_true_color_alpha, 4, 0, 0 }, - { "ftUseFontConfig", gd_sm_ft_use_fontconfig, 1, 0, 0 }, - { NULL, NULL, 0, 0, 0 } -}; - -/* Static properties */ -static struct property_spec gd_sprops[] = { - SP(gdDisposalNone), - SP(gdDisposalUnknown), - SP(gdDisposalRestoreBackground), - SP(gdDisposalRestorePrevious), - SP(GD2_FMT_RAW), - SP(GD2_FMT_COMPRESSED), - SP(gdAntiAliased), - SP(gdBrushed), - SP(gdMaxColors), - SP(gdStyled), - SP(gdStyledBrushed), - SP(gdDashSize), - SP(gdTiled), - SP(gdTransparent), - SP(gdArc), - SP(gdChord), - SP(gdPie), - SP(gdNoFill), - SP(gdEdged), - SP(gdAlphaOpaque), - SP(gdAlphaTransparent), - SP(GD_CMP_IMAGE), - SP(GD_CMP_NUM_COLORS), - SP(GD_CMP_COLOR), - SP(GD_CMP_SIZE_X), - SP(GD_CMP_SIZE_Y), - SP(GD_CMP_TRANSPARENT), - SP(GD_CMP_BACKGROUND), - SP(GD_CMP_INTERLACE), - SP(GD_CMP_TRUECOLOR), - SP(gdFTEX_Unicode), - SP(gdFTEX_Shift_JIS), - SP(gdFTEX_Big5), - { NULL, 0 } -}; - - - -/* - * GDImage - */ - -/* Class */ -static JSClass gdimage_class = { - "GDImage", JSCLASS_HAS_PRIVATE, JS_PropertyStub, JS_PropertyStub, - gdimage_get_property, gdimage_set_property, JS_EnumerateStub, - JS_ResolveStub, JS_ConvertStub, gdimage_finalize -}; - -/* Properties */ -enum gdimage_props { - GDIMAGE_P_SX, - GDIMAGE_P_SY, - GDIMAGE_P_MAX -}; - -static JSPropertySpec gdimage_properties[GDIMAGE_P_MAX + 1] = { - { "sx", GDIMAGE_P_SX, JSPROP_ENUMERATE | JSPROP_READONLY, NULL, NULL }, - { "sy", GDIMAGE_P_SY, JSPROP_ENUMERATE | JSPROP_READONLY, NULL, NULL }, - { NULL, 0, 0, NULL, NULL } -}; - -/* Methods */ -static JSFunctionSpec gdimage_methods[] = { - { "destroy", gdimage_m_image_destroy, 0, 0, 0 }, - { "jpeg", gdimage_m_jpeg, 2, 0, 0 }, - { "jpegStr", gdimage_m_jpeg_str, 1, 0, 0 }, - { "gif", gdimage_m_gif, 1, 0, 0 }, - { "gifStr", gdimage_m_gif_str, 0, 0, 0 }, - { "gifAnimBegin", gdimage_m_gif_anim_begin, 3, 0, 0 }, - { "gifAnimBeginStr", gdimage_m_gif_anim_begin_str, 2, 0, 0 }, - { "gifAnimAdd", gdimage_m_gif_anim_add, 7, 0, 0 }, - { "gifAnimAddStr", gdimage_m_gif_anim_add_str, 6, 0, 0 }, - { "gifAnimEnd", gdimage_m_gif_anim_end, 1, 0, 0 }, - { "gifAnimEndStr", gdimage_m_gif_anim_end_str, 0, 0, 0 }, - { "png", gdimage_m_png, 1, 0, 0 }, - { "pngStr", gdimage_m_png_str, 0, 0, 0 }, - { "pngEx", gdimage_m_png_ex, 2, 0, 0 }, - { "pngExStr", gdimage_m_png_ex_str, 1, 0, 0 }, - { "wbmp", gdimage_m_wbmp, 2, 0, 0 }, - { "wbmpStr", gdimage_m_wbmp_str, 1, 0, 0 }, - { "gd", gdimage_m_gd, 1, 0, 0 }, - { "gdStr", gdimage_m_gd_str, 0, 0, 0 }, - { "gd2", gdimage_m_gd2, 3, 0, 0 }, - { "gd2Str", gdimage_m_gd2_str, 2, 0, 0 }, - { "trueColorToPalette", gdimage_m_truecolor_to_palette, 2, 0, 0 }, - { "createPaletteFromTrueColor", - gdimage_m_create_palette_from_truecolor, 2, 0, 0 }, - - { "setPixel", gdimage_m_set_pixel, 3, 0, 0 }, - { "line", gdimage_m_line, 5, 0, 0 }, - { "dashedLine", gdimage_m_dashed_line, 5, 0, 0 }, - { "polygon", gdimage_m_polygon, 2, 0, 0 }, - { "openPolygon", gdimage_m_open_polygon, 2, 0, 0 }, - { "rectangle", gdimage_m_rectangle, 5, 0, 0 }, - { "filledPolygon", gdimage_m_filled_polygon, 2, 0, 0 }, - { "filledRectangle", gdimage_m_filled_rectangle, 5, 0, 0 }, - { "arc", gdimage_m_arc, 7, 0, 0 }, - { "filledArc", gdimage_m_filled_arc, 8, 0, 0 }, - { "filledEllipse", gdimage_m_filled_ellipse, 5, 0, 0 }, - { "fillToBorder", gdimage_m_fill_to_border, 4, 0, 0 }, - { "fill", gdimage_m_fill, 3, 0, 0 }, - { "setAntiAliased", gdimage_m_set_antialiased, 1, 0, 0 }, - { "setAntiAliasedDontBlend", gdimage_m_set_antialiaseddontblend, - 2, 0, 0 }, - { "setBrush", gdimage_m_set_brush, 1, 0, 0 }, - { "setTile", gdimage_m_set_tile, 1, 0, 0 }, - { "setStyle", gdimage_m_set_style, 1, 0, 0 }, - { "setThickness", gdimage_m_set_thickness, 1, 0, 0 }, - { "alphaBlending", gdimage_m_alpha_blending, 1, 0, 0 }, - { "saveAlpha", gdimage_m_save_alpha, 1, 0, 0 }, - { "setClip", gdimage_m_set_clip, 4, 0, 0 }, - { "getClip", gdimage_m_get_clip, 0, 0, 0 }, - { "alpha", gdimage_m_alpha, 1, 0, 0 }, - { "getPixel", gdimage_m_get_pixel, 2, 0, 0 }, - { "boundsSafe", gdimage_m_bounds_safe, 2, 0, 0 }, - { "imageSX", gdimage_m_image_sx, 0, 0, 0 }, - { "imageSY", gdimage_m_image_sy, 0, 0, 0 }, - - { "colorAllocate", gdimage_m_color_allocate, 3, 0, 0 }, - { "colorAllocateAlpha", gdimage_m_color_allocate_alpha, 4, 0, 0 }, - { "colorClosest", gdimage_m_color_closest, 3, 0, 0 }, - { "colorClosestAlpha", gdimage_m_color_closest_alpha, 4, 0, 0 }, - { "colorClosestHWB", gdimage_m_color_closest_hwb, 3, 0, 0 }, - { "colorExact", gdimage_m_color_exact, 3, 0, 0 }, - { "colorResolve", gdimage_m_color_resolve, 3, 0, 0 }, - { "colorResolveAlpha", gdimage_m_color_resolve_alpha, 4, 0, 0 }, - { "colorsTotal", gdimage_m_colors_total, 0, 0, 0 }, - { "red", gdimage_m_red, 1, 0, 0 }, - { "green", gdimage_m_green, 1, 0, 0 }, - { "blue", gdimage_m_blue, 1, 0, 0 }, - { "getInterlaced", gdimage_m_get_interlaced, 0, 0, 0 }, - { "getTransparent", gdimage_m_get_transparent, 0, 0, 0 }, - { "colorDeallocate", gdimage_m_color_deallocate, 1, 0, 0 }, - { "colorTransparent", gdimage_m_color_transparent, 1, 0, 0 }, - - /* - * We don't implement charRight16, charUp16, stringRight16 and - * stringUp16 because GD fonts actually don't support 16-bit chars. - * stringFt[Ex] may be used instead which allow UTF-8 and use of - * external fonts. Moreover, we don't support stringFtt which was for - * use with FontType v1, and is now deprecated (calling the v2 - * function internally for compatibility). - */ - { "charRight", gdimage_m_char, 5, 0, 0 }, /* char == reserved! */ - { "charUp", gdimage_m_char_up, 5, 0, 0 }, - { "stringRight", gdimage_m_string, 5, 0, 0 }, /* string reserved! */ - { "stringUp", gdimage_m_string_up, 5, 0, 0 }, - { "stringFt", gdimage_m_string_ft, 7, 0, 0 }, - { "stringFtEx", gdimage_m_string_ft_ex, 8, 0, 0 }, - { "stringFtCircle", gdimage_m_string_ft_circle, 10, 0, 0 }, - - { "copy", gdimage_m_copy, 7, 0, 0 }, - { "copyResized", gdimage_m_copy_resized, 9, 0, 0 }, - { "copyResampled", gdimage_m_copy_resampled, 9, 0, 0 }, - { "copyRotated", gdimage_m_copy_rotated, 8, 0, 0 }, - { "copyMerge", gdimage_m_copy_merge, 8, 0, 0 }, - { "copyMergeGray", gdimage_m_copy_merge_gray, 8, 0, 0 }, - { "paletteCopy", gdimage_m_palette_copy, 1, 0, 0 }, - { "squareToCircle", gdimage_m_square_to_circle, 1, 0, 0 }, - - { "compare", gdimage_m_compare, 1, 0, 0 }, - { "interlace", gdimage_m_interlace, 1, 0, 0 }, - { NULL, NULL, 0, 0, 0 } -}; - - - -/* - * GDFont - */ - -/* Class */ -static JSClass gdfont_class = { - "GDFont", JSCLASS_HAS_PRIVATE, JS_PropertyStub, JS_PropertyStub, - JS_PropertyStub, JS_PropertyStub, JS_EnumerateStub, JS_ResolveStub, - JS_ConvertStub, gdfont_finalize -}; - - - -/* - * GD object control - */ - -JSObject * -js_InitGDClass(JSContext *cx, JSObject *obj) -{ - JSObject *proto, *ctor; - struct property_spec *sp; - - if ((proto = JS_InitClass(cx, obj, NULL, &gd_class, gd_constructor, - 0, NULL, NULL, NULL, gd_smethods)) == NULL) { - (void) fprintf(stderr, "Error initializing GD class\n"); - goto err; - } - - /* Create static properties. */ - if ((ctor = JS_GetConstructor(cx, proto)) == NULL) { - (void) fprintf(stderr, "GD: JS_GetConstructor == NULL\n"); - return NULL; - } - for (sp = gd_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, - "GD: Error defining property %s\n", sp->name); - return NULL; - } - } - - /* - * Initialize GDImage class since we'll need to instanciate it from C - */ - if (js_InitGDImageClass(cx, obj) == NULL) { - (void) fprintf(stderr, "GD: InitGDImageClass()\n"); - goto err; - } - /* - * Same for GDFont, which will use our supplied GD object pointer to - * set properties for existing fonts. - */ - if (js_InitGDFontClass(cx, obj, ctor) == NULL) { - (void) fprintf(stderr, "GD: InitGDFontClass()\n"); - goto err; - } - - if (gdFontCacheSetup() != 0) { - (void) fprintf(stderr, "gdFontCacheSetup()\n"); - goto err; - } - - return proto; - -err: - return NULL; -} - -/* ARGSUSED */ -static JSBool -gd_constructor(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - - *rval = OBJECT_TO_JSVAL(NULL); - QUEUE_EXCEPTION("GD object not user-instanciable"); - - return JS_FALSE; -} - -/* ARGSUSED */ -static void -gd_finalize(JSContext *cx, JSObject *obj) -{ - - /* NOOP */ -} - - - -/* GD static methods */ -static JSBool -gd_sm_create(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - gdImagePtr img = NULL; - JSObject *o; - - if (argc != 2) { - QUEUE_EXCEPTION("Wrong number of arguments"); - goto err; - } - if (!JSVAL_IS_INT(argv[0])) { - QUEUE_EXCEPTION("Argument 1 not an Integer"); - goto err; - } - if (!JSVAL_IS_INT(argv[1])) { - QUEUE_EXCEPTION("Argument 2 not an Integer"); - goto err; - } - - if ((img = gdImageCreate(JSVAL_TO_INT(argv[0]), - JSVAL_TO_INT(argv[1]))) == NULL) { - QUEUE_EXCEPTION("gdImageCreate()"); - goto err; - } - - if ((o = JS_NewObject(cx, &gdimage_class, NULL, NULL)) == NULL) { - QUEUE_EXCEPTION("Internal error!"); - goto err; - } - *rval = OBJECT_TO_JSVAL(o); - if (!JS_SetPrivate(cx, o, img)) { - QUEUE_EXCEPTION("Internal error!"); - goto err; - } - - return JS_TRUE; - -err: - *rval = OBJECT_TO_JSVAL(NULL); - if (img) - gdImageDestroy(img); - - return JS_FALSE; -} - -static JSBool -gd_sm_create_truecolor(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - gdImagePtr img = NULL; - JSObject *o; - - if (argc != 2) { - QUEUE_EXCEPTION("Wrong number of arguments"); - goto err; - } - if (!JSVAL_IS_INT(argv[0])) { - QUEUE_EXCEPTION("Argument 1 not an Integer"); - goto err; - } - if (!JSVAL_IS_INT(argv[1])) { - QUEUE_EXCEPTION("Argument 2 not an Integer"); - goto err; - } - - if ((img = gdImageCreateTrueColor(JSVAL_TO_INT(argv[0]), - JSVAL_TO_INT(argv[1]))) == NULL) { - QUEUE_EXCEPTION("gdImageCreate()"); - goto err; - } - - if ((o = JS_NewObject(cx, &gdimage_class, NULL, NULL)) == NULL) { - QUEUE_EXCEPTION("Internal error!"); - goto err; - } - *rval = OBJECT_TO_JSVAL(o); - if (!JS_SetPrivate(cx, o, img)) { - QUEUE_EXCEPTION("Internal error!"); - goto err; - } - - return JS_TRUE; - -err: - *rval = OBJECT_TO_JSVAL(NULL); - if (img != NULL) - gdImageDestroy(img); - - return JS_FALSE; -} - -static JSBool -gd_sm_create_from_file_i(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval, gdImagePtr (*funcptr)(FILE *), const char *funcname) -{ - FILE *fh; - gdImagePtr img = NULL; - JSObject *o; - - if (argc != 1) { - QUEUE_EXCEPTION("Wrong number of arguments"); - goto err; - } - if ((fh = file_fh(cx, argv[0])) == NULL) { - QUEUE_EXCEPTION("Argument 1 not a File object"); - goto err; - } - - if ((img = funcptr(fh)) == NULL) { - QUEUE_EXCEPTION(funcname); - goto err; - } - - if ((o = JS_NewObject(cx, &gdimage_class, NULL, NULL)) == NULL) { - QUEUE_EXCEPTION("Internal error!"); - goto err; - } - *rval = OBJECT_TO_JSVAL(o); - if (!JS_SetPrivate(cx, o, img)) { - QUEUE_EXCEPTION("Internal error!"); - goto err; - } - - return JS_TRUE; - -err: - *rval = OBJECT_TO_JSVAL(NULL); - if (img != NULL) - gdImageDestroy(img); - - return JS_FALSE; -} - -/* - * We replace gdImageCreateFrom*Ptr() by methods using a String. - */ -static JSBool -gd_sm_create_from_str_i(JSContext *cx, JSObject *obj, uintN argc, - jsval *argv, jsval *rval, gdImagePtr (*funcptr)(int, void *), - const char *funcname) -{ - JSString *str; - char *bytes; - size_t size; - gdImagePtr img = NULL; - JSObject *o; - - 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; - } - - str = JSVAL_TO_STRING(argv[0]); - if ((bytes = JS_GetStringBytes(str)) == NULL) { - QUEUE_EXCEPTION("Internal error!"); - goto err; - } - size = JS_GetStringLength(str); - - if ((img = funcptr((int)size, bytes)) == NULL) { - QUEUE_EXCEPTION(funcname); - goto err; - } - - if ((o = JS_NewObject(cx, &gdimage_class, NULL, NULL)) == NULL) { - QUEUE_EXCEPTION("Internal error!"); - goto err; - } - *rval = OBJECT_TO_JSVAL(o); - if (!JS_SetPrivate(cx, o, img)) { - QUEUE_EXCEPTION("Internal error!"); - goto err; - } - - return JS_TRUE; - -err: - *rval = OBJECT_TO_JSVAL(NULL); - if (img != NULL) - gdImageDestroy(img); - - return JS_FALSE; -} - -static JSBool -gd_sm_create_from_jpeg(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - - return gd_sm_create_from_file_i(cx, obj, argc, argv, rval, - gdImageCreateFromJpeg, "gdImageCreateFromJpeg()"); -} - -static JSBool -gd_sm_create_from_jpeg_str(JSContext *cx, JSObject *obj, uintN argc, - jsval *argv, jsval *rval) -{ - - return gd_sm_create_from_str_i(cx, obj, argc, argv, rval, - gdImageCreateFromJpegPtr, "gdImageCreateFromJpegPtr()"); -} - -static JSBool -gd_sm_create_from_png(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - - return gd_sm_create_from_file_i(cx, obj, argc, argv, rval, - gdImageCreateFromPng, "gdImageCreateFromPng()"); -} - -static JSBool -gd_sm_create_from_png_str(JSContext *cx, JSObject *obj, uintN argc, - jsval *argv, jsval *rval) -{ - - return gd_sm_create_from_str_i(cx, obj, argc, argv, rval, - gdImageCreateFromPngPtr, "gdImageCreateFromPngPtr()"); -} - -static JSBool -gd_sm_create_from_gif(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - - return gd_sm_create_from_file_i(cx, obj, argc, argv, rval, - gdImageCreateFromGif, "gdImageCreateFromGif()"); -} - -static JSBool -gd_sm_create_from_gif_str(JSContext *cx, JSObject *obj, uintN argc, - jsval *argv, jsval *rval) -{ - - return gd_sm_create_from_str_i(cx, obj, argc, argv, rval, - gdImageCreateFromGifPtr, "gdImageCreateFromGifPtr()"); -} - -static JSBool -gd_sm_create_from_gd(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - - return gd_sm_create_from_file_i(cx, obj, argc, argv, rval, - gdImageCreateFromGd, "gdImageCreateFromGd()"); -} - -static JSBool -gd_sm_create_from_gd_str(JSContext *cx, JSObject *obj, uintN argc, - jsval *argv, jsval *rval) -{ - - return gd_sm_create_from_str_i(cx, obj, argc, argv, rval, - gdImageCreateFromGdPtr, "gdImageCreateFromGdPtr()"); -} - -static JSBool -gd_sm_create_from_gd2(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - - return gd_sm_create_from_file_i(cx, obj, argc, argv, rval, - gdImageCreateFromGd2, "gdImageCreateFromGd2()"); -} - -static JSBool -gd_sm_create_from_gd2_str(JSContext *cx, JSObject *obj, uintN argc, - jsval *argv, jsval *rval) -{ - - return gd_sm_create_from_str_i(cx, obj, argc, argv, rval, - gdImageCreateFromGd2Ptr, "gdImageCreateFromGd2Ptr()"); -} - -static JSBool -gd_sm_create_from_gd2_part(JSContext *cx, JSObject *obj, uintN argc, - jsval *argv, jsval *rval) -{ - FILE *fh; - gdImagePtr img = NULL; - JSObject *o; - - if (argc != 5) { - QUEUE_EXCEPTION("Wrong number of arguments"); - goto err; - } - if ((fh = file_fh(cx, argv[0])) == NULL) { - QUEUE_EXCEPTION("Argument 1 not a File object"); - goto err; - } - if (!JSVAL_IS_INT(argv[1])) { - QUEUE_EXCEPTION("Argument 2 not an Integer"); - goto err; - } - if (!JSVAL_IS_INT(argv[2])) { - QUEUE_EXCEPTION("Argument 3 not an Integer"); - goto err; - } - if (!JSVAL_IS_INT(argv[3])) { - QUEUE_EXCEPTION("Argument 4 not an Integer"); - goto err; - } - if (!JSVAL_IS_INT(argv[4])) { - QUEUE_EXCEPTION("Argument 5 not an Integer"); - goto err; - } - - if ((img = gdImageCreateFromGd2Part(fh, JSVAL_TO_INT(argv[1]), - JSVAL_TO_INT(argv[2]), JSVAL_TO_INT(argv[3]), - JSVAL_TO_INT(argv[4]))) == NULL) { - QUEUE_EXCEPTION("gdImageCreateFromGd2Part()"); - goto err; - } - - if ((o = JS_NewObject(cx, &gdimage_class, NULL, NULL)) == NULL) { - QUEUE_EXCEPTION("Internal error!"); - goto err; - } - *rval = OBJECT_TO_JSVAL(o); - if (!JS_SetPrivate(cx, o, img)) { - QUEUE_EXCEPTION("Internal error!"); - goto err; - } - - return JS_TRUE; - -err: - *rval = OBJECT_TO_JSVAL(NULL); - if (img != NULL) - gdImageDestroy(img); - - return JS_FALSE; -} - -/* - * We replace gdImageCreateFrom*Ptr() by methods using a String. - */ -static JSBool -gd_sm_create_from_gd2_part_str(JSContext *cx, JSObject *obj, uintN argc, - jsval *argv, jsval *rval) -{ - JSString *str; - char *bytes; - size_t size; - gdImagePtr img = NULL; - JSObject *o; - - if (argc != 5) { - 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 Integer"); - goto err; - } - if (!JSVAL_IS_INT(argv[2])) { - QUEUE_EXCEPTION("Argument 3 not an Integer"); - goto err; - } - if (!JSVAL_IS_INT(argv[3])) { - QUEUE_EXCEPTION("Argument 4 not an Integer"); - goto err; - } - if (!JSVAL_IS_INT(argv[4])) { - QUEUE_EXCEPTION("Argument 5 not an Integer"); - goto err; - } - - str = JSVAL_TO_STRING(argv[0]); - if ((bytes = JS_GetStringBytes(str)) == NULL) { - QUEUE_EXCEPTION("Internal error!"); - goto err; - } - size = JS_GetStringLength(str); - - if ((img = gdImageCreateFromGd2PartPtr((int)size, bytes, - JSVAL_TO_INT(argv[1]), JSVAL_TO_INT(argv[2]), - JSVAL_TO_INT(argv[3]), JSVAL_TO_INT(argv[4]))) == NULL) { - QUEUE_EXCEPTION("GdImageCreateFromGd2PartPtr()"); - goto err; - } - - if ((o = JS_NewObject(cx, &gdimage_class, NULL, NULL)) == NULL) { - QUEUE_EXCEPTION("Internal error!"); - goto err; - } - *rval = OBJECT_TO_JSVAL(o); - if (!JS_SetPrivate(cx, o, img)) { - QUEUE_EXCEPTION("Internal error!"); - goto err; - } - - return JS_TRUE; - -err: - *rval = OBJECT_TO_JSVAL(NULL); - if (img != NULL) - gdImageDestroy(img); - - return JS_FALSE; -} - -static JSBool -gd_sm_create_from_wbmp(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - - return gd_sm_create_from_file_i(cx, obj, argc, argv, rval, - gdImageCreateFromWBMP, "gdImageCreateFromWBMP()"); -} - -static JSBool -gd_sm_create_from_wbmp_str(JSContext *cx, JSObject *obj, uintN argc, - jsval *argv, jsval *rval) -{ - - return gd_sm_create_from_str_i(cx, obj, argc, argv, rval, - gdImageCreateFromWBMPPtr, "gdImageCreateFromWBMPPtr()"); -} - -static JSBool -gd_sm_create_from_xbm(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - - return gd_sm_create_from_file_i(cx, obj, argc, argv, rval, - gdImageCreateFromXbm, "gdImageCreateFromXbm()"); -} - -#ifdef HAVE_XPM -static JSBool -gd_sm_create_from_xpm(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval, gdImagePtr (*funcptr)(FILE *), const char *funcname) -{ - const char *bytes; - gdImagePtr img = NULL; - JSObject *o; - - if (argc != 1) { - QUEUE_EXCEPTION("Wrong number of arguments"); - goto err; - } - if (!JSVAL_IS_STRING(argv[0]) || - (bytes = JS_GetStringBytes(argv[0])) == NULL) { - QUEUE_EXCEPTION("Argument 1 not a String"); - goto err; - } - - /* XXX Perform path sanity/access checking */ - if ((img = gdImageCreateFromXpm(bytes)) == NULL) { - QUEUE_EXCEPTION("gdImageCreateFromXpm()"); - goto err; - } - - if ((o = JS_NewObject(cx, &gdimage_class, NULL, NULL)) == NULL) { - QUEUE_EXCEPTION("Internal error!"); - goto err; - } - *rval = OBJECT_TO_JSVAL(o); - if (!JS_SetPrivate(cx, o, img)) { - QUEUE_EXCEPTION("Internal error!"); - goto err; - } - - return JS_TRUE; - -err: - *rval = OBJECT_TO_JSVAL(NULL); - if (img != NULL) - gdImageDestroy(img); - - return JS_FALSE; -} -#endif - -static JSBool -gd_sm_true_color(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - int col, i1, i2, i3; - - if (argc != 3) { - QUEUE_EXCEPTION("Wrong number of arguments"); - goto err; - } - if (!JSVAL_IS_INT(argv[0])) { - QUEUE_EXCEPTION("Argument 1 not an Integer"); - goto err; - } - if (!JSVAL_IS_INT(argv[1])) { - QUEUE_EXCEPTION("Argument 2 not an Integer"); - goto err; - } - if (!JSVAL_IS_INT(argv[2])) { - QUEUE_EXCEPTION("Argument 3 not an Integer"); - goto err; - } - - /* Since gdTrueColor is a macro, avoid side-effects */ - i1 = JSVAL_TO_INT(argv[0]); - i2 = JSVAL_TO_INT(argv[1]); - i3 = JSVAL_TO_INT(argv[2]); - col = gdTrueColor(i1, i2, i3); - *rval = INT_TO_JSVAL(col); - - return JS_TRUE; - -err: - *rval = OBJECT_TO_JSVAL(NULL); - - return JS_FALSE; -} - -static JSBool -gd_sm_true_color_alpha(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - int col, i1, i2, i3, i4; - - if (argc != 4) { - QUEUE_EXCEPTION("Wrong number of arguments"); - goto err; - } - if (!JSVAL_IS_INT(argv[0])) { - QUEUE_EXCEPTION("Argument 1 not an Integer"); - goto err; - } - if (!JSVAL_IS_INT(argv[1])) { - QUEUE_EXCEPTION("Argument 2 not an Integer"); - goto err; - } - if (!JSVAL_IS_INT(argv[2])) { - QUEUE_EXCEPTION("Argument 3 not an Integer"); - goto err; - } - if (!JSVAL_IS_INT(argv[3])) { - QUEUE_EXCEPTION("Argument 4 not an Integer"); - goto err; - } - - /* Since gdTrueColorAlpha() is a macro, avoid side-effects */ - i1 = JSVAL_TO_INT(argv[0]); - i2 = JSVAL_TO_INT(argv[1]); - i3 = JSVAL_TO_INT(argv[2]); - i4 = JSVAL_TO_INT(argv[3]); - col = gdTrueColorAlpha(i1, i2, i3, i4); - *rval = INT_TO_JSVAL(col); - - return JS_TRUE; - -err: - *rval = OBJECT_TO_JSVAL(NULL); - - return JS_FALSE; -} - -static JSBool -gd_sm_ft_use_fontconfig(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - int v; - - if (argc != 1) { - QUEUE_EXCEPTION("Wrong number of arguments"); - goto err; - } - if (!JSVAL_IS_BOOLEAN(argv[0])) { - QUEUE_EXCEPTION("Argument 1 not a Boolean"); - goto err; - } - - v = gdFTUseFontConfig((int)JSVAL_TO_BOOLEAN(argv[0])); - *rval = BOOLEAN_TO_JSVAL((JSBool)v); - - return JS_TRUE; - -err: - *rval = OBJECT_TO_JSVAL(NULL); - - return JS_FALSE; -} - - -/* - * GDImage object control - */ - -static JSObject * -js_InitGDImageClass(JSContext *cx, JSObject *obj) -{ - - return (JS_InitClass(cx, obj, NULL, &gdimage_class, - gdimage_constructor, 0, gdimage_properties, gdimage_methods, NULL, - NULL)); -} - -/* ARGSUSED */ -static JSBool -gdimage_constructor(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - - QUEUE_EXCEPTION("GDImage class not user-instanciable"); - - return JS_FALSE; -} - -static void -gdimage_finalize(JSContext *cx, JSObject *obj) -{ - gdImagePtr img; - - if ((img = JS_GetInstancePrivate(cx, obj, &gdimage_class, NULL)) - != NULL) { - gdImageDestroy(img); - (void) JS_SetPrivate(cx, obj, NULL); - } -} - - -/* GDImage object property setters/getters */ -static JSBool -gdimage_get_property(JSContext *cx, JSObject *obj, jsval id, jsval *vp) -{ - jsint p; - gdImagePtr img; - - if (!JSVAL_IS_INT(id)) - return JS_TRUE; - p = (int)JSVAL_TO_INT(id); - - img = JS_GetInstancePrivate(cx, obj, &gdimage_class, NULL); - assert(img != NULL); - - switch (p) { - case GDIMAGE_P_SX: - *vp = INT_TO_JSVAL(img->sx); - break; - case GDIMAGE_P_SY: - *vp = INT_TO_JSVAL(img->sy); - break; - } - - return JS_TRUE; -} - -static JSBool -gdimage_set_property(JSContext *cx, JSObject *obj, jsval id, jsval *vp) -{ - jsint p; - gdImagePtr img; - - if (!JSVAL_IS_INT(id)) - return JS_TRUE; - p = (int)JSVAL_TO_INT(id); - - img = JS_GetInstancePrivate(cx, obj, &gdimage_class, NULL); - assert(img != NULL); - - switch (p) { - /* XXX No writable property yet */ - } - - return JS_TRUE; -} - - -/* - * GDImage object properties - */ - -/* - * GDImage object methods - */ - -static JSBool -gdimage_m_image_destroy(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - gdImagePtr img; - - img = JS_GetInstancePrivate(cx, obj, &gdimage_class, NULL); - assert(img != NULL); - - gdImageDestroy(img); - (void) JS_SetPrivate(cx, obj, NULL); - *rval = OBJECT_TO_JSVAL(NULL); - - return JS_TRUE; -} - -/* - * Called by multiple similar functions - */ -static JSBool -gdimage_m_save_file_i0(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval, void (*funcptr)(gdImagePtr, FILE *)) -{ - FILE *fh; - gdImagePtr img; - - *rval = OBJECT_TO_JSVAL(NULL); - - if (argc != 1) { - QUEUE_EXCEPTION("Wrong number of arguments"); - return JS_FALSE; - } - if ((fh = file_fh(cx, argv[0])) == NULL) { - QUEUE_EXCEPTION("Argument 1 not a File object"); - return JS_FALSE; - } - - img = JS_GetInstancePrivate(cx, obj, &gdimage_class, NULL); - assert(img != NULL); - - funcptr(img, fh); - - return JS_TRUE; -} - -static JSBool -gdimage_m_save_file_i1(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval, void (*funcptr)(gdImagePtr, FILE *, int)) -{ - FILE *fh; - gdImagePtr img; - - *rval = OBJECT_TO_JSVAL(NULL); - - if (argc != 2) { - QUEUE_EXCEPTION("Wrong number of arguments"); - return JS_FALSE; - } - if ((fh = file_fh(cx, argv[0])) == NULL) { - QUEUE_EXCEPTION("Argument 1 not a File object"); - return JS_FALSE; - } - if (!JSVAL_IS_INT(argv[1])) { - QUEUE_EXCEPTION("Argument 2 not an Integer"); - return JS_FALSE; - } - - img = JS_GetInstancePrivate(cx, obj, &gdimage_class, NULL); - assert(img != NULL); - - funcptr(img, fh, JSVAL_TO_INT(argv[1])); - - return JS_TRUE; -} - -static JSBool -gdimage_m_save_str_i0(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval, void *(*funcptr)(gdImagePtr, int *), const char *funcname) -{ - gdImagePtr img; - void *data = NULL; - int size; - JSString *str; - - if (argc != 0) { - QUEUE_EXCEPTION("Wrong number of arguments"); - goto err; - } - - img = JS_GetInstancePrivate(cx, obj, &gdimage_class, NULL); - assert(img != NULL); - - if ((data = funcptr(img, &size)) == NULL) { - QUEUE_EXCEPTION(funcname); - goto err; - } - - if ((str = JS_NewStringCopyN(cx, data, size)) == NULL) { - QUEUE_EXCEPTION("Out of memory!"); - goto err; - } - *rval = STRING_TO_JSVAL(str); - gdFree(data); - - return JS_TRUE; - -err: - *rval = OBJECT_TO_JSVAL(NULL); - if (data != NULL) - gdFree(data); - - return JS_FALSE; -} - -static JSBool -gdimage_m_save_str_i1(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval, void *(*funcptr)(gdImagePtr, int *, int), - const char *funcname) -{ - gdImagePtr img; - void *data = NULL; - int size; - JSString *str; - - if (argc != 1) { - QUEUE_EXCEPTION("Wrong number of arguments"); - goto err; - } - if (!JSVAL_IS_INT(argv[0])) { - QUEUE_EXCEPTION("Argument 1 not an Integer"); - goto err; - } - - img = JS_GetInstancePrivate(cx, obj, &gdimage_class, NULL); - assert(img != NULL); - - if ((data = funcptr(img, &size, JSVAL_TO_INT(argv[0]))) == NULL) { - QUEUE_EXCEPTION(funcname); - goto err; - } - - if ((str = JS_NewStringCopyN(cx, data, size)) == NULL) { - QUEUE_EXCEPTION("Out of memory!"); - goto err; - } - *rval = STRING_TO_JSVAL(str); - gdFree(data); - - return JS_TRUE; - -err: - *rval = OBJECT_TO_JSVAL(NULL); - if (data != NULL) - gdFree(data); - - return JS_FALSE; -} - -static JSBool -gdimage_m_jpeg(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - - return gdimage_m_save_file_i1(cx, obj, argc, argv, rval, gdImageJpeg); -} - -static JSBool -gdimage_m_jpeg_str(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - - return gdimage_m_save_str_i1(cx, obj, argc, argv, rval, - gdImageJpegPtr, "gdImageJpegPtr()"); -} - -static JSBool -gdimage_m_gif(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - - return gdimage_m_save_file_i0(cx, obj, argc, argv, rval, gdImageGif); -} - -static JSBool -gdimage_m_gif_str(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - - return gdimage_m_save_str_i0(cx, obj, argc, argv, rval, - gdImageGifPtr, "gdImageGifPtr()"); -} - -static JSBool -gdimage_m_gif_anim_begin(JSContext *cx, JSObject *obj, uintN argc, - jsval *argv, jsval *rval) -{ - FILE *fh; - gdImagePtr img; - - if (argc != 3) { - QUEUE_EXCEPTION("Wrong number of arguments"); - goto err; - } - if ((fh = file_fh(cx, argv[0])) == NULL) { - QUEUE_EXCEPTION("Argument 1 not a File object"); - goto err; - } - if (!JSVAL_IS_INT(argv[1])) { - QUEUE_EXCEPTION("Argument 2 not an Integer"); - goto err; - } - if (!JSVAL_IS_INT(argv[2])) { - QUEUE_EXCEPTION("Argument 3 not an Integer"); - goto err; - } - - img = JS_GetInstancePrivate(cx, obj, &gdimage_class, NULL); - assert(img != NULL); - - gdImageGifAnimBegin(img, fh, JSVAL_TO_INT(argv[1]), - JSVAL_TO_INT(argv[2])); - - return JS_TRUE; - -err: - *rval = OBJECT_TO_JSVAL(NULL); - - return JS_FALSE; -} - -static JSBool -gdimage_m_gif_anim_begin_str(JSContext *cx, JSObject *obj, uintN argc, - jsval *argv, jsval *rval) -{ - gdImagePtr img; - void *data = NULL; - int size; - JSString *str; - - if (argc != 2) { - QUEUE_EXCEPTION("Wrong number of arguments"); - goto err; - } - if (!JSVAL_IS_INT(argv[0])) { - QUEUE_EXCEPTION("Argument 1 not an Integer"); - goto err; - } - if (!JSVAL_IS_INT(argv[1])) { - QUEUE_EXCEPTION("Argument 2 not an Integer"); - goto err; - } - - img = JS_GetInstancePrivate(cx, obj, &gdimage_class, NULL); - assert(img != NULL); - - if ((data = gdImageGifAnimBeginPtr(img, &size, JSVAL_TO_INT(argv[0]), - JSVAL_TO_INT(argv[1]))) == NULL) { - QUEUE_EXCEPTION("gdImageGifAnimBeginPtr()"); - goto err; - } - - if ((str = JS_NewStringCopyN(cx, data, size)) == NULL) { - QUEUE_EXCEPTION("Out of memory!"); - goto err; - } - *rval = STRING_TO_JSVAL(str); - gdFree(data); - - return JS_TRUE; - -err: - *rval = OBJECT_TO_JSVAL(NULL); - if (data != NULL) - gdFree(data); - - return JS_FALSE; -} - -static JSBool -gdimage_m_gif_anim_add(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - FILE *fh; - gdImagePtr img, img2; - - if (argc != 7) { - QUEUE_EXCEPTION("Wrong number of arguments"); - goto err; - } - if ((fh = file_fh(cx, argv[0])) == NULL) { - QUEUE_EXCEPTION("Argument 1 not a File object"); - goto err; - } - if (!JSVAL_IS_INT(argv[1])) { - QUEUE_EXCEPTION("Argument 2 not an Integer"); - goto err; - } - if (!JSVAL_IS_INT(argv[2])) { - QUEUE_EXCEPTION("Argument 3 not an Integer"); - goto err; - } - if (!JSVAL_IS_INT(argv[3])) { - QUEUE_EXCEPTION("Argument 4 not an Integer"); - goto err; - } - if (!JSVAL_IS_INT(argv[4])) { - QUEUE_EXCEPTION("Argument 5 not an Integer"); - goto err; - } - if (!JSVAL_IS_INT(argv[5])) { - QUEUE_EXCEPTION("Argument 6 not an Integer"); - goto err; - } - if (JSVAL_IS_NULL(argv[6])) - img2 = NULL; - else { - JSObject *o; - - if (!JSVAL_IS_OBJECT(argv[6])) { - QUEUE_EXCEPTION( - "Argument 7 not Null or GDImage object"); - goto err; - } - o = JSVAL_TO_OBJECT(argv[6]); - if (!JS_InstanceOf(cx, o, &gdimage_class, NULL)) { - QUEUE_EXCEPTION( - "Argument 7 not Null or GDImage object"); - goto err; - } - - img2 = JS_GetInstancePrivate(cx, o, &gdimage_class, NULL); - assert(img2 != NULL); - } - - img = JS_GetInstancePrivate(cx, obj, &gdimage_class, NULL); - assert(img != NULL); - - gdImageGifAnimAdd(img, fh, JSVAL_TO_INT(argv[1]), - JSVAL_TO_INT(argv[2]), JSVAL_TO_INT(argv[3]), - JSVAL_TO_INT(argv[4]), JSVAL_TO_INT(argv[5]), img2); - - return JS_TRUE; - -err: - *rval = OBJECT_TO_JSVAL(NULL); - - return JS_FALSE; -} - -static JSBool -gdimage_m_gif_anim_add_str(JSContext *cx, JSObject *obj, uintN argc, - jsval *argv, jsval *rval) -{ - gdImagePtr img, img2; - void *data = NULL; - int size; - JSString *str; - - if (argc != 6) { - QUEUE_EXCEPTION("Wrong number of arguments"); - goto err; - } - if (!JSVAL_IS_INT(argv[0])) { - QUEUE_EXCEPTION("Argument 1 not an Integer"); - goto err; - } - if (!JSVAL_IS_INT(argv[1])) { - QUEUE_EXCEPTION("Argument 2 not an Integer"); - goto err; - } - if (!JSVAL_IS_INT(argv[2])) { - QUEUE_EXCEPTION("Argument 3 not an Integer"); - goto err; - } - if (!JSVAL_IS_INT(argv[3])) { - QUEUE_EXCEPTION("Argument 4 not an Integer"); - goto err; - } - if (!JSVAL_IS_INT(argv[4])) { - QUEUE_EXCEPTION("Argument 5 not an Integer"); - goto err; - } - if (JSVAL_IS_NULL(argv[5])) - img2 = NULL; - else { - JSObject *o; - - if (!JSVAL_IS_OBJECT(argv[5])) { - QUEUE_EXCEPTION( - "Argument 6 not Null or GDImage object"); - goto err; - } - o = JSVAL_TO_OBJECT(argv[6]); - if (!JS_InstanceOf(cx, o, &gdimage_class, NULL)) { - QUEUE_EXCEPTION( - "Argument 6 not Null or GDImage object"); - goto err; - } - - img2 = JS_GetInstancePrivate(cx, o, &gdimage_class, NULL); - assert(img2 != NULL); - } - - img = JS_GetInstancePrivate(cx, obj, &gdimage_class, NULL); - assert(img != NULL); - - if ((data = gdImageGifAnimAddPtr(img, &size, JSVAL_TO_INT(argv[0]), - JSVAL_TO_INT(argv[1]), JSVAL_TO_INT(argv[2]), JSVAL_TO_INT(argv[3]), - JSVAL_TO_INT(argv[4]), img2)) == NULL) { - QUEUE_EXCEPTION("gdImageGifAnimAddPtr()"); - goto err; - } - - if ((str = JS_NewStringCopyN(cx, data, size)) == NULL) { - QUEUE_EXCEPTION("Out of memory!"); - goto err; - } - *rval = STRING_TO_JSVAL(str); - gdFree(data); - - return JS_TRUE; - -err: - *rval = OBJECT_TO_JSVAL(NULL); - if (data != NULL) - gdFree(data); - - return JS_FALSE; -} - -/* - * For consistency, GD authors should have made these two still require the - * gdImagePtr argument. However, they don't... so we had to rewrite these. - * We however still present a consistent API and attach this method to the - * GDImage object. - */ -static JSBool -gdimage_m_gif_anim_end(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - FILE *fh; - - *rval = OBJECT_TO_JSVAL(NULL); - - if (argc != 1) { - QUEUE_EXCEPTION("Wrong number of arguments"); - return JS_FALSE; - } - if ((fh = file_fh(cx, argv[0])) == NULL) { - QUEUE_EXCEPTION("Argument 1 not a File object"); - return JS_FALSE; - } - - gdImageGifAnimEnd(fh); - - return JS_TRUE; -} - -static JSBool -gdimage_m_gif_anim_end_str(JSContext *cx, JSObject *obj, uintN argc, - jsval *argv, jsval *rval) -{ - void *data = NULL; - int size; - JSString *str; - - if (argc != 0) { - QUEUE_EXCEPTION("Wrong number of arguments"); - goto err; - } - - if ((data = gdImageGifAnimEndPtr(&size)) == NULL) { - QUEUE_EXCEPTION("gdImageGifAnimEndPtr()"); - goto err; - } - - if ((str = JS_NewStringCopyN(cx, data, size)) == NULL) { - QUEUE_EXCEPTION("Out of memory!"); - goto err; - } - *rval = STRING_TO_JSVAL(str); - gdFree(data); - - return JS_TRUE; - -err: - *rval = OBJECT_TO_JSVAL(NULL); - if (data != NULL) - gdFree(data); - - return JS_FALSE; -} - -static JSBool -gdimage_m_png(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - - return gdimage_m_save_file_i0(cx, obj, argc, argv, rval, gdImagePng); -} - -static JSBool -gdimage_m_png_str(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - - return gdimage_m_save_str_i0(cx, obj, argc, argv, rval, gdImagePngPtr, - "gdImagePngPtr()"); -} - -static JSBool -gdimage_m_png_ex(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - - return gdimage_m_save_file_i1(cx, obj, argc, argv, rval, gdImagePngEx); -} - -static JSBool -gdimage_m_png_ex_str(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - - return gdimage_m_save_str_i1(cx, obj, argc, argv, rval, - gdImagePngPtrEx, "gdImagePngPtrEx()"); -} - -/* - * GD is inconsistent and gdImageWBMP() takes int argument before the FILE *. - * We however make it consistent in this API. - */ -static JSBool -gdimage_m_wbmp(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - FILE *fh; - gdImagePtr img; - - *rval = OBJECT_TO_JSVAL(NULL); - - if (argc != 2) { - QUEUE_EXCEPTION("Wrong number of arguments"); - return JS_FALSE; - } - if ((fh = file_fh(cx, argv[0])) == NULL) { - QUEUE_EXCEPTION("Argument 1 not a File object"); - return JS_FALSE; - } - if (!JSVAL_IS_INT(argv[1])) { - QUEUE_EXCEPTION("Argument 1 not an Integer"); - return JS_FALSE; - } - - img = JS_GetInstancePrivate(cx, obj, &gdimage_class, NULL); - assert(img != NULL); - - gdImageWBMP(img, JSVAL_TO_INT(argv[1]), fh); - - return JS_TRUE; -} - -/* - * GD documentation states that gdImageWBMPPtr() does't take an int, but it - * does after the FILE *, like gdImageWBMP() but with revesed arguments order. - * This function seemed consistent, at least. - */ -static JSBool -gdimage_m_wbmp_str(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - - return gdimage_m_save_str_i1(cx, obj, argc, argv, rval, gdImageWBMPPtr, - "gdImageWBMPPtr()"); -} - -static JSBool -gdimage_m_gd(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - - return gdimage_m_save_file_i0(cx, obj, argc, argv, rval, gdImageGd); -} - -static JSBool -gdimage_m_gd_str(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - - return gdimage_m_save_str_i0(cx, obj, argc, argv, rval, gdImageGdPtr, - "gdImageGdPtr()"); -} - -static JSBool -gdimage_m_gd2(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - FILE *fh; - gdImagePtr img; - - if (argc != 3) { - QUEUE_EXCEPTION("Wrong number of arguments"); - goto err; - } - if ((fh = file_fh(cx, argv[0])) == NULL) { - QUEUE_EXCEPTION("Argument 1 not a File object"); - goto err; - } - if (!JSVAL_IS_INT(argv[1])) { - QUEUE_EXCEPTION("Argument 2 not an Integer"); - goto err; - } - if (!JSVAL_IS_INT(argv[2])) { - QUEUE_EXCEPTION("Argument 3 not an Integer"); - goto err; - } - - img = JS_GetInstancePrivate(cx, obj, &gdimage_class, NULL); - assert(img != NULL); - - gdImageGd2(img, fh, JSVAL_TO_INT(argv[1]), JSVAL_TO_INT(argv[2])); - - return JS_TRUE; - -err: - *rval = OBJECT_TO_JSVAL(NULL); - - return JS_FALSE; -} - -static JSBool -gdimage_m_gd2_str(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - gdImagePtr img; - void *data = NULL; - int size; - JSString *str; - - if (argc != 2) { - QUEUE_EXCEPTION("Wrong number of arguments"); - goto err; - } - if (!JSVAL_IS_INT(argv[0])) { - QUEUE_EXCEPTION("Argument 1 not an Integer"); - goto err; - } - if (!JSVAL_IS_INT(argv[1])) { - QUEUE_EXCEPTION("Argument 2 not an Integer"); - goto err; - } - - img = JS_GetInstancePrivate(cx, obj, &gdimage_class, NULL); - assert(img != NULL); - - /* - * Also an inconsistent GD function as it requires the int pointer - * after the other integer arguments. - */ - if ((data = gdImageGd2Ptr(img, JSVAL_TO_INT(argv[0]), - JSVAL_TO_INT(argv[1]), &size)) == NULL) { - QUEUE_EXCEPTION("gdImageGd2Ptr()"); - goto err; - } - - if ((str = JS_NewStringCopyN(cx, data, size)) == NULL) { - QUEUE_EXCEPTION("Out of memory!"); - goto err; - } - *rval = STRING_TO_JSVAL(str); - gdFree(data); - - return JS_TRUE; - -err: - *rval = OBJECT_TO_JSVAL(NULL); - if (data != NULL) - gdFree(data); - - return JS_FALSE; -} - -static JSBool -gdimage_m_truecolor_to_palette(JSContext *cx, JSObject *obj, uintN argc, - jsval *argv, jsval *rval) -{ - gdImagePtr img; - - *rval = OBJECT_TO_JSVAL(NULL); - - if (argc != 2) { - QUEUE_EXCEPTION("Wrong number of arguments"); - return JS_FALSE; - } - if (!JSVAL_IS_INT(argv[0])) { - QUEUE_EXCEPTION("Argument 1 not an Integer"); - return JS_FALSE; - } - if (!JSVAL_IS_INT(argv[1])) { - QUEUE_EXCEPTION("Argument 2 not an Integer"); - return JS_FALSE; - } - - img = JS_GetInstancePrivate(cx, obj, &gdimage_class, NULL); - assert(img != NULL); - - gdImageTrueColorToPalette(img, JSVAL_TO_INT(argv[0]), - JSVAL_TO_INT(argv[1])); - - return JS_TRUE; -} - -static JSBool -gdimage_m_create_palette_from_truecolor(JSContext *cx, JSObject *obj, - uintN argc, jsval *argv, jsval *rval) -{ - gdImagePtr img, img2 = NULL; - JSObject *o; - - if (argc != 2) { - QUEUE_EXCEPTION("Wrong number of arguments"); - goto err; - } - if (!JSVAL_IS_INT(argv[0])) { - QUEUE_EXCEPTION("Argument 1 not an Integer"); - goto err; - } - if (!JSVAL_IS_INT(argv[1])) { - QUEUE_EXCEPTION("Argument 2 not an Integer"); - goto err; - } - - img = JS_GetInstancePrivate(cx, obj, &gdimage_class, NULL); - assert(img != NULL); - - if ((img2 = gdImageCreatePaletteFromTrueColor(img, - JSVAL_TO_INT(argv[0]), JSVAL_TO_INT(argv[1]))) == NULL) { - QUEUE_EXCEPTION("gdImageCreatePaletteFromTrueColor()"); - goto err; - } - - if ((o = JS_NewObject(cx, &gdimage_class, NULL, NULL)) == NULL) { - QUEUE_EXCEPTION("Internal error!"); - goto err; - } - *rval = OBJECT_TO_JSVAL(o); - if (!JS_SetPrivate(cx, o, img2)) { - QUEUE_EXCEPTION("Internal error!"); - goto err; - } - - return JS_TRUE; - -err: - *rval = OBJECT_TO_JSVAL(NULL); - if (img2 != NULL) - gdFree(img2); - - return JS_FALSE; -} - - -static JSBool -gdimage_m_set_pixel(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - gdImagePtr img; - - *rval = OBJECT_TO_JSVAL(NULL); - - if (argc != 3) { - QUEUE_EXCEPTION("Wrong number of arguments"); - return JS_FALSE; - } - if (!JSVAL_IS_INT(argv[0])) { - QUEUE_EXCEPTION("Argument 1 not an Integer"); - return JS_FALSE; - } - if (!JSVAL_IS_INT(argv[1])) { - QUEUE_EXCEPTION("Argument 2 not an Integer"); - return JS_FALSE; - } - if (!JSVAL_IS_INT(argv[2])) { - QUEUE_EXCEPTION("Argument 3 not an Integer"); - return JS_FALSE; - } - - img = JS_GetInstancePrivate(cx, obj, &gdimage_class, NULL); - assert(img != NULL); - - gdImageSetPixel(img, JSVAL_TO_INT(argv[0]), JSVAL_TO_INT(argv[1]), - JSVAL_TO_INT(argv[2])); - - return JS_TRUE; -} - -static JSBool -gdimage_m_primitive5_i(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval, void (*funcptr)(gdImagePtr, int, int, int, int, int)) -{ - gdImagePtr img; - - *rval = OBJECT_TO_JSVAL(NULL); - - if (argc != 5) { - QUEUE_EXCEPTION("Wrong number of arguments"); - return JS_FALSE; - } - if (!JSVAL_IS_INT(argv[0])) { - QUEUE_EXCEPTION("Argument 1 not an Integer"); - return JS_FALSE; - } - if (!JSVAL_IS_INT(argv[1])) { - QUEUE_EXCEPTION("Argument 2 not an Integer"); - return JS_FALSE; - } - if (!JSVAL_IS_INT(argv[2])) { - QUEUE_EXCEPTION("Argument 3 not an Integer"); - return JS_FALSE; - } - if (!JSVAL_IS_INT(argv[3])) { - QUEUE_EXCEPTION("Argument 4 not an Integer"); - return JS_FALSE; - } - if (!JSVAL_IS_INT(argv[4])) { - QUEUE_EXCEPTION("Argument 5 not an Integer"); - return JS_FALSE; - } - - img = JS_GetInstancePrivate(cx, obj, &gdimage_class, NULL); - assert(img != NULL); - - funcptr(img, JSVAL_TO_INT(argv[0]), JSVAL_TO_INT(argv[1]), - JSVAL_TO_INT(argv[2]), JSVAL_TO_INT(argv[3]), - JSVAL_TO_INT(argv[4])); - - return JS_TRUE; -} - -static JSBool -gdimage_m_line(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - - return gdimage_m_primitive5_i(cx, obj, argc, argv, rval, gdImageLine); -} - -static JSBool -gdimage_m_dashed_line(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - - return gdimage_m_primitive5_i(cx, obj, argc, argv, rval, - gdImageDashedLine); -} - -/* - * Converts a JS array of integer pairs to a C gdPoint array, which must be - * freed by the caller on success. On failure, we return NULL and - * automatically send an appropriate exception. On success, we return the - * gdPoint array pointer and store the number of verticles stored in it via - * the provided size pointer. - */ -static gdPointPtr -array_to_gdpoints_i(JSContext *cx, jsval a, int *size) -{ - JSObject *o; - jsuint len; - JSIdArray *ida = NULL; - gdPointPtr pts = NULL; - int i, i2, i3; - jsval id, val; - - if (!JSVAL_IS_OBJECT(a) || - !JS_IsArrayObject(cx, (o = JSVAL_TO_OBJECT(a)))) { - QUEUE_EXCEPTION("Argument not an Array object"); - goto err; - } - if (!JS_GetArrayLength(cx, o, &len) || ((len & 1) != 0)) { - QUEUE_EXCEPTION("Array not holding even pairs"); - goto err; - } - - len /= 2; - if ((pts = malloc(sizeof(gdPoint) * len)) == NULL) { - QUEUE_EXCEPTION("Out of memory!"); - goto err; - } - - if ((ida = JS_Enumerate(cx, o)) == NULL) { - QUEUE_EXCEPTION("Internal error!"); - goto err; - } - for (i3 = i2 = i = 0; i < ida->length; i++) { - JS_IdToValue(cx, ida->vector[i], &id); - if (!JS_LookupElement(cx, o, JSVAL_TO_INT(id), &val) || - JSVAL_IS_VOID(val)) - continue; - if (!JSVAL_IS_INT(val)) { - QUEUE_EXCEPTION("Array elements not all Integers"); - goto err; - } - if ((i2++ & 1) == 0) - pts[i3].x = JSVAL_TO_INT(val); - else - pts[i3++].y = JSVAL_TO_INT(val); - } - JS_DestroyIdArray(cx, ida); - ida = NULL; - - *size = len; - return pts; - -err: - if (ida != NULL) - JS_DestroyIdArray(cx, ida); - if (pts != NULL) - free(pts); - *size = 0; - - return NULL; -} - -/* - * Converts a JS array of integer pairs to a C int array, which must be - * freed by the caller on success. On failure, we return NULL and - * automatically send an appropriate exception. On success, we return the - * int array pointer and store the number of entries stored in it via - * the provided size pointer. - */ -static int * -array_to_ints_i(JSContext *cx, jsval a, int *size) -{ - JSObject *o; - jsuint len; - JSIdArray *ida = NULL; - int *pts = NULL; - int i, i2; - jsval id, val; - - if (!JSVAL_IS_OBJECT(a) || - !JS_IsArrayObject(cx, (o = JSVAL_TO_OBJECT(a)))) { - QUEUE_EXCEPTION("Argument not an Array object"); - goto err; - } - if (!JS_GetArrayLength(cx, o, &len)) { - QUEUE_EXCEPTION("Internal error!"); - goto err; - } - - if ((pts = malloc(sizeof(int) * len)) == NULL) { - QUEUE_EXCEPTION("Out of memory!"); - goto err; - } - - if ((ida = JS_Enumerate(cx, o)) == NULL) { - QUEUE_EXCEPTION("Internal error!"); - goto err; - } - for (i2 = i = 0; i < ida->length; i++) { - JS_IdToValue(cx, ida->vector[i], &id); - if (!JS_LookupElement(cx, o, JSVAL_TO_INT(id), &val) || - JSVAL_IS_VOID(val)) - continue; - if (!JSVAL_IS_INT(val)) { - QUEUE_EXCEPTION("Array elements not all Integers"); - goto err; - } - pts[i2++] = JSVAL_TO_INT(val); - } - JS_DestroyIdArray(cx, ida); - ida = NULL; - - *size = len; - return pts; - -err: - if (ida != NULL) - JS_DestroyIdArray(cx, ida); - if (pts != NULL) - free(pts); - *size = 0; - - return NULL; -} - -static JSBool -gdimage_m_polygon_i(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval, void (*funcptr)(gdImagePtr, gdPointPtr, int, int)) -{ - gdImagePtr img; - gdPointPtr pts = NULL; - int size; - - *rval = OBJECT_TO_JSVAL(NULL); - - if (argc != 2) { - QUEUE_EXCEPTION("Wrong number of arguments"); - goto err; - } - if ((pts = array_to_gdpoints_i(cx, argv[0], &size)) == NULL) - goto err; - if (!JSVAL_IS_INT(argv[1])) { - QUEUE_EXCEPTION("Argument 2 not an Integer"); - goto err; - } - - img = JS_GetInstancePrivate(cx, obj, &gdimage_class, NULL); - assert(img != NULL); - - funcptr(img, pts, size, JSVAL_TO_INT(argv[1])); - free(pts); - - return JS_TRUE; - -err: - if (pts != NULL) - free(pts); - - return JS_FALSE; -} - -static JSBool -gdimage_m_polygon(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - - return gdimage_m_polygon_i(cx, obj, argc, argv, rval, gdImagePolygon); -} - -static JSBool -gdimage_m_open_polygon(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - - return gdimage_m_polygon_i(cx, obj, argc, argv, rval, - gdImageOpenPolygon); -} - -static JSBool -gdimage_m_rectangle(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - - return gdimage_m_primitive5_i(cx, obj, argc, argv, rval, - gdImageRectangle); -} - -static JSBool -gdimage_m_filled_polygon(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - - return gdimage_m_polygon_i(cx, obj, argc, argv, rval, - gdImageFilledPolygon); -} - -static JSBool -gdimage_m_filled_rectangle(JSContext *cx, JSObject *obj, uintN argc, - jsval *argv, jsval *rval) -{ - - return gdimage_m_primitive5_i(cx, obj, argc, argv, rval, - gdImageFilledRectangle); -} - -static JSBool -gdimage_m_arc(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - gdImagePtr img; - int i; - - *rval = OBJECT_TO_JSVAL(NULL); - - if (argc != 7) { - QUEUE_EXCEPTION("Wrong number of arguments"); - return JS_FALSE; - } - for (i = 0; i < 7; i++) { - if (!JSVAL_IS_INT(argv[i])) { - QUEUE_EXCEPTION("Argument not an Integer"); - return JS_FALSE; - } - } - - img = JS_GetInstancePrivate(cx, obj, &gdimage_class, NULL); - assert(img != NULL); - - gdImageArc(img, JSVAL_TO_INT(argv[0]), JSVAL_TO_INT(argv[1]), - JSVAL_TO_INT(argv[2]), JSVAL_TO_INT(argv[3]), - JSVAL_TO_INT(argv[4]), JSVAL_TO_INT(argv[5]), - JSVAL_TO_INT(argv[6])); - - return JS_TRUE; -} - -static JSBool -gdimage_m_filled_arc(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - gdImagePtr img; - int i; - - *rval = OBJECT_TO_JSVAL(NULL); - - if (argc != 8) { - QUEUE_EXCEPTION("Wrong number of arguments"); - return JS_FALSE; - } - for (i = 0; i < 8; i++) { - if (!JSVAL_IS_INT(argv[i])) { - QUEUE_EXCEPTION("Argument not an Integer"); - return JS_FALSE; - } - } - - img = JS_GetInstancePrivate(cx, obj, &gdimage_class, NULL); - assert(img != NULL); - - gdImageFilledArc(img, JSVAL_TO_INT(argv[0]), JSVAL_TO_INT(argv[1]), - JSVAL_TO_INT(argv[2]), JSVAL_TO_INT(argv[3]), - JSVAL_TO_INT(argv[4]), JSVAL_TO_INT(argv[5]), - JSVAL_TO_INT(argv[6]), JSVAL_TO_INT(argv[7])); - - return JS_TRUE; -} - -static JSBool -gdimage_m_filled_ellipse(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - - return gdimage_m_primitive5_i(cx, obj, argc, argv, rval, - gdImageFilledEllipse); -} - -static JSBool -gdimage_m_fill_to_border(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - gdImagePtr img; - - *rval = OBJECT_TO_JSVAL(NULL); - - if (argc != 4) { - QUEUE_EXCEPTION("Wrong number of arguments"); - return JS_FALSE; - } - if (!JSVAL_IS_INT(argv[0])) { - QUEUE_EXCEPTION("Argument 1 not an Integer"); - return JS_FALSE; - } - if (!JSVAL_IS_INT(argv[1])) { - QUEUE_EXCEPTION("Argument 2 not an Integer"); - return JS_FALSE; - } - if (!JSVAL_IS_INT(argv[2])) { - QUEUE_EXCEPTION("Argument 3 not an Integer"); - return JS_FALSE; - } - if (!JSVAL_IS_INT(argv[3])) { - QUEUE_EXCEPTION("Argument 4 not an Integer"); - return JS_FALSE; - } - - img = JS_GetInstancePrivate(cx, obj, &gdimage_class, NULL); - assert(img != NULL); - - gdImageFillToBorder(img, JSVAL_TO_INT(argv[0]), JSVAL_TO_INT(argv[1]), - JSVAL_TO_INT(argv[2]), JSVAL_TO_INT(argv[3])); - - return JS_TRUE; -} - -static JSBool -gdimage_m_fill(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - gdImagePtr img; - - *rval = OBJECT_TO_JSVAL(NULL); - - if (argc != 3) { - QUEUE_EXCEPTION("Wrong number of arguments"); - return JS_FALSE; - } - if (!JSVAL_IS_INT(argv[0])) { - QUEUE_EXCEPTION("Argument 1 not an Integer"); - return JS_FALSE; - } - if (!JSVAL_IS_INT(argv[1])) { - QUEUE_EXCEPTION("Argument 2 not an Integer"); - return JS_FALSE; - } - if (!JSVAL_IS_INT(argv[2])) { - QUEUE_EXCEPTION("Argument 3 not an Integer"); - return JS_FALSE; - } - - img = JS_GetInstancePrivate(cx, obj, &gdimage_class, NULL); - assert(img != NULL); - - gdImageFill(img, JSVAL_TO_INT(argv[0]), JSVAL_TO_INT(argv[1]), - JSVAL_TO_INT(argv[2])); - - return JS_TRUE; -} - -static JSBool -gdimage_m_set_antialiased(JSContext *cx, JSObject *obj, uintN argc, - jsval *argv, jsval *rval) -{ - gdImagePtr img; - - *rval = OBJECT_TO_JSVAL(NULL); - - if (argc != 1) { - QUEUE_EXCEPTION("Wrong number of arguments"); - return JS_FALSE; - } - if (!JSVAL_IS_INT(argv[0])) { - QUEUE_EXCEPTION("Argument 1 not an Integer"); - return JS_FALSE; - } - - img = JS_GetInstancePrivate(cx, obj, &gdimage_class, NULL); - assert(img != NULL); - - gdImageSetAntiAliased(img, JSVAL_TO_INT(argv[0])); - - return JS_TRUE; -} - -/* - * Although documentation specifies that this gdImageSetAntialiasedDontBlend() - * requires two parameters, it actually requires three. - */ -static JSBool -gdimage_m_set_antialiaseddontblend(JSContext *cx, JSObject *obj, uintN argc, - jsval *argv, jsval *rval) -{ - gdImagePtr img; - - *rval = OBJECT_TO_JSVAL(NULL); - - if (argc != 2) { - QUEUE_EXCEPTION("Wrong number of arguments"); - return JS_FALSE; - } - if (!JSVAL_IS_INT(argv[0])) { - QUEUE_EXCEPTION("Argument 1 not an Integer"); - return JS_FALSE; - } - if (!JSVAL_IS_INT(argv[1])) { - QUEUE_EXCEPTION("Argument 2 not an Integer"); - return JS_FALSE; - } - - img = JS_GetInstancePrivate(cx, obj, &gdimage_class, NULL); - assert(img != NULL); - - gdImageSetAntiAliasedDontBlend(img, JSVAL_TO_INT(argv[0]), - JSVAL_TO_INT(argv[1])); - - return JS_TRUE; -} - -static JSBool -gdimage_m_set_brush(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - gdImagePtr img, img2; - JSObject *o; - - *rval = OBJECT_TO_JSVAL(NULL); - - if (argc != 1) { - QUEUE_EXCEPTION("Wrong number of arguments"); - return JS_FALSE; - } - if (!JSVAL_IS_OBJECT(argv[0])) { - QUEUE_EXCEPTION("Argument 1 not a GDImage object"); - return JS_FALSE; - } - o = JSVAL_TO_OBJECT(argv[0]); - if (!JS_InstanceOf(cx, o, &gdimage_class, NULL)) { - QUEUE_EXCEPTION("Argument 1 not a GDImage object"); - return JS_FALSE; - } - - img = JS_GetInstancePrivate(cx, obj, &gdimage_class, NULL); - assert(img != NULL); - img2 = JS_GetInstancePrivate(cx, o, &gdimage_class, NULL); - assert(img2 != NULL); - - /* - * Delete any previously associated GDImage property and reassign the - * new one so that the JS GC knows the dependency. - */ - (void) JS_DeleteProperty(cx, obj, "gdimage_brush"); - if (!JS_DefineProperty(cx, obj, "gdimage_brush", argv[0], NULL, - NULL, 0)) { - QUEUE_EXCEPTION("Internal error!"); - return JS_FALSE; - } - - gdImageSetBrush(img, img2); - - return JS_TRUE; -} - -static JSBool -gdimage_m_set_tile(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - gdImagePtr img, img2; - JSObject *o; - - *rval = OBJECT_TO_JSVAL(NULL); - - if (argc != 1) { - QUEUE_EXCEPTION("Wrong number of arguments"); - return JS_FALSE; - } - if (!JSVAL_IS_OBJECT(argv[0])) { - QUEUE_EXCEPTION("Argument 1 not a GDImage object"); - return JS_FALSE; - } - o = JSVAL_TO_OBJECT(argv[0]); - if (!JS_InstanceOf(cx, o, &gdimage_class, NULL)) { - QUEUE_EXCEPTION("Argument 1 not a GDImage object"); - return JS_FALSE; - } - - img = JS_GetInstancePrivate(cx, obj, &gdimage_class, NULL); - assert(img != NULL); - img2 = JS_GetInstancePrivate(cx, o, &gdimage_class, NULL); - assert(img2 != NULL); - - /* - * Delete any previously associated GDImage property and reassign the - * new one so that the JS GC knows the dependency. - */ - (void) JS_DeleteProperty(cx, obj, "gdimage_tile"); - if (!JS_DefineProperty(cx, obj, "gdimage_tile", argv[0], NULL, NULL, - 0)) { - QUEUE_EXCEPTION("Internal error!"); - return JS_FALSE; - } - - gdImageSetTile(img, img2); - - return JS_TRUE; -} - -static JSBool -gdimage_m_set_style(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - gdImagePtr img; - int *pts = NULL; - int size; - - *rval = OBJECT_TO_JSVAL(NULL); - - if (argc != 1) { - QUEUE_EXCEPTION("Wrong number of arguments"); - goto err; - } - if ((pts = array_to_ints_i(cx, argv[0], &size)) == NULL) - goto err; - - img = JS_GetInstancePrivate(cx, obj, &gdimage_class, NULL); - assert(img != NULL); - - /* The supplied array is copied as of gd v1.1.1 and up. */ - gdImageSetStyle(img, pts, size); - free(pts); - - return JS_TRUE; - -err: - if (pts != NULL) - free(pts); - - return JS_FALSE; -} - -static JSBool -gdimage_m_set_thickness(JSContext *cx, JSObject *obj, uintN argc, - jsval *argv, jsval *rval) -{ - gdImagePtr img; - - *rval = OBJECT_TO_JSVAL(NULL); - - if (argc != 1) { - QUEUE_EXCEPTION("Wrong number of arguments"); - return JS_FALSE; - } - if (!JSVAL_IS_INT(argv[0])) { - QUEUE_EXCEPTION("Argument 1 not an Integer"); - return JS_FALSE; - } - - img = JS_GetInstancePrivate(cx, obj, &gdimage_class, NULL); - assert(img != NULL); - - gdImageSetThickness(img, JSVAL_TO_INT(argv[0])); - - return JS_TRUE; -} - -static JSBool -gdimage_m_alpha_blending(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - gdImagePtr img; - - *rval = OBJECT_TO_JSVAL(NULL); - - if (argc != 1) { - QUEUE_EXCEPTION("Wrong number of arguments"); - return JS_FALSE; - } - if (!JSVAL_IS_BOOLEAN(argv[0])) { - QUEUE_EXCEPTION("Argument 1 not a Boolean"); - return JS_FALSE; - } - - img = JS_GetInstancePrivate(cx, obj, &gdimage_class, NULL); - assert(img != NULL); - - gdImageAlphaBlending(img, (int)JSVAL_TO_BOOLEAN(argv[0])); - - return JS_TRUE; -} - -static JSBool -gdimage_m_save_alpha(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - gdImagePtr img; - - *rval = OBJECT_TO_JSVAL(NULL); - - if (argc != 1) { - QUEUE_EXCEPTION("Wrong number of arguments"); - return JS_FALSE; - } - if (!JSVAL_IS_BOOLEAN(argv[0])) { - QUEUE_EXCEPTION("Argument 1 not a Boolean"); - return JS_FALSE; - } - - img = JS_GetInstancePrivate(cx, obj, &gdimage_class, NULL); - assert(img != NULL); - - gdImageSaveAlpha(img, (int)JSVAL_TO_BOOLEAN(argv[0])); - - return JS_TRUE; -} - -static JSBool -gdimage_m_set_clip(JSContext *cx, JSObject *obj, uintN argc, - jsval *argv, jsval *rval) -{ - gdImagePtr img; - - *rval = OBJECT_TO_JSVAL(NULL); - - if (argc != 4) { - QUEUE_EXCEPTION("Wrong number of arguments"); - return JS_FALSE; - } - if (!JSVAL_IS_INT(argv[0])) { - QUEUE_EXCEPTION("Argument 1 not an Integer"); - return JS_FALSE; - } - if (!JSVAL_IS_INT(argv[1])) { - QUEUE_EXCEPTION("Argument 2 not an Integer"); - return JS_FALSE; - } - if (!JSVAL_IS_INT(argv[2])) { - QUEUE_EXCEPTION("Argument 3 not an Integer"); - return JS_FALSE; - } - if (!JSVAL_IS_INT(argv[3])) { - QUEUE_EXCEPTION("Argument 4 not an Integer"); - return JS_FALSE; - } - - img = JS_GetInstancePrivate(cx, obj, &gdimage_class, NULL); - assert(img != NULL); - - gdImageSetClip(img, JSVAL_TO_INT(argv[0]), JSVAL_TO_INT(argv[1]), - JSVAL_TO_INT(argv[2]), JSVAL_TO_INT(argv[3])); - - return JS_TRUE; -} - -/* - * Unlike gdImageGetClip() which fills four ints via their supplied pointers, - * this method returns an object with x1, y1, x2, y2 properties set. - */ -static JSBool -gdimage_m_get_clip(JSContext *cx, JSObject *obj, uintN argc, - jsval *argv, jsval *rval) -{ - gdImagePtr img; - int x1, y1, x2, y2; - JSObject *o; - - if (argc != 0) { - QUEUE_EXCEPTION("Wrong number of arguments"); - goto err; - } - - img = JS_GetInstancePrivate(cx, obj, &gdimage_class, NULL); - assert(img != NULL); - - gdImageGetClip(img, &x1, &y1, &x2, &y2); - - if ((o = JS_NewObject(cx, NULL, NULL, NULL)) == NULL) { - QUEUE_EXCEPTION("Internal error!"); - goto err; - } - *rval = OBJECT_TO_JSVAL(o); - if (!JS_DefineProperty(cx, o, "x1", INT_TO_JSVAL(x1), NULL, NULL, - JSPROP_ENUMERATE) || - !JS_DefineProperty(cx, o, "y1", INT_TO_JSVAL(y1), NULL, NULL, - JSPROP_ENUMERATE) || - !JS_DefineProperty(cx, o, "x2", INT_TO_JSVAL(x2), NULL, NULL, - JSPROP_ENUMERATE) || - !JS_DefineProperty(cx, o, "y2", INT_TO_JSVAL(y2), NULL, NULL, - JSPROP_ENUMERATE)) { - QUEUE_EXCEPTION("Internal error!"); - goto err; - } - - return JS_TRUE; - -err: - *rval = OBJECT_TO_JSVAL(NULL); - - return JS_FALSE; -} - -static JSBool -gdimage_m_alpha(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - gdImagePtr img; - int v; - - if (argc != 1) { - QUEUE_EXCEPTION("Wrong number of arguments"); - goto err; - } - if (!JSVAL_IS_INT(argv[0])) { - QUEUE_EXCEPTION("Argument 1 not an Integer"); - goto err; - } - - img = JS_GetInstancePrivate(cx, obj, &gdimage_class, NULL); - assert(img != NULL); - - /* A macro, so avoid side-effects */ - v = JSVAL_TO_INT(argv[0]); - v = gdImageAlpha(img, v); - *rval = INT_TO_JSVAL(v); - - return JS_TRUE; - -err: - *rval = OBJECT_TO_JSVAL(NULL); - - return JS_FALSE; -} - -static JSBool -gdimage_m_get_pixel(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - gdImagePtr img; - int v; - - if (argc != 2) { - QUEUE_EXCEPTION("Wrong number of arguments"); - goto err; - } - if (!JSVAL_IS_INT(argv[0])) { - QUEUE_EXCEPTION("Argument 1 not an Integer"); - goto err; - } - if (!JSVAL_IS_INT(argv[1])) { - QUEUE_EXCEPTION("Argument 2 not an Integer"); - goto err; - } - - img = JS_GetInstancePrivate(cx, obj, &gdimage_class, NULL); - assert(img != NULL); - - v = gdImageGetPixel(img, JSVAL_TO_INT(argv[0]), JSVAL_TO_INT(argv[1])); - *rval = INT_TO_JSVAL(v); - - return JS_TRUE; - -err: - *rval = OBJECT_TO_JSVAL(NULL); - - return JS_FALSE; -} - -static JSBool -gdimage_m_bounds_safe(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - gdImagePtr img; - int v; - - if (argc != 2) { - QUEUE_EXCEPTION("Wrong number of arguments"); - goto err; - } - if (!JSVAL_IS_INT(argv[0])) { - QUEUE_EXCEPTION("Argument 1 not an Integer"); - goto err; - } - if (!JSVAL_IS_INT(argv[1])) { - QUEUE_EXCEPTION("Argument 2 not an Integer"); - goto err; - } - - img = JS_GetInstancePrivate(cx, obj, &gdimage_class, NULL); - assert(img != NULL); - - v = gdImageBoundsSafe(img, JSVAL_TO_INT(argv[0]), - JSVAL_TO_INT(argv[1])); - *rval = BOOLEAN_TO_JSVAL((JSBool)v); - - return JS_TRUE; - -err: - *rval = OBJECT_TO_JSVAL(NULL); - - return JS_FALSE; -} - -static JSBool -gdimage_m_image_sx(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - gdImagePtr img; - int v; - - if (argc != 0) { - QUEUE_EXCEPTION("Wrong number of arguments"); - goto err; - } - - img = JS_GetInstancePrivate(cx, obj, &gdimage_class, NULL); - assert(img != NULL); - - /* A macro, so avoid side-effects */ - v = gdImageSX(img); - *rval = INT_TO_JSVAL(v); - - return JS_TRUE; - -err: - *rval = OBJECT_TO_JSVAL(NULL); - - return JS_FALSE; -} - -static JSBool -gdimage_m_image_sy(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - gdImagePtr img; - int v; - - if (argc != 0) { - QUEUE_EXCEPTION("Wrong number of arguments"); - goto err; - } - - img = JS_GetInstancePrivate(cx, obj, &gdimage_class, NULL); - assert(img != NULL); - - /* A macro, so avoid side-effects */ - v = gdImageSY(img); - *rval = INT_TO_JSVAL(v); - - return JS_TRUE; - -err: - *rval = OBJECT_TO_JSVAL(NULL); - - return JS_FALSE; -} - - -static JSBool -gdimage_m_int_int3_i(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval, int (*funcptr)(gdImagePtr, int, int, int)) -{ - gdImagePtr img; - int col; - - if (argc != 3) { - QUEUE_EXCEPTION("Wrong number of arguments"); - goto err; - } - if (!JSVAL_IS_INT(argv[0])) { - QUEUE_EXCEPTION("Argument 1 not an Integer"); - goto err; - } - if (!JSVAL_IS_INT(argv[1])) { - QUEUE_EXCEPTION("Argument 2 not an Integer"); - goto err; - } - if (!JSVAL_IS_INT(argv[2])) { - QUEUE_EXCEPTION("Argument 3 not an Integer"); - goto err; - } - - img = JS_GetInstancePrivate(cx, obj, &gdimage_class, NULL); - assert(img != NULL); - - col = funcptr(img, JSVAL_TO_INT(argv[0]), JSVAL_TO_INT(argv[1]), - JSVAL_TO_INT(argv[2])); - *rval = INT_TO_JSVAL(col); - - return JS_TRUE; - -err: - *rval = OBJECT_TO_JSVAL(NULL); - - return JS_FALSE; -} - -static JSBool -gdimage_m_int_int4_i(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval, int (*funcptr)(gdImagePtr, int, int, int, int)) -{ - gdImagePtr img; - int col; - - if (argc != 4) { - QUEUE_EXCEPTION("Wrong number of arguments"); - goto err; - } - if (!JSVAL_IS_INT(argv[0])) { - QUEUE_EXCEPTION("Argument 1 not an Integer"); - goto err; - } - if (!JSVAL_IS_INT(argv[1])) { - QUEUE_EXCEPTION("Argument 2 not an Integer"); - goto err; - } - if (!JSVAL_IS_INT(argv[2])) { - QUEUE_EXCEPTION("Argument 3 not an Integer"); - goto err; - } - if (!JSVAL_IS_INT(argv[3])) { - QUEUE_EXCEPTION("Argument 4 not an Integer"); - goto err; - } - - img = JS_GetInstancePrivate(cx, obj, &gdimage_class, NULL); - assert(img != NULL); - - col = funcptr(img, JSVAL_TO_INT(argv[0]), JSVAL_TO_INT(argv[1]), - JSVAL_TO_INT(argv[2]), JSVAL_TO_INT(argv[3])); - *rval = INT_TO_JSVAL(col); - - return JS_TRUE; - -err: - *rval = OBJECT_TO_JSVAL(NULL); - - return JS_FALSE; -} - -static JSBool -gdimage_m_color_allocate(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - - return gdimage_m_int_int3_i(cx, obj, argc, argv, rval, - gdImageColorAllocate); -} - -static JSBool -gdimage_m_color_allocate_alpha(JSContext *cx, JSObject *obj, uintN argc, - jsval *argv, jsval *rval) -{ - - return gdimage_m_int_int4_i(cx, obj, argc, argv, rval, - gdImageColorAllocateAlpha); -} - -static JSBool -gdimage_m_color_closest(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - - return gdimage_m_int_int3_i(cx, obj, argc, argv, rval, - gdImageColorClosest); -} - -static JSBool -gdimage_m_color_closest_alpha(JSContext *cx, JSObject *obj, uintN argc, - jsval *argv, jsval *rval) -{ - - return gdimage_m_int_int4_i(cx, obj, argc, argv, rval, - gdImageColorClosestAlpha); -} - -static JSBool -gdimage_m_color_closest_hwb(JSContext *cx, JSObject *obj, uintN argc, - jsval *argv, jsval *rval) -{ - - return gdimage_m_int_int3_i(cx, obj, argc, argv, rval, - gdImageColorClosestHWB); -} - -static JSBool -gdimage_m_color_exact(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - - return gdimage_m_int_int3_i(cx, obj, argc, argv, rval, - gdImageColorExact); -} - -static JSBool -gdimage_m_color_resolve(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - - return gdimage_m_int_int3_i(cx, obj, argc, argv, rval, - gdImageColorResolve); -} - -static JSBool -gdimage_m_color_resolve_alpha(JSContext *cx, JSObject *obj, uintN argc, - jsval *argv, jsval *rval) -{ - - return gdimage_m_int_int4_i(cx, obj, argc, argv, rval, - gdImageColorResolveAlpha); -} - -static JSBool -gdimage_m_colors_total(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - gdImagePtr img; - int v; - - if (argc != 0) { - QUEUE_EXCEPTION("Wrong number of arguments"); - goto err; - } - - img = JS_GetInstancePrivate(cx, obj, &gdimage_class, NULL); - assert(img != NULL); - - /* A macro, so avoid side-effects */ - v = gdImageColorsTotal(img); - *rval = INT_TO_JSVAL(v); - - return JS_TRUE; - -err: - *rval = OBJECT_TO_JSVAL(NULL); - - return JS_FALSE; -} - -static JSBool -gdimage_m_red(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - gdImagePtr img; - int v; - - if (argc != 1) { - QUEUE_EXCEPTION("Wrong number of arguments"); - goto err; - } - if (!JSVAL_IS_INT(argv[0])) { - QUEUE_EXCEPTION("Argument 1 not an Integer"); - goto err; - } - - img = JS_GetInstancePrivate(cx, obj, &gdimage_class, NULL); - assert(img != NULL); - - /* A macro, so avoid side-effects */ - v = JSVAL_TO_INT(argv[0]); - v = gdImageRed(img, v); - *rval = INT_TO_JSVAL(v); - - return JS_TRUE; - -err: - *rval = OBJECT_TO_JSVAL(NULL); - - return JS_FALSE; -} - -static JSBool -gdimage_m_green(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - gdImagePtr img; - int v; - - if (argc != 1) { - QUEUE_EXCEPTION("Wrong number of arguments"); - goto err; - } - if (!JSVAL_IS_INT(argv[0])) { - QUEUE_EXCEPTION("Argument 1 not an Integer"); - goto err; - } - - img = JS_GetInstancePrivate(cx, obj, &gdimage_class, NULL); - assert(img != NULL); - - /* A macro, so avoid side-effects */ - v = JSVAL_TO_INT(argv[0]); - v = gdImageGreen(img, v); - *rval = INT_TO_JSVAL(v); - - return JS_TRUE; - -err: - *rval = OBJECT_TO_JSVAL(NULL); - - return JS_FALSE; -} - -static JSBool -gdimage_m_blue(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - gdImagePtr img; - int v; - - if (argc != 1) { - QUEUE_EXCEPTION("Wrong number of arguments"); - goto err; - } - if (!JSVAL_IS_INT(argv[0])) { - QUEUE_EXCEPTION("Argument 1 not an Integer"); - goto err; - } - - img = JS_GetInstancePrivate(cx, obj, &gdimage_class, NULL); - assert(img != NULL); - - /* A macro, so avoid side-effects */ - v = JSVAL_TO_INT(argv[0]); - v = gdImageBlue(img, v); - *rval = INT_TO_JSVAL(v); - - return JS_TRUE; - -err: - *rval = OBJECT_TO_JSVAL(NULL); - - return JS_FALSE; -} - -static JSBool -gdimage_m_get_interlaced(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - gdImagePtr img; - int v; - - if (argc != 0) { - QUEUE_EXCEPTION("Wrong number of arguments"); - goto err; - } - - img = JS_GetInstancePrivate(cx, obj, &gdimage_class, NULL); - assert(img != NULL); - - /* A macro, so avoid side-effects */ - v = gdImageGetInterlaced(img); - *rval = BOOLEAN_TO_JSVAL((JSBool)v); - - return JS_TRUE; - -err: - *rval = OBJECT_TO_JSVAL(NULL); - - return JS_FALSE; -} - -static JSBool -gdimage_m_get_transparent(JSContext *cx, JSObject *obj, uintN argc, - jsval *argv, jsval *rval) -{ - gdImagePtr img; - int v; - - if (argc != 0) { - QUEUE_EXCEPTION("Wrong number of arguments"); - goto err; - } - - img = JS_GetInstancePrivate(cx, obj, &gdimage_class, NULL); - assert(img != NULL); - - /* A macro, so avoid side-effects */ - v = gdImageGetTransparent(img); - *rval = BOOLEAN_TO_JSVAL((JSBool)v); - - return JS_TRUE; - -err: - *rval = OBJECT_TO_JSVAL(NULL); - - return JS_FALSE; -} - -static JSBool -gdimage_m_color_deallocate(JSContext *cx, JSObject *obj, uintN argc, - jsval *argv, jsval *rval) -{ - gdImagePtr img; - - *rval = OBJECT_TO_JSVAL(NULL); - - if (argc != 1) { - QUEUE_EXCEPTION("Wrong number of arguments"); - return JS_FALSE; - } - if (!JSVAL_IS_INT(argv[0])) { - QUEUE_EXCEPTION("Argument 1 not an Integer"); - return JS_FALSE; - } - - img = JS_GetInstancePrivate(cx, obj, &gdimage_class, NULL); - assert(img != NULL); - - gdImageColorDeallocate(img, JSVAL_TO_INT(argv[0])); - - return JS_TRUE; -} - -static JSBool -gdimage_m_color_transparent(JSContext *cx, JSObject *obj, uintN argc, - jsval *argv, jsval *rval) -{ - gdImagePtr img; - - *rval = OBJECT_TO_JSVAL(NULL); - - if (argc != 1) { - QUEUE_EXCEPTION("Wrong number of arguments"); - return JS_FALSE; - } - if (!JSVAL_IS_INT(argv[0])) { - QUEUE_EXCEPTION("Argument 1 not an Integer"); - return JS_FALSE; - } - - img = JS_GetInstancePrivate(cx, obj, &gdimage_class, NULL); - assert(img != NULL); - - gdImageColorTransparent(img, JSVAL_TO_INT(argv[0])); - - return JS_TRUE; -} - - -static JSBool -gdimage_m_char_i(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval, void (*funcptr)(gdImagePtr, gdFontPtr, int, int, int, int)) -{ - gdImagePtr img; - gdFontPtr f; - - *rval = OBJECT_TO_JSVAL(NULL); - - if (argc != 5) { - QUEUE_EXCEPTION("Wrong number of arguments"); - return JS_FALSE; - } - if ((f = gdfont_get(cx, argv[0])) == NULL) { - QUEUE_EXCEPTION("Argument 1 not a GDFont object"); - return JS_FALSE; - } - if (!JSVAL_IS_INT(argv[1])) { - QUEUE_EXCEPTION("Argument 2 not an Integer"); - return JS_FALSE; - } - if (!JSVAL_IS_INT(argv[2])) { - QUEUE_EXCEPTION("Argument 3 not an Integer"); - return JS_FALSE; - } - if (!JSVAL_IS_INT(argv[3])) { - QUEUE_EXCEPTION("Argument 4 not an Integer"); - return JS_FALSE; - } - if (!JSVAL_IS_INT(argv[4])) { - QUEUE_EXCEPTION("Argument 5 not an Integer"); - return JS_FALSE; - } - - img = JS_GetInstancePrivate(cx, obj, &gdimage_class, NULL); - assert(img != NULL); - - funcptr(img, f, JSVAL_TO_INT(argv[1]), JSVAL_TO_INT(argv[2]), - JSVAL_TO_INT(argv[3]), JSVAL_TO_INT(argv[4])); - - return JS_TRUE; -} - -static JSBool -gdimage_m_string_i(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval, - void (*funcptr)(gdImagePtr, gdFontPtr, int, int, unsigned char *, int)) -{ - gdImagePtr img; - gdFontPtr f; - char *bytes; - - *rval = OBJECT_TO_JSVAL(NULL); - - if (argc != 5) { - QUEUE_EXCEPTION("Wrong number of arguments"); - return JS_FALSE; - } - if ((f = gdfont_get(cx, argv[0])) == NULL) { - QUEUE_EXCEPTION("Argument 1 not a GDFont object"); - return JS_FALSE; - } - if (!JSVAL_IS_INT(argv[1])) { - QUEUE_EXCEPTION("Argument 2 not an Integer"); - return JS_FALSE; - } - if (!JSVAL_IS_INT(argv[2])) { - QUEUE_EXCEPTION("Argument 3 not an Integer"); - return JS_FALSE; - } - if (!JSVAL_IS_STRING(argv[3]) || - (bytes = JS_GetStringBytes(JSVAL_TO_STRING(argv[3]))) == NULL) { - QUEUE_EXCEPTION("Argument 4 not a String"); - return JS_FALSE; - } - if (!JSVAL_IS_INT(argv[4])) { - QUEUE_EXCEPTION("Argument 5 not an Integer"); - return JS_FALSE; - } - - img = JS_GetInstancePrivate(cx, obj, &gdimage_class, NULL); - assert(img != NULL); - - funcptr(img, f, JSVAL_TO_INT(argv[1]), JSVAL_TO_INT(argv[2]), bytes, - JSVAL_TO_INT(argv[4])); - - return JS_TRUE; -} - -static JSBool -gdimage_m_char(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - - return gdimage_m_char_i(cx, obj, argc, argv, rval, gdImageChar); -} - -static JSBool -gdimage_m_char_up(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - - return gdimage_m_char_i(cx, obj, argc, argv, rval, gdImageCharUp); -} - -static JSBool -gdimage_m_string(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - - return gdimage_m_string_i(cx, obj, argc, argv, rval, gdImageString); -} - -static JSBool -gdimage_m_string_up(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - - return gdimage_m_string_i(cx, obj, argc, argv, rval, gdImageStringUp); -} - -static JSBool -gdimage_m_string_ft(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - gdImagePtr img; - char *font, *string, *error; - jsdouble ptsize, angle; - int brect[8], i; - jsval rect[8]; - JSObject *a; - - if (argc != 7) { - QUEUE_EXCEPTION("Wrong number of arguments"); - goto err; - } - if (!JSVAL_IS_INT(argv[0])) { - QUEUE_EXCEPTION("Argument 1 not an Integer"); - goto err; - } - if (!JSVAL_IS_STRING(argv[1]) || - (font = JS_GetStringBytes(JSVAL_TO_STRING(argv[1]))) == NULL) { - QUEUE_EXCEPTION("Argument 2 not a String"); - goto err; - } - if (!JSVAL_IS_NUMBER(argv[2]) || - !JS_ValueToNumber(cx, argv[2], &ptsize)) { - QUEUE_EXCEPTION("Argument 3 not a Number"); - goto err; - } - if (!JSVAL_IS_NUMBER(argv[3]) || - !JS_ValueToNumber(cx, argv[3], &angle)) { - QUEUE_EXCEPTION("Argument 4 not a Number"); - goto err; - } - if (!JSVAL_IS_INT(argv[4])) { - QUEUE_EXCEPTION("Argument 5 not an Integer"); - goto err; - } - if (!JSVAL_IS_INT(argv[5])) { - QUEUE_EXCEPTION("Argument 6 not an Integer"); - goto err; - } - if (!JSVAL_IS_STRING(argv[6]) || - (string = JS_GetStringBytes(JSVAL_TO_STRING(argv[6]))) == NULL) { - QUEUE_EXCEPTION("Argument 7 not a String"); - goto err; - } - - img = JS_GetInstancePrivate(cx, obj, &gdimage_class, NULL); - assert(img != NULL); - - if ((error = gdImageStringFT(img, brect, JSVAL_TO_INT(argv[0]), font, - (double)ptsize, (double)angle, JSVAL_TO_INT(argv[4]), - JSVAL_TO_INT(argv[5]), string)) != NULL) { - QUEUE_EXCEPTION(error); - goto err; - } - - for (i = 0; i < 8; i++) - rect[i] = INT_TO_JSVAL(brect[i]); - if ((a = JS_NewArrayObject(cx, 8, rect)) == NULL) { - QUEUE_EXCEPTION("Internal error!"); - goto err; - } - *rval = OBJECT_TO_JSVAL(a); - - return JS_TRUE; - -err: - *rval = OBJECT_TO_JSVAL(NULL); - - return JS_FALSE; -} - -static JSBool -gdimage_m_string_ft_ex(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - gdImagePtr img; - char *font, *string, *error; - jsdouble ptsize, angle; - int brect[8], i; - jsval rect[8]; - JSObject *o, *a; - JSString *str; - gdFTStringExtra ex; - - ex.flags = gdFTEX_XSHOW | gdFTEX_RETURNFONTPATHNAME | - gdFTEX_RESOLUTION; - ex.hdpi = ex.vdpi = 96; - ex.xshow = ex.fontpath = NULL; - - if (argc != 8) { - QUEUE_EXCEPTION("Wrong number of arguments"); - goto err; - } - if (!JSVAL_IS_INT(argv[0])) { - QUEUE_EXCEPTION("Argument 1 not an Integer"); - goto err; - } - if (!JSVAL_IS_STRING(argv[1]) || - (font = JS_GetStringBytes(JSVAL_TO_STRING(argv[1]))) == NULL) { - QUEUE_EXCEPTION("Argument 2 not a String"); - goto err; - } - if (!JSVAL_IS_NUMBER(argv[2]) || - !JS_ValueToNumber(cx, argv[2], &ptsize)) { - QUEUE_EXCEPTION("Argument 3 not a Number"); - goto err; - } - if (!JSVAL_IS_NUMBER(argv[3]) || - !JS_ValueToNumber(cx, argv[3], &angle)) { - QUEUE_EXCEPTION("Argument 4 not a Number"); - goto err; - } - if (!JSVAL_IS_INT(argv[4])) { - QUEUE_EXCEPTION("Argument 5 not an Integer"); - goto err; - } - if (!JSVAL_IS_INT(argv[5])) { - QUEUE_EXCEPTION("Argument 6 not an Integer"); - goto err; - } - if (!JSVAL_IS_STRING(argv[6]) || - (string = JS_GetStringBytes(JSVAL_TO_STRING(argv[6]))) == NULL) { - QUEUE_EXCEPTION("Argument 7 not a String"); - goto err; - } - if (!JSVAL_IS_NULL(argv[7]) && !JSVAL_IS_OBJECT(argv[7])) { - QUEUE_EXCEPTION("Argument 8 not Null or Object"); - goto err; - } - - if (!JSVAL_IS_NULL(argv[7])) { - JSObject *o; - jsval v; - - o = JSVAL_TO_OBJECT(argv[7]); - - if (JS_GetProperty(cx, o, "linespacing", &v) && - !JSVAL_IS_VOID(v)) { - jsdouble d; - - if (!JSVAL_IS_NUMBER(v) || - !JS_ValueToNumber(cx, v, &d)) { - QUEUE_EXCEPTION("linespacing not a Number"); - goto err; - } - ex.linespacing = (double)d; - ex.flags |= gdFTEX_LINESPACE; - } - if (JS_GetProperty(cx, o, "charmap", &v) && - !JSVAL_IS_VOID(v)) { - if (!JSVAL_IS_INT(v)) { - QUEUE_EXCEPTION("charmap not an Integer"); - goto err; - } - ex.charmap = JSVAL_TO_INT(v); - ex.flags |= gdFTEX_CHARMAP; - } - if (JS_GetProperty(cx, o, "hdpi", &v) && !JSVAL_IS_VOID(v)) { - if (!JSVAL_IS_INT(v)) { - QUEUE_EXCEPTION("hdpi not an Integer"); - goto err; - } - ex.hdpi = JSVAL_TO_INT(v); - } - if (JS_GetProperty(cx, o, "vdpi", &v) && !JSVAL_IS_VOID(v)) { - if (!JSVAL_IS_INT(v)) { - QUEUE_EXCEPTION("vdpi not an Integer"); - goto err; - } - ex.vdpi = JSVAL_TO_INT(v); - } - } - - img = JS_GetInstancePrivate(cx, obj, &gdimage_class, NULL); - assert(img != NULL); - - if ((error = gdImageStringFTEx(img, brect, JSVAL_TO_INT(argv[0]), font, - (double)ptsize, (double)angle, JSVAL_TO_INT(argv[4]), - JSVAL_TO_INT(argv[5]), string, &ex)) != NULL) { - QUEUE_EXCEPTION(error); - goto err; - } - - if ((o = JS_NewObject(cx, NULL, NULL, NULL)) == NULL) - goto err2; - *rval = OBJECT_TO_JSVAL(o); - - if (ex.xshow != NULL) { - if ((str = JS_NewStringCopyZ(cx, ex.xshow)) == NULL) - goto err2; - if (!JS_DefineProperty(cx, o, "xshow", STRING_TO_JSVAL(str), - NULL, NULL, JSPROP_ENUMERATE)) - goto err2; - gdFree(ex.xshow); - ex.xshow = NULL; - } - if (ex.fontpath != NULL) { - if ((str = JS_NewStringCopyZ(cx, ex.fontpath)) == NULL) - goto err2; - if (!JS_DefineProperty(cx, o, "fontpath", STRING_TO_JSVAL(str), - NULL, NULL, JSPROP_ENUMERATE)) - goto err2; - gdFree(ex.fontpath); - ex.fontpath = NULL; - } - - for (i = 0; i < 8; i++) - rect[i] = INT_TO_JSVAL(brect[i]); - if ((a = JS_NewArrayObject(cx, 8, rect)) == NULL) - goto err2; - if (!JS_DefineProperty(cx, o, "brect", OBJECT_TO_JSVAL(a), NULL, NULL, - JSPROP_ENUMERATE)) - goto err2; - - return JS_TRUE; - -err2: - QUEUE_EXCEPTION("Internal error!"); -err: - *rval = OBJECT_TO_JSVAL(NULL); - if (ex.xshow != NULL) - gdFree(ex.xshow); - if (ex.fontpath != NULL) - gdFree(ex.fontpath); - - return JS_FALSE; -} - -static JSBool -gdimage_m_string_ft_circle(JSContext *cx, JSObject *obj, uintN argc, - jsval *argv, jsval *rval) -{ - gdImagePtr img; - char *font, *topstr, *botstr, *error; - jsdouble radius, textradius, fillportion, ptsize; - - *rval = OBJECT_TO_JSVAL(NULL); - - if (argc != 10) { - QUEUE_EXCEPTION("Wrong number of arguments"); - return JS_FALSE; - } - if (!JSVAL_IS_INT(argv[0])) { - QUEUE_EXCEPTION("Argument 1 not an Integer"); - return JS_FALSE; - } - if (!JSVAL_IS_INT(argv[1])) { - QUEUE_EXCEPTION("Argument 2 not an Integer"); - return JS_FALSE; - } - if (!JSVAL_IS_NUMBER(argv[2]) || - !JS_ValueToNumber(cx, argv[2], &radius)) { - QUEUE_EXCEPTION("Argument 3 not a Number"); - return JS_FALSE; - } - if (!JSVAL_IS_NUMBER(argv[3]) || - !JS_ValueToNumber(cx, argv[3], &textradius)) { - QUEUE_EXCEPTION("Argument 4 not a Number"); - return JS_FALSE; - } - if (!JSVAL_IS_NUMBER(argv[4]) || - !JS_ValueToNumber(cx, argv[4], &fillportion)) { - QUEUE_EXCEPTION("Argument 5 not a Number"); - return JS_FALSE; - } - if (!JSVAL_IS_STRING(argv[5]) || - (font = JS_GetStringBytes(JSVAL_TO_STRING(argv[5]))) == NULL) { - QUEUE_EXCEPTION("Argument 6 not a String"); - return JS_FALSE; - } - if (!JSVAL_IS_NUMBER(argv[6]) || - !JS_ValueToNumber(cx, argv[6], &ptsize)) { - QUEUE_EXCEPTION("Argument 7 not a Number"); - return JS_FALSE; - } - if (!JSVAL_IS_STRING(argv[7]) || - (topstr = JS_GetStringBytes(JSVAL_TO_STRING(argv[7]))) == NULL) { - QUEUE_EXCEPTION("Argument 8 not a String"); - return JS_FALSE; - } - if (!JSVAL_IS_STRING(argv[8]) || - (botstr = JS_GetStringBytes(JSVAL_TO_STRING(argv[8]))) == NULL) { - QUEUE_EXCEPTION("Argument 9 not a String"); - return JS_FALSE; - } - if (!JSVAL_IS_INT(argv[9])) { - QUEUE_EXCEPTION("Argument 10 not an Integer"); - return JS_FALSE; - } - - img = JS_GetInstancePrivate(cx, obj, &gdimage_class, NULL); - assert(img != NULL); - - if ((error = gdImageStringFTCircle(img, JSVAL_TO_INT(argv[0]), - JSVAL_TO_INT(argv[1]), (double)radius, (double)textradius, - (double)fillportion, font, (double)ptsize, topstr, botstr, - JSVAL_TO_INT(argv[9]))) != NULL) { - QUEUE_EXCEPTION(error); - return JS_FALSE; - } - - return JS_TRUE; -} - - -static JSBool -gdimage_m_copy(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - gdImagePtr src, dst; - JSObject *o; - int i; - - *rval = OBJECT_TO_JSVAL(NULL); - - if (argc != 7) { - QUEUE_EXCEPTION("Wrong number of argumens"); - return JS_FALSE; - } - if (!JSVAL_IS_OBJECT(argv[0])) { - QUEUE_EXCEPTION("Argument 1 not a GDImage object"); - return JS_FALSE; - } - o = JSVAL_TO_OBJECT(argv[0]); - if (!JS_InstanceOf(cx, o, &gdimage_class, NULL)) { - QUEUE_EXCEPTION("Argument 1 not a GDImage object"); - return JS_FALSE; - } - for (i = 1; i < 7; i++) { - if (!JSVAL_IS_INT(argv[i])) { - char str[256]; - - (void) snprintf(str, 255, - "Argument %d not an Integer", i + 1); - QUEUE_EXCEPTION(str); - return JS_FALSE; - } - } - - src = JS_GetInstancePrivate(cx, obj, &gdimage_class, NULL); - assert(src != NULL); - dst = JS_GetInstancePrivate(cx, o, &gdimage_class, NULL); - assert(dst != NULL); - - gdImageCopy(dst, src, JSVAL_TO_INT(argv[1]), JSVAL_TO_INT(argv[2]), - JSVAL_TO_INT(argv[3]), JSVAL_TO_INT(argv[4]), - JSVAL_TO_INT(argv[5]), JSVAL_TO_INT(argv[6])); - - return JS_TRUE; -} - -static JSBool -gdimage_m_copy_resized(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - gdImagePtr src, dst; - JSObject *o; - int i; - - *rval = OBJECT_TO_JSVAL(NULL); - - if (argc != 9) { - QUEUE_EXCEPTION("Wrong number of argumens"); - return JS_FALSE; - } - if (!JSVAL_IS_OBJECT(argv[0])) { - QUEUE_EXCEPTION("Argument 1 not a GDImage object"); - return JS_FALSE; - } - o = JSVAL_TO_OBJECT(argv[0]); - if (!JS_InstanceOf(cx, o, &gdimage_class, NULL)) { - QUEUE_EXCEPTION("Argument 1 not a GDImage object"); - return JS_FALSE; - } - for (i = 1; i < 9; i++) { - if (!JSVAL_IS_INT(argv[i])) { - char str[256]; - - (void) snprintf(str, 255, - "Argument %d not an Integer", i + 1); - QUEUE_EXCEPTION(str); - return JS_FALSE; - } - } - - src = JS_GetInstancePrivate(cx, obj, &gdimage_class, NULL); - assert(src != NULL); - dst = JS_GetInstancePrivate(cx, o, &gdimage_class, NULL); - assert(dst != NULL); - - gdImageCopyResized(dst, src, JSVAL_TO_INT(argv[1]), - JSVAL_TO_INT(argv[2]), JSVAL_TO_INT(argv[3]), - JSVAL_TO_INT(argv[4]), JSVAL_TO_INT(argv[5]), - JSVAL_TO_INT(argv[6]), JSVAL_TO_INT(argv[7]), - JSVAL_TO_INT(argv[8])); - - return JS_TRUE; -} - -static JSBool -gdimage_m_copy_resampled(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - gdImagePtr src, dst; - JSObject *o; - int i; - - *rval = OBJECT_TO_JSVAL(NULL); - - if (argc != 9) { - QUEUE_EXCEPTION("Wrong number of argumens"); - return JS_FALSE; - } - if (!JSVAL_IS_OBJECT(argv[0])) { - QUEUE_EXCEPTION("Argument 1 not a GDImage object"); - return JS_FALSE; - } - o = JSVAL_TO_OBJECT(argv[0]); - if (!JS_InstanceOf(cx, o, &gdimage_class, NULL)) { - QUEUE_EXCEPTION("Argument 1 not a GDImage object"); - return JS_FALSE; - } - for (i = 1; i < 9; i++) { - if (!JSVAL_IS_INT(argv[i])) { - char str[256]; - - (void) snprintf(str, 255, - "Argument %d not an Integer", i + 1); - QUEUE_EXCEPTION(str); - return JS_FALSE; - } - } - - src = JS_GetInstancePrivate(cx, obj, &gdimage_class, NULL); - assert(src != NULL); - dst = JS_GetInstancePrivate(cx, o, &gdimage_class, NULL); - assert(dst != NULL); - - gdImageCopyResampled(dst, src, JSVAL_TO_INT(argv[1]), - JSVAL_TO_INT(argv[2]), JSVAL_TO_INT(argv[3]), - JSVAL_TO_INT(argv[4]), JSVAL_TO_INT(argv[5]), - JSVAL_TO_INT(argv[6]), JSVAL_TO_INT(argv[7]), - JSVAL_TO_INT(argv[8])); - - return JS_TRUE; -} - -static JSBool -gdimage_m_copy_rotated(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - gdImagePtr src, dst; - JSObject *o; - int i; - jsdouble dstx, dsty; - - *rval = OBJECT_TO_JSVAL(NULL); - - if (argc != 8) { - QUEUE_EXCEPTION("Wrong number of argumens"); - return JS_FALSE; - } - if (!JSVAL_IS_OBJECT(argv[0])) { - QUEUE_EXCEPTION("Argument 1 not a GDImage object"); - return JS_FALSE; - } - o = JSVAL_TO_OBJECT(argv[0]); - if (!JS_InstanceOf(cx, o, &gdimage_class, NULL)) { - QUEUE_EXCEPTION("Argument 1 not a GDImage object"); - return JS_FALSE; - } - if (!JSVAL_IS_NUMBER(argv[1]) || - !JS_ValueToNumber(cx, argv[1], &dstx)) { - QUEUE_EXCEPTION("Argument 2 not a Number"); - return JS_FALSE; - } - if (!JSVAL_IS_NUMBER(argv[2]) || - !JS_ValueToNumber(cx, argv[2], &dsty)) { - QUEUE_EXCEPTION("Argument 3 not a Number"); - return JS_FALSE; - } - for (i = 3; i < 8; i++) { - if (!JSVAL_IS_INT(argv[i])) { - char str[256]; - - (void) snprintf(str, 255, - "Argument %d not an Integer", i + 1); - QUEUE_EXCEPTION(str); - return JS_FALSE; - } - } - - src = JS_GetInstancePrivate(cx, obj, &gdimage_class, NULL); - assert(src != NULL); - dst = JS_GetInstancePrivate(cx, o, &gdimage_class, NULL); - assert(dst != NULL); - - gdImageCopyRotated(dst, src, (double)dstx, (double)dsty, - JSVAL_TO_INT(argv[3]), JSVAL_TO_INT(argv[4]), - JSVAL_TO_INT(argv[5]), JSVAL_TO_INT(argv[6]), - JSVAL_TO_INT(argv[7])); - - return JS_TRUE; -} - -static JSBool -gdimage_m_copy_merge(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - gdImagePtr src, dst; - JSObject *o; - int i; - - *rval = OBJECT_TO_JSVAL(NULL); - - if (argc != 8) { - QUEUE_EXCEPTION("Wrong number of argumens"); - return JS_FALSE; - } - if (!JSVAL_IS_OBJECT(argv[0])) { - QUEUE_EXCEPTION("Argument 1 not a GDImage object"); - return JS_FALSE; - } - o = JSVAL_TO_OBJECT(argv[0]); - if (!JS_InstanceOf(cx, o, &gdimage_class, NULL)) { - QUEUE_EXCEPTION("Argument 1 not a GDImage object"); - return JS_FALSE; - } - for (i = 1; i < 8; i++) { - if (!JSVAL_IS_INT(argv[i])) { - char str[256]; - - (void) snprintf(str, 255, - "Argument %d not an Integer", i + 1); - QUEUE_EXCEPTION(str); - return JS_FALSE; - } - } - - src = JS_GetInstancePrivate(cx, obj, &gdimage_class, NULL); - assert(src != NULL); - dst = JS_GetInstancePrivate(cx, o, &gdimage_class, NULL); - assert(dst != NULL); - - gdImageCopyMerge(dst, src, JSVAL_TO_INT(argv[1]), - JSVAL_TO_INT(argv[2]), JSVAL_TO_INT(argv[3]), - JSVAL_TO_INT(argv[4]), JSVAL_TO_INT(argv[5]), - JSVAL_TO_INT(argv[6]), JSVAL_TO_INT(argv[7])); - - return JS_TRUE; -} - -static JSBool -gdimage_m_copy_merge_gray(JSContext *cx, JSObject *obj, uintN argc, - jsval *argv, jsval *rval) -{ - gdImagePtr src, dst; - JSObject *o; - int i; - - *rval = OBJECT_TO_JSVAL(NULL); - - if (argc != 8) { - QUEUE_EXCEPTION("Wrong number of argumens"); - return JS_FALSE; - } - if (!JSVAL_IS_OBJECT(argv[0])) { - QUEUE_EXCEPTION("Argument 1 not a GDImage object"); - return JS_FALSE; - } - o = JSVAL_TO_OBJECT(argv[0]); - if (!JS_InstanceOf(cx, o, &gdimage_class, NULL)) { - QUEUE_EXCEPTION("Argument 1 not a GDImage object"); - return JS_FALSE; - } - for (i = 1; i < 8; i++) { - if (!JSVAL_IS_INT(argv[i])) { - char str[256]; - - (void) snprintf(str, 255, - "Argument %d not an Integer", i + 1); - QUEUE_EXCEPTION(str); - return JS_FALSE; - } - } - - src = JS_GetInstancePrivate(cx, obj, &gdimage_class, NULL); - assert(src != NULL); - dst = JS_GetInstancePrivate(cx, o, &gdimage_class, NULL); - assert(dst != NULL); - - gdImageCopyMergeGray(dst, src, JSVAL_TO_INT(argv[1]), - JSVAL_TO_INT(argv[2]), JSVAL_TO_INT(argv[3]), - JSVAL_TO_INT(argv[4]), JSVAL_TO_INT(argv[5]), - JSVAL_TO_INT(argv[6]), JSVAL_TO_INT(argv[7])); - - return JS_TRUE; -} - -static JSBool -gdimage_m_palette_copy(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - gdImagePtr src, dst; - JSObject *o; - - *rval = OBJECT_TO_JSVAL(NULL); - - if (argc != 1) { - QUEUE_EXCEPTION("Wrong number of argumens"); - return JS_FALSE; - } - if (!JSVAL_IS_OBJECT(argv[0])) { - QUEUE_EXCEPTION("Argument 1 not a GDImage object"); - return JS_FALSE; - } - o = JSVAL_TO_OBJECT(argv[0]); - if (!JS_InstanceOf(cx, o, &gdimage_class, NULL)) { - QUEUE_EXCEPTION("Argument 1 not a GDImage object"); - return JS_FALSE; - } - - src = JS_GetInstancePrivate(cx, obj, &gdimage_class, NULL); - assert(src != NULL); - dst = JS_GetInstancePrivate(cx, o, &gdimage_class, NULL); - assert(dst != NULL); - - gdImagePaletteCopy(dst, src); - - return JS_TRUE; -} - -static JSBool -gdimage_m_square_to_circle(JSContext *cx, JSObject *obj, uintN argc, - jsval *argv, jsval *rval) -{ - gdImagePtr img; - - *rval = OBJECT_TO_JSVAL(NULL); - - if (argc != 1) { - QUEUE_EXCEPTION("Wrong number of argumens"); - return JS_FALSE; - } - if (!JSVAL_IS_INT(argv[0])) { - QUEUE_EXCEPTION("Argument 1 not an Integer"); - return JS_FALSE; - } - - img = JS_GetInstancePrivate(cx, obj, &gdimage_class, NULL); - assert(img != NULL); - - gdImageSquareToCircle(img, JSVAL_TO_INT(argv[0])); - - return JS_TRUE; -} - - -/* - * Like the C function, returns a bitmap which may be compared against the - * GD.GD_CMP_* bits. - */ -static JSBool -gdimage_m_compare(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - gdImagePtr img1, img2; - JSObject *o; - int v; - - if (argc != 1) { - QUEUE_EXCEPTION("Wrong number of argumens"); - goto err; - } - if (!JSVAL_IS_OBJECT(argv[0])) { - QUEUE_EXCEPTION("Argument 1 not a GDImage object"); - goto err; - } - o = JSVAL_TO_OBJECT(argv[0]); - if (!JS_InstanceOf(cx, o, &gdimage_class, NULL)) { - QUEUE_EXCEPTION("Argument 1 not a GDImage object"); - goto err; - } - - img1 = JS_GetInstancePrivate(cx, obj, &gdimage_class, NULL); - assert(img1 != NULL); - img2 = JS_GetInstancePrivate(cx, o, &gdimage_class, NULL); - assert(img2 != NULL); - - v = gdImageCompare(img1, img2); - *rval = INT_TO_JSVAL(v); - - return JS_TRUE; - -err: - *rval = OBJECT_TO_JSVAL(NULL); - - return JS_FALSE; -} - -static JSBool -gdimage_m_interlace(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - gdImagePtr img; - - *rval = OBJECT_TO_JSVAL(NULL); - - if (argc != 1) { - QUEUE_EXCEPTION("Wrong number of argumens"); - return JS_FALSE; - } - if (!JSVAL_IS_BOOLEAN(argv[0])) { - QUEUE_EXCEPTION("Argument 1 not a Boolean"); - return JS_FALSE; - } - - img = JS_GetInstancePrivate(cx, obj, &gdimage_class, NULL); - assert(img != NULL); - - gdImageInterlace(img, (int)JSVAL_TO_BOOLEAN(argv[0])); - - return JS_TRUE; -} - - -/* - * GDFont object control - */ - -JSObject * -js_InitGDFontClass(JSContext *cx, JSObject *obj, JSObject *obj2) -{ - JSObject *proto, *o; - - if ((proto = JS_InitClass(cx, obj, NULL, &gdfont_class, - gdfont_constructor, 0, NULL, NULL, NULL, NULL)) == NULL) - return NULL; - - /* Attach font objects statically to the global GD object */ - if ((o = gdfont_new(cx, gdFontGetSmall())) != NULL) - JS_DefineProperty(cx, obj2, "gdFontSmall", OBJECT_TO_JSVAL(o), - NULL, NULL, JSPROP_READONLY | JSPROP_PERMANENT | - JSPROP_ENUMERATE); - if ((o = gdfont_new(cx, gdFontGetLarge())) != NULL) - JS_DefineProperty(cx, obj2, "gdFontLarge", OBJECT_TO_JSVAL(o), - NULL, NULL, JSPROP_READONLY | JSPROP_PERMANENT | - JSPROP_ENUMERATE); - if ((o = gdfont_new(cx, gdFontGetMediumBold())) != NULL) - JS_DefineProperty(cx, obj2, "gdFontMediumBold", - OBJECT_TO_JSVAL(o), NULL, NULL, - JSPROP_READONLY | JSPROP_PERMANENT | JSPROP_ENUMERATE); - if ((o = gdfont_new(cx, gdFontGetGiant())) != NULL) - JS_DefineProperty(cx, obj2, "gdFontGiant", OBJECT_TO_JSVAL(o), - NULL, NULL, JSPROP_READONLY | JSPROP_PERMANENT | - JSPROP_ENUMERATE); - if ((o = gdfont_new(cx, gdFontGetTiny())) != NULL) - JS_DefineProperty(cx, obj2, "gdFontTiny", OBJECT_TO_JSVAL(o), - NULL, NULL, JSPROP_READONLY | JSPROP_PERMANENT | - JSPROP_ENUMERATE); - - return proto; -} - -static JSBool -gdfont_constructor(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - - *rval = OBJECT_TO_JSVAL(NULL); - QUEUE_EXCEPTION("GDFont object not user-instanciable"); - - return JS_FALSE; -} - -static void -gdfont_finalize(JSContext *cx, JSObject *obj) -{ - - if (JS_GetInstancePrivate(cx, obj, &gdfont_class, NULL) != NULL) - (void) JS_SetPrivate(cx, obj, NULL); -} - -static JSObject * -gdfont_new(JSContext *cx, gdFontPtr f) -{ - JSObject *o; - - if (f == NULL) - return NULL; - - if ((o = JS_NewObject(cx, &gdfont_class, NULL, NULL)) == NULL) - return NULL; - - if (!JS_SetPrivate(cx, o, f)) - return NULL; - - return o; -} - -static gdFontPtr -gdfont_get(JSContext *cx, jsval v) -{ - JSObject *o; - gdFontPtr f; - - if (!JSVAL_IS_OBJECT(v)) - return NULL; - o = JSVAL_TO_OBJECT(v); - - if (!JS_InstanceOf(cx, o, &gdfont_class, NULL)) - return NULL; - - f = JS_GetInstancePrivate(cx, o, &gdfont_class, NULL); - assert(f != NULL); - - return f; -} diff --git a/mmsoftware/js/classes/js_gd.h b/mmsoftware/js/classes/js_gd.h deleted file mode 100644 index 1294377..0000000 --- a/mmsoftware/js/classes/js_gd.h +++ /dev/null @@ -1,15 +0,0 @@ -/* $Id: js_gd.h,v 1.1 2006/10/24 12:42:43 mmondor Exp $ */ - -/* - * Copyright (c) 2006, Matthew Mondor - * ALL RIGHTS RESERVED. - */ - -#ifndef JSGD_H -#define JSGD_H - -#include - -extern JSObject *js_InitGDClass(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 57f151d..0000000 --- a/mmsoftware/js/classes/js_pgsql.c +++ /dev/null @@ -1,4744 +0,0 @@ -/* $Id: js_pgsql.c,v 1.12 2006/10/27 06:09:44 mmondor Exp $ */ - -/* - * Copyright (c) 2006, Matthew Mondor - * ALL RIGHTS RESERVED. - */ - -/* - * XXX TODO XXX - * - Verify if JS_GetStringLength() really safe to continue using - * - (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). - * - See what to do about the following functions: - * - PQgetssl() (returns an SSL object!) - */ - - - -#include - -#include -#include -#include -#include -#include - -#include - -#include -#include /* Large Objects API */ - -#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 void pg_finalize(JSContext *, JSObject *); - -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 JSBool pgconn_m_PQsetNoticeReceiver(JSContext *, JSObject *, uintN, - jsval *, jsval *); -static void notice_receiver(void *, const PGresult *); -static JSBool pgconn_m_PQsetNoticeProcessor(JSContext *, JSObject *, uintN, - jsval *, jsval *); -static void notice_processor(void *, const char *); -static JSBool pgconn_m_lo_creat(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool pgconn_m_lo_create(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool pgconn_m_lo_import(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool pgconn_m_lo_export(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool pgconn_m_lo_open(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool pgconn_m_lo_write(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool pgconn_m_lo_read(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool pgconn_m_lo_lseek(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool pgconn_m_lo_tell(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool pgconn_m_lo_close(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool pgconn_m_lo_unlink(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 JSBool pgresult_m_PQprint(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 JSObject *js_InitPGPrintOptClass(JSContext *, JSObject *); -static JSBool pgprintopt_constructor(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(). - * XXX To be thread-safe, these would need to be tied to objects rather than - * being shared globals. This obviously would require more memory and add - * additional object creation overhead. - */ -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, pg_finalize -}; - -/* 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), - SP(INV_READ), - SP(INV_WRITE), - SP(SEEK_SET), - SP(SEEK_CUR), - SP(SEEK_END), - { 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, 1, 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 }, - { "setNoticeReceiver", pgconn_m_PQsetNoticeReceiver, 2, 0, 0 }, - { "setNoticeProcessor", pgconn_m_PQsetNoticeProcessor, 2, 0, 0 }, - { "loCreat", pgconn_m_lo_creat, 1, 0, 0 }, - { "loCreate", pgconn_m_lo_create, 1, 0, 0 }, - { "loImport", pgconn_m_lo_import, 1, 0, 0 }, - { "loExport", pgconn_m_lo_export, 2, 0, 0 }, - { "loOpen", pgconn_m_lo_open, 2, 0, 0 }, - { "loWrite", pgconn_m_lo_write, 2, 0, 0 }, - { "loRead", pgconn_m_lo_read, 2, 0, 0 }, - { "loSeek", pgconn_m_lo_lseek, 3, 0, 0 }, - { "loTell", pgconn_m_lo_tell, 1, 0, 0 }, - { "loClose", pgconn_m_lo_close, 1, 0, 0 }, - { "loUnlink", pgconn_m_lo_unlink, 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 }, - { "print", pgresult_m_PQprint, 2, 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 } -}; - - -/* PGPrintOpt class */ -static JSClass pgprintopt_class = { - "PGPrintOpt", 0, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, - JS_PropertyStub, JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, - JS_FinalizeStub -}; - - - -static int -buffer_grow(size_t required) -{ - size_t new; - void *ptr; - - /* Account space for possible NUL */ - 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; - - /* Account space for NULL */ - 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 object/classes which we'll need to instanciate objects - * from - */ - if (js_InitPGconnClass(cx, obj) == NULL) { - (void) fprintf(stderr, "PG: InitPGconnClass()\n"); - goto err; - } - if (js_InitPGresultClass(cx, obj) == NULL) { - (void) fprintf(stderr, "PG: InitPGresultClass()\n"); - goto err; - } - if (js_InitPGcancelClass(cx, obj) == NULL) { - (void) fprintf(stderr, "PG: InitPGcancelClass()\n"); - goto err; - } - if (js_InitPGPrintOptClass(cx, obj) == NULL) { - (void) fprintf(stderr, "PG: InitPGPrintOptClass()\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; -} - -/* ARGSUSED */ -static void -pg_finalize(JSContext *cx, JSObject *obj) -{ - - /* NOOP */ -} - - -/* - * 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]) || - (str = JS_GetStringBytes(JSVAL_TO_STRING(argv[0]))) == NULL) { - QUEUE_EXCEPTION("Argument not a String"); - goto err; - } - - 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; - } - /* Add reverse resolve entry */ - if (!js_map_add(pgc, cx, o, NULL)) { - QUEUE_EXCEPTION("js_map_add()"); - 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]) || - (str = JS_GetStringBytes(JSVAL_TO_STRING(argv[0]))) == NULL) { - QUEUE_EXCEPTION("Argument not a String"); - goto err; - } - - 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; - } - /* Reverse resolve entry */ - if (!js_map_add(pgc, cx, o, NULL)) { - QUEUE_EXCEPTION("js_map_add()"); - 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; - JSString *str; - - 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])); - if ((str = JS_NewStringCopyZ(cx, res)) == NULL) { - QUEUE_EXCEPTION("Internal error!"); - goto err; - } - *rval = STRING_TO_JSVAL(str); - - 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]) || - (from = JS_GetStringBytes(JSVAL_TO_STRING(argv[0]))) == NULL) { - QUEUE_EXCEPTION("Argument not a String"); - goto err; - } - - pgc = JS_GetInstancePrivate(cx, obj, &pgconn_class, NULL); - assert(pgc != NULL); - - 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) { - omap_t *omap; - - PQfinish(pgc); - (void) JS_SetPrivate(cx, obj, NULL); - if ((omap = js_map_lookup(pgc)) != NULL) - js_map_remove(omap); - } -} - - -/* - * 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) { - omap_t *omap; - - PQfinish(pgc); - (void) JS_SetPrivate(cx, obj, NULL); - if ((omap = js_map_lookup(pgc))) - js_map_remove(omap); - } - - *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; - int ret; - - 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); - - ret = PQresetStart(pgc); - *rval = INT_TO_JSVAL((int)ret); - - 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; - JSString *jstr; - - 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); - if ((jstr = JS_NewStringCopyZ(cx, str)) == NULL) { - QUEUE_EXCEPTION("Internal error!"); - *rval = OBJECT_TO_JSVAL(NULL); - return JS_FALSE; - } - *rval = STRING_TO_JSVAL(jstr); - - return JS_TRUE; -} - -static JSBool -pgconn_m_PQuser(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - PGconn *pgc; - char *str; - JSString *jstr; - - 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); - if ((jstr = JS_NewStringCopyZ(cx, str)) == NULL) { - QUEUE_EXCEPTION("Internal error!"); - *rval = OBJECT_TO_JSVAL(NULL); - return JS_FALSE; - } - *rval = STRING_TO_JSVAL(jstr); - - return JS_TRUE; -} - -static JSBool -pgconn_m_PQpass(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - PGconn *pgc; - char *str; - JSString *jstr; - - 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); - if ((jstr = JS_NewStringCopyZ(cx, str)) == NULL) { - QUEUE_EXCEPTION("Internal error!"); - *rval = OBJECT_TO_JSVAL(NULL); - return JS_FALSE; - } - *rval = STRING_TO_JSVAL(jstr); - - return JS_TRUE; -} - -static JSBool -pgconn_m_PQhost(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - PGconn *pgc; - char *str; - JSString *jstr; - - 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); - if ((jstr = JS_NewStringCopyZ(cx, str)) == NULL) { - QUEUE_EXCEPTION("Internal error!"); - *rval = OBJECT_TO_JSVAL(NULL); - return JS_FALSE; - } - *rval = STRING_TO_JSVAL(jstr); - - return JS_TRUE; -} - -static JSBool -pgconn_m_PQport(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - PGconn *pgc; - char *str; - JSString *jstr; - - 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); - if ((jstr = JS_NewStringCopyZ(cx, str)) == NULL) { - QUEUE_EXCEPTION("Internal error!"); - *rval = OBJECT_TO_JSVAL(NULL); - return JS_FALSE; - } - *rval = STRING_TO_JSVAL(jstr); - - return JS_TRUE; -} - -static JSBool -pgconn_m_PQtty(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - PGconn *pgc; - char *str; - JSString *jstr; - - 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); - if ((jstr = JS_NewStringCopyZ(cx, str)) == NULL) { - QUEUE_EXCEPTION("Internal error!"); - *rval = OBJECT_TO_JSVAL(NULL); - return JS_FALSE; - } - *rval = STRING_TO_JSVAL(jstr); - - return JS_TRUE; -} - -static JSBool -pgconn_m_PQoptions(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - PGconn *pgc; - char *str; - JSString *jstr; - - 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); - if ((jstr = JS_NewStringCopyZ(cx, str)) == NULL) { - QUEUE_EXCEPTION("Internal error!"); - *rval = OBJECT_TO_JSVAL(NULL); - return JS_FALSE; - } - *rval = STRING_TO_JSVAL(jstr); - - 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]) || - (param = JS_GetStringBytes(JSVAL_TO_STRING(argv[0]))) == NULL) { - QUEUE_EXCEPTION("Argument not a String"); - return JS_FALSE; - } - - pgc = JS_GetInstancePrivate(cx, obj, &pgconn_class, NULL); - assert(pgc != NULL); - - if ((str = PQparameterStatus(pgc, param)) != NULL) { - JSString *jstr; - - if ((jstr = JS_NewStringCopyZ(cx, str)) == NULL) { - QUEUE_EXCEPTION("Internal error!"); - return JS_FALSE; - } - *rval = STRING_TO_JSVAL(jstr); - } - - return JS_TRUE; -} - -static JSBool -pgconn_m_PQprotocolVersion(JSContext *cx, JSObject *obj, uintN argc, - jsval *argv, jsval *rval) -{ - PGconn *pgc; - int v; - - 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); - - v = PQprotocolVersion(pgc); - *rval = INT_TO_JSVAL(v); - - return JS_TRUE; -} - -static JSBool -pgconn_m_PQserverVersion(JSContext *cx, JSObject *obj, uintN argc, - jsval *argv, jsval *rval) -{ - PGconn *pgc; - int v; - - 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); - - v = PQserverVersion(pgc); - *rval = INT_TO_JSVAL(v); - - return JS_TRUE; -} - -static JSBool -pgconn_m_PQerrorMessage(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - PGconn *pgc; - char *str; - JSString *jstr; - - 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); - if ((jstr = JS_NewStringCopyZ(cx, str)) == NULL) { - QUEUE_EXCEPTION("Internal error!"); - *rval = OBJECT_TO_JSVAL(NULL); - return JS_FALSE; - } - *rval = STRING_TO_JSVAL(jstr); - - return JS_TRUE; -} - -static JSBool -pgconn_m_PQsocket(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - PGconn *pgc; - int fd; - - 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); - - fd = PQsocket(pgc); - *rval = INT_TO_JSVAL(fd); - - return JS_TRUE; -} - -static JSBool -pgconn_m_PQbackendPID(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - PGconn *pgc; - int v; - - 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); - - v = PQbackendPID(pgc); - *rval = INT_TO_JSVAL(v); - - 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]) || - (str = JS_GetStringBytes(JSVAL_TO_STRING(argv[0]))) == NULL) { - QUEUE_EXCEPTION("Argument not a String"); - 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 = PQexec(pgc, str)) == NULL) { - QUEUE_EXCEPTION("PQexec()"); - goto err; - } - if (!JS_SetPrivate(cx, o, pgr)) { - QUEUE_EXCEPTION("Internal error!"); - goto err; - } - /* Record reverse resolve entry */ - if (!js_map_add(pgr, cx, o, obj)) { - QUEUE_EXCEPTION("js_map_add()"); - 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; - int v; - - if (argc != 1) { - QUEUE_EXCEPTION("Wrong number of arguments"); - goto err; - } - - if (!JSVAL_IS_STRING(argv[0]) || - (str = JS_GetStringBytes(JSVAL_TO_STRING(argv[0]))) == NULL) { - QUEUE_EXCEPTION("Argument not a String"); - goto err; - } - - pgc = JS_GetInstancePrivate(cx, obj, &pgconn_class, NULL); - assert(pgc != NULL); - - v = PQsendQuery(pgc, str); - *rval = INT_TO_JSVAL(v); - - 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; - } - if ((values[i2++] = JS_GetStringBytes( - JSVAL_TO_STRING(val))) == NULL) { - QUEUE_EXCEPTION("Internal error!"); - goto err; - } - } - } - 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) { - char *str; - - if ((str = JS_GetStringBytes(JSVAL_TO_STRING(argv[0]))) - == NULL) { - QUEUE_EXCEPTION("Internal error!"); - goto err; - } - - if ((pgr = PQexecParams(pgc, str, 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; - } - /* Reverse resolve entry */ - if (!js_map_add(pgr, cx, o, obj)) { - QUEUE_EXCEPTION("js_map_add()"); - goto err; - } - - } else { - int v; - char *str; - - if ((str = JS_GetStringBytes(JSVAL_TO_STRING(argv[0]))) - == NULL) { - QUEUE_EXCEPTION("Internal error!"); - goto err; - } - - v = PQsendQueryParams(pgc, str, nargs, types, - (const char * const *)values, lengths, formats, - JSVAL_TO_INT(argv[6])); - *rval = INT_TO_JSVAL(v); - } - - 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) { - char *str1, *str2; - - if ((str1 = JS_GetStringBytes(JSVAL_TO_STRING(argv[0]))) - == NULL || (str2 = JS_GetStringBytes(JSVAL_TO_STRING( - argv[1]))) == NULL) { - QUEUE_EXCEPTION("Internal error!"); - goto err; - } - - if ((pgr = PQprepare(pgc, str1, str2, 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; - } - /* Reverse resolve entry */ - if (!js_map_add(pgr, cx, o, obj)) { - QUEUE_EXCEPTION("js_map_add()"); - goto err; - } - - } else { - int v; - char *str1, *str2; - - if ((str1 = JS_GetStringBytes(JSVAL_TO_STRING(argv[0]))) - == NULL || (str2 = JS_GetStringBytes(JSVAL_TO_STRING( - argv[1]))) == NULL) { - QUEUE_EXCEPTION("Internal error!"); - goto err; - } - - v = PQsendPrepare(pgc, str1, str2, nargs, types); - *rval = INT_TO_JSVAL(v); - - } - - 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; - } - if ((values[i2++] = JS_GetStringBytes( - JSVAL_TO_STRING(val))) == NULL) { - QUEUE_EXCEPTION("Internal error!"); - goto err; - } - } - } - 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) { - char *str; - - if ((str = JS_GetStringBytes(JSVAL_TO_STRING(argv[0]))) - == NULL) { - QUEUE_EXCEPTION("Internal error!"); - goto err; - } - - if ((pgr = PQexecPrepared(pgc, str, 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; - } - /* Reverse resolve entry */ - if (!js_map_add(pgr, cx, o, obj)) { - QUEUE_EXCEPTION("js_map_add()"); - goto err; - } - - } else { - int v; - char *str; - - if ((str = JS_GetStringBytes(JSVAL_TO_STRING(argv[0]))) - == NULL) { - QUEUE_EXCEPTION("Internal error!"); - goto err; - } - - v = PQsendQueryPrepared(pgc, str, nargs, - (const char * const *)values, lengths, formats, - JSVAL_TO_INT(argv[5])); - *rval = INT_TO_JSVAL(v); - } - - 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; - } - /* Revere resolve entry */ - if (!js_map_add(pgr, cx, o, obj)) { - QUEUE_EXCEPTION("js_map_add()"); - 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]); - if ((from = JS_GetStringBytes(str)) == NULL) { - QUEUE_EXCEPTION("Internal error!"); - goto err; - } - 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]); - if ((from = JS_GetStringBytes(str)) == NULL) { - QUEUE_EXCEPTION("Internal error!"); - goto err; - } - 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; - } - /* Reverse resolve entry */ - if (!js_map_add(pgr, cx, o, obj)) { - QUEUE_EXCEPTION("js_map_add()"); - 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; - int v; - - 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); - - v = PQconsumeInput(pgc); - *rval = INT_TO_JSVAL(v); - - return JS_TRUE; -} - -static JSBool -pgconn_m_PQisBusy(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - PGconn *pgc; - int v; - - 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); - - v = PQisBusy(pgc); - *rval = INT_TO_JSVAL(v); - - return JS_TRUE; -} - -static JSBool -pgconn_m_PQsetnonblocking(JSContext *cx, JSObject *obj, uintN argc, - jsval *argv, jsval *rval) -{ - PGconn *pgc; - int v; - - 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); - - v = PQsetnonblocking(pgc, JSVAL_TO_INT(argv[0])); - *rval = INT_TO_JSVAL(v); - - 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; - int v; - - 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); - - v = PQisnonblocking(pgc); - *rval = INT_TO_JSVAL(v); - - return JS_TRUE; -} - -static JSBool -pgconn_m_PQflush(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - PGconn *pgc; - int v; - - 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); - - v = PQflush(pgc); - *rval = INT_TO_JSVAL(v); - - return JS_TRUE; -} - -static JSBool -pgconn_m_PQsetErrorVerbosity(JSContext *cx, JSObject *obj, uintN argc, - jsval *argv, jsval *rval) -{ - PGconn *pgc; - int v; - - 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); - - v = PQsetErrorVerbosity(pgc, JSVAL_TO_INT(argv[0])); - *rval = INT_TO_JSVAL(v); - - return JS_TRUE; - -err: - *rval = OBJECT_TO_JSVAL(NULL); - - return JS_FALSE; -} - -/* - * Requires a File object, which we root to ensure that it doesn't get - * unexpectedly finalized. We unroot any previously provided File object when - * supplied with a new one. - */ -static JSBool -pgconn_m_PQtrace(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - PGconn *pgc; - FILE *fh; - JSObject *gcroot; - - *rval = OBJECT_TO_JSVAL(NULL); - - if (argc != 1) { - QUEUE_EXCEPTION("Wrong number of arguments"); - return JS_FALSE; - } - if ((fh = file_fh(cx, argv[0])) == NULL) { - QUEUE_EXCEPTION("Argument 1 not a File"); - return JS_FALSE; - } - - pgc = JS_GetInstancePrivate(cx, obj, &pgconn_class, NULL); - assert(pgc != NULL); - - /* - * Unroot previously rooted File object, if any, then root newly - * provided File object. - */ - gcroot = js_GCRoot(cx); - assert(gcroot != NULL); - (void) JS_DeleteProperty(cx, gcroot, "trace_file_root"); - if (!JS_DefineProperty(cx, gcroot, "trace_file_root", argv[0], - NULL, NULL, 0)) { - QUEUE_EXCEPTION("Internal error!"); - return JS_FALSE; - } - - PQtrace(pgc, fh); - - return JS_TRUE; -} - -/* - * We unroot any previously rooted File object supplied to trace(). - */ -static JSBool -pgconn_m_PQuntrace(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - PGconn *pgc; - JSObject *gcroot; - - *rval = OBJECT_TO_JSVAL(NULL); - - if (argc != 0) { - QUEUE_EXCEPTION("Function allows no arguments"); - return JS_FALSE; - } - - pgc = JS_GetInstancePrivate(cx, obj, &pgconn_class, NULL); - assert(pgc != NULL); - - /* - * Unroot previously rooted File object - */ - gcroot = js_GCRoot(cx); - assert(gcroot != NULL); - (void) JS_DeleteProperty(cx, gcroot, "trace_file_root"); - - PQuntrace(pgc); - - return JS_TRUE; -} - -static JSBool -pgconn_m_PQputCopyData(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - PGconn *pgc; - JSString *str; - char *bytes; - int v; - - 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]); - if ((bytes = JS_GetStringBytes(str)) == NULL) { - QUEUE_EXCEPTION("Internal error!"); - goto err; - } - v = PQputCopyData(pgc, bytes, JS_GetStringLength(str)); - *rval = INT_TO_JSVAL(v); - - 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; - int v; - - 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); - - v = PQputCopyEnd(pgc, (JSVAL_IS_NULL(argv[0]) ? NULL : - JS_GetStringBytes(JSVAL_TO_STRING(argv[0])))); - *rval = INT_TO_JSVAL(v); - - 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; -} - -static JSBool -pgconn_m_PQsetNoticeReceiver(JSContext *cx, JSObject *obj, uintN argc, - jsval *argv, jsval *rval) -{ - PGconn *pgc; - - /* - * Verify if our connection object has the nr_function property set, - * in which case we set it as the return code. If it doesn't have - * any, return null. - */ - if (!JS_GetProperty(cx, obj, "nr_function", &rval[0])) - *rval = OBJECT_TO_JSVAL(NULL); - - if (argc != 2) { - QUEUE_EXCEPTION("Wrong number of arguments"); - return JS_FALSE; - } - if (!JSVAL_IS_OBJECT(argv[0]) || - !JS_ObjectIsFunction(cx, JSVAL_TO_OBJECT(argv[0]))) { - QUEUE_EXCEPTION("Argument 1 not a Function Object"); - return JS_FALSE; - } - if (!JSVAL_IS_OBJECT(argv[1]) && !JSVAL_IS_NULL(argv[1])) { - QUEUE_EXCEPTION("Argument 2 not an Object or null"); - return JS_FALSE; - } - - /* - * Set the nr_function and nr_udata properties. This simplifies - * things while ensuring that the ojects live as long as the - * connection object does. We don't make them enumerable. - */ - if (!JS_DefineProperty(cx, obj, "nr_function", argv[0], NULL, NULL, - JSPROP_READONLY | JSPROP_PERMANENT)) { - QUEUE_EXCEPTION("Internal error!"); - return JS_FALSE; - } - if (!JS_DefineProperty(cx, obj, "nr_udata", argv[1], NULL, NULL, - JSPROP_READONLY | JSPROP_PERMANENT)) { - QUEUE_EXCEPTION("Internal error!"); - return JS_FALSE; - } - - /* - * Now set our custom notice_processor function. We need to pass both - * cx and obj to it, although we can only pass a single pointer. And - * we can't set obj as cx specific data, since the data must be - * connection specific. Moreover, we can't store private data on the - * supplied function object, since the JSFunction * is set already. - * Moreover, JS_GetPrivate()/JS_GetInstancePrivate() both need the - * JSObject * AND the JSContext *! This means that I need - * connection-specific udata other than PGconn *, since it would not - * be possible to trace back the allocated data at object - * finalization if we just allocated memory and provided the pointer - * as udata. Unless I stored the pointer as a property in the - * connection object, heh. - */ - pgc = JS_GetInstancePrivate(cx, obj, &pgconn_class, NULL); - assert(pgc != NULL); - - (void) PQsetNoticeReceiver(pgc, notice_receiver, NULL); - - return JS_TRUE; -} - -static void -notice_receiver(void *udata, const PGresult *pgr) -{ - omap_t *omap; - jsval args[2], ret, pgrv, nr_function, nr_udata; - - omap = js_map_lookup((void *)pgr); - assert(omap != NULL); - - /* - * JSContext *cx; - * JSObject *obj; PGResult - * void *udata; PGConn JSObject * - */ - - pgrv = OBJECT_TO_JSVAL(omap->obj); - - /* We call the user supplied function with the arguments: - * udata, PGResult object. - * We root the PGResult object to the PGConn meanwhile. - * We get out the udata and function to call from the connection - * object. - */ - if (!JS_DefineProperty(omap->cx, omap->udata, "nr_pgresult", pgrv, - NULL, NULL, JSPROP_READONLY | JSPROP_PERMANENT)) - return; - - if (!JS_GetProperty(omap->cx, omap->udata, "nr_function", - &nr_function) || !JS_GetProperty(omap->cx, omap->udata, - "nr_udata", &nr_udata)) - goto err; - - args[0] = nr_udata; - args[1] = pgrv; - - (void) JS_CallFunctionValue(omap->cx, omap->udata, nr_function, 2, - args, &ret); - -err: - (void) JS_DeleteProperty(omap->cx, omap->udata, "nr_pgresult"); -} - -static JSBool -pgconn_m_PQsetNoticeProcessor(JSContext *cx, JSObject *obj, uintN argc, - jsval *argv, jsval *rval) -{ - PGconn *pgc; - - /* - * Verify if our connection object has the np_function property set, - * in which case we set it as the return code. If it doesn't have - * any, return null. - */ - if (!JS_GetProperty(cx, obj, "np_function", &rval[0])) - *rval = OBJECT_TO_JSVAL(NULL); - - if (argc != 2) { - QUEUE_EXCEPTION("Wrong number of arguments"); - return JS_FALSE; - } - if (!JSVAL_IS_OBJECT(argv[0]) || - !JS_ObjectIsFunction(cx, JSVAL_TO_OBJECT(argv[0]))) { - QUEUE_EXCEPTION("Argument 1 not a Function Object"); - return JS_FALSE; - } - if (!JSVAL_IS_OBJECT(argv[1]) && !JSVAL_IS_NULL(argv[1])) { - QUEUE_EXCEPTION("Argument 2 not an Object or null"); - return JS_FALSE; - } - - /* - * Set the np_function and np_udata properties. This simplifies - * things while ensuring that the ojects live as long as the - * connection object does. We don't make them enumerable. - */ - if (!JS_DefineProperty(cx, obj, "np_function", argv[0], NULL, NULL, - JSPROP_READONLY | JSPROP_PERMANENT)) { - QUEUE_EXCEPTION("Internal error!"); - return JS_FALSE; - } - if (!JS_DefineProperty(cx, obj, "np_udata", argv[1], NULL, NULL, - JSPROP_READONLY | JSPROP_PERMANENT)) { - QUEUE_EXCEPTION("Internal error!"); - return JS_FALSE; - } - - /* - * Now set our custom notice_processor function. We need to pass both - * cx and obj to it, although we can only pass a single pointer. And - * we can't set obj as cx specific data, since the data must be - * connection specific. Moreover, we can't store private data on the - * supplied function object, since the JSFunction * is set already. - * Moreover, JS_GetPrivate()/JS_GetInstancePrivate() both need the - * JSObject * AND the JSContext *! This means that I need - * connection-specific udata other than PGconn *, since it would not - * be possible to trace back the allocated data at object - * finalization if we just allocated memory and provided the pointer - * as udata. Unless I stored the pointer as a property in the - * connection object, heh. - */ - pgc = JS_GetInstancePrivate(cx, obj, &pgconn_class, NULL); - assert(pgc != NULL); - - (void) PQsetNoticeProcessor(pgc, notice_processor, pgc); - - return JS_TRUE; -} - -/* Internal C notive processor handler which delegates to JS */ -static void -notice_processor(void *udata, const char *msg) -{ - omap_t *omap; - PGconn *pgc; - jsval args[2], ret, strv, np_function, np_udata; - JSString *str; - - /* - * The pgc pointer is passed through udata. Lookup by pgc to obtain - * the corresponding PGConn JSObject * and its JSContext *. - */ - pgc = udata; - omap = js_map_lookup(pgc); - assert(omap != NULL); - - /* - * JSContext *cx; - * JSObject *obj; PGConn - * void *udata; - */ - - if ((str = JS_NewStringCopyZ(omap->cx, msg)) == NULL) - return; - strv = STRING_TO_JSVAL(str); - - /* - * Obtain np_function and np_udata from the connection object we were - * supplied with, and all the function with parameters: - * udata, String. - * Temporarily root the JSString meanwhile. - */ - if (!JS_DefineProperty(omap->cx, omap->obj, "np_message", strv, - NULL, NULL, JSPROP_READONLY | JSPROP_PERMANENT)) - return; - - if (!JS_GetProperty(omap->cx, omap->udata, "np_function", - &np_function) || !JS_GetProperty(omap->cx, omap->udata, - "np_udata", &np_udata)) - goto err; - - args[0] = np_udata; - args[1] = strv; - - (void) JS_CallFunctionValue(omap->cx, omap->obj, np_function, 2, - args, &ret); - -err: - (void) JS_DeleteProperty(omap->cx, omap->obj, "np_message"); -} - -static JSBool -pgconn_m_lo_creat(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - PGconn *pgc; - Oid oid; - - if (argc != 1) { - QUEUE_EXCEPTION("Wrong number of arguments"); - goto err; - } - if (!JSVAL_IS_INT(argv[0])) { - QUEUE_EXCEPTION("Argument 1 not an Integer"); - goto err; - } - - pgc = JS_GetInstancePrivate(cx, obj, &pgconn_class, NULL); - assert(pgc != NULL); - - oid = lo_creat(pgc, 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 -pgconn_m_lo_create(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - PGconn *pgc; - Oid oid; - jsdouble v; - - if (argc != 1) { - QUEUE_EXCEPTION("Wrong number of arguments"); - goto err; - } - if (!JSVAL_IS_NUMBER(argv[0]) || !JS_ValueToNumber(cx, argv[0], &v)) { - QUEUE_EXCEPTION("Argument 1 not a number"); - goto err; - } - - pgc = JS_GetInstancePrivate(cx, obj, &pgconn_class, NULL); - assert(pgc != NULL); - - oid = lo_create(pgc, (Oid)v); - - 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 -pgconn_m_lo_import(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - PGconn *pgc; - Oid oid; - char *str; - - if (argc != 1) { - QUEUE_EXCEPTION("Wrong number of arguments"); - goto err; - } - if (!JSVAL_IS_STRING(argv[0]) || - ((str = JS_GetStringBytes(JSVAL_TO_STRING(argv[0])))) == NULL) { - QUEUE_EXCEPTION("Argument 1 not a String"); - goto err; - } - - /* XXX Perform path/access sanity checking */ - - pgc = JS_GetInstancePrivate(cx, obj, &pgconn_class, NULL); - assert(pgc != NULL); - - oid = lo_import(pgc, str); - - 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 -pgconn_m_lo_export(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - PGconn *pgc; - jsdouble v; - char *str; - int r; - - if (argc != 2) { - QUEUE_EXCEPTION("Wrong number of arguments"); - goto err; - } - if (!JSVAL_IS_NUMBER(argv[0]) || !JS_ValueToNumber(cx, argv[0], &v)) { - QUEUE_EXCEPTION("Argument 1 not a number"); - goto err; - } - if (!JSVAL_IS_STRING(argv[1]) || - ((str = JS_GetStringBytes(JSVAL_TO_STRING(argv[1])))) == NULL) { - QUEUE_EXCEPTION("Argument 2 not a String"); - goto err; - } - - /* XXX Perform path/access sanity checking */ - - pgc = JS_GetInstancePrivate(cx, obj, &pgconn_class, NULL); - assert(pgc != NULL); - - r = lo_export(pgc, (Oid)v, str); - *rval = INT_TO_JSVAL(r); - - return JS_TRUE; - -err: - *rval = OBJECT_TO_JSVAL(NULL); - - return JS_FALSE; -} - -/* - * Unlike FD.open(), we don't wrap the provided integer descriptor into an - * object and define read()/write() function as object methods. - * This integer descriptor automatically becomes invalid once the transaction - * terminates and as such doesn't require automatic finalization. - */ -static JSBool -pgconn_m_lo_open(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - PGconn *pgc; - jsdouble v; - int r; - - if (argc != 2) { - QUEUE_EXCEPTION("Wrong number of arguments"); - goto err; - } - if (!JSVAL_IS_NUMBER(argv[0]) || !JS_ValueToNumber(cx, argv[0], &v)) { - QUEUE_EXCEPTION("Argument 1 not a number"); - goto err; - } - if (!JSVAL_IS_INT(argv[1])) { - QUEUE_EXCEPTION("Argument 2 not an Integer"); - goto err; - } - - pgc = JS_GetInstancePrivate(cx, obj, &pgconn_class, NULL); - assert(pgc != NULL); - - r = lo_open(pgc, (Oid)v, JSVAL_TO_INT(argv[1])); - *rval = INT_TO_JSVAL(r); - - return JS_TRUE; - -err: - *rval = OBJECT_TO_JSVAL(NULL); - - return JS_FALSE; -} - -/* - * Unlike lo_write(), does not need a length argument, since we can obtain it - * using the JS string length. Moreover, we convert to a string whatever type - * of data was provided to us. - */ -static JSBool -pgconn_m_lo_write(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - PGconn *pgc; - JSString *str; - char *bytes; - size_t size; - int r; - - if (argc != 2) { - QUEUE_EXCEPTION("Wrong number of arguments"); - goto err; - } - if (!JSVAL_IS_INT(argv[0])) { - QUEUE_EXCEPTION("Argument 1 not an Integer"); - goto err; - } - - /* Convert argv[1] to a JSString */ - if ((str = JS_ValueToString(cx, argv[1])) == NULL || - (bytes = JS_GetStringBytes(str)) == NULL) { - QUEUE_EXCEPTION("Internal error!"); - goto err; - } - size = JS_GetStringLength(str); - - pgc = JS_GetInstancePrivate(cx, obj, &pgconn_class, NULL); - assert(pgc != NULL); - - r = lo_write(pgc, JSVAL_TO_INT(argv[0]), bytes, size); - *rval = INT_TO_JSVAL(r); - - return JS_TRUE; - -err: - *rval = OBJECT_TO_JSVAL(NULL); - - return JS_FALSE; -} - -/* - * Unlike lo_read(), returns a JS String object rather than being provided a - * buffer to fill. - */ -static JSBool -pgconn_m_lo_read(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - PGconn *pgc; - JSString *str; - uint32 size; - int ret; - - if (argc != 2) { - QUEUE_EXCEPTION("Wrong number of arguments"); - goto err; - } - if (!JSVAL_IS_INT(argv[0])) { - QUEUE_EXCEPTION("Argument 1 not an Integer"); - goto err; - } - if (!JSVAL_IS_NUMBER(argv[1]) || - !JS_ValueToECMAUint32(cx, argv[1], &size)) { - QUEUE_EXCEPTION("Argument 1 not a number"); - goto err; - } - - pgc = JS_GetInstancePrivate(cx, obj, &pgconn_class, NULL); - assert(pgc != NULL); - - if (buffer_grow((size_t)size) == -1) { - QUEUE_EXCEPTION("Out of memory!"); - goto err; - } - - if ((ret = lo_read(pgc, JSVAL_TO_INT(argv[0]), buffer, (size_t)size)) - < 0) { - QUEUE_EXCEPTION("lo_read()"); - goto err; - } - - if ((str = JS_NewStringCopyN(cx, buffer, (size_t)ret)) == 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; -} - -static JSBool -pgconn_m_lo_lseek(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - PGconn *pgc; - int r; - - if (argc != 3) { - QUEUE_EXCEPTION("Wrong number of arguments"); - goto err; - } - if (!JSVAL_IS_INT(argv[0])) { - QUEUE_EXCEPTION("Argument 1 not an Integer"); - goto err; - } - if (!JSVAL_IS_INT(argv[1])) { - QUEUE_EXCEPTION("Argument 2 not an Integer"); - goto err; - } - if (!JSVAL_IS_INT(argv[2])) { - QUEUE_EXCEPTION("Argument 3 not an Integer"); - goto err; - } - - pgc = JS_GetInstancePrivate(cx, obj, &pgconn_class, NULL); - assert(pgc != NULL); - - r = lo_lseek(pgc, JSVAL_TO_INT(argv[0]), JSVAL_TO_INT(argv[1]), - JSVAL_TO_INT(argv[2])); - *rval = INT_TO_JSVAL(r); - - return JS_TRUE; - -err: - *rval = OBJECT_TO_JSVAL(NULL); - - return JS_FALSE; -} - -static JSBool -pgconn_m_lo_tell(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - PGconn *pgc; - int r; - - if (argc != 1) { - QUEUE_EXCEPTION("Wrong number of arguments"); - goto err; - } - if (!JSVAL_IS_INT(argv[0])) { - QUEUE_EXCEPTION("Argument 1 not an Integer"); - goto err; - } - - pgc = JS_GetInstancePrivate(cx, obj, &pgconn_class, NULL); - assert(pgc != NULL); - - r = lo_tell(pgc, JSVAL_TO_INT(argv[0])); - *rval = INT_TO_JSVAL(r); - - return JS_TRUE; - -err: - *rval = OBJECT_TO_JSVAL(NULL); - - return JS_FALSE; -} - -static JSBool -pgconn_m_lo_close(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - PGconn *pgc; - int r; - - if (argc != 1) { - QUEUE_EXCEPTION("Wrong number of arguments"); - goto err; - } - if (!JSVAL_IS_INT(argv[0])) { - QUEUE_EXCEPTION("Argument 1 not an Integer"); - goto err; - } - - pgc = JS_GetInstancePrivate(cx, obj, &pgconn_class, NULL); - assert(pgc != NULL); - - r = lo_close(pgc, JSVAL_TO_INT(argv[0])); - *rval = INT_TO_JSVAL(r); - - return JS_TRUE; - -err: - *rval = OBJECT_TO_JSVAL(NULL); - - return JS_FALSE; -} - -static JSBool -pgconn_m_lo_unlink(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - PGconn *pgc; - jsdouble v; - int r; - - if (argc != 1) { - QUEUE_EXCEPTION("Wrong number of arguments"); - goto err; - } - if (!JSVAL_IS_NUMBER(argv[0]) || !JS_ValueToNumber(cx, argv[0], &v)) { - QUEUE_EXCEPTION("Argument 1 not a number"); - goto err; - } - - pgc = JS_GetInstancePrivate(cx, obj, &pgconn_class, NULL); - assert(pgc != NULL); - - r = lo_unlink(pgc, (Oid)v); - *rval = INT_TO_JSVAL(r); - - return JS_TRUE; - -err: - *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) { - omap_t *omap; - - PQclear(pgr); - (void) JS_SetPrivate(cx, obj, NULL); - if ((omap = js_map_lookup(pgr)) != NULL) - js_map_remove(omap); - } -} - - -/* - * PGresult object methods - */ - -static JSBool -pgresult_m_PQclear(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - PGresult *pgr; - - *rval = OBJECT_TO_JSVAL(NULL); - - if (argc != 0) { - QUEUE_EXCEPTION("Function allows no arguments"); - return JS_FALSE; - } - - if ((pgr = JS_GetInstancePrivate(cx, obj, &pgresult_class, NULL)) - != NULL) { - omap_t *omap; - - PQclear(pgr); - (void) JS_SetPrivate(cx, obj, NULL); - if ((omap = js_map_lookup(pgr)) != NULL) - js_map_remove(omap); - } - - 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; - JSString *str; - - 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); - if ((str = JS_NewStringCopyZ(cx, res)) == NULL) { - QUEUE_EXCEPTION("Internal error!"); - *rval = OBJECT_TO_JSVAL(NULL); - return JS_FALSE; - } - *rval = STRING_TO_JSVAL(str); - - return JS_TRUE; -} - -static JSBool -pgresult_m_PQresultErrorField(JSContext *cx, JSObject *obj, uintN argc, - jsval *argv, jsval *rval) -{ - PGresult *pgr; - char *res; - JSString *str; - - 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])); - if ((str = JS_NewStringCopyZ(cx, res)) == NULL) { - QUEUE_EXCEPTION("Internal error!"); - *rval = OBJECT_TO_JSVAL(NULL); - return JS_FALSE; - } - *rval = STRING_TO_JSVAL(str); - - 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; - int r; - - 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); - - r = PQntuples(pgr); - *rval = INT_TO_JSVAL(r); - - return JS_TRUE; -} - -static JSBool -pgresult_m_PQnfields(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - PGresult *pgr; - int r; - - 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); - - r = PQnfields(pgr); - *rval = INT_TO_JSVAL(r); - - return JS_TRUE; -} - -static JSBool -pgresult_m_PQfname(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - PGresult *pgr; - char *res; - JSString *str; - - 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])); - if ((str = JS_NewStringCopyZ(cx, res)) == NULL) { - QUEUE_EXCEPTION("Internal error!"); - goto err; - } - *rval = STRING_TO_JSVAL(str); - - 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; - int r; - - if (argc != 1) { - QUEUE_EXCEPTION("Wrong number of arguments"); - goto err; - } - if (!JSVAL_IS_STRING(argv[0]) || - (str = JS_GetStringBytes(JSVAL_TO_STRING(argv[0]))) == NULL) { - QUEUE_EXCEPTION("Argument not a String"); - goto err; - } - - pgr = JS_GetInstancePrivate(cx, obj, &pgresult_class, NULL); - assert(pgr != NULL); - - r = PQfnumber(pgr, str); - *rval = INT_TO_JSVAL(r); - - 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; - int r; - - 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); - - r = PQftablecol(pgr, JSVAL_TO_INT(argv[0])); - *rval = INT_TO_JSVAL(r); - - 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; - int r; - - 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); - - r = PQfformat(pgr, JSVAL_TO_INT(argv[0])); - *rval = INT_TO_JSVAL(r); - - 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; - int r; - - 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); - - r = PQfmod(pgr, JSVAL_TO_INT(argv[0])); - *rval = INT_TO_JSVAL(r); - - 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; - int r; - - 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); - - r = PQfsize(pgr, JSVAL_TO_INT(argv[0])); - *rval = INT_TO_JSVAL(r); - - 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; - int r; - - 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); - - r = PQbinaryTuples(pgr); - *rval = INT_TO_JSVAL(r); - - 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; - JSBool v; - - 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); - - v = PQgetisnull(pgr, JSVAL_TO_INT(argv[0]), JSVAL_TO_INT(argv[1])); - *rval = BOOLEAN_TO_JSVAL(v); - - 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; - int r; - - 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); - - r = PQgetlength(pgr, JSVAL_TO_INT(argv[0]), JSVAL_TO_INT(argv[1])); - *rval = INT_TO_JSVAL(r); - - 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; - JSString *str; - - 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); - - if ((str = JS_NewStringCopyZ(cx, PQcmdStatus(pgr))) == NULL) { - QUEUE_EXCEPTION("Internal error!"); - *rval = OBJECT_TO_JSVAL(NULL); - return JS_FALSE; - } - *rval = STRING_TO_JSVAL(str); - - 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; - JSString *str; - - 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); - - if ((str = JS_NewStringCopyZ(cx, PQcmdTuples(pgr))) == NULL) { - QUEUE_EXCEPTION("Internal error!"); - *rval = OBJECT_TO_JSVAL(NULL); - return JS_FALSE; - } - *rval = STRING_TO_JSVAL(str); - - 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; -} - -/* - * XXX Not reentrant as it uses a global buffer. - * For simplicity, we currently use param_grow() and param_values. - */ -static JSBool -pgresult_m_PQprint(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - PGresult *pgr; - FILE *fh; - JSObject *o; - PQprintOpt opt; - jsval v; - JSIdArray *a = NULL; - - *rval = OBJECT_TO_JSVAL(NULL); - - if (argc != 2) { - QUEUE_EXCEPTION("Wrong number of arguments"); - return JS_FALSE; - } - if ((fh = file_fh(cx, argv[0])) == NULL) { - QUEUE_EXCEPTION("Argument 1 not a File"); - return JS_FALSE; - } - if (!JSVAL_IS_OBJECT(argv[1])) { - QUEUE_EXCEPTION("Argument 2 not a PGPrintOpt"); - return JS_FALSE; - } - o = JSVAL_TO_OBJECT(argv[1]); - if (!JS_InstanceOf(cx, o, &pgprintopt_class, NULL)) { - QUEUE_EXCEPTION("Argument 2 not a PGPrintOpt"); - return JS_FALSE; - } - - pgr = JS_GetInstancePrivate(cx, obj, &pgresult_class, NULL); - assert(pgr != NULL); - - /* Fill opt according to supplied object properties */ - - /* Boolean properties */ - if (!JS_GetProperty(cx, o, "header", &v) || !JSVAL_IS_BOOLEAN(v)) { - QUEUE_EXCEPTION("property 'header' not a Boolean"); - return JS_FALSE; - } - opt.header = JSVAL_TO_BOOLEAN(v); - - if (!JS_GetProperty(cx, o, "align", &v) || !JSVAL_IS_BOOLEAN(v)) { - QUEUE_EXCEPTION("property 'align' not a Boolean"); - return JS_FALSE; - } - opt.align = JSVAL_TO_BOOLEAN(v); - - if (!JS_GetProperty(cx, o, "standard", &v) || !JSVAL_IS_BOOLEAN(v)) { - QUEUE_EXCEPTION("property 'standard' not a Boolean"); - return JS_FALSE; - } - opt.standard = JSVAL_TO_BOOLEAN(v); - - if (!JS_GetProperty(cx, o, "html3", &v) || !JSVAL_IS_BOOLEAN(v)) { - QUEUE_EXCEPTION("property 'html3' not a Boolean"); - return JS_FALSE; - } - opt.html3 = JSVAL_TO_BOOLEAN(v); - - if (!JS_GetProperty(cx, o, "expanded", &v) || !JSVAL_IS_BOOLEAN(v)) { - QUEUE_EXCEPTION("property 'expanded' not a Boolean"); - return JS_FALSE; - } - opt.expanded = JSVAL_TO_BOOLEAN(v); - - if (!JS_GetProperty(cx, o, "pager", &v) || !JSVAL_IS_BOOLEAN(v)) { - QUEUE_EXCEPTION("property 'pager' not a Boolean"); - return JS_FALSE; - } - opt.pager = JSVAL_TO_BOOLEAN(v); - - /* String properties */ - if (!JS_GetProperty(cx, o, "fieldSep", &v) || !JSVAL_IS_STRING(v)) { - QUEUE_EXCEPTION("property 'fieldSep' not a String"); - return JS_FALSE; - } - if ((opt.fieldSep = JS_GetStringBytes(JSVAL_TO_STRING(v))) == NULL) { - QUEUE_EXCEPTION("Internal error!"); - return JS_FALSE; - } - - if (!JS_GetProperty(cx, o, "tableOpt", &v) || !JSVAL_IS_STRING(v)) { - QUEUE_EXCEPTION("property 'tableOpt' not a String"); - return JS_FALSE; - } - if ((opt.tableOpt = JS_GetStringBytes(JSVAL_TO_STRING(v))) == NULL) { - QUEUE_EXCEPTION("Internal error!"); - return JS_FALSE; - } - - if (!JS_GetProperty(cx, o, "caption", &v) || !JSVAL_IS_STRING(v)) { - QUEUE_EXCEPTION("property 'caption' not a String"); - return JS_FALSE; - } - if ((opt.caption = JS_GetStringBytes(JSVAL_TO_STRING(v))) == NULL) { - QUEUE_EXCEPTION("Internal error!"); - return JS_FALSE; - } - - /* String Array property */ - if (!JS_GetProperty(cx, o, "fieldName", &v) || !JSVAL_IS_OBJECT(v)) { - QUEUE_EXCEPTION("property 'fieldName' not an Array"); - return JS_FALSE; - } - o = JSVAL_TO_OBJECT(v); - if (!JS_IsArrayObject(cx, o)) { - QUEUE_EXCEPTION("property 'fieldName' not an Array"); - return JS_FALSE; - } - { - jsint len; - - if (!JS_GetArrayLength(cx, o, &len)) { - QUEUE_EXCEPTION("Internal error!"); - return JS_FALSE; - } - if (len == 0) - opt.fieldName = NULL; - else { - if (param_grow(len) == -1) { - QUEUE_EXCEPTION("Out of memory!"); - return JS_FALSE; - } - opt.fieldName = param_values; - } - } - if (opt.fieldName != NULL) { - jsval id; - int i, i2; - char str[256]; - - if ((a = JS_Enumerate(cx, o)) == NULL) { - QUEUE_EXCEPTION("Internal error!"); - return JS_FALSE; - } - for (i2 = i = 0; i < a->length; i++) { - JS_IdToValue(cx, a->vector[i], &id); - if (JS_LookupElement(cx, o, JSVAL_TO_INT(id), &v) && - !JSVAL_IS_VOID(v)) { - if (JSVAL_IS_NULL(v) || !JSVAL_IS_STRING(v)) { - (void) snprintf(str, 255, - "fieldName Array element %d " - "not a String", i2); - QUEUE_EXCEPTION(str); - goto err; - } - if ((opt.fieldName[i2++] = JS_GetStringBytes( - JSVAL_TO_STRING(v))) == NULL) { - QUEUE_EXCEPTION("Internal error!"); - goto err; - } - } - } - JS_DestroyIdArray(cx, a); - a = NULL; - opt.fieldName[i2] = NULL; - } - - /* Finally call our function */ - PQprint(fh, pgr, &opt); - - return JS_TRUE; - -err: - if (a != NULL) - JS_DestroyIdArray(cx, a); - - 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; - - *rval = OBJECT_TO_JSVAL(NULL); - - if (argc != 0) { - QUEUE_EXCEPTION("Function allows no arguments"); - return JS_FALSE; - } - - if ((pgc = JS_GetInstancePrivate(cx, obj, &pgcancel_class, NULL)) - != NULL) { - PQfreeCancel(pgc); - (void) JS_SetPrivate(cx, obj, 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; - int r; - - 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]); - if ((str = JS_GetStringBytes(s)) == NULL) { - QUEUE_EXCEPTION("Internal error!"); - goto err; - } - len = JS_GetStringLength(s); - - r = PQcancel(pgc, str, len); - *rval = INT_TO_JSVAL(r); - - return JS_TRUE; - -err: - *rval = OBJECT_TO_JSVAL(NULL); - - return JS_FALSE; -} - - -/* - * PGPrintOpt object control - */ -static JSObject * -js_InitPGPrintOptClass(JSContext *cx, JSObject *obj) -{ - JSObject *proto; - JSString *str; - JSObject *array; - - if ((proto = JS_InitClass(cx, obj, NULL, &pgprintopt_class, - pgprintopt_constructor, 0, NULL, NULL, NULL, NULL)) == NULL) - return NULL; - - /* - * Add default properties to this object's prototype - */ - if (!JS_DefineProperty(cx, proto, "header", BOOLEAN_TO_JSVAL(JS_TRUE), - NULL, NULL, JSPROP_ENUMERATE | JSPROP_PERMANENT) || - !JS_DefineProperty(cx, proto, "align", BOOLEAN_TO_JSVAL(JS_TRUE), - NULL, NULL, JSPROP_ENUMERATE | JSPROP_PERMANENT) || - !JS_DefineProperty(cx, proto, "standard", - BOOLEAN_TO_JSVAL(JS_FALSE), NULL, NULL, - JSPROP_ENUMERATE | JSPROP_PERMANENT) || - !JS_DefineProperty(cx, proto, "html3", BOOLEAN_TO_JSVAL(JS_FALSE), - NULL, NULL, JSPROP_ENUMERATE | JSPROP_PERMANENT) || - !JS_DefineProperty(cx, proto, "expanded", - BOOLEAN_TO_JSVAL(JS_FALSE), NULL, NULL, - JSPROP_ENUMERATE | JSPROP_PERMANENT) || - !JS_DefineProperty(cx, proto, "pager", BOOLEAN_TO_JSVAL(JS_FALSE), - NULL, NULL, JSPROP_ENUMERATE | JSPROP_PERMANENT)) - return NULL; - - if ((str = JS_NewStringCopyZ(cx, " | ")) == NULL) - return NULL; - if (!JS_DefineProperty(cx, proto, "fieldSep", STRING_TO_JSVAL(str), - NULL, NULL, JSPROP_ENUMERATE | JSPROP_PERMANENT)) - return NULL; - - if ((str = JS_NewStringCopyZ(cx, "")) == NULL) - return NULL; - if (!JS_DefineProperty(cx, proto, "tableOpt", STRING_TO_JSVAL(str), - NULL, NULL, JSPROP_ENUMERATE | JSPROP_PERMANENT)) - return NULL; - - if ((str = JS_NewStringCopyZ(cx, "")) == NULL) - return NULL; - if (!JS_DefineProperty(cx, proto, "caption", STRING_TO_JSVAL(str), - NULL, NULL, JSPROP_ENUMERATE | JSPROP_PERMANENT)) - return NULL; - - if ((array = JS_NewArrayObject(cx, 0, NULL)) == NULL) - return NULL; - if (!JS_DefineProperty(cx, proto, "fieldName", OBJECT_TO_JSVAL(array), - NULL, NULL, JSPROP_ENUMERATE | JSPROP_PERMANENT)) - return NULL; - - return proto; -} - -static JSBool -pgprintopt_constructor(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - - if (!JS_IsConstructing(cx)) { - QUEUE_EXCEPTION("PGPrintOpt constuctor called as a function"); - *rval = OBJECT_TO_JSVAL(NULL); - return JS_FALSE; - } - - return JS_TRUE; -} 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/classes/js_syslog.c b/mmsoftware/js/classes/js_syslog.c deleted file mode 100644 index 16c13b4..0000000 --- a/mmsoftware/js/classes/js_syslog.c +++ /dev/null @@ -1,340 +0,0 @@ -/* $Id: js_syslog.c,v 1.1 2006/10/28 22:00:43 mmondor Exp $ */ - -/* - * Copyright (c) 2006, Matthew Mondor - * ALL RIGHTS RESERVED. - */ - - - -#include - -#include - -#include -#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) - - -struct property_spec { - const char *name; - int value; -}; - -#define SP(n) \ - { #n, n } - - - -/* - * Static prototypes - */ -static JSBool syslog_constructor(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static void syslog_finalize(JSContext *, JSObject *); - -static JSBool syslog_sm_open(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool syslog_sm_close(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool syslog_sm_setmask(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool syslog_sm_mask(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool syslog_sm_mask_upto(JSContext *, JSObject *, uintN, jsval *, - jsval *); -static JSBool syslog_sm_log(JSContext *, JSObject *, uintN, jsval *, - jsval *); - - - -/* - * Static globals - */ - -/* Syslog class */ -static JSClass syslog_class = { - "Syslog", 0, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, - JS_PropertyStub, JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, - syslog_finalize -}; - -/* Provided methods */ -static JSFunctionSpec syslog_smethods[] = { - { "open", syslog_sm_open, 3, 0, 0 }, - { "close", syslog_sm_close, 0, 0, 0 }, - { "setMask", syslog_sm_setmask, 1, 0, 0 }, - { "mask", syslog_sm_mask, 1, 0, 0 }, - { "maskUpto", syslog_sm_mask_upto, 1, 0, 0 }, - { "log", syslog_sm_log, 2, 0, 0 }, - { NULL, NULL, 0, 0, 0 }, -}; - -/* Static properties */ - -static struct property_spec syslog_sprops[] = { - SP(LOG_EMERG), - SP(LOG_ALERT), - SP(LOG_CRIT), - SP(LOG_ERR), - SP(LOG_WARNING), - SP(LOG_NOTICE), - SP(LOG_INFO), - SP(LOG_DEBUG), - - SP(LOG_CONS), - SP(LOG_NDELAY), - SP(LOG_PERROR), - SP(LOG_PID), - - SP(LOG_AUTH), - SP(LOG_AUTHPRIV), - SP(LOG_CRON), - SP(LOG_DAEMON), - SP(LOG_FTP), - SP(LOG_KERN), - SP(LOG_LPR), - SP(LOG_MAIL), - SP(LOG_NEWS), - SP(LOG_SYSLOG), - SP(LOG_USER), - SP(LOG_UUCP), - SP(LOG_LOCAL0), - SP(LOG_LOCAL1), - SP(LOG_LOCAL2), - SP(LOG_LOCAL3), - SP(LOG_LOCAL4), - SP(LOG_LOCAL5), - SP(LOG_LOCAL6), - SP(LOG_LOCAL7), - { NULL, 0 } -}; - - - -/* - * Syslog object control - */ - -JSObject * -js_InitSyslogClass(JSContext *cx, JSObject *obj) -{ - JSObject *proto, *ctor; - struct property_spec *sp; - - if ((proto = JS_InitClass(cx, obj, NULL, &syslog_class, - syslog_constructor, 0, NULL, NULL, NULL, syslog_smethods)) - == NULL) { - (void) fprintf(stderr, "Error initializing Syslog class\n"); - goto err; - } - - /* Create static properties. */ - if ((ctor = JS_GetConstructor(cx, proto)) == NULL) { - (void) fprintf(stderr, "Syslog: JS_GetConstructor == NULL\n"); - return NULL; - } - for (sp = syslog_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, - "Syslog: Error defining property %s\n", sp->name); - return NULL; - } - } - - return proto; - -err: - return NULL; -} - -static JSBool -syslog_constructor(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - - QUEUE_EXCEPTION("Not user instanciable"); - return JS_FALSE; -} - -static void -syslog_finalize(JSContext *cx, JSObject *obj) -{ - - /* NOOP */ -} - - - -/* Static methods */ -static JSBool -syslog_sm_open(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - char *id; - - *rval = OBJECT_TO_JSVAL(NULL); - - if (argc != 3) { - QUEUE_EXCEPTION("Wrong number of arguments"); - return JS_FALSE; - } - if (!JSVAL_IS_STRING(argv[0]) || - (id = JS_GetStringBytes(JSVAL_TO_STRING(argv[0]))) == NULL) { - QUEUE_EXCEPTION("Argument 1 not a String"); - return JS_FALSE; - } - if (!JSVAL_IS_INT(argv[1])) { - QUEUE_EXCEPTION("Argument 2 not an Integer"); - return JS_FALSE; - } - if (!JSVAL_IS_INT(argv[2])) { - QUEUE_EXCEPTION("Argument 3 not an Integer"); - return JS_FALSE; - } - - openlog(id, JSVAL_TO_INT(argv[1]), JSVAL_TO_INT(argv[2])); - - return JS_TRUE; -} - -static JSBool -syslog_sm_close(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - - *rval = OBJECT_TO_JSVAL(NULL); - - if (argc != 0) { - QUEUE_EXCEPTION("Wrong number of arguments"); - return JS_FALSE; - } - - closelog(); - - return JS_TRUE; -} - -static JSBool -syslog_sm_setmask(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - int v; - - if (argc != 1) { - QUEUE_EXCEPTION("Wrong number of arguments"); - goto err; - } - if (!JSVAL_IS_INT(argv[0])) { - QUEUE_EXCEPTION("Argument 1 not an Integer"); - goto err; - } - - v = setlogmask(JSVAL_TO_INT(argv[0])); - *rval = INT_TO_JSVAL(v); - - return JS_TRUE; - -err: - *rval = OBJECT_TO_JSVAL(NULL); - - return JS_FALSE; -} - -static JSBool -syslog_sm_mask(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - int v; - - if (argc != 1) { - QUEUE_EXCEPTION("Wrong number of arguments"); - goto err; - } - if (!JSVAL_IS_INT(argv[0])) { - QUEUE_EXCEPTION("Argument 1 not an Integer"); - goto err; - } - - v = JSVAL_TO_INT(argv[0]); - v = LOG_MASK(v); - *rval = INT_TO_JSVAL(v); - - return JS_TRUE; - -err: - *rval = OBJECT_TO_JSVAL(NULL); - - return JS_FALSE; -} - -static JSBool -syslog_sm_mask_upto(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - int v; - - if (argc != 1) { - QUEUE_EXCEPTION("Wrong number of arguments"); - goto err; - } - if (!JSVAL_IS_INT(argv[0])) { - QUEUE_EXCEPTION("Argument 1 not an Integer"); - goto err; - } - - v = JSVAL_TO_INT(argv[0]); - v = LOG_UPTO(v); - *rval = INT_TO_JSVAL(v); - - return JS_TRUE; - -err: - *rval = OBJECT_TO_JSVAL(NULL); - - return JS_FALSE; -} - -static JSBool -syslog_sm_log(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - char *str; - - *rval = OBJECT_TO_JSVAL(NULL); - - if (argc != 2) { - QUEUE_EXCEPTION("Wrong number of arguments"); - return JS_FALSE; - } - if (!JSVAL_IS_INT(argv[0])) { - QUEUE_EXCEPTION("Argument 1 not an Integer"); - return JS_FALSE; - } - if (!JSVAL_IS_STRING(argv[1]) || - (str = JS_GetStringBytes(JSVAL_TO_STRING(argv[1]))) == NULL) { - QUEUE_EXCEPTION("Argument 2 not a String"); - return JS_FALSE; - } - - syslog(JSVAL_TO_INT(argv[0]), "%s", str); - - return JS_TRUE; -} diff --git a/mmsoftware/js/classes/js_syslog.h b/mmsoftware/js/classes/js_syslog.h deleted file mode 100644 index 99cd5b8..0000000 --- a/mmsoftware/js/classes/js_syslog.h +++ /dev/null @@ -1,15 +0,0 @@ -/* $Id: js_syslog.h,v 1.1 2006/10/28 22:00:43 mmondor Exp $ */ - -/* - * Copyright (c) 2006, Matthew Mondor - * ALL RIGHTS RESERVED. - */ - -#ifndef JSSYSLOG_H -#define JSSYSLOG_H - -#include - -extern JSObject *js_InitSyslogClass(JSContext *, JSObject *); - -#endif diff --git a/mmsoftware/js/js-appserv/app/test/handle.js b/mmsoftware/js/js-appserv/app/test/handle.js deleted file mode 100644 index acaebbe..0000000 --- a/mmsoftware/js/js-appserv/app/test/handle.js +++ /dev/null @@ -1,53 +0,0 @@ -/* $Id: handle.js,v 1.1 2006/08/20 06:58:57 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. - */ - -/* - * This function handles every request, provided with session id and - * request object. - */ -function handle(req) -{ - /* XXX Only test for now */ - var o = {result: true, req: req, msg: "NOOP test"}; - fd.put(o.toSource() + "\r\n"); - - return true; -} - -/* Perform application-specific saving before connection close */ -function handle_close() -{ - /* XXX */ -} diff --git a/mmsoftware/js/js-appserv/app/test/main.js b/mmsoftware/js/js-appserv/app/test/main.js deleted file mode 100644 index 7c013d6..0000000 --- a/mmsoftware/js/js-appserv/app/test/main.js +++ /dev/null @@ -1,350 +0,0 @@ -/* $Id: main.js,v 1.2 2006/10/28 08:34:21 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. - */ - -/* - * Test application for js-appserv. - * See the mmserver2(3) man page for more details. - * - * All of these functions should be provided, although they may optionally - * be empty. - */ - -/* - * TODO: - * - js-appserv should export C->js functions to: - * - create SIDs - * - import and export single line JSON-like objects in a safe and efficient - * manner - * - Access shared memory hash tables and properties (maybe) - * - Log events - * - Create contexts, and cache/load/execute scripts within the wanted - * context - * - Probably also use some config.js - * - Define all details of the protocol, allowed request types, etc. - * - Should include ping/pong functionality for extended idle persistent - * connections - * - Should have a maximum delay where if no requests are received but - * session not yet expired, session data be saved and connection freed - * for other requests - * - Reconsider if using HTTP 1.1 with Keep-Alive would be justified. - * Despite that the protocol is lame (unidirectional, doesn't allow - * pings, awkward etc), this would permit to use simple proxy support - * already existing in some HTTPds... - * - Would be nice if we also allow logging of errors (stderr?) back to - * the main HTTPd, perhaps, like fcgi does. - * - Write/modify an HTTPd to support this protocol - * - Also provide a routes or such system - * - Provide load balancing among several js-appserv daemons, as well as - * failover - * - Add optional JS debugging support for development phases which can be - * disabled for production, possibly via a special server socket, or - * via an application for use from HTTP - * - Import lithium's js preprocessor code or write mine - * - Import lithium's web application framework or write mine - * - Implement a cooperative client and server-side rfc2945 or hmac auth - * system - * - Add authentication support for wanted HTTPd only to use the system - * - Add optional compression and encryption between the HTTPd and the - * js-appserv. If doing this, we'll need to provide read/write/flush - * wrappers to JS. - * - Add stderr to logfile option - */ - -ilibprefix = '/home/mmondor/work/mmondor/mmsoftware/js/jslib/'; -iappprefix = '/home/mmondor/work/mmondor/mmsoftware/js/js-appserv/app/test/'; -hello_timeout = 30000; -request_timeout = 180000; -database = "dbname=test"; -sid_expiration = 30; - - - -function file_read(file) -{ - var contents = ''; - var data = ''; - var fd; - - try { - fd = new FD(); - fd.open(file, FD.O_RDONLY); - for (;;) { - data = fd.read(65536); - if (data.length == 0) - break; - contents += data; - } - } catch (x) { - stderr.write(x + "\n"); - } finally { - fd.close(); - } - - return contents; -} - - - -eval(file_read(ilibprefix + "fd.js")); -eval(file_read(ilibprefix + "pgsql.js")); -eval(file_read(ilibprefix + "string.js")); -eval(file_read(ilibprefix + "json.js")); -eval(file_read(iappprefix + "session.js")); -eval(file_read(iappprefix + "handle.js")); - - - -/* - * Control for the children server processes - */ - -/* Called when a new child process is created before serving requests */ -function child_init_hook() -{ - try { - /* Set pgc globally */ - pgc = PG.connectDb(database); - } catch (x) { - stderr.write(x + "\n"); - } - sid_init(); -} - -/* Called before the child process exists (interrupted or not) */ -function child_exit_hook() -{ - sid_gc(); /* XXX Not an ideal place for this */ - sid_finish(); - pgc.finish(); -} - -/* Called upon SIGALRM signal events (recursion-protected) */ -function child_sigalrm_hook() -{ - /* XXX Handle application specific timers with this */ -} - - -/* - * Control for handling clients - */ - -/* Called if client has to be rejected for */ -function reject_handler(r, reason) -{ - var o; - - try { - fd = new FD(); - fd.binit(r.client_socket, hello_timeout, hello_timeout); - } catch (x) { - stderr.write(x + "\n"); - } - - try { - o = {result: false, msg: "Rejected", reason: reason}; - fd.put(o.toSource() + "\r\n"); - } catch (x) { - stderr.write(x + "\n"); - } -} - -/* Called before closing request connection, rejected/interrupted or not */ -function request_close_hook(r) -{ - if (SID != undefined && DATA != undefined) { - /* Call application specific save hook if any */ - if (handle_close != undefined) - handle_close(); - /* Save SID state */ - sid_save(SID, DATA); - SID = undefined; - DATA = undefined; - } -} - -/* - * Called if our application allows SIGHUP or SIGTERM to interrupt currently - * active requests, upon reception of such signal event, before closing - */ -function request_interrupt_hook(r) -{ - /* XXX */ -} - -/* Normal client request handling function */ -function request_handler(r) -{ - var line; - var o; - var session; - var request; - - try { - fd = new FD(); - fd.binit(r.client_socket, hello_timeout, hello_timeout); - } catch (x) { - stderr.write(x + "\n"); - } - - o = {result: true, msg: "Ready"}; - fd.put(o.toSource() + "\r\n"); - - /* Listen for incoming requests for that session */ - for (;;) { - if ((line = fd.breadline(16384, true)) == null) { - o = {result: false, msg: fd.berrorStr[fd.berror]}; - fd.put(o.toSource() + "\r\n"); - return; - } - try { - request = json_parse(line, 10); - if (request == undefined) - throw(json_error); - } catch (x) { - o = {result: false, msg: "Malformed request", x: x}; - fd.put(o.toSource() + "\r\n"); - return; - } - try { - /* Handle a few special system requests */ - if (request.cmd == 'sid_create') { - if (SID != undefined) { - o = {result: false, - msg: "Already serving a SID"}; - fd.put(o.toSource() + "\r\n"); - continue; - } - if ((o = sid_create(sid_expiration)) - == undefined) { - o = {result: false, - msg: "Error creating SID"}; - fd.put(o.toSource() + "\r\n"); - continue; - } - SID = o.sid; - EXPIRES = o.expires; - DATA = {}; - o = {result: true, msg: "Created", sid: SID}; - fd.btimeouts(request_timeout, request_timeout); - fd.put(o.toSource() + "\r\n"); - continue; - } else if (request.cmd == 'sid_destroy' && - request.sid != undefined) { - if (request.sid != SID) { - o = {result: false, - msg: "Not current SID"}; - fd.put(o.toSource() + "\r\n"); - continue; - } - sid_destroy(SID); - SID = undefined; - EXPIRES = undefined; - DATA = undefined; - o = {result: true, msg: "SID interrupted"}; - fd.put(o.toSource() + "\r\n"); - return; - } else if (request.cmd == 'sid_load' && - request.sid != undefined && - request.sid.length == 64) { - if (SID != undefined) { - o = {result: false, - msg: "Already serving a SID"}; - fd.put(o.toSource() + "\r\n"); - continue; - } - if ((o = sid_load(request.sid)) == undefined) { - o = {result: false, - msg: "Error loading SID data"}; - fd.put(o.toSource() + "\r\n"); - continue; - } else { - SID = request.sid; - EXPIRES = o.expires; - DATA = o.data; - o = {result: true, - msg: "SID data loaded"}; - fd.btimeouts(request_timeout, - request_timeout); - fd.put(o.toSource() + "\r\n"); - continue; - } - } else if (request.cmd == 'ping') { - o = {result: true, msg: "Pong"}; - fd.put(o.toSource() + "\r\n"); - continue; - } else if (request.cmd == 'quit') { - o = {result: true, msg: - (SID != undefined ? "SID data saved" : - "Closing")}; - fd.put(o.toSource() + "\r\n"); - return; - } - - /* - * We should only go beyond this point if serving - * a valid, unexpired SID. - */ - if (SID == undefined || DATA == undefined) { - o = {result: true, msg: "No active SID"}; - fd.put(o.toSource() + "\r\n"); - continue; - } - if (EXPIRES < Date.parse(new Date)) { - o = {result: false, msg: "SID expired"}; - fd.put(o.toSource() + "\r\n"); - return; - } - - /* Handle user application requests */ - if (!handle(request)) { - /* Destroy SID */ - if (SID != undefined) { - sid_destroy(SID); - SID = undefined; - EXPIRES = undefined; - DATA = undefined; - } - o = {result: false, command: "delete"}; - fd.put(o.toSource() + "\r\n"); - return; - } - } catch (x) { - o = {result: false, msg: "Application error", - exception: x}; - fd.put(o.toSource() + "\r\n"); - } - } -} diff --git a/mmsoftware/js/js-appserv/app/test/session.js b/mmsoftware/js/js-appserv/app/test/session.js deleted file mode 100644 index aea1cf1..0000000 --- a/mmsoftware/js/js-appserv/app/test/session.js +++ /dev/null @@ -1,249 +0,0 @@ -/* $Id: session.js,v 1.1 2006/08/20 06:58:57 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. - */ - -/* - * Code to manage sessions and session-specific persistent data - * - * session table should be as follows: - * - * CREATE TABLE session ( - * sid bytea NOT NULL, - * expires timestamp NOT NULL, - * valid boolean NOT NULL DEFAULT true, - * busy boolean NOT NULL DEFAULT true, - * data text NOT NULL DEFAULT '({})', - * PRIMARY KEY (sid) - * ); - * - * sid consists of a 64-bytes case-sensitive ASCII session ID. - * expires specifies the time at which this sid stops being valid. - * valid specifies if this sid is still valid, to invalidate before - * expiration, such as when logging out. - * busy specifies that this sid is currently being served by a - * process, useful when using distributed appserv on multiple - * boxes. - * data contains a string, resulting of DATA.toSource(), to hold - * the user session-specific persistent data. - */ - - - -var SID = undefined; -var DATA = {}; - - - -/* - * Application initialization - */ -function sid_init() -{ - var pgr; - - try { - sid_rnd = new FD(); - sid_rnd.open('/dev/urandom', FD.O_RDONLY); - } catch (x) { - /* XXX */ - } - - /* valid = true, busy = true */ - pgr = pgc.prepare("sid_create", - "INSERT INTO session (sid, expires) " + - "VALUES($1, 'now'::timestamp + $2)", - 2, null); - pgr.clear(); - - pgr = pgc.prepare("sid_load1", - "UPDATE session SET busy = true WHERE sid = $1 " + - "AND expires >= 'now'::timestamp " + - "AND valid = true AND busy = false", - 1, null); - pgr.clear(); - pgr = pgc.prepare("sid_load2", - "SELECT expires::timestamp(0),data FROM session WHERE sid = $1", - 1, null); - pgr.clear(); - - pgr = pgc.prepare("sid_save", - "UPDATE session SET data = $2, busy = false WHERE sid = $1", - 2, null); - pgr.clear(); - - pgr = pgc.prepare("sid_destroy", - "UPDATE session SET valid = false, busy = false WHERE sid = $1", - 1, null); - pgr.clear(); - - pgr = pgc.prepare("sid_gc", - "DELETE FROM session WHERE " + - "expires < 'now'::timestamp " + - "OR valid = false", - 0, null); - pgr.clear(); - - sid_chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' + - 'abcdefghijklmnopqrstuvwxyz' + - '0123456789-_'; - sid_chars = sid_chars.split(''); -} - -/* - * Application cleanup - */ -function sid_finish() -{ - - sid_rnd.close(); -} - -/* - * Generate and create new SID, making it active (busy). - * Returns an object, containing: - * new sid - * expiration timestamp in milliseconds (to compare against current - * time in milliseconds). - */ -function sid_create(exp) -{ - var rval; - var pgr; - var sid; - var i; - var o = {}; - - /* - * XXX Should be done from C for efficiency and to avoid needing so - * many bytes off urandom(4). - */ - try { - rval = sid_rnd.read(64); - } catch (x) { - /* XXX */ - } - sid = ''; - /* This is apparently slower for some reason - for (i = 0; i < 64; i++) - sid += sid_chars.charAt(rval.charCodeAt(i) % 64); - */ - rval = rval.split(''); - for (i = 0; i < 64; i++) - sid += sid_chars[rval[i].charCodeAt() % 64]; - - o.expires = Date.parse(new Date) + (exp * 60000); - - pgr = pgc.execPrepared("sid_create", - 2, [sid, exp + 'minutes'], [64, 0], [1, 0], 0); - if (pgr.resultStatus() != PG.PGRES_COMMAND_OK) - sid = undefined; - pgr.clear(); - - if (sid == undefined) - o = undefined; - else - o.sid = sid; - - return o; -} - -/* - * Load SID if still valid not already busy, and mark it busy. - * Returns an object containing: - * the imported session-specific data - * the expiration timstamp in milliseconds (to be compared against - * current time in milliseconds). - */ -function sid_load(sid) -{ - var pgr1, pgr2; - var s; - var o = {}; - - pgr1 = pgc.execPrepared("sid_load1", 1, [sid], [64], [1], 0); - if (pgr1.resultStatus() == PG.PGRES_COMMAND_OK && - pgr1.cmdTuples() == 1) { - pgr2 = pgc.execPrepared("sid_load2", 1, [sid], [64], [1], 0); - if (pgr2.resultStatus() == PG.PGRES_TUPLES_OK && - pgr2.nTuples() == 1) { - o.expires = Date.parse(pgr2.getValue(0, 0).replace( - /-/g, '/')); - s = 'o.data = ' + pgr2.getValue(0, 1); - eval(s); - } else - o = undefined; - pgr2.clear(); - } else - o = undefined; - pgr1.clear(); - - return o; -} - -/* - * Save/close SID and unmark busy flag - */ -function sid_save(sid, o) -{ - var pgr; - var s; - - s = pgc.escapeStringConn(o.toSource()); - pgr = pgc.execPrepared("sid_save", 2, - [sid, s], [64, s.length], [1, 0], 0); - pgr.clear(); -} - -/* - * Close SID, unmark busy flag and invalidate it - */ -function sid_destroy(sid) -{ - var pgr; - - pgr = pgc.execPrepared("sid_destroy", 1, [sid], [64], [1], 0); - pgr.clear(); -} - -/* - * Delete all expired or invalid unbusy SIDs. - * To be called at spaced intervals. - */ -function sid_gc() -{ - var pgr; - - pgr = pgc.execPrepared("sid_gc", 0, null, null, null, 0); - pgr.clear(); -} diff --git a/mmsoftware/js/js-appserv/app/test/test.conf b/mmsoftware/js/js-appserv/app/test/test.conf deleted file mode 100644 index 85b7e9f..0000000 --- a/mmsoftware/js/js-appserv/app/test/test.conf +++ /dev/null @@ -1,8 +0,0 @@ -SCRIPT_PATH "/home/mmondor/work/mmondor/mmsoftware/js/js-appserv/app/test/main.js" -LISTEN_TO "0.0.0.0:8100" -PROCTITLE "test" -USER "mmondor" -GROUPS "users" -PID_PATH "/tmp/js-appserv_test.pid" -LOCK_PATH "/tmp/js-appserv_test.lock" -SHLOCKS_PATH "/tmp/js-appserv_test.lock" diff --git a/mmsoftware/js/js-appserv/doc/js-appserv-protocol.lyx b/mmsoftware/js/js-appserv/doc/js-appserv-protocol.lyx deleted file mode 100644 index e1789d5..0000000 --- a/mmsoftware/js/js-appserv/doc/js-appserv-protocol.lyx +++ /dev/null @@ -1,871 +0,0 @@ -#LyX 1.3 created this file. For more info see http://www.lyx.org/ -\lyxformat 221 -\textclass article -\language english -\inputencoding auto -\fontscheme default -\graphics default -\paperfontsize default -\spacing single -\papersize letterpaper -\paperpackage a4 -\use_geometry 0 -\use_amsmath 0 -\use_natbib 0 -\use_numerical_citations 0 -\paperorientation portrait -\secnumdepth 3 -\tocdepth 3 -\paragraph_separation indent -\defskip medskip -\quotes_language english -\quotes_times 2 -\papercolumns 1 -\papersides 1 -\paperpagestyle default - -\layout Title - -The JS-AppServ Protocol (JSASP) -\layout Author - -By Matthew Mondor -\layout Abstract - -The FastCGI protocol, although bearing significant advantages over plain - old CGI, and even often over HTTP-specific APIs, was considered an overly - complex protocol after an in-depth study. - Using binary format headers would not have been a problem by itself, but - representing multiple-octets words as separate octet fields instead of - using a big-endian representation, as well as using multiple header structure - types depending on the size of the data to be processed, instead of a fixed - sized word of the maximum needed size, seemed bad design. -\layout Abstract - -Moreover, although this could be for the sake of flexibility, the numerous - ways to design server-side applications with FastCGI also can lead to problems. - Ultimately, the use of FastCGI appears most efficient with a heavily threaded - language such as Java, which language and threaded methodology have their - own set of problems. - Having the HTTPd launch application-server-side processes was also considered - unadequate. -\layout Abstract - -This protocol is specifically designed to work on unix systems using an - independent, persistent process per user session, along with long-lasting - bidirectional socket connections between those persistent processes and - the HTTP daemon which initiates the requests. - This document describes the very details of the system. - For convenience, the -\noun on -JSON -\noun default - ( -\noun on -JavaScript Object Notation -\noun default -) is used to transmit requests and responses. -\layout Abstract - -Disdadventages exist; For instance this system is not compatible with the - existing CGI or FastCGI protocols. - Moreover, although parts could be re-implemented to support other languages, - the system was specifically made for use with the SpiderMonkey JavaScript - engine. - However, a provided compatible HTTPd is included as part of the system, - which is especially tailored to this model. -\layout Abstract -\pagebreak_top - -\begin_inset LatexCommand \tableofcontents{} - -\end_inset - - -\layout Section -\pagebreak_top -HTTPd specification -\layout Subsection - -Model -\layout Standard - -Within this system, the HTTP server model is simple and only requires a - single process. - Non-blocking I/O is used and the server merely serves as a proxy between - the js-appserv servers and the remote HTTP clients. -\layout Standard - -This service should be as efficient as possible, since it must avoid becoming - a main bottleneck. - It should ideally be written in C and care should be taken to ensure that - no memory leaks or security problems are found. - Nevertheless, a re-spawner process could restart this service whenever - it dies or locks, if this situation ever was to occur. - If it is stable enough, the active session IDs and associated information - can be kept in RAM instead of having to be stored into a database. - It then remains up to the dynamic application to, as necessary, save database - information associated with this state if needed and to retreive it, etc. -\layout Standard - -Optionally, the HTTPd can remember the client IP address associated with - the create SIDs to only keep accepting requests for that SID from that - very address, depending on the nature of the application. -\layout Subsection - -Operations -\layout Standard - -Here are the steps performed by the HTTP server when it obtains a request - from a user: -\layout Itemize - -Verification weither the vhost/path combination was defined by the administrator - as being handled by a particular js-appserv application. - If not, serve static pages as requested if they exist and are accessible. -\layout Itemize - -If path corresponds to an application and that the user did not provide - a unique session ID cookie which is still considered valid, generate a - new session ID for the user and send along an HTTP cookie to the client, - redirecting it to the same URL it requested with a few seconds of delay. -\layout Itemize - -If path corresponds to an application and that the client provides us a - unique session ID which still is valid, verify if there already was an - established socket connection for this particular client/application pair. - If so, direct request to that application. - Otherwise, attempt to establish a socket connection to an application, - and then forward the request there. - The connection to the application remains open to serve further requests. -\layout Itemize - -In the event where the session ID expires, or that the application initiates - a command to discard the session ID, the corresponding socket connection - to the application is closed and the entry for the session data is deleted. -\layout Itemize - -In the event where the connection to an application is lost unexpectedly - or as the result of an inactivity timeout and that the session did not - expire, the session is marked as no longer having a connection, so that - it can be re-established on the next session. - It becomes the responsibility of the application to gracefully retrieve - the persistent session associated data as needed so that a session may - still resume normally. -\layout Subsection - -Current implementation -\layout Standard - - -\emph on -XXX -\layout Section - -Application (JS-AppServ) specification -\layout Subsection - -Model -\layout Standard - -The server uses a multi-processes model, along with a pool of pre-forked - processes for performance (Apache 1.3.x style). - Basically, there is a master controller process, along with a number of - children processes listening on the specified bound interface(s) and port(s), - serialization being used among them. -\layout Standard - -These processes expect lenghty persistent connections originating from the - HTTP server, and generally survive after disconnection so that multiple - connections may be served without needing a process to be restarted everytime. - However, they can be configured to recycle after a number of connections - were served, in order to avoid long term resources leaks. - It is also possible for the process to exit as the result of an -\emph on -execve(2) -\emph default - call being completed in the same process, in which case it immediately - gets replaced by a new process. -\layout Standard - -This model simplifies application design, avoiding the requirement of reentrant, - thread-safe, non-blocking and leakless code to be used, which can require - a considerable time to properly write and debug. - Moreover, it provides more security than using a single process, avoiding - a crash or exploit from propagating to the whole application. - Using a multi-processes model also allows additional tricks such as privilege - separation if the application requires this. -\layout Standard - -js-appserv applications can run remotely from the HTTPd, or locally. - Moreover, it may run under other user credentials. - It can also run into a -\emph on -chroot(2) -\emph default - environment, sandboxed using -\emph on -systrace(1) -\emph default -, etc. - Simple authentication is performed between the HTTPd and the js-appserv - daemons when the connections are initiated, in order to prevent unwanted - local or remote users from accessing the service. - -\emph on -Under local unix domain sockets, it is possible to authenticate using AF_LOCAL - ancillary data to determine that only the allowed user(s) can connect (XXX - to be implemented). -\layout Standard - -Just like with FastCGI or other proxying distribution methods, it is still - important for the administrator to ensure that as few as possible entry - points are allowed. - This can be done by making the applications listen to an interface/port - combination to which only the HTTP server can connect, through a route - on which sniffing is considered impractical. -\layout Standard - - -\emph on -Optional data stream compression and encryption between the server and the - application will be provided, using a proprietary private key cryptography - method which is simpler although more efficient than SSL (XXX to be implemented -). - -\emph default - Care is taken to not disclose the password over the connection and to not - reuse existing encryption session keys. - There however is no guarentee whatsoever as to the security of the system. -\layout Subsection - -Operations -\layout Standard - -A typical js-appserv application child process behaves as follows: -\layout Itemize - -Setup the JS context, loading the application scripts, preprocessing them - (can be done by the parent process and inherited by the children, telling - children to recycle upon reloading when signalled with -\emph on -SIGHUP -\emph default -). - Note that in-progress connections can either be interrupted upon reception - of -\emph on -SIGHUP -\emph default - by the parent, or notified to exit whenever closing normally, depending - on the application requirements. - This is configurable via a configuration file. -\layout Itemize - -Listen for a connection until one can be obtained. -\layout Itemize - -Authenticate client/HTTPd, and drop it with an error immediately if it should - not be allowed, returning to connection waiting again. -\layout Itemize - -Connection was accepted. - Retrieve SID and previously saved session persistent data if resuming, - or create a new SID depending on the context. - If loading, persistent session saved data is retrieved as well for unexpired, - valid SIDs. -\layout Itemize - -The JS application now waits for requests and serves them. - These are expected to all be requests to serve the same user (under the - same SID). - Therefore it is possible to cache a lot of data for efficiency. -\layout Itemize - -The application can send a response to discard the session ID and then exit, - causing the connection to be dropped and process to wait again for connections. - It can also drop the connection for inactivity timeout. - When closing connection for a still valid SID, persistent data is saved - to allow resuming under another connection (which generally will be served - by another process which will need to reload this data). - -\emph on -It is also possible for the application to use application-global shared - memory to cache some SID specific data (XXX to be implemented). -\layout Itemize - -From time to time, the process will exit after serving a certain number - of persistent connections, to release dangling resources that it may be - leaking, if any. -\layout Itemize - -The application manages the session-specific unique IDs, which the HTTP - translates to a cookie, -\emph on -GET -\emph default - or -\emph on -POST -\emph default - variable. - In this protocol, these IDs (Session ID or SID) consist of 64 character - strings which each character consists of one of a list of 64 characters. - As such, these can be efficiently generated using a reliable random source - of 32-bit or 64-bit input values, to generate batches of 4 to 8 characters - at a time of the SID. - -\emph on -A noonce is also used so that it is unlikely for two identical SIDs to ever - be used (XXX to be implemented). -\layout Subsection - -Current implementation -\layout Subsubsection - -JS-AppServ core -\layout Standard - -The core of the server is writtein in C and uses the -\emph on -mmserver2(3) -\emph default - library, which easily allows support for IPv4, IPv6, -\emph on -AF_LOCAL -\emph default - and a pool of processes. - This library calls application-specific callbacks, which in turn, delegate - to JavaScript functions, allowing the application to be written into JS. - The tasks which were considered to be best written in C for efficiency - were exported as JS objects for use by the js-appserv application's scripts. -\layout Standard - -The parent process initializes a JS runtime and context, loads in the main - application script ( -\emph on -main.js -\emph default -) and executes it to create the JS functions. - This context is inherited ( -\emph on -MAP_COPY -\emph default -) to the children processes. - This means that multiple connections within a process can be served under - the same context, although because of the nature of SpiderMonkey, which - includes a good Garbage Collector, and the fact that between connections - we clear -\emph on -SID -\emph default - and -\emph on -DATA -\emph default -, this should generally not be a problem, and it increases efficiency. - -\emph on - However, hooks are provided to the scripts so that they may load and cache - scripts and then execute them under different contexts as needed (XXX to - be implemented). -\layout Subsubsection - -JS-AppServ Web applications -\layout Standard - -Once in the -\emph on -Request Server State -\emph default - ( -\begin_inset LatexCommand \ref{sub:The-request-server} - -\end_inset - -), the non-system requests are forwarded to JS functions provided by the - custom user Web application. - -\emph on -This system may dispatch operations under various contexts as necessary. -\layout Standard - -XXX -\layout Section - -The JS-AppServ protocol (JSASP) -\layout Standard - -Here we define the JSASP. -\layout Subsection - -Overview -\layout Standard - -The use of the -\noun on -JSON -\noun default - ( -\noun on -JavaScript Object Notation -\noun default -) format was chosen for its simplicity and efficiency, using a simple custom - protocol, over other approaches often using XML and HTTP, or complex proprietar -y binary formats. - A custom JSON parser was implemented in order to avoid needing to use the - insecure -\emph on -eval() -\emph default - function. -\layout Standard - -The JSASP allows the applications to run independently from the HTTP daemon - and to be distributed as necessary among various servers as application - load requirements increase. - It permits the HTTPd to create and maintain sessions and perform requests - on behalf of a session to serve a user. -\layout Standard - -These session-specific requests, similarily as with FastCGI, can be classified - under three roles, -\emph on -Responder -\emph default - (serving -\emph on -GET -\emph default -, -\emph on -POST -\emph default -, -\emph on -PUT -\emph default -), -\emph on -Authorizer -\emph default - (allowing or not access to a particular resource), and -\emph on -Filter -\emph default - (allowing to transparently filter/process files). -\layout Subsection - -Stateless commands -\layout Standard - -These commands are always available independently of the current connection - state. -\layout Subsubsection - -ping -\layout Itemize - -Request: -\layout LyX-Code - -({cmd: -\begin_inset Quotes erd -\end_inset - -ping -\begin_inset Quotes erd -\end_inset - -}) -\layout Itemize - -Response: -\layout LyX-Code - -({result:true, msg: -\begin_inset Quotes erd -\end_inset - -pong -\begin_inset Quotes erd -\end_inset - -}) -\layout Standard - -The main purpose of this command is to either verify if the connection still - works properly, and/or to prevent the connection from being closed by js-appser -v for inactivity timeout. - Oftentimes the HTTPd will simply let the connection timeout automatically, - which allows to free processes serving very idle sessions to serve more - busy sessions. -\layout Subsubsection - - -\begin_inset LatexCommand \label{sub:quit} - -\end_inset - -quit -\layout Itemize - -Request: -\layout LyX-Code - -({cmd: -\begin_inset Quotes erd -\end_inset - -quit -\begin_inset Quotes erd -\end_inset - -}) -\layout Itemize - -Response: -\layout LyX-Code - -({result:true, msg: -\begin_inset Quotes erd -\end_inset - - -\begin_inset Quotes erd -\end_inset - -}) -\layout Standard - -This command may be issued to immediately interrupt the connection at any - time. - If currently serving a SID, the session-specific data will be saved so - that the session may be resumed. -\layout Subsection - -The authentication state -\layout Standard - -Every httpd->js-appserv connection starts in the greeting state. -\layout Standard - - -\emph on -XXX -\layout Subsection - -The SID management state -\layout Standard - -After successful authentication, the HTTPd can request to resume a previous - valid SID, or request that a new SID be created. - It is also possible to verify for validity of a SID and then request a - new one if the requested one did not exist, in which case the newly created - SID should be sent again as session cookie to the user by the HTTPd. -\layout Standard - -Care is made so that once activated on a connection, no other connection - can activate the same SID, among other processes of the same js-appserv - or between multiple local or remote ones. - For this reason, SIDs are often stored into a common database (postgresql - in the case of the reference implementation). -\layout Standard - -However, if it is known that no conflict can occur (only a single js-appserv - being run for instance, and that shared memory allows the various connections - to share an in-memory hash table), an implementation could potentially - use an in-memory system instead of depending on an RDBMS. - -\layout Standard - -This state begins with the message: -\layout LyX-Code - -({result:true, msg:"Ready"}) -\layout Subsubsection - -sid_load -\layout Itemize - -Request: -\layout LyX-Code - -({cmd: -\begin_inset Quotes erd -\end_inset - -sid_load -\begin_inset Quotes erd -\end_inset - -, sid: -\begin_inset Quotes erd -\end_inset - - -\begin_inset Quotes erd -\end_inset - -}) -\layout Itemize - -Response: -\layout LyX-Code - -({result:, msg: -\begin_inset Quotes erd -\end_inset - - -\begin_inset Quotes erd -\end_inset - -}) -\layout Standard - -Request to resume serving a specific session. - This occurs after unexpected connection loss between the HTTPd and the - js-appserv application, or after disconnection due to request inactivity - timeout for the SID, where the closing phase saved persistent state information - for it. -\layout Standard - -If successful, this request causes the persistent data to be loaded back - and the specified SID to become the active one for this connection. - This request can only succeed if not currently serving a SID. -\layout Standard - -On error, -\emph on -result -\emph default - will be -\emph on -false -\emph default - in the response, but the connection is left open for the HTTPd to optionally - request creation of a new SID with the -\emph on -sid_create -\emph default - ( -\begin_inset LatexCommand \ref{sub:sid_create} - -\end_inset - -) command, although it may also issue the -\emph on -quit -\emph default - ( -\begin_inset LatexCommand \ref{sub:quit} - -\end_inset - -) command. -\layout Subsubsection - - -\begin_inset LatexCommand \label{sub:sid_create} - -\end_inset - -sid_create -\layout Itemize - -Request: -\layout LyX-Code - -({cmd: -\begin_inset Quotes erd -\end_inset - -sid_create -\begin_inset Quotes erd -\end_inset - -}) -\layout Itemize - -Response: -\layout LyX-Code - -{{result:, msg: -\begin_inset Quotes erd -\end_inset - - -\begin_inset Quotes erd -\end_inset - -[, sid: -\begin_inset Quotes erd -\end_inset - - -\begin_inset Quotes erd -\end_inset - -]}) -\layout Standard - -Requests that a new session be created. - This is only possible if not currently serving a SID. - When successful, the SID is returned and becomes the currently active SID - being served by this connection. -\layout Standard - -On error, -\emph on -result -\emph default - is -\emph on -false -\emph default - but the connection is left open. - Failure here consists of an abnormal application error. -\layout Subsubsection - -sid_destroy -\layout Itemize - -Request: -\layout LyX-Code - -({cmd: -\begin_inset Quotes erd -\end_inset - -sid_destroy -\begin_inset Quotes erd -\end_inset - -, sid: -\begin_inset Quotes erd -\end_inset - - -\begin_inset Quotes erd -\end_inset - -}) -\layout Itemize - -Response: -\layout LyX-Code - -({result:, msg: -\begin_inset Quotes erd -\end_inset - - -\begin_inset Quotes erd -\end_inset - -}) -\layout Standard - -Requests that the currently being served session be destroyed. - This is only possible if currently serving that particular SID. - In general, SID expiration or the application will decide when to destroy - the SID. - This however is provided for convenience where the request could be initiated - via the HTTPd. - The connection is immediately closed after the SID is destroyed. -\layout Subsection - - -\begin_inset LatexCommand \label{sub:The-request-server} - -\end_inset - -The request server state -\layout Standard - -In this state the application can serve various requests for a specific - SID until it expires or a request inactivity timeout occurs, causing a - switch to -\emph on -The Closing State -\emph default - ( -\begin_inset LatexCommand \ref{sub:The-closing-state} - -\end_inset - -). - These requests are only possible if currently serving a valid, unexpired - SID. - Multiple such requests are processed over the same persistent connection. -\layout Subsubsection - -responder_get -\layout Standard - - -\emph on -XXX -\layout Subsubsection - -responder_post -\layout Standard - - -\emph on -XXX -\layout Subsubsection - -responder_put -\layout Standard - - -\emph on -XXX -\layout Subsubsection - -authorizer_* -\layout Standard - - -\emph on -XXX -\layout Subsubsection - -filter_* -\layout Standard - - -\emph on -XXX Might not need SID? -\layout Subsection - - -\begin_inset LatexCommand \label{sub:The-closing-state} - -\end_inset - -The closing state -\layout Standard - -If the SID is not yet to be discarded, the application saves any persistent - data for the SID before closing, so that resuming requests for it under - a future connection be possible. - The connection is then closed. - This state information includes the -\emph on -DATA -\emph default - object which is automatically hanled, as well as optional application specific - data which might need saving (the application can provide a function to - be called at this effect). -\layout Section - -The Web Application Framework -\layout Standard - - -\emph on -XXX -\the_end diff --git a/mmsoftware/js/js-appserv/src/GNUmakefile b/mmsoftware/js/js-appserv/src/GNUmakefile deleted file mode 100644 index bcc6a0f..0000000 --- a/mmsoftware/js/js-appserv/src/GNUmakefile +++ /dev/null @@ -1,45 +0,0 @@ -# $Id: GNUmakefile,v 1.5 2006/10/28 22:02:51 mmondor Exp $ - -CFLAGS += -Wall -DDEBUG - -JS_CFLAGS := $(shell spidermonkey-config -dc) -JS_LDFLAGS := $(shell spidermonkey-config -dl) - -PG_CFLAGS := $(shell pg_config --cppflags) -PG_CFLAGS += -I$(shell pg_config --includedir) -PG_LDFLAGS := $(shell pg_config --ldflags) -PG_LDFLAGS += -lpq - -GD_CFLAGS := $(shell gdlib-config --cflags) -GD_LDFLAGS := $(shell gdlib-config --ldflags --libs) -GD_LDFLAGS += -lgd - - -MMOBJS := $(addprefix ../../../mmlib/,mmpool.o mmlog.o mmreadcfg.o \ - mmstring.o mmhash.o mmalarm.o mmheap.o mmlimitrate.o mmserver2.o) -JSOBJS := $(addprefix ../../classes/,js_gcroot.o js_errno.o js_fd.o js_file.o \ - js_pgsql.o js_dir.o js_gd.o js_syslog.o) - - -CFLAGS += $(JS_CFLAGS) $(PG_CFLAGS) $(GD_CFLAGS) -I. -I../../../mmlib \ - -I../../classes -LDFLAGS += $(JS_LDFLAGS) $(PG_LDFLAGS) $(GD_LDFLAGS) - -OBJ := js-appserv.o - - -all: js-appserv - -%.o: %.c - cc -c $(CFLAGS) -o $@ $< - -js-appserv: $(OBJ) $(MMOBJS) $(JSOBJS) - cc -o $@ $(OBJ) $(LDFLAGS) -lc $(MMOBJS) $(JSOBJS) - -install: all - install -cs -o 0 -g 0 -m 500 js-appserv /usr/local/sbin - install -c -o 0 -g 0 -m 444 js-appserv.conf.5 /usr/local/man/man5 - install -c -o 0 -g 0 -m 444 js-appserv.8 /usr/local/man/man8 - -clean: - rm -f js-appserv $(OBJ) $(MMOBJS) $(JSOBJS) diff --git a/mmsoftware/js/js-appserv/src/js-appserv.8 b/mmsoftware/js/js-appserv/src/js-appserv.8 deleted file mode 100644 index 844f954..0000000 --- a/mmsoftware/js/js-appserv/src/js-appserv.8 +++ /dev/null @@ -1,160 +0,0 @@ -.\" $Id: js-appserv.8,v 1.1 2006/08/20 06:59:00 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 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 June 24, 2006 -.Dt JS-CGID 8 -.Os mmsoftware -.Sh NAME -.Nm js-appserv -.Nd -.Xr inetd 8 -alternative for better security -.Sh SYNOPSIS -.Nm js-appserv 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 js-appserv.conf 5 -manual page for configuration file description. -.Pp -A good usage for -.Xr js-appserv 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/js-appserv -The actual server binary -.It Pa /usr/local/etc/js-appserv.conf -The default configuration file to read -.El -.Sh AUTHOR -.Nm -was written by Matthew Mondor, and is -Copyright (c) 2006, Matthew Mondor, All Rights Reserved. -.Sh SEE ALSO -.Xr js-appserv.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/js/js-appserv/src/js-appserv.c b/mmsoftware/js/js-appserv/src/js-appserv.c deleted file mode 100644 index c9e2ad0..0000000 --- a/mmsoftware/js/js-appserv/src/js-appserv.c +++ /dev/null @@ -1,1075 +0,0 @@ -/* $Id: js-appserv.c,v 1.6 2006/10/28 22:02:51 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. - */ - -/* - * TODO: - * - Create the request structure object only once per request for performance - * - 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. - * - Provide an enumeration for the reject handler reason parameter. - * - Provide a few utility functions to the application, such as context - * creation and script execution, setting up timers, etc. - * - Possibly let the application specify a setsockopt function. - * - Perhaps create the JS contexts in the children processes between - * every connection in order to avoid any possible memory leaks. - * This of course would have a performance impact. Interestingly, it is - * possible to not be necessary because of the GC, and moreover because - * we can recycle a process after serving a number of requests. - */ - - -#include -#include -#include -#include -#include -#include -#include /* strerror(3) */ -#include -#include - -#include - -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - - - -/* DEFINITIONS */ - -struct config { - char SCRIPT_PATH[256], PROCTITLE[256], SYSLOG_FACILITY[32], - PID_PATH[256], LOCK_PATH[256], SHLOCKS_PATH[256], CHROOT_DIR[256], - USER[32], GROUPS[64], LISTEN_TO[256]; - long CHILDREN_INITIAL, CHILDREN_MINSPARE, CHILDREN_MAXSPARE, - CHILDREN_MAXIMUM, CHILDREN_MAXIMUM_REQUESTS, - CHILDREN_AVERAGE_SECONDS, LISTEN_BACKLOG, - 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 USING_EXECVE, DROP_PRIVILEGES, EXIT_KILL_CHILDREN, - CHILDREN_SETSID, ADDRESS_RESOLVE, ADDRESS_RATE_LIMITS, - ADDRESS_CONCURRENCY_LIMITS, RLIMITS, SIGHUP_INTERRUPT_CHILDREN, - JS_GC_SIZE, JS_STACK_SIZE; -}; - - - -/* 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 listen_to(const char *); -static int rlimit_security(void); -static JSBool branch_callback(JSContext *, JSScript *); -static void context_error_reporter(JSContext *, const char *, - JSErrorReport *); -static JSContext *context_create(JSRuntime *, size_t, JSObject **); -static int script_reload(const char *); -static int parent_init_hook(void); -static void parent_exit_hook(void); -static void parent_sighup_hook(void); -static int child_init_hook(void); -static void child_exit_hook(void); -static void child_sigalrm_hook(void); -static JSObject *server_request_object(struct server_request *); -static void server_request_object_destroy(void); -static void request_handler(struct server_request *); -static void reject_handler(struct server_request *, int); -static void request_close_hook(struct server_request *); -static void request_interrupt_hook(struct server_request *); - - - -/* GLOBALS */ - -static struct config CONF; -static char daemon_title[64]; - -/* For mmreadcfg() */ -static carg_t cargs[] = { - {CAT_STR, CAF_NONE, 1, 255, "SCRIPT_PATH", CONF.SCRIPT_PATH}, - {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, "CHROOT_DIR", CONF.CHROOT_DIR}, - {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, 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, 1, 1024, "CHILDREN_MAXIMUM_REQUESTS", - &CONF.CHILDREN_MAXIMUM_REQUESTS}, - {CAT_VAL, CAF_NONE, 1, 1024, "CHILDREN_AVERAGE_SECONDS", - &CONF.CHILDREN_AVERAGE_SECONDS}, - {CAT_VAL, CAF_NONE, 0, 99999, "LISTEN_BACKLOG", &CONF.LISTEN_BACKLOG}, - {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_VAL, CAF_NONE, 0, 0, "JS_GC_SIZE", &CONF.JS_GC_SIZE}, - {CAT_VAL, CAF_NONE, 0, 0, "JS_STACK_SIZE", &CONF.JS_STACK_SIZE}, - {CAT_BOOL, CAF_NONE, 0, 0, "USING_EXECVE", &CONF.USING_EXECVE}, - {CAT_BOOL, CAF_NONE, 0, 0, "DROP_PRIVILEGES", &CONF.DROP_PRIVILEGES}, - {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, "RLIMITS", &CONF.RLIMITS}, - {CAT_BOOL, CAF_NONE, 0, 0, "SIGHUP_INTERRUPT_CHILDREN", - &CONF.SIGHUP_INTERRUPT_CHILDREN}, - {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} -}; - -static JSClass class_global = { - "global", 0, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, - JS_PropertyStub, JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, - JS_FinalizeStub -}; - -/* - * The JS runtime and context is global, inherited from the parent to the - * children processes. - */ -JSRuntime *p_rt = NULL; -JSBool p_map = JS_FALSE; -JSContext *p_ctx = NULL; -JSObject *p_obj = NULL; -JSScript *p_script = NULL; -jsval p_robj; - -/* - * And these are used by the children processes - */ -JSContext *c_ctx; - - - -/* TEXT */ - -int -main(int argc, char **argv) -{ - struct server_config c; - uid_t uid; - gid_t *gids; - int ngids, ch; - char *conf_file = "/usr/local/etc/js-appserv.conf"; - - /* - * Make sure that only the superuser may launch this daemon - */ -#ifdef SUPERUSER_ONLY - if (getuid() != 0) { - (void) fprintf(stderr, - "Only can be started by the superuser\n"); - syslog(LOG_NOTICE, - "User %d attempted to launch js-appserv with '%s'", - getuid(), conf_file); - exit(EXIT_FAILURE); - } -#endif - - /* - * 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: js-appserv [-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); - } - - /* - * Chroot if wanted. Note that server_start() will chdir(2), but since - * we can setup any files we may need, 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("/"); - } - - /* - * Bind network server ports. - */ - server_init(); - if (listen_to(CONF.LISTEN_TO) == -1) - exit(EXIT_FAILURE); - - /* - * Finally drop privileges. - */ - if ((CONF.DROP_PRIVILEGES && getuid() == 0) && - !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 = CONF.CHILDREN_AVERAGE_SECONDS; - c.children_maxrequests = CONF.CHILDREN_MAXIMUM_REQUESTS; - c.serialization_lock = TRUE; - c.exit_interrupt_requests = CONF.EXIT_KILL_CHILDREN; - c.children_setsid = CONF.CHILDREN_SETSID; - c.using_execve = CONF.USING_EXECVE; - c.parent_init_hook = parent_init_hook; - c.parent_exit_hook = parent_exit_hook; - c.parent_sighup_hook = parent_sighup_hook; - c.parent_timer_hook = NULL; - c.parent_timer_seconds = 0; - c.child_init_hook = child_init_hook; - c.child_exit_hook = child_exit_hook; - c.child_sigalrm_hook = child_sigalrm_hook; - 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 - */ - (void) mm_strcpy(CONF.SCRIPT_PATH, "/app/main.js"); - *CONF.PROCTITLE = '\0'; - (void) mm_strcpy(CONF.SYSLOG_FACILITY, "LOG_DAEMON"); - (void) mm_strcpy(CONF.PID_PATH, "/var/run/js-appserv.pid"); - (void) mm_strcpy(CONF.LOCK_PATH, "/var/run/js-appserv-lock"); - (void) mm_strcpy(CONF.SHLOCKS_PATH, "/var/run/js-appserv-lock"); - *CONF.CHROOT_DIR = '\0'; - (void) mm_strcpy(CONF.USER, "js-appserv"); - (void) mm_strcpy(CONF.GROUPS, "js-appserv"); - (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 = 64; - CONF.CHILDREN_MAXIMUM_REQUESTS = 5000; - CONF.CHILDREN_AVERAGE_SECONDS = 300; - CONF.LISTEN_BACKLOG = 256; - CONF.USING_EXECVE = FALSE; - CONF.DROP_PRIVILEGES = TRUE; - 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.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; - CONF.JS_GC_SIZE = 1024; - CONF.JS_STACK_SIZE = 8; - CONF.SIGHUP_INTERRUPT_CHILDREN = FALSE; - - /* - * 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, "js-appserv:%s", - CONF.PROCTITLE); - else - (void) mm_strcpy(daemon_title, "js-appserv"); - 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; - } - - /* 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; - - /* XXX Add TCP_NODELAY */ - - return 0; -} - -/* - * 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.LISTEN_BACKLOG; - 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 = reject_handler; - sc.request_interrupt_hook = request_interrupt_hook; - sc.request_close_hook = request_close_hook; - 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 JSBool -branch_callback(JSContext *ctx, JSScript *s) -{ - static int count = 0; - - if ((++count & 0x3fff) == 1) - JS_MaybeGC(ctx); - - return JS_TRUE; -} - -static void -context_error_reporter(JSContext *cx, const char *msg, JSErrorReport *rep) -{ - - syslog(LOG_NOTICE, "context_error_reporter() - %s : %s", - msg, rep->linebuf); -} - -static JSContext * -context_create(JSRuntime *rt, size_t stacksize, JSObject **obj) -{ - JSContext *ctx; - - if ((ctx = JS_NewContext(rt, stacksize)) == NULL) { - syslog(LOG_NOTICE, "context_create() - JS_NewContext()"); - goto err; - } - (void) JS_SetErrorReporter(ctx, context_error_reporter); - if ((*obj = JS_NewObject(ctx, &class_global, NULL, NULL)) == NULL) { - syslog(LOG_NOTICE, "context_create() - JS_NewObject()"); - goto err; - } - if (!JS_InitStandardClasses(ctx, *obj)) { - syslog(LOG_NOTICE, - "context_create() - JS_InitStandardClasses()"); - goto err; - } - - /* Wanted custom classes */ - if (!js_InitGCRoot(ctx)) { - syslog(LOG_NOTICE, "context_create() - js_InitGCRoot()"); - goto err; - } - if (!js_InitSyslogClass(ctx, *obj)) { - syslog(LOG_NOTICE, "context_create() - js_InitSyslogClass()"); - goto err; - } - if (!js_InitErrnoClass(ctx, *obj)) { - syslog(LOG_NOTICE, "context_create() - js_InitErrnoClass()"); - goto err; - } - if (!js_InitFDClass(ctx, *obj)) { - syslog(LOG_NOTICE, "context_create() - js_InitFDClass()"); - goto err; - } - if (!js_InitFileClass(ctx, *obj)) { - syslog(LOG_NOTICE, "context_create() - js_InitFileClass()"); - goto err; - } - if (!js_InitPGClass(ctx, *obj)) { - syslog(LOG_NOTICE, "context_create() - js_InitPGClass()"); - goto err; - } - if (!js_InitDirClass(ctx, *obj)) { - syslog(LOG_NOTICE, "context_create() - js_InitDirClass()"); - goto err; - } - if (!js_InitGDClass(ctx, *obj)) { - syslog(LOG_NOTICE, "context_create() - js_InitGDClass()"); - goto err; - } - - /* Set our GC handler callback */ - (void) JS_SetBranchCallback(ctx, branch_callback); - - return ctx; - -err: - if (ctx != NULL) { - js_DestroyGCRoot(ctx); - JS_DestroyContext(ctx); - } - - return NULL; -} - -/* - * Attempt to load, preprocess and compile the supplied script. - * This also sets up the runtime environment and the classes. - * Occurs in the parent process, but before the children are started/recycled. - * On failure, we return -1 and we leave the previously compiled script - * as-is. On success, any previously compiled script is freed and we - * return 0. - * If called with file == NULL, only frees any allocated script. - */ -static int -script_reload(const char *file) -{ - JSRuntime *rt = NULL; - JSBool map = JS_FALSE; - JSContext *ctx = NULL; - JSObject *obj, *robj; - JSScript *script = NULL; - jsval val; - - if (file == NULL) { - if (p_script != NULL && p_ctx != NULL) { - JS_DestroyScript(p_ctx, p_script); - p_script = NULL; - } - if (p_ctx != NULL) { - (void) JS_RemoveRoot(p_ctx, &p_robj); - js_DestroyGCRoot(p_ctx); - JS_DestroyContext(p_ctx); - p_ctx = NULL; - } - if (p_map) { - js_map_destroy(); - p_map = JS_FALSE; - } - if (p_rt != NULL) { - JS_DestroyRuntime(p_rt); - p_rt = NULL; - } - - return 0; - } - - if ((rt = JS_NewRuntime(CONF.JS_GC_SIZE * 1024)) == NULL) { - syslog(LOG_NOTICE, "script_reload() - JS_NewRuntime()"); - goto err; - } - if (!(map = js_map_init())) { - syslog(LOG_NOTICE, "script_reload() - js_map_init()"); - goto err; - } - if ((ctx = context_create(rt, CONF.JS_STACK_SIZE * 1024, &obj)) - == NULL) { - syslog(LOG_NOTICE, "script_reload() - context_create()"); - goto err; - } - - if ((script = JS_CompileFile(ctx, obj, CONF.SCRIPT_PATH)) == NULL) { - syslog(LOG_NOTICE, "script_reload() - JS_CompileFile()"); - goto err; - } - if (!JS_ExecuteScript(ctx, obj, script, &val)) { - syslog(LOG_NOTICE, "script_reload() - JS_ExecuteScript()"); - goto err; - } - - /* - * Create a rooted object which can be used to store arguments objects - * sent to called functions - */ - if ((robj = JS_NewObject(ctx, NULL, NULL, NULL)) == NULL) { - syslog(LOG_NOTICE, "script_reload() - JS_NewObject()"); - goto err; - } - p_robj = OBJECT_TO_JSVAL(robj); - if (!JS_AddRoot(ctx, &p_robj)) - syslog(LOG_NOTICE, "script_reload() - JS_AddRoot()"); - - /* Trick to free previous values :) */ - (void) script_reload(NULL); - p_obj = obj; - p_script = script; - p_ctx = ctx; - p_map = map; - p_rt = rt; - - return 0; - -err: - if (script && ctx) - JS_DestroyScript(ctx, script); - if (ctx) { - js_DestroyGCRoot(ctx); - JS_DestroyContext(ctx); - } - if (map) - js_map_destroy(); - if (rt) - JS_DestroyRuntime(rt); - - syslog(LOG_NOTICE, - "script_reload() - Preserving previous script if any"); - - return -1; -} - -static int -parent_init_hook(void) -{ - - return script_reload(CONF.SCRIPT_PATH); -} - -static void -parent_exit_hook(void) -{ - - (void) script_reload(NULL); -} - -static void -parent_sighup_hook(void) -{ - - syslog(LOG_NOTICE, "Received SIGHUP, reloading JS application"); - - if (script_reload(CONF.SCRIPT_PATH) == 0) - server_recycle(CONF.SIGHUP_INTERRUPT_CHILDREN); -} - -static int -child_init_hook(void) -{ - jsval args, ret; - - if (CONF.RLIMITS && rlimit_security() == -1) - return -1; - - if (!JS_CallFunctionName(p_ctx, p_obj, "child_init_hook", 0, &args, - &ret)) { - syslog(LOG_NOTICE, - "child_init_hook() - JS_CallFunctionName()"); - return -1; - } - - return 0; -} - -static void -child_exit_hook(void) -{ - jsval args, ret; - - if (!JS_CallFunctionName(p_ctx, p_obj, "child_exit_hook", 0, &args, - &ret)) - syslog(LOG_NOTICE, - "child_exit_hook() - JS_CallFunctionName()"); -} - -static void -child_sigalrm_hook(void) -{ - jsval args, ret; - - if (!JS_CallFunctionName(p_ctx, p_obj, "child_sigalrm_hook", 0, &args, - &ret)) - syslog(LOG_NOTICE, - "child_sigalrm_hook() - JS_CallFunctionName()"); -} - -static JSObject * -server_request_object(struct server_request *r) -{ - JSObject *o; - - if ((o = JS_NewObject(p_ctx, NULL, NULL, NULL)) == NULL) { - syslog(LOG_NOTICE, "request_handler() - JS_NewObject()"); - goto err; - } - /* Root object immediately */ - if (!JS_DefineProperty(p_ctx, JSVAL_TO_OBJECT(p_robj), "args", - OBJECT_TO_JSVAL(o), NULL, NULL, JSPROP_ENUMERATE)) { - syslog(LOG_NOTICE, "request_handler() - Root"); - goto err; - } - - if (!JS_DefineProperty(p_ctx, o, "server_socket", - INT_TO_JSVAL(r->server_socket), NULL, NULL, JSPROP_ENUMERATE)) - goto err2; - if (!JS_DefineProperty(p_ctx, o, "client_socket", - INT_TO_JSVAL(r->client_socket), NULL, NULL, JSPROP_ENUMERATE)) - goto err2; - if (!JS_DefineProperty(p_ctx, o, "server_socket_type", - INT_TO_JSVAL(r->server_socket_type), NULL, NULL, - JSPROP_ENUMERATE)) - goto err2; - if (!JS_DefineProperty(p_ctx, o, "server_socket_family", - INT_TO_JSVAL(r->server_socket_family), NULL, NULL, - JSPROP_ENUMERATE)) - goto err2; - if (!JS_DefineProperty(p_ctx, o, "client_port", - INT_TO_JSVAL(r->client_port), NULL, NULL, JSPROP_ENUMERATE)) - goto err2; - if (!JS_DefineProperty(p_ctx, o, "server_socket_port", - INT_TO_JSVAL(r->server_socket_port), NULL, NULL, - JSPROP_ENUMERATE)) - goto err2; - if (!JS_DefineProperty(p_ctx, o, "server_socket_address_name", - STRING_TO_JSVAL(JS_NewStringCopyZ(p_ctx, - r->server_socket_address_name)), NULL, NULL, JSPROP_ENUMERATE)) - goto err2; - if (!JS_DefineProperty(p_ctx, o, "client_address_name", - STRING_TO_JSVAL(JS_NewStringCopyZ(p_ctx, r->client_address_name)), - NULL, NULL, JSPROP_ENUMERATE)) - goto err2; - if (!JS_DefineProperty(p_ctx, o, "client_address_hostname", - STRING_TO_JSVAL(JS_NewStringCopyZ(p_ctx, - r->client_address_hostname)), NULL, NULL, JSPROP_ENUMERATE)) - goto err2; - { - jsval val; - - /* uint32_t so might require a double JS object */ - if (!JS_NewDoubleValue(p_ctx, - (jsdouble)r->client_address_concurrency, &val)) { - syslog(LOG_NOTICE, - "server_request_object() - JS_NewDoubleValue()"); - goto err; - } - if (!JS_DefineProperty(p_ctx, o, "client_address_concurrency", - val, NULL, NULL, JSPROP_ENUMERATE)) - goto err2; - } - if (r->packet_data != NULL) { - if (!JS_DefineProperty(p_ctx, o, "packet_data", - STRING_TO_JSVAL(JS_NewStringCopyN(p_ctx, r->packet_data, - r->packet_size)), NULL, NULL, JSPROP_ENUMERATE)) - goto err2; - } - - return o; - -err2: - syslog(LOG_NOTICE, "server_request_object() - JS_DefineProperty()"); -err: - return NULL; -} - -static void -server_request_object_destroy(void) -{ - - (void) JS_DeleteProperty(p_ctx, JSVAL_TO_OBJECT(p_robj), "args"); -} - -static void -request_handler(struct server_request *r) -{ - JSObject *o; - jsval args[1], ret; - - syslog(LOG_NOTICE, "Request from [%s]", r->client_address_name); - - if ((o = server_request_object(r)) == NULL) { - syslog(LOG_NOTICE, - "request_handler() - server_request_object()"); - goto err; - } - *args = OBJECT_TO_JSVAL(o); - - if (!JS_CallFunctionName(p_ctx, p_obj, "request_handler", 1, args, - &ret)) - syslog(LOG_NOTICE, - "request_handler() - JS_CallFunctionName()"); - -err: - if (o != NULL) - server_request_object_destroy(); -} - -static void -reject_handler(struct server_request *r, int res) -{ - JSObject *o; - jsval args[2], ret; - - syslog(LOG_NOTICE, "Rejected request from [%s]", - r->client_address_name); - - if ((o = server_request_object(r)) == NULL) { - syslog(LOG_NOTICE, - "reject_handler() - server_request_object()"); - goto err; - } - args[0] = OBJECT_TO_JSVAL(o); - args[1] = INT_TO_JSVAL(res); - - if (!JS_CallFunctionName(p_ctx, p_obj, "reject_handler", 2, args, - &ret)) - syslog(LOG_NOTICE, "reject_handler() - JS_CallFunctionName()"); - -err: - if (o != NULL) - server_request_object_destroy(); -} - -static void -request_close_hook(struct server_request *r) -{ - JSObject *o; - jsval args[1], ret; - - syslog(LOG_NOTICE, "Closing for [%s]", r->client_address_name); - - if ((o = server_request_object(r)) == NULL) { - syslog(LOG_NOTICE, - "request_close_hook() - server_request_object()"); - goto err; - } - *args = OBJECT_TO_JSVAL(o); - - if (!JS_CallFunctionName(p_ctx, p_obj, "request_close_hook", 1, args, - &ret)) - syslog(LOG_NOTICE, - "request_close_hook() - JS_CallFunctionName()"); - -err: - if (o != NULL) - server_request_object_destroy(); -} - -static void -request_interrupt_hook(struct server_request *r) -{ - JSObject *o; - jsval args[1], ret; - - syslog(LOG_NOTICE, "Interrupted by SIGHUP"); - - if ((o = server_request_object(r)) == NULL) { - syslog(LOG_NOTICE, - "request_interrupt_hook() - server_request_object()"); - goto err; - } - *args = OBJECT_TO_JSVAL(o); - - if (!JS_CallFunctionName(p_ctx, p_obj, "request_interrupt_hook", 1, - args, &ret)) - syslog(LOG_NOTICE, - "request_interrupt_hook() - JS_CallFunctionName()"); - -err: - if (o != NULL) - server_request_object_destroy(); -} diff --git a/mmsoftware/js/js-appserv/src/js-appserv.conf.5 b/mmsoftware/js/js-appserv/src/js-appserv.conf.5 deleted file mode 100644 index 9049c74..0000000 --- a/mmsoftware/js/js-appserv/src/js-appserv.conf.5 +++ /dev/null @@ -1,279 +0,0 @@ -.\" $Id: js-appserv.conf.5,v 1.1 2006/08/20 06:59:00 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 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 June 24, 2006 -.Dt JS-CGID.CONF 5 -.Os mmsoftware -.Sh NAME -.Nm js-appserv.conf -.Nd -.Xr js-appserv.conf 5 -configuration file for -.Xr js-appserv 8 -.Sh DESCRIPTION -The -.Nm /usr/local/etc/js-appserv.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 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 PROCTITLE Ar "string" -This is useful if multiple services are served using -.Xr js-appserv 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 js-appserv -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 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" -XXX -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 js-appserv 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/js-appserv.lock" -PID_PATH "/var/run/js-appserv.pid" -USER "js-appserv" -GROUPS "js-appserv" -LOG_FACILITY "LOG_AUTHPRIV" -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 - -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 js-appserv -was written by Matthew Mondor, and is -Copyright (c) 2006, Matthew Mondor, All rights reserved. -.Sh FILES -.Bl -tag -width indent -offset indent -.It Pa /usr/local/etc/js-appserv.conf -This file -.It Pa /usr/local/sbin/js-appserv -The -.Xr js-appserv 8 -server binary itself. -.El -.Sh SEE ALSO -.Xr js-appserv 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/js/js-sh/app/httpd/httpd.js b/mmsoftware/js/js-sh/app/httpd/httpd.js deleted file mode 100644 index b6dcc3b..0000000 --- a/mmsoftware/js/js-sh/app/httpd/httpd.js +++ /dev/null @@ -1,2125 +0,0 @@ -/* $Id: httpd.js,v 1.53 2007/01/25 17:47:44 mmondor Exp $ */ - -/* - * Copyright (c) 2005-2006, 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 js-sh. - * - * 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: - * - For efficiency the FD C class probably should return -1 with error - * or such rather than throwing exceptions. This would also minimize the - * many try/catch ugly blocks. - * - 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. - * - Implement logging - * - Enhance the JS shell to report errors better - * - XXX There is some bug where if multiple listening sockets are used one - * eventually appears to be garbage collected! Actually, even if a single - * one, it eventually gets closed! Maybe a bug in FD where it doesn't - * properly root its objects in a GC-friendly way? - * Another possibility would be a bug in PollSet (i.e. int overflow?) - * - XXX Fix n/a bug in Location: when no Host: is provided for HTTP 1.0/1.1 - * - * Transfer states - * - file to client, must take into account client connection status. - * - client to appserv, appserver to client, must take into account both - * client and appserv connections status. - * Both states must use a transitional read/write buffer. - * - * File to client state - * 1) read(2) form file to buffer, switch to write/POLLOUT to client - * 2) write(2) to client, switch to (1) again, or stop - * 3) stop, do state change or close... - * - * Client to appserv request - * - read(2) request from client - * - Connect to appserv if required, send initial request to appserv - * - if necessary read result from appserv - * 1) read(2) from client, filling buffer, switch to write/POLLOUT to appserv - * 2) write(2) to appserv, switch to (1) again, or stop - * 3) read(2) from appserv, filling buffer, switch to write/POLLOUT to client - * 4) write(2) to client, switch to (3) again or stop - * - * --- - * For performance and efficiency, a state switch should be done by changing - * the process/state function reference. The less conditionals each of these - * functions can perform, the more efficient. - * When possible, it is nice to be able to chain a read/write phase into the - * same FD event processing session (such as state_http_transfer_file() does). - * Nevertheless, these require a transitional buffer. - * - * - client fd -> appserv connect state - * - file to client, must take into account client connection status - * (state_http_transfer_file()). - * - client to appserv, must take into account both connections status - * (state_http_transfer_toappserv()). client == POLLIN, appserv == POLLOUT - * - appserv to client, must take into account both connections status - * (state_http_transfer_fromappserv()). appserv == POLLIN, client == POLLOUT - */ - - - -/* - * Server identification - */ -const SERVER_VERSION = 'mmondor_js_httpd/0.2.1 (NetBSD)'; -const SERVER_CVSID = '$Id: httpd.js,v 1.53 2007/01/25 17:47:44 mmondor Exp $'; - - - -/* - * 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) { - stderr.write(x + " at file_read()\n"); - } - - try { - for (;;) { - data = fd.read(65536); - if (data.length == 0) - break; - contents += data; - } - } catch (x) { - stderr.write(x + " at file_read()\n"); - } - - fd.close(); - - return contents; -} - -try { - eval(file_read('options.js')); /* Configuration */ -} catch (x) { - stderr.write(x + " while reading options file\n"); - exit(); -} -eval(file_read(ilibprefix + 'fd.js')); -eval(file_read(ilibprefix + 'string.js')); -eval(file_read(ilibprefix + 'ml_machine.js')); -eval(file_read(ilibprefix + 'root.js')); - - - -/* - * Socket types (enumeration) - */ -const STYPE_LISTEN = 0; -const STYPE_CONNECT = 1; -const STYPE_HTTP = 2; -const STYPE_APPSERV = 3; -/* -const STYPE_LDEBUG = 4; -const STYPE_DEBUG = 5; -*/ - -/* - * Client file transfer states (enumeration) - */ -const TSTATE_READ = 0; -const TSTATE_WRITE = 1; - -/* - * General status return for state functions - */ -const PSTAT_CONTINUE = 0; -const PSTAT_CLOSE_SUCCESS = 1; -const PSTAT_CLOSE_ERROR = 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) -{ - var i; - - /* - * 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.appserv != undefined) { - if (o.appserv.paths_cache_max == undefined || - o.appserv.paths_cache_max < 0) - throw ('paths_cache_max missing from appserv or ' + - 'invalid'); - if (o.appserv.paths == undefined || o.appserv.paths.length < 1) - throw ('paths missing from appserv'); - if (o.appserv.servers == undefined || o.appserv.servers < 1) - throw ('servers missing from appserv'); - /* - * Used for faster lookups, faster than match(). - * A URL object is cached with true if matches, false if not. - * If full, the cache is destroyed and restarted. This will - * pevent cache poisoning issues which could be exploited for - * denial of service attacks. - */ - o.appserv.paths_cache = {}; - o.appserv.paths_cache_items = 0; - /* - * Used for load distribution among js-appserv(s). - * List will be sorted by average time to respond to a request - * to enhance performance resending requests to faster - * servers. If a server can no longer accept more clients, it - * also will be avoided until it's free again. - */ - for (i in o.servers) { - if (o.servers[i].host == undefined) - throw ('host missing from server object'); - if (o.servers[i].port == undefined) - throw ('port missing from server object'); - if (o.servers[i].clients == undefined) - throw ('clients missing from server object'); - o.servers[i].free_slots = o.servers[i].clients; - o.servers[i].ms_avg = 0; - o.servers[i].ms_total = 0; - o.servers[i].ms_count = 0; - /* XXX - * To be turned off for long timeing out js-appserv - * after logging the issue. - */ - o.servers[i].valid = true; - } - this.appserv = o.appserv; - } - - /* - * Create VHost object - */ - try { - this.htdocs_root = new Root(o.root); - } catch (x) { - throw (x); - } - - this.index = (o.index != undefined ? o.index : options.default_index); - this.charset = (o.charset != undefined ? o.charset : - options.default_charset); - - /* - * Link object to vhosts table - */ - if (vhosts_table[o.name] != undefined) - throw ('Conflicting vhost name: ' + o.name); - vhosts_table[o.name] = this; - - /* - * Also provide links for vhost aliases - */ - 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; - } - } -} - -VHost.prototype = { - /* - * Utility function internally used by server_ms_account() - */ - serv_ms_compare: function(a, b) - { - if (a.ms_avg < b.ms_avg) - return -1; - if (a.ms_avg > b.ms_avg) - return 1; - return 0; - }, - - /* - * Account ms taken for last request and resort priority array if - * necessary. - */ - server_ms_account: function(s, ms) - { - var o = this.appserv.servers[i]; - s.ms_total += ms; - if (++s.ms_count == this.appserv.servers.length) { - s.ms_avg = s.ms_total / s.ms_count; - s.ms_total = s.ms_count = 0; - this.appserv.servers.sort(serv_ms_compare); - } - }, - - /* - * Choose a server to next connect to, taking in consideration the - * average ms per request and the number of clients connected to each - * server. This allows to perform load balancing among them. - * This returns a reference to a server object within the servers - * array, which remains valid even when sorting the array. - * Returns undefined if no more client slots remain. - */ - server_get: function() - { - var a = this.appserv.servers; - var s = undefined; - - for (i in a) { - if (a[i].free_slots > 0 && a[i].valid) { - s = a[i]; - s.free_slots--; - break; - } - } - - if (s == undefined) - Syslog.log(Syslog.LOG_NOTICE, - "* No more free appserv slots"); - - return s; - }, - - /* - * Releases a server; updates currently connected number of clients - */ - server_put: function(s) - { - s.free_slots++; - }, - - /* - * Returns true if the specified path matches one of the appserv - * paths matching patterns, or false otherwise. Also maintains a - * cache for efficiency since an object properly lookup is faster than - * regexp matching. - * Note: path should already be free of GET variables. - */ - host_appmatch: function(p) - { - var b, o; - var appserv = this.appserv; - - b = false; - - if (appserv == undefined) - return b; - - o = appserv.paths; - - if (appserv.paths_cache[p] != undefined) - return appserv.paths_cache[p]; - - if (++appserv.paths_cache_items == appserv.paths_cache_max) { - appserv.paths_cache = {}; - appserv.paths_cache_items = 1; - } - for (i in o) { - if (p.match(o[i])) - b = true; - } - appserv.paths_cache[p] = b; - - return b; - } -} - - - -/* - * Quick SID->appserv FD lookup table - */ -var sid_table = {}; - - - -/* - * For the mime types database - */ -var mimetypes_table = {}; - - - -/* - * 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, t; - - for (i = 0; i < this.contents.length; i++) - contents += this.contents[i]; - - t = new Date(); - Syslog.log(Syslog.LOG_NOTICE, fd.client_addr + ' - - ' + - http_log_date() + ' ' + fd.http_vhost.name + ' "' + - fd.http_request + '" ' + this.code + ' ' + - contents.length + ' "' + fd.http_referer + '" "' + - fd.http_agent + '"'); - - this.headers.push('Content-Length: ' + - (size == null ? contents.length : size)); - if (options.debug == true) - stdout.write(' --> ' + this.code + ' ' + - this.headers.toSource() + "\n"); - 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"; - - if (!fd.http_old_get) - fd.bwrite(headers + contents, true, false); - else - fd.bwrite(contents, true, false); - } -} - -/* - * 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 + '


'); - - if (options.debug == true) - res.addContent(fd.httpDebug()); - - res.addContent('
' + SERVER_VERSION + '
' + SERVER_CVSID + - '
'); - - res.flush(fd, null); -} - -/* - * Redirects the HTTP client to another path. - */ -function http_redirect(fd, vpath) -{ - var res, path; - - if (!(path = fd.http_vhost.htdocs_root.valid_virtual(vpath))) { - http_error(this, 403, 'Permission Denied', - 'You do not have the permission to access this ' + - 'resource.'); - return; - } - - res = new HTTPReply(301, 'Moved Permanently', - 'text/html; charset=' + options.default_charset); - res.addNoCacheHeaders(); - - res.addHeader('Location: http://' + fd.http_host + path.virtual); - res.addContent('301 Moved Permanently' + - '

301 Moved Permanently

The document was ' + - 'permanently movedhere.

'); - - res.flush(fd, null); -} - - -function http_log_init() -{ - var i; - - /* XXX Make facility configurable */ - Syslog.open('js-httpd', Syslog.LOG_PID | Syslog.LOG_NDELAY, - Syslog.LOG_AUTH); - - http_log_months = [ - 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', - 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec' ]; - - http_log_digits = []; - for (i = 0; i < 60; i++) { - if (i < 10) - http_log_digits[i] = '0' + i; - else - http_log_digits[i] = i; - } - - http_log_zone = (new Date).getTimezoneOffset() / 60; - if (http_log_zone > 0) { - if (http_log_zone < 10) - http_log_zone = '0' + http_log_zone; - http_log_zone = '+' + http_log_zone; - } else { - http_log_zone = http_log_zone.toString(); - if (http_log_zone.length < 3) - http_log_zone = '-0' + http_log_zone.charAt(1); - } - http_log_zone += '00'; -} - -function http_log_date() -{ - var t = new Date(); - var s = '[' + t.getDate() + '/' + http_log_months[t.getMonth()] + '/' + - (1900 + t.getYear()) + ':' + http_log_digits[t.getHours()] + ':' + - http_log_digits[t.getMinutes()] + ':' + - http_log_digits[t.getSeconds()] + ' ' + - http_log_zone + ']'; - - return s; -} - - -/* - * 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. - */ - -/* -function state_debug_read(time) -{ - var data; - - while ((data = this.breadline(255, false)) != null) { - if (data.length > 0) { - this.bwrite('you typed: "' + data + '"' + "\n", - true, false); - } else - break; - } - if (data == null) { - if (this.berror != this.BEAGAIN) { - stderr.write(this.berrorStr[this.berror] + - " at state_debug_read()\n"); - return PSTAT_CLOSE_SUCCESS; - } - } - - return PSTAT_CONTINUE; -} -*/ - -/* - * Reset FD to a consistent known state, to use after accept(2). - * To be passed current time in seconds since epoch. - */ -FD.prototype.state_http_init = function(time) -{ - /* For differenciation after poll(2) */ - this.type = STYPE_HTTP; - this.stat = PSTAT_CONTINUE; - - /* Empty request */ - this.query_data = []; - this.query_data_length = 0; - - /* - * 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.state = state_http_query_get; - /* Initial input timeout */ - this.updateTimeout(time); - - /* - * 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. - */ -} - -/* - * Handles request query processing, to be assigned to FD.state() while in - * the request query state. This then invokes parseQuery() which may - * either send an HTTP response and/or cause a switch to another state. - */ -function state_http_query_get(time) -{ - var done = false; - var stat = PSTAT_CONTINUE; - var data, w; - - /* - * Read lines buffering, until a maximum of options.max_query_size - * length. Close connection on error. Upon receiving a terminating - * empty line, or a two fields old-style GET request as the first - * line, proceed with the query. - * - * If continueing, parse query to modify state accordingly, and let - * the parsing method decide if we'll close the connection. - */ - while ((data = this.breadline(options.max_query_size - - this.query_data_length, false)) != null) { - this.updateTimeout(time); - if (data.length > 0) { - this.query_data.push(data); - this.query_data_length += data.length + 2; - if (this.query_data.length == 1) { - w = data.split(' '); - if (w.length == 2 && w[0] == 'GET') { - /* Old-style GET */ - done = true; - break; - } - } - } else { - /* End of query */ - done = true; - break; - } - } - if (data == null) { - /* Error */ - if (this.berror != this.BEAGAIN) { - /* XXX */ Syslog.log(Syslog.LOG_NOTICE, - '* ' + this.berrorStr[this.berror]); - if (this.berror == this.BTOOLONG) - http_error(this, 413, - 'Request Entity Too Large', - 'Query length exceeds ' + - options.max_query_size + ' bytes.'); - return PSTAT_CLOSE_SUCCESS; - } - /* Must poll */ - return PSTAT_CONTINUE; - } - - if (done) - stat = this.parseQuery(time); - - return stat; -} - -function state_http_post_get(time) -{ - var stat = PSTAT_CONTINUE; - var len; - var data; - - if (this.post_data.length < this.http_content_length) { - len = this.http_content_length - this.post_data.length; - if ((data = this.bread(len, false)) == null) { - if (this.berror != this.BEAGAIN) { - /* XXX */ Syslog.log(Syslog.LOG_NOTICE, - '* ' + this.berrorStr[this.berror]); - return PSTAT_CLOSE_ERROR; - } - } - this.post_data += data; - this.updateTimeout(time); - } else - stat = this.parsePost(time); - - return stat; -} - -/* - * Handles transfer processing from an open file to an open client descriptor, - * to be assigned to FD.state() while in the transfer state. - */ -function state_http_transfer_file(time) -{ - var stat = PSTAT_CONTINUE; - var bufsiz; - - /* - * TSTATE_READ specifies that we're reading to the buffer from - * transfer_src, or TSTATE_WRITE that we're writing from the buffer - * to transfer_dst. 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. - */ - if (this.transfer_state == TSTATE_READ) { - /* Reading from file */ - bufsiz = this.transfer_size; - if (bufsiz > options.file_buf_size) - bufsiz = options.file_buf_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 = TSTATE_WRITE; - } catch (x) { - stat = PSTAT_CLOSE_ERROR; - } - } - if (this.transfer_state == TSTATE_WRITE) { - /* Writing to client */ - if (this.transfer_eof) - stat = PSTAT_CLOSE_SUCCESS; - else { - /* - * XXX For some reason, we now get problems with - * immediate writing mode. Dandling client and file - * descriptors remain, like if HUP was ignored. - * Very odd. - */ - if (this.bwrite(this.transfer_data, false, false) - != -1) { - this.transfer_state = TSTATE_READ; - this.updateTimeout(time); - } else { - if (this.berror == this.BEAGAIN) - this.events |= FD.POLLOUT; - else - stat = PSTAT_CLOSE_ERROR; - } - } - } - - /* Currently redundant unless we supported Keep-Alive - if (stat != PSTAT_CONTINUE) { - try { - this.transfer_src.close(); - } catch (x) {} - } - */ - - return stat; -} - -/* - * Set a few HTTP parsing defaults as part of the prototype for efficiency - * (they get inherited using COW) - */ - -/* - * 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; -} - -/* - * To be called once our client request has been read, to decide what action - * to perform and modify state accordingly. - */ -FD.prototype.parseQuery = function(time) -{ - var stat = PSTAT_CONTINUE; - var valid = false; - var evil_browser = evil_os = false; - var vhost = ''; - var lines; - var words, w; - var i; - /* - var sessid, sess; - */ - - /* Initial HTTP query state */ - this.http_protocol = ''; - this.http_method = ''; - this.http_request = undefined; - this.http_vhost = this.http_host = 'n/a'; - this.http_path = ''; - this.http_vars_get = {}; - this.http_vars_post = {}; - this.http_vars_cookies = {}; - this.http_vars_cookies_count = 0; - this.http_agent = '-'; - this.http_content_length = -1; - this.http_modified_since = undefined; - /* - this.http_sessid = undefined; - */ - this.http_range = undefined; - this.http_referer = '-'; - this.http_old_get = false; - - /* Split request lines */ - lines = this.query_data; - if (options.debug == true) - stdout.write(this.client_addr + ':' + this.client_port + ' ' + - lines.toSource()); - - /* Verify if first line has a request which seems valid */ - if (lines.length > 0) { - this.http_request = lines[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. - * The element name is case-insensitive. - */ - if (valid && !this.http_old_get) { - for (i in lines) { - words = lines[i].split(' '); - w = words[0].toLowerCase(); - if (w == 'host:' && words.length == 2) { - var i2; - - this.http_host = words[1]; - if ((i2 = words[1].indexOf(':')) != -1) - words[1] = words[1].substr(0, i2); - vhost = words[1]; - } else if (w == 'cookie:') { - words = (lines[i].substr(8)).split('='); - if (words.length == 2) { - property_add(this.http_vars_cookies, - words[0], words[1]); - this.http_vars_cookies_count++; - } - } else if (w == '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 (w == 'content-length:' && words.length == 2) - this.http_content_length = words[1].valueOf(); - else if (w == 'if-modified-since:') - this.http_modified_since = Math.round( - Date.parse(lines[i].substr(19)) / 1000); - else if (w == 'range:') - this.http_range = lines[i].substr(7); - else if (w == 'referer:') - this.http_referer = lines[i].substr(9); - } - } - - /* - * 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.appserv != undefined) { - /* - * We only allow old style GET request on static vhosts - */ - http_error(this, 505, 'HTTP Version not supported', - 'Old-Style HTTP requests unsupported by this virtual ' + - 'host since it supports dynamic content generation.'); - return PSTAT_CLOSE_SUCCESS; - } - - /* - * Filter out definitely invalid requests - */ - if (!valid) { - http_error(this, 400, 'Bad Request', - 'Your browser sent an invalid HTTP query.'); - return PSTAT_CLOSE_SUCCESS; - } - - if (this.http_content_length != -1 && - this.http_content_length > options.max_post_size) { - http_error(this, 413, 'Request Entity Too Large', - 'POST Content-Length exceeds ' + options.max_post_size + - ' bytes.'); - return PSTAT_CLOSE_SUCCESS; - } - - /* - * 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 PSTAT_CLOSE_SUCCESS; - } 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 PSTAT_CLOSE_SUCCESS; - } - - /* - * Fill in associative array with any GET-style supplied - * variables (as part of the URL). We want this even for POST method. - * XXX We might need to account for vars without content, and for %xx - * characters... - */ - 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(). - * XXX POST will need to be transfered from client to appserv - * directly. - */ - if (this.http_method == 'POST' && this.http_content_length != -1) { - /* - * Switch to state_http_post_get() state. Invoke it ourselves - * at least once to empty the read buffer if any, then go back - * to polling. If that first call is enough to satisfy the - * needed size, immediately call parsePost() which will return - * close status after invoking httpRespond(). - */ - this.post_data = ''; - this.state = state_http_post_get; - if (!this.state(time)) - return PSTAT_CONTINUE; - if (this.post_data.length == this.http_content_length) - return this.parsePost(time); - /* Go back to polling under state_http_post_get() state */ - return PSTAT_CONTINUE; - } - - /* XXX Condition would always be true here at current time */ - if (stat == PSTAT_CONTINUE) - stat = this.httpRespond(time); - - return stat; -} - -/* - * 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, size, appserv_match, doc; - /* - var sess; - */ - - /* - * 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 PSTAT_CLOSE_SUCCESS; - } - - /* - * General strategy: - * 1) Verify if path matches appserver. If not, attempt to open file, - * return 404 or 403 on failure. - * On success, if it consists of a directory, apply index path, - * match again against appserver paths, close fd and forward to - * appserver also if so. If not, close dir fd and attempt to open - * again after applying index. On failure, return 404 or 403. - * 2) If file successfully opened, upload it to client. - * 3) If to forward request to appserver, perform the following: - * - * XXX Rewrite this - * 1) Verify if session cookie provided. If not, invoke appserver - * to create a new session and send new session cookie to client, - * redirecting it to the same path after a few seconds. Record - * SID->appservfd but close connection to avoid creating too many - * long connections for people providing invalid or no cookie. - * This SID should be considered very temporary at this time and - * we should ideally tell the appserver to destroy it if no - * first request is made for it until some little time. - * 2) If we get a client cookie, verify that we know about it. - * If not, proceed like for (1) again. If necessary, reopen - * link to appserver. Then forward request to the server, and - * send back results from it to the client. Upon loss of a - * connection, we can set again an expiry (although longer) for the - * SID before asking appserv to destroy it... - * XXX Hmm actually to test if user supports cookies initially, we - * should probably simply send some temporary cookie so that we do not - * need to create a new SID unless we know client is likely to use it. - * Otherwise we would be creating a number of SIDs for nothing. - * Moreover, even for POST processing, which happens before, cookie - * must be existing. - */ - - if (!(appserv_match = this.http_vhost.host_appmatch(path.virtual))) { - - 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 PSTAT_CLOSE_SUCCESS; - } - - /* Directory? */ - if ((st.st_mode & FD.S_IFDIR) != 0) { - var error = false; - var redirect = true; - - /* - * Refit URL to configured index, - * check if under appserver path and if so process - * accordingly, else check if there's a file under it. - */ - try { - fd.close(); - } catch (x) {} - if (this.http_vhost.index.charAt(0) == '/') { - /* Recompute since index is fullpath */ - if (!(path = this.http_vhost.htdocs_root. - valid_virtual(this.http_vhost.index))) - error = true; - } else { - /* Append index path and retry */ - path.real += '/' + this.http_vhost.index; - path.virtual += '/' + this.http_vhost.index; - } - - /* - * Verify again if new index matches an appserv path - */ - if (!error) { - if (!(appserv_match = this.http_vhost. - host_appmatch(path.virtual))) { - - /* - * Attempt to reopen static file. - * On success, close it and redirect - * client to it. - */ - try { - fd.open(path.real, - FD.O_RDONLY); - fd.close(); - http_redirect(this, - path.virtual); - return PSTAT_CLOSE_SUCCESS; - } 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 PSTAT_CLOSE_SUCCESS; - } else - this.http_path = path.virtual; - } - } - - /* - * If under appserv path, make sure client supports HTTP cookies. - * We do this by verifying that we are able to obtain a "cookie=yes" - * cookie, without which we send one to the client redirecting to the - * same URL. - */ - if (appserv_match && this.http_vars_cookies_count == 0) { - /* Ensure that client supports HTTP cookies */ - doc = new HTTPReply(200, "OK", - 'text/html; charset=' + options.default_charset); - doc.addNoCacheHeaders(); - doc.addHeader('Set-Cookie: cookies=yes; 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 PSTAT_CLOSE_SUCCESS; - } - - /* XXX */ - if (appserv_match) { - http_error(this, 500, 'Internal Server Error', - 'You have requested an application server handled path ' + - 'which cannot be processed at this time. Please try ' + - 'again later.'); - return PSTAT_CLOSE_SUCCESS; - } - - /* - * Only continue if file is a regular file - */ - if (!appserv_match && (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 PSTAT_CLOSE_SUCCESS; - } - - /* - * 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; - } - /* - * Add charset to mime type - */ - mimetype += '; charset=' + this.http_vhost.charset; - - /* - * 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 PSTAT_CLOSE_SUCCESS; - } - - /* - * 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) { - Syslog.log(Syslog.LOG_NOTICE, - '* ' + x + ' at lseek()'); - } - } - } - } - } - 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_state = TSTATE_READ; - this.transfer_src = fd; - this.transfer_dst = this; - this.transfer_eof = false; - this.transfer_data = ''; - this.state = state_http_transfer_file; - this.events = FD.POLLOUT; - - /* - * Return with PSTAT_CONTINUE, to delegate operations to - * state_http_transfer_file(). - */ - return PSTAT_CONTINUE; -} - -/* - * Triggers a connection to an appserv if possible, queueing a new FD in the - * event queue for asynchroneous connection. state_connect_appserv() will - * be called whenever the event occurs. If connection is already established - * to an appserv for this sid, sets appserv_fd and returns true immediately. - * cfd and sid are passed in a matter for state_connect_appserv() to be able - * to resume. sid can be null if a new sid is to be created, or a sid to be - * resumed. - * Returns true on success or false on error. - */ -FD.prototype.appserv_connect = function(sid, set) -{ - var s, fd; - - /* - * SID already served by a current appserv connection? - */ - if ((fd = sid_table[sid]) != undefined) { - this.appserv_fd = fd; - return true; - } - - /* - * Allocate a new appserv slot - */ - if ((s = this.vhost.server_get()) == undefined) - return false; - - /* - * Create new appserv fd and initiate connection, delegating to - * state_connect_appserv() - */ - try { - fd = new FD(); - fd.type = STYPE_CONNECT; - fd.fcntl(FD.F_SETFL, FD.O_NONBLOCK); - - fd.connect(s.host, s.port); - fd.events = FD.POLLOUT; - fd.state = state_connect_appserv; - - fd.client_fd = this; - fd.sid = sid; - fd.set = set; - fd.appserv = s; - - pollset.add(fd); - } catch (x) { - Syslog.log(Syslog.LOG_NOTICE, - '* ' + x + ' in appserv_connect()'); - return false; - } - - return true; -} - -/* - * Once a non-blocking connect(2) has been called on a new socket, it is added - * to the polling set with POLLOUT, which causes this function to be called to - * attempt to complete or cancel the connection. - * This function switches to the state_appserv_sid() on success, or causes - * the connection to be closed on error. - */ -function state_connect_appserv(time) -{ - - if (this.getsockopt(FD.SO_ERROR) != 0) { - /* - * Error. Close appserv fd, removing it from the polling set - * and send error to corresponding client fd. - */ - try { - this.close(); - } catch (x) {}; - /* - * XXX Hmm this appears ridiculous. - * We ideally shouldn't care about the http client fd here, - * but instead the client fd should remain in a state to - * continue processing once the sid it awaits for is being - * served by an appserv connection. It also should get status - * via the same system, so that it may return an error - * instead in response to the client request. - * Possibly that instead of only POLL* events, we could also - * have other types of internal events? This would still - * expect poll to return regularily, though. Maybe that we - * just need a state that does nothing until response is - * obtained, and a way for this system to "notify" that fd? - * Then we still need the client_fd, though. - * Hmm... interestingly we could mute the client fd from poll - * events setting events to 0, and put it back to POLLOUT - * causing the state function to be called again whenever it - * makes sense to. This would prevent the main loop from - * being clobbered by dummy POLLOUT events for nothing while - * the client fd waits for an appserv connection to be - * established. Remains to see if we need to awake multiple - * pending requests at the same time... Then those requests - * must also be paused whenever a connection is attempted for - * a particular sid, too, using a table for lookup. - * say... sid_connecting{} containing objects such as - * indexed arrays, holding a list of client FD objects which - * are queued waiting for an appserv to be available for that - * sid. - * XXX Then we have another potential problem: multiple - * concurrent http client requests must be able to query the - * appserv, in which case they should be serialized. In a - * similar manner, we would need to block those until they can - * gain right to send a query, which case that particular fd - * must also be blocked until answer or appserv disconnection - * takes place. - * Hence we seem to need some general purpose FD based - * semaphore system... - * XXX Let's resume this... we need states: - * WAIT_CONNECTION (per-sid, must also have create/load - * command and if create, be able to return to client the - * new SID) - * WAIT_SID (per-client) - * TRANSFER_SID (one client at a time) - */ - http_error(this.client_fd, 500, 'Internal Server Error', - 'There was an error establishing connection to an ' + - 'application server. Please try again later.'); - this.client_fd.state = state_close_success; - this.client_fd.events = FD.POLLOUT; - /* - this.set[this.index] = undefined; - this.vhost.server_put(this.appserv); - this.appserv = undefined; - */ - return PSTAT_CLOSE_ERROR; - } - - /* Success */ - this.type = STYPE_APPSERV; - this.client_fd = undefined; - this.transfer_state = TSTATE_WRITE; - /* XXX */ - - return PSTAT_CONTINUE; -} - -/* - * Upon successful connection, a switch to this state is made. - * This sends the initial sid creation or loading/resuming request to the - * server and closes on error, or sets up the sid->fd relationship on success - * for client requests to be handled through that fd for that sid later on. - */ -function state_appserv_sid(time) -{ - var data; - - if (this.transfer_state == TSTATE_WRITE) { - } else { /* TSTATE_READ */ - while ((data = this.breadline(256, false)) != null) { - } - if (data == null) { - /* Error */ - } - } -} - -/* - * Dummy function allowing to cause a wanted fd from the set to be closed. - */ -function state_close_success(time) -{ - - return PSTAT_CLOSE_SUCCESS; -} - - -FD.prototype.httpDebug = function() -{ - var table, tr, td, pre, font; - - table = new MLTag('table', true); - table.addAttr('width', '100%'); - table.addAttr('border', '1'); - - tr = new MLTag('tr', true); - 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); - - try { - 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); - font = new MLTag('font', true); - font.addAttr('size', '-3'); - pre = new MLTag('pre', true); - pre.addContent(this.query_data.toSource()); - font.addContent(pre); - td.addContent(font); - tr.addContent(td); - table.addContent(tr); - } catch (x) {} - - try { - 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); - font = new MLTag('font', true); - font.addAttr('size', '-3'); - pre = new MLTag('pre', true); - pre.addContent(this.http_vars_get.toSource()); - font.addContent(pre); - td.addContent(font); - tr.addContent(td); - table.addContent(tr); - } catch (x) {} - - try { - 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); - font = new MLTag('font', true); - font.addAttr('size', '-3'); - pre = new MLTag('pre', true); - pre.addContent(this.http_vars_post.toSource()); - font.addContent(pre); - td.addContent(font); - tr.addContent(td); - table.addContent(tr); - } catch (x) {} - - try { - 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); - font = new MLTag('font', true); - font.addAttr('size', '-3'); - pre = new MLTag('pre', true); - pre.addContent(this.http_vars_cookies.toSource()); - font.addContent(pre); - td.addContent(font); - tr.addContent(td); - table.addContent(tr); - } catch (x) {} - - try { - 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); - } catch (x) {} - - try { - 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); - } catch (x) {} - - try { - 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); - } catch (x) {} - - try { - 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); - } catch (x) {} - - return table.toStr(0); -} - -/* - * 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. - * XXX We probably need additional parsing here, i.e. to replace % - */ -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; -} - - - -/* - * Connections limits management - */ - -function CLimits(maxtotal, maxaddr) -{ - this.connections = 0; - this.addresses = {}; - this.maxtotal = maxtotal; - this.maxaddr = maxaddr; -} - -CLimits.prototype = { - add: function(fd) - { - var addr, addrcnt; - - if (this.connections >= this.maxtotal) - return false; - - addr = fd.client_addr; - if ((addrcnt = this.addresses[addr]) != undefined && - addrcnt >= this.maxaddr) - return false; - - if (addrcnt == undefined) - addrcnt = 1; - else - addrcnt++; - this.addresses[addr] = addrcnt; - this.connections++; - - return true; - }, - - remove: function(fd) - { - var addr; - - addr = fd.client_addr; - if ((--this.addresses[addr]) == 0) - delete this.addresses[addr]; - - this.connections--; - } -}; - -var climits = new CLimits(); - - - -/* - * Descriptor polling set management - */ - -function PollSet() -{ - this.count = 0; - this.min = 0; - this.set = {}; - this.locked = false; -} - -PollSet.prototype = { - add: function(fd) - { - - if (this.count > 999999) - this.count = this.min + 1; - fd.fdidx = this.count; - this.set[this.count++] = fd; - }, - - remove: function(fd) - { - - if (fd.fdidx >= this.min) - delete this.set[fd.fdidx]; - }, - - staticlock: function() - { - - if (this.locked) - throw ('PollSet already staticlock()ed'); - this.min = ++this.count; - this.locked = true; - } -}; - -var pollset = new PollSet(); - - - -/* - * Main program - */ -function main() { - var i; - /* - var sess_gc_secs = 0; - */ - var fd, e, efd, flush; - - http_log_init(); - - /* - * Populate vhosts database - */ - for (i in vhosts) { - try { - var v = new VHost(vhosts[i]); - } catch (x) { - Syslog.log(Syslog.LOG_NOTICE, - '* ' + x + ' creating VHost object'); - } - } - /* - * Attempt to link default_vhost to a VHost object - */ - if (options.default_vhost == undefined) { - Syslog.log(Syslog.LOG_NOTICE, - '* No default_vhost property in options, exiting'); - exit(); - } - if (vhosts_table[options.default_vhost.toLowerCase()] != undefined) - default_vhost = - vhosts_table[options.default_vhost.toLowerCase()]; - else { - Syslog.log(Syslog.LOG_NOTICE, - '* Default vhost ' + options.default_vhost + - ' unknown, exiting'); - 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) { - Syslog.log(Syslog.LOG_NOTICE, - '* Conflicting mime type ' + ext + - ' -> ' + i); - continue; - } - mimetypes_table[ext] = i; - } - } - - /* - * Add debugging port XXX Make this optional and allow to specify - * address and port number - */ - /* - try { - fd = new FD(); - fd.socket(FD.AF_INET, FD.SOCK_STREAM, 0); - fd.bind('127.0.0.1', 8199); - fd.setsockopt(FD.SO_REUSEADDR, 1); - fd.fcntl(FD.F_SETFL, FD.O_NONBLOCK); - fd.listen(0); - fd.events = FD.POLLIN; - fd.type = STYPE_LDEBUG; - pollset.add(fd); - } catch (x) { - stderr.write(x + " preparing debugging socket\n"); - } - */ - - /* - * Initialize server - */ - for (i in listen) { - 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.type = STYPE_LISTEN; - pollset.add(fd); - } catch (x) { - Syslog.log(Syslog.LOG_NOTICE, - '* ' + x + ' preparing listening socket'); - } - } - if (pollset.count == 0) - exit(); - - /* - * Protect all currently added descriptors to the set from removal - * or from being overridden, makig them static. - */ - pollset.staticlock(); - - /* - * Main loop - */ - for (;;) { - /* - * Determine next to expire FD event, so that we can sleep - * as long as possible. Also get rid of expired requests. - * XXX Could be in a function - */ - var cur = Date.parse(new Date) / 1000; - var exp = cur + 3600; - var old; - - for (i in pollset.set) { - if ((fd = pollset.set[i]) == undefined || - fd.type != STYPE_HTTP) - continue; - if (fd.expires <= cur) { - /* - * Request timeout expired for this - * descriptor, we must close it. - */ - Syslog.log(Syslog.LOG_NOTICE, - '- Closing expired FD'); - try { - fd.close(); - } catch (x) {} - climits.remove(fd); - pollset.remove(fd); - continue; - } - if (fd.expires < exp) - exp = fd.expires; - } - exp -= cur; - - /* - * Poll our set of descriptors for events. - * XXX We definitely should use libevent here. - * The polling set gets regenerated by the C code at every - * call, which is suboptimal. I guess that we still could use - * poll while providing C primitives to add/remove descriptors - * from the set a-la select(2). But libevent(3) is always - * superior while being able to run on various systems unlike - * BSD kqueue(2). - */ - try { - e = FD.poll(pollset.set, exp * 1000); - } catch (x) { - Syslog.log(Syslog.LOG_NOTICE, - '* ' + x + ' for poll(2)'); - 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. - * XXX We probably shouldn't do this anymore - */ - /* - 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; - efd = e[i]; - - /* XXX Make this optional */ - /* - if (efd.type == STYPE_LDEBUG && - (efd.revents & FD.POLLIN) != 0) { - try { - fd = efd.accept(); - fd.type = STYPE_DEBUG; - fd.state = state_debug_read; - pollset.add(fd); - } catch (x) { - stderr.write(x + " at accept(2)\n"); - fd.close(); - } - continue; - } - if (efd.type == STYPE_DEBUG) { - if ((efd.revents & (FD.POLLHUP | FD.POLLERR)) - != 0) - efd.stat = PSTAT_CLOSE_ERROR; - if (efd.stat == PSTAT_CONTINUE && - (efd.revents & (FD.POLLIN | FD.POLLOUT)) - != 0) { - try { - efd.stat = efd.state(cur); - } catch (x) { - efd.stat == PSTAT_CLOSE_ERROR; - } - } - if (efd.stat == PSTAT_CONTINUE) - continue; - try { - efd.close(); - } catch (x) {} - pollset.remove(efd); - continue; - } - */ - - /* - * If descriptor is a bound one, attempt to accept - * the new client connection. Continue anyway on other - * types of events for the listening descriptor. - */ - if (efd.type == STYPE_LISTEN) { - if ((efd.revents & FD.POLLIN) != 0) { - try { - fd = efd.accept(); - if (!climits.add(fd)) { - http_error(fd, 403.9, - 'Too Many Connections', - 'Your browser has exceeded its maximum ' + - 'allowed number of concurrent ' + - 'connections.'); - try { - fd.close(); - } catch (x) {} - continue; - } - /* - * Setup client's initial state and - * add FD to polling set - */ - fd.state_http_init(cur); - pollset.add(fd); - } catch (x) { - Syslog.log(Syslog.LOG_NOTICE, - '* ' + x + - ' at accept(2)'); - /* No need to close fd */ - } - } - continue; - } - - /* - * Most of the code for STYPE_HTTP and STYPE_APPSERV - * is shared. STYPE_CONNECT is transitional and - * handled as other events. - */ - - /* - * Close connection on error conditions, - * Call the FD's state function on interesting - * events, which will tell when we should drop the - * client. - */ - if ((efd.revents & (FD.POLLHUP | FD.POLLERR)) != 0 || - /* XXX poll(2) says POLLOUT | POLLHUP impossible */ - ((efd.events & FD.POLLOUT) == 1 && - (efd.revents & FD.POLLIN) == 1)) - efd.stat = PSTAT_CLOSE_ERROR; - else if (efd.stat != PSTAT_CLOSE_ERROR && - (efd.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. - * If PSTAT_CLOSE_SUCCESS, we must not - * close until we flushed everything, but - * immediately close once we do. - * Otherwise, on PSTAT_CLOSE_ERROR, - * we must close immediately without flushing. - */ - if ((flush = efd.bflushw(false)) == -1) - efd.stat = PSTAT_CLOSE_ERROR; - else if (flush == 0 && - efd.stat == PSTAT_CONTINUE) - efd.stat = efd.state(cur); - else if (flush == 1 && - efd.stat != PSTAT_CLOSE_ERROR) - continue; - } - - if (efd.stat != PSTAT_CONTINUE) { - try { - /* - * XXX This is our closing point, - * where we actually should log. - */ - efd.close(); - } catch (x) {} - if (efd.type == STYPE_APPSERV) { - /* Dismiss SID->FD table entry */ - sid_table[efd.sid] = undefined; - /* appserv object back to pool */ - efd.vhost.server_put(efd.appserv); - efd.appserv = undefined; - /* - * XXX Unlock any waiters and cause - * them to return a 500 error - */ - } - /* - * If there were any static file uploads in - * progress, close source file's descriptor - */ - if (efd.transfer_src != undefined) { - try { - efd.transfer_src.close(); - } catch (x) {} - } - climits.remove(efd); - pollset.remove(efd); - } - } - } - - /* NOTREACHED */ - err.close(); -} - -main(); -/* NOTREACHED */ -exit(0); diff --git a/mmsoftware/js/js-sh/app/httpd/options.js b/mmsoftware/js/js-sh/app/httpd/options.js deleted file mode 100644 index 2091160..0000000 --- a/mmsoftware/js/js-sh/app/httpd/options.js +++ /dev/null @@ -1,112 +0,0 @@ -/* $Id: options.js,v 1.27 2006/11/16 06:59:18 mmondor Exp $ */ - -var ilibprefix = "/home/mmondor/work/mmondor/mmsoftware/js/jslib/"; - -/* Global options */ -var options = { - debug: true, - max_connections: 64, - max_connections_addr: 8, /* 8 minimum recommended */ - max_query_size: 4096, - max_post_size: 65535, - io_timeout: 60, - file_buf_size: 65535, - default_vhost: "appserv.pulsar-zone.net", - default_mimetype: "application/octet-stream", - default_charset: "us-ascii", - default_index: "index.html", - ban_msie: false, - ban_windows: false -}; - -/* Address:port combinations to listen to */ -var listen = [ - { - address: "127.0.0.1", - port: 8180 - }, - { - address: "192.168.1.10", - port: 8180 - } -]; - -/* Simple mapping from appserv application name to port number it runs on */ -var ap = { - "test": 8100, -}; - -/* Configuration for each vhost, options may override global options */ -var vhosts = [ - /* Default virtual host */ - { - name: "appserv.pulsar-zone.net", - aliases: [ - "appserv.ginseng.xisop" - ], - root: "/home/data/jshttpd/appserv", - index: "/app/index", - appserv: { - paths_cache_max: 64, - /* URL patterns redirected to js-appserv (regexp) */ - paths: [ - "^/app/" - ], - /* Where to connect to js-appserv(s) */ - /* If more than one, load balancing is done */ - servers: [ - { - host: "localhost", - port: ap['test'], - clients: 64 - } - /* - { - host: "ginseng.xisop", - port: ap['test'], - clients: 64 - } - */ - ] - }, - charset: "iso-8859-1" - }, - { - name: "mmondor.pulsar-zone.net", - aliases: [ - "mmondor.ginseng.xisop" - ], - root: "/home/data/jshttpd/mmondor", - charset: "iso-8859-1" - } -]; - -/* Static definitions of file type -> mime type description */ -var mimetypes = { - "text/html": [ "html", "htm", "dhtml" ], - "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" ], - "image/svg+xml": [ "svg" ], - "video/mpeg": [ "mpeg", "mpg" ], - "video/quicktime": [ "mov" ], - "video/x-msvideo": [ "asf", "asx", "wmv", "avi" ] -}; diff --git a/mmsoftware/js/js-sh/app/irclog/config.js b/mmsoftware/js/js-sh/app/irclog/config.js deleted file mode 100644 index fa05521..0000000 --- a/mmsoftware/js/js-sh/app/irclog/config.js +++ /dev/null @@ -1,56 +0,0 @@ -/* $Id: config.js,v 1.5 2007/01/03 07:21:32 mmondor Exp $ */ - -/* Configuration */ -var irc_channel = '#gurumeditation'; -var irc_servers = [ - 'irc.stealth.net:6667' - /* - 'irc.freenode.net:6667' - */ - /* - 'ginseng.xisop:6667', - 'mudbug.org:6667' - */ -]; -var irc_nicknames = [ - 'nanobit', - 'e-chemical', - 'e-alchemy', - 'nick_', - 'nick__' -]; -var irc_user = 'nanobit', irc_name = 'e-chemical e-alchemy'; -var irc_reconnect_delay = 10; - -var irc_op_passwd = 'somepassword'; -var irc_voice_passwd = 'somepassword'; -var irc_quit_passwd = 'somepassword'; -var irc_msg_passwd = 'somepassword'; -var irc_ban_passwd = 'somepassword'; - -var irc_part_message = 'Farewell'; -var irc_quit_message = 'Farewell'; -var irc_auto_whois = true; -var irc_log_pings = false; -var irc_auto_voice = []; -var irc_auto_op = [ - "^mur![^@]*@smtp.uiah.fi$", - "^phadthai![^@]*@ginseng.pulsar-zone.net$", - "^cprior![^@]*@unaffiliated/cprior$", - "^Kool-Aid![^@]*@[^.]*.[^.]*.or.comcast.net$" -]; -var irc_auto_ban = []; - -/* XXX Would be more complex, but ideal -var networks = [ - { - name: "rubiks", - channels: [ - "#test" - ], - servers: [ - "ginseng.xisop:6667" - ] - } -]; -*/ diff --git a/mmsoftware/js/js-sh/app/irclog/irclog.js b/mmsoftware/js/js-sh/app/irclog/irclog.js deleted file mode 100644 index 0d95bc4..0000000 --- a/mmsoftware/js/js-sh/app/irclog/irclog.js +++ /dev/null @@ -1,469 +0,0 @@ -/* $Id: irclog.js,v 1.16 2007/01/04 05:29:38 mmondor Exp $ */ - -/* - * XXX TODO XXX - * - Use an FD instead of a File so that we may also implement timeouts. - * This is because despite TCP_KEEP_ALIVE the connection can occasionally - * remain locked for a long time with inactivity. The client should - * reconnect to a server once it reaches a configurable input timeout. - * - Write a timer class to avoid having to use popen() with /bin/sleep! - */ - -var irc_version = '$Id: irclog.js,v 1.16 2007/01/04 05:29:38 mmondor Exp $'; - -function file_read(name) -{ - var fh; - var data = ''; - - try { - fh = new File(name, 'r'); - } catch (x) { - stderr.write('file_read() - open: ' + x + "\n"); - return null; - } - - try { - for (;;) - data += fh.read(1, 16384); - } catch (x) { - if (!fh.eof()) { - stderr.write('file_read() - read: ' + x + "\n"); - data = null; - } - } - fh.close(); - - return data; -} - -eval (file_read('config.js')); - - - -/* XXX Hahaha the ugly hack! I need to write a Timer class... */ -function sleep(seconds) -{ - try { - var fh = File.popen('/bin/sleep ' + seconds, 'r'); - try { - for (;;) - fh.read(256); - } catch (x) {} - fh.close(); - } catch (x) { stderr.write(x + "\n"); } -} - -/* - * Returns an object with the address and port of a server to (re)connect to. - * Uses round-robin among the specified servers array. We also sleep during - * a few seconds to prevent connect-flooding. - */ -var last_server = 0; -function select_server() -{ - var w = irc_servers[last_server++].split(':'); - - if (last_server == irc_servers.length) - last_server = 0; - if (w[1] == undefined) - w[1] = 6667; - else - w[1] = Number(w[1]); - - last_nickname = 0; - - return { address: w[0], port: w[1] }; -} - -var last_nickname = 0; -function select_nickname() -{ - var s = irc_nicknames[last_nickname++]; - - if (last_nickname == irc_nicknames.length) - last_nickname = 0; - - irc_nickname = s; - return s; -} - -function log_init() -{ - var i; - - log_digits = []; - for (i = 0; i < 60; i++) { - if (i < 10) - log_digits[i] = '0' + i; - else - log_digits[i] = i.toString(); - } - - stdout.setvbuf(File._IOLBF, 0); -} - -function log_timestamp() -{ - var t = new Date(); - var s = t.getUTCFullYear() + log_digits[t.getUTCMonth() + 1] + - log_digits[t.getUTCDate()] + log_digits[t.getUTCHours()] + - log_digits[t.getUTCMinutes()] + log_digits[t.getUTCSeconds()] + - '-0000'; - - return s; -} - -File.prototype.getline = function() -{ - var line = this.gets(1024, true); - - /* Specifying false to gets() should strip these, but it's broken */ - line = line.substr(0, line.length - 2); - - if (!irc_log_pings && line.match(/^PING :/) != null) - return line; - - /* Log input line as-is with timestamp immediately */ - stdout.write(log_timestamp() + ' < ' + line + "\n"); - - return line; -} - -File.prototype.putline = function(line) -{ - if (!irc_log_pings && line.match(/^PONG :/) != null) { - this.write(line + "\r\n"); - return; - } - - stdout.write(log_timestamp() + ' > ' + line + "\n"); - this.write(line + "\r\n"); -} - -File.prototype.state_all = function(line) -{ - var s, w, from, to, msg; - - /* PING : */ - if (line.match(/^PING :/) != null) { - this.putline('PONG ' + line.substr(5)); - return true; - } - - /* :!@ PRIVMSG : */ - if ((s = line.match(/^:[^\b!:]*![^\b@:]*@[^\b:]* PRIVMSG [^\b:]* :/)) - != null) { - s = s.toString(); - msg = line.substr(s.length); - from = line.substr(1, line.indexOf('!') - 1); - w = line.split(' '); - to = w[2]; - if (msg.charCodeAt(0) == 0x01 && - msg.charCodeAt(msg.length - 1) == 0x01) { - /* CTCP */ - msg = msg.substr(1, msg.length - 2); - if (msg.match(/^PING [0-9]* [0-9]*$/) != null) - this.putline('NOTICE ' + from + " :\001" + - msg + "\001"); - else if (msg == 'VERSION') - this.putline('NOTICE ' + from + " :\001" + - 'VERSION ' + irc_version + "\001"); - else if (msg == 'TIME') - this.putline('NOTICE ' + from + " :\001" + - 'TIME ' + (new Date()).toUTCString() + - "\001"); - return true; - } - } - - return false; -} - -File.prototype.state_nick = function() -{ - var line, w; - - stdout.write(log_timestamp() + ' -+- Entering state_nick()' + "\n"); - - this.putline('NICK ' + select_nickname()); - for (;;) { - line = this.getline(); - - if (this.state_all(line)) - continue; - - /* : 433 * :Nick already used */ - if (line.match(/^:[^\b:]* 433 * [^\b:]* :/) != null) { - this.putline('NICK ' + select_nickname()); - continue; - } - - /* : 376 :End of MOTD */ - if (line.match(/^:[^\b:]* 376 [^\b:]* :/) != null) - break; - } - - this.state = File.prototype.state_log; - return true; -} - -File.prototype.state_log = function() -{ - var line, s, from, to, chan, msg, w, i; - - stdout.write(log_timestamp() + ' -+- Entering state_log()' + "\n"); - - for (;;) { - if (!on_channel) { - sleep(++join_attempts); - if (join_attempts > 10) - join_attempts = 10; - this.putline('JOIN ' + irc_channel); - } - - line = this.getline(); - - if (this.state_all(line)) - continue; - - /* - * XXX - * Ideally, for more security, we should detect JOIN and PART - * events which are for us but which we did not trigger. - * For instance, we want to leave automatically channels - * which an oper might have forced us into. Similarily, - * we want to rejoin in case of an unexpected PART event from - * our part. Moreover, it would be nice to also auto-detect - * further nickname changes applied to us automatically so - * that we may return to the nick state. - */ - - /* :!@ JOIN : */ - if ((s = line.match(/^:[^\b!:]*![^\b@:]*@[^\b:]* JOIN :/)) - != null) { - s = s.toString(); - msg = line.substr(s.length); - from = line.substr(1, line.indexOf('!') - 1); - if (from.toLowerCase() == irc_nickname.toLowerCase() && - msg.toLowerCase() == irc_channel.toLowerCase()) { - on_channel = true; - this.putline('TOPIC ' + irc_channel); - } else { - if (irc_auto_whois) - this.putline('WHOIS ' + from); - /* Check for auto op/voice/ban */ - w = line.split(' '); - s = w[0].substr(1); - for (i in irc_auto_voice) { - if (s.match(irc_auto_voice[i]) - != null) { - this.putline('MODE ' + - irc_channel + ' +v ' + - from); - break; - } - } - for (i in irc_auto_op) { - if (s.match(irc_auto_op[i]) - != null) { - this.putline('MODE ' + - irc_channel + ' +o ' + - from); - break; - } - } - for (i in irc_auto_ban) { - if (s.match(irc_auto_ban[i].regex) - != null) { - this.putline('MODE ' + - irc_channel + ' +b ' + - irc_auto_ban[i].mask); - this.putline('KICK ' + - irc_channel + ' ' + - from + ' :automatic ban'); - break; - } - } - } - continue; - } - - /* :!@ KICK : */ - if (line.match( - /^:[^\b!:]*![^\b@:]*@[^\b:]* KICK [^\b:]* [^\b:]* :/) - != null) { - w = line.split(' '); - chan = w[2]; - to = w[3]; - if (chan.toLowerCase() == irc_channel.toLowerCase() && - to.toLowerCase() == irc_nickname.toLowerCase()) { - on_channel = false; - join_attempts = 10; - continue; - } - } - - /* :!@ PRIVMSG : */ - if ((s = line.match( - /^:[^\b!:]*![^\b@:]*@[^\b:]* PRIVMSG [^\b:]* :/)) - != null) { - s = s.toString(); - msg = line.substr(s.length); - from = line.substr(1, line.indexOf('!') - 1); - w = line.split(' '); - to = w[2]; - if (to.toLowerCase() == irc_nickname.toLowerCase()) { - if (on_channel && - msg.match(/^!OP [^\b]* [^\b]*$/) != null) { - w = msg.split(' '); - if (w[1] == irc_op_passwd) - this.putline('MODE ' + - irc_channel + ' +o ' + - w[2]); - continue; - } else if (on_channel && - msg.match(/^!DEOP [^\b]* [^\b]*$/) - != null) { - w = msg.split(' '); - if (w[1] == irc_op_passwd) - this.putline('MODE ' + - irc_channel + ' -o ' + - w[2]); - continue; - } else if (on_channel && - msg.match(/^!VOICE [^\b]* [^\b]*$/) - != null) { - w = msg.split(' '); - if (w[1] == irc_voice_passwd) - this.putline('MODE ' + - irc_channel + ' +v ' + - w[2]); - continue; - } if (on_channel && - msg.match(/^!DEVOICE [^\b]* [^\b]*$/) - != null) { - w = msg.split(' '); - if (w[1] == irc_voice_passwd) - this.putline('MODE ' + - irc_channel + ' -v ' + - w[2]); - continue; - } else if (on_channel && - msg.match(/^!BAN [^\b]* [^\b]* [^\b]*$/) - != null) { - w = msg.split(' '); - if (w[1] == irc_ban_passwd) { - this.putline('MODE ' + - irc_channel + ' +b ' + - w[3]); - this.putline('KICK ' + - irc_channel + ' ' + - w[2] + ' :relayed ban'); - } - continue; - } else if (on_channel && - msg.match(/^!DEBAN [^\b]* [^\b]*$/) - != null) { - w = msg.split(' '); - if (w[1] == irc_ban_passwd) { - this.putline('MODE ' + - irc_channel + ' -b ' + - w[2]); - } - continue; - } else if (msg.match(/^!QUIT [^\b]*$/) - != null) { - w = msg.split(' '); - if (w[1] == irc_quit_passwd) { - should_quit = true; - break; - } - continue; - } else if ((s = msg.match( - /^!PM [^\b]* [^\b]* :/)) != null) { - s = s.toString(); - w = msg.split(' '); - if (w[1] == irc_msg_passwd) - this.putline('PRIVMSG ' + - w[2] + ' :' + - msg.substr(s.length)); - continue; - } else if (on_channel && - (s = msg.match(/^!M [^\b]* :/)) != null) { - s = s.toString(); - w = msg.split(' '); - if (w[1] == irc_msg_passwd) - this.putline('PRIVMSG ' + - irc_channel + ' :' + - msg.substr(s.length)); - continue; - } else if (on_channel && - (s = msg.match(/^!A [^\n]* :/)) != null) { - s = s.toString(); - w = msg.split(' '); - if (w[1] == irc_msg_passwd) - this.putline('PRIVMSG ' + - irc_channel + - " :\001ACTION " + - msg.substr(s.length) + - "\001"); - continue; - } - } - } - } - - return false; -} - -function main() -{ - var server; - var fd; - var fh; - var line; - - log_init(); - should_quit = false; - - for (;;) { try { - on_channel = false; - join_attempts = 0; - - server = select_server(); - stdout.write(log_timestamp() + ' -+- Connecting to ' + - server.address + ':' + server.port + "\n"); - fd = new FD(); - fd.socket(FD.AF_INET, FD.SOCK_STREAM, 0); - fd.connect(server.address, server.port); - fd.setsockopt(FD.SO_KEEPALIVE, 1); - fh = new File(fd.fd, 'r+'); - fh.setvbuf(File._IOLBF, 0); - fh.putline('USER ' + irc_user + ' hostname servername :' + - irc_name); - fh.state = File.prototype.state_nick; - for (;;) { - if (!fh.state()) - break; - } - if (on_channel) - fh.putline('PART ' + irc_channel + ' :' + - irc_part_message); - fh.putline('QUIT :' + irc_quit_message); - } catch (x) { - stdout.write(log_timestamp() + ' -+- Error: ' + x + "\n"); - } - try { - fh.close(); - fd.close(); - } catch (x) {} - stdout.write(log_timestamp() + ' -+- Disconnected from ' + - server.address + ':' + server.port + "\n"); - if (should_quit) - break; - sleep(irc_reconnect_delay); - } -} - -main(); diff --git a/mmsoftware/js/js-sh/app/randstat/randstat.js b/mmsoftware/js/js-sh/app/randstat/randstat.js deleted file mode 100644 index ba72017..0000000 --- a/mmsoftware/js/js-sh/app/randstat/randstat.js +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Configuration; Modify as necessary - */ -var possibilities = 4; -var maxloops = 10000; - -/* - * Variables - */ -var results = [], found = []; -var total, loop, i, rnd, found_total; - -/* - * Returns a random number between 0 and possibilities - 1 - */ -function random_possibility() -{ - - return Math.floor(Math.random() * possibilities); -} - -/* - * To get reasonable statistics, sample maxloops times - */ -for (loop = 0; loop < maxloops; loop++) { - - /* - * Flag all possibilities as unprocessed, as well as our processed - * possibilities counter which we'll use as optimization - */ - for (i = 0; i < possibilities; i++) - found[i] = false; - found_total = 0; - - /* - * Loop indefinitely increasing total from zero - */ - for (total = 0; ; total++) { - rnd = random_possibility(); - if (!found[rnd]) { - /* - * New unprocessed possibility occured, flag as such - * and increase our processed possibilities by one. - * Then verify if our counter reached the number of - * possibilities, in which case they all were - * satisfied. We then record/mark a point in our - * sparse results array and break this innner loop. - */ - found[rnd] = true; - if (++found_total == possibilities) { - if (results[total] == undefined) - results[total] = 0; - results[total]++; - break; - } - } - } - -} - -/* - * For every existing result, print its score - */ -stdout.write("Iterat.\t= Frequency:\n"); -for (total = results.length, i = 0; i < total; i++) { - if (results[i] != undefined) - stdout.write((i + 1) + "\t= " + results[i] + "\n"); -} - -/* - * XXX TODO XXX - * - It would be nice to automatically create a graphic with the results - */ diff --git a/mmsoftware/js/js-sh/app/thumb/thumb.js b/mmsoftware/js/js-sh/app/thumb/thumb.js deleted file mode 100644 index b29c3f4..0000000 --- a/mmsoftware/js/js-sh/app/thumb/thumb.js +++ /dev/null @@ -1,259 +0,0 @@ -/* $Id: thumb.js,v 1.9 2007/01/07 11:39:44 mmondor Exp $ */ - -/* - * Copyright (c) 2006, Matthew Mondor - * ALL RIGHTS RESERVED. - */ - -/* - * TODO: - * - Implement getopt-like class and allow to provide command line arguments - * - Generate multiple indexes with list of index links at top and bottom - * for large directories - * - Work on better presentation - */ - -function thumbnail_jpeg(fh) -{ - this.jpeg(fh, 50); -} - -function thumbnail(from, to, xmax, ymax) -{ - var ofh, tfd, nfd, oimg, ox, oy, timg, tfh; - var ret = false; - var type = undefined; - var i, to; - - /* XXX */ - stderr.write('Processing: ' + from + "\n"); - - /* - * Set load and save functions relative to image file type. - */ - if (from.match(/\.jpg$|\.jpeg$/)) { - GD.load = GD.createFromJpeg; - GDImage.prototype.save = thumbnail_jpeg; - } else if (from.match(/\.png$/)) { - GD.load = GD.createFromPng; - GDImage.prototype.save = GDImage.prototype.png; - } else if (from.match(/\.gif$/)) { - GD.load = GD.createFromGif; - GDImage.prototype.save = GDImage.prototype.gif; - } - - /* - * We use a single try block which breaks out of for before setting - * res to true. We use a dummy for loop since goto doesn't work. - */ - for (;;) { - try { - var otime, tstat; - - /* - * First open descriptors so that we may fstat(2) - * to determine if the thumbnail is more recent than - * the original image, in which case we don't avoid to - * create a thumb for nothing. We'll use File's - * fdopen(3) functionality afterwards if need be. - * We do this because image resizing is a rather CPU - * intensive operation. - */ - ofd = new FD(); - ofd.open(from, FD.O_RDONLY); - otime = (ofd.fstat()).st_mtime; - - tfd = new FD(); - tfd.open(to, FD.O_WRONLY | FD.O_CREAT); - tstat = tfd.fstat(); - - if (otime <= tstat.st_mtime && tstat.st_size != 0) { - ofd.close(); - tfd.close(); - return true; - } - - /* - * Load original image and determine thumb size. - */ - ofh = new File(ofd.fd, 'r'); - oimg = GD.load(ofh); - - ox = oimg.imageSX(); - oy = oimg.imageSY(); - if (ox <= xmax && oy <= ymax) { - tx = ox; - ty = oy; - } else if (ox < oy) { - tx = Math.round((xmax / oy) * ox); - ty = ymax; - } else { - tx = xmax; - ty = Math.round((ymax / ox) * oy); - } - - /* - * Create and save new thumbnail. It's important to - * first truncate the file to zero length which - * fopen(3) would have done for us but which fdopen(3) - * won't. - */ - tfd.ftruncate(0); - tfh = new File(tfd.fd, 'w'); - - timg = GD.createTrueColor(tx, ty); - //oimg.copyResampled(timg, 0, 0, 0, 0, tx, ty, ox, oy); - oimg.copyResized(timg, 0, 0, 0, 0, tx, ty, ox, oy); - - timg.save(tfh); - } catch (x) { - Syslog.log(Syslog.LOG_NOTICE, x + ' (' + from + ')'); - break; - } - - /* Success */ - ret = true; - break; - } - - /* - * We close/destroy the objects in the same order they appeared since - * if we get an exception the others won't execute. Although the - * language supports a GC and objects will eventually be destroyed, - * there's no point in leaving them dangling unnecessary resources for - * a while. File descriptors shouldn't be open for nothing, and - * images take up a lot of memory. - */ - try { - ofh.close(); - oimg.destroy(); - tfh.close(); - timg.destroy(); - } catch (x) {} - - delete GD.load; - delete GDImage.prototype.save; - - return ret; -} - -function thumbnail_dir(path, xmax, ymax) -{ - var dir, fh; - var ret = false; - var i, f, index = []; - - for (;;) { - try { - - dir = new Dir(path); - while ((e = dir.read()) != null) { - if ((e.type & Dir.DT_DIR) != 0 && - e.name.charAt(0) != '.') { - index.push({t: 'DIR', l: e.name}); - if (!thumbnail_dir(path + '/' + e.name, xmax, - ymax)) - break; - } else if ((e.type & Dir.DT_REG) != 0 && e.name.match( - /\.jpg$|\.jpeg$|\.png$|\.gif$/)) { - if (e.name.match(/_thumb/)) - continue; - i = e.name.lastIndexOf('.'); - newname = e.name.substring(0, i) + '_thumb' + - e.name.substring(i); - if (!thumbnail(path + '/' + e.name, path + - '/' + newname, xmax, ymax)) - break; - index.push({t: 'IMG', o: e.name, n: newname}); - } - } - if (index.length == 0) { - ret = true; - break; - } - /* XXX sort */ - fh = new File(path + '/index.html', 'w'); - fh.write("\n" + - '
../ (parent directory)' + - "

\n"); - for (i in index) { - f = index[i]; - if (f.t == 'DIR') { - var t; - - fh.write('' + - "
' + - ''); - if ((t = dir_findthumb(path + '/' + f.l)) - != null) - fh.write('' + t + ''); - fh.write('
' + f.l + '/
\n"); - } - } - for (i in index) { - f = index[i]; - if (f.t == 'IMG') - fh.write('' + f.n +
-				    '' + "\n"); - } - fh.write('
Thumb gallery generated by: ' + - '$Id: thumb.js,v 1.9 2007/01/07 11:39:44 mmondor Exp $' + - '
(source)
' + "\n"); - fh.write("\n"); - fh.close(); - - } catch (x) { - Syslog.log(Syslog.LOG_NOTICE, x.toString()); - break; - } - - ret = true; - break; - } - - try { - dir.close(); - fh.close(); - } catch (x) {} - - return ret; -} - -function dir_findthumb(path) -{ - var ret = null; - var dir, e, o, s = 0; - - for (;;) { - try { - dir = new Dir(path); - while ((e = dir.read()) != null) { - if (e.name.match(/_thumb/)) { - o = FS.stat(path + '/' + e.name); - if (o.st_size > s) - ret = e.name; - } - } - } catch (x) { - Syslog.log(Syslog.LOG_NOTICE, x.toString()); - break; - } - break; - } - - try { - dir.close(); - } catch (x) {} - - return ret; -} - -thumbnail_dir('/home/data/jshttpd/mmondor/img_gallery', 100, 100); diff --git a/mmsoftware/js/js-sh/src/GNUmakefile b/mmsoftware/js/js-sh/src/GNUmakefile deleted file mode 100644 index 89a5ab3..0000000 --- a/mmsoftware/js/js-sh/src/GNUmakefile +++ /dev/null @@ -1,37 +0,0 @@ -# $Id: GNUmakefile,v 1.8 2006/12/14 17:13:58 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 - -GD_CFLAGS := $(shell gdlib-config --cflags) -GD_LDFLAGS := $(shell gdlib-config --ldflags --libs) -GD_LDFLAGS += -lgd - -MMOBJS := $(addprefix ../../../mmlib/,mmpool.o mmhash.o mmlog.o mmstring.o) -JSOBJS := $(addprefix ../../classes/,js_gcroot.o js_fd.o js_errno.o \ - js_signal.o js_file.o js_pgsql.o js_dir.o js_fs.o js_gd.o js_syslog.o) - -CFLAGS += $(JS_CFLAGS) $(PG_CFLAGS) $(GD_CFLAGS) -I../../../mmlib \ - -I../../classes -LDFLAGS += $(JS_LDFLAGS) $(PG_LDFLAGS) $(GD_LDFLAGS) - -OBJ := js-sh.o - - -all: js-sh - -%.o: %.c - $(CC) -c $(CFLAGS) -o $@ $< - -js-sh: $(OBJ) $(MMOBJS) $(JSOBJS) - $(CC) -o $@ $(OBJ) $(LDFLAGS) -lc $(MMOBJS) $(JSOBJS) - -clean: - rm -f js-sh $(OBJ) $(MMOBJS) $(JSOBJS) 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 12a3080..0000000 --- a/mmsoftware/js/js-sh/src/js-sh.c +++ /dev/null @@ -1,348 +0,0 @@ -/* $Id: js-sh.c,v 1.11 2006/12/14 17:13:58 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 -#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_syslog, *class_fd, *class_errno, - *class_signal, *class_file, *class_pgsql, *class_dir, - *class_gd, *class_fs; -} 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 void context_error_reporter(JSContext *, const char *, - JSErrorReport *); - -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); - } - - /* Ignore SIGPIPE signal */ - { - struct sigaction act; - - act.sa_handler = SIG_IGN; - act.sa_flags = SA_NOCLDSTOP; - (void) sigemptyset(&act.sa_mask); - (void) sigaction(SIGPIPE, &act, NULL); - } - - /* - * 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); - /* And error reporter */ - (void) JS_SetErrorReporter(cctx->ctx, context_error_reporter); - - /* - * 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; - JSString *str; - - if (JS_EvaluateScript(cctx->ctx, cctx->global, file->data, - file->size, argv[1], 1, &rval)) { - str = JS_ValueToString(cctx->ctx, rval); - (void) fprintf(stderr, "Script result: %s\n", - JS_GetStringBytes(str)); - } 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 > 0x3fff) { - count = 0; - JS_MaybeGC(ctx); - } - - /* Returning JS_FALSE here aborts the script */ - return JS_TRUE; -} - -static void -context_error_reporter(JSContext *cx, const char *msg, JSErrorReport *rep) -{ - (void) fprintf(stderr, "context_error_reporter() - %s : %s", - msg, rep->linebuf); -} - - - -/* - * 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 || - !js_map_init() || - (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) || - !js_InitGCRoot(cctx->ctx) || - (cctx->class_syslog = js_InitSyslogClass(cctx->ctx, cctx->global)) - == NULL || - (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_file = js_InitFileClass(cctx->ctx, cctx->global)) - == NULL || - (cctx->class_pgsql = js_InitPGClass(cctx->ctx, cctx->global)) - == NULL || - (cctx->class_dir = js_InitDirClass(cctx->ctx, cctx->global)) - == NULL || - (cctx->class_gd = js_InitGDClass(cctx->ctx, cctx->global)) - == NULL || - (cctx->class_fs = js_InitFSClass(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_DestroyGCRoot(cctx->ctx); - JS_DestroyContext(cctx->ctx); - } - js_map_destroy(); - 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/jslib/fd.js b/mmsoftware/js/jslib/fd.js deleted file mode 100644 index 953261a..0000000 --- a/mmsoftware/js/jslib/fd.js +++ /dev/null @@ -1,309 +0,0 @@ -/* $Id: fd.js,v 1.11 2006/08/22 14:08:45 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. - */ - -/* - * Extend FD object with buffered read/write functions to be used on - * non-blocking descriptors. - * - * XXX TODO XXX - * - Fix so that it is possible to read/write without polling, - * for use by httpd.js for instance. - */ - - - -FD.prototype.bread_buffer = ''; -FD.prototype.bwrite_buffer = ''; -FD.prototype.input_timeout = 30000; -FD.prototype.output_timeout = 30000; - -FD.prototype.berror = 0; -FD.prototype.BSUCCESS = 0; -FD.prototype.BEAGAIN = 1; -FD.prototype.BEOF = 2; -FD.prototype.BTIMEOUT = 3; -FD.prototype.BTOOLONG = 4; -FD.prototype.BERROR = 5; /* To delegate to errno */ - -FD.prototype.berrorStr = { - 0: "Success", - 1: "No data available", - 2: "End of file", - 3: "Timeout", - 4: "Line too long", - 5: "Other error" -}; - -/* - * Initializes state for a buffered FD out of a specified file descriptor int - */ -FD.prototype.binit = function(fd, input_timeout, output_timeout) -{ - this.set(fd); - this.fcntl(FD.F_SETFL, FD.O_NONBLOCK); - this.input_timeout = input_timeout; - this.output_timeout = output_timeout; -} - -/* - * Resets timeouts for a buffered FD - */ -FD.prototype.btimeouts = function(input_timeout, output_timeout) -{ - this.input_timeout = input_timeout; - this.output_timeout = output_timeout; -} - -/* - * Reads at most bytes, using buffering internally as necessary when - * reading. If the data is already available it is immediately returned. - * Returns null on error or EOF, or a String holding the read data. - * If no data is available, polling is performed for at most input_timeout - * milliseconds. - */ -FD.prototype.bread = function(max, poll) -{ - var data = ''; - var e; - - if (max == 0) - return ''; - - while (this.bread_buffer.length == 0) { - try { - data = this.read(max); - if (data.length == 0) { - this.berror = this.BEOF; - return null; - } - } catch (x) { - if (this.errno != Errno.EAGAIN) - return null; - data = ''; - this.berror = this.BERROR; - } - if (data.length != 0) - this.bread_buffer += data; - else { - if (!poll) { - this.berror = this.BEAGAIN; - return data; - } - try { - this.events = FD.POLLIN; - e = FD.poll([this], this.input_timeout); - if (e.length == 0) { - this.berror = this.BTIMEOUT; - return null; - } - if ((e[0].revents & (FD.POLLHUP | FD.POLLERR)) - != 0) { - this.berror = this.BEOF; - return null; - } - } catch (x) { - this.berror = this.BERROR; - return null; - } - } - } - - data = this.bread_buffer.substr(0, max); - this.bread_buffer = this.bread_buffer.substr(data.length); - - this.berror = 0; - return data; -} - -/* - * Re-inserts the specified data into the read buffer so that next read should - * retreive it first. - */ -FD.prototype.bunread = function(data) -{ - this.bread_buffer = data + this.bread_buffer; -} - -/* - * Writes specified data to a buffer, and flush the buffer as necessary if the - * buffer is too large. Returns 0 on success, or -1 on error. May also - * return 1 if poll is false and there is data to flush exceeding the 64KB - * buffer. - */ -FD.prototype.bwrite = function(data, immediate, poll) -{ - if (data.length == 0) - return 0; - - if (immediate) { - var len; - - try { - len = this.write(data); - if (len < 1) { - this.events &= ~FD.POLLOUT; - this.berror = this.BERROR; - return -1; - } - } catch (x) { - if (this.errno != Errno.EAGAIN) { - this.events &= ~FD.POLLOUT; - this.berror = this.BERROR; - return -1; - } - } - if (len == data.length) { - this.events &= ~FD.POLLOUT; - return 0; - } - - this.bwrite_buffer += data.substr(len); - /* FALLTHROUGH */ - } else - this.bwrite_buffer += data; - - this.events |= FD.POLLOUT; - if (this.bwrite_buffer.length > 65534) - return this.bflushw(poll); - - return 1; -} - -/* - * Clears the read buffer, if any bytes were ready. - */ -FD.prototype.bflushr = function() -{ - this.bread_buffer = ''; -} - -/* - * Attempts to flush the write buffer, returning -1 on error, 0 on success. - * Polls as necessary for as up to output_timeout milliseconds. - * If poll is false, but that there remains data to be flushed, returns 1, - * with berror set to BEAGAIN. - */ -FD.prototype.bflushw = function(poll) -{ - var len; - var e; - - while (this.bwrite_buffer.length > 0) { - try { - len = this.write(this.bwrite_buffer); - if (len < 1) { - this.events &= ~FD.POLLOUT; - this.berror = this.BERROR; - return -1; - } - } catch (x) { - if (this.errno != Errno.EAGAIN) { - this.events &= ~FD.POLLOUT; - this.berror = this.BERROR; - return -1; - } - } - this.bwrite_buffer = this.bwrite_buffer.substr(len); - if (this.bwrite_buffer.length == 0) - break; - if (!poll) { - this.berror = this.BEAGAIN; - this.events |= FD.POLLOUT; - return 1; - } - try { - this.events = FD.POLLOUT; - e = FD.poll([this], this.output_timeout); - if (e.length == 0) { - this.berror = this.BTIMEOUT; - return -1; - } - if ((e[0].revents & (FD.POLLHUP | FD.POLLERR)) != 0) { - this.berror = this.BEOF; - return -1; - } - } catch (x) { - this.berror = this.BEOF; - return -1; - } - } - - this.berror = this.BSUCCESS; - this.events &= ~FD.POLLOUT; - return 0; -} - -/* - * Internally uses buffered reads, waiting until a maximum number of bytes - * have been read or EOF occurs (in which case it returns null), or until a - * full '\r\n' delimited line could be read, in which case the line is returned - * and extraneous data buffered for further reads. The returned line will not - * have the trailing "\r\n". If poll is false, it may also return null but - * with a berror of BEAGAIN. - */ -FD.prototype.breadline = function(max, poll) -{ - var data = ''; - var data2; - var idx; - - while (data.length < max) { - if ((data2 = this.bread(max - data.length, poll)) == null) - return null; - data += data2; - if ((idx = data.indexOf("\r\n")) != -1) { - this.bunread(data.substr(idx + 2)); - return data.substr(0, idx); - } - /* - * XXX For some reason this doesn't work with stdin, only - * SOCK_STREAM sockets descriptors. Needed in case we are not - * polling because although bread() would return null with - * BEAGAIN in case it doesn't poll and no data remains in the - * buffer to provide, if there is buffered data but no new - * data to read, we could loop endlessly taking a lot of CPU - * time until an EOL is really detected. - */ - if (!poll) { - this.bunread(data2); - this.berror = this.BEAGAIN; - return null; - } - } - - this.bunread(data); - this.berror = this.BTOOLONG; - return null; -} diff --git a/mmsoftware/js/jslib/json.js b/mmsoftware/js/jslib/json.js deleted file mode 100644 index 7d6e428..0000000 --- a/mmsoftware/js/jslib/json.js +++ /dev/null @@ -1,283 +0,0 @@ -/* $Id: json.js,v 1.3 2006/11/18 03:03:58 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. - */ - -/* - * Safe JSON object parser. - * This implementation expects a base object to be supplied, into which there - * can be other objects and arrays, as well as native elements. - * Designed to parse strings possibly containing user-provided data. - * Note that this implementation is not thread-reentrant safe, although it - * would be easy to adapt to make it so, wrapping the state information into - * an object which would need to be instantiated once per thread. - */ - -var json_rlevel = 0, json_rlevel_max = 0; -var json_str = ''; -var json_len = 0; -var json_error = 'Success'; - -function json_parse(s, rmax) -{ - var o; - - if (s.substr(0, 1) == '(' && s.substr(s.length - 1, 1) == ')') - s = s.substr(1, s.length - 2); - - json_rlevel = 0; - json_rlevel_max = rmax; - json_str = s; - json_len = s.length; - json_off = 0; - json_error = 'Success'; - - o = {}; - if (!json_parse_object(o)) - return undefined; - return o; -} - -function json_isnumdigit(c) -{ - return ((c >= '0' && c <= '9') || c == '-' || c == '+' || c == '.' || - c == 'e' || c == 'E'); -} - -function json_parse_r() -{ - var o, s; - - if (++json_rlevel > json_rlevel_max) { - --json_rlevel; - json_error = 'Recursion level exceeded'; - return undefined; - } - - for (; json_off < json_len; ) { - if (json_str.charAt(json_off) == ' ') { - json_off++; - continue; - } - /* Object */ - if (json_str.charAt(json_off) == '{') { - o = {}; - if (!json_parse_object(o)) - return undefined; - --json_rlevel; - return o; - } - /* Array */ - if (json_str.charAt(json_off) == '[') { - o = []; - if (!json_parse_array(o)) - return undefined; - --json_rlevel; - return o; - } - /* String */ - if (json_str.charAt(json_off) == '"') { - for (json_off++, s = ''; json_off < json_len && - json_str.charAt(json_off) != '"'; json_off++) - s += json_str.charAt(json_off); - if (json_str.charAt(json_off) != '"') { - --json_rlevel; - json_error = 'Missing end of string quote'; - return undefined; - } - json_off++; - --json_rlevel; - return s; - } - /* Various constants */ - if (json_str.substr(json_off, 9) == 'undefined') { - json_off += 9; - --json_rlevel; - return undefined; - } - if (json_str.substr(json_off, 4) == 'null') { - json_off += 4; - --json_rlevel; - return null; - } - if (json_str.substr(json_off, 4) == 'true') { - json_off += 4; - --json_rlevel; - return true; - } - if (json_str.substr(json_off, 5) == 'false') { - json_off += 5; - --json_rlevel; - return false; - } - if (json_str.substr(json_off, 3) == 'NaN') { - json_off += 3; - --json_rlevel; - return NaN; - } - if (json_str.substr(json_off, 8) == 'Infinity') { - json_off += 8; - --json_rlevel; - return Infinity; - } - /* Number */ - for (s = ''; json_off < json_len && - json_isnumdigit(json_str.charAt(json_off)); json_off++) - s += json_str.charAt(json_off); - --json_rlevel; - if (s.length > 0) - return new Number(s); - else { - json_error = 'Invalid data element'; - return undefined; - } - } - - --json_rlevel; - json_error = 'End of string reached'; - return undefined; -} - -function json_parse_object(o) -{ - var key; - - /* Skip spaces */ - for (; json_off < json_len && - json_str.charAt(json_off) == ' '; - json_off++) ; - /* Make sure we start with '{' */ - if (json_str.charAt(json_off) != '{') { - json_error = '{ expected to start an object'; - return false; - } - json_off++; - - for (; json_off < json_len; ) { - /* Skip spaces */ - for (; json_off < json_len && - json_str.charAt(json_off) == ' '; - json_off++) ; - /* Verify for end of object boundary */ - if (json_str.charAt(json_off) == '}') { - json_off++; - return true; - } - /* Parse key name */ - key = ''; - for (; json_off < json_len && - json_str.charAt(json_off) != ':' && - json_str.charAt(json_off) != ' '; - json_off++) - key += json_str.charAt(json_off); - /* Skip spaces */ - for (; json_off < json_len && - json_str.charAt(json_off) == ' '; - json_off++) ; - if (json_str.charAt(json_off) != ':') { - json_error = ': expected after object member name'; - return false; - } - json_off++; - /* Strip possible " around key name */ - if (key.length > 1 && key.substr(0, 1) == '"' && - s.substr(key.length - 1, 1) == '"') - key = key.substr(1, key.length - 2); - if (key.length < 1) { - json_error = 'Object member name missing'; - return false; - } - /* Fill object element */ - o[key] = json_parse_r(); - /* Skip spaces */ - for (; json_off < json_len && - json_str.charAt(json_off) == ' '; - json_off++) ; - /* Expect separator or end of object boundary */ - if (json_str.charAt(json_off) == '}') - continue; - if (json_str.charAt(json_off) == ',') { - json_off++; - continue; - } - json_error = 'Unexpected end of object element separator'; - return false; - } - - json_error = 'End of string reached'; - return false; -} - -function json_parse_array(o) -{ - /* Skip spaces */ - for (; json_off < json_len && - json_str.charAt(json_off) == ' '; - json_off++) ; - /* Make sure we start with '[' */ - if (json_str.charAt(json_off) != '[') { - json_error = '[ expected to start an Array object'; - return false; - } - json_off++; - - for (; json_off < json_len; ) { - /* Skip spaces */ - for (; json_off < json_len && - json_str.charAt(json_off) == ' '; - json_off++) ; - /* Verify for end of array object boundary */ - if (json_str.charAt(json_off) == ']') { - json_off++; - return true; - } - /* Fill array element */ - o.push(json_parse_r()); - /* Skip spaces */ - for (; json_off < json_len && - json_str.charAt(json_off) == ' '; - json_off++) ; - /* Expect separator or end of array object boundary */ - if (json_str.charAt(json_off) == ']') - continue; - if (json_str.charAt(json_off) == ',') { - json_off++; - continue; - } - json_error = 'Unexpected end of array element separator'; - return false; - } - - json_error = 'End of string reached'; - return false; -} diff --git a/mmsoftware/js/jslib/ml_clean.js b/mmsoftware/js/jslib/ml_clean.js deleted file mode 100644 index 2b13636..0000000 --- a/mmsoftware/js/jslib/ml_clean.js +++ /dev/null @@ -1,221 +0,0 @@ -/* $Id: ml_clean.js,v 1.1 2006/08/06 12:11:23 mmondor Exp $ */ - -/* - * Copyright (C) 2004-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. - */ - - - -/* - * 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/mmsoftware/js/jslib/ml_machine.js b/mmsoftware/js/jslib/ml_machine.js deleted file mode 100644 index 4101539..0000000 --- a/mmsoftware/js/jslib/ml_machine.js +++ /dev/null @@ -1,175 +0,0 @@ -/* $Id: ml_machine.js,v 1.1 2006/08/06 12:11:23 mmondor Exp $ */ - -/* - * Copyright (C) 2004-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. - */ - - - -/* - * 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/mmsoftware/js/jslib/pgsql.js b/mmsoftware/js/jslib/pgsql.js deleted file mode 100644 index 023b037..0000000 --- a/mmsoftware/js/jslib/pgsql.js +++ /dev/null @@ -1,76 +0,0 @@ -/* $Id: pgsql.js,v 1.1 2006/08/06 11:58:11 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. - */ - -/* - * Utility functions added to the postgresql access objects - */ - - -PGResult.prototype.jsGetResults = function() -{ - var rows = this.nTuples(); - var cols = this.nFields(); - - var columns = []; - for (col = 0; col < cols; col++) - columns.push(this.fName(col)); - - var results = []; - for (row = 0; row < rows; row++) { - var o = {}; - - for (col = 0; col < cols; col++) - o[columns[col]] = this.getValue(row, col); - results.push(o); - } - this.clear(); - - return results; -} - -PGConn.prototype.jsExecParams = function(q, args) -{ - return this.execParams(q, args.length, null, args, null, null, 0); -} - -PGConn.prototype.jsPrepare = function(name, query) -{ - return this.prepare(name, query, 0, null); -} - -PGConn.prototype.jsExecPrepared = function(name, args) -{ - return this.execPrepared(name, args.length, args, null, null, 0); -} diff --git a/mmsoftware/js/jslib/root.js b/mmsoftware/js/jslib/root.js deleted file mode 100644 index 58f9fe7..0000000 --- a/mmsoftware/js/jslib/root.js +++ /dev/null @@ -1,225 +0,0 @@ -/* $Id: root.js,v 1.1 2006/08/06 12:11:23 mmondor Exp $ */ - -/* - * Copyright (C) 2005-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. - */ - -/* - * 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/mmsoftware/js/jslib/string.js b/mmsoftware/js/jslib/string.js deleted file mode 100644 index b8affc2..0000000 --- a/mmsoftware/js/jslib/string.js +++ /dev/null @@ -1,62 +0,0 @@ -/* $Id: string.js,v 1.1 2006/08/06 11:58:11 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. - */ - -/* - * 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/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/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/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 4469aea..0000000 Binary files a/mmsoftware/mmmail2/notes/mmmail-tables.dia and /dev/null differ diff --git a/mmsoftware/mmpasswd/GNUmakefile b/mmsoftware/mmpasswd/GNUmakefile deleted file mode 100644 index 5fbd7c3..0000000 --- a/mmsoftware/mmpasswd/GNUmakefile +++ /dev/null @@ -1,23 +0,0 @@ -# $Id: GNUmakefile,v 1.2 2004/05/31 06:15:43 mmondor Exp $ - -MMLIBS := $(addprefix ../mmlib/,mmstring.o) - -OBJS := mmpasswd.o - -CFLAGS += -Wall - - -all: mmpasswd - -%.o: %.c - cc -c ${CFLAGS} -I. -I../mmlib -o $@ $< - -mmpasswd: $(MMLIBS) $(OBJS) - cc -o $@ -lc -lcrypt $(OBJS) $(MMLIBS) - -install: - install -cs -o 0 -g 0 -m 550 mmpasswd /usr/local/sbin - install -c -o 0 -g 0 -m 444 mmpasswd.8 /usr/local/man/man8 - -clean: - rm -f mmpasswd $(OBJS) $(MMLIBS) diff --git a/mmsoftware/mmpasswd/Makefile b/mmsoftware/mmpasswd/Makefile deleted file mode 100644 index ad414b4..0000000 --- a/mmsoftware/mmpasswd/Makefile +++ /dev/null @@ -1,36 +0,0 @@ -# $Id: Makefile,v 1.3 2003/01/01 14:54:11 mmondor Exp $ - -CC = gcc -MAKE = make - -CFLAGS += -D_REENTRANT -Wall - -INCDIR = -I/usr/include -I/usr/pkg/include -I../mmlib -LIBDIR = -L/lib -L/usr/lib -L/usr/pkg/lib - -LIBS = ../mmlib/libmmondor.a -lc -lcrypt - -OBJS = mmpasswd.o - - - -CCOBJ = $(CC) $(CFLAGS) -c $(INCDIR) - - - - -mmpasswd: $(OBJS) - $(CC) $(CFLAGS) -o mmpasswd $(INCDIR) $(LIBDIR) $(OBJS) $(LIBS) - - - - -clean: - -rm -f $(OBJS) mmpasswd - - - - -mmpasswd.o: - $(CCOBJ) mmpasswd.c - diff --git a/mmsoftware/mmpasswd/make.sh b/mmsoftware/mmpasswd/make.sh deleted file mode 100755 index 4d25a62..0000000 --- a/mmsoftware/mmpasswd/make.sh +++ /dev/null @@ -1,8 +0,0 @@ -#!/bin/sh -# $Id: make.sh,v 1.1 2002/12/11 10:16:19 mmondor Exp $ - -. ../mmlib/makefuncs.sh - -clean mmpasswd -makebin mmpasswd -instbin mmpasswd diff --git a/mmsoftware/mmpasswd/makepart.sh b/mmsoftware/mmpasswd/makepart.sh deleted file mode 100755 index 1d296d4..0000000 --- a/mmsoftware/mmpasswd/makepart.sh +++ /dev/null @@ -1,25 +0,0 @@ -#!/bin/sh -# $Id: makepart.sh,v 1.3 2003/01/01 14:54:11 mmondor Exp $ - -. ../mmlib/makedefs.sh - -OBJS='mmpasswd.o' -BIN1='mmpasswd' - -if [ "$1" = "clean" ]; then - show $RM $OBJS $BIN1 - exit 0 -fi - -INCDIR="-I../mmlib $STDINC" -LIBDIR="$STDLIB" -LIBS='../mmlib/libmmondor.a -lc -lcrypt' - -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/mmpasswd/mmpasswd.8 b/mmsoftware/mmpasswd/mmpasswd.8 deleted file mode 100644 index 526cd73..0000000 --- a/mmsoftware/mmpasswd/mmpasswd.8 +++ /dev/null @@ -1,93 +0,0 @@ -.\" $Id: mmpasswd.8,v 1.4 2004/05/06 00:05:35 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 22 Dec, 2002 -.Dt MMPASSWD 8 -.Os -.Sh NAME -.Nm mmpasswd -.Nd password hash generation for -.Xr mmmail 8 -and -.Xr mmftpd 8 -and matching for related utilities -.Sh SYNOPSIS -.Nm mmpasswd -.Ar 'password' -.Op 'hash' -.Sh DESCRIPTION -In it's first form, with only -.Ar password -argument given, -.Nm mmpasswd -utility uses -.Xr crypt 3 -to hash the provided password string using MD5, which should be enclosed -in quotes if the password contains spaces. It then outputs a text form -of the hash in a case-sensitive representation. This hash should be -compatible with those of the current system. -.Pp -The resulting string is suitable for use in -.Xr mmmail 8 -.Nm user -MySQL table, or for -.Xr mmftpd 8 -user database -.Xr mmftpdpasswd 5 -file. -.Pp -The authentication of these daemons will be performed by performing the same -conversions on the user supplied password, still using -.Xr crypt 3 , -and then comparing the result with the database entry for that user, -case-sensitively. -.Pp -The second invokation form of -.Nm mmpasswd -requires a second argument, -.Ar hash , -which when provided will be used to evaluate matching of the supplied -password with the supplied hash. This works for both traditional DES -and recent MD5 implementations. When invoked this way, -.Nm mmpasswd -will output to standard output '0' for success (authentication successful) -or '-1' for failiure. Similarly, the executable will return the same value -as it's error code. This can be useful for the administrator and for scripts -or various frontends. -.Sh AUTHOR -.Nm mmpasswd -was written by Matthew Mondor -.Sh SEE ALSO -.Xr mmftpdpasswd 5 , -.Xr mmmail 8 , -.Xr crypt 3 . -.Sh BUGS -Please report any bug to mmondor@accela.net diff --git a/mmsoftware/mmpasswd/mmpasswd.c b/mmsoftware/mmpasswd/mmpasswd.c deleted file mode 100644 index af96d8a..0000000 --- a/mmsoftware/mmpasswd/mmpasswd.c +++ /dev/null @@ -1,169 +0,0 @@ -/* $Id: mmpasswd.c,v 1.6 2004/05/06 00:05:35 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. - */ - - - - -#include -#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 bed0e54..0000000 --- a/mmsoftware/mmstatd/src/mmstat.c +++ /dev/null @@ -1,425 +0,0 @@ -/* $Id: mmstat.c,v 1.19 2006/11/01 01:32:40 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.19 2006/11/01 01:32:40 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; - int64_t max = 0; - char fmt[32]; - - DLIST_FOREACH(&list, entnod) { - index[i++] = entnod; - if (entnod->ent.value > max) - max = entnod->ent.value; - else if (-entnod->ent.value > max) - max = -entnod->ent.value; - } - index[i] = NULL; - - (void) snprintf(fmt, 31, "%lld", max); - i = mm_strlen(fmt) + 1; - (void) snprintf(fmt, 31, "%%%dlld %%s %%s\n", i); - - 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); - (void) printf(fmt, 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/mystic_ships/LICENSE b/mmsoftware/mystic_ships/LICENSE deleted file mode 100644 index a2baaa3..0000000 --- a/mmsoftware/mystic_ships/LICENSE +++ /dev/null @@ -1,34 +0,0 @@ -/* $Id: LICENSE,v 1.1 2006/12/31 08:32:39 mmondor Exp $ */ - -/* - * Copyright (C) 2005-2007, 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/mmsoftware/mystic_ships/README b/mmsoftware/mystic_ships/README deleted file mode 100644 index ef0d54f..0000000 --- a/mmsoftware/mystic_ships/README +++ /dev/null @@ -1,284 +0,0 @@ -$Id: README,v 1.1 2006/12/31 08:32:39 mmondor Exp $ - -TODO -==== - -SERVER - -- Add support for collisions between torpedoes and ships. -- Add support for multiframe objects. For instance, detonations could have - multiple frames, as well as the cloaking effect. These should possibly more - be time-dependent than actual frame dependent however, considering that - frames can be delayed and their rate changed. - If there was a fixed client-side FPS for those, this would also mean that - all other objects obtained from the server would need to be recorded as - well... Possibly that if we did that it might also be possible to transmit - less frames over the network but with thrust/angle information for the client - to be able to evaluate smooth transitions between them... -- Cause older torpedoes to automatically get detonated whenever a new - torpido is fired and the maximum number of torpedoes has been reached. -- Implement authentication credentials checking, limit same user to one - connection only -- Limit connections per ip address in number and frequency -- Implement and test using rc4 or openssl with aes. Verify if the overhead - is negligeable enough to be worth keeping. Otherwise, we could use crypto- - graphy for authentication only if wanted. -- Possibly implement as part of the authentication system a method to only - allow signed client keys, the client key would be sent to us, and then also - make sure that only one client using that key can connect at a time, - logging when it's not the case. This client-specific unique signed key - could be used as ID possibly? Or other than signing we perhaps should also - sign against the player's name and verify at login that he can only use that - login with that key, too. -- Add proper connection/disconnection/events logging - -CLIENT - -- Continue test: draw objects as they are received by the server until the - end of frame packet, at which time we would update the display flipping the - double buffered pages. This also means that we must find a way to not take - all CPU time while waiting for both SDL events and server packets ones at - the same time! Since receiving is prioritary, perhaps that we could wait - for messages from the server using a ring, since SDL events would - automatically be queued meanwhile? - Also now send user events to the server. -- Add an auto-ping along with fps... Use SDL_GetTicks() to calculate the - delay between sending ping and receiving pong. -- Create packets to also send user updates about his ship's statistics. - These could probably be updated less frequently than 10 times per second. - - -NOTES -===== - -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). - - -Encrypting communications -========================= - -A stream cypher such as RC4 would be simple and efficient to encrypt -communication between the client and the server. If RC4 is used, -the following must however be taken into consideration: - -1) Session keys must always be unique. To achieve this, a function -like HMAC to obtain from a long term key and a unique noonce could -be used if necessary. This however is best for private key -cryptography, I believe. - -2) The key must be renegotiated from time to time after enough data -has been sent using it. There exist vulnerabilities if 1GB of data -is transfered using the same key. - -3) At least 1024 bytes of the pseudo-random generator keystream -must be discarded after key creation to avoid a key predictability -vulnerability. - -If public key cryptography was used, at least 256-bit RSA could be -used. Such an implemetation theoretically would not need larger -numbers than 128-bit ones to function, I think. Then a "complex" -bignum library could probably be used which is faster and simpler -than a general purpose more complex arbitrary precision math library. - -We probably should borrow NetBSD's SHA512 (or at least SHA1) and RMD160 -algorithms. My ARC4 implementation could be used as well. - - -Compressing communications -========================== - -Investigate if using huffman compression would be wanted and worth -it, or some other simple compression system suiting well to the -transfered data. Possibly that the dictionary could be static and -would never need to be transfered. - -zlib could probably also be used. It however unfortunately is -famous for frequently discovered vulnerabilities. - - - -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/mmsoftware/mystic_ships/client/GNUmakefile b/mmsoftware/mystic_ships/client/GNUmakefile deleted file mode 100644 index a681414..0000000 --- a/mmsoftware/mystic_ships/client/GNUmakefile +++ /dev/null @@ -1,106 +0,0 @@ -# $Id: GNUmakefile,v 1.1 2006/12/31 08:32:39 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 -Isrc -I../common - -# Enable for verbosity/debugging -#CFLAGS += -v -H -g -#LDFLAGS += -v -g - -# And to disable assertions -CFLAGS += -DNDEBUG -#CFLAGS += -g - -# And to enable profiling -#CFLAGS += -pg -#LDFLAGS += -pg - -OBJS := $(addprefix src/,main.o debug.o pool.o packets.o thread_msg.o \ - thread_net_recv.o recvq.o thread_net_send.o screen.o decode.o enc.o) \ - $(addprefix ../common/,sha1.o rmd160.o hmac_sha1.o hmac_rmd160.o \ - mmenc.o) -BIN := tms-client -RAWOBJS := $(addsuffix .enc.o,$(addprefix bmp/,RomDD.bmp FedCA.bmp) \ - $(addprefix wav/,nt_cloaked.wav nt_uncloak.wav nt_shield_up.wav \ - nt_shield_down.wav nt_fire_torp_other.wav nt_plasma_hit.wav \ - nt_explosion_other.wav) \ - $(addprefix fnt/,7x13.fnt)) - -SDL_CFLAGS := $(shell sdl-config --cflags) -SDL_LDFLAGS := $(shell sdl-config --libs) -SDL_LDFLAGS += -lSDL_image -lSDL_mixer -lSDL_net -lSDL_gfx - -Z_CFLAGS := -I/usr/local/include -Z_LDFLAGS := -lz - -# 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) - -# Architecture independent settings follow -CFLAGS += -DBIG_ENDIAN=4321 -DLITTLE_ENDIAN=1234 -ifeq ($(OBJARCH),i386) - CFLAGS += -DBYTE_ORDER=LITTLE_ENDIAN -else - CFLAGS += -DBYTE_ORDER=BIG_ENDIAN -endif - -#CFLAGS += $(SDL_CFLAGS) $(GL_CFLAGS) -#LDFLAGS += $(SDL_LDFLAGS) $(GL_LDFLAGS) -CFLAGS += $(SDL_CFLAGS) $(Z_CFLAGS) -LDFLAGS += $(SDL_LDFLAGS) $(Z_LDFLAGS) - -all: $(BIN) - -%.o: %.c - $(CC) -c $(CFLAGS) -I. -o $@ $< - -encode: - $(CC) -o src/encode src/encode.c - -$(RAWOBJS): encode - src/encode $(basename $(basename $@)) $(basename $@) $(SEED) - $(OBJCOPY) -I binary -B $(OBJARCH) -O $(OBJTARGET) $(basename $@) $@ - $(RM) -f $(basename $@) - -$(BIN): $(OBJS) $(RAWOBJS) - $(CC) -o $@ $(OBJS) $(RAWOBJS) $(LDFLAGS) - $(STRIP) -s -w -R .comment -R .ident -R .debug* $@* - -clean: - $(RM) -f $(BIN) $(BIN).exe $(OBJS) $(RAWOBJS) \ - src/encode src/encode.exe stdout.txt stderr.txt diff --git a/mmsoftware/mystic_ships/client/bmp/FedCA.bmp b/mmsoftware/mystic_ships/client/bmp/FedCA.bmp deleted file mode 100644 index be3a562..0000000 Binary files a/mmsoftware/mystic_ships/client/bmp/FedCA.bmp and /dev/null differ diff --git a/mmsoftware/mystic_ships/client/bmp/RomDD.bmp b/mmsoftware/mystic_ships/client/bmp/RomDD.bmp deleted file mode 100644 index 50e429a..0000000 Binary files a/mmsoftware/mystic_ships/client/bmp/RomDD.bmp and /dev/null differ diff --git a/mmsoftware/mystic_ships/client/bmp/fedship.bmp b/mmsoftware/mystic_ships/client/bmp/fedship.bmp deleted file mode 100755 index 465806f..0000000 Binary files a/mmsoftware/mystic_ships/client/bmp/fedship.bmp and /dev/null differ diff --git a/mmsoftware/mystic_ships/client/bmp/indship.bmp b/mmsoftware/mystic_ships/client/bmp/indship.bmp deleted file mode 100755 index 1a7e39b..0000000 Binary files a/mmsoftware/mystic_ships/client/bmp/indship.bmp and /dev/null differ diff --git a/mmsoftware/mystic_ships/client/bmp/kliship.bmp b/mmsoftware/mystic_ships/client/bmp/kliship.bmp deleted file mode 100755 index cf52598..0000000 Binary files a/mmsoftware/mystic_ships/client/bmp/kliship.bmp and /dev/null differ diff --git a/mmsoftware/mystic_ships/client/bmp/misc-fixed-bold-r-normal--13-120-75-75-c-80-iso8859-1.bmp b/mmsoftware/mystic_ships/client/bmp/misc-fixed-bold-r-normal--13-120-75-75-c-80-iso8859-1.bmp deleted file mode 100644 index c55b172..0000000 Binary files a/mmsoftware/mystic_ships/client/bmp/misc-fixed-bold-r-normal--13-120-75-75-c-80-iso8859-1.bmp and /dev/null differ diff --git a/mmsoftware/mystic_ships/client/bmp/misc-fixed-bold-r-normal--15-120-100-100-c-90-iso8859-1.bmp b/mmsoftware/mystic_ships/client/bmp/misc-fixed-bold-r-normal--15-120-100-100-c-90-iso8859-1.bmp deleted file mode 100644 index 5c2ab05..0000000 Binary files a/mmsoftware/mystic_ships/client/bmp/misc-fixed-bold-r-normal--15-120-100-100-c-90-iso8859-1.bmp and /dev/null differ diff --git a/mmsoftware/mystic_ships/client/bmp/misc-fixed-medium-r-normal--13-120-75-75-c-80-iso8859-1.bmp b/mmsoftware/mystic_ships/client/bmp/misc-fixed-medium-r-normal--13-120-75-75-c-80-iso8859-1.bmp deleted file mode 100644 index e8ac5d4..0000000 Binary files a/mmsoftware/mystic_ships/client/bmp/misc-fixed-medium-r-normal--13-120-75-75-c-80-iso8859-1.bmp and /dev/null differ diff --git a/mmsoftware/mystic_ships/client/bmp/misc-fixed-medium-r-normal--15-120-100-100-c-90-iso8859-1.bmp b/mmsoftware/mystic_ships/client/bmp/misc-fixed-medium-r-normal--15-120-100-100-c-90-iso8859-1.bmp deleted file mode 100644 index 9e1d9f2..0000000 Binary files a/mmsoftware/mystic_ships/client/bmp/misc-fixed-medium-r-normal--15-120-100-100-c-90-iso8859-1.bmp and /dev/null differ diff --git a/mmsoftware/mystic_ships/client/bmp/oriship.bmp b/mmsoftware/mystic_ships/client/bmp/oriship.bmp deleted file mode 100755 index 166fb1a..0000000 Binary files a/mmsoftware/mystic_ships/client/bmp/oriship.bmp and /dev/null differ diff --git a/mmsoftware/mystic_ships/client/bmp/romship.bmp b/mmsoftware/mystic_ships/client/bmp/romship.bmp deleted file mode 100755 index 753d38a..0000000 Binary files a/mmsoftware/mystic_ships/client/bmp/romship.bmp and /dev/null differ diff --git a/mmsoftware/mystic_ships/client/bmp/sbexpl.bmp b/mmsoftware/mystic_ships/client/bmp/sbexpl.bmp deleted file mode 100755 index b06ffcf..0000000 Binary files a/mmsoftware/mystic_ships/client/bmp/sbexpl.bmp and /dev/null differ diff --git a/mmsoftware/mystic_ships/client/bmp/shexpl.bmp b/mmsoftware/mystic_ships/client/bmp/shexpl.bmp deleted file mode 100755 index 72a62c4..0000000 Binary files a/mmsoftware/mystic_ships/client/bmp/shexpl.bmp and /dev/null differ diff --git a/mmsoftware/mystic_ships/client/fnt/10x20.fnt b/mmsoftware/mystic_ships/client/fnt/10x20.fnt deleted file mode 100644 index 99b3486..0000000 Binary files a/mmsoftware/mystic_ships/client/fnt/10x20.fnt and /dev/null differ diff --git a/mmsoftware/mystic_ships/client/fnt/5x7.fnt b/mmsoftware/mystic_ships/client/fnt/5x7.fnt deleted file mode 100644 index 3612165..0000000 Binary files a/mmsoftware/mystic_ships/client/fnt/5x7.fnt and /dev/null differ diff --git a/mmsoftware/mystic_ships/client/fnt/5x8.fnt b/mmsoftware/mystic_ships/client/fnt/5x8.fnt deleted file mode 100644 index 01add72..0000000 Binary files a/mmsoftware/mystic_ships/client/fnt/5x8.fnt and /dev/null differ diff --git a/mmsoftware/mystic_ships/client/fnt/6x10.fnt b/mmsoftware/mystic_ships/client/fnt/6x10.fnt deleted file mode 100644 index 3c49f20..0000000 Binary files a/mmsoftware/mystic_ships/client/fnt/6x10.fnt and /dev/null differ diff --git a/mmsoftware/mystic_ships/client/fnt/6x12.fnt b/mmsoftware/mystic_ships/client/fnt/6x12.fnt deleted file mode 100644 index 156865b..0000000 Binary files a/mmsoftware/mystic_ships/client/fnt/6x12.fnt and /dev/null differ diff --git a/mmsoftware/mystic_ships/client/fnt/6x13.fnt b/mmsoftware/mystic_ships/client/fnt/6x13.fnt deleted file mode 100644 index e259f1a..0000000 Binary files a/mmsoftware/mystic_ships/client/fnt/6x13.fnt and /dev/null differ diff --git a/mmsoftware/mystic_ships/client/fnt/6x13B.fnt b/mmsoftware/mystic_ships/client/fnt/6x13B.fnt deleted file mode 100644 index 503b773..0000000 Binary files a/mmsoftware/mystic_ships/client/fnt/6x13B.fnt and /dev/null differ diff --git a/mmsoftware/mystic_ships/client/fnt/6x13O.fnt b/mmsoftware/mystic_ships/client/fnt/6x13O.fnt deleted file mode 100644 index cb5a27f..0000000 Binary files a/mmsoftware/mystic_ships/client/fnt/6x13O.fnt and /dev/null differ diff --git a/mmsoftware/mystic_ships/client/fnt/6x9.fnt b/mmsoftware/mystic_ships/client/fnt/6x9.fnt deleted file mode 100644 index cca4689..0000000 Binary files a/mmsoftware/mystic_ships/client/fnt/6x9.fnt and /dev/null differ diff --git a/mmsoftware/mystic_ships/client/fnt/7x13.fnt b/mmsoftware/mystic_ships/client/fnt/7x13.fnt deleted file mode 100644 index d20852e..0000000 Binary files a/mmsoftware/mystic_ships/client/fnt/7x13.fnt and /dev/null differ diff --git a/mmsoftware/mystic_ships/client/fnt/7x13B.fnt b/mmsoftware/mystic_ships/client/fnt/7x13B.fnt deleted file mode 100644 index 9f3fbed..0000000 Binary files a/mmsoftware/mystic_ships/client/fnt/7x13B.fnt and /dev/null differ diff --git a/mmsoftware/mystic_ships/client/fnt/7x13O.fnt b/mmsoftware/mystic_ships/client/fnt/7x13O.fnt deleted file mode 100644 index e5b7732..0000000 Binary files a/mmsoftware/mystic_ships/client/fnt/7x13O.fnt and /dev/null differ diff --git a/mmsoftware/mystic_ships/client/fnt/7x14.fnt b/mmsoftware/mystic_ships/client/fnt/7x14.fnt deleted file mode 100644 index fdcd752..0000000 Binary files a/mmsoftware/mystic_ships/client/fnt/7x14.fnt and /dev/null differ diff --git a/mmsoftware/mystic_ships/client/fnt/7x14B.fnt b/mmsoftware/mystic_ships/client/fnt/7x14B.fnt deleted file mode 100644 index 939aa63..0000000 Binary files a/mmsoftware/mystic_ships/client/fnt/7x14B.fnt and /dev/null differ diff --git a/mmsoftware/mystic_ships/client/fnt/8x13.fnt b/mmsoftware/mystic_ships/client/fnt/8x13.fnt deleted file mode 100644 index 9de72b4..0000000 Binary files a/mmsoftware/mystic_ships/client/fnt/8x13.fnt and /dev/null differ diff --git a/mmsoftware/mystic_ships/client/fnt/8x13B.fnt b/mmsoftware/mystic_ships/client/fnt/8x13B.fnt deleted file mode 100644 index 96179a0..0000000 Binary files a/mmsoftware/mystic_ships/client/fnt/8x13B.fnt and /dev/null differ diff --git a/mmsoftware/mystic_ships/client/fnt/8x13O.fnt b/mmsoftware/mystic_ships/client/fnt/8x13O.fnt deleted file mode 100644 index a38a371..0000000 Binary files a/mmsoftware/mystic_ships/client/fnt/8x13O.fnt and /dev/null differ diff --git a/mmsoftware/mystic_ships/client/fnt/9x15.fnt b/mmsoftware/mystic_ships/client/fnt/9x15.fnt deleted file mode 100644 index 349e5bd..0000000 Binary files a/mmsoftware/mystic_ships/client/fnt/9x15.fnt and /dev/null differ diff --git a/mmsoftware/mystic_ships/client/fnt/9x15B.fnt b/mmsoftware/mystic_ships/client/fnt/9x15B.fnt deleted file mode 100644 index 23bc97e..0000000 Binary files a/mmsoftware/mystic_ships/client/fnt/9x15B.fnt and /dev/null differ diff --git a/mmsoftware/mystic_ships/client/fnt/9x18.fnt b/mmsoftware/mystic_ships/client/fnt/9x18.fnt deleted file mode 100644 index 0651c97..0000000 Binary files a/mmsoftware/mystic_ships/client/fnt/9x18.fnt and /dev/null differ diff --git a/mmsoftware/mystic_ships/client/fnt/9x18B.fnt b/mmsoftware/mystic_ships/client/fnt/9x18B.fnt deleted file mode 100644 index d93d09d..0000000 Binary files a/mmsoftware/mystic_ships/client/fnt/9x18B.fnt and /dev/null differ diff --git a/mmsoftware/mystic_ships/client/ogg/1.ogg b/mmsoftware/mystic_ships/client/ogg/1.ogg deleted file mode 100644 index 44810a8..0000000 Binary files a/mmsoftware/mystic_ships/client/ogg/1.ogg and /dev/null differ diff --git a/mmsoftware/mystic_ships/client/src/conf.h b/mmsoftware/mystic_ships/client/src/conf.h deleted file mode 100644 index 4cb04d8..0000000 --- a/mmsoftware/mystic_ships/client/src/conf.h +++ /dev/null @@ -1,31 +0,0 @@ -/* $Id: conf.h,v 1.1 2006/12/31 08:32:39 mmondor Exp $ */ - -/* - * Copyright (c) 2006, Matthew Mondor - * ALL RIGHTS RESERVED. - */ - -#ifndef _CONF_H_ -#define _CONF_H_ - - - -/* - * Various hardcoded configuration parameters. - */ - -#define SERVER_HOST "hal.xisop" -/*#define SERVER_HOST "tms-play.pulsar-zone.net"*/ - -#define SERVER_PORT 7777 - -#define CLIENT_VERSION 1 -#define CLIENT_LOGIN "login" -#define CLIENT_PASSWD "d9d19c4285a4782a1b231d98aff232a046978cc2" - -#define USE_COMPRESSION -#define USE_ENCRYPTION - - - -#endif diff --git a/mmsoftware/mystic_ships/client/src/debug.c b/mmsoftware/mystic_ships/client/src/debug.c deleted file mode 100644 index 6c5fe44..0000000 --- a/mmsoftware/mystic_ships/client/src/debug.c +++ /dev/null @@ -1,42 +0,0 @@ -/* $Id: debug.c,v 1.1 2006/12/31 08:32:39 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/mmsoftware/mystic_ships/client/src/debug.h b/mmsoftware/mystic_ships/client/src/debug.h deleted file mode 100644 index e89f851..0000000 --- a/mmsoftware/mystic_ships/client/src/debug.h +++ /dev/null @@ -1,44 +0,0 @@ -/* $Id: debug.h,v 1.1 2006/12/31 08:32:39 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/mmsoftware/mystic_ships/client/src/decode.c b/mmsoftware/mystic_ships/client/src/decode.c deleted file mode 100644 index 36f2fff..0000000 --- a/mmsoftware/mystic_ships/client/src/decode.c +++ /dev/null @@ -1,38 +0,0 @@ -/* $Id: decode.c,v 1.1 2006/12/31 08:32:39 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/mmsoftware/mystic_ships/client/src/decode.h b/mmsoftware/mystic_ships/client/src/decode.h deleted file mode 100644 index ac8ef6c..0000000 --- a/mmsoftware/mystic_ships/client/src/decode.h +++ /dev/null @@ -1,23 +0,0 @@ -/* $Id: decode.h,v 1.1 2006/12/31 08:32:39 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/mmsoftware/mystic_ships/client/src/dlist.h b/mmsoftware/mystic_ships/client/src/dlist.h deleted file mode 100644 index 9c368e0..0000000 --- a/mmsoftware/mystic_ships/client/src/dlist.h +++ /dev/null @@ -1,174 +0,0 @@ -/* $Id: dlist.h,v 1.1 2006/12/31 08:32:39 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/mmsoftware/mystic_ships/client/src/enc.c b/mmsoftware/mystic_ships/client/src/enc.c deleted file mode 100644 index 99b07ad..0000000 --- a/mmsoftware/mystic_ships/client/src/enc.c +++ /dev/null @@ -1,27 +0,0 @@ -/* $Id: enc.c,v 1.1 2006/12/31 08:32:39 mmondor Exp $ */ - -/* - * Copyright (c) 2006, Matthew Mondor - * ALL RIGHTS RESERVED. - */ - - - -#include - - - -#ifdef USE_ENCRYPTION - - - -#include - - - -mmenc_t enc_in, enc_out; -int mmencrypt = 0; - - - -#endif diff --git a/mmsoftware/mystic_ships/client/src/enc.h b/mmsoftware/mystic_ships/client/src/enc.h deleted file mode 100644 index 1788efd..0000000 --- a/mmsoftware/mystic_ships/client/src/enc.h +++ /dev/null @@ -1,22 +0,0 @@ -/* $Id: enc.h,v 1.1 2006/12/31 08:32:39 mmondor Exp $ */ - -/* - * Copyright (c) 2006, Matthew Mondor - * ALL RIGHTS RESERVED. - */ - -#ifndef _ENC_H_ -#define _ENC_H_ - - - -#include - - - -extern mmenc_t enc_in, enc_out; -extern int mmencrypt; - - - -#endif diff --git a/mmsoftware/mystic_ships/client/src/encode.c b/mmsoftware/mystic_ships/client/src/encode.c deleted file mode 100644 index f5ce243..0000000 --- a/mmsoftware/mystic_ships/client/src/encode.c +++ /dev/null @@ -1,107 +0,0 @@ -/* $Id: encode.c,v 1.1 2006/12/31 08:32:39 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/mmsoftware/mystic_ships/client/src/endian.h b/mmsoftware/mystic_ships/client/src/endian.h deleted file mode 100644 index a0f55ec..0000000 --- a/mmsoftware/mystic_ships/client/src/endian.h +++ /dev/null @@ -1,52 +0,0 @@ -/* $Id: endian.h,v 1.1 2006/12/31 08:32:39 mmondor Exp $ */ - -/* - * Copyright (c) 2004-2006, Matthew Mondor - * ALL RIGHTS RESERVED. - */ - -/* - * Efficient macros for working with endian issues. - */ - - - -#ifndef _MM_ENDIAN_H_ -#define _MM_ENDIAN_H_ - - - -#include - -#include -#include - - - -#if SDL_BYTEORDER == SDL_LIL_ENDIAN - -/* Little endian is not network order, we need conversions */ -#define BYTEORDER_NETWORK16 SDL_Swap16 -#define BYTEORDER_HOST16 SDL_Swap16 -#define BYTEORDER_NETWORK32 SDL_Swap32 -#define BYTEORDER_HOST32 SDL_Swap32 -#define BYTEORDER_NETWORK64 SDL_Swap64 -#define BYTEORDER_HOST64 SDL_Swap64 - -#elif SDL_BYTEORDER == SDL_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 SDL_BYTEORDER"; -#endif - - - -#endif diff --git a/mmsoftware/mystic_ships/client/src/main.c b/mmsoftware/mystic_ships/client/src/main.c deleted file mode 100644 index 4362e88..0000000 --- a/mmsoftware/mystic_ships/client/src/main.c +++ /dev/null @@ -1,852 +0,0 @@ -/* $Id: main.c,v 1.1 2006/12/31 08:32:39 mmondor Exp $ */ - -/* - * Copyright (c) 2006, Matthew Mondor - * ALL RIGHTS RESERVED. - */ - - - -/* STANDARD HEADERS */ -#include -#include -#include -#include -#include -/* XXX UNIX DEBUG */ -#include - -/* THIRD PARTY LIBRARY HEADERS */ -#include -#include -#include -#include -#include -#include -#include -#include - -/* APPLICATION HEADERS */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - - - -/* DEFINITIONS */ - -enum userevents { - UE_TORP = 0, - UE_PHASER, - UE_PLASMA, - UE_DIRECTION, - UE_SHIELD, - UE_CLOAK, - UE_THRUST_ACC, - UE_THRUST_DEC -}; - -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 send_event(int, ...); - -static SDL_Surface *bmp_load_key(void *, size_t); -static int surface_blit_angle(SDL_Surface *, int, int, int); - -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 interval_callback(Uint32, void *); - -static void trig_init(void); - - - -/* PUBLIC GLOBALS */ - -thread_port_t main_port; - -char pingstr[8]; -Uint32 pingticks; - -char shipinfostr[64]; - - - -/* PRIVATE GLOBALS */ - -static int main_quit = 0; - -static int joy_angle = 0; - -static SDL_Surface *rship, *fship; -static int shields = 1, cloaked = 0; - -static Mix_Chunk *snd_cloak, *snd_uncloak, *snd_shield, *snd_unshield, - *snd_torp, *snd_hit, *snd_explode; - -static SDL_TimerID interval; -static char fpsstr[8]; -static int fpscnt; - -static struct font *font; - -static double cos_table[256], sin_table[256]; - - - -/* PRIVATE FUNCTIONS */ - -/* ARGSUSED */ -int -main(int argc, char **argv) -{ - thread_ring_t main_ring; - SDL_Thread *recv_threadid, *send_threadid; - Mix_Music *mus; - int pending_many; - - /* Initialization */ - screen_init(); - - /* XXX UNIX DEBUGGING! */ - signal(SIGSEGV, SIG_DFL); - - 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 and ping counter and initialization */ - (void) strcpy(fpsstr, "000 fps"); - (void) strcpy(pingstr, "0000 ms"); - fpscnt = 0; - interval = SDL_AddTimer(5000, interval_callback, NULL); - - /* - * 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). - */ - pending_many = 0; - while (!main_quit) { - thread_amsg_t *amsg; - SDL_Event ev; - int pending; - - /* Process incomming server messages. */ - /* - * XXX If receiving packets and drawing them is too CPU - * intensive, the client can hang here if the loop was - * infinite. Possibly that receiving complete frame data - * rather than single messages would cause less overhead. - * However it could possibly cause problems because of the - * packets which need to be aligned... Should we allow - * clients at 20fps and fallback to 10fps for slower boxes? - * I actually could support 20, 10, 5, and use heuristics for - * client to automatically send a slow down request to the - * server as necessary. Perhaps also request for faster - * updates when we notice that there's really no overload? - * How can we evaluate this efficiently? - * Checking if queue accumulates or not? And/or, checking the - * delay between two EndOfFrame packets? - */ - if ((amsg = (thread_amsg_t *)thread_msg_get(&main_port)) != - NULL) { - spackets_handle(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); - - if ((pending = thread_port_pending(&main_port)) == 0) { - pending_many = 0; - /* - * XXX On wincrap it appears that the reading thread - * can block indefinitely and thus -1 will freeze. - */ - (void) thread_ring_wait(&main_ring, 100); - } else { - if (++pending_many > 2) { - cpacket_slow_send(); - pending_many = 0; - /* Drop pending frames */ - while ((amsg = (thread_amsg_t *)thread_msg_get( - &main_port)) != NULL) - thread_amsg_destroy(amsg); - } else { - /* Drop single frame */ - if ((amsg = (thread_amsg_t *)thread_msg_get( - &main_port)) != NULL) - thread_amsg_destroy(amsg); - } - } - } - - /* Fadeout music */ - (void) Mix_FadeOutMusic(1000); - SDL_Delay(1000); - - 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 = 192; - else if (x == 1 && y == -1) - angle = 224; - else if (x == 1 && y == 0) - angle = 0; - else if (x == 1 && y == 1) - angle = 32; - else if (x == 0 && y == 1) - angle = 64; - else if (x == -1 && y == 1) - angle = 96; - else if (x == -1 && y == 0) - angle = 128; - else if (x == -1 && y == -1) - angle = 160; - - 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) { - main_quit = 1; - return; - } - - /* - * Button events. joy_angle affects several of them. - * We currently ignore release events. - */ - if (ev->type == SDL_JOYBUTTONDOWN) { - - if (ev->jbutton.state != 1) - return; - - switch (ev->jbutton.button) { - case 0: - send_event(UE_TORP); - break; - case 1: - send_event(UE_PHASER); - break; - case 2: - send_event(UE_DIRECTION, joy_angle); - break; - case 3: - send_event(UE_PLASMA); - break; - case 4: - send_event(UE_SHIELD); - break; - case 5: - send_event(UE_CLOAK); - break; - case 6: - send_event(UE_THRUST_ACC); - break; - case 7: - send_event(UE_THRUST_DEC); - break; - } - - return; - } - - /* - * Keyboard events - */ - if (ev->type == SDL_KEYDOWN) { - int angle = joy_angle; - - switch (ev->key.keysym.sym) { - /* Angle changes */ - case SDLK_i: - angle = 192; - break; - case SDLK_o: - angle = 224; - break; - case SDLK_l: - angle = 0; - break; - case SDLK_PERIOD: - angle = 32; - break; - case SDLK_COMMA: - angle = 64; - break; - case SDLK_m: - angle = 96; - break; - case SDLK_j: - angle = 128; - break; - case SDLK_u: - angle = 160; - break; - /* Navigation change */ - case SDLK_d: - send_event(UE_DIRECTION, joy_angle); - break; - case SDLK_z: - send_event(UE_THRUST_ACC); - break; - case SDLK_a: - send_event(UE_THRUST_DEC); - break; - /* Weapons */ - case SDLK_SPACE: - send_event(UE_TORP); - break; - case SDLK_f: - send_event(UE_PHASER); - break; - case SDLK_g: - send_event(UE_PLASMA); - break; - /* Toggles */ - case SDLK_s: - send_event(UE_SHIELD); - break; - case SDLK_w: - send_event(UE_CLOAK); - break; - default: - break; - } - - if (joy_angle != angle) - joy_angle = angle; - return; - } - - return; -} - -static void -send_event(int type, ...) -{ - int ch, err = 0; - - switch (type) { - case UE_DIRECTION: - err = cpacket_direction_send(((int *)&type)[1]); - break; - case UE_TORP: - if ((ch = Mix_PlayChannel(-1, snd_torp, 0)) != -1) - Mix_SetPosition(ch, joy_angle, 50); - err = cpacket_torp_send(joy_angle); - break; - case UE_PHASER: - /* XXX */ - if ((ch = Mix_PlayChannel(-1, snd_hit, 0)) != -1) - Mix_SetPosition(ch, joy_angle, 50); - break; - case UE_PLASMA: - /* XXX */ - if ((ch = Mix_PlayChannel(-1, snd_explode, 0)) != -1) - Mix_SetPosition(ch, joy_angle, 50); - break; - case UE_SHIELD: - shields = (shields == 0 ? 1 : 0); - Mix_PlayChannel(1, - (shields == 1 ? snd_shield : snd_unshield), 0); - err = cpacket_shield_send(); - break; - case UE_CLOAK: - cloaked = (cloaked == 0 ? 1 : 0); - Mix_PlayChannel(2, - (cloaked == 1 ? snd_cloak : snd_uncloak), 0); - err = cpacket_cloak_send(); - break; - case UE_THRUST_ACC: - err = cpacket_thrust_acc_send(); - break; - case UE_THRUST_DEC: - err = cpacket_thrust_dec_send(); - break; - } - - if (err == -1) { - (void) fprintf(stderr, "Error writing to server socket\n"); - exit(EXIT_FAILURE); - } -} - -/* Extern, called by spacket_eof_handler */ -void -frame_switch(void) -{ - - /* Draw text stats */ - font_blit_string(font, 17, 461, fpsstr, 0x00, 0x00, 0x00, 0xff); - font_blit_string(font, 16, 460, fpsstr, 0xff, 0xff, 0xff, 0xff); - font_blit_string(font, 101, 461, pingstr, 0x00, 0x00, 0x00, 0xff); - font_blit_string(font, 100, 460, pingstr, 0xff, 0xff, 0xff, 0xff); - font_blit_string(font, 201, 461, shipinfostr, 0x00, 0x00, 0x00, 0xff); - font_blit_string(font, 200, 460, shipinfostr, 0xff, 0xff, 0xff, 0xff); - - /* Switch buffers */ - (void) SDL_Flip(screen_surface); - - /* Clear new buffer */ - /* - { - uint32_t *ptr, *tptr; - - if (SDL_MUSTLOCK(screen_surface)) - SDL_LockSurface(screen_surface); - 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; - } - if (SDL_MUSTLOCK(screen_surface)) - SDL_UnlockSurface(screen_surface); - } - */ - (void) SDL_FillRect(screen_surface, NULL, 0x00000000); - - /* Update fps counter for stats */ - fpscnt++; -} - -void -ship_draw(int id, int x, int y, double angle, uint8_t flags) -{ - char str[8]; - - (void) surface_blit_angle(rship, x, y, angle); - if ((flags & SHIPF_CLOAK) != 0) - filledCircleRGBA(screen_surface, x, y, 25, - 0x00, 0x00, 0x00, 0x80); - if ((flags & SHIPF_SHIELD) != 0) { - aacircleRGBA(screen_surface, x, y, 25, - 0xf0, 0xf0, 0x20, 0xb0); - filledCircleRGBA(screen_surface, x, y, 25, - 0x20, 0x50, 0xf0, 0x50); - } - - x -= 24; - y -= 24; - (void) snprintf(str, 8, "%d", id); - font_blit_string(font, x, y, str, 0x00, 0x00, 0x00, 0xff); - font_blit_string(font, x + 1, y + 1, str, 0xff, 0xff, 0xff, 0xff); -} - -void -torp_draw(int x, int y, int r) -{ - static const uint8_t torp_colors[3][3] = { - { 0xff, 0xff, 0x00 }, /* 90% */ - { 0xff, 0x30, 0x00 }, /* 5% */ - { 0x00, 0x30, 0xff } /* 5% */ - }; - int rnd, c; - - rnd = rand() % 100; - if (rnd < 5) - c = 2; - else if (rnd < 10) - c = 1; - else - c = 0; - - filledCircleRGBA(screen_surface, x, y, 1 + (rand() % r), - torp_colors[c][0], torp_colors[c][1], torp_colors[c][2], - 0x7f + (rand() % 0x80)); -} - -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, int a) -{ - SDL_Surface *t; - SDL_Rect d; - int r; - - ASSERT(a > -1 && a < 256); - - /* - * Convert 0-255 angle to 0-359 and add 90 degrees so that shapes - * bitmaps can point upwards, although angle 0 should point - * rightwards, - */ - a = (a * 360 / 256) + 90; - - /* Interestingly, the angle has to be reversed... */ - 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 -interval_callback(Uint32 interval, void *args) -{ - - /* Calculate fps average in last 5 secs */ - (void) sprintf(fpsstr, "%03d fps", fpscnt / 5); - fpscnt = 0; - - /* Initiate ping */ - pingticks = SDL_GetTicks(); - cpacket_ping_send(); - - /* XXX Send faster updates request periodically */ - cpacket_fast_send(); - - return interval; -} - -/* Initialize trigonometric tables */ -static void -trig_init(void) -{ - int i; - double f; - - for (i = 0; i < 256; i++) { - f = ((2 * M_PI) / 256) * i; - sin_table[i] = sin(f); - cos_table[i] = cos(f); - } -} diff --git a/mmsoftware/mystic_ships/client/src/main.h b/mmsoftware/mystic_ships/client/src/main.h deleted file mode 100644 index 9ec699c..0000000 --- a/mmsoftware/mystic_ships/client/src/main.h +++ /dev/null @@ -1,40 +0,0 @@ -/* $Id: main.h,v 1.1 2006/12/31 08:32:39 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 - - - -void frame_switch(void); -void ship_draw(int, int, int, double, uint8_t); -void torp_draw(int, int, int); - - - -extern thread_port_t main_port; - -extern Uint32 pingticks; -extern char pingstr[8]; - -extern char shipinfostr[64]; - - - -#endif diff --git a/mmsoftware/mystic_ships/client/src/packets.c b/mmsoftware/mystic_ships/client/src/packets.c deleted file mode 100644 index 850872a..0000000 --- a/mmsoftware/mystic_ships/client/src/packets.c +++ /dev/null @@ -1,450 +0,0 @@ -/* $Id: packets.c,v 1.1 2006/12/31 08:32:39 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 -#include -#include -#include -#include -#include -#include -#include -#include - - - -static int spacket_auth_handler(uint16_t *); -static int spacket_ping_handler(uint16_t *); -static int spacket_eof_handler(uint16_t *); -static int spacket_ship_handler(uint16_t *); -static int spacket_shipinfo_handler(uint16_t *); -static int spacket_torp_handler(uint16_t *); -static int spacket_collision_handler(uint16_t *); -static int svpacket_message_handler(uint16_t *); - -static int cpacket_send(const void *, size_t); -#ifdef USE_ENCRYPTION -static int cpacket_send_callback(const void *, size_t, - void (*)(void *), void *); -static void cpacket_callback_encrypt(void *); -#endif - - - -struct packet_index spacket_index[SVPACKET_MAX] = { - /* Fixed sized packets types */ - {sizeof(struct spacket_auth), spacket_auth_handler}, - {sizeof(struct spacket_ping), spacket_ping_handler}, - {sizeof(struct spacket_pong), spacket_pong_handler}, - {sizeof(struct spacket_eof), spacket_eof_handler}, - {sizeof(struct spacket_ship), spacket_ship_handler}, - {sizeof(struct spacket_shipinfo), spacket_shipinfo_handler}, - {sizeof(struct spacket_torp), spacket_torp_handler}, - {sizeof(struct spacket_collision), spacket_collision_handler}, - /* Dynamic sized packets types */ - {sizeof(struct svpacket_message), svpacket_message_handler} -}; - - - -/* - * There generally are multiple aligned packets into the supplied buffer. - * Process each of them calling their handler function. - */ -void -spackets_handle(thread_amsg_t *amsg) -{ - uint8_t *data; - size_t size, off, len; - - if (amsg->size == -1) { - (void) fprintf(stderr, "Error reading from server socket\n"); - exit(EXIT_FAILURE); - } - - data = (uint8_t *)amsg->data; - size = amsg->size; - - for (off = 0; off < size; off += len) { - int8_t id; - - /* - * Obtain packet type and evaluate its length. - * Because they are 16-bit aligned, make sure to - * increase len as necessary if it is odd. - */ - id = (int8_t)data[off]; - if (id >= SPACKET_MAX) - len = data[off + 1]; - else - len = spacket_index[id].size; - if ((len & 1) != 0) - len++; - - /* Kill client on packet processing error */ - ASSERT(spacket_index[id].handler != NULL); - if (spacket_index[id].handler((uint16_t *)&data[off]) == -1) { - (void) fprintf(stderr, - "Error processing server packet 0x%02x\n", id); - exit(EXIT_FAILURE); - } - } -} - -static int -spacket_auth_handler(uint16_t *ptr) -{ - struct spacket_auth *p = (struct spacket_auth *)ptr; - - if (p->protocol_version != CLIENT_VERSION) { - (void) fprintf(stderr, - "Client and server versions do not match\n"); - exit(EXIT_FAILURE); - } - - return cpacket_auth_send(CLIENT_LOGIN, CLIENT_PASSWD, - p->noncerand1, p->noncerand2); -} - -/* ARGSUSED */ -static int -spacket_ping_handler(uint16_t *ptr) -{ - - /* Handled by the recvq code for better accuracy */ - /* NOOP */ - - return 0; -} - -/* Special since called by recvq.c */ -/* ARGSUSED */ -int -spacket_pong_handler(uint16_t *ptr) -{ - - (void) snprintf(pingstr, 8, "%04d ms", SDL_GetTicks() - pingticks); - - return 0; -} - -/* ARGSUSED */ -static int -spacket_eof_handler(uint16_t *ptr) -{ - - frame_switch(); - - return 0; -} - -static int -spacket_ship_handler(uint16_t *ptr) -{ - struct spacket_ship *p = (struct spacket_ship *)ptr; - - p->id = BYTEORDER_HOST16(p->id); - p->x = BYTEORDER_HOST16(p->x); - p->y = BYTEORDER_HOST16(p->y); - - ship_draw(p->id, p->x, p->y, p->angle, p->flags); - - return 0; -} - -static int -spacket_shipinfo_handler(uint16_t *ptr) -{ - struct spacket_shipinfo *p = (struct spacket_shipinfo *)ptr; - - p->fuel = BYTEORDER_HOST16(p->fuel); - p->shield = BYTEORDER_HOST16(p->shield); - p->hull = BYTEORDER_HOST16(p->hull); - - (void) snprintf(shipinfostr, sizeof(shipinfostr), - "Thrust: %2d, Fuel: %5d, Shield: %3d, Hull: %3d", - p->thrust, p->fuel, p->shield, p->hull); - - return 0; -} - -static int -spacket_torp_handler(uint16_t *ptr) -{ - struct spacket_torp *p = (struct spacket_torp *)ptr; - - p->x = BYTEORDER_HOST16(p->x); - p->y = BYTEORDER_HOST16(p->y); - - torp_draw(p->x, p->y, p->radius); - - return 0; -} - -static int -spacket_collision_handler(uint16_t *ptr) -{ - /*struct spacket_collision *p = (struct spacket_collision *)ptr;*/ - - /* XXX */ - - return 0; -} - -static int -svpacket_message_handler(uint16_t *ptr) -{ - /*struct svpacket_message *p = (struct svpacket_message *)ptr;*/ - - /* XXX */ - - return 0; -} - -/* - * Send asynchroneous message to the network sender thread with the data. - */ -static int -cpacket_send(const void *buf, size_t size) -{ - thread_amsg_t *amsg; - - if ((amsg = thread_amsg_create()) == NULL) { - (void) fprintf(stderr, - "cpacket_send() - thread_amsg_create()\n"); - exit(EXIT_FAILURE); - } - - (void) memcpy(amsg->data, buf, size); - amsg->size = size; - (void) thread_msg_put(&send_port, &amsg->msg); - - return 0; -} - -#ifdef USE_ENCRYPTION -static int -cpacket_send_callback(const void *buf, size_t size, - void (*callback)(void *), void *callback_arg) -{ - thread_amsg_t *amsg; - - if ((amsg = thread_amsg_create()) == NULL) { - (void) fprintf(stderr, - "cpacket_send() - thread_amsg_create()\n"); - exit(EXIT_FAILURE); - } - - thread_amsg_setcallback(amsg, callback, callback_arg); - (void) memcpy(amsg->data, buf, size); - amsg->size = size; - (void) thread_msg_put(&send_port, &amsg->msg); - - return 0; -} - -/* ARGSUSED */ -static void -cpacket_callback_encrypt(void *arg) -{ - - /* Enable encryption */ - mmencrypt = 1; -} -#endif /* USE_ENCRYPTION */ - -int -cpacket_auth_send(const char *login, const char *passwd, - const uint8_t *noncerand1, const uint8_t *noncerand2) -{ - struct cpacket_auth p; - uint8_t hmac1[40], hmac2[40], ppasswd[32]; - int ret; -#ifdef USE_ENCRYPTION - uint8_t key[512]; -#endif - - p.packet_type = CPACKET_AUTH; - p.protocol_version = CLIENT_VERSION; - - /* Pad login string */ - (void) memset(p.login, '\0', 16); - (void) strncpy((char *)p.login, login, 15); - - /* - * Calculate sha1 and rmd160 hmac hashes for password and place first - * 80 bits of each into the hmac field. This is calculated using the - * nonce+random data previously obtained from the server. We do it - * for both noncerand. - */ - - /* Pad passwd */ - (void) memset(ppasswd, '\0', 32); - (void) strncpy((char *)ppasswd, passwd, 31); - - hmac_sha1(noncerand1, 32, ppasswd, 32, hmac1); - hmac_rmd160(noncerand1, 32, ppasswd, 32, &hmac1[20]); - (void) memcpy(p.hmac1, hmac1, 10); - (void) memcpy(&p.hmac1[10], &hmac1[20], 10); - - hmac_sha1(noncerand2, 32, ppasswd, 32, hmac2); - hmac_rmd160(noncerand2, 32, ppasswd, 32, &hmac2[20]); - (void) memcpy(p.hmac2, hmac2, 10); - (void) memcpy(&p.hmac2[10], &hmac2[20], 10); - -#ifdef USE_ENCRYPTION - /* - * Use the full hmac results (320 bits) to create a session private - * key. This is done by using the hmac process again using the - * current full hmac against the user password. This generates a - * 320 bits key for use with the mmenc cipher, which is immediately - * enabled. We need to send our previous hmac back to the server - * under the new tunnel. This is done to create an input key with - * hmac1 and output one with hmac2. - */ - hmac_sha1(hmac1, 40, ppasswd, 32, key); - hmac_rmd160(hmac1, 40, ppasswd, 32, &key[20]); - mmenc_init(&enc_in, key, 40); - - hmac_sha1(hmac2, 40, ppasswd, 32, key); - hmac_rmd160(hmac2, 40, ppasswd, 32, &key[20]); - mmenc_init(&enc_out, key, 40); - - (void) memset(key, '\0', 40); - - ret = cpacket_send_callback(&p, sizeof(p), - cpacket_callback_encrypt, NULL); -#else - ret = cpacket_send(&p, sizeof(p)); -#endif - - (void) memset(hmac1, '\0', 40); - (void) memset(hmac2, '\0', 40); - (void) memset(ppasswd, '\0', 32); - - return ret; -} - -int -cpacket_ping_send(void) -{ - struct cpacket_ping p; - - p.packet_type = CPACKET_PING; - - return cpacket_send(&p, sizeof(p)); -} - -int -cpacket_pong_send(void) -{ - struct cpacket_pong p; - - p.packet_type = CPACKET_PONG; - - return cpacket_send(&p, sizeof(p)); -} - -int -cpacket_direction_send(int angle) -{ - struct cpacket_direction p; - - p.packet_type = CPACKET_DIRECTION; - p.angle = (uint8_t)angle; - - return cpacket_send(&p, sizeof(p)); -} - -int -cpacket_thrust_acc_send(void) -{ - struct cpacket_thrust_acc p; - - p.packet_type = CPACKET_THRUST_ACC; - - return cpacket_send(&p, sizeof(p)); -} - -int -cpacket_thrust_dec_send(void) -{ - struct cpacket_thrust_dec p; - - p.packet_type = CPACKET_THRUST_DEC; - - return cpacket_send(&p, sizeof(p)); -} - -int -cpacket_shield_send(void) -{ - struct cpacket_shield p; - - p.packet_type = CPACKET_SHIELD; - - return cpacket_send(&p, sizeof(p)); -} - -int -cpacket_cloak_send(void) -{ - struct cpacket_cloak p; - - p.packet_type = CPACKET_CLOAK; - - return cpacket_send(&p, sizeof(p)); -} - -int -cpacket_torp_send(int angle) -{ - struct cpacket_torp p; - - p.packet_type = CPACKET_TORP; - p.angle = (uint8_t)angle; - - return cpacket_send(&p, sizeof(p)); -} - -int -cpacket_slow_send(void) -{ - struct cpacket_slow p; - - p.packet_type = CPACKET_SLOW; - - return cpacket_send(&p, sizeof(p)); -} - -int -cpacket_fast_send(void) -{ - struct cpacket_fast p; - - p.packet_type = CPACKET_FAST; - - return cpacket_send(&p, sizeof(p)); -} diff --git a/mmsoftware/mystic_ships/client/src/packets.h b/mmsoftware/mystic_ships/client/src/packets.h deleted file mode 100644 index b399545..0000000 --- a/mmsoftware/mystic_ships/client/src/packets.h +++ /dev/null @@ -1,57 +0,0 @@ -/* $Id: packets.h,v 1.1 2006/12/31 08:32:39 mmondor Exp $ */ - -/* - * Copyright (c) Matthew Mondor - * ALL RIGHTS RESERVED. - */ - - - -#ifndef _PACKETS_H_ -#define _PACKETS_H_ - - - -#include -#include -#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 *); -}; - - - -void spackets_handle(thread_amsg_t *); -int spacket_pong_handler(uint16_t *); -int cpacket_auth_send(const char *, const char *, const uint8_t *, - const uint8_t *); -int cpacket_ping_send(void); -int cpacket_pong_send(void); -int cpacket_direction_send(int); -int cpacket_thrust_acc_send(void); -int cpacket_thrust_dec_send(void); -int cpacket_shield_send(void); -int cpacket_cloak_send(void); -int cpacket_torp_send(int); -int cpacket_slow_send(void); -int cpacket_fast_send(void); - - - -extern struct packet_index spacket_index[SVPACKET_MAX]; - - - -#endif diff --git a/mmsoftware/mystic_ships/client/src/pool.c b/mmsoftware/mystic_ships/client/src/pool.c deleted file mode 100644 index 65f6ac3..0000000 --- a/mmsoftware/mystic_ships/client/src/pool.c +++ /dev/null @@ -1,417 +0,0 @@ -/* $Id: pool.c,v 1.1 2006/12/31 08:32:39 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/mmsoftware/mystic_ships/client/src/pool.h b/mmsoftware/mystic_ships/client/src/pool.h deleted file mode 100644 index b34a248..0000000 --- a/mmsoftware/mystic_ships/client/src/pool.h +++ /dev/null @@ -1,122 +0,0 @@ -/* $Id: pool.h,v 1.1 2006/12/31 08:32:39 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/mmsoftware/mystic_ships/client/src/rawobjs.h b/mmsoftware/mystic_ships/client/src/rawobjs.h deleted file mode 100644 index dc993d1..0000000 --- a/mmsoftware/mystic_ships/client/src/rawobjs.h +++ /dev/null @@ -1,79 +0,0 @@ -/* $Id: rawobjs.h,v 1.1 2006/12/31 08:32:39 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/mmsoftware/mystic_ships/client/src/recvq.c b/mmsoftware/mystic_ships/client/src/recvq.c deleted file mode 100644 index 3596f2f..0000000 --- a/mmsoftware/mystic_ships/client/src/recvq.c +++ /dev/null @@ -1,318 +0,0 @@ -/* $Id: recvq.c,v 1.1 2006/12/31 08:32:39 mmondor Exp $ */ - -/* - * Copyright (c) 2006, Matthew Mondor - * ALL RIGHTS RESERVED. - */ - - - -#include -#include -#include - -#include - -#include - -#ifdef USE_COMPRESSION -#include -#endif - -#include -#include -#ifdef USE_ENCRYPTION -#include -#endif - - - -#define RBUFFER_SIZE 4096 -#ifdef USE_COMPRESSION -#define IBUFFER_SIZE (RBUFFER_SIZE * 64) -#endif - - - -static int8_t rbuffer[RBUFFER_SIZE]; -#ifdef USE_COMPRESSION -static char ibuffer[IBUFFER_SIZE]; -#endif - - - -int -recvq_init(recvq_t *q, TCPsocket sock, size_t size) -{ - - if ((size & 1) != 0) - size++; - - if ((q->buffer = malloc(size)) != NULL) { -#ifdef USE_COMPRESSION - q->zin.next_in = q->zin.next_out = NULL; - q->zin.avail_in = q->zin.avail_out = 0; - q->zin.zalloc = NULL; - q->zin.zfree = NULL; - q->zin.opaque = NULL; - if (inflateInit(&q->zin) == Z_OK) { -#endif - q->sock = sock; - q->size = size; - q->tail = q->ptail = 0; - - q->variable = q->last = -1; - q->length = -1; - - return 0; -#ifdef USE_COMPRESSION - } else - free(q->buffer); -#endif - } - - return -1; -} - -void -recvq_destroy(recvq_t *q) -{ - - free(q->buffer); -#ifdef USE_COMPRESSION - (void) inflateEnd(&q->zin); -#endif -} - -/* - * Reads up to RBUFFER_SIZE of ready data from the socket and fills the - * received packets queue buffer. Buffered packets are 16-bit aligned with 0 - * padding. Minimizes calls to SDLNet_TCP_Recv by using a buffer and - * recopying it instead of reading one byte at a time for the packet type - * and length. - * Automatically proceses SPACKET_PING for accurate latency evaluation, - * which packets will not be queued. - * Returns 0 on success, or -1 on error or 1 when an End Of Frame packet - * was just received. - */ -int -recvq_read(recvq_t *q) -{ - int s, eof; - int8_t *ptr, *tptr, *buf; -#ifdef USE_COMPRESSION - int ret; -#endif - - /* - * First read available data in an unaligned buffer, which may then - * begin and end by partial packets and contain multiple unaligned - * packets. - */ - if ((s = SDLNet_TCP_Recv(q->sock, rbuffer, RBUFFER_SIZE)) == -1 || - s == 0) - return -1; - -#ifdef USE_ENCRYPTION - /* Decrypt incomming data */ - if (mmencrypt) - mmenc_decrypt(&enc_in, (uint8_t *)rbuffer, s); -#endif - - /* Decompress incomming data */ -#ifdef USE_COMPRESSION - q->zin.next_in = (Bytef *)rbuffer; - q->zin.avail_in = s; - q->zin.next_out = (Bytef *)ibuffer; - q->zin.avail_out = IBUFFER_SIZE; - if ((ret = inflate(&q->zin, Z_SYNC_FLUSH)) != Z_OK && - ret != Z_STREAM_END) - return -1; - buf = (int8_t *)ibuffer; - s = IBUFFER_SIZE - q->zin.avail_out; -#else - buf = rbuffer; -#endif /* USE_COMPRESSION */ - - /* - * Process previous raw buffer and queue aligned full packets. - * We take care to verify recvq boundaries when adding even bytes, - * we know that the buffer is 16-bit aligned. - */ - eof = 0; - for (ptr = buf, tptr = &buf[s]; ptr < tptr; ) { - if (q->length != -1) { - /* Still reading incomplete packet */ - - if (q->length > 0) { - /* Byte may be odd or even check recvq limit */ - if (q->tail == q->size) { - (void) fprintf(stderr, - "recv_read() - recvq full\n"); - return -1; - } - q->buffer[q->tail++] = *ptr++; - q->length--; - } - if (q->length == 0) { -complete: - /* Packet complete, align buffer for next */ - q->length = -1; - if ((q->tail & 1) != 0) - q->buffer[q->tail++] = 0; - - /* - * Immediately process ping request packets - * since we must properly permit to measure - * the network latency. We also dequeue ping - * packets transparently. - * We only allow one ping request from server - * heartbeat cycle, and we only allow them if - * the client is already authenticated. - * Note that server pong packets are sent - * directly rather than passing through the - * sendq, unlike other packets. - */ - if (q->last == SPACKET_PING) { - /* Discard packet from queue */ - q->tail -= 2; - if (cpacket_pong_send() == -1) { - (void) fprintf(stderr, - "recvq_read() - " - "spacket_pong_send()\n"); - return -1; - } - continue; - } - - /* Same with incomming pong replies */ - if (q->last == SPACKET_PONG) { - q->tail -= 2; - (void) spacket_pong_handler(NULL); - continue; - } - - /* - * If we received an End Of Frame packet, - * we must be sure to return with 1 on - * success. - */ - eof = 1; - - /* - * Update the completed packets tail pointer, - * ptail. - */ - q->ptail = q->tail; - - continue; - } - } else { - /* - * New packet. We must evaluate packet length by - * packet type, and take into consideration variable - * packets which length will be provided as a second - * byte. For variable packets, we must make sure that - * the received length is at least high enough for the - * minimum length of the packet. - */ - if (q->variable != -1) { - /* Receiving length of variable packet */ - q->length = (ssize_t)((uint8_t)*ptr); - if (q->length < - spacket_index[q->variable].size) { - (void) fprintf(stderr, - "recvq_read() - " - "Invalid packet size %d\n", - (int)q->length); - q->variable = -1; - q->length = -1; - return -1; - } - q->variable = -1; - - /* Odd */ - q->buffer[q->tail++] = *ptr++; - q->length--; - } else { - int8_t t; - - /* Receiving type of packet. Even */ - if (q->tail == q->size) { - (void) fprintf(stderr, - "recv_read() - recvq full\n"); - return -1; - } - t = (q->buffer[q->tail++] = *ptr++); - q->last = t; - - if (t < 0 || t > SVPACKET_MAX) { - (void) fprintf(stderr, - "recv_read() - " - "Invalid packet type 0x%02x\n", - (uint8_t)t); - return -1; - } - if (t < SPACKET_MAX) { - /* Fixed packet */ - q->length = spacket_index[t].size - 1; - q->variable = -1; - /* - * Special provision for single byte - * packets - */ - if (q->length == 0) - goto complete; - } else { - /* Variable packet, awaiting size */ - q->length = -1; - q->variable = t; - } - } - } - } - - if (eof) - return 1; - - return 0; -} - -/* - * Returns a pointer to the starting point of the buffer and the size of the - * buffer. May contain multiple contiguous 16-bit aligned packets with - * padding. Only full packets are returned. - */ -void -recvq_content(recvq_t *q, uint8_t **buf, size_t *size) -{ - - *buf = (uint8_t *)q->buffer; - *size = q->ptail; - if ((*size & 1) != 0) - (*size)++; -} - -/* - * To be called after recvq_content() supplied buffer is processed. - * Repositions offsets while making sure to preserve any previously incomplete - * packets. - */ -void -recvq_content_reset(recvq_t *q) -{ - - if (q->ptail == 0) - return; - - if (q->tail > q->ptail) { - register size_t s; - - s = q->tail - q->ptail; - (void) memmove(q->buffer, &q->buffer[q->ptail], s); - q->tail = s; - } else - q->tail = 0; - - q->ptail = 0; -} diff --git a/mmsoftware/mystic_ships/client/src/recvq.h b/mmsoftware/mystic_ships/client/src/recvq.h deleted file mode 100644 index bde3d13..0000000 --- a/mmsoftware/mystic_ships/client/src/recvq.h +++ /dev/null @@ -1,44 +0,0 @@ -/* $Id: recvq.h,v 1.1 2006/12/31 08:32:39 mmondor Exp $ */ - -/* - * Copyright (c) 2006, Matthew Mondor - * ALL RIGHTS RESERVED. - */ - - - -#ifndef _RECVQ_H_ -#define _RECVQ_H_ - - - -#include - -#include -#include - -#include - - - -typedef struct recvq { - TCPsocket sock; - uint8_t *buffer; - size_t size, tail, ptail; - /* Necessary for fragmented packets reassembly */ - int variable, last; - ssize_t length; - z_stream zin; -} recvq_t; - - - -int recvq_init(recvq_t *, TCPsocket sock, size_t); -void recvq_destroy(recvq_t *); -int recvq_read(recvq_t *); -void recvq_content(recvq_t *, uint8_t **, size_t *); -void recvq_content_reset(recvq_t *); - - - -#endif diff --git a/mmsoftware/mystic_ships/client/src/screen.c b/mmsoftware/mystic_ships/client/src/screen.c deleted file mode 100644 index 5617a63..0000000 --- a/mmsoftware/mystic_ships/client/src/screen.c +++ /dev/null @@ -1,65 +0,0 @@ -/* $Id: screen.c,v 1.1 2006/12/31 08:32:39 mmondor Exp $ */ - -/* - * Copyright (c) 2006, Matthew Mondor - * ALL RIGHTS RESERVED. - */ - -/* - * Display related initialization. - */ - - - -#include -#include - -#include - -#include -#include - - - -#define SCREEN_WIDTH 640 -#define SCREEN_HEIGHT 480 - - - -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/mmsoftware/mystic_ships/client/src/screen.h b/mmsoftware/mystic_ships/client/src/screen.h deleted file mode 100644 index 1c901a8..0000000 --- a/mmsoftware/mystic_ships/client/src/screen.h +++ /dev/null @@ -1,34 +0,0 @@ -/* $Id: screen.h,v 1.1 2006/12/31 08:32:39 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/mmsoftware/mystic_ships/client/src/thread_msg.c b/mmsoftware/mystic_ships/client/src/thread_msg.c deleted file mode 100644 index c458dae..0000000 --- a/mmsoftware/mystic_ships/client/src/thread_msg.c +++ /dev/null @@ -1,456 +0,0 @@ -/* $Id: thread_msg.c,v 1.1 2006/12/31 08:32:39 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); - amsg->callback = NULL; - } - - return amsg; -} - -void -thread_amsg_setcallback(thread_amsg_t *msg, - void (*callback)(void *), void *callback_arg) -{ - - ASSERT(msg != NULL); - msg->callback = callback; - msg->callback_arg = callback_arg; -} - -void -thread_amsg_callback(thread_amsg_t *msg) -{ - - ASSERT(msg != NULL); - - if (msg->callback != NULL) - msg->callback(msg->callback_arg); -} - -/* - * 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); - } else - (void) fprintf(stderr, - "thread_amsg_destroy() - SDL_LockMutex() - %s", - SDL_GetError()); -} diff --git a/mmsoftware/mystic_ships/client/src/thread_msg.h b/mmsoftware/mystic_ships/client/src/thread_msg.h deleted file mode 100644 index 38b71ec..0000000 --- a/mmsoftware/mystic_ships/client/src/thread_msg.h +++ /dev/null @@ -1,91 +0,0 @@ -/* $Id: thread_msg.h,v 1.1 2006/12/31 08:32:39 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 32768 - -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 pnode; - uint32_t magic; - thread_port_t *reply; -} thread_msg_t; - -typedef struct { - thread_msg_t msg; - void (*callback)(void *); - void *callback_arg; - 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_setcallback(thread_amsg_t *, - void (*)(void *), void *); -extern void thread_amsg_callback(thread_amsg_t *); -extern void thread_amsg_destroy(thread_amsg_t *); - - - -#endif diff --git a/mmsoftware/mystic_ships/client/src/thread_net_recv.c b/mmsoftware/mystic_ships/client/src/thread_net_recv.c deleted file mode 100644 index 3f593e5..0000000 --- a/mmsoftware/mystic_ships/client/src/thread_net_recv.c +++ /dev/null @@ -1,188 +0,0 @@ -/* $Id: thread_net_recv.c,v 1.1 2006/12/31 08:32:39 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 -#include -#include - - - -static void ssend(thread_msg_t *); -static int state_connect(void); -static void state_recv(void); - - - -static thread_ring_t recv_ring; -static thread_port_t recv_port; - - - -TCPsocket server_socket = NULL; -recvq_t recvq; - - - -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"; - } - - if (recvq_init(&recvq, server_socket, 16384) == -1) { - msg.status = -1; - msg.error = "Could not allocate memory for recvq buffer"; - } - -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 *buf; - size_t size; - int res; - - /* - * 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); - } - - /* - * Call recvq_read() until we either have 1, in which case - * we'll send an async message, or -1 for error. - */ - while ((res = recvq_read(&recvq)) == 0) ; - - if (res == -1) { - (void) fprintf(stderr, - "Connection with server lost\n"); - break; - } - - /* - * res == 1. We therefore can fill our message and dispatch - * it asynchroneously. - */ - recvq_content(&recvq, &buf, &size); - if (size > AMSG_MAXSIZE) { - (void) fprintf(stderr, - "state_recv() - increase AMSG_MAXSIZE!\n"); - exit(EXIT_FAILURE); - } - (void) memcpy(amsg->data, buf, size); - amsg->size = size; - recvq_content_reset(&recvq); - - /* 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); -} diff --git a/mmsoftware/mystic_ships/client/src/thread_net_recv.h b/mmsoftware/mystic_ships/client/src/thread_net_recv.h deleted file mode 100644 index 09f8811..0000000 --- a/mmsoftware/mystic_ships/client/src/thread_net_recv.h +++ /dev/null @@ -1,65 +0,0 @@ -/* $Id: thread_net_recv.h,v 1.1 2006/12/31 08:32:39 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/mmsoftware/mystic_ships/client/src/thread_net_send.c b/mmsoftware/mystic_ships/client/src/thread_net_send.c deleted file mode 100644 index 0d3a73f..0000000 --- a/mmsoftware/mystic_ships/client/src/thread_net_send.c +++ /dev/null @@ -1,129 +0,0 @@ -/* $Id: thread_net_send.c,v 1.1 2006/12/31 08:32:39 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 - -#ifdef USE_COMPRESSION -#include -#endif - -#include -#include -#ifdef USE_ENCRYPTION -#include -#endif - - - -#define DBUFFER_SIZE 4096 - - - -static thread_ring_t send_ring; -#ifdef USE_COMPRESSION -static char dbuffer[DBUFFER_SIZE]; -#endif - - - -thread_port_t send_port; - - - -int -thread_net_send(void *data) -{ - thread_amsg_t *amsg; -#ifdef USE_COMPRESSION - z_stream zout; - - /* Initialize outgoing data compressor */ - zout.next_in = zout.next_out = NULL; - zout.avail_in = zout.avail_out = 0; - zout.zalloc = NULL; - zout.zfree = NULL; - zout.opaque = NULL; - if (deflateInit(&zout, Z_DEFAULT_COMPRESSION) != Z_OK) { - (void) fprintf(stderr, "thread_net_recv() - deflateInit()\n"); - exit(EXIT_FAILURE); - } -#endif - - /* 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) { - uint8_t *buf; - size_t s; - -#ifdef USE_COMPRESSION - /* Compress outgoing data */ - zout.next_in = (void *)amsg->data; - zout.avail_in = amsg->size; - zout.next_out = (Bytef *)dbuffer; - zout.avail_out = DBUFFER_SIZE; - if (deflate(&zout, Z_SYNC_FLUSH) != Z_OK) { - (void) fprintf(stderr, - "thread_net_send() - deflate()\n"); - exit(EXIT_FAILURE); - } - buf = (uint8_t *)dbuffer; - s = DBUFFER_SIZE - zout.avail_out; -#else - buf = (uint8_t *)amsg->data; - s = amsg->size; -#endif /* USE_COMPRESSION */ - if (s > 0) { -#ifdef USE_ENCRYPTION - /* Encrypt outgoing data */ - if (mmencrypt) - mmenc_encrypt(&enc_out, buf, s); -#endif - if (SDLNet_TCP_Send(server_socket, buf, s) - != s) - (void) fprintf(stderr, - "Error writing to server socket\n"); - } - thread_amsg_callback(amsg); - thread_amsg_destroy(amsg); - } - (void) thread_ring_wait(&send_ring, -1); - } - - /* NOTREACHED */ - - return 0; -} diff --git a/mmsoftware/mystic_ships/client/src/thread_net_send.h b/mmsoftware/mystic_ships/client/src/thread_net_send.h deleted file mode 100644 index 7eec9ef..0000000 --- a/mmsoftware/mystic_ships/client/src/thread_net_send.h +++ /dev/null @@ -1,35 +0,0 @@ -/* $Id: thread_net_send.h,v 1.1 2006/12/31 08:32:39 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/mmsoftware/mystic_ships/client/wav/nt_cloaked.wav b/mmsoftware/mystic_ships/client/wav/nt_cloaked.wav deleted file mode 100755 index 3e010dd..0000000 Binary files a/mmsoftware/mystic_ships/client/wav/nt_cloaked.wav and /dev/null differ diff --git a/mmsoftware/mystic_ships/client/wav/nt_explosion_other.wav b/mmsoftware/mystic_ships/client/wav/nt_explosion_other.wav deleted file mode 100755 index b7ba8cb..0000000 Binary files a/mmsoftware/mystic_ships/client/wav/nt_explosion_other.wav and /dev/null differ diff --git a/mmsoftware/mystic_ships/client/wav/nt_fire_torp_other.wav b/mmsoftware/mystic_ships/client/wav/nt_fire_torp_other.wav deleted file mode 100755 index dddf2c6..0000000 Binary files a/mmsoftware/mystic_ships/client/wav/nt_fire_torp_other.wav and /dev/null differ diff --git a/mmsoftware/mystic_ships/client/wav/nt_plasma_hit.wav b/mmsoftware/mystic_ships/client/wav/nt_plasma_hit.wav deleted file mode 100755 index 73ecd7c..0000000 Binary files a/mmsoftware/mystic_ships/client/wav/nt_plasma_hit.wav and /dev/null differ diff --git a/mmsoftware/mystic_ships/client/wav/nt_shield_down.wav b/mmsoftware/mystic_ships/client/wav/nt_shield_down.wav deleted file mode 100755 index 0888f08..0000000 Binary files a/mmsoftware/mystic_ships/client/wav/nt_shield_down.wav and /dev/null differ diff --git a/mmsoftware/mystic_ships/client/wav/nt_shield_up.wav b/mmsoftware/mystic_ships/client/wav/nt_shield_up.wav deleted file mode 100755 index 909f72c..0000000 Binary files a/mmsoftware/mystic_ships/client/wav/nt_shield_up.wav and /dev/null differ diff --git a/mmsoftware/mystic_ships/client/wav/nt_uncloak.wav b/mmsoftware/mystic_ships/client/wav/nt_uncloak.wav deleted file mode 100755 index e4df979..0000000 Binary files a/mmsoftware/mystic_ships/client/wav/nt_uncloak.wav and /dev/null differ diff --git a/mmsoftware/mystic_ships/common/encrypt_mmenc.c b/mmsoftware/mystic_ships/common/encrypt_mmenc.c deleted file mode 100644 index f2a265c..0000000 --- a/mmsoftware/mystic_ships/common/encrypt_mmenc.c +++ /dev/null @@ -1,116 +0,0 @@ -/* $Id: encrypt_mmenc.c,v 1.1 2006/12/31 08:32:40 mmondor Exp $ */ - -/* - * Copyright (c) 2006, Matthew Mondor - * ALL RIGHTS RESERVED. - */ - - -#include -#include -#include -#include - -#include - - - -#define RBUF_SIZE 4096 - - - -int main(int, char **); - -static mmenc_t *key_load(const char *); -static void key_free(mmenc_t *); - - - -int -main(int argc, char **argv) -{ - char rbuf[RBUF_SIZE]; - size_t size; - mmenc_t *ctx; - - if (argc != 2) { - (void) fprintf(stderr, "Usage: mmenc <4096-bit-keyfile>\n"); - exit(EXIT_FAILURE); - } - if ((ctx = key_load(argv[1])) == NULL) { - perror("key_load()"); - exit(EXIT_FAILURE); - } - - if (setvbuf(stdin, NULL, _IONBF, 0) == EOF) - perror("setvbuf(stdin)"); - if (setvbuf(stdout, NULL, _IONBF, 0) == EOF) - perror("setvbuf(stdout)"); - - while (!feof(stdin) && !ferror(stdin)) { - if ((size = fread(rbuf, 1, RBUF_SIZE, stdin)) < 1) - continue; - mmenc_encrypt(ctx, rbuf, size); - if (fwrite(rbuf, 1, size, stdout) != size) { - perror("fwrite()"); - exit(EXIT_FAILURE); - } - } - - key_free(ctx); - - exit(feof(stdin) ? EXIT_SUCCESS : EXIT_FAILURE); -} - -static mmenc_t * -key_load(const char *file) -{ - mmenc_t *ctx = NULL; - uint8_t *key = NULL; - FILE *fh = NULL; - - if ((ctx = malloc(sizeof(mmenc_t))) == NULL || - (key = malloc(512)) == NULL) { - perror("malloc()"); - goto err; - } - - if ((fh = fopen(file, "r")) == NULL) { - perror("fopen()"); - goto err; - } - (void) setvbuf(fh, NULL, _IONBF, 0); - if ((fread(key, 1, 512, fh)) != 512) { - perror("fread()"); - goto err; - } - - (void) fclose(fh); - mmenc_init(ctx, key, 512, 4096); - (void) memset(key, '\0', 512); - free(key); - - return ctx; - -err: - if (fh) - (void) fclose(fh); - if (key) { - (void) memset(key, '\0', 512); - free(key); - } - if (ctx) { - mmenc_destroy(ctx); - free(ctx); - } - - return NULL; -} - -static void -key_free(mmenc_t *key) -{ - - mmenc_destroy(key); - free(key); -} diff --git a/mmsoftware/mystic_ships/common/encrypt_rc4.c b/mmsoftware/mystic_ships/common/encrypt_rc4.c deleted file mode 100644 index 306845e..0000000 --- a/mmsoftware/mystic_ships/common/encrypt_rc4.c +++ /dev/null @@ -1,116 +0,0 @@ -/* $Id: encrypt_rc4.c,v 1.1 2006/12/31 08:32:40 mmondor Exp $ */ - -/* - * Copyright (c) 2006, Matthew Mondor - * ALL RIGHTS RESERVED. - */ - - -#include -#include -#include -#include - -#include - - - -#define RBUF_SIZE 4096 - - - -int main(int, char **); - -static rc4_t *key_load(const char *); -static void key_free(rc4_t *); - - - -int -main(int argc, char **argv) -{ - char rbuf[RBUF_SIZE]; - size_t size; - rc4_t *ctx; - - if (argc != 2) { - (void) fprintf(stderr, "Usage: rc4 <4096-bit-keyfile>\n"); - exit(EXIT_FAILURE); - } - if ((ctx = key_load(argv[1])) == NULL) { - perror("key_load()"); - exit(EXIT_FAILURE); - } - - if (setvbuf(stdin, NULL, _IONBF, 0) == EOF) - perror("setvbuf(stdin)"); - if (setvbuf(stdout, NULL, _IONBF, 0) == EOF) - perror("setvbuf(stdout)"); - - while (!feof(stdin) && !ferror(stdin)) { - if ((size = fread(rbuf, 1, RBUF_SIZE, stdin)) < 1) - continue; - rc4_encrypt(ctx, rbuf, size); - if (fwrite(rbuf, 1, size, stdout) != size) { - perror("fwrite()"); - exit(EXIT_FAILURE); - } - } - - key_free(ctx); - - exit(feof(stdin) ? EXIT_SUCCESS : EXIT_FAILURE); -} - -static rc4_t * -key_load(const char *file) -{ - rc4_t *ctx = NULL; - uint8_t *key = NULL; - FILE *fh = NULL; - - if ((ctx = malloc(sizeof(rc4_t))) == NULL || - (key = malloc(512)) == NULL) { - perror("malloc()"); - goto err; - } - - if ((fh = fopen(file, "r")) == NULL) { - perror("fopen()"); - goto err; - } - (void) setvbuf(fh, NULL, _IONBF, 0); - if ((fread(key, 1, 512, fh)) != 512) { - perror("fread()"); - goto err; - } - - (void) fclose(fh); - rc4_init(ctx, key, 512, 4096); - (void) memset(key, '\0', 512); - free(key); - - return ctx; - -err: - if (fh) - (void) fclose(fh); - if (key) { - (void) memset(key, '\0', 512); - free(key); - } - if (ctx) { - rc4_destroy(ctx); - free(ctx); - } - - return NULL; -} - -static void -key_free(rc4_t *key) -{ - - rc4_destroy(key); - free(key); -} diff --git a/mmsoftware/mystic_ships/common/hmac.c b/mmsoftware/mystic_ships/common/hmac.c deleted file mode 100644 index 69d23f6..0000000 --- a/mmsoftware/mystic_ships/common/hmac.c +++ /dev/null @@ -1,137 +0,0 @@ -/* $Id: hmac.c,v 1.1 2006/12/31 08:32:40 mmondor Exp $ */ - -/* - * ANSIfied and cleaned up by Matthew Mondor, 2006 - */ - -/* - * Copyright (c) 2004, Juniper Networks, Inc. - * 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. Neither the name of the copyright holders 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 COPYRIGHT HOLDERS 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 COPYRIGHT - * OWNER 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. - */ -/* - * Implement HMAC as described in RFC 2104 - * - * You need to define the following before including this file. - * - * HMAC_FUNC the name of the function (hmac_sha1 or hmac_md5 etc) - * HASH_LENGTH the size of the digest (20 for SHA1, 16 for MD5) - * HASH_BLOCKSZ size of internal block size (64 for SHA1 and MD5) - * HASH_CTX the name of the HASH CTX - * HASH_Init - * HASH_Update - * Hash_Final - */ - -#include -#include -#include - -#include - - - -/* Don't change these */ -#define HMAC_IPAD 0x36 -#define HMAC_OPAD 0x5c - - - -/* - * The logic here is lifted straight from RFC 2104 except that - * rather than filling the pads with 0, copying in the key and then - * XOR with the pad byte, we just fill with the pad byte and - * XOR with the key. - */ -void -HMAC_FUNC(const uint8_t *text, size_t text_len, - const uint8_t *key, size_t key_len, uint8_t *digest) -{ - HASH_CTX context; - /* Inner padding key XOR'd with ipad */ - uint8_t k_ipad[HASH_BLOCKSZ + 1]; - /* Outer padding key XOR'd with opad */ - uint8_t k_opad[HASH_BLOCKSZ + 1]; - /* HASH(key) if needed */ - uint8_t tk[HASH_LENGTH]; - int i; - - /* - * If key is longer than HASH_BLOCKSZ bytes - * reset it to key=HASH(key) - */ - if (key_len > HASH_BLOCKSZ) { - HASH_CTX tctx; - - HASH_Init(&tctx); - HASH_Update(&tctx, key, key_len); - HASH_Final(tk, &tctx); - - key = tk; - key_len = HASH_LENGTH; - } - - /* - * The HMAC_ transform looks like: - * - * HASH(K XOR opad, HASH(K XOR ipad, text)) - * - * where K is an n byte key - * ipad is the byte HMAC_IPAD repeated HASH_BLOCKSZ times - * opad is the byte HMAC_OPAD repeated HASH_BLOCKSZ times - * and text is the data being protected - */ - - /* - * Fill the pads and XOR in the key - */ - (void) memset(k_ipad, HMAC_IPAD, sizeof(k_ipad)); - (void) memset(k_opad, HMAC_OPAD, sizeof(k_opad)); - for (i = 0; i < key_len; i++) { - k_ipad[i] ^= key[i]; - k_opad[i] ^= key[i]; - } - - /* - * Perform inner HASH. - * Start with inner pad, - * then the text. - */ - HASH_Init(&context); - HASH_Update(&context, k_ipad, HASH_BLOCKSZ); - HASH_Update(&context, text, text_len); - HASH_Final(digest, &context); - - /* - * Perform outer HASH. - * Start with the outer pad, - * then the result of the inner hash. - */ - HASH_Init(&context); - HASH_Update(&context, k_opad, HASH_BLOCKSZ); - HASH_Update(&context, digest, HASH_LENGTH); - HASH_Final(digest, &context); -} diff --git a/mmsoftware/mystic_ships/common/hmac.h b/mmsoftware/mystic_ships/common/hmac.h deleted file mode 100644 index 1567457..0000000 --- a/mmsoftware/mystic_ships/common/hmac.h +++ /dev/null @@ -1,13 +0,0 @@ -/* $Id: hmac.h,v 1.1 2006/12/31 08:32:40 mmondor Exp $ */ - -#ifndef _HMAC_H_ -#define _HMAC_H_ - - - -void hmac_sha1(const uint8_t *, size_t, const uint8_t *, size_t, uint8_t *); -void hmac_rmd160(const uint8_t *, size_t, const uint8_t *, size_t, uint8_t *); - - - -#endif diff --git a/mmsoftware/mystic_ships/common/hmac_rmd160.c b/mmsoftware/mystic_ships/common/hmac_rmd160.c deleted file mode 100644 index 377241c..0000000 --- a/mmsoftware/mystic_ships/common/hmac_rmd160.c +++ /dev/null @@ -1,31 +0,0 @@ -/* $Id: hmac_rmd160.c,v 1.1 2006/12/31 08:32:40 mmondor Exp $ */ -/* $NetBSD: hmac_sha1.c,v 1.2 2005/02/09 21:35:46 kleink Exp $ */ - -/* - * hmac_rmd160 - using HMAC from RFC 2104 - */ - -#ifndef _HMAC_RMD160_ -#define _HMAC_RMD160_ - - - -#include - - - -#define HMAC_FUNC hmac_rmd160 -#define HASH_BLOCKSZ 64 -#define HASH_LENGTH 20 -#define HASH_CTX rmd160_ctx_t -#define HASH_Init rmd160_init -#define HASH_Update rmd160_update -#define HASH_Final rmd160_final - - - -#include - - - -#endif diff --git a/mmsoftware/mystic_ships/common/hmac_sha1.c b/mmsoftware/mystic_ships/common/hmac_sha1.c deleted file mode 100644 index 0a41b45..0000000 --- a/mmsoftware/mystic_ships/common/hmac_sha1.c +++ /dev/null @@ -1,31 +0,0 @@ -/* $Id: hmac_sha1.c,v 1.1 2006/12/31 08:32:40 mmondor Exp $ */ -/* $NetBSD: hmac_sha1.c,v 1.2 2005/02/09 21:35:46 kleink Exp $ */ - -/* - * hmac_sha1 - using HMAC from RFC 2104 - */ - -#ifndef _HMAC_SHA1_ -#define _HMAC_SHA1_ - - - -#include - - - -#define HMAC_FUNC hmac_sha1 -#define HASH_BLOCKSZ 64 -#define HASH_LENGTH 20 -#define HASH_CTX sha1_ctx_t -#define HASH_Init sha1_init -#define HASH_Update sha1_update -#define HASH_Final sha1_final - - - -#include - - - -#endif diff --git a/mmsoftware/mystic_ships/common/mmenc.c b/mmsoftware/mystic_ships/common/mmenc.c deleted file mode 100644 index 2bbc073..0000000 --- a/mmsoftware/mystic_ships/common/mmenc.c +++ /dev/null @@ -1,302 +0,0 @@ -/* $Id: mmenc.c,v 1.1 2006/12/31 08:32:40 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-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. - */ - -/* - * This consists of an rc4 variation which is to be used with 512-bit keys, - * as opposed to the original implementation made to be used with 128-bit - * ones. The matrix has 4096 entries (64x64) rather than 256 (16x16). - * Moreover, a z axis was added, transforming on x like x does on y, but - * with an increment of 3 steps rather than 1. It is recommnded to tell - * mmenc_init() to discard/skip 4999 or 16369 bytes of the PRNG keystream. - * Some aspects of the key scheduling alrorithm were also modified. - * - * - * KNOWN ARCFOUR RELATED SECURITY CONSIDERATIONS - * ============================================= - * - * The keystream generated by RC4 is slightly biased in favour of certain - * sequences of bytes. The best attack based on this bias is due to Fluhrer - * and McGrew, which will distinguish the keystream from a random stream given - * a gigabyte of output. To counter this, the session key should be - * regenerated after either a certain delay or after enough number of bytes - * have been processed through it. - * - * RC4 does not take a separate nonce alongside the key. As with any cipher, - * but particularly with Vernam ciphers, such a nonce is a requirement for - * security, so that encrypting the same message twice produces a different - * ciphertext each time. A secure solution to this that works for any secure - * cipher is to generate each RC4 key by hashing a long-term key with a unique - * nonce using a construction such as HMAC. However, many applications that - * use RC4 simply concatenate key and nonce; RC4's weak key schedule then - * gives rise to a variety of serious problems. - * - * In 2001 a new and surprising discovery was made by Fluhrer, Mantin and - * Shamir: over all possible RC4 keys, the statistics for the first few bytes - * of output keystream are strongly non-random, leaking information about the - * key. If the long-term key and nonce are simply concatenated to generate the - * RC4 key, this long-term key can be discovered by analysing large number of - * messages encrypted with this key. This and related effects were then used - * to break the WEP ("wired equivalent privacy") encryption used with 802.11 - * wireless networks. This caused a scramble for a standards-based replacement - * for WEP in the 802.11 market, and led to the IEEE 802.11i effort and WPA. - * Cryptosystems can defend against this attack by discarding the initial - * portion of the keystream (say the first 1024 bytes) before using it. - */ - - - -#include -#include - -#include - - - -/* - * XXX Hmm would it be better to seed using 32-bit at a time form the key - * and avoid the * 16 part? - */ -void -mmenc_init(mmenc_t *ctx, uint8_t key[512], size_t keylen) -{ - register unsigned int i, t, u, ki, si, *state; - - if (keylen == 0) - key[keylen++] = 37; - for (ki = 0; keylen < 512; keylen++) { - key[keylen] = ((key[ki] + 1) * keylen) & 0xff; - if (++ki >= keylen) - ki = 0; - } - - state = ctx->state; - ctx->x = 23; - ctx->y = 127; - ctx->z = 251; - for (i = 0; i < 4096; i++) - state[i] = i; - ki = si = 0; - for (i = 0; i < 4096; i++) { - t = state[i]; - si = (si + (((int)key[ki]) * 16) + t) & 0xfff; - u = state[si]; - state[si] = t; - state[i] = u; - if (++ki >= keylen) - ki = 0; - } - - { - register unsigned int x, y, z, sx, sz; - - x = ctx->x; - y = ctx->y; - z = ctx->z; - for (i = 0; i < 4999; i++) { - - x = (x + 1) & 0xfff; - sx = state[x]; - y = (sx + y) & 0xfff; - state[x] = state[y]; - state[y] = sx; - - z = (z + 3) & 0xfff; - sz = state[z]; - x = (sz + x) & 0xfff; - state[z] = state[x]; - state[x] = sz; - } - ctx->x = x; - ctx->y = y; - ctx->z = z; - } -} - -void -mmenc_destroy(mmenc_t *ctx) -{ - - (void) memset(ctx, '\0', sizeof(mmenc_t)); -} - -void -mmenc_random(mmenc_t *ctx, uint8_t *buf, size_t len) -{ - register unsigned int x, y, z, sx, sy, sz, *state; - register uint8_t *endbuf; - - state = ctx->state; - x = ctx->x; - y = ctx->y; - z = ctx->z; - for (endbuf = buf + len; buf < endbuf; buf++) { - x = (x + 1) & 0xfff; - sx = state[x]; - y = (sx + y) & 0xfff; - state[x] = sy = state[y]; - state[y] = sx; - - z = (z + 3) & 0xfff; - sz = state[z]; - x = (sz + x) & 0xfff; - state[z] = sx = state[x]; - state[x] = sz; - - *buf = state[(sx + sy + sz) & 0xfff] & 0xff; - } - ctx->x = x; - ctx->y = y; - ctx->z = z; -} - -void -mmenc_seed(mmenc_t *ctx, const uint8_t *buf, size_t len) -{ - register unsigned int x, y, z, sx, sy, sz, *state; - register const uint8_t *endbuf; - - state = ctx->state; - x = ctx->x; - y = ctx->y; - z = ctx->z; - for (endbuf = buf + len; buf < endbuf; buf++) { - - x = (x + 1) & 0xfff; - sx = state[x]; - y = (sx + y) & 0xfff; - state[x] = sy = state[y]; - state[y] = sx; - - z = (z + 3) & 0xfff; - sz = state[z]; - x = (sz + x) & 0xfff; - state[z] = sx = state[x]; - state[x] = sz; - - /* - * XXX Again using the * 16 oddity. We probably could also - * seed using 32-bit values. - */ - state[(sx + sy + sz) & 0xfff] ^= ((*buf + 1) * 16) & 0xfff; - } - ctx->x = x; - ctx->y = y; - ctx->z = z; -} - -void -mmenc_encrypt(mmenc_t *ctx, uint8_t *buf, size_t len) -{ - register unsigned int x, y, z, sx, sy, sz, *state; - register uint8_t *endbuf; - - state = ctx->state; - x = ctx->x; - y = ctx->y; - z = ctx->z; - for (endbuf = buf + len; buf < endbuf; buf++) { - - x = (x + 1) & 0xfff; - sx = state[x]; - y = (sx + y) & 0xfff; - state[x] = sy = state[y]; - state[y] = sx; - - z = (z + 3) & 0xfff; - sz = state[z]; - x = (sz + x) & 0xfff; - state[z] = sx = state[x]; - state[x] = sz; - - *buf ^= state[(sx + sy + sz) & 0xfff] & 0xff; - } - ctx->x = x; - ctx->y = y; - ctx->z = z; -} - -void -mmenc_encrypt2(mmenc_t *ctx, uint8_t *dst, const uint8_t *src, size_t len) -{ - register unsigned int x, y, z, sx, sy, sz, *state; - register const uint8_t *endsrc; - - state = ctx->state; - x = ctx->x; - y = ctx->y; - z = ctx->z; - for (endsrc = src + len; src < endsrc; src++, dst++) { - - x = (x + 1) & 0xfff; - sx = state[x]; - y = (sx + y) & 0xfff; - state[x] = sy = state[y]; - state[y] = sx; - - z = (z + 3) & 0xfff; - sz = state[z]; - x = (sz + x) & 0xfff; - state[z] = sx = state[x]; - state[x] = sz; - - *dst = *src ^ (state[(sx + sy + sz) & 0xfff] & 0xff); - } - ctx->x = x; - ctx->y = y; - ctx->z = z; -} diff --git a/mmsoftware/mystic_ships/common/mmenc.h b/mmsoftware/mystic_ships/common/mmenc.h deleted file mode 100644 index 4bdfb91..0000000 --- a/mmsoftware/mystic_ships/common/mmenc.h +++ /dev/null @@ -1,70 +0,0 @@ -/* $Id: mmenc.h,v 1.1 2006/12/31 08:32:40 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 _MMENC_H_ -#define _MMENC_H_ - - - -#include -#include - - - -#define mmenc_decrypt mmenc_encrypt -#define mmenc_decrypt2 mmenc_encrypt2 - - - -typedef struct mmenc { - int x, y, z; - unsigned int state[4096]; -} mmenc_t; - - - -void mmenc_init(mmenc_t *, uint8_t key[512], size_t); -void mmenc_destroy(mmenc_t *); -void mmenc_random(mmenc_t *, uint8_t *, size_t); -void mmenc_seed(mmenc_t *, const uint8_t *, size_t); -void mmenc_encrypt(mmenc_t *, uint8_t *, size_t); -void mmenc_encrypt2(mmenc_t *, uint8_t *, const uint8_t *, size_t); - - - -#endif diff --git a/mmsoftware/mystic_ships/common/packets_common.h b/mmsoftware/mystic_ships/common/packets_common.h deleted file mode 100644 index 421e9d7..0000000 --- a/mmsoftware/mystic_ships/common/packets_common.h +++ /dev/null @@ -1,219 +0,0 @@ -/* $Id: packets_common.h,v 1.1 2006/12/31 08:32:40 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_COMMON_H_ -#define _PACKETS_COMMON_H_ - - - -#include - - - -/* - * Server to client packets - */ - -enum spacket_types { - SPACKET_AUTH = 0, - SPACKET_PING, - SPACKET_PONG, - SPACKET_EOF, - SPACKET_SHIP, - SPACKET_SHIPINFO, - SPACKET_TORP, - SPACKET_COLLISION, - SPACKET_MAX -}; - -enum svpacket_types { - SVPACKET_MESSAGE = SPACKET_MAX, - SVPACKET_MAX -}; - -#define SHIPF_SHIELD (1 << 0) -#define SHIPF_CLOAK (1 << 1) - -/* Used to request client authentication and respond success/failure */ -struct spacket_auth { - int8_t packet_type; - int8_t protocol_version; - /* 32-bit time_t + high 32-bit counter + 24 random bytes */ - uint8_t noncerand1[32], noncerand2[32]; -} __attribute__((__packed__)); - -/* And for server to test idle connections */ -struct spacket_ping { - int8_t packet_type; -} __attribute__((__packed__)); - -/* And clients to test latency */ -struct spacket_pong { - int8_t packet_type; -} __attribute__((__packed__)); - -/* To notify end of frame data */ -struct spacket_eof { - int8_t packet_type; -} __attribute__((__packed__)); - -/* Ship position/direction update to client */ -struct spacket_ship { - int8_t packet_type; - int8_t flags; - int16_t id; - int16_t x, y; - uint8_t angle; -} __attribute__((__packed__)); - -/* - * Information on own's ship, possibly sent once per second. - * Possibly that we could send such packet at start to describe your ship's - * maximum capabilities, too. - */ -struct spacket_shipinfo { - int8_t packet_type; - int8_t thrust; - int16_t fuel; - int16_t shield; - int16_t hull; -} __attribute__((__packed__)); - -/* Torp to client */ -struct spacket_torp { - int8_t packet_type; - int8_t radius; /* XXX Add flags/team */ - int16_t x, y; /* XXX Add angle? */ -} __attribute__((__packed__)); - -/* Collision/detonation update to client */ -struct spacket_collision { - int8_t packet_type; - int8_t collision_type; - int16_t object1_id, object2_id; -} __attribute__((__packed__)); - -struct svpacket_message { - int8_t packet_type; - int8_t packet_length; - int8_t destination; - char string[0]; -} __attribute__((__packed__)); - - - -/* - * Client to server packets - */ - -enum cpacket_tyoes { - CPACKET_AUTH = 0, - CPACKET_PING, - CPACKET_PONG, - CPACKET_DIRECTION, - CPACKET_THRUST_ACC, - CPACKET_THRUST_DEC, - CPACKET_SHIELD, - CPACKET_CLOAK, - CPACKET_TORP, - CPACKET_SLOW, - CPACKET_FAST, - CPACKET_QUIT, - CPACKET_MAX -}; - -enum cvpacket_types { - CVPACKET_MESSAGE = CPACKET_MAX, - CVPACKET_MAX -}; - -/* Used to respond to server authentication request */ -struct cpacket_auth { - int8_t packet_type; - int8_t protocol_version; - int8_t login[16]; - uint8_t hmac1[20], hmac2[20]; -} __attribute__((__packed__)); - -/* To ping server for latency mesurement */ -struct cpacket_ping { - int8_t packet_type; -} __attribute__((__packed__)); - -/* To respond to server ping requests */ -struct cpacket_pong { - int8_t packet_type; -} __attribute__((__packed__)); - -/* Angle/direction change request */ -struct cpacket_direction { - int8_t packet_type; - uint8_t angle; -} __attribute__((__packed__)); - -/* Speed change requests */ -struct cpacket_thrust_acc { - int8_t packet_type; -} __attribute__((__packed__)); - -struct cpacket_thrust_dec { - int8_t packet_type; -} __attribute__((__packed__)); - -struct cpacket_shield { - int8_t packet_type; -} __attribute__((__packed__)); - -struct cpacket_cloak { - int8_t packet_type; -} __attribute__((__packed__)); - -/* Torpedo fire request */ -struct cpacket_torp { - int8_t packet_type; - uint8_t angle; -} __attribute__((__packed__)); - -/* Tells server to skip one more frame */ -struct cpacket_slow { - int8_t packet_type; -} __attribute__((__packed__)); - -/* Or to skip less */ -struct cpacket_fast { - int8_t packet_type; -} __attribute__((__packed__)); - -/* Game quit request */ -struct cpacket_quit { - int8_t packet_type; -} __attribute__((__packed__)); - -struct cvpacket_message { - int8_t packet_type; - int8_t packet_length; - int8_t destination; - char string[0]; -} __attribute__((__packed__)); - - - -#endif diff --git a/mmsoftware/mystic_ships/common/rc4.c b/mmsoftware/mystic_ships/common/rc4.c deleted file mode 100644 index e982479..0000000 --- a/mmsoftware/mystic_ships/common/rc4.c +++ /dev/null @@ -1,166 +0,0 @@ -/* $Id: rc4.c,v 1.1 2006/12/31 08:32:40 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 - - - -void -rc4_init(rc4_t *ctx, const uint8_t *key, size_t len, size_t skip) -{ - register unsigned int i, t, u, ki, si, *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; - } - - /* - * 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) { - register unsigned int x, y, sx; - - 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; - } - ctx->x = x; - ctx->y = y; - } -} - - -void -rc4_destroy(rc4_t *ctx) -{ - - (void) memset(ctx, '\0', sizeof(rc4_t)); -} - - -void -rc4_encrypt(rc4_t *ctx, uint8_t *buf, size_t len) -{ - register unsigned int x, y, sx, sy, *state; - register uint8_t *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, uint8_t *dst, const uint8_t *src, size_t len) -{ - register unsigned int x, y, sx, sy, *state; - register const uint8_t *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/mystic_ships/common/rc4.h b/mmsoftware/mystic_ships/common/rc4.h deleted file mode 100644 index 3965dde..0000000 --- a/mmsoftware/mystic_ships/common/rc4.h +++ /dev/null @@ -1,66 +0,0 @@ -/* $Id: rc4.h,v 1.1 2006/12/31 08:32:40 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 - - - -#define rc4_decrypt rc4_encrypt -#define rc4_decrypt2 rc4_encrypt2 - - - -typedef struct rc4 { - int x, y, state[256]; -} rc4_t; - - - -void rc4_init(rc4_t *, const uint8_t *, size_t, size_t); -void rc4_destroy(rc4_t *); -void rc4_encrypt(rc4_t *, uint8_t *, size_t); -void rc4_encrypt2(rc4_t *, uint8_t *, const uint8_t *, size_t); - - - -#endif diff --git a/mmsoftware/mystic_ships/common/rmd160.c b/mmsoftware/mystic_ships/common/rmd160.c deleted file mode 100644 index 580cd04..0000000 --- a/mmsoftware/mystic_ships/common/rmd160.c +++ /dev/null @@ -1,427 +0,0 @@ -/* $Id: rmd160.c,v 1.1 2006/12/31 08:32:40 mmondor Exp $ */ -/* $NetBSD: rmd160.c,v 1.8 2003/10/27 00:12:42 lukem Exp $ */ - -/* - * ANSIfied and cleaned up by Matthew Mondor, 2006 - */ - -/********************************************************************\ - * - * FILE: rmd160.c - * - * CONTENTS: A sample C-implementation of the RIPEMD-160 - * hash-function. - * TARGET: any computer with an ANSI C compiler - * - * AUTHOR: Antoon Bosselaers, ESAT-COSIC - * (Arranged for libc by Todd C. Miller) - * DATE: 1 March 1996 - * VERSION: 1.0 - * - * Copyright (c) Katholieke Universiteit Leuven - * 1996, All Rights Reserved - * -\********************************************************************/ - -/* header files */ -#include -#include - -#ifdef __NetBSD__ -#include -#endif - -#include - - - -#ifndef BYTE_ORDER -#error "BYTEORDER undefined!" -#endif - - - -/* macro definitions */ - -/* collect four bytes into one word: */ -typedef uint8_t * uint8ptr_t; -#define BYTES_TO_DWORD(uint8ptr_t) \ - (((uint32_t) *((uint8_t *) + 3) << 24) | \ - ((uint32_t) *((uint8_t *) + 2) << 16) | \ - ((uint32_t) *((uint8_t *) + 1) << 8) | \ - ((uint32_t) *(uint8_t *))) - -/* ROL(x, n) cyclically rotates x over n bits to the left */ -/* x must be of an unsigned 32 bits type and 0 <= n < 32. */ -#define ROL(x, n) (((x) << (n)) | ((x) >> (32 - (n)))) - -/* the three basic functions F(), G() and H() */ -#define F(x, y, z) ((x) ^ (y) ^ (z)) -#define G(x, y, z) (((x) & (y)) | (~(x) & (z))) -#define H(x, y, z) (((x) | ~(y)) ^ (z)) -#define I(x, y, z) (((x) & (z)) | ((y) & ~(z))) -#define J(x, y, z) ((x) ^ ((y) | ~(z))) - -/* the eight basic operations FF() through III() */ -#define FF(a, b, c, d, e, x, s) { \ - (a) += F((b), (c), (d)) + (x); \ - (a) = ROL((a), (s)) + (e); \ - (c) = ROL((c), 10); \ -} -#define GG(a, b, c, d, e, x, s) { \ - (a) += G((b), (c), (d)) + (x) + 0x5a827999U; \ - (a) = ROL((a), (s)) + (e); \ - (c) = ROL((c), 10); \ -} -#define HH(a, b, c, d, e, x, s) { \ - (a) += H((b), (c), (d)) + (x) + 0x6ed9eba1U; \ - (a) = ROL((a), (s)) + (e); \ - (c) = ROL((c), 10); \ -} -#define II(a, b, c, d, e, x, s) { \ - (a) += I((b), (c), (d)) + (x) + 0x8f1bbcdcU; \ - (a) = ROL((a), (s)) + (e); \ - (c) = ROL((c), 10); \ -} -#define JJ(a, b, c, d, e, x, s) { \ - (a) += J((b), (c), (d)) + (x) + 0xa953fd4eU; \ - (a) = ROL((a), (s)) + (e); \ - (c) = ROL((c), 10); \ -} -#define FFF(a, b, c, d, e, x, s) { \ - (a) += F((b), (c), (d)) + (x); \ - (a) = ROL((a), (s)) + (e); \ - (c) = ROL((c), 10); \ -} -#define GGG(a, b, c, d, e, x, s) { \ - (a) += G((b), (c), (d)) + (x) + 0x7a6d76e9U; \ - (a) = ROL((a), (s)) + (e); \ - (c) = ROL((c), 10); \ -} -#define HHH(a, b, c, d, e, x, s) { \ - (a) += H((b), (c), (d)) + (x) + 0x6d703ef3U; \ - (a) = ROL((a), (s)) + (e); \ - (c) = ROL((c), 10); \ -} -#define III(a, b, c, d, e, x, s) { \ - (a) += I((b), (c), (d)) + (x) + 0x5c4dd124U; \ - (a) = ROL((a), (s)) + (e); \ - (c) = ROL((c), 10); \ -} -#define JJJ(a, b, c, d, e, x, s) { \ - (a) += J((b), (c), (d)) + (x) + 0x50a28be6U; \ - (a) = ROL((a), (s)) + (e); \ - (c) = ROL((c), 10); \ -} - - - -void -rmd160_init(rmd160_ctx_t *context) -{ - - /* ripemd-160 initialization constants */ - context->state[0] = 0x67452301U; - context->state[1] = 0xefcdab89U; - context->state[2] = 0x98badcfeU; - context->state[3] = 0x10325476U; - context->state[4] = 0xc3d2e1f0U; - context->length[0] = context->length[1] = 0; - context->buflen = 0; -} - -void -rmd160_transform(uint32_t state[5], const uint32_t block[16]) -{ - uint32_t aa, bb, cc, dd, ee; - uint32_t aaa, bbb, ccc, ddd, eee; - - aa = aaa = state[0]; - bb = bbb = state[1]; - cc = ccc = state[2]; - dd = ddd = state[3]; - ee = eee = state[4]; - - /* round 1 */ - FF(aa, bb, cc, dd, ee, block[ 0], 11); - FF(ee, aa, bb, cc, dd, block[ 1], 14); - FF(dd, ee, aa, bb, cc, block[ 2], 15); - FF(cc, dd, ee, aa, bb, block[ 3], 12); - FF(bb, cc, dd, ee, aa, block[ 4], 5); - FF(aa, bb, cc, dd, ee, block[ 5], 8); - FF(ee, aa, bb, cc, dd, block[ 6], 7); - FF(dd, ee, aa, bb, cc, block[ 7], 9); - FF(cc, dd, ee, aa, bb, block[ 8], 11); - FF(bb, cc, dd, ee, aa, block[ 9], 13); - FF(aa, bb, cc, dd, ee, block[10], 14); - FF(ee, aa, bb, cc, dd, block[11], 15); - FF(dd, ee, aa, bb, cc, block[12], 6); - FF(cc, dd, ee, aa, bb, block[13], 7); - FF(bb, cc, dd, ee, aa, block[14], 9); - FF(aa, bb, cc, dd, ee, block[15], 8); - - /* round 2 */ - GG(ee, aa, bb, cc, dd, block[ 7], 7); - GG(dd, ee, aa, bb, cc, block[ 4], 6); - GG(cc, dd, ee, aa, bb, block[13], 8); - GG(bb, cc, dd, ee, aa, block[ 1], 13); - GG(aa, bb, cc, dd, ee, block[10], 11); - GG(ee, aa, bb, cc, dd, block[ 6], 9); - GG(dd, ee, aa, bb, cc, block[15], 7); - GG(cc, dd, ee, aa, bb, block[ 3], 15); - GG(bb, cc, dd, ee, aa, block[12], 7); - GG(aa, bb, cc, dd, ee, block[ 0], 12); - GG(ee, aa, bb, cc, dd, block[ 9], 15); - GG(dd, ee, aa, bb, cc, block[ 5], 9); - GG(cc, dd, ee, aa, bb, block[ 2], 11); - GG(bb, cc, dd, ee, aa, block[14], 7); - GG(aa, bb, cc, dd, ee, block[11], 13); - GG(ee, aa, bb, cc, dd, block[ 8], 12); - - /* round 3 */ - HH(dd, ee, aa, bb, cc, block[ 3], 11); - HH(cc, dd, ee, aa, bb, block[10], 13); - HH(bb, cc, dd, ee, aa, block[14], 6); - HH(aa, bb, cc, dd, ee, block[ 4], 7); - HH(ee, aa, bb, cc, dd, block[ 9], 14); - HH(dd, ee, aa, bb, cc, block[15], 9); - HH(cc, dd, ee, aa, bb, block[ 8], 13); - HH(bb, cc, dd, ee, aa, block[ 1], 15); - HH(aa, bb, cc, dd, ee, block[ 2], 14); - HH(ee, aa, bb, cc, dd, block[ 7], 8); - HH(dd, ee, aa, bb, cc, block[ 0], 13); - HH(cc, dd, ee, aa, bb, block[ 6], 6); - HH(bb, cc, dd, ee, aa, block[13], 5); - HH(aa, bb, cc, dd, ee, block[11], 12); - HH(ee, aa, bb, cc, dd, block[ 5], 7); - HH(dd, ee, aa, bb, cc, block[12], 5); - - /* round 4 */ - II(cc, dd, ee, aa, bb, block[ 1], 11); - II(bb, cc, dd, ee, aa, block[ 9], 12); - II(aa, bb, cc, dd, ee, block[11], 14); - II(ee, aa, bb, cc, dd, block[10], 15); - II(dd, ee, aa, bb, cc, block[ 0], 14); - II(cc, dd, ee, aa, bb, block[ 8], 15); - II(bb, cc, dd, ee, aa, block[12], 9); - II(aa, bb, cc, dd, ee, block[ 4], 8); - II(ee, aa, bb, cc, dd, block[13], 9); - II(dd, ee, aa, bb, cc, block[ 3], 14); - II(cc, dd, ee, aa, bb, block[ 7], 5); - II(bb, cc, dd, ee, aa, block[15], 6); - II(aa, bb, cc, dd, ee, block[14], 8); - II(ee, aa, bb, cc, dd, block[ 5], 6); - II(dd, ee, aa, bb, cc, block[ 6], 5); - II(cc, dd, ee, aa, bb, block[ 2], 12); - - /* round 5 */ - JJ(bb, cc, dd, ee, aa, block[ 4], 9); - JJ(aa, bb, cc, dd, ee, block[ 0], 15); - JJ(ee, aa, bb, cc, dd, block[ 5], 5); - JJ(dd, ee, aa, bb, cc, block[ 9], 11); - JJ(cc, dd, ee, aa, bb, block[ 7], 6); - JJ(bb, cc, dd, ee, aa, block[12], 8); - JJ(aa, bb, cc, dd, ee, block[ 2], 13); - JJ(ee, aa, bb, cc, dd, block[10], 12); - JJ(dd, ee, aa, bb, cc, block[14], 5); - JJ(cc, dd, ee, aa, bb, block[ 1], 12); - JJ(bb, cc, dd, ee, aa, block[ 3], 13); - JJ(aa, bb, cc, dd, ee, block[ 8], 14); - JJ(ee, aa, bb, cc, dd, block[11], 11); - JJ(dd, ee, aa, bb, cc, block[ 6], 8); - JJ(cc, dd, ee, aa, bb, block[15], 5); - JJ(bb, cc, dd, ee, aa, block[13], 6); - - /* parallel round 1 */ - JJJ(aaa, bbb, ccc, ddd, eee, block[ 5], 8); - JJJ(eee, aaa, bbb, ccc, ddd, block[14], 9); - JJJ(ddd, eee, aaa, bbb, ccc, block[ 7], 9); - JJJ(ccc, ddd, eee, aaa, bbb, block[ 0], 11); - JJJ(bbb, ccc, ddd, eee, aaa, block[ 9], 13); - JJJ(aaa, bbb, ccc, ddd, eee, block[ 2], 15); - JJJ(eee, aaa, bbb, ccc, ddd, block[11], 15); - JJJ(ddd, eee, aaa, bbb, ccc, block[ 4], 5); - JJJ(ccc, ddd, eee, aaa, bbb, block[13], 7); - JJJ(bbb, ccc, ddd, eee, aaa, block[ 6], 7); - JJJ(aaa, bbb, ccc, ddd, eee, block[15], 8); - JJJ(eee, aaa, bbb, ccc, ddd, block[ 8], 11); - JJJ(ddd, eee, aaa, bbb, ccc, block[ 1], 14); - JJJ(ccc, ddd, eee, aaa, bbb, block[10], 14); - JJJ(bbb, ccc, ddd, eee, aaa, block[ 3], 12); - JJJ(aaa, bbb, ccc, ddd, eee, block[12], 6); - - /* parallel round 2 */ - III(eee, aaa, bbb, ccc, ddd, block[ 6], 9); - III(ddd, eee, aaa, bbb, ccc, block[11], 13); - III(ccc, ddd, eee, aaa, bbb, block[ 3], 15); - III(bbb, ccc, ddd, eee, aaa, block[ 7], 7); - III(aaa, bbb, ccc, ddd, eee, block[ 0], 12); - III(eee, aaa, bbb, ccc, ddd, block[13], 8); - III(ddd, eee, aaa, bbb, ccc, block[ 5], 9); - III(ccc, ddd, eee, aaa, bbb, block[10], 11); - III(bbb, ccc, ddd, eee, aaa, block[14], 7); - III(aaa, bbb, ccc, ddd, eee, block[15], 7); - III(eee, aaa, bbb, ccc, ddd, block[ 8], 12); - III(ddd, eee, aaa, bbb, ccc, block[12], 7); - III(ccc, ddd, eee, aaa, bbb, block[ 4], 6); - III(bbb, ccc, ddd, eee, aaa, block[ 9], 15); - III(aaa, bbb, ccc, ddd, eee, block[ 1], 13); - III(eee, aaa, bbb, ccc, ddd, block[ 2], 11); - - /* parallel round 3 */ - HHH(ddd, eee, aaa, bbb, ccc, block[15], 9); - HHH(ccc, ddd, eee, aaa, bbb, block[ 5], 7); - HHH(bbb, ccc, ddd, eee, aaa, block[ 1], 15); - HHH(aaa, bbb, ccc, ddd, eee, block[ 3], 11); - HHH(eee, aaa, bbb, ccc, ddd, block[ 7], 8); - HHH(ddd, eee, aaa, bbb, ccc, block[14], 6); - HHH(ccc, ddd, eee, aaa, bbb, block[ 6], 6); - HHH(bbb, ccc, ddd, eee, aaa, block[ 9], 14); - HHH(aaa, bbb, ccc, ddd, eee, block[11], 12); - HHH(eee, aaa, bbb, ccc, ddd, block[ 8], 13); - HHH(ddd, eee, aaa, bbb, ccc, block[12], 5); - HHH(ccc, ddd, eee, aaa, bbb, block[ 2], 14); - HHH(bbb, ccc, ddd, eee, aaa, block[10], 13); - HHH(aaa, bbb, ccc, ddd, eee, block[ 0], 13); - HHH(eee, aaa, bbb, ccc, ddd, block[ 4], 7); - HHH(ddd, eee, aaa, bbb, ccc, block[13], 5); - - /* parallel round 4 */ - GGG(ccc, ddd, eee, aaa, bbb, block[ 8], 15); - GGG(bbb, ccc, ddd, eee, aaa, block[ 6], 5); - GGG(aaa, bbb, ccc, ddd, eee, block[ 4], 8); - GGG(eee, aaa, bbb, ccc, ddd, block[ 1], 11); - GGG(ddd, eee, aaa, bbb, ccc, block[ 3], 14); - GGG(ccc, ddd, eee, aaa, bbb, block[11], 14); - GGG(bbb, ccc, ddd, eee, aaa, block[15], 6); - GGG(aaa, bbb, ccc, ddd, eee, block[ 0], 14); - GGG(eee, aaa, bbb, ccc, ddd, block[ 5], 6); - GGG(ddd, eee, aaa, bbb, ccc, block[12], 9); - GGG(ccc, ddd, eee, aaa, bbb, block[ 2], 12); - GGG(bbb, ccc, ddd, eee, aaa, block[13], 9); - GGG(aaa, bbb, ccc, ddd, eee, block[ 9], 12); - GGG(eee, aaa, bbb, ccc, ddd, block[ 7], 5); - GGG(ddd, eee, aaa, bbb, ccc, block[10], 15); - GGG(ccc, ddd, eee, aaa, bbb, block[14], 8); - - /* parallel round 5 */ - FFF(bbb, ccc, ddd, eee, aaa, block[12] , 8); - FFF(aaa, bbb, ccc, ddd, eee, block[15] , 5); - FFF(eee, aaa, bbb, ccc, ddd, block[10] , 12); - FFF(ddd, eee, aaa, bbb, ccc, block[ 4] , 9); - FFF(ccc, ddd, eee, aaa, bbb, block[ 1] , 12); - FFF(bbb, ccc, ddd, eee, aaa, block[ 5] , 5); - FFF(aaa, bbb, ccc, ddd, eee, block[ 8] , 14); - FFF(eee, aaa, bbb, ccc, ddd, block[ 7] , 6); - FFF(ddd, eee, aaa, bbb, ccc, block[ 6] , 8); - FFF(ccc, ddd, eee, aaa, bbb, block[ 2] , 13); - FFF(bbb, ccc, ddd, eee, aaa, block[13] , 6); - FFF(aaa, bbb, ccc, ddd, eee, block[14] , 5); - FFF(eee, aaa, bbb, ccc, ddd, block[ 0] , 15); - FFF(ddd, eee, aaa, bbb, ccc, block[ 3] , 13); - FFF(ccc, ddd, eee, aaa, bbb, block[ 9] , 11); - FFF(bbb, ccc, ddd, eee, aaa, block[11] , 11); - - /* combine results */ - ddd += cc + state[1]; /* final result for state[0] */ - state[1] = state[2] + dd + eee; - state[2] = state[3] + ee + aaa; - state[3] = state[4] + aa + bbb; - state[4] = state[0] + bb + ccc; - state[0] = ddd; -} - -void -rmd160_update(rmd160_ctx_t *context, const uint8_t *data, uint32_t nbytes) -{ - uint32_t X[16]; - uint32_t ofs = 0; - uint32_t i; -#if BYTE_ORDER != LITTLE_ENDIAN - uint32_t j; -#endif - - /* update length[] */ - if (context->length[0] + nbytes < context->length[0]) - context->length[1]++; /* overflow to msb of length */ - context->length[0] += nbytes; - - (void) memset(X, 0, sizeof(X)); - - if (context->buflen + nbytes < 64) { - (void) memcpy(context->bbuffer + context->buflen, data, - nbytes); - context->buflen += nbytes; - } else { - /* process first block */ - ofs = 64 - context->buflen; - (void) memcpy(context->bbuffer + context->buflen, data, ofs); -#if BYTE_ORDER == LITTLE_ENDIAN - (void) memcpy(X, context->bbuffer, sizeof(X)); -#else - for (j = 0; j < 16; j++) - X[j] = BYTES_TO_DWORD(context->bbuffer + (4 * j)); -#endif - rmd160_transform(context->state, X); - nbytes -= ofs; - - /* process remaining complete blocks */ - for (i = 0; i < (nbytes >> 6); i++) { -#if BYTE_ORDER == LITTLE_ENDIAN - (void) memcpy(X, data + (64 * i) + ofs, sizeof(X)); -#else - for (j = 0; j < 16; j++) - X[j] = BYTES_TO_DWORD(data + (64 * i) + - (4 * j) + ofs); -#endif - rmd160_transform(context->state, X); - } - - /* - * Put last bytes from data into context's buffer - */ - context->buflen = nbytes & 63; - (void) memcpy(context->bbuffer, data + (64 * i) + ofs, - context->buflen); - } -} - -void -rmd160_final(uint8_t digest[20], rmd160_ctx_t *context) -{ - uint32_t i; - uint32_t X[16]; -#if BYTE_ORDER != LITTLE_ENDIAN - uint32_t j; -#endif - - /* append the bit m_n == 1 */ - context->bbuffer[context->buflen] = (uint8_t)'\200'; - - (void) memset(context->bbuffer + context->buflen + 1, 0, - 63 - context->buflen); -#if BYTE_ORDER == LITTLE_ENDIAN - (void) memcpy(X, context->bbuffer, sizeof(X)); -#else - for (j = 0; j < 16; j++) - X[j] = BYTES_TO_DWORD(context->bbuffer + (4 * j)); -#endif - if ((context->buflen) > 55) { - /* length goes to next block */ - rmd160_transform(context->state, X); - (void) memset(X, 0, sizeof(X)); - } - - /* append length in bits */ - X[14] = context->length[0] << 3; - X[15] = (context->length[0] >> 29) | (context->length[1] << 3); - rmd160_transform(context->state, X); - - for (i = 0; i < 20; i += 4) { - /* extracts the 8 least significant bits. */ - digest[i] = context->state[i>>2]; - digest[i + 1] = (context->state[i>>2] >> 8); - digest[i + 2] = (context->state[i>>2] >> 16); - digest[i + 3] = (context->state[i>>2] >> 24); - } -} diff --git a/mmsoftware/mystic_ships/common/rmd160.h b/mmsoftware/mystic_ships/common/rmd160.h deleted file mode 100644 index dc33460..0000000 --- a/mmsoftware/mystic_ships/common/rmd160.h +++ /dev/null @@ -1,56 +0,0 @@ -/* $Id: rmd160.h,v 1.1 2006/12/31 08:32:40 mmondor Exp $ */ -/* $NetBSD: rmd160.h,v 1.2 2000/07/07 10:47:06 ad Exp $ */ - -/* - * ANSIfied and cleaned up by Matthew Mondor, 2006 - */ - -/********************************************************************\ - * - * FILE: rmd160.h - * - * CONTENTS: Header file for a sample C-implementation of the - * RIPEMD-160 hash-function. - * TARGET: any computer with an ANSI C compiler - * - * AUTHOR: Antoon Bosselaers, ESAT-COSIC - * DATE: 1 March 1996 - * VERSION: 1.0 - * - * Copyright (c) Katholieke Universiteit Leuven - * 1996, All Rights Reserved - * -\********************************************************************/ - -/* - * from OpenBSD: rmd160.h,v 1.4 1999/08/16 09:59:04 millert Exp - */ - -#ifndef _RMD160_H_ -#define _RMD160_H_ - - - -#include - - - -#define RMD160_DIGEST_LENGTH 20 - -typedef struct { - uint32_t state[5]; /* state (ABCDE) */ - uint32_t length[2]; /* number of bits */ - uint8_t bbuffer[64]; /* overflow buffer */ - uint32_t buflen; /* number of chars in bbuffer */ -} rmd160_ctx_t; - - - -void rmd160_init(rmd160_ctx_t *); -void rmd160_tansform(uint32_t[5], const uint32_t[16]); -void rmd160_update(rmd160_ctx_t *, const uint8_t *, uint32_t); -void rmd160_final(uint8_t[20], rmd160_ctx_t *); - - - -#endif /* !_RMD160_H_ */ diff --git a/mmsoftware/mystic_ships/common/sha1.c b/mmsoftware/mystic_ships/common/sha1.c deleted file mode 100644 index 7f1a07d..0000000 --- a/mmsoftware/mystic_ships/common/sha1.c +++ /dev/null @@ -1,271 +0,0 @@ -/* $Id: sha1.c,v 1.1 2006/12/31 08:32:40 mmondor Exp $ */ -/* $NetBSD: sha1.c,v 1.12 2003/10/27 00:12:42 lukem Exp $ */ -/* $OpenBSD: sha1.c,v 1.9 1997/07/23 21:12:32 kstailey Exp $ */ - -/* - * ANSIfied and cleaned up by Matthew Mondor, 2006 - */ - -/* - * SHA-1 in C - * By Steve Reid - * 100% Public Domain - * - * Test Vectors (from FIPS PUB 180-1) - * "abc" - * A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D - * "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" - * 84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1 - * A million repetitions of "a" - * 34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F - */ - - - -#include -#include - -#ifdef __NetBSD__ -#include -#endif - -#include - - - -#ifndef BYTE_ORDER -#error "BYTEORDER undefined!" -#endif - - - -#define SHA1HANDSOFF /* Copies data before messing with it. */ - -#define _DIAGASSERT(x) (void)0 - -#define rol(value, bits) \ - (((value) << (bits)) | ((value) >> (32 - (bits)))) - -/* - * blk0() and blk() perform the initial expand. - * I got the idea of expanding during the round function from SSLeay - */ -#if BYTE_ORDER == LITTLE_ENDIAN -#define blk0(i) \ - (block->l[i] = (rol(block->l[i], 24) & 0xFF00FF00) | \ - (rol(block->l[i], 8) & 0x00FF00FF)) -#else -#define blk0(i) block->l[i] -#endif -#define blk(i) \ - (block->l[i & 15] = rol(block->l[(i + 13) & 15] ^ \ - block->l[(i + 8) & 15] ^ block->l[(i + 2) & 15] ^ \ - block->l[ i & 15], 1)) - -/* - * (R0+R1), R2, R3, R4 are the different operations (rounds) used in SHA1 - */ -#define R0(v, w, x, y, z, i) \ - z += ((w & (x ^ y)) ^ y) + blk0(i) + 0x5A827999 + rol(v, 5); \ - w = rol(w, 30); -#define R1(v, w, x, y, z, i) \ - z += ((w & (x ^ y)) ^ y) + blk(i) + 0x5A827999 + rol(v, 5); \ - w = rol(w, 30); -#define R2(v, w, x, y, z, i) \ - z += (w ^ x ^ y) + blk(i) + 0x6ED9EBA1 + rol(v, 5); w = rol(w, 30); -#define R3(v, w, x, y, z, i) \ - z += (((w | x) & y) | (w & x)) + blk(i) + 0x8F1BBCDC + rol(v, 5); \ - w = rol(w, 30); -#define R4(v, w, x, y, z, i) \ - z += (w ^ x ^ y) + blk(i) + 0xCA62C1D6 + rol(v, 5); w = rol(w, 30); - -typedef union { - uint8_t c[64]; - uint32_t l[16]; -} CHAR64LONG16; - - - -/* - * Hash a single 512-bit block. This is the core of the algorithm. - */ -void -sha1_transform(uint32_t state[5], const uint8_t buffer[64]) -{ - uint32_t a, b, c, d, e; - CHAR64LONG16 *block; - -#ifdef SHA1HANDSOFF - CHAR64LONG16 workspace; -#endif - -#ifdef SHA1HANDSOFF - block = &workspace; - (void) memcpy(block, buffer, 64); -#else - block = (CHAR64LONG16 *)(void *)buffer; -#endif - - /* Copy context->state[] to working vars */ - a = state[0]; - b = state[1]; - c = state[2]; - d = state[3]; - e = state[4]; - - /* 4 rounds of 20 operations each. Loop unrolled. */ - R0(a, b, c, d, e, 0); - R0(e, a, b, c, d, 1); - R0(d, e, a, b, c, 2); - R0(c, d, e, a, b, 3); - R0(b, c, d, e, a, 4); - R0(a, b, c, d, e, 5); - R0(e, a, b, c, d, 6); - R0(d, e, a, b, c, 7); - R0(c, d, e, a, b, 8); - R0(b, c, d, e, a, 9); - R0(a, b, c, d, e, 10); - R0(e, a, b, c, d, 11); - R0(d, e, a, b, c, 12); - R0(c, d, e, a, b, 13); - R0(b, c, d, e, a, 14); - R0(a, b, c, d, e, 15); - R1(e, a, b, c, d, 16); - R1(d, e, a, b, c, 17); - R1(c, d, e, a, b, 18); - R1(b, c, d, e, a, 19); - R2(a, b, c, d, e, 20); - R2(e, a, b, c, d, 21); - R2(d, e, a, b, c, 22); - R2(c, d, e, a, b, 23); - R2(b, c, d, e, a, 24); - R2(a, b, c, d, e, 25); - R2(e, a, b, c, d, 26); - R2(d, e, a, b, c, 27); - R2(c, d, e, a, b, 28); - R2(b, c, d, e, a, 29); - R2(a, b, c, d, e, 30); - R2(e, a, b, c, d, 31); - R2(d, e, a, b, c, 32); - R2(c, d, e, a, b, 33); - R2(b, c, d, e, a, 34); - R2(a, b, c, d, e, 35); - R2(e, a, b, c, d, 36); - R2(d, e, a, b, c, 37); - R2(c, d, e, a, b, 38); - R2(b, c, d, e, a, 39); - R3(a, b, c, d, e, 40); - R3(e, a, b, c, d, 41); - R3(d, e, a, b, c, 42); - R3(c, d, e, a, b, 43); - R3(b, c, d, e, a, 44); - R3(a, b, c, d, e, 45); - R3(e, a, b, c, d, 46); - R3(d, e, a, b, c, 47); - R3(c, d, e, a, b, 48); - R3(b, c, d, e, a, 49); - R3(a, b, c, d, e, 50); - R3(e, a, b, c, d, 51); - R3(d, e, a, b, c, 52); - R3(c, d, e, a, b, 53); - R3(b, c, d, e, a, 54); - R3(a, b, c, d, e, 55); - R3(e, a, b, c, d, 56); - R3(d, e, a, b, c, 57); - R3(c, d, e, a, b, 58); - R3(b, c, d, e, a, 59); - R4(a, b, c, d, e, 60); - R4(e, a, b, c, d, 61); - R4(d, e, a, b, c, 62); - R4(c, d, e, a, b, 63); - R4(b, c, d, e, a, 64); - R4(a, b, c, d, e, 65); - R4(e, a, b, c, d, 66); - R4(d, e, a, b, c, 67); - R4(c, d, e, a, b, 68); - R4(b, c, d, e, a, 69); - R4(a, b, c, d, e, 70); - R4(e, a, b, c, d, 71); - R4(d, e, a, b, c, 72); - R4(c, d, e, a, b, 73); - R4(b, c, d, e, a, 74); - R4(a, b, c, d, e, 75); - R4(e, a, b, c, d, 76); - R4(d, e, a, b, c, 77); - R4(c, d, e, a, b, 78); - R4(b, c, d, e, a, 79); - - /* Add the working vars back into context.state[] */ - state[0] += a; - state[1] += b; - state[2] += c; - state[3] += d; - state[4] += e; - - /* Wipe variables */ - a = b = c = d = e = 0; -} - -/* - * Initialize new context - */ -void -sha1_init(sha1_ctx_t *context) -{ - - /* SHA1 initialization constants */ - context->state[0] = 0x67452301; - context->state[1] = 0xEFCDAB89; - context->state[2] = 0x98BADCFE; - context->state[3] = 0x10325476; - context->state[4] = 0xC3D2E1F0; - context->count[0] = context->count[1] = 0; -} - -/* - * Run your data through this. - */ -void -sha1_update(sha1_ctx_t *context, const uint8_t *data, uint32_t len) -{ - uint32_t i, j; - - j = context->count[0]; - if ((context->count[0] += len << 3) < j) - context->count[1] += (len >> 29) + 1; - j = (j >> 3) & 63; - if ((j + len) > 63) { - (void) memcpy(&context->buffer[j], data, (i = 64 - j)); - sha1_transform(context->state, context->buffer); - for (; i + 63 < len; i += 64) - sha1_transform(context->state, &data[i]); - j = 0; - } else - i = 0; - (void) memcpy(&context->buffer[j], &data[i], len - i); -} - -/* - * Add padding and return the message digest. - */ -void -sha1_final(uint8_t digest[20], sha1_ctx_t *context) -{ - unsigned int i; - uint8_t finalcount[8]; - - for (i = 0; i < 8; i++) { - /* Endian independent */ - finalcount[i] = (uint8_t)((context->count[(i >= 4 ? 0 : 1)] - >> ((3 - (i & 3)) * 8)) & 255); - } - sha1_update(context, (uint8_t *)"\200", 1); - while ((context->count[0] & 504) != 448) - sha1_update(context, (uint8_t *) "\0", 1); - /* Should cause an sha1_transform() */ - sha1_update(context, finalcount, 8); - - for (i = 0; i < 20; i++) - digest[i] = (uint8_t)((context->state[i >> 2] >> - ((3 - (i & 3)) * 8)) & 255); -} diff --git a/mmsoftware/mystic_ships/common/sha1.h b/mmsoftware/mystic_ships/common/sha1.h deleted file mode 100644 index b119b0a..0000000 --- a/mmsoftware/mystic_ships/common/sha1.h +++ /dev/null @@ -1,40 +0,0 @@ -/* $Id: sha1.h,v 1.1 2006/12/31 08:32:40 mmondor Exp $ */ -/* $NetBSD: sha1.h,v 1.5.2.1 2005/06/10 14:38:35 tron Exp $ */ - -/* - * ANSIfied and cleaned up by Matthew Mondor, 2006 - */ - -/* - * SHA-1 in C - * By Steve Reid - * 100% Public Domain - */ - -#ifndef _SHA1_H_ -#define _SHA1_H_ - - - -#include - - - -#define SHA1_DIGEST_LENGTH 20 - -typedef struct { - uint32_t state[5]; - uint32_t count[2]; - uint8_t buffer[64]; -} sha1_ctx_t; - - - -void sha1_transform(uint32_t[5], const uint8_t[64]); -void sha1_init(sha1_ctx_t *); -void sha1_update(sha1_ctx_t *, const uint8_t *, uint32_t); -void sha1_final(uint8_t[SHA1_DIGEST_LENGTH], sha1_ctx_t *); - - - -#endif diff --git a/mmsoftware/mystic_ships/server/GNUmakefile b/mmsoftware/mystic_ships/server/GNUmakefile deleted file mode 100644 index 4a91bac..0000000 --- a/mmsoftware/mystic_ships/server/GNUmakefile +++ /dev/null @@ -1,25 +0,0 @@ -# $Id: GNUmakefile,v 1.1 2006/12/31 08:32:40 mmondor Exp $ - -MMLIB_PATH := ../../../mmlib - -MMLIBS := $(addprefix $(MMLIB_PATH)/,mmpool.o mmstring.o mmarch.o) -LIBS := -lc -lm -lz -OBJS := $(addprefix src/,main.o trigonometry.o net.o kqueue.o sendq.o recvq.o \ - packets.o daemon.o client.o ships.o torp.o enc.o) \ - $(addprefix ../common/,hmac_sha1.o hmac_rmd160.o sha1.o rmd160.o \ - mmenc.o) -CFLAGS += -Wall -g -BINS := tms-server - -all: $(BINS) - -%.o: %.c - cc -c $(CFLAGS) -Isrc -I../common -I$(MMLIB_PATH) -o $@ $< - - -tms-server: $(MMLIBS) $(LIBS) $(OBJS) - cc -o $@ $(MMLIBS) $(OBJS) $(LIBS) - - -clean: - rm -f $(BINS) $(OBJS) $(MMLIBS) $(LIBS) diff --git a/mmsoftware/mystic_ships/server/README b/mmsoftware/mystic_ships/server/README deleted file mode 100644 index 43516e0..0000000 --- a/mmsoftware/mystic_ships/server/README +++ /dev/null @@ -1,48 +0,0 @@ -$Id: README,v 1.1 2006/12/31 08:32:40 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/mmsoftware/mystic_ships/server/src/client.c b/mmsoftware/mystic_ships/server/src/client.c deleted file mode 100644 index 99e1107..0000000 --- a/mmsoftware/mystic_ships/server/src/client.c +++ /dev/null @@ -1,222 +0,0 @@ -/* $Id: client.c,v 1.1 2006/12/31 08:32:40 mmondor Exp $ */ - -/* - * Copyright (c) 2006, Matthew Mondor - * ALL RIGHTS RESERVED. - */ - - - -#include -#include -#include -#include -#include - -#include -#include -#include - - - -static bool client_constructor(pnode_t *); -static void client_destructor(pnode_t *); - - - -static list_t clients_gc_list; -static pool_t clients_pool; - - - -list_t clients_list; - - - -void -client_init(void) -{ - - DLIST_INIT(&clients_list); - DLIST_INIT(&clients_gc_list); - - if (!pool_init(&clients_pool, "clients_pool", malloc, free, - client_constructor, client_destructor, sizeof(client_t), - 8, 1, MAX_CLIENTS / 8)) { - 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) { - c->fd = fd; - sendq_reset(&c->sendq, c); - recvq_reset(&c->recvq, c); - (void) memcpy(&c->saddr, saddr, sizeof(struct sockaddr)); - - ship_init(&c->ship, SHIP_CA); - - c->authenticated = c->todestroy = c->toclose = 0; - c->writepolling = 1; - c->askedping = 0; - c->slowlevel = c->slowcnt = 0; -#ifdef USE_ENCRYPTION - c->mmencrypt = 0; -#endif - c->created = current_time; - - DLIST_APPEND(&clients_list, (node_t *)c); - } else - syslog(LOG_NOTICE, "client_create() - pool_alloc() - %s", - strerror(errno)); - - return c; -} - -void -client_destroy_mark(client_t *c) -{ - - if (c->todestroy) - return; - - c->todestroy = 1; - DLIST_SWAP(&clients_gc_list, &clients_list, (node_t *)c, FALSE); -} - -void -client_destroy_marked(void) -{ - client_t *c, *n; - - for (c = (client_t *)DLIST_TOP(&clients_gc_list); c != NULL; c = n) { - n = (client_t *)DLIST_NEXT((node_t *)c); - - /* - * Delay destruction of client structure until all torpidoes - * expired, since torpidoes access client structure data. - * XXX This is extremely hackish, and it wastes a lot of - * CPU time! - */ - if (c->ship.torps > 0) - continue; - - /* Closing descriptor automatically deletes its kevents */ - (void) close(c->fd); - -#ifdef USE_ENCRYPTION - /* Make sure to destroy cryptographic keys information */ - (void) memset(&c->recvq.enc_in, '\0', sizeof(mmenc_t)); - (void) memset(&c->sendq.enc_out, '\0', sizeof(mmenc_t)); -#endif - - DLIST_UNLINK(&clients_gc_list, (node_t *)c); - pool_free((pnode_t *)c); - } -} - -/* - * Runs through list of clients and verify if the number of received packets - * exceeds zero, unless the client was too recently created. Those with input - * timeout are to be destroyed. - */ -void -client_timeout(void) -{ - client_t *c; - - /* - * We can safely use DLIST_FOREACH() since client_destroy_mark() - * doesn't yet destroy the node, and we're single-threaded. - */ - DLIST_FOREACH(&clients_list, (client_t *)c) { - if (current_time - c->created >= TIMEOUT_SECONDS && - c->recvq.recvpackets == 0) - client_destroy_mark(c); - c->recvq.recvpackets = 0; - } -} - -static bool -client_constructor(pnode_t *n) -{ - client_t *c = (client_t *)n; - int sendq = 0; - - if (sendq_init(&c->sendq, SENDQ_SIZE) != 0) { - syslog(LOG_NOTICE, - "client_constructor() - sendq_init() - %s", - strerror(errno)); - goto err; - } - sendq = 1; - - if (recvq_init(&c->recvq, RECVQ_SIZE) != 0) { - syslog(LOG_NOTICE, - "client_constructor() - recvq_init() - %s", - strerror(errno)); - goto err; - } - - return TRUE; - -err: - if (sendq) - sendq_destroy(&c->sendq); - - return FALSE; -} - -static void -client_destructor(pnode_t *n) -{ - client_t *c = (client_t *)n; - - recvq_destroy(&c->recvq); - sendq_destroy(&c->sendq); -} diff --git a/mmsoftware/mystic_ships/server/src/client.h b/mmsoftware/mystic_ships/server/src/client.h deleted file mode 100644 index b6d71bb..0000000 --- a/mmsoftware/mystic_ships/server/src/client.h +++ /dev/null @@ -1,63 +0,0 @@ -/* $Id: client.h,v 1.1 2006/12/31 08:32:40 mmondor Exp $ */ - -/* - * Copyright (c) 2006, Matthew Mondor - * ALL RIGHTS RESERVED. - */ - - - -#ifndef _CLIENT_H_ -#define _CLIENT_H_ - - - -#include -#include -#include - -#include -#include - -#include -#include -#include -#include - - - -typedef struct client { - pnode_t node; - ship_t ship; - struct sockaddr saddr; - int fd; - sendq_t sendq; - recvq_t recvq; - int authenticated, todestroy, toclose, writepolling, - askedping, slowlevel, slowcnt; -#ifdef USE_ENCRYPTION - int mmencrypt; -#endif - time_t created; - uint32_t noncerand1[8], noncerand2[8]; -} client_t; - - - -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); - -void client_timeout(void); - - - -extern list_t clients_list; - - - -#endif diff --git a/mmsoftware/mystic_ships/server/src/conf.h b/mmsoftware/mystic_ships/server/src/conf.h deleted file mode 100644 index 999e4b2..0000000 --- a/mmsoftware/mystic_ships/server/src/conf.h +++ /dev/null @@ -1,45 +0,0 @@ -/* $Id: conf.h,v 1.1 2006/12/31 08:32:40 mmondor Exp $ */ - -/* - * Copyright (c) 2006, Matthew Mondor - * ALL RIGHTS RESERVED. - */ - - - -#ifndef _CONF_H_ -#define _CONF_H_ - - - -#define DAEMON_NODETACH 0 - -#define SERVER_VERSION 1 -#define SERVER_STRING "tms-server/mmondor\n" - -#define FPS 20 -#define FPS_MS (1000 / FPS) -#define TIMEOUT_SECONDS 30 - -#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 - -#define PIDFILE "/tmp/daemon.pid" -#define RNDFILE "/dev/urandom" - -#define SERVER_LOGIN "login" -#define SERVER_PASSWD "d9d19c4285a4782a1b231d98aff232a046978cc2" - -#define USE_COMPRESSION -#define USE_ENCRYPTION - - - -#endif diff --git a/mmsoftware/mystic_ships/server/src/daemon.c b/mmsoftware/mystic_ships/server/src/daemon.c deleted file mode 100644 index 7756b62..0000000 --- a/mmsoftware/mystic_ships/server/src/daemon.c +++ /dev/null @@ -1,129 +0,0 @@ -/* $Id: daemon.c,v 1.1 2006/12/31 08:32:40 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(int nodetach) -{ - pid_t pid; - int fd; - - /* Create new process */ - if (!nodetach) { - 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 */ - if (!nodetach) { - (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/mmsoftware/mystic_ships/server/src/daemon.h b/mmsoftware/mystic_ships/server/src/daemon.h deleted file mode 100644 index 46d7b66..0000000 --- a/mmsoftware/mystic_ships/server/src/daemon.h +++ /dev/null @@ -1,19 +0,0 @@ -/* $Id: daemon.h,v 1.1 2006/12/31 08:32:40 mmondor Exp $ */ - -/* - * Copyright (c) 2006, Matthew Mondor - * ALL RIGHTS RESERVED. - */ - - - -#ifndef _DAEMON_H_ -#define _DAEMON_H_ - - - -int daemon_init(int); - - - -#endif diff --git a/mmsoftware/mystic_ships/server/src/enc.c b/mmsoftware/mystic_ships/server/src/enc.c deleted file mode 100644 index b2a199f..0000000 --- a/mmsoftware/mystic_ships/server/src/enc.c +++ /dev/null @@ -1,98 +0,0 @@ -/* $Id: enc.c,v 1.1 2006/12/31 08:32:40 mmondor Exp $ */ - -/* - * Copyright (c) 2006, Matthew Mondor - * ALL RIGHTS RESERVED. - */ - -#include -#include -#include -#include -#include -#include - -#include - -#include -#include -#include /* current_time */ -#include - - - -static inline uint32_t noncerand_rnd(void); -static inline void noncerand_seed(void); - - - -static uint32_t rndcnt; -static mmenc_t prng; -static int fd; - - - -/* - * Open random device, initialize PRNG and rndcnt. - */ -void -noncerand_init(void) -{ - uint32_t key[128]; - - if ((fd = open(RNDFILE, O_RDONLY)) == -1) { - syslog(LOG_NOTICE, - "noncerand_init() - open(%s) - %s", - RNDFILE, strerror(errno)); - exit(EXIT_FAILURE); - } - if (read(fd, key, sizeof(key)) != sizeof(key)) { - syslog(LOG_NOTICE, - "noncerand_init() - read() - %s", - strerror(errno)); - exit(EXIT_FAILURE); - } - mmenc_init(&prng, (uint8_t *)key, sizeof(key)); - - rndcnt = noncerand_rnd(); -} - -/* XXX Make more efficient eventually */ -void -noncerand_gen(uint32_t tnr[8], uint8_t nr[32]) -{ - - noncerand_seed(); - rndcnt += ((1 + noncerand_rnd()) % 32); - tnr[0] = BYTEORDER_NETWORK32(current_time); - tnr[1] = BYTEORDER_NETWORK32(rndcnt); - tnr[2] = BYTEORDER_NETWORK32(noncerand_rnd()); - tnr[3] = BYTEORDER_NETWORK32(noncerand_rnd()); - tnr[4] = BYTEORDER_NETWORK32(noncerand_rnd()); - tnr[5] = BYTEORDER_NETWORK32(noncerand_rnd()); - tnr[6] = BYTEORDER_NETWORK32(noncerand_rnd()); - tnr[7] = BYTEORDER_NETWORK32(noncerand_rnd()); - (void) memcpy(nr, tnr, 32); -} - -static inline uint32_t -noncerand_rnd(void) -{ - uint32_t r; - - mmenc_random(&prng, (uint8_t *)&r, sizeof(r)); - - return r; -} - -static inline void -noncerand_seed(void) -{ - uint32_t rs; - - if (read(fd, &rs, sizeof(rs)) != sizeof(rs)) - syslog(LOG_NOTICE, "noncerand_seed() - read() - %s", - strerror(errno)); - else - mmenc_seed(&prng, (uint8_t *)&rs, sizeof(rs)); -} diff --git a/mmsoftware/mystic_ships/server/src/enc.h b/mmsoftware/mystic_ships/server/src/enc.h deleted file mode 100644 index 2c9a3a1..0000000 --- a/mmsoftware/mystic_ships/server/src/enc.h +++ /dev/null @@ -1,22 +0,0 @@ -/* $Id: enc.h,v 1.1 2006/12/31 08:32:40 mmondor Exp $ */ - -/* - * Copyright (c) 2006, Matthew Mondor - * ALL RIGHTS RESERVED. - */ - -#ifndef _ENC_H_ -#define _ENC_H_ - - - -#include - - - -void noncerand_init(void); -void noncerand_gen(uint32_t tnr[8], uint8_t nr[32]); - - - -#endif diff --git a/mmsoftware/mystic_ships/server/src/kqueue.c b/mmsoftware/mystic_ships/server/src/kqueue.c deleted file mode 100644 index e783cc8..0000000 --- a/mmsoftware/mystic_ships/server/src/kqueue.c +++ /dev/null @@ -1,305 +0,0 @@ -/* $Id: kqueue.c,v 1.1 2006/12/31 08:32:40 mmondor Exp $ */ - -/* - * Copyright (c) 2006, Matthew Mondor - * ALL RIGHTS RESERVED. - */ - - - -#include -#include -#include -#include -#include -#include - -#include - -#include -#include -#include -#include -#include -#include - - - -static struct kevent rev[R_EVENTS], sev[S_EVENTS]; -static int kqid, rev_cnt, sev_cnt; - - - -/* - * Optimization to only need to query the current time once per second for the - * whole application. - */ -time_t current_time; - - - -/* 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; - current_time = time(NULL); -} - -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_addtimer(int id, int64_t ms) -{ - - EV_SET(sev, id, EVFILT_TIMER, EV_ADD | EV_ENABLE, 0, ms, - (intptr_t)NULL); - if (kevent(kqid, sev, 1, NULL, 0, NULL) == -1) { - syslog(LOG_NOTICE, "kevent_addtimer(%d) - kevent(%lld) - %s", - id, 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; - case SIGPIPE: - /* XXX */ - syslog(LOG_NOTICE, - "Received SIGPIPE!"); - client_destroy_marked(); - break; - } - continue; - } - - /* Process timeouts if any */ - if (kev->filter == EVFILT_TIMER) { - switch (kev->ident) { - case TIMER_FPS: - packets_update(); - break; - case TIMER_SECOND: - current_time = time(NULL); - break; - case TIMER_TIMEOUT: - client_timeout(); - break; - } - continue; - } - - 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 || - 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 flushing data, also mark it to be - * destroyed, since we're 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) { - /* - * Data to read from client. Simply read - * available packets into a 16-bit aligned - * queue to process it at the next - * server heartbeat event. This also - * transparently handles ping requests which - * packets are not queued. - */ - if (recvq_read(&c->recvq) == -1) { - client_destroy_mark(c); - continue; - } - 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/mmsoftware/mystic_ships/server/src/kqueue.h b/mmsoftware/mystic_ships/server/src/kqueue.h deleted file mode 100644 index c692c02..0000000 --- a/mmsoftware/mystic_ships/server/src/kqueue.h +++ /dev/null @@ -1,44 +0,0 @@ -/* $Id: kqueue.h,v 1.1 2006/12/31 08:32:40 mmondor Exp $ */ - -/* - * Copyright (c) 2006, Matthew Mondor - * ALL RIGHTS RESERVED. - */ - - - -#ifndef _KQUEUE_H_ -#define _KQUEUE_H_ - - - -#include -#include - - - -enum kqueue_timers { - TIMER_FPS = 0, - TIMER_SECOND, - TIMER_TIMEOUT, - TIMER_MAX -}; - - - -void kqueue_init(void); -void kqueue_addlisten(int); -void kqueue_addsignal(int); -void kqueue_addtimer(int, 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); - - - -extern time_t current_time; - - - -#endif diff --git a/mmsoftware/mystic_ships/server/src/main.c b/mmsoftware/mystic_ships/server/src/main.c deleted file mode 100644 index a96c362..0000000 --- a/mmsoftware/mystic_ships/server/src/main.c +++ /dev/null @@ -1,72 +0,0 @@ -/* $Id: main.c,v 1.1 2006/12/31 08:32:40 mmondor Exp $ */ - -/* - * Copyright (c) 2006, Matthew Mondor - * ALL RIGHTS RESERVED. - */ - - - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - - - -int main(int, char **); - - - -int -main(int argc, char **argv) -{ - - (void) openlog("tms-server", LOG_NDELAY | LOG_PID, LOG_USER); - - if (daemon_init(DAEMON_NODETACH) != 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(); - trigonometry_init(); - torp_init(); - noncerand_init(); - kqueue_addlisten(listen_fd); - kqueue_addsignal(SIGTERM); - kqueue_addsignal(SIGPIPE); - kqueue_addtimer(TIMER_FPS, FPS_MS); - kqueue_addtimer(TIMER_SECOND, 1000); - kqueue_addtimer(TIMER_TIMEOUT, TIMEOUT_SECONDS * 1000); - - kqueue_main(); - /* NOTREACHED */ - - return EXIT_SUCCESS; -} diff --git a/mmsoftware/mystic_ships/server/src/net.c b/mmsoftware/mystic_ships/server/src/net.c deleted file mode 100644 index 6206c2e..0000000 --- a/mmsoftware/mystic_ships/server/src/net.c +++ /dev/null @@ -1,131 +0,0 @@ -/* $Id: net.c,v 1.1 2006/12/31 08:32:40 mmondor Exp $ */ - -/* - * Copyright (c) 2006, Matthew Mondor - * ALL RIGHTS RESERVED. - */ - - - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include - -#include -#include - - - -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/mmsoftware/mystic_ships/server/src/net.h b/mmsoftware/mystic_ships/server/src/net.h deleted file mode 100644 index 41963bd..0000000 --- a/mmsoftware/mystic_ships/server/src/net.h +++ /dev/null @@ -1,23 +0,0 @@ -/* $Id: net.h,v 1.1 2006/12/31 08:32:40 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/mmsoftware/mystic_ships/server/src/packets.c b/mmsoftware/mystic_ships/server/src/packets.c deleted file mode 100644 index 2168a78..0000000 --- a/mmsoftware/mystic_ships/server/src/packets.c +++ /dev/null @@ -1,543 +0,0 @@ -/* $Id: packets.c,v 1.1 2006/12/31 08:32:40 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 -#include -#include - -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - - - -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_acc_handler(client_t *, uint16_t *); -static int cpacket_thrust_dec_handler(client_t *, uint16_t *); -static int cpacket_shield_handler(client_t *, uint16_t *); -static int cpacket_cloak_handler(client_t *, uint16_t *); -static int cpacket_torp_handler(client_t *, uint16_t *); -static int cpacket_slow_handler(client_t *, uint16_t *); -static int cpacket_fast_handler(client_t *, uint16_t *); -static int cpacket_quit_handler(client_t *, uint16_t *); -static int cvpacket_message_handler(client_t *, uint16_t *); - - - -static int ticks = 0; - - - -struct packet_index cpacket_index[CVPACKET_MAX] = { - /* Fixed sized packets */ - {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_acc), cpacket_thrust_acc_handler}, - {sizeof(struct cpacket_thrust_dec), cpacket_thrust_dec_handler}, - {sizeof(struct cpacket_shield), cpacket_shield_handler}, - {sizeof(struct cpacket_cloak), cpacket_cloak_handler}, - {sizeof(struct cpacket_torp), cpacket_torp_handler}, - {sizeof(struct cpacket_slow), cpacket_slow_handler}, - {sizeof(struct cpacket_fast), cpacket_fast_handler}, - {sizeof(struct cpacket_quit), cpacket_quit_handler}, - /* Variable size packets */ - {sizeof(struct cvpacket_message), cvpacket_message_handler} -}; - - - -void -packets_update(void) -{ - client_t *c, *next; - uint8_t *data; - size_t size, off, len; - - /* - * XXX We can't do this since if no clients but torps we keep waiting - * for torps to all have expired, which never occurs! - if (DLIST_NODES(&clients_list) == 0) - return; - */ - - if (++ticks == FPS) - ticks = 0; - - for (c = (client_t *)DLIST_TOP(&clients_list); c != NULL; c = next) { - next = DLIST_NEXT((node_t *)c); - - if (c->todestroy || c->toclose) - continue; - - /* Reset ping request throttling flag */ - c->askedping = 0; - - /* - * First process 16bit-aligned queued incomming packets. - * Only full packets are returned by recvq_contents() and - * the packets types and sizes have already been validated - * by recvq_read(). - */ - recvq_content(&c->recvq, &data, &size); - for (off = 0; off < size; off += len) { - int8_t id; - - /* - * Obtain packet type and evaluate its length. - * Because they are 16bit-aligned, make sure to - * increase len as necessary if it is odd. - */ - id = (int8_t )data[off]; - if (id >= CPACKET_MAX) - len = data[off + 1]; - else - len = cpacket_index[id].size; - if ((len & 1) != 0) - len++; - - /* - * We cannot accept any packets but the authentication - * one from unauthenticated clients. - */ - if (!c->authenticated && id != CPACKET_AUTH) - goto k; - - /* Kill client on packet processing error */ - DEBUG_ASSERT(cpacket_index[id].handler != NULL); - if (cpacket_index[id].handler(c, - (uint16_t *)&data[off]) == -1) { - syslog(LOG_NOTICE, "update(%d) - " - "Error processing packet 0x%02x", - c->fd, (uint8_t)id); - goto k; - } - - continue; - -k: - client_destroy_mark(c); - break; - } - recvq_content_reset(&c->recvq); - - /* Run game frame for client ship */ - if (c->authenticated) - ship_update(&c->ship); - } - - torps_update(); - - /* XXX Send all ships and all torps to all clients for now */ - DLIST_FOREACH(&clients_list, (node_t *)c) { - client_t *c2; - torp_t *t; - - if (!c->authenticated) - continue; - - /* - * Should only be done every second. - * Send self information packet. - */ - if (ticks == 0) { - (void) spacket_shipinfo_send(c); - /* XXX */ - } - - if (c->slowlevel > 0) { - if (c->slowcnt > 0) { - c->slowcnt--; - continue; - } else - c->slowcnt = c->slowlevel; - } - - DLIST_FOREACH(&clients_list, (node_t *)c2) { - - if (!c2->authenticated) - continue; - - if (!c2->ship.cloak_on || c2 == c) { - if (spacket_ship_send(c, (uint16_t)c2->fd, - &c2->ship) == -1) - break; - } - } - - DLIST_FOREACH(&torps_list, (node_t *)t) { - if (spacket_torp_send(c, t) == -1) - break; - } - - if (spacket_eof_send(c) == -1) - break; - (void) sendq_zflush(&c->sendq, NULL, NULL); - (void) sendq_flush(&c->sendq, -1); - } -} - -int -spacket_auth_send(client_t *c) -{ - struct spacket_auth p; - - p.packet_type = SPACKET_AUTH; - p.protocol_version = SERVER_VERSION; - /* - * Generate unique noonce/rand pairs. - * We also must remember these because we expect the - * cpacket_auth_handler to verify if the reply matches. - */ - noncerand_gen(c->noncerand1, p.noncerand1); - noncerand_gen(c->noncerand2, p.noncerand2); - - return client_write(c, (uint8_t *)&p, sizeof(p), 0); -} - -/* - * Unlike other functions, sends the packet immediately without buffering. - * Also, this function is not called by the general packets input handler, - * but by the recvq code. - */ -int -spacket_pong_send(client_t *c) -{ - struct spacket_pong p; - - p.packet_type = SPACKET_PONG; - - return sendq_write(&c->sendq, (uint8_t *)&p, sizeof(p), NULL, NULL, 0); -} - -int -spacket_eof_send(client_t *c) -{ - struct spacket_eof p; - - p.packet_type = SPACKET_EOF; - - return client_write(c, (uint8_t *)&p, sizeof(p), 1); -} - -int -spacket_ship_send(client_t *c, uint16_t id, ship_t *s) -{ - struct spacket_ship p; - - p.packet_type = SPACKET_SHIP; - p.flags = 0; - if (s->shield_on) - p.flags |= SHIPF_SHIELD; - if (s->cloak_on) - p.flags |= SHIPF_CLOAK; - p.id = BYTEORDER_NETWORK16(id); - p.x = BYTEORDER_NETWORK16(s->x); - p.y = BYTEORDER_NETWORK16(s->y); - p.angle = (uint8_t)s->angle; - - return client_write(c, (uint8_t *)&p, sizeof(p), 1); -} - -int -spacket_shipinfo_send(client_t *c) -{ - struct spacket_shipinfo p; - - p.packet_type = SPACKET_SHIPINFO; - p.thrust = c->ship.thrust; - p.fuel = BYTEORDER_NETWORK16(c->ship.fuel); - p.shield = BYTEORDER_NETWORK16(c->ship.shield); - p.hull = BYTEORDER_NETWORK16(c->ship.hull); - - return client_write(c, (uint8_t *)&p, sizeof(p), 1); -} - -int -spacket_torp_send(client_t *c, torp_t *t) -{ - struct spacket_torp p; - - p.packet_type = SPACKET_TORP; - /* p.radius = c->ship.ship->torp_radius; XXX */ - p.radius = c->ship.ship->torp_radius; - p.x = BYTEORDER_NETWORK16(t->x); - p.y = BYTEORDER_NETWORK16(t->y); - - return client_write(c, (uint8_t *)&p, sizeof(p), 1); -} - - -static int -cpacket_auth_handler(client_t *c, uint16_t *ptr) -{ - struct cpacket_auth *p = (struct cpacket_auth *)ptr; - uint8_t hmac1[40], hmac2[40], ppasswd[32], - phmac1[20], phmac2[20], plogin[16]; - int validuser; -#ifdef USE_ENCRYPTION - uint8_t key[512]; -#endif - - if (c->authenticated || p->protocol_version != SERVER_VERSION) - return -1; - - /* - * XXX Only authenticate against a single user/password pair for - * now. Should actually check user limits, user existance, etc, - * and make sure to take the same time for cryptography for - * unexisting users. - * Ideally, there also should be a private server key used as - * initial tunnel so that the user name be not in pain text. - * XXX - * Beware to parse login info properly. We might want to not do - * string compares but to use whole padding for hash key generation... - * XXX - * Note that we can't expect this response to be encrypted because we - * don't know the user login yet to use a key for... We could if - * there was an additional step, and/or of we initially went with - * server private key/password tunnel first which we renegotiated - * here. - */ - - /* Pad login and memcmp */ - /* - * XXX We should lookup login in database. - */ - validuser = 0; - (void) memset(plogin, '\0', 16); - (void) strncpy(plogin, SERVER_LOGIN, 15); - if (memcmp(p->login, plogin, 16) == 0) - validuser = 1; - else - syslog(LOG_NOTICE, "cpacket_auth_handler(%d) - Bad user", - c->fd); - - /* - * Calculate sha1 and rmd160 hmac hashes for password and place first - * 80 bits of each into the hmac field. This is calculated using the - * nonce+random data previously sent to the client. We do it for both - * noncerand. - */ - /* XXX If !validuser we should use a dummy password */ - (void) memset(ppasswd, '\0', 32); - (void) strncpy(ppasswd, SERVER_PASSWD, 31); - - hmac_sha1((uint8_t *)c->noncerand1, 32, ppasswd, 32, hmac1); - hmac_rmd160((uint8_t *)c->noncerand1, 32, ppasswd, 32, &hmac1[20]); - (void) memcpy(phmac1, hmac1, 10); - (void) memcpy(&phmac1[10], &hmac1[20], 10); - - hmac_sha1((uint8_t *)c->noncerand2, 32, ppasswd, 32, hmac2); - hmac_rmd160((uint8_t *)c->noncerand2, 32, ppasswd, 32, &hmac2[20]); - (void) memcpy(phmac2, hmac2, 10); - (void) memcpy(&phmac2[10], &hmac2[20], 10); - - /* Now verify if client results match exactly ours, drop it if not. */ - if (!validuser || memcmp(phmac1, p->hmac1, 20) != 0 || - memcmp(phmac2, p->hmac2, 20) != 0) { - /* XXX Also log username, maybe also hmacs */ - syslog(LOG_NOTICE, - "cpacket_auth_handler(%d) - Failed auth!", c->fd); - /* XXX Clear crypto info! We need a common exit point! */ - return -1; - } - -#ifdef USE_ENCRYPTION - /* - * Matching hmacs. - * Use the full hmac results (320 bits) to create a session private - * key. This is done by using the hmac process again using the - * current full hmac against te user password. This generates a 320 - * bits key for use with the mmenc cipher, which is immediately - * enabled. This is done to create an input key wiht hmac1 and output - * one with hmac2. - */ - hmac_sha1(hmac1, 40, ppasswd, 32, key); - hmac_rmd160(hmac1, 40, ppasswd, 32, &key[20]); - mmenc_init(&c->sendq.enc_out, key, 40); - - hmac_sha1(hmac2, 40, ppasswd, 32, key); - hmac_rmd160(hmac2, 40, ppasswd, 32, &key[20]); - mmenc_init(&c->recvq.enc_in, key, 40); - - c->mmencrypt = 1; - - (void) memset(key, '\0', 40); -#endif - - (void) memset(hmac1, '\0', 40); - (void) memset(hmac2, '\0', 40); - (void) memset(phmac1, '\0', 40); - (void) memset(phmac2, '\0', 40); - (void) memset(ppasswd, '\0', 32); - - 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 NOOP */ - - return 0; -} - -static int -cpacket_direction_handler(client_t *c, uint16_t *ptr) -{ - struct cpacket_direction *p = (struct cpacket_direction *)ptr; - - /* uint8_t so is always between 0 and 255 */ - c->ship.i_angle = p->angle; - - return 0; -} - -/* ARGSUSED */ -static int -cpacket_thrust_acc_handler(client_t *c, uint16_t *ptr) -{ - - if (++(c->ship.i_thrust) > c->ship.ship->thrust_max) - c->ship.i_thrust = c->ship.ship->thrust_max; - - return 0; -} - -/* ARGSUSED */ -static int -cpacket_thrust_dec_handler(client_t *c, uint16_t *ptr) -{ - - if (--(c->ship.i_thrust) < 0) - c->ship.i_thrust = 0; - - return 0; -} - -/* ARGSUSED */ -static int -cpacket_shield_handler(client_t *c, uint16_t *ptr) -{ - - c->ship.i_shield_on = (c->ship.i_shield_on != 0 ? 0 : 1); - - return 0; -} - -/* ARGSUSED */ -static int -cpacket_cloak_handler(client_t *c, uint16_t *ptr) -{ - - c->ship.i_cloak_on = (c->ship.i_cloak_on != 0 ? 0 : 1); - - return 0; -} - -static int -cpacket_torp_handler(client_t *c, uint16_t *ptr) -{ - struct cpacket_torp *p = (struct cpacket_torp *)ptr; - - torp_create(c, p->angle); - - return 0; -} - -/* ARGSUSED */ -static int -cpacket_slow_handler(client_t *c, uint16_t *cptr) -{ - - if (c->slowlevel < 2) - c->slowlevel++; - c->slowcnt = c->slowlevel; - - return 0; -} - -/* ARGSUSED */ -static int -cpacket_fast_handler(client_t *c, uint16_t *cptr) -{ - - if (c->slowlevel > 0) - c->slowlevel--; - c->slowcnt = c->slowlevel; - - return 0; -} - -/* ARGSUSED */ -static int -cpacket_quit_handler(client_t *c, uint16_t *ptr) -{ - - return -1; -} - - -/* ARGSUSED */ -static int -cvpacket_message_handler(client_t *c, uint16_t *ptr) -{ -/* struct cvpacket_message *p = (struct cvpacket_message *)ptr;*/ - - /* XXX NOOP */ - return 0; -} diff --git a/mmsoftware/mystic_ships/server/src/packets.h b/mmsoftware/mystic_ships/server/src/packets.h deleted file mode 100644 index b45cb05..0000000 --- a/mmsoftware/mystic_ships/server/src/packets.h +++ /dev/null @@ -1,52 +0,0 @@ -/* $Id: packets.h,v 1.1 2006/12/31 08:32:40 mmondor Exp $ */ - -/* - * Copyright (c) Matthew Mondor - * ALL RIGHTS RESERVED. - */ - - - -#ifndef _PACKETS_H_ -#define _PACKETS_H_ - - - -#include -#include -#include -#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)(client_t *, uint16_t *); -}; - - - -void packets_init(void); -void packets_update(void); -int spacket_auth_send(client_t *); -int spacket_pong_send(client_t *); -int spacket_eof_send(client_t *); -int spacket_ship_send(client_t *, uint16_t, ship_t *); -int spacket_shipinfo_send(client_t *); -int spacket_torp_send(client_t *, torp_t *); - - - -extern struct packet_index cpacket_index[CVPACKET_MAX]; - - - -#endif diff --git a/mmsoftware/mystic_ships/server/src/recvq.c b/mmsoftware/mystic_ships/server/src/recvq.c deleted file mode 100644 index 6cd296d..0000000 --- a/mmsoftware/mystic_ships/server/src/recvq.c +++ /dev/null @@ -1,358 +0,0 @@ -/* $Id: recvq.c,v 1.1 2006/12/31 08:32:40 mmondor Exp $ */ - -/* - * Copyright (c) 2006, Matthew Mondor - * ALL RIGHTS RESERVED. - */ - - - -#include -#include -#include -#include -#include -#include - -#include - -#include -#include - -#ifdef USE_ENCRYPTION -#include -#endif - -#include -#include - -#ifdef USE_COMPRESSION -#include -#endif - - - -#define RBUFFER_SIZE 4096 -#ifdef USE_COMPRESSION -#define IBUFFER_SIZE (RBUFFER_SIZE * 64) -#endif - - - -static int8_t rbuffer[RBUFFER_SIZE]; -#ifdef USE_COMPRESSION -static char ibuffer[IBUFFER_SIZE]; -#endif - - - -int -recvq_init(recvq_t *q, size_t size) -{ - - if ((size & 1) != 0) - size++; - - if ((q->buffer = malloc(size)) != NULL) { -#ifdef USE_COMPRESSION - q->zin.next_in = q->zin.next_out = NULL; - q->zin.avail_in = q->zin.avail_out = 0; - q->zin.zalloc = NULL; - q->zin.zfree = NULL; - q->zin.opaque = NULL; - if (inflateInit(&q->zin) == Z_OK) { - q->size = size; - - return 0; - } else { - syslog(LOG_NOTICE, "recvq_init() - inflateInit() - %s", - strerror(errno)); - free(q->buffer); - } -#else - q->size = size; - return 0; -#endif /* USE_COMPRESSION */ - } else - syslog(LOG_NOTICE, "recvq_init() - malloc() - %s", - strerror(errno)); - - return -1; -} - -void -recvq_destroy(recvq_t *q) -{ - - free(q->buffer); -#ifdef USE_COMPRESSION - (void) inflateEnd(&q->zin); -#endif -#ifdef USE_ENCRYPTION - (void) memset(&q->enc_in, '\0', sizeof(mmenc_t)); -#endif -} - -void -recvq_reset(recvq_t *q, void *cptr) -{ - client_t *c = (client_t *)cptr; - - q->client = c; - q->fd = c->fd; - q->tail = q->ptail = 0; - - q->variable = q->last = -1; - q->length = -1; - - q->recvpackets = 0; - -#ifdef USE_COMPRESSION - (void) inflateReset(&q->zin); -#endif -} - -/* - * Reads up to RBUFFER_SIZE of ready data from the socket and fills the - * received packets queue buffer. Buffered packets are 16-bit aligned with 0 - * padding. Minimizes calls to read(2) by using a buffer and recopying it - * instead of reading one byte at a time for the packet type and length. - * Automatically proceses CPACKET_PING for accurate latency evaluation, - * which packets will not be queued. - * Returns 0 on success or -1 on error. - */ -int -recvq_read(recvq_t *q) -{ - ssize_t s; - int8_t *ptr, *tptr, *buf; - client_t *c = (client_t *)q->client; - - /* - * First read available data in an unaligned buffer, which may then - * begin and end by partial packets and contain multiple unaligned - * packets. - */ - { - int eof = 0; -#ifdef USE_COMPRESSION - int ret; -#endif - - if ((s = read(q->fd, rbuffer, RBUFFER_SIZE)) == -1) { - if (errno == EAGAIN) - return 0; - else - eof = 1; - } - if (s == 0) - eof = 1; - - if (eof) { - syslog(LOG_NOTICE, "recvq_read(%d) - EOF", q->fd); - return -1; - } - -#ifdef USE_ENCRYPTION - /* Decrypt incomming data */ - if (c->mmencrypt) - mmenc_decrypt(&q->enc_in, rbuffer, s); -#endif - -#ifdef USE_COMPRESSION - /* Decompress incomming data */ - q->zin.next_in = rbuffer; - q->zin.avail_in = s; - q->zin.next_out = ibuffer; - q->zin.avail_out = IBUFFER_SIZE; - if ((ret = inflate(&q->zin, Z_SYNC_FLUSH)) != Z_OK && - ret != Z_STREAM_END) { - syslog(LOG_NOTICE, "recvq_read(%d) - inflate()", - q->fd); - return -1; - } - buf = ibuffer; - s = IBUFFER_SIZE - q->zin.avail_out; -#else - buf = rbuffer; -#endif /* USE_COMPRESSION */ - } - - /* - * Process previous raw buffer and queue aligned full packets. - * We take care to verify recvq boundaries when adding even bytes, - * we know that the buffer is 16-bit aligned. - */ - for (ptr = buf, tptr = &buf[s]; ptr < tptr; ) { - if (q->length != -1) { - /* Still reading incomplete packet */ - - if (q->length > 0) { - /* Byte may be odd or even check recvq limit */ - if (q->tail == q->size) { - syslog(LOG_NOTICE, - "recv_read(%d) - recvq full", - q->fd); - return -1; - } - q->buffer[q->tail++] = *ptr++; - q->length--; - } - if (q->length == 0) { -complete: - /* Packet complete, align buffer for next */ - q->length = -1; - if ((q->tail & 1) != 0) - q->buffer[q->tail++] = 0; - - /* - * Update counter for timeout checking - * we don't account received ping or fast - * packets, because they are automatically - * sent by the client regularily. - */ - if (q->last != CPACKET_PING && - q->last != CPACKET_FAST) - q->recvpackets++; - - /* - * Immediately process ping request packets - * since we must properly permit to measure - * the network latency. We also dequeue ping - * packets transparently. - * We only allow one ping request from server - * heartbeat cycle, and we only allow them if - * the client is already authenticated. - * Note that server pong packets are sent - * directly rather than passing through the - * sendq, unlike other packets. - */ - if (q->last == CPACKET_PING) { - /* Discard packet from queue */ - q->tail -= 2; - if (!c->authenticated || - c->askedping || - spacket_pong_send(c) == -1) { - syslog(LOG_NOTICE, - "recvq_read(%d) - " - "spacket_pong_send()", - q->fd); - return -1; - } - c->askedping = 1; - continue; - } - - /* - * Update the completed packets tail pointer, - * ptail. - */ - q->ptail = q->tail; - - continue; - } - } else { - /* - * New packet. We must evaluate packet length by - * packet type, and take into consideration variable - * packets which length will be provided as a second - * byte. For variable packets, we must make sure that - * the received length is at least high enough for the - * minimum length of the packet. - */ - if (q->variable != -1) { - /* Receiving length of variable packet */ - q->length = (ssize_t)((uint8_t)*ptr); - if (q->length < - cpacket_index[q->variable].size) { - syslog(LOG_NOTICE, - "recvq_read(%d) - " - "Invalid packet size %d", - q->fd, q->length); - q->variable = -1; - q->length = -1; - return -1; - } - q->variable = -1; - - /* Odd */ - q->buffer[q->tail++] = *ptr++; - q->length--; - } else { - int8_t t; - - /* Receiving type of packet. Even */ - if (q->tail == q->size) { - syslog(LOG_NOTICE, - "recv_read(%d) - recvq full", - q->fd); - return -1; - } - t = (q->buffer[q->tail++] = *ptr++); - q->last = t; - - if (t < 0 || t > CVPACKET_MAX) { - syslog(LOG_NOTICE, "recv_read(%d) - " - "Invalid packet type 0x%02x", - q->fd, (uint8_t)t); - return -1; - } - if (t < CPACKET_MAX) { - /* Fixed packet */ - q->length = cpacket_index[t].size - 1; - q->variable = -1; - /* - * Special provision for single byte - * packets - */ - if (q->length == 0) - goto complete; - } else { - /* Variable packet, awaiting size */ - q->length = -1; - q->variable = t; - } - } - } - } - - return 0; -} - -/* - * Returns a pointer to the starting point of the buffer and the size of the - * buffer. May contain multiple contiguous 16-bit aligned packets with - * padding. Only full packets are returned. - */ -void -recvq_content(recvq_t *q, uint8_t **buf, size_t *size) -{ - - *buf = (uint8_t *)q->buffer; - *size = q->ptail; - if ((*size & 1) != 0) - (*size)++; -} - -/* - * To be called after recvq_content() supplied buffer is processed. - * Repositions offsets while making sure to preserve any previously incomplete - * packets. - */ -void -recvq_content_reset(recvq_t *q) -{ - if (q->ptail == 0) - return; - - if (q->tail > q->ptail) { - register size_t s; - - s = q->tail - q->ptail; - (void) mm_memcpy(q->buffer, &q->buffer[q->ptail], s); - q->tail = s; - } else - q->tail = 0; - - q->ptail = 0; -} diff --git a/mmsoftware/mystic_ships/server/src/recvq.h b/mmsoftware/mystic_ships/server/src/recvq.h deleted file mode 100644 index bc31155..0000000 --- a/mmsoftware/mystic_ships/server/src/recvq.h +++ /dev/null @@ -1,59 +0,0 @@ -/* $Id: recvq.h,v 1.1 2006/12/31 08:32:40 mmondor Exp $ */ - -/* - * Copyright (c) 2006, Matthew Mondor - * ALL RIGHTS RESERVED. - */ - - - -#ifndef _RECVQ_H_ -#define _RECVQ_H_ - - - -#include - -#include - -#ifdef USE_COMPRESSION -#include -#endif - -#ifdef USE_ENCRYPTION -#include -#include -#endif - - - -typedef struct recvq { - void *client; /* (client_t *) */ - int fd; - uint8_t *buffer; - size_t size, tail, ptail; - /* Necessary for fragmented packets reassembly */ - int variable, last; - ssize_t length; - /* To implement input timeout */ - int recvpackets; -#ifdef USE_COMPRESSION - z_stream zin; -#endif -#ifdef USE_ENCRYPTION - mmenc_t enc_in; -#endif -} recvq_t; - - - -int recvq_init(recvq_t *, size_t); -void recvq_destroy(recvq_t *); -void recvq_reset(recvq_t *, void *); -int recvq_read(recvq_t *); -void recvq_content(recvq_t *, uint8_t **, size_t *); -void recvq_content_reset(recvq_t *); - - - -#endif diff --git a/mmsoftware/mystic_ships/server/src/sendq.c b/mmsoftware/mystic_ships/server/src/sendq.c deleted file mode 100644 index e2e428c..0000000 --- a/mmsoftware/mystic_ships/server/src/sendq.c +++ /dev/null @@ -1,248 +0,0 @@ -/* $Id: sendq.c,v 1.1 2006/12/31 08:32:40 mmondor Exp $ */ - -/* - * Copyright (c) 2006, Matthew Mondor - * ALL RIGHTS RESERVED. - */ - - - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include -#include -#include - -#ifdef USE_ENCRYPTION -#include -#endif - -#ifdef USE_COMPRESSION -#include -#endif - - - -#ifdef USE_COMPRESSION -#define DBUFFER_SIZE 16384 -#endif - - -#ifdef USE_COMPRESSION -static char dbuffer[DBUFFER_SIZE]; -#endif - - - -/* - * Initializes a sendq object, allocating the queue buffer - */ -int -sendq_init(sendq_t *q, size_t size) -{ - - if ((q->buffer = malloc(size)) != NULL) { -#ifdef USE_COMPRESSION - q->zout.next_in = q->zout.next_out = NULL; - q->zout.avail_in = q->zout.avail_out = 0; - q->zout.zalloc = NULL; - q->zout.zfree = NULL; - q->zout.opaque = NULL; - if (deflateInit(&q->zout, Z_DEFAULT_COMPRESSION) == Z_OK) { - q->size = size; - - return 0; - } else { - syslog(LOG_NOTICE, "sendq_init() - deflateInit() - %s", - strerror(errno)); - free(q->buffer); - } -#else - q->size = size; - return 0; -#endif /* USE_COMPRESSION */ - } else - syslog(LOG_NOTICE, "sendq_init() - malloc() - %s", - strerror(errno)); - - return -1; -} - -void -sendq_reset(sendq_t *q, void *cptr) -{ - client_t *c = (client_t *)cptr; - - q->client = c; - q->fd = c->fd; - q->head = q->tail = 0; - -#ifdef USE_COMPRESSION - (void) deflateReset(&q->zout); -#endif -} - -/* - * Destroys a sendq object, freeing the queue buffer. - * Note that this only gets called when a full page of client objects are - * destroyed. As such, they cryptographic keys, if any, are destroyed by - * client_destroy_marked(). - */ -void -sendq_destroy(sendq_t *q) -{ - - free(q->buffer); -#ifdef USE_COMPRESSION - (void) deflateEnd(&q->zout); -#endif -} - -/* - * 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) -{ -#ifdef USE_ENCRYPTION - client_t *c = (client_t *)q->client; -#endif - - /* Compress outgoing data, in flushed mode if unbuffered */ -#ifdef USE_COMPRESSION - q->zout.next_in = buf; - q->zout.avail_in = size; - q->zout.next_out = dbuffer; - q->zout.avail_out = DBUFFER_SIZE; - if (deflate(&q->zout, (buffer ? Z_NO_FLUSH : Z_SYNC_FLUSH)) != Z_OK) - return -1; - buf = dbuffer; - size = DBUFFER_SIZE - q->zout.avail_out; -#endif - - /* Queue new data to sendq buffer */ - if (size > 0) { -#ifdef USE_ENCRYPTION - /* Encrypt outgoing data */ - if (c->mmencrypt) - mmenc_encrypt(&q->enc_out, buf, size); -#endif - - if (q->tail + size > q->size) - return -1; - - (void) mm_memcpy(&q->buffer[q->tail], buf, size); - q->tail += size; - if (bfunc != NULL && buffer) - bfunc(bfuncarg); - } - - /* Flush sendq if unbuffered write request */ - if (size > 0 && !buffer) - return sendq_flush(q, -1); - - 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; -} - -/* - * Allows to flush the zlib compression output buffer. - * Queues any flushed data to the sendq. - */ -int -sendq_zflush(sendq_t *q, void (*bfunc)(void *), void *bfuncarg) -{ -#ifdef USE_ENCRYPTION - client_t *c = (client_t *)q->client; -#endif - -#ifdef USE_COMPRESSION - char ch; - size_t size; - - q->zout.next_in = &ch; - q->zout.avail_in = 0; - q->zout.next_out = dbuffer; - q->zout.avail_out = DBUFFER_SIZE; - if (deflate(&q->zout, Z_SYNC_FLUSH) != Z_OK) - return -1; - - if ((size = DBUFFER_SIZE - q->zout.avail_out) > 0) { - if (q->tail + size > q->size) - return -1; - -#ifdef USE_ENCRYPTION - /* Encrypt outgoing data */ - if (c->mmencrypt) - mmenc_encrypt(&q->enc_out, dbuffer, size); -#endif - - (void) mm_memcpy(&q->buffer[q->tail], dbuffer, size); - q->tail += size; - if (bfunc != NULL) - bfunc(bfuncarg); - } -#endif - - return 0; -} diff --git a/mmsoftware/mystic_ships/server/src/sendq.h b/mmsoftware/mystic_ships/server/src/sendq.h deleted file mode 100644 index 49b47b1..0000000 --- a/mmsoftware/mystic_ships/server/src/sendq.h +++ /dev/null @@ -1,57 +0,0 @@ -/* $Id: sendq.h,v 1.1 2006/12/31 08:32:40 mmondor Exp $ */ - -/* - * Copyright (c) 2006, Matthew Mondor - * ALL RIGHTS RESERVED. - */ - - - -#ifndef _SENDQ_H_ -#define _SENDQ_H_ - - - -#include - -#include - -#ifdef USE_COMPRESSION -#include -#endif - -#include - -#ifdef USE_ENCRYPTION -#include -#include -#endif - - - -typedef struct sendq { - void *client; /* (client_t *) */ - int fd; - uint8_t *buffer; - size_t size, head, tail; -#ifdef USE_COMPRESSION - z_stream zout; -#endif -#ifdef USE_ENCRYPTION - mmenc_t enc_out; -#endif -} sendq_t; - - - -int sendq_init(sendq_t *, size_t); -void sendq_destroy(sendq_t *); -void sendq_reset(sendq_t *, void *); -int sendq_write(sendq_t *, uint8_t *, size_t, void (*)(void *), void *, - int); -int sendq_zflush(sendq_t *, void (*)(void *), void *); -int sendq_flush(sendq_t *, size_t); - - - -#endif diff --git a/mmsoftware/mystic_ships/server/src/ships.c b/mmsoftware/mystic_ships/server/src/ships.c deleted file mode 100644 index e341fce..0000000 --- a/mmsoftware/mystic_ships/server/src/ships.c +++ /dev/null @@ -1,244 +0,0 @@ -/* $Id: ships.c,v 1.1 2006/12/31 08:32:40 mmondor Exp $ */ - -/* - * Copyright (c) 2006, Matthew Mondor - * ALL RIGHTS RESERVED. - */ - - - -#include -#include - -#include - -#include -#include -#include - - - -/* XXX We need to set these to decent values */ -struct shipdesc ships[SHIP_MAX] = { - { /* CA */ - 30.0, /* Radius */ - 10.0, /* Weight */ - 100.0, /* Hull max */ - 1.0, /* Hull regen */ - 100.0, /* Shield max */ - 10.0, /* Shield cost */ - 3.0, /* Shield regen */ - 50.0, /* Cloak cost */ - 10000.0, /* Fuel max */ - 30.0, /* Fuel regen */ - 9.0, /* Thrust max */ - 0.20, /* Thrust acc */ - 0.30, /* Thrust dec */ - 5.0, /* Thrust acc cost */ - 5.0, /* Thrust dec cost */ - 6.0, /* Thrust cost */ - 80/*2.0*/, /* Turn thrust */ - 0.5, /* Turn cost */ - 100.0, /* Torp cost */ - 90.0, /* Torp damage */ - 2.0, /* Torp radius */ - 11.0, /* Torp speed */ - 60, /* Torp life */ - 90.0, /* Phaser cost */ - 90.0, /* Phaser damage */ - 5.0, /* Phaser recharge */ - 100.0 /* Phaser radius */ - }, - { /* DD */ - 40.0, /* Radius */ - 10.0, /* Weight */ - 100.0, /* Hull max */ - 1.0, /* Hull regen */ - 100.0, /* Shield max */ - 10.0, /* Shield cost */ - 3.0, /* Shield regen */ - 50.0, /* Cloak cost */ - 10000.0, /* Fuel max */ - 50.0, /* Fuel regen */ - 9.0, /* Thrust max */ - 0.60, /* Thrust acc */ - 0.60, /* Thrust dec */ - 2.0, /* Thrust acc cost */ - 2.0, /* Thrust dec cost */ - 1.0, /* Thrust cost */ - 1.0, /* Turn thrust */ - 0.5, /* Turn cost */ - 90.0, /* Torp cost */ - 90.0, /* Torp damage */ - 3.0, /* Torp radius */ - 12.0, /* Torp speed */ - 60, /* Torp life */ - 90.0, /* Phaser cost */ - 90.0, /* Phaser damage */ - 5.0, /* Phaser recharge */ - 100.0 /* Phaser radius */ - } -}; - - - -void -ship_init(ship_t *s, int type) -{ - struct shipdesc *d; - - DEBUG_ASSERT(s != NULL); - DEBUG_ASSERT(type > -1 && type < SHIP_MAX); - - d = &ships[type]; - - s->ship = d; - s->hull = d->hull_max; - s->shield = d->shield_max; - s->fuel = d->fuel_max; - s->thrust = s->i_thrust = 0.0; - s->phaser_delay = 0.0; - s->shield_on = s->i_shield_on = 1; - s->cloak_on = s->i_cloak_on = 0; - s->torps = 0; - - /* XXX Random position/angle for now */ - s->angle = s->i_angle = (random() % 255); - /* - s->x = d->radius + (random() % (WORLD_X_MAX - (d->radius * 2) - 1)); - s->y = d->radius + (random() % (WORLD_Y_MAX - (d->radius * 2) - 1)); - XXX - */ - s->x = WORLD_X_MAX / 2; - s->y = WORLD_Y_MAX / 2; -} - -void -ship_update(ship_t *s) -{ - double d; - - /* - * Regenerate resources - */ - if (s->fuel < s->ship->fuel_max) { - if ((s->fuel += s->ship->fuel_regen) > s->ship->fuel_max) - s->fuel = s->ship->fuel_max; - } - if (s->shield < s->ship->shield_max) { - if ((s->shield += s->ship->shield_regen) > s->ship->shield_max) - s->shield = s->ship->shield_max; - } - if (s->hull < s->ship->hull_max) { - if ((s->hull += s->ship->hull_regen) > s->ship->hull_max) - s->hull = s->ship->hull_max; - } - if (s->phaser_delay > 0) { - if ((s->phaser_delay -= s->ship->phaser_recharge) < 0) - s->phaser_delay = 0; - } - - /* - * Adjust angle towards intended direction and account cost - */ - if (s->fuel >= s->ship->turn_cost && s->angle != s->i_angle) { - s->fuel -= s->ship->turn_cost; - int i, min, max; - - /* - * Calculate how much we can turn taking into account - * inertia relative to thrust/velocity - */ - /* - i = (int)(s->ship->turn_thrust * - (s->ship->thrust_max - s->thrust + 0.3)); - */ - - if (s->thrust == 0) - i = 255; - else - i = (int)(s->ship->turn_thrust / - (s->thrust * s->thrust)); - - /* - * Turn in wanted direction if needed, taking into account - * the shortest path - */ - if (s->i_angle < s->angle) { - min = s->i_angle; - max = s->angle; - } else { - min = s->angle; - max = s->i_angle; - } - if (i > max - min) - i = max - min; - - if (i > 256 - max + min) - s->angle = s->i_angle; - else if ((uint8_t)(s->angle - s->i_angle) > 127) - s->angle += i; - else - s->angle -= i; - - s->angle &= 0xff; - } - - /* - * Adjust thrust towards intended thrust if needed and account - * acceleration/deceleration cost - */ - if (s->fuel >= s->ship->thrust_acc_cost && s->thrust < s->i_thrust) { - s->fuel -= s->ship->thrust_acc_cost; - if ((s->thrust += s->ship->thrust_acc) > s->i_thrust) - s->thrust = s->i_thrust; - } else if (s->fuel >= s->ship->thrust_dec_cost && - s->thrust > s->i_thrust) { - s->fuel -= s->ship->thrust_dec_cost; - if ((s->thrust -= s->ship->thrust_dec) < s->i_thrust) - s->thrust = s->i_thrust; - } - - /* - * Adjust ship position and account thrust cost. - * If we don't have enough fuel to maintain current thrust, we - * decelerate at no cost. - */ - if (s->thrust > 0 && - s->fuel >= (d = s->ship->thrust_cost * s->thrust)) { - int i, ox, oy, r; - - r = s->ship->radius; - ox = s->x; - oy = s->y; - s->fuel -= d; - i = ((int)s->thrust) + 1; - s->x = VECTOR_X(s->x, s->angle, i); - s->y = VECTOR_Y(s->y, s->angle, i); - /* XXX Observe world limits and block/bounce */ - if (s->x < r || s->x > WORLD_X_MAX - r - 1 || - s->y < r || s->y > WORLD_Y_MAX - r - 1) { - s->x = ox; - s->y = oy; - s->angle = s->i_angle = angle_bounce(s->angle); - } - } else { - if ((s->thrust -= s->ship->thrust_dec) < 0) - s->thrust = 0; - s->i_thrust = s->thrust; - } - - /* - * Adjust shield and cloak states. Movement is prioritary. - */ - if (s->i_shield_on && s->fuel >= s->ship->shield_cost) { - s->fuel -= s->ship->shield_cost; - s->shield_on = 1; - } else - s->shield_on = 0; - if (s->i_cloak_on && s->fuel >= s->ship->cloak_cost) { - s->fuel -= s->ship->cloak_cost; - s->cloak_on = 1; - } else - s->cloak_on = 0; -} diff --git a/mmsoftware/mystic_ships/server/src/ships.h b/mmsoftware/mystic_ships/server/src/ships.h deleted file mode 100644 index 2c3b595..0000000 --- a/mmsoftware/mystic_ships/server/src/ships.h +++ /dev/null @@ -1,62 +0,0 @@ -/* $Id: ships.h,v 1.1 2006/12/31 08:32:40 mmondor Exp $ */ - -/* - * Copyright (c) 2006, Matthew Mondor - * ALL RIGHTS RESERVED. - */ - - - -#ifndef _SHIPS_H_ -#define _SHIPS_H_ - - - -enum ship_types { - SHIP_CA = 0, - SHIP_DD, - SHIP_MAX -}; - -struct shipdesc { - double radius; - double weight; - double hull_max, hull_regen; - double shield_max, shield_cost, shield_regen; - double cloak_cost; - double fuel_max, fuel_regen; - double thrust_max, thrust_acc, thrust_dec, - thrust_acc_cost, thrust_dec_cost, thrust_cost; - double turn_thrust, turn_cost; - double torp_cost, torp_damage, torp_radius, torp_speed; - int torp_life; - double phaser_cost, phaser_damage, phaser_recharge, - phaser_radius; -}; - -typedef struct ship { - struct shipdesc *ship; - double hull; - double shield; - double fuel; - double thrust, i_thrust; - double phaser_delay; /* phaser_cost - phaser_recharge */ - int shield_on, i_shield_on; - int cloak_on, i_cloak_on; - int torps; - int angle, i_angle; - int x, y; -} ship_t; - - - -void ship_init(ship_t *, int); -void ship_update(ship_t *); - - - -extern struct shipdesc ships[SHIP_MAX]; - - - -#endif diff --git a/mmsoftware/mystic_ships/server/src/torp.c b/mmsoftware/mystic_ships/server/src/torp.c deleted file mode 100644 index 2680216..0000000 --- a/mmsoftware/mystic_ships/server/src/torp.c +++ /dev/null @@ -1,116 +0,0 @@ -/* $Id: torp.c,v 1.1 2006/12/31 08:32:40 mmondor Exp $ */ - -/* - * Copyright (c) 2006, Matthew Mondor - * ALL RIGHTS RESERVED. - */ - - - -#include -#include - -#include - -#include -#include -#include - - - -list_t torps_list; -static pool_t torps_pool; - - - -void -torp_init(void) -{ - - DLIST_INIT(&torps_list); - if (!pool_init(&torps_pool, "torps_pool", malloc, free, NULL, NULL, - sizeof(torp_t), 65536 / sizeof(torp_t), 1, 0)) { - syslog(LOG_NOTICE, "torp_init() - pool_init()"); - exit(EXIT_FAILURE); - } -} - -void -torp_create(client_t *c, int angle) -{ - torp_t *t; - - if (c->ship.torps > 15) - return; - - if (c->ship.fuel < c->ship.ship->torp_cost) - return; - c->ship.fuel -= c->ship.ship->torp_cost; - - if ((t = (torp_t *)pool_alloc(&torps_pool, FALSE)) != NULL) { - int r = c->ship.ship->torp_radius; - - t->client = c; - t->life = t->client->ship.ship->torp_life; - t->angle = angle; - t->x = VECTOR_X(c->ship.x, angle, c->ship.ship->radius); - t->y = VECTOR_Y(c->ship.y, angle, c->ship.ship->radius); - if (t->x < r) - t->x = r; - if (t->y < r) - t->y = r; - if (t->x > WORLD_X_MAX - 1 - r) - t->x = WORLD_X_MAX - 1 - r; - if (t->y > WORLD_Y_MAX - 1 - r) - t->x = WORLD_Y_MAX - 1 - r; - c->ship.torps++; - DLIST_APPEND(&torps_list, (node_t *)t); - } else - syslog(LOG_NOTICE, "torp_create() - pool_alloc(torps_list)"); -} - -/* - * XXX We probably should cause an explosion here, possibly type specified - * as argument. - */ -void -torp_destroy(torp_t *t) -{ - - DEBUG_ASSERT(t != NULL); - - t->client->ship.torps--; - DLIST_UNLINK(&torps_list, (node_t *)t); - (void) pool_free((pnode_t *)t); -} - -void -torps_update(void) -{ - torp_t *t, *next; - int s, r, ox, oy; - - for (t = (torp_t *)DLIST_TOP(&torps_list); t != NULL; t = next) { - next = DLIST_NEXT((node_t *)t); - - if (--(t->life) == 0) { - torp_destroy(t); - continue; - } - - ox = t->x; - oy = t->y; - s = t->client->ship.ship->torp_speed; - r = t->client->ship.ship->torp_radius; - t->x = VECTOR_X(t->x, t->angle, s); - t->y = VECTOR_Y(t->y, t->angle, s); - t->x += (-4 + (random() % 8)); - t->y += (-4 + (random() % 8)); - if (t->x < r || t->x > WORLD_X_MAX - 1 - r || - t->y < r || t->y > WORLD_Y_MAX - 1 - r) { - t->x = ox; - t->y = oy; - t->angle = angle_bounce(t->angle); - } - } -} diff --git a/mmsoftware/mystic_ships/server/src/torp.h b/mmsoftware/mystic_ships/server/src/torp.h deleted file mode 100644 index 4fa2aec..0000000 --- a/mmsoftware/mystic_ships/server/src/torp.h +++ /dev/null @@ -1,44 +0,0 @@ -/* $Id: torp.h,v 1.1 2006/12/31 08:32:40 mmondor Exp $ */ - -/* - * Copyright (c) 2006, Matthew Mondor - * ALL RIGHTS RESERVED. - */ - - - -#ifndef _TORP_H_ -#define _TORP_H_ - - - -#include - - - -/* - * Through the client_t structure we access the ship_t as well and can thus - * determine parameters like torp_damage, torp_radius etc. - */ -typedef struct torp { - pnode_t pnode; - client_t *client; - int life; - int angle; - int x, y; -} torp_t; - - - -void torp_init(void); -void torp_create(client_t *, int); -void torp_destroy(torp_t *); -void torps_update(void); - - - -list_t torps_list; - - - -#endif diff --git a/mmsoftware/mystic_ships/server/src/trigonometry.c b/mmsoftware/mystic_ships/server/src/trigonometry.c deleted file mode 100644 index bf2fced..0000000 --- a/mmsoftware/mystic_ships/server/src/trigonometry.c +++ /dev/null @@ -1,50 +0,0 @@ -/* $Id: trigonometry.c,v 1.1 2006/12/31 08:32:40 mmondor Exp $ */ - -/* - * Copyright (c) 2006, Matthew Mondor - * ALL RIGHTS RESERVED. - */ - - - -#include - -#include - -#include - - - -double sin_table[256], cos_table[256]; - - - -void -trigonometry_init(void) -{ - int i; - double f; - - /* Initialize trigonometric tables. 0 points rightwards. */ - for (i = 0; i < 256; i++) { - f = ((2 * M_PI) / 256) * i; - sin_table[i] = sin(f); - cos_table[i] = cos(f); - } -} - -int -angle_bounce(int angle) -{ - - DEBUG_ASSERT(angle > -1 && angle < 256); - - /* - * XXX - * 128 means perpendicular angle bouncing. - * We shoud actually offset by 64 etc as needed too... - * Should we be told via another parameter the relative angle of - * the object we had collision with? Or the direction? - */ - return ((angle + 128) & 0xff); -} diff --git a/mmsoftware/mystic_ships/server/src/trigonometry.h b/mmsoftware/mystic_ships/server/src/trigonometry.h deleted file mode 100644 index f34b447..0000000 --- a/mmsoftware/mystic_ships/server/src/trigonometry.h +++ /dev/null @@ -1,29 +0,0 @@ -/* $Id: trigonometry.h,v 1.1 2006/12/31 08:32:40 mmondor Exp $ */ - -/* - * Copyright (c) 2006, Matthew Mondor - * ALL RIGHTS RESERVED. - */ - - - -#ifndef _TRIGONOMETRY_H_ -#define _TRIGONOMETRY_H_ - - - -#define VECTOR_X(x, a, r) ((int)(x) + (cos_table[(a)] * (r))) -#define VECTOR_Y(y, a, r) ((int)(y) + (sin_table[(a)] * (r))) - - - -void trigonometry_init(void); -int angle_bounce(int); - - - -extern double sin_table[256], cos_table[256]; - - - -#endif diff --git a/mmsoftware/paradise_adventure/GNUmakefile b/mmsoftware/paradise_adventure/GNUmakefile deleted file mode 100644 index de1b3f9..0000000 --- a/mmsoftware/paradise_adventure/GNUmakefile +++ /dev/null @@ -1,57 +0,0 @@ -# $Id: GNUmakefile,v 1.3 2007/01/07 09:38:13 mmondor Exp $ - -CC := cc -RM := rm -UNAME := uname -STRIP := strip - -CFLAGS += -Wall -Isrc - -# Enable for verbosity/debugging -#CFLAGS += -v -H -g -#LDFLAGS += -v -g - -# And to disable assertions -CFLAGS += -DNDEBUG -#CFLAGS += -g - -# And to enable profiling -#CFLAGS += -pg -#LDFLAGS += -pg - -OBJS := $(addprefix src/,main.o debug.o screen.o) -BIN := paradise_adventure - -SDL_CFLAGS := $(shell sdl-config --cflags) -SDL_LDFLAGS := $(shell sdl-config --libs) -SDL_LDFLAGS += -lSDL_image -lSDL_mixer -lSDL_ttf - -JS_CFLAGS := $(shell spidermonkey-config -c) -JS_LDFLAGS := $(shell spidermonkey-config -l) - -# 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 -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 -endif - -CFLAGS += $(SDL_CFLAGS) $(JS_CFLAGS) -LDFLAGS += $(SDL_LDFLAGS) $(JS_LDFLAGS) - -all: $(BIN) - -%.o: %.c - $(CC) -c $(CFLAGS) -I. -o $@ $< - -$(BIN): $(OBJS) - $(CC) -o $@ $(OBJS) $(LDFLAGS) - $(STRIP) -s -w -R .comment -R .ident -R .debug* $@* - -clean: - $(RM) -f $(BIN) $(BIN).exe $(OBJS) stdout.txt stderr.txt diff --git a/mmsoftware/paradise_adventure/LICENSE.txt b/mmsoftware/paradise_adventure/LICENSE.txt deleted file mode 100644 index d0f95aa..0000000 --- a/mmsoftware/paradise_adventure/LICENSE.txt +++ /dev/null @@ -1,14 +0,0 @@ -$Id: LICENSE.txt,v 1.2 2007/01/25 21:17:59 mmondor Exp $ - -Programming Copyright (c) 2007, Matthew Mondor. -Graphic and Music art Copyright (c) 2007, - Martin Mondor, Christiane Locas and Matthew Mondor. -Game design Copyright (c) 2007, Martin Mondor and Matthew Mondor. - -Rights granted for personal, private, non-commercial entertainment -use. - -Redistribution in any form (source or binary), with or without -modifications expressly forbidden without prior written permission -from both Matthew Mondor and Martin Mondor. - diff --git a/mmsoftware/paradise_adventure/doc/specs.txt b/mmsoftware/paradise_adventure/doc/specs.txt deleted file mode 100755 index f805cf0..0000000 --- a/mmsoftware/paradise_adventure/doc/specs.txt +++ /dev/null @@ -1,29 +0,0 @@ -$Id: specs.txt,v 1.1 2007/01/06 19:48:04 mmondor Exp $ - - -Screen resolution: 1024x768 -Borders: 16px - -Paintings: 766x592 (scan at 72bps, rotate, rescale) -Text Area: 210x592 - -Icons area: 766x128 -Icon size: 64x64 - -?Text: 22 columns. 29 lines using Courrier New 14pts - - -To process images -================= - -Script-Fu->FuzzyBorder - Color: black - Border size: 8 - Blur border: no - Granularity: 1.85 - Add Shadow: yes - Shadow weight: 60 - Work on copy: yes - Flatten image: yes - -Save as PNG diff --git a/mmsoftware/paradise_adventure/font/deloise.ttf b/mmsoftware/paradise_adventure/font/deloise.ttf deleted file mode 100644 index bc16c1c..0000000 Binary files a/mmsoftware/paradise_adventure/font/deloise.ttf and /dev/null differ diff --git a/mmsoftware/paradise_adventure/img/castle_afar.png b/mmsoftware/paradise_adventure/img/castle_afar.png deleted file mode 100644 index 011b03d..0000000 Binary files a/mmsoftware/paradise_adventure/img/castle_afar.png and /dev/null differ diff --git a/mmsoftware/paradise_adventure/img/castle_afar.readme b/mmsoftware/paradise_adventure/img/castle_afar.readme deleted file mode 100644 index ea782be..0000000 --- a/mmsoftware/paradise_adventure/img/castle_afar.readme +++ /dev/null @@ -1 +0,0 @@ -Copyright (c) 2006, Martin Mondor. diff --git a/mmsoftware/paradise_adventure/img/castle_front.png b/mmsoftware/paradise_adventure/img/castle_front.png deleted file mode 100644 index 7000647..0000000 Binary files a/mmsoftware/paradise_adventure/img/castle_front.png and /dev/null differ diff --git a/mmsoftware/paradise_adventure/img/castle_front.readme b/mmsoftware/paradise_adventure/img/castle_front.readme deleted file mode 100644 index ea782be..0000000 --- a/mmsoftware/paradise_adventure/img/castle_front.readme +++ /dev/null @@ -1 +0,0 @@ -Copyright (c) 2006, Martin Mondor. diff --git a/mmsoftware/paradise_adventure/img/castle_king.png b/mmsoftware/paradise_adventure/img/castle_king.png deleted file mode 100644 index 50c13cf..0000000 Binary files a/mmsoftware/paradise_adventure/img/castle_king.png and /dev/null differ diff --git a/mmsoftware/paradise_adventure/img/castle_king.readme b/mmsoftware/paradise_adventure/img/castle_king.readme deleted file mode 100644 index ea782be..0000000 --- a/mmsoftware/paradise_adventure/img/castle_king.readme +++ /dev/null @@ -1 +0,0 @@ -Copyright (c) 2006, Martin Mondor. diff --git a/mmsoftware/paradise_adventure/img/castle_threeway.png b/mmsoftware/paradise_adventure/img/castle_threeway.png deleted file mode 100644 index ef13930..0000000 Binary files a/mmsoftware/paradise_adventure/img/castle_threeway.png and /dev/null differ diff --git a/mmsoftware/paradise_adventure/img/castle_threeway.readme b/mmsoftware/paradise_adventure/img/castle_threeway.readme deleted file mode 100644 index ea782be..0000000 --- a/mmsoftware/paradise_adventure/img/castle_threeway.readme +++ /dev/null @@ -1 +0,0 @@ -Copyright (c) 2006, Martin Mondor. diff --git a/mmsoftware/paradise_adventure/img/cavern_deadend.png b/mmsoftware/paradise_adventure/img/cavern_deadend.png deleted file mode 100644 index e78f704..0000000 Binary files a/mmsoftware/paradise_adventure/img/cavern_deadend.png and /dev/null differ diff --git a/mmsoftware/paradise_adventure/img/cavern_deadend.readme b/mmsoftware/paradise_adventure/img/cavern_deadend.readme deleted file mode 100644 index ea782be..0000000 --- a/mmsoftware/paradise_adventure/img/cavern_deadend.readme +++ /dev/null @@ -1 +0,0 @@ -Copyright (c) 2006, Martin Mondor. diff --git a/mmsoftware/paradise_adventure/img/cavern_entry.png b/mmsoftware/paradise_adventure/img/cavern_entry.png deleted file mode 100644 index 21a503e..0000000 Binary files a/mmsoftware/paradise_adventure/img/cavern_entry.png and /dev/null differ diff --git a/mmsoftware/paradise_adventure/img/cavern_entry.readme b/mmsoftware/paradise_adventure/img/cavern_entry.readme deleted file mode 100644 index ea782be..0000000 --- a/mmsoftware/paradise_adventure/img/cavern_entry.readme +++ /dev/null @@ -1 +0,0 @@ -Copyright (c) 2006, Martin Mondor. diff --git a/mmsoftware/paradise_adventure/img/cavern_fork.png b/mmsoftware/paradise_adventure/img/cavern_fork.png deleted file mode 100644 index b35761b..0000000 Binary files a/mmsoftware/paradise_adventure/img/cavern_fork.png and /dev/null differ diff --git a/mmsoftware/paradise_adventure/img/cavern_fork.readme b/mmsoftware/paradise_adventure/img/cavern_fork.readme deleted file mode 100644 index ea782be..0000000 --- a/mmsoftware/paradise_adventure/img/cavern_fork.readme +++ /dev/null @@ -1 +0,0 @@ -Copyright (c) 2006, Martin Mondor. diff --git a/mmsoftware/paradise_adventure/img/cavern_gold.png b/mmsoftware/paradise_adventure/img/cavern_gold.png deleted file mode 100644 index 26703ee..0000000 Binary files a/mmsoftware/paradise_adventure/img/cavern_gold.png and /dev/null differ diff --git a/mmsoftware/paradise_adventure/img/cavern_gold.readme b/mmsoftware/paradise_adventure/img/cavern_gold.readme deleted file mode 100644 index ea782be..0000000 --- a/mmsoftware/paradise_adventure/img/cavern_gold.readme +++ /dev/null @@ -1 +0,0 @@ -Copyright (c) 2006, Martin Mondor. diff --git a/mmsoftware/paradise_adventure/img/cavern_lake.png b/mmsoftware/paradise_adventure/img/cavern_lake.png deleted file mode 100644 index 9debb76..0000000 Binary files a/mmsoftware/paradise_adventure/img/cavern_lake.png and /dev/null differ diff --git a/mmsoftware/paradise_adventure/img/cavern_lake.readme b/mmsoftware/paradise_adventure/img/cavern_lake.readme deleted file mode 100644 index ea782be..0000000 --- a/mmsoftware/paradise_adventure/img/cavern_lake.readme +++ /dev/null @@ -1 +0,0 @@ -Copyright (c) 2006, Martin Mondor. diff --git a/mmsoftware/paradise_adventure/img/cavern_stairs.png b/mmsoftware/paradise_adventure/img/cavern_stairs.png deleted file mode 100644 index e94b326..0000000 Binary files a/mmsoftware/paradise_adventure/img/cavern_stairs.png and /dev/null differ diff --git a/mmsoftware/paradise_adventure/img/cavern_stairs.readme b/mmsoftware/paradise_adventure/img/cavern_stairs.readme deleted file mode 100644 index ea782be..0000000 --- a/mmsoftware/paradise_adventure/img/cavern_stairs.readme +++ /dev/null @@ -1 +0,0 @@ -Copyright (c) 2006, Martin Mondor. diff --git a/mmsoftware/paradise_adventure/img/cavern_tunnel.png b/mmsoftware/paradise_adventure/img/cavern_tunnel.png deleted file mode 100644 index 6d565cb..0000000 Binary files a/mmsoftware/paradise_adventure/img/cavern_tunnel.png and /dev/null differ diff --git a/mmsoftware/paradise_adventure/img/cavern_tunnel.readme b/mmsoftware/paradise_adventure/img/cavern_tunnel.readme deleted file mode 100644 index ea782be..0000000 --- a/mmsoftware/paradise_adventure/img/cavern_tunnel.readme +++ /dev/null @@ -1 +0,0 @@ -Copyright (c) 2006, Martin Mondor. diff --git a/mmsoftware/paradise_adventure/img/desert_horizon.png b/mmsoftware/paradise_adventure/img/desert_horizon.png deleted file mode 100644 index 2ec0059..0000000 Binary files a/mmsoftware/paradise_adventure/img/desert_horizon.png and /dev/null differ diff --git a/mmsoftware/paradise_adventure/img/desert_horizon.readme b/mmsoftware/paradise_adventure/img/desert_horizon.readme deleted file mode 100644 index ea782be..0000000 --- a/mmsoftware/paradise_adventure/img/desert_horizon.readme +++ /dev/null @@ -1 +0,0 @@ -Copyright (c) 2006, Martin Mondor. diff --git a/mmsoftware/paradise_adventure/img/desert_scorpion.png b/mmsoftware/paradise_adventure/img/desert_scorpion.png deleted file mode 100644 index 626328e..0000000 Binary files a/mmsoftware/paradise_adventure/img/desert_scorpion.png and /dev/null differ diff --git a/mmsoftware/paradise_adventure/img/desert_scorpion.readme b/mmsoftware/paradise_adventure/img/desert_scorpion.readme deleted file mode 100644 index ea782be..0000000 --- a/mmsoftware/paradise_adventure/img/desert_scorpion.readme +++ /dev/null @@ -1 +0,0 @@ -Copyright (c) 2006, Martin Mondor. diff --git a/mmsoftware/paradise_adventure/img/desert_vaulture.png b/mmsoftware/paradise_adventure/img/desert_vaulture.png deleted file mode 100644 index a1e81d5..0000000 Binary files a/mmsoftware/paradise_adventure/img/desert_vaulture.png and /dev/null differ diff --git a/mmsoftware/paradise_adventure/img/desert_vaulture.readme b/mmsoftware/paradise_adventure/img/desert_vaulture.readme deleted file mode 100644 index ea782be..0000000 --- a/mmsoftware/paradise_adventure/img/desert_vaulture.readme +++ /dev/null @@ -1 +0,0 @@ -Copyright (c) 2006, Martin Mondor. diff --git a/mmsoftware/paradise_adventure/img/dungeon_deadend.png b/mmsoftware/paradise_adventure/img/dungeon_deadend.png deleted file mode 100644 index 732d7fb..0000000 Binary files a/mmsoftware/paradise_adventure/img/dungeon_deadend.png and /dev/null differ diff --git a/mmsoftware/paradise_adventure/img/dungeon_deadend.readme b/mmsoftware/paradise_adventure/img/dungeon_deadend.readme deleted file mode 100644 index f7e1dd1..0000000 --- a/mmsoftware/paradise_adventure/img/dungeon_deadend.readme +++ /dev/null @@ -1 +0,0 @@ -Copyright (c) 2006 Martin Mondor. diff --git a/mmsoftware/paradise_adventure/img/forest_dense.png b/mmsoftware/paradise_adventure/img/forest_dense.png deleted file mode 100644 index f28db94..0000000 Binary files a/mmsoftware/paradise_adventure/img/forest_dense.png and /dev/null differ diff --git a/mmsoftware/paradise_adventure/img/forest_dense.readme b/mmsoftware/paradise_adventure/img/forest_dense.readme deleted file mode 100644 index ea782be..0000000 --- a/mmsoftware/paradise_adventure/img/forest_dense.readme +++ /dev/null @@ -1 +0,0 @@ -Copyright (c) 2006, Martin Mondor. diff --git a/mmsoftware/paradise_adventure/img/forest_entry.png b/mmsoftware/paradise_adventure/img/forest_entry.png deleted file mode 100644 index 8b43e67..0000000 Binary files a/mmsoftware/paradise_adventure/img/forest_entry.png and /dev/null differ diff --git a/mmsoftware/paradise_adventure/img/forest_entry.readme b/mmsoftware/paradise_adventure/img/forest_entry.readme deleted file mode 100644 index ea782be..0000000 --- a/mmsoftware/paradise_adventure/img/forest_entry.readme +++ /dev/null @@ -1 +0,0 @@ -Copyright (c) 2006, Martin Mondor. diff --git a/mmsoftware/paradise_adventure/img/forest_entry2.png b/mmsoftware/paradise_adventure/img/forest_entry2.png deleted file mode 100644 index 5914de8..0000000 Binary files a/mmsoftware/paradise_adventure/img/forest_entry2.png and /dev/null differ diff --git a/mmsoftware/paradise_adventure/img/forest_entry2.readme b/mmsoftware/paradise_adventure/img/forest_entry2.readme deleted file mode 100644 index ea782be..0000000 --- a/mmsoftware/paradise_adventure/img/forest_entry2.readme +++ /dev/null @@ -1 +0,0 @@ -Copyright (c) 2006, Martin Mondor. diff --git a/mmsoftware/paradise_adventure/img/forest_fork.png b/mmsoftware/paradise_adventure/img/forest_fork.png deleted file mode 100644 index f618ebf..0000000 Binary files a/mmsoftware/paradise_adventure/img/forest_fork.png and /dev/null differ diff --git a/mmsoftware/paradise_adventure/img/forest_fork.readme b/mmsoftware/paradise_adventure/img/forest_fork.readme deleted file mode 100644 index ea782be..0000000 --- a/mmsoftware/paradise_adventure/img/forest_fork.readme +++ /dev/null @@ -1 +0,0 @@ -Copyright (c) 2006, Martin Mondor. diff --git a/mmsoftware/paradise_adventure/img/forest_straightway.png b/mmsoftware/paradise_adventure/img/forest_straightway.png deleted file mode 100644 index 63b8460..0000000 Binary files a/mmsoftware/paradise_adventure/img/forest_straightway.png and /dev/null differ diff --git a/mmsoftware/paradise_adventure/img/forest_straightway.readme b/mmsoftware/paradise_adventure/img/forest_straightway.readme deleted file mode 100644 index ea782be..0000000 --- a/mmsoftware/paradise_adventure/img/forest_straightway.readme +++ /dev/null @@ -1 +0,0 @@ -Copyright (c) 2006, Martin Mondor. diff --git a/mmsoftware/paradise_adventure/img/forest_underoaktree.png b/mmsoftware/paradise_adventure/img/forest_underoaktree.png deleted file mode 100644 index bd5cdb1..0000000 Binary files a/mmsoftware/paradise_adventure/img/forest_underoaktree.png and /dev/null differ diff --git a/mmsoftware/paradise_adventure/img/forest_underoaktree.readme b/mmsoftware/paradise_adventure/img/forest_underoaktree.readme deleted file mode 100644 index fdb8c35..0000000 --- a/mmsoftware/paradise_adventure/img/forest_underoaktree.readme +++ /dev/null @@ -1 +0,0 @@ -Copyright (c) 2006, Matthew Mondor. diff --git a/mmsoftware/paradise_adventure/img/gardens_house.png b/mmsoftware/paradise_adventure/img/gardens_house.png deleted file mode 100644 index f3b5dce..0000000 Binary files a/mmsoftware/paradise_adventure/img/gardens_house.png and /dev/null differ diff --git a/mmsoftware/paradise_adventure/img/gardens_house.readme b/mmsoftware/paradise_adventure/img/gardens_house.readme deleted file mode 100644 index ea782be..0000000 --- a/mmsoftware/paradise_adventure/img/gardens_house.readme +++ /dev/null @@ -1 +0,0 @@ -Copyright (c) 2006, Martin Mondor. diff --git a/mmsoftware/paradise_adventure/img/gardens_smallbridge.png b/mmsoftware/paradise_adventure/img/gardens_smallbridge.png deleted file mode 100644 index 58657e8..0000000 Binary files a/mmsoftware/paradise_adventure/img/gardens_smallbridge.png and /dev/null differ diff --git a/mmsoftware/paradise_adventure/img/gardens_smallbridge.readme b/mmsoftware/paradise_adventure/img/gardens_smallbridge.readme deleted file mode 100644 index ea782be..0000000 --- a/mmsoftware/paradise_adventure/img/gardens_smallbridge.readme +++ /dev/null @@ -1 +0,0 @@ -Copyright (c) 2006, Martin Mondor. diff --git a/mmsoftware/paradise_adventure/img/guild_bar.png b/mmsoftware/paradise_adventure/img/guild_bar.png deleted file mode 100644 index 952ae21..0000000 Binary files a/mmsoftware/paradise_adventure/img/guild_bar.png and /dev/null differ diff --git a/mmsoftware/paradise_adventure/img/guild_bar.readme b/mmsoftware/paradise_adventure/img/guild_bar.readme deleted file mode 100644 index ea782be..0000000 --- a/mmsoftware/paradise_adventure/img/guild_bar.readme +++ /dev/null @@ -1 +0,0 @@ -Copyright (c) 2006, Martin Mondor. diff --git a/mmsoftware/paradise_adventure/img/house_room1.png b/mmsoftware/paradise_adventure/img/house_room1.png deleted file mode 100755 index 7ddca00..0000000 Binary files a/mmsoftware/paradise_adventure/img/house_room1.png and /dev/null differ diff --git a/mmsoftware/paradise_adventure/img/house_room1.readme b/mmsoftware/paradise_adventure/img/house_room1.readme deleted file mode 100644 index ea782be..0000000 --- a/mmsoftware/paradise_adventure/img/house_room1.readme +++ /dev/null @@ -1 +0,0 @@ -Copyright (c) 2006, Martin Mondor. diff --git a/mmsoftware/paradise_adventure/img/house_room2.png b/mmsoftware/paradise_adventure/img/house_room2.png deleted file mode 100755 index 26f5620..0000000 Binary files a/mmsoftware/paradise_adventure/img/house_room2.png and /dev/null differ diff --git a/mmsoftware/paradise_adventure/img/house_room2.readme b/mmsoftware/paradise_adventure/img/house_room2.readme deleted file mode 100644 index ea782be..0000000 --- a/mmsoftware/paradise_adventure/img/house_room2.readme +++ /dev/null @@ -1 +0,0 @@ -Copyright (c) 2006, Martin Mondor. diff --git a/mmsoftware/paradise_adventure/img/icon_ant.png b/mmsoftware/paradise_adventure/img/icon_ant.png deleted file mode 100755 index 91b294c..0000000 Binary files a/mmsoftware/paradise_adventure/img/icon_ant.png and /dev/null differ diff --git a/mmsoftware/paradise_adventure/img/icon_ant.readme b/mmsoftware/paradise_adventure/img/icon_ant.readme deleted file mode 100755 index b67c67a..0000000 --- a/mmsoftware/paradise_adventure/img/icon_ant.readme +++ /dev/null @@ -1 +0,0 @@ -Copyright (c) 2007, Martin Mondor. diff --git a/mmsoftware/paradise_adventure/img/icon_apple.png b/mmsoftware/paradise_adventure/img/icon_apple.png deleted file mode 100755 index 9bdd282..0000000 Binary files a/mmsoftware/paradise_adventure/img/icon_apple.png and /dev/null differ diff --git a/mmsoftware/paradise_adventure/img/icon_apple.readme b/mmsoftware/paradise_adventure/img/icon_apple.readme deleted file mode 100755 index b67c67a..0000000 --- a/mmsoftware/paradise_adventure/img/icon_apple.readme +++ /dev/null @@ -1 +0,0 @@ -Copyright (c) 2007, Martin Mondor. diff --git a/mmsoftware/paradise_adventure/img/icon_bag.png b/mmsoftware/paradise_adventure/img/icon_bag.png deleted file mode 100755 index ae5d004..0000000 Binary files a/mmsoftware/paradise_adventure/img/icon_bag.png and /dev/null differ diff --git a/mmsoftware/paradise_adventure/img/icon_bag.readme b/mmsoftware/paradise_adventure/img/icon_bag.readme deleted file mode 100755 index b67c67a..0000000 --- a/mmsoftware/paradise_adventure/img/icon_bag.readme +++ /dev/null @@ -1 +0,0 @@ -Copyright (c) 2007, Martin Mondor. diff --git a/mmsoftware/paradise_adventure/img/icon_banana.png b/mmsoftware/paradise_adventure/img/icon_banana.png deleted file mode 100755 index b9f957e..0000000 Binary files a/mmsoftware/paradise_adventure/img/icon_banana.png and /dev/null differ diff --git a/mmsoftware/paradise_adventure/img/icon_banana.readme b/mmsoftware/paradise_adventure/img/icon_banana.readme deleted file mode 100755 index b67c67a..0000000 --- a/mmsoftware/paradise_adventure/img/icon_banana.readme +++ /dev/null @@ -1 +0,0 @@ -Copyright (c) 2007, Martin Mondor. diff --git a/mmsoftware/paradise_adventure/img/icon_bee.png b/mmsoftware/paradise_adventure/img/icon_bee.png deleted file mode 100755 index ea0f04b..0000000 Binary files a/mmsoftware/paradise_adventure/img/icon_bee.png and /dev/null differ diff --git a/mmsoftware/paradise_adventure/img/icon_bee.readme b/mmsoftware/paradise_adventure/img/icon_bee.readme deleted file mode 100755 index b67c67a..0000000 --- a/mmsoftware/paradise_adventure/img/icon_bee.readme +++ /dev/null @@ -1 +0,0 @@ -Copyright (c) 2007, Martin Mondor. diff --git a/mmsoftware/paradise_adventure/img/icon_beenest.png b/mmsoftware/paradise_adventure/img/icon_beenest.png deleted file mode 100755 index c1f451c..0000000 Binary files a/mmsoftware/paradise_adventure/img/icon_beenest.png and /dev/null differ diff --git a/mmsoftware/paradise_adventure/img/icon_beenest.readme b/mmsoftware/paradise_adventure/img/icon_beenest.readme deleted file mode 100755 index b67c67a..0000000 --- a/mmsoftware/paradise_adventure/img/icon_beenest.readme +++ /dev/null @@ -1 +0,0 @@ -Copyright (c) 2007, Martin Mondor. diff --git a/mmsoftware/paradise_adventure/img/icon_beenest2.png b/mmsoftware/paradise_adventure/img/icon_beenest2.png deleted file mode 100755 index 0fb7525..0000000 Binary files a/mmsoftware/paradise_adventure/img/icon_beenest2.png and /dev/null differ diff --git a/mmsoftware/paradise_adventure/img/icon_beenest2.readme b/mmsoftware/paradise_adventure/img/icon_beenest2.readme deleted file mode 100755 index b67c67a..0000000 --- a/mmsoftware/paradise_adventure/img/icon_beenest2.readme +++ /dev/null @@ -1 +0,0 @@ -Copyright (c) 2007, Martin Mondor. diff --git a/mmsoftware/paradise_adventure/img/icon_breadloaf.png b/mmsoftware/paradise_adventure/img/icon_breadloaf.png deleted file mode 100755 index 494f6c0..0000000 Binary files a/mmsoftware/paradise_adventure/img/icon_breadloaf.png and /dev/null differ diff --git a/mmsoftware/paradise_adventure/img/icon_breadloaf.readme b/mmsoftware/paradise_adventure/img/icon_breadloaf.readme deleted file mode 100755 index b67c67a..0000000 --- a/mmsoftware/paradise_adventure/img/icon_breadloaf.readme +++ /dev/null @@ -1 +0,0 @@ -Copyright (c) 2007, Martin Mondor. diff --git a/mmsoftware/paradise_adventure/img/icon_chauldron.png b/mmsoftware/paradise_adventure/img/icon_chauldron.png deleted file mode 100755 index e43d328..0000000 Binary files a/mmsoftware/paradise_adventure/img/icon_chauldron.png and /dev/null differ diff --git a/mmsoftware/paradise_adventure/img/icon_chauldron.readme b/mmsoftware/paradise_adventure/img/icon_chauldron.readme deleted file mode 100755 index 9004bb0..0000000 --- a/mmsoftware/paradise_adventure/img/icon_chauldron.readme +++ /dev/null @@ -1 +0,0 @@ -Copyright (c) 2007, Matthew Mondor. diff --git a/mmsoftware/paradise_adventure/img/icon_chest.png b/mmsoftware/paradise_adventure/img/icon_chest.png deleted file mode 100755 index 44f7bd4..0000000 Binary files a/mmsoftware/paradise_adventure/img/icon_chest.png and /dev/null differ diff --git a/mmsoftware/paradise_adventure/img/icon_chest.readme b/mmsoftware/paradise_adventure/img/icon_chest.readme deleted file mode 100755 index b67c67a..0000000 --- a/mmsoftware/paradise_adventure/img/icon_chest.readme +++ /dev/null @@ -1 +0,0 @@ -Copyright (c) 2007, Martin Mondor. diff --git a/mmsoftware/paradise_adventure/img/icon_chickenleg.png b/mmsoftware/paradise_adventure/img/icon_chickenleg.png deleted file mode 100755 index 538ebb2..0000000 Binary files a/mmsoftware/paradise_adventure/img/icon_chickenleg.png and /dev/null differ diff --git a/mmsoftware/paradise_adventure/img/icon_chickenleg.readme b/mmsoftware/paradise_adventure/img/icon_chickenleg.readme deleted file mode 100755 index b67c67a..0000000 --- a/mmsoftware/paradise_adventure/img/icon_chickenleg.readme +++ /dev/null @@ -1 +0,0 @@ -Copyright (c) 2007, Martin Mondor. diff --git a/mmsoftware/paradise_adventure/img/icon_crystal.png b/mmsoftware/paradise_adventure/img/icon_crystal.png deleted file mode 100755 index f54f0d3..0000000 Binary files a/mmsoftware/paradise_adventure/img/icon_crystal.png and /dev/null differ diff --git a/mmsoftware/paradise_adventure/img/icon_crystal.readme b/mmsoftware/paradise_adventure/img/icon_crystal.readme deleted file mode 100755 index 9004bb0..0000000 --- a/mmsoftware/paradise_adventure/img/icon_crystal.readme +++ /dev/null @@ -1 +0,0 @@ -Copyright (c) 2007, Matthew Mondor. diff --git a/mmsoftware/paradise_adventure/img/icon_dagger.png b/mmsoftware/paradise_adventure/img/icon_dagger.png deleted file mode 100755 index f296aa0..0000000 Binary files a/mmsoftware/paradise_adventure/img/icon_dagger.png and /dev/null differ diff --git a/mmsoftware/paradise_adventure/img/icon_dagger.readme b/mmsoftware/paradise_adventure/img/icon_dagger.readme deleted file mode 100755 index b67c67a..0000000 --- a/mmsoftware/paradise_adventure/img/icon_dagger.readme +++ /dev/null @@ -1 +0,0 @@ -Copyright (c) 2007, Martin Mondor. diff --git a/mmsoftware/paradise_adventure/img/icon_egg.png b/mmsoftware/paradise_adventure/img/icon_egg.png deleted file mode 100755 index 0722740..0000000 Binary files a/mmsoftware/paradise_adventure/img/icon_egg.png and /dev/null differ diff --git a/mmsoftware/paradise_adventure/img/icon_egg.readme b/mmsoftware/paradise_adventure/img/icon_egg.readme deleted file mode 100755 index b67c67a..0000000 --- a/mmsoftware/paradise_adventure/img/icon_egg.readme +++ /dev/null @@ -1 +0,0 @@ -Copyright (c) 2007, Martin Mondor. diff --git a/mmsoftware/paradise_adventure/img/icon_fishingrod.png b/mmsoftware/paradise_adventure/img/icon_fishingrod.png deleted file mode 100755 index e4fc2eb..0000000 Binary files a/mmsoftware/paradise_adventure/img/icon_fishingrod.png and /dev/null differ diff --git a/mmsoftware/paradise_adventure/img/icon_fishingrod.readme b/mmsoftware/paradise_adventure/img/icon_fishingrod.readme deleted file mode 100755 index b67c67a..0000000 --- a/mmsoftware/paradise_adventure/img/icon_fishingrod.readme +++ /dev/null @@ -1 +0,0 @@ -Copyright (c) 2007, Martin Mondor. diff --git a/mmsoftware/paradise_adventure/img/icon_gigot.png b/mmsoftware/paradise_adventure/img/icon_gigot.png deleted file mode 100755 index 41c0a0a..0000000 Binary files a/mmsoftware/paradise_adventure/img/icon_gigot.png and /dev/null differ diff --git a/mmsoftware/paradise_adventure/img/icon_gigot.readme b/mmsoftware/paradise_adventure/img/icon_gigot.readme deleted file mode 100755 index b67c67a..0000000 --- a/mmsoftware/paradise_adventure/img/icon_gigot.readme +++ /dev/null @@ -1 +0,0 @@ -Copyright (c) 2007, Martin Mondor. diff --git a/mmsoftware/paradise_adventure/img/icon_glassbottle.png b/mmsoftware/paradise_adventure/img/icon_glassbottle.png deleted file mode 100755 index f03efdc..0000000 Binary files a/mmsoftware/paradise_adventure/img/icon_glassbottle.png and /dev/null differ diff --git a/mmsoftware/paradise_adventure/img/icon_glassbottle.readme b/mmsoftware/paradise_adventure/img/icon_glassbottle.readme deleted file mode 100755 index b67c67a..0000000 --- a/mmsoftware/paradise_adventure/img/icon_glassbottle.readme +++ /dev/null @@ -1 +0,0 @@ -Copyright (c) 2007, Martin Mondor. diff --git a/mmsoftware/paradise_adventure/img/icon_glasstank.png b/mmsoftware/paradise_adventure/img/icon_glasstank.png deleted file mode 100755 index b864113..0000000 Binary files a/mmsoftware/paradise_adventure/img/icon_glasstank.png and /dev/null differ diff --git a/mmsoftware/paradise_adventure/img/icon_glasstank.readme b/mmsoftware/paradise_adventure/img/icon_glasstank.readme deleted file mode 100755 index b67c67a..0000000 --- a/mmsoftware/paradise_adventure/img/icon_glasstank.readme +++ /dev/null @@ -1 +0,0 @@ -Copyright (c) 2007, Martin Mondor. diff --git a/mmsoftware/paradise_adventure/img/icon_goldcoin.png b/mmsoftware/paradise_adventure/img/icon_goldcoin.png deleted file mode 100755 index e394ec6..0000000 Binary files a/mmsoftware/paradise_adventure/img/icon_goldcoin.png and /dev/null differ diff --git a/mmsoftware/paradise_adventure/img/icon_goldcoin.readme b/mmsoftware/paradise_adventure/img/icon_goldcoin.readme deleted file mode 100755 index 9004bb0..0000000 --- a/mmsoftware/paradise_adventure/img/icon_goldcoin.readme +++ /dev/null @@ -1 +0,0 @@ -Copyright (c) 2007, Matthew Mondor. diff --git a/mmsoftware/paradise_adventure/img/icon_goldnugget.png b/mmsoftware/paradise_adventure/img/icon_goldnugget.png deleted file mode 100755 index 64f9735..0000000 Binary files a/mmsoftware/paradise_adventure/img/icon_goldnugget.png and /dev/null differ diff --git a/mmsoftware/paradise_adventure/img/icon_goldnugget.readme b/mmsoftware/paradise_adventure/img/icon_goldnugget.readme deleted file mode 100755 index b67c67a..0000000 --- a/mmsoftware/paradise_adventure/img/icon_goldnugget.readme +++ /dev/null @@ -1 +0,0 @@ -Copyright (c) 2007, Martin Mondor. diff --git a/mmsoftware/paradise_adventure/img/icon_ham.png b/mmsoftware/paradise_adventure/img/icon_ham.png deleted file mode 100755 index 9f7d028..0000000 Binary files a/mmsoftware/paradise_adventure/img/icon_ham.png and /dev/null differ diff --git a/mmsoftware/paradise_adventure/img/icon_ham.readme b/mmsoftware/paradise_adventure/img/icon_ham.readme deleted file mode 100755 index b67c67a..0000000 --- a/mmsoftware/paradise_adventure/img/icon_ham.readme +++ /dev/null @@ -1 +0,0 @@ -Copyright (c) 2007, Martin Mondor. diff --git a/mmsoftware/paradise_adventure/img/icon_haystack.png b/mmsoftware/paradise_adventure/img/icon_haystack.png deleted file mode 100755 index bf9c204..0000000 Binary files a/mmsoftware/paradise_adventure/img/icon_haystack.png and /dev/null differ diff --git a/mmsoftware/paradise_adventure/img/icon_haystack.readme b/mmsoftware/paradise_adventure/img/icon_haystack.readme deleted file mode 100755 index b67c67a..0000000 --- a/mmsoftware/paradise_adventure/img/icon_haystack.readme +++ /dev/null @@ -1 +0,0 @@ -Copyright (c) 2007, Martin Mondor. diff --git a/mmsoftware/paradise_adventure/img/icon_honeypot.png b/mmsoftware/paradise_adventure/img/icon_honeypot.png deleted file mode 100755 index 10879b4..0000000 Binary files a/mmsoftware/paradise_adventure/img/icon_honeypot.png and /dev/null differ diff --git a/mmsoftware/paradise_adventure/img/icon_honeypot.readme b/mmsoftware/paradise_adventure/img/icon_honeypot.readme deleted file mode 100755 index b67c67a..0000000 --- a/mmsoftware/paradise_adventure/img/icon_honeypot.readme +++ /dev/null @@ -1 +0,0 @@ -Copyright (c) 2007, Martin Mondor. diff --git a/mmsoftware/paradise_adventure/img/icon_ironwok.png b/mmsoftware/paradise_adventure/img/icon_ironwok.png deleted file mode 100755 index c08f683..0000000 Binary files a/mmsoftware/paradise_adventure/img/icon_ironwok.png and /dev/null differ diff --git a/mmsoftware/paradise_adventure/img/icon_ironwok.readme b/mmsoftware/paradise_adventure/img/icon_ironwok.readme deleted file mode 100755 index b67c67a..0000000 --- a/mmsoftware/paradise_adventure/img/icon_ironwok.readme +++ /dev/null @@ -1 +0,0 @@ -Copyright (c) 2007, Martin Mondor. diff --git a/mmsoftware/paradise_adventure/img/icon_key.png b/mmsoftware/paradise_adventure/img/icon_key.png deleted file mode 100755 index cac3091..0000000 Binary files a/mmsoftware/paradise_adventure/img/icon_key.png and /dev/null differ diff --git a/mmsoftware/paradise_adventure/img/icon_key.readme b/mmsoftware/paradise_adventure/img/icon_key.readme deleted file mode 100755 index 9004bb0..0000000 --- a/mmsoftware/paradise_adventure/img/icon_key.readme +++ /dev/null @@ -1 +0,0 @@ -Copyright (c) 2007, Matthew Mondor. diff --git a/mmsoftware/paradise_adventure/img/icon_key2.png b/mmsoftware/paradise_adventure/img/icon_key2.png deleted file mode 100755 index 20f76e6..0000000 Binary files a/mmsoftware/paradise_adventure/img/icon_key2.png and /dev/null differ diff --git a/mmsoftware/paradise_adventure/img/icon_key2.readme b/mmsoftware/paradise_adventure/img/icon_key2.readme deleted file mode 100755 index b67c67a..0000000 --- a/mmsoftware/paradise_adventure/img/icon_key2.readme +++ /dev/null @@ -1 +0,0 @@ -Copyright (c) 2007, Martin Mondor. diff --git a/mmsoftware/paradise_adventure/img/icon_key3.png b/mmsoftware/paradise_adventure/img/icon_key3.png deleted file mode 100755 index 0c2641a..0000000 Binary files a/mmsoftware/paradise_adventure/img/icon_key3.png and /dev/null differ diff --git a/mmsoftware/paradise_adventure/img/icon_key3.readme b/mmsoftware/paradise_adventure/img/icon_key3.readme deleted file mode 100755 index 9004bb0..0000000 --- a/mmsoftware/paradise_adventure/img/icon_key3.readme +++ /dev/null @@ -1 +0,0 @@ -Copyright (c) 2007, Matthew Mondor. diff --git a/mmsoftware/paradise_adventure/img/icon_knife.png b/mmsoftware/paradise_adventure/img/icon_knife.png deleted file mode 100755 index 8d152e3..0000000 Binary files a/mmsoftware/paradise_adventure/img/icon_knife.png and /dev/null differ diff --git a/mmsoftware/paradise_adventure/img/icon_knife.readme b/mmsoftware/paradise_adventure/img/icon_knife.readme deleted file mode 100755 index 9004bb0..0000000 --- a/mmsoftware/paradise_adventure/img/icon_knife.readme +++ /dev/null @@ -1 +0,0 @@ -Copyright (c) 2007, Matthew Mondor. diff --git a/mmsoftware/paradise_adventure/img/icon_log.png b/mmsoftware/paradise_adventure/img/icon_log.png deleted file mode 100755 index 0dcf10b..0000000 Binary files a/mmsoftware/paradise_adventure/img/icon_log.png and /dev/null differ diff --git a/mmsoftware/paradise_adventure/img/icon_log.readme b/mmsoftware/paradise_adventure/img/icon_log.readme deleted file mode 100755 index b67c67a..0000000 --- a/mmsoftware/paradise_adventure/img/icon_log.readme +++ /dev/null @@ -1 +0,0 @@ -Copyright (c) 2007, Martin Mondor. diff --git a/mmsoftware/paradise_adventure/img/icon_magnifyingglass.png b/mmsoftware/paradise_adventure/img/icon_magnifyingglass.png deleted file mode 100755 index e077343..0000000 Binary files a/mmsoftware/paradise_adventure/img/icon_magnifyingglass.png and /dev/null differ diff --git a/mmsoftware/paradise_adventure/img/icon_magnifyingglass.readme b/mmsoftware/paradise_adventure/img/icon_magnifyingglass.readme deleted file mode 100755 index b67c67a..0000000 --- a/mmsoftware/paradise_adventure/img/icon_magnifyingglass.readme +++ /dev/null @@ -1 +0,0 @@ -Copyright (c) 2007, Martin Mondor. diff --git a/mmsoftware/paradise_adventure/img/icon_matches.png b/mmsoftware/paradise_adventure/img/icon_matches.png deleted file mode 100755 index a192b16..0000000 Binary files a/mmsoftware/paradise_adventure/img/icon_matches.png and /dev/null differ diff --git a/mmsoftware/paradise_adventure/img/icon_matches.readme b/mmsoftware/paradise_adventure/img/icon_matches.readme deleted file mode 100755 index b67c67a..0000000 --- a/mmsoftware/paradise_adventure/img/icon_matches.readme +++ /dev/null @@ -1 +0,0 @@ -Copyright (c) 2007, Martin Mondor. diff --git a/mmsoftware/paradise_adventure/img/icon_mushroom.png b/mmsoftware/paradise_adventure/img/icon_mushroom.png deleted file mode 100755 index c7a9925..0000000 Binary files a/mmsoftware/paradise_adventure/img/icon_mushroom.png and /dev/null differ diff --git a/mmsoftware/paradise_adventure/img/icon_mushroom.readme b/mmsoftware/paradise_adventure/img/icon_mushroom.readme deleted file mode 100755 index 9004bb0..0000000 --- a/mmsoftware/paradise_adventure/img/icon_mushroom.readme +++ /dev/null @@ -1 +0,0 @@ -Copyright (c) 2007, Matthew Mondor. diff --git a/mmsoftware/paradise_adventure/img/icon_mushroom2.png b/mmsoftware/paradise_adventure/img/icon_mushroom2.png deleted file mode 100755 index 487f6c3..0000000 Binary files a/mmsoftware/paradise_adventure/img/icon_mushroom2.png and /dev/null differ diff --git a/mmsoftware/paradise_adventure/img/icon_mushroom2.readme b/mmsoftware/paradise_adventure/img/icon_mushroom2.readme deleted file mode 100755 index 9004bb0..0000000 --- a/mmsoftware/paradise_adventure/img/icon_mushroom2.readme +++ /dev/null @@ -1 +0,0 @@ -Copyright (c) 2007, Matthew Mondor. diff --git a/mmsoftware/paradise_adventure/img/icon_onion.png b/mmsoftware/paradise_adventure/img/icon_onion.png deleted file mode 100755 index 29079f5..0000000 Binary files a/mmsoftware/paradise_adventure/img/icon_onion.png and /dev/null differ diff --git a/mmsoftware/paradise_adventure/img/icon_onion.readme b/mmsoftware/paradise_adventure/img/icon_onion.readme deleted file mode 100755 index b67c67a..0000000 --- a/mmsoftware/paradise_adventure/img/icon_onion.readme +++ /dev/null @@ -1 +0,0 @@ -Copyright (c) 2007, Martin Mondor. diff --git a/mmsoftware/paradise_adventure/img/icon_powerdrink.png b/mmsoftware/paradise_adventure/img/icon_powerdrink.png deleted file mode 100755 index 2a20fbe..0000000 Binary files a/mmsoftware/paradise_adventure/img/icon_powerdrink.png and /dev/null differ diff --git a/mmsoftware/paradise_adventure/img/icon_powerdrink.readme b/mmsoftware/paradise_adventure/img/icon_powerdrink.readme deleted file mode 100755 index b67c67a..0000000 --- a/mmsoftware/paradise_adventure/img/icon_powerdrink.readme +++ /dev/null @@ -1 +0,0 @@ -Copyright (c) 2007, Martin Mondor. diff --git a/mmsoftware/paradise_adventure/img/icon_redruby.png b/mmsoftware/paradise_adventure/img/icon_redruby.png deleted file mode 100755 index 97531e8..0000000 Binary files a/mmsoftware/paradise_adventure/img/icon_redruby.png and /dev/null differ diff --git a/mmsoftware/paradise_adventure/img/icon_redruby.readme b/mmsoftware/paradise_adventure/img/icon_redruby.readme deleted file mode 100755 index b67c67a..0000000 --- a/mmsoftware/paradise_adventure/img/icon_redruby.readme +++ /dev/null @@ -1 +0,0 @@ -Copyright (c) 2007, Martin Mondor. diff --git a/mmsoftware/paradise_adventure/img/icon_saussage.png b/mmsoftware/paradise_adventure/img/icon_saussage.png deleted file mode 100755 index b02cd2c..0000000 Binary files a/mmsoftware/paradise_adventure/img/icon_saussage.png and /dev/null differ diff --git a/mmsoftware/paradise_adventure/img/icon_saussage.readme b/mmsoftware/paradise_adventure/img/icon_saussage.readme deleted file mode 100755 index b67c67a..0000000 --- a/mmsoftware/paradise_adventure/img/icon_saussage.readme +++ /dev/null @@ -1 +0,0 @@ -Copyright (c) 2007, Martin Mondor. diff --git a/mmsoftware/paradise_adventure/img/icon_scroll.png b/mmsoftware/paradise_adventure/img/icon_scroll.png deleted file mode 100755 index d1747aa..0000000 Binary files a/mmsoftware/paradise_adventure/img/icon_scroll.png and /dev/null differ diff --git a/mmsoftware/paradise_adventure/img/icon_scroll.readme b/mmsoftware/paradise_adventure/img/icon_scroll.readme deleted file mode 100755 index 9004bb0..0000000 --- a/mmsoftware/paradise_adventure/img/icon_scroll.readme +++ /dev/null @@ -1 +0,0 @@ -Copyright (c) 2007, Matthew Mondor. diff --git a/mmsoftware/paradise_adventure/img/icon_yellowperson.png b/mmsoftware/paradise_adventure/img/icon_yellowperson.png deleted file mode 100755 index 9c17396..0000000 Binary files a/mmsoftware/paradise_adventure/img/icon_yellowperson.png and /dev/null differ diff --git a/mmsoftware/paradise_adventure/img/icon_yellowperson.readme b/mmsoftware/paradise_adventure/img/icon_yellowperson.readme deleted file mode 100755 index b67c67a..0000000 --- a/mmsoftware/paradise_adventure/img/icon_yellowperson.readme +++ /dev/null @@ -1 +0,0 @@ -Copyright (c) 2007, Martin Mondor. diff --git a/mmsoftware/paradise_adventure/img/land_fork.png b/mmsoftware/paradise_adventure/img/land_fork.png deleted file mode 100644 index d83386a..0000000 Binary files a/mmsoftware/paradise_adventure/img/land_fork.png and /dev/null differ diff --git a/mmsoftware/paradise_adventure/img/land_fork.readme b/mmsoftware/paradise_adventure/img/land_fork.readme deleted file mode 100644 index ea782be..0000000 --- a/mmsoftware/paradise_adventure/img/land_fork.readme +++ /dev/null @@ -1 +0,0 @@ -Copyright (c) 2006, Martin Mondor. diff --git a/mmsoftware/paradise_adventure/img/land_house.png b/mmsoftware/paradise_adventure/img/land_house.png deleted file mode 100644 index 933924f..0000000 Binary files a/mmsoftware/paradise_adventure/img/land_house.png and /dev/null differ diff --git a/mmsoftware/paradise_adventure/img/land_house.readme b/mmsoftware/paradise_adventure/img/land_house.readme deleted file mode 100644 index ea782be..0000000 --- a/mmsoftware/paradise_adventure/img/land_house.readme +++ /dev/null @@ -1 +0,0 @@ -Copyright (c) 2006, Martin Mondor. diff --git a/mmsoftware/paradise_adventure/img/land_house2.png b/mmsoftware/paradise_adventure/img/land_house2.png deleted file mode 100644 index 0f46115..0000000 Binary files a/mmsoftware/paradise_adventure/img/land_house2.png and /dev/null differ diff --git a/mmsoftware/paradise_adventure/img/land_house2.readme b/mmsoftware/paradise_adventure/img/land_house2.readme deleted file mode 100644 index ea782be..0000000 --- a/mmsoftware/paradise_adventure/img/land_house2.readme +++ /dev/null @@ -1 +0,0 @@ -Copyright (c) 2006, Martin Mondor. diff --git a/mmsoftware/paradise_adventure/img/land_hut.png b/mmsoftware/paradise_adventure/img/land_hut.png deleted file mode 100644 index ce990bf..0000000 Binary files a/mmsoftware/paradise_adventure/img/land_hut.png and /dev/null differ diff --git a/mmsoftware/paradise_adventure/img/land_hut.readme b/mmsoftware/paradise_adventure/img/land_hut.readme deleted file mode 100644 index ea782be..0000000 --- a/mmsoftware/paradise_adventure/img/land_hut.readme +++ /dev/null @@ -1 +0,0 @@ -Copyright (c) 2006, Martin Mondor. diff --git a/mmsoftware/paradise_adventure/img/land_plain.png b/mmsoftware/paradise_adventure/img/land_plain.png deleted file mode 100644 index 5462066..0000000 Binary files a/mmsoftware/paradise_adventure/img/land_plain.png and /dev/null differ diff --git a/mmsoftware/paradise_adventure/img/land_plain.readme b/mmsoftware/paradise_adventure/img/land_plain.readme deleted file mode 100644 index ea782be..0000000 --- a/mmsoftware/paradise_adventure/img/land_plain.readme +++ /dev/null @@ -1 +0,0 @@ -Copyright (c) 2006, Martin Mondor. diff --git a/mmsoftware/paradise_adventure/img/land_sun.png b/mmsoftware/paradise_adventure/img/land_sun.png deleted file mode 100644 index c2bec2c..0000000 Binary files a/mmsoftware/paradise_adventure/img/land_sun.png and /dev/null differ diff --git a/mmsoftware/paradise_adventure/img/land_sun.readme b/mmsoftware/paradise_adventure/img/land_sun.readme deleted file mode 100644 index ea782be..0000000 --- a/mmsoftware/paradise_adventure/img/land_sun.readme +++ /dev/null @@ -1 +0,0 @@ -Copyright (c) 2006, Martin Mondor. diff --git a/mmsoftware/paradise_adventure/img/mountains_landscape.png b/mmsoftware/paradise_adventure/img/mountains_landscape.png deleted file mode 100644 index bcf73d5..0000000 Binary files a/mmsoftware/paradise_adventure/img/mountains_landscape.png and /dev/null differ diff --git a/mmsoftware/paradise_adventure/img/mountains_landscape.readme b/mmsoftware/paradise_adventure/img/mountains_landscape.readme deleted file mode 100644 index ea782be..0000000 --- a/mmsoftware/paradise_adventure/img/mountains_landscape.readme +++ /dev/null @@ -1 +0,0 @@ -Copyright (c) 2006, Martin Mondor. diff --git a/mmsoftware/paradise_adventure/img/pyramid_afar.png b/mmsoftware/paradise_adventure/img/pyramid_afar.png deleted file mode 100644 index 89fb712..0000000 Binary files a/mmsoftware/paradise_adventure/img/pyramid_afar.png and /dev/null differ diff --git a/mmsoftware/paradise_adventure/img/pyramid_afar.readme b/mmsoftware/paradise_adventure/img/pyramid_afar.readme deleted file mode 100644 index ea782be..0000000 --- a/mmsoftware/paradise_adventure/img/pyramid_afar.readme +++ /dev/null @@ -1 +0,0 @@ -Copyright (c) 2006, Martin Mondor. diff --git a/mmsoftware/paradise_adventure/img/pyramid_close.png b/mmsoftware/paradise_adventure/img/pyramid_close.png deleted file mode 100644 index e5a12b1..0000000 Binary files a/mmsoftware/paradise_adventure/img/pyramid_close.png and /dev/null differ diff --git a/mmsoftware/paradise_adventure/img/pyramid_close.readme b/mmsoftware/paradise_adventure/img/pyramid_close.readme deleted file mode 100644 index ea782be..0000000 --- a/mmsoftware/paradise_adventure/img/pyramid_close.readme +++ /dev/null @@ -1 +0,0 @@ -Copyright (c) 2006, Martin Mondor. diff --git a/mmsoftware/paradise_adventure/img/pyramid_deadend.png b/mmsoftware/paradise_adventure/img/pyramid_deadend.png deleted file mode 100644 index c69c780..0000000 Binary files a/mmsoftware/paradise_adventure/img/pyramid_deadend.png and /dev/null differ diff --git a/mmsoftware/paradise_adventure/img/pyramid_deadend.readme b/mmsoftware/paradise_adventure/img/pyramid_deadend.readme deleted file mode 100644 index ea782be..0000000 --- a/mmsoftware/paradise_adventure/img/pyramid_deadend.readme +++ /dev/null @@ -1 +0,0 @@ -Copyright (c) 2006, Martin Mondor. diff --git a/mmsoftware/paradise_adventure/img/pyramid_entry.png b/mmsoftware/paradise_adventure/img/pyramid_entry.png deleted file mode 100644 index 45850f7..0000000 Binary files a/mmsoftware/paradise_adventure/img/pyramid_entry.png and /dev/null differ diff --git a/mmsoftware/paradise_adventure/img/pyramid_entry.readme b/mmsoftware/paradise_adventure/img/pyramid_entry.readme deleted file mode 100644 index ea782be..0000000 --- a/mmsoftware/paradise_adventure/img/pyramid_entry.readme +++ /dev/null @@ -1 +0,0 @@ -Copyright (c) 2006, Martin Mondor. diff --git a/mmsoftware/paradise_adventure/img/pyramid_front.png b/mmsoftware/paradise_adventure/img/pyramid_front.png deleted file mode 100755 index 5541ddf..0000000 Binary files a/mmsoftware/paradise_adventure/img/pyramid_front.png and /dev/null differ diff --git a/mmsoftware/paradise_adventure/img/pyramid_front.readme b/mmsoftware/paradise_adventure/img/pyramid_front.readme deleted file mode 100644 index ea782be..0000000 --- a/mmsoftware/paradise_adventure/img/pyramid_front.readme +++ /dev/null @@ -1 +0,0 @@ -Copyright (c) 2006, Martin Mondor. diff --git a/mmsoftware/paradise_adventure/img/pyramid_frontleft.png b/mmsoftware/paradise_adventure/img/pyramid_frontleft.png deleted file mode 100644 index 8842207..0000000 Binary files a/mmsoftware/paradise_adventure/img/pyramid_frontleft.png and /dev/null differ diff --git a/mmsoftware/paradise_adventure/img/pyramid_frontleft.readme b/mmsoftware/paradise_adventure/img/pyramid_frontleft.readme deleted file mode 100644 index ea782be..0000000 --- a/mmsoftware/paradise_adventure/img/pyramid_frontleft.readme +++ /dev/null @@ -1 +0,0 @@ -Copyright (c) 2006, Martin Mondor. diff --git a/mmsoftware/paradise_adventure/img/pyramid_frontright.png b/mmsoftware/paradise_adventure/img/pyramid_frontright.png deleted file mode 100644 index a65c91d..0000000 Binary files a/mmsoftware/paradise_adventure/img/pyramid_frontright.png and /dev/null differ diff --git a/mmsoftware/paradise_adventure/img/pyramid_frontright.readme b/mmsoftware/paradise_adventure/img/pyramid_frontright.readme deleted file mode 100644 index ea782be..0000000 --- a/mmsoftware/paradise_adventure/img/pyramid_frontright.readme +++ /dev/null @@ -1 +0,0 @@ -Copyright (c) 2006, Martin Mondor. diff --git a/mmsoftware/paradise_adventure/img/pyramid_left.png b/mmsoftware/paradise_adventure/img/pyramid_left.png deleted file mode 100755 index ef6845e..0000000 Binary files a/mmsoftware/paradise_adventure/img/pyramid_left.png and /dev/null differ diff --git a/mmsoftware/paradise_adventure/img/pyramid_left.readme b/mmsoftware/paradise_adventure/img/pyramid_left.readme deleted file mode 100644 index ea782be..0000000 --- a/mmsoftware/paradise_adventure/img/pyramid_left.readme +++ /dev/null @@ -1 +0,0 @@ -Copyright (c) 2006, Martin Mondor. diff --git a/mmsoftware/paradise_adventure/img/pyramid_leftfrontright.png b/mmsoftware/paradise_adventure/img/pyramid_leftfrontright.png deleted file mode 100755 index 0192c77..0000000 Binary files a/mmsoftware/paradise_adventure/img/pyramid_leftfrontright.png and /dev/null differ diff --git a/mmsoftware/paradise_adventure/img/pyramid_leftfrontright.readme b/mmsoftware/paradise_adventure/img/pyramid_leftfrontright.readme deleted file mode 100644 index ea782be..0000000 --- a/mmsoftware/paradise_adventure/img/pyramid_leftfrontright.readme +++ /dev/null @@ -1 +0,0 @@ -Copyright (c) 2006, Martin Mondor. diff --git a/mmsoftware/paradise_adventure/img/pyramid_leftright.png b/mmsoftware/paradise_adventure/img/pyramid_leftright.png deleted file mode 100755 index 6370e38..0000000 Binary files a/mmsoftware/paradise_adventure/img/pyramid_leftright.png and /dev/null differ diff --git a/mmsoftware/paradise_adventure/img/pyramid_leftright.readme b/mmsoftware/paradise_adventure/img/pyramid_leftright.readme deleted file mode 100644 index ea782be..0000000 --- a/mmsoftware/paradise_adventure/img/pyramid_leftright.readme +++ /dev/null @@ -1 +0,0 @@ -Copyright (c) 2006, Martin Mondor. diff --git a/mmsoftware/paradise_adventure/img/pyramid_right.png b/mmsoftware/paradise_adventure/img/pyramid_right.png deleted file mode 100755 index 24d9174..0000000 Binary files a/mmsoftware/paradise_adventure/img/pyramid_right.png and /dev/null differ diff --git a/mmsoftware/paradise_adventure/img/pyramid_right.readme b/mmsoftware/paradise_adventure/img/pyramid_right.readme deleted file mode 100644 index ea782be..0000000 --- a/mmsoftware/paradise_adventure/img/pyramid_right.readme +++ /dev/null @@ -1 +0,0 @@ -Copyright (c) 2006, Martin Mondor. diff --git a/mmsoftware/paradise_adventure/img/screen_borders.png b/mmsoftware/paradise_adventure/img/screen_borders.png deleted file mode 100644 index cb70571..0000000 Binary files a/mmsoftware/paradise_adventure/img/screen_borders.png and /dev/null differ diff --git a/mmsoftware/paradise_adventure/img/screen_borders.readme b/mmsoftware/paradise_adventure/img/screen_borders.readme deleted file mode 100644 index 9004bb0..0000000 --- a/mmsoftware/paradise_adventure/img/screen_borders.readme +++ /dev/null @@ -1 +0,0 @@ -Copyright (c) 2007, Matthew Mondor. diff --git a/mmsoftware/paradise_adventure/img/screen_borders_orig.png b/mmsoftware/paradise_adventure/img/screen_borders_orig.png deleted file mode 100644 index 3da0584..0000000 Binary files a/mmsoftware/paradise_adventure/img/screen_borders_orig.png and /dev/null differ diff --git a/mmsoftware/paradise_adventure/img/screen_borders_orig.readme b/mmsoftware/paradise_adventure/img/screen_borders_orig.readme deleted file mode 100644 index 9004bb0..0000000 --- a/mmsoftware/paradise_adventure/img/screen_borders_orig.readme +++ /dev/null @@ -1 +0,0 @@ -Copyright (c) 2007, Matthew Mondor. diff --git a/mmsoftware/paradise_adventure/img/screen_directions.png b/mmsoftware/paradise_adventure/img/screen_directions.png deleted file mode 100755 index 800e774..0000000 Binary files a/mmsoftware/paradise_adventure/img/screen_directions.png and /dev/null differ diff --git a/mmsoftware/paradise_adventure/img/screen_directions.readme b/mmsoftware/paradise_adventure/img/screen_directions.readme deleted file mode 100644 index ea782be..0000000 --- a/mmsoftware/paradise_adventure/img/screen_directions.readme +++ /dev/null @@ -1 +0,0 @@ -Copyright (c) 2006, Martin Mondor. diff --git a/mmsoftware/paradise_adventure/img/screen_directions_down.png b/mmsoftware/paradise_adventure/img/screen_directions_down.png deleted file mode 100755 index 58f57ad..0000000 Binary files a/mmsoftware/paradise_adventure/img/screen_directions_down.png and /dev/null differ diff --git a/mmsoftware/paradise_adventure/img/screen_directions_down.readme b/mmsoftware/paradise_adventure/img/screen_directions_down.readme deleted file mode 100644 index ea782be..0000000 --- a/mmsoftware/paradise_adventure/img/screen_directions_down.readme +++ /dev/null @@ -1 +0,0 @@ -Copyright (c) 2006, Martin Mondor. diff --git a/mmsoftware/paradise_adventure/img/screen_directions_up.png b/mmsoftware/paradise_adventure/img/screen_directions_up.png deleted file mode 100755 index eccdd92..0000000 Binary files a/mmsoftware/paradise_adventure/img/screen_directions_up.png and /dev/null differ diff --git a/mmsoftware/paradise_adventure/img/screen_directions_up.readme b/mmsoftware/paradise_adventure/img/screen_directions_up.readme deleted file mode 100644 index ea782be..0000000 --- a/mmsoftware/paradise_adventure/img/screen_directions_up.readme +++ /dev/null @@ -1 +0,0 @@ -Copyright (c) 2006, Martin Mondor. diff --git a/mmsoftware/paradise_adventure/img/screen_directions_updown.png b/mmsoftware/paradise_adventure/img/screen_directions_updown.png deleted file mode 100644 index 0f0fb40..0000000 Binary files a/mmsoftware/paradise_adventure/img/screen_directions_updown.png and /dev/null differ diff --git a/mmsoftware/paradise_adventure/img/screen_scroll_horizontal.png b/mmsoftware/paradise_adventure/img/screen_scroll_horizontal.png deleted file mode 100755 index 87a15d7..0000000 Binary files a/mmsoftware/paradise_adventure/img/screen_scroll_horizontal.png and /dev/null differ diff --git a/mmsoftware/paradise_adventure/img/screen_scroll_horizontal.readme b/mmsoftware/paradise_adventure/img/screen_scroll_horizontal.readme deleted file mode 100644 index ea782be..0000000 --- a/mmsoftware/paradise_adventure/img/screen_scroll_horizontal.readme +++ /dev/null @@ -1 +0,0 @@ -Copyright (c) 2006, Martin Mondor. diff --git a/mmsoftware/paradise_adventure/img/screen_scroll_vertical.png b/mmsoftware/paradise_adventure/img/screen_scroll_vertical.png deleted file mode 100755 index 3a594e2..0000000 Binary files a/mmsoftware/paradise_adventure/img/screen_scroll_vertical.png and /dev/null differ diff --git a/mmsoftware/paradise_adventure/img/screen_scroll_vertical.readme b/mmsoftware/paradise_adventure/img/screen_scroll_vertical.readme deleted file mode 100644 index ea782be..0000000 --- a/mmsoftware/paradise_adventure/img/screen_scroll_vertical.readme +++ /dev/null @@ -1 +0,0 @@ -Copyright (c) 2006, Martin Mondor. diff --git a/mmsoftware/paradise_adventure/img/screen_textarea.png b/mmsoftware/paradise_adventure/img/screen_textarea.png deleted file mode 100644 index 8dec6e0..0000000 Binary files a/mmsoftware/paradise_adventure/img/screen_textarea.png and /dev/null differ diff --git a/mmsoftware/paradise_adventure/img/screen_textarea.readme b/mmsoftware/paradise_adventure/img/screen_textarea.readme deleted file mode 100644 index 9004bb0..0000000 --- a/mmsoftware/paradise_adventure/img/screen_textarea.readme +++ /dev/null @@ -1 +0,0 @@ -Copyright (c) 2007, Matthew Mondor. diff --git a/mmsoftware/paradise_adventure/img/screen_toolsarea.png b/mmsoftware/paradise_adventure/img/screen_toolsarea.png deleted file mode 100644 index a53bcda..0000000 Binary files a/mmsoftware/paradise_adventure/img/screen_toolsarea.png and /dev/null differ diff --git a/mmsoftware/paradise_adventure/img/store.png b/mmsoftware/paradise_adventure/img/store.png deleted file mode 100755 index 698599a..0000000 Binary files a/mmsoftware/paradise_adventure/img/store.png and /dev/null differ diff --git a/mmsoftware/paradise_adventure/img/store.readme b/mmsoftware/paradise_adventure/img/store.readme deleted file mode 100644 index ea782be..0000000 --- a/mmsoftware/paradise_adventure/img/store.readme +++ /dev/null @@ -1 +0,0 @@ -Copyright (c) 2006, Martin Mondor. diff --git a/mmsoftware/paradise_adventure/mus/adventure1.ogg b/mmsoftware/paradise_adventure/mus/adventure1.ogg deleted file mode 100644 index 56d5a24..0000000 Binary files a/mmsoftware/paradise_adventure/mus/adventure1.ogg and /dev/null differ diff --git a/mmsoftware/paradise_adventure/mus/adventure1.readme b/mmsoftware/paradise_adventure/mus/adventure1.readme deleted file mode 100644 index 618cb43..0000000 --- a/mmsoftware/paradise_adventure/mus/adventure1.readme +++ /dev/null @@ -1 +0,0 @@ -Copyright (c) 1989, 2007, Matthew Mondor diff --git a/mmsoftware/paradise_adventure/mus/aria_prima.ogg b/mmsoftware/paradise_adventure/mus/aria_prima.ogg deleted file mode 100644 index 6f18829..0000000 Binary files a/mmsoftware/paradise_adventure/mus/aria_prima.ogg and /dev/null differ diff --git a/mmsoftware/paradise_adventure/mus/aria_prima.readme b/mmsoftware/paradise_adventure/mus/aria_prima.readme deleted file mode 100644 index b02c0cd..0000000 --- a/mmsoftware/paradise_adventure/mus/aria_prima.readme +++ /dev/null @@ -1 +0,0 @@ -Copyright (c) 2007, Christiane Locas diff --git a/mmsoftware/paradise_adventure/mus/castle.ogg b/mmsoftware/paradise_adventure/mus/castle.ogg deleted file mode 100644 index cc02fc2..0000000 Binary files a/mmsoftware/paradise_adventure/mus/castle.ogg and /dev/null differ diff --git a/mmsoftware/paradise_adventure/mus/castle.readme b/mmsoftware/paradise_adventure/mus/castle.readme deleted file mode 100644 index 618cb43..0000000 --- a/mmsoftware/paradise_adventure/mus/castle.readme +++ /dev/null @@ -1 +0,0 @@ -Copyright (c) 1989, 2007, Matthew Mondor diff --git a/mmsoftware/paradise_adventure/mus/cavern.ogg b/mmsoftware/paradise_adventure/mus/cavern.ogg deleted file mode 100644 index ea522a7..0000000 Binary files a/mmsoftware/paradise_adventure/mus/cavern.ogg and /dev/null differ diff --git a/mmsoftware/paradise_adventure/mus/cavern.readme b/mmsoftware/paradise_adventure/mus/cavern.readme deleted file mode 100644 index 618cb43..0000000 --- a/mmsoftware/paradise_adventure/mus/cavern.readme +++ /dev/null @@ -1 +0,0 @@ -Copyright (c) 1989, 2007, Matthew Mondor diff --git a/mmsoftware/paradise_adventure/mus/cavern2.ogg b/mmsoftware/paradise_adventure/mus/cavern2.ogg deleted file mode 100644 index ef095dd..0000000 Binary files a/mmsoftware/paradise_adventure/mus/cavern2.ogg and /dev/null differ diff --git a/mmsoftware/paradise_adventure/mus/cavern2.readme b/mmsoftware/paradise_adventure/mus/cavern2.readme deleted file mode 100644 index 618cb43..0000000 --- a/mmsoftware/paradise_adventure/mus/cavern2.readme +++ /dev/null @@ -1 +0,0 @@ -Copyright (c) 1989, 2007, Matthew Mondor diff --git a/mmsoftware/paradise_adventure/mus/forest.ogg b/mmsoftware/paradise_adventure/mus/forest.ogg deleted file mode 100644 index 7caeb27..0000000 Binary files a/mmsoftware/paradise_adventure/mus/forest.ogg and /dev/null differ diff --git a/mmsoftware/paradise_adventure/mus/forest.readme b/mmsoftware/paradise_adventure/mus/forest.readme deleted file mode 100644 index edb5385..0000000 --- a/mmsoftware/paradise_adventure/mus/forest.readme +++ /dev/null @@ -1 +0,0 @@ -Copyright (c) 2007, Martin Mondor diff --git a/mmsoftware/paradise_adventure/mus/guitar.ogg b/mmsoftware/paradise_adventure/mus/guitar.ogg deleted file mode 100644 index b070d65..0000000 Binary files a/mmsoftware/paradise_adventure/mus/guitar.ogg and /dev/null differ diff --git a/mmsoftware/paradise_adventure/mus/guitar.readme b/mmsoftware/paradise_adventure/mus/guitar.readme deleted file mode 100644 index 5f50356..0000000 --- a/mmsoftware/paradise_adventure/mus/guitar.readme +++ /dev/null @@ -1 +0,0 @@ -Copyright (c) 2006, Martin Mondor and Matthew Mondor diff --git a/mmsoftware/paradise_adventure/mus/store.ogg b/mmsoftware/paradise_adventure/mus/store.ogg deleted file mode 100644 index 40f6901..0000000 Binary files a/mmsoftware/paradise_adventure/mus/store.ogg and /dev/null differ diff --git a/mmsoftware/paradise_adventure/mus/store.readme b/mmsoftware/paradise_adventure/mus/store.readme deleted file mode 100644 index f0cb77a..0000000 --- a/mmsoftware/paradise_adventure/mus/store.readme +++ /dev/null @@ -1 +0,0 @@ -Copyright (c) 2007, Matthew Mondor diff --git a/mmsoftware/paradise_adventure/src/debug.c b/mmsoftware/paradise_adventure/src/debug.c deleted file mode 100644 index 5c8cced..0000000 --- a/mmsoftware/paradise_adventure/src/debug.c +++ /dev/null @@ -1,42 +0,0 @@ -/* $Id: debug.c,v 1.1 2007/01/07 07:51:12 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/mmsoftware/paradise_adventure/src/debug.h b/mmsoftware/paradise_adventure/src/debug.h deleted file mode 100644 index e934ff5..0000000 --- a/mmsoftware/paradise_adventure/src/debug.h +++ /dev/null @@ -1,44 +0,0 @@ -/* $Id: debug.h,v 1.1 2007/01/07 07:51:12 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/mmsoftware/paradise_adventure/src/dlist.h b/mmsoftware/paradise_adventure/src/dlist.h deleted file mode 100644 index 7b9685d..0000000 --- a/mmsoftware/paradise_adventure/src/dlist.h +++ /dev/null @@ -1,174 +0,0 @@ -/* $Id: dlist.h,v 1.1 2007/01/07 07:51:12 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/mmsoftware/paradise_adventure/src/main.c b/mmsoftware/paradise_adventure/src/main.c deleted file mode 100644 index 4a1cab7..0000000 --- a/mmsoftware/paradise_adventure/src/main.c +++ /dev/null @@ -1,572 +0,0 @@ -/* $Id: main.c,v 1.19 2007/01/28 18:17:01 mmondor Exp $ */ - -/* - * Copyright (c) 2006, Matthew Mondor - * ALL RIGHTS RESERVED. - */ - - - -/* STANDARD HEADERS */ -#include -#include -#include -#include - -/* THIRD PARTY LIBRARY HEADERS */ -#include -#include -#include -#include - -/* APPLICATION HEADERS */ -#include -#include -#include - - - -/* DEFINITIONS */ - -#define FADEOUT_DELAY 2000 - -#define TEXT_WIDTH 185 -#define TEXT_LINE_SIZE 64 - -struct scene { - const char *img, *mus; - int vol; - const char *text; -}; - -typedef struct text { - TTF_Font *fnt; - int numlines, alloclines; - char **lines; -} text_t; - - - -/* PRIVATE PROTOTYPES */ - -int main(int, char **); - -static void screen_draw(void); -static void scene_switch(const char *, const char *, int, - const char *); -static void scene_next(void); - -static char *text_tok(char *, char *); -static char *text_alloc(text_t *); -static text_t *text_create(TTF_Font *, const char *); -static void text_free(text_t *); - - - -/* PUBLIC GLOBALS */ - - - -/* PRIVATE GLOBALS */ - -static int main_quit = 0; - -static SDL_Surface *img_border = NULL, *img_textarea = NULL, - *img_painting = NULL, *img_toolsarea = NULL; - -static Mix_Music *music = NULL; - -static char *painting_file = NULL, *music_file = NULL; - -static TTF_Font *font; -static SDL_Color fontcol = { 0x00, 0x00, 0x00 }; - -/* These will actually be provided via dynamic configuration, but to test */ -static const struct scene scenes[] = { - { "gardens_smallbridge", "adventure1", 80, - "You discover a fantastic landscape. A very calm water bed " - "contains various fish species. On the water floats leaves " - "near the other side. The floor on this side is covered " - "with grass and green moss.\n\nAt the right stands a tree at " - "which feet are small blue flowers. At the left can be seen " - "small mushrooms. At the far right can be seen a small " - "bridge. On the other side in front of you can be seen the " - "beginning of a dense forest." - }, - { "forest_entry", "forest", 70, - "You are at the entrance of a deep forest. A wooden smell " - "now dominates. The forest is so dense that you can hardly " - "see inside, but a dark hole." - }, - { "dungeon_deadend", "cavern", 80, - "You reached a deadly dungeon dead-end! You realize that " - "you also are locked on all sides. 'How will I ever get out " - "of here?' you wonder. 'Well, I guess that there's nothing " - "else to do here than trying to find a way out', you say." - }, - { "cavern_stairs", "cavern", 80, - "Some text here" - }, - { "cavern_gold", "cavern2", 70, - "Some more text here" - }, - { "gardens_house", "castle", 80, - "Here can be seen a quiet asian-style castle behind a wooden " - "gate. Trees of various colors enhance the landscape. " - "At the far west can be seen stone stairs leading up to what " - "appears to be ruins." - }, - { "mountains_landscape", "aria_prima", 45, - "What a marvelous mountain landscape! A spring runs between " - "you and the range of high rocky tops. These appear very " - "inviting to visit and may very well hide interesting things. " - "At the far west can be seen a cascade of the clearest water." - }, - { NULL, NULL, 0, NULL } -}; -static int curscene = 0; - - - -/* PRIVATE FUNCTIONS */ - -/* ARGSUSED */ -int -main(int argc, char **argv) -{ - - /* Initialization */ - screen_init(); - - (void) SDL_ShowCursor(1); - (void) SDL_EnableKeyRepeat(0, 0); - - painting_file = strdup(""); - music_file = strdup(""); - - /* - * 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); - - /* - * System images - */ - if ((img_border = IMG_Load("img/screen_borders.png")) == NULL) { - (void) fprintf(stderr, "main() - IMG_Load(border) - %s\n", - IMG_GetError()); - exit(EXIT_FAILURE); - } - if ((img_textarea = IMG_Load("img/screen_textarea.png")) == NULL) { - (void) fprintf(stderr, "main() - IMG_Load(textarea) - %s\n", - IMG_GetError()); - exit(EXIT_FAILURE); - } - if ((img_toolsarea = IMG_Load("img/screen_toolsarea.png")) == NULL) { - (void) fprintf(stderr, "main() - IMG_Load(toolsarea) - %s\n", - IMG_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); - } - (void) Mix_AllocateChannels(4); - (void) Mix_ReserveChannels(2); - - /* - * TTF - */ - if (TTF_Init() == -1) { - (void) fprintf(stderr, "main() - TTF_Init() - %s\n", - TTF_GetError()); - exit(EXIT_FAILURE); - } - if ((font = TTF_OpenFont("font/deloise.ttf", 20)) == NULL) { - (void) fprintf(stderr, "main() - TTF_OpenFont(deloise) - %s\n", - TTF_GetError()); - exit(EXIT_FAILURE); - } - - /* XXX */ - screen_draw(); - scene_next(); - - /* - * Main loop. - */ - while (!main_quit) { - SDL_Event ev; - - (void) SDL_WaitEvent(NULL); - while (SDL_PollEvent(&ev)) { - switch (ev.type) { - case SDL_QUIT: - main_quit = 1; - break; - case SDL_KEYDOWN: - switch (ev.key.keysym.sym) { - case SDLK_f: - if ((ev.key.keysym.mod & KMOD_LCTRL) - != 0) - screen_fulltoggle(); - break; - case SDLK_ESCAPE: - main_quit = 1; - break; - case SDLK_SPACE: - scene_next(); - break; - default: - break; - } - } - } - } - - /* Fadeout music */ - (void) Mix_FadeOutMusic(FADEOUT_DELAY); - SDL_Delay(FADEOUT_DELAY); - - exit(EXIT_SUCCESS); -} - -void -screen_draw(void) -{ - SDL_Rect r; - - (void) SDL_BlitSurface(img_border, NULL, screen_surface, NULL); - r = (SDL_Rect){ 798, 16, 0, 0 }; - (void) SDL_BlitSurface(img_textarea, NULL, screen_surface, &r); - r = (SDL_Rect) { 798, 624, 0, 0 }; - (void) SDL_BlitSurface(img_toolsarea, NULL, screen_surface, &r); - if (img_painting != NULL) { - r = (SDL_Rect){ 16, 16, 0, 0 }; - (void) SDL_BlitSurface(img_painting, NULL, screen_surface, &r); - } - /* XXX Draw any existing text, icons etc? */ - - (void) SDL_Flip(screen_surface); -} - -static void -scene_switch(const char *img, const char *mus, int vol, const char *text) -{ - SDL_Surface *s = NULL; - Mix_Music *m = NULL; - char path[1024], *str = NULL; - int music_new = 0; - text_t *txt; - - ASSERT(img != NULL && mus != NULL && text != NULL); - - /* - * Because redrawing a lot while the music is just starting appears - * to degrade the music starting point, we change painting before - * changing music if we need to switch. We however first need to - * also fade out now if we're about to change musical track. - */ - if (strcasecmp(mus, music_file) != 0) { - music_new = 1; - if (music != NULL) { - (void) Mix_FadeOutMusic(FADEOUT_DELAY); - SDL_Delay(FADEOUT_DELAY); - } - (void) Mix_HaltMusic(); - } - - /* Load new image as necessary and display it */ - if (strcasecmp(img, painting_file) != 0) { - SDL_Rect r; - - (void) snprintf(path, 1023, "img/%s.png", img); - if ((s = IMG_Load(path)) != NULL) { - if (img_painting != NULL) - SDL_FreeSurface(img_painting); - img_painting = s; - if ((str = strdup(img)) != NULL) { - if (painting_file != NULL) - free(painting_file); - painting_file = str; - } - painting_file = str; - } else - (void) fprintf(stderr, - "scene_switch() - IMG_Load(%s) - %s\n", - path, IMG_GetError()); - - r = (SDL_Rect){ 16, 16, 0, 0 }; - (void) SDL_BlitSurface(img_painting, NULL, screen_surface, &r); - } - - /* Draw text */ - if ((txt = text_create(font, text)) != NULL) { - SDL_Rect r; - SDL_Surface *s; - int i, y, h; - - r = (SDL_Rect){ 798, 16, 0, 0 }; - (void) SDL_BlitSurface(img_textarea, NULL, screen_surface, &r); - - h = TTF_FontHeight(txt->fnt); - y = 32; - for (i = 0; i < txt->numlines; i++, y += h) { - if ((s = TTF_RenderText_Blended(txt->fnt, - txt->lines[i], fontcol)) != NULL) { - r = (SDL_Rect){ 815, y, 0, 0 }; - (void) SDL_BlitSurface(s, NULL, screen_surface, - &r); - SDL_FreeSurface(s); - } - } - text_free(txt); - } else - (void) fprintf(stderr, "scene_switch() - text_create()\n"); - - (void) SDL_Flip(screen_surface); - - /* Load new music track if necessary and start it */ - if (music_new) { - (void) snprintf(path, 1023, "mus/%s.ogg", mus); - if ((m = Mix_LoadMUS(path)) != NULL) { - if (music != NULL) - Mix_FreeMusic(music); - music = m; - if ((str = strdup(mus)) != NULL) { - if (music_file != NULL) - free(music_file); - music_file = str; - } - } else - (void) fprintf(stderr, - "scene_switch() - Mix_LoadMUS(%s) - %s\n", - path, Mix_GetError()); - - (void) Mix_VolumeMusic(vol); - if (Mix_PlayMusic(music, -1) != 0) - (void) fprintf(stderr, - "scene_switch() - Mix_PlayMusic() - %s\n", - Mix_GetError()); - } -} - -static void -scene_next(void) -{ - - if (scenes[curscene].img == NULL) - curscene = 0; - - scene_switch(scenes[curscene].img, scenes[curscene].mus, - scenes[curscene].vol, scenes[curscene].text); - curscene++; -} - -/* - * strtok(3) replacement which only takes '\0' ' ' and '\n' in consideration - * and also returns the terminating character. - */ -static char * -text_tok(char *str, char *term) -{ - static char *tsptr; - - /* - * If str not NULL, set tsptr to start of new string. Resume and set - * str to tsptr if NULL to resume tokenization. - */ - if (str != NULL) - tsptr = str; - else - str = tsptr; - - /* End of string reached? */ - if (*tsptr == '\0') - return NULL; - - /* Run until we find one of our separators or the terminator */ - for (; *tsptr != '\0' && *tsptr != ' ' && *tsptr != '\n'; tsptr++) ; - - /* - * Set terminator character and terminate token string. - * Also skip separator character unless the string terminator. - */ - *term = *tsptr; - if (*tsptr != '\0') - *tsptr++ = '\0'; - - /* Return pointer to start of token string. */ - return str; -} - -static char * -text_alloc(text_t *text) -{ - char *line = NULL; - - if (text->numlines == text->alloclines) { - char **a; - - if ((a = realloc(text->lines, sizeof(char *) * - (text->alloclines * 2))) == NULL) - goto err; - text->lines = a; - text->alloclines *= 2; - } - if ((line = malloc(sizeof(char) * TEXT_LINE_SIZE)) == NULL) - goto err; - *line = '\0'; - -err: - return line; -} - -static text_t * -text_create_new(TTF_Font *font, const char *string) -{ - text_t *text = NULL; - char *str, *lptr, *olptr, *word, term; - - if ((str = strdup(string)) == NULL) - return NULL; - if ((text = malloc(sizeof(text_t))) == NULL) - goto err; - text->numlines = 0; - if ((text->lines = malloc(sizeof(char *) * 16)) == NULL) - goto err; - text->alloclines = 16; - text->fnt = font; - - if ((lptr = text_alloc(text)) == NULL) - goto err; - olptr = lptr; - - for (word = text_tok(str, &term); word != NULL; - word = text_tok(NULL, &term)) { - int l, w; - - } - -err: - free(str); - if (text != NULL) - text_free(text); - return NULL; -} - -static text_t * -text_create(TTF_Font *font, const char *string) -{ - text_t *text; - char *str, *ptr, *olptr, *lptr, term; - - /* - * Since not performance-critical, and that we could be supplied - * a read-only string, let's duplicate the supplied string. - */ - if ((str = strdup(string)) == NULL) - return NULL; - - if ((text = malloc(sizeof(text_t))) == NULL) - goto err; - text->numlines = 0; - if ((text->lines = malloc(sizeof(char *) * 16)) == NULL) - goto err; - text->alloclines = 16; - text->fnt = font; - - /* Allocate an initial line */ - if ((olptr = malloc(sizeof(char) * TEXT_LINE_SIZE)) == NULL) - goto err; - lptr = text->lines[text->numlines++] = olptr; - - /* - * Populate line with words until it exceeds the allowed width, - * in which case we delete the last word from it and create a new - * line, adding it to it. - */ - /* XXX Bug: when '\n' found, last word may exceed allowed width */ - for (ptr = text_tok(str, &term); ptr != NULL; - ptr = text_tok(NULL, &term)) { - int l, w; - - l = strlen(ptr); - -again: - /* Append word to current line */ - (void) memcpy(lptr, ptr, l); - lptr[l] = ' '; - lptr[l + 1] = '\0'; - /* Is current line still smaller than allowed width? */ - if (TTF_SizeText(font, olptr, &w, NULL) == -1) - goto err; - if (w < TEXT_WIDTH) { - lptr += l + 1; - /* If terminator was '\n' we need a new line anyway */ - if (term != '\n') - continue; - } - - /* - * Discard last word we appended to current line unless. - * terminator was '\n'. - */ - if (term != '\n') - *lptr = '\0'; - - /* Allocate a new line. */ - if (text->numlines == text->alloclines) { - /* Grow strings array buffer */ - char **a; - - if ((a = realloc(text->lines, sizeof(char *) * - (text->alloclines * 2))) == NULL) - goto err; - text->lines = a; - text->alloclines *= 2; - } - if ((olptr = malloc(sizeof(char) * TEXT_LINE_SIZE)) == NULL) - goto err; - *olptr = '\0'; - lptr = text->lines[text->numlines++] = olptr; - - /* Reiterate. */ - if (term != '\n') - goto again; - } - - free(str); - return text; - -err: - if (str != NULL) - free(str); - text_free(text); - return NULL; -} - -static void -text_free(text_t *text) -{ - int i; - - if (text != NULL) { - if (text->lines != NULL) { - for (i = 0; i < text->numlines; i++) - free(text->lines[i]); - free(text->lines); - } - free(text); - } -} diff --git a/mmsoftware/paradise_adventure/src/main.h b/mmsoftware/paradise_adventure/src/main.h deleted file mode 100644 index 06511fd..0000000 --- a/mmsoftware/paradise_adventure/src/main.h +++ /dev/null @@ -1,19 +0,0 @@ -/* $Id: main.h,v 1.1 2007/01/07 07:51:12 mmondor Exp $ */ - -/* - * Copyright (c) 2006, Matthew Mondor - * ALL RIGHTS RESERVED. - */ - - - -#ifndef MAIN_H -#define MAIN_H - - - -#include - - - -#endif diff --git a/mmsoftware/paradise_adventure/src/screen.c b/mmsoftware/paradise_adventure/src/screen.c deleted file mode 100644 index 7d35611..0000000 --- a/mmsoftware/paradise_adventure/src/screen.c +++ /dev/null @@ -1,71 +0,0 @@ -/* $Id: screen.c,v 1.4 2007/01/28 01:57:16 mmondor Exp $ */ - -/* - * Copyright (c) 2006, Matthew Mondor - * ALL RIGHTS RESERVED. - */ - -/* - * Display related initialization. - */ - - - -#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_fulltoggle(void) -{ - - (void) SDL_WM_ToggleFullScreen(screen_surface); -} - -void -screen_destroy(void) -{ - - SDL_Quit(); -} diff --git a/mmsoftware/paradise_adventure/src/screen.h b/mmsoftware/paradise_adventure/src/screen.h deleted file mode 100644 index d2c9e34..0000000 --- a/mmsoftware/paradise_adventure/src/screen.h +++ /dev/null @@ -1,35 +0,0 @@ -/* $Id: screen.h,v 1.2 2007/01/28 01:57:16 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_fulltoggle(void); -void screen_destroy(void); - - - -#endif 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