merge'.
+++ /dev/null
-$Id: BUGS,v 1.8 2003/01/10 06:45:59 mmondor Exp $
-
-
-
-- Fix anoncvs pserver
+++ /dev/null
-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.
+++ /dev/null
-$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<dateformat> +c<columnsformat> 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
+++ /dev/null
-# $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)
+++ /dev/null
-.\" $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|<address>
-.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|<vhost>|GET|<file>
-apache|vhost|<vhost>|agent|<useragent>
-apache|vhost|<vhost>|bytes
-apache|vhost|<vhost>|referer|<referer>
-apache|vhost|<vhost>|requests
-apache|vhost|<vhost>|denied
-apache|vhost|<vhost>|denied|<address>
-apache|vhost|<vhost>|errors
-apache|vhost|<vhost>|errors|<address>
-.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
-<address> 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 .
+++ /dev/null
-/* $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 <sys/types.h>
-#include <errno.h>
-#include <stdlib.h>
-#include <fcntl.h>
-#include <unistd.h>
-#include <stdio.h>
-#include <syslog.h>
-#include <signal.h>
-
-#include <mmtypes.h>
-#include <mmreadcfg.h> /* Only used for user/group related functions */
-#include <mmstring.h>
-#include <mmstat.h>
-
-
-
-#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));
- }
-}
+++ /dev/null
-#!/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
+++ /dev/null
-# $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
-
+++ /dev/null
-:<nick>!<ident>@<host> <command> ... :<text>
-":*!*@* * *" 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.
-
-
-
-:<server> <command> ... :<text>
-":* * *"
-
-:irc.gobot.ca NOTICE nanobit :*** Notice -- motd was last changed at
-
-
-
-:<server> <code> ... ":
-":* ??? *" 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 | <Isky> 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 :<server>
-"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 <password>
-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 <password>
-: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 \ 2LrdBritish\ 2.: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_ :[\ 2BX-Wall\ 2/\ 2#unices\ 2] hey
-mode LrdBritish
-:paranoia.se.eu.dal.net 221 LrdBritish +i
-mode LrdBritish +s
-:LrdBritish MODE LrdBritish :+s
-
-
-
-silence :+<nick|pattern>
-silence :-<nick|pattern>
-silence
-
-ping :<server>
-:<server> PONG <server> :<ournick>
-whois :<nick>
-...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:
-
-:<server> NOTICE AUTH :*** Got Ident %
-user me me me <nick>
-nick <nick>
-
-:<server> 001 <nick> :%
-:<nick> MODE <nick> :+i
-we can then perform nickserv identification
-
-
-.....identify...
-then we can join our channels
-
-
-:<server> 353 <nick> = <chan> :<nick> <nick> <nick> etc
-add to userlist for that channel if not already existing
-
-:<nick>!<ident>@<hostname> JOIN :<channel>
-add user to channel userlist
-
-:<nick>!<ident>@<hostname> PART :<channel>
-del user from <channel> userlist
-
-:<nick>!<ident>@<hostname> QUIT :%
-del user from all channels
-
-:ChanServ!service@dal.net KICK #linux LoLos17 :User has been banned
-:<nick>!<ident>@<host> KICK <chan> <nick> :%
-del user from <chan> userlist
-
-
-
-
-:<nick>!<ident>@<hostname> MODE <channel> <mode> [<nick>]
-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 :<pattern>
-
-:<nick> MODE <ournick> <mode>
-update our own user mode
-to set my own user modes: mode <ournick> <mode>
-to know our current mode: mode <ournick>
-
-also watch out for +oooo modes <nick nick nick nick>
-
-
-
-
-:<nick>!<ident>@<host> NICK :%
-update <nick> to <%> in all channels
-
-:<nick>!<ident>@<hostname> PRIVMSG <channel> :%
-consider % as a normal message sent to <channel>
-
-:<nick>!<ident>@<hostname> PRIVMSG <ournick> :%
-consider % as a private message sent to us
-
-:<nick>!<ident>@<hostname> NOTICE <channel> :%
-consider % as a notice sent to the channel
-
-:<nick>!<ident>@<hostname> NOTICE <ournick> :%
-consider % as a private notice sent to us
-
-:<nick>!<ident>@<hostname> PRIVMSG <ournick> :^APING %^A
-NOTICE <nick> :^APING <%>^A
-
-:<nick>!<ident>@<hostname> PRIVMSG <ournick> :^AVERSION^A
-NOTICE <nick> :^A<versionstring>^A
-
-:<nick>!<ident>@<host> INVITE <ournick> :<chan>
-consider an invite and act
-
-PING :<server>
-respond with PONG :<server>
-
-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
-
+++ /dev/null
-/* $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 <sys/types.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <fcntl.h>
-
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
-#include <arpa/nameser.h>
-#include <resolv.h>
-#include <poll.h>
-#include <errno.h>
-
-#include <syslog.h>
-#include <time.h>
-#include <ctype.h>
-
-#include <mmreadcfg.h>
-#include <mmfd.h>
-#include <mmlist.h>
-#include <mmpool.h>
-#include <mmhash.h>
-#include <mmlog.h>
-#include <mmstring.h>
-
-
-
-
-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 <nick> <ipaddress>\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;
-}
+++ /dev/null
-/* $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 <sys/types.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <fcntl.h>
-
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
-#include <arpa/nameser.h>
-#include <resolv.h>
-#include <poll.h>
-#include <errno.h>
-
-#include <syslog.h>
-#include <time.h>
-#include <ctype.h>
-
-#include <mmreadcfg.h>
-#include <mmfd.h>
-#include <mmlist.h>
-#include <mmpool.h>
-#include <mmhash.h>
-#include <mmlog.h>
-#include <mmstring.h>
-
-
-
-
-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 <nick> <ipaddress>\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;
-}
-
+++ /dev/null
-#!/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 ../../../
+++ /dev/null
-#!/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
+++ /dev/null
-- 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.
+++ /dev/null
-/* $Id: js_cgi.c,v 1.1 2006/09/08 08:04:58 mmondor Exp $ */
-
-/*
- * Copyright (c) 2006, Matthew Mondor
- * ALL RIGHTS RESERVED.
- */
-
-
-
-#include <stdint.h>
-
-#include <assert.h>
-#include <errno.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include <jsapi.h>
-
-#include <js_cgi.h>
-
-
-
-
-#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 *)
-{
-}
+++ /dev/null
-/* $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 <jsapi.h>
-
-extern JSObject *js_InitCGIClass(JSContext *, JSObject *);
-
-#endif
+++ /dev/null
-/* $Id: js_dir.c,v 1.7 2006/10/27 05:38:50 mmondor Exp $ */
-
-/*
- * Copyright (c) 2006, Matthew Mondor
- * ALL RIGHTS RESERVED.
- */
-
-
-
-#include <sys/types.h>
-
-#include <stdint.h>
-
-#include <assert.h>
-#include <dirent.h>
-#include <errno.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include <jsapi.h>
-
-#include <js_dir.h>
-
-
-
-
-#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;
-}
+++ /dev/null
-/* $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 <js_dir.h>
-
-extern JSObject *js_InitDirClass(JSContext *, JSObject *);
-
-#endif
+++ /dev/null
-/* $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 <sys/types.h>
-
-#include <assert.h>
-#include <errno.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include <jsapi.h>
-
-
-
-/* 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;
-}
+++ /dev/null
-/* $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
+++ /dev/null
-/* $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 <sys/types.h>
-#include <sys/stat.h>
-#include <sys/mman.h>
-
-#include <sys/socket.h>
-#include <arpa/inet.h>
-#include <netdb.h>
-#include <netinet/in.h>
-#include <netinet/tcp.h>
-
-#include <assert.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <grp.h>
-#include <poll.h>
-#include <pwd.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-#include <jsapi.h>
-
-#include <js_fd.h>
-
-
-
-/* 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;
-}
+++ /dev/null
-/* $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 <jsapi.h>
-
-extern JSObject *js_InitFDClass(JSContext *, JSObject *);
-
-#endif
+++ /dev/null
-/* $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 <sys/types.h>
-
-#include <stdint.h>
-
-#include <assert.h>
-#include <dirent.h>
-#include <errno.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-#include <jsapi.h>
-
-#include <js_file.h>
-
-
-
-#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;
-}
+++ /dev/null
-/* $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 <js_file.h>
-
-extern JSObject *js_InitFileClass(JSContext *, JSObject *);
-
-extern JSObject *file_new(JSContext *, FILE *, int);
-extern FILE *file_fh(JSContext *, jsval);
-
-#endif
+++ /dev/null
-/* $Id: js_fs.c,v 1.3 2006/10/27 05:41:30 mmondor Exp $ */
-
-/*
- * Copyright (c) 2006, Matthew Mondor
- * ALL RIGHTS RESERVED.
- */
-
-
-
-#include <sys/types.h>
-#include <sys/param.h>
-#include <sys/stat.h>
-
-#include <stdint.h>
-
-#include <assert.h>
-#include <dirent.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <grp.h>
-#include <pwd.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-#include <jsapi.h>
-
-#include <js_fs.h>
-
-
-
-#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;
-}
+++ /dev/null
-/* $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
+++ /dev/null
-/* $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 <sys/types.h>
-
-#include <stdint.h>
-
-#include <assert.h>
-#include <dirent.h>
-#include <errno.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include <jsapi.h>
-
-#include <js_gcroot.h>
-
-
-
-/*
- * 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);
-}
+++ /dev/null
-/* $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 <jsapi.h>
-
-#include <mmlist.h>
-#include <mmpool.h>
-#include <mmhash.h>
-
-
-
-#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
+++ /dev/null
-/* $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 <sys/types.h>
-
-#include <stdint.h>
-
-#include <assert.h>
-#include <dirent.h>
-#include <errno.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include <jsapi.h>
-
-#include <gd.h>
-#include <gdfonts.h>
-#include <gdfontl.h>
-#include <gdfontmb.h>
-#include <gdfontg.h>
-#include <gdfontt.h>
-
-#include <js_gd.h>
-#include <js_file.h>
-
-
-
-#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;
-}
+++ /dev/null
-/* $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 <js_gd.h>
-
-extern JSObject *js_InitGDClass(JSContext *, JSObject *);
-
-#endif
+++ /dev/null
-/* $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 <stdint.h>
-
-#include <assert.h>
-#include <errno.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include <jsapi.h>
-
-#include <js_global.h>
-
-
-
-#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;
-}
+++ /dev/null
-/* $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 <jsapi.h>
-
-extern JSObject *js_InitGlobalClass(JSContext *, JSObject *);
-
-#endif
+++ /dev/null
-/* $Id: js_mysql.c,v 1.1 2006/07/22 04:18:47 mmondor Exp $ */
-
-/*
- * Copyright (c) 2006, Matthew Mondor
- * ALL RIGHTS RESERVED.
- */
+++ /dev/null
-/* $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
+++ /dev/null
-/* $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 <stdint.h>
-
-#include <assert.h>
-#include <errno.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include <jsapi.h>
-
-#include <libpq-fe.h>
-#include <libpq/libpq-fs.h> /* Large Objects API */
-
-#include <js_pgsql.h>
-#include <js_file.h>
-#include <js_gcroot.h>
-
-
-
-/*
- * 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;
-}
+++ /dev/null
-/* $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 <jsapi.h>
-
-extern JSObject *js_InitPGClass(JSContext *, JSObject *);
-
-#endif
+++ /dev/null
-/* $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 <sys/types.h>
-
-#include <assert.h>
-#include <signal.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include <jsapi.h>
-
-
-
-/* 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;
-}
+++ /dev/null
-/* $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
+++ /dev/null
-/* $Id: js_syslog.c,v 1.1 2006/10/28 22:00:43 mmondor Exp $ */
-
-/*
- * Copyright (c) 2006, Matthew Mondor
- * ALL RIGHTS RESERVED.
- */
-
-
-
-#include <sys/types.h>
-
-#include <stdint.h>
-
-#include <assert.h>
-#include <dirent.h>
-#include <errno.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <syslog.h>
-
-#include <jsapi.h>
-
-#include <js_syslog.h>
-
-
-
-#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;
-}
+++ /dev/null
-/* $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 <jsapi.h>
-
-extern JSObject *js_InitSyslogClass(JSContext *, JSObject *);
-
-#endif
+++ /dev/null
-/* $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 */
-}
+++ /dev/null
-/* $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 <reason> */
-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");
- }
- }
-}
+++ /dev/null
-/* $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:
- * <sid> new sid
- * <expires> 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:
- * <data> the imported session-specific data
- * <expires> 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();
-}
+++ /dev/null
-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"
+++ /dev/null
-#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
-
-<message>
-\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
-
-<sid>
-\begin_inset Quotes erd
-\end_inset
-
-})
-\layout Itemize
-
-Response:
-\layout LyX-Code
-
-({result:<boolean>, msg:
-\begin_inset Quotes erd
-\end_inset
-
-<string>
-\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:<boolean>, msg:
-\begin_inset Quotes erd
-\end_inset
-
-<string>
-\begin_inset Quotes erd
-\end_inset
-
-[, sid:
-\begin_inset Quotes erd
-\end_inset
-
-<sid>
-\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
-
-<sid>
-\begin_inset Quotes erd
-\end_inset
-
-})
-\layout Itemize
-
-Response:
-\layout LyX-Code
-
-({result:<boolean>, msg:
-\begin_inset Quotes erd
-\end_inset
-
-<string>
-\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
+++ /dev/null
-# $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)
+++ /dev/null
-.\" $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
+++ /dev/null
-/* $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 <sys/types.h>
-#include <sys/resource.h>
-#include <fcntl.h>
-#include <errno.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h> /* strerror(3) */
-#include <syslog.h>
-#include <unistd.h>
-
-#include <jsapi.h>
-
-#include <mmtypes.h>
-#include <mmreadcfg.h>
-#include <mmstring.h>
-#include <mmserver2.h>
-
-#include <js_gcroot.h>
-#include <js_syslog.h>
-#include <js_errno.h>
-#include <js_fd.h>
-#include <js_file.h>
-#include <js_pgsql.h>
-#include <js_dir.h>
-#include <js_gd.h>
-
-
-
-/* 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 <configfile>]\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 <address>:<port> 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 <address>:<port> 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();
-}
+++ /dev/null
-.\" $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
+++ /dev/null
-/* $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('<html><head><title>' + code + ' ' + desc +
- '</title></head><body><h1>' + code + ' ' + desc + '</h1>' +
- '<p>' + ldesc + '</p><br>');
-
- if (options.debug == true)
- res.addContent(fd.httpDebug());
-
- res.addContent('<br><sub>' + SERVER_VERSION + '<br>' + SERVER_CVSID +
- '</sub></body></html>');
-
- 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('<html><head><title>301 Moved Permanently</title>' +
- '</head><body><h1>301 Moved Permanently</h1><p>The document was ' +
- 'permanently moved<a href="http://' + fd.http_host + path.virtual +
- '">here</a>.</p></body></html>');
-
- 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.<br>At least ' +
- '<b>upgrade</b> to a <a href="http://netbsd.org">' +
- 'decent</a> OS to survive on these grounds.<br>');
- return PSTAT_CLOSE_SUCCESS;
- } else if (evil_browser) {
- http_error(this, 666, 'Evil Browser Banished!',
- 'Your browser is evil born.<br>At least ' +
- '<b>upgrade</b> to a <a href="http://mozilla.org">' +
- 'decent</a> browser to survive on these grounds.<br>');
- 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('<html><head><META HTTP-EQUIV="refresh" ' +
- 'CONTENT="5; URL=' + path.virtual + '"><title>Cookies' +
- '</title></head><body><h1>Cookies</h1><p>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.</p>' +
- ' <p>If it doesn\'t, please ensure that HTTP cookies ' +
- 'are enabled on your HTTP client and are accepted ' +
- 'from this server.</p>' +
- '<p>If the page still does not load properly after a ' +
- 'few seconds, please click on <a href="' + path.virtual +
- '">this link</a>.</p><br><sub>' + SERVER_VERSION +
- '<br>' + SERVER_CVSID + '</sub></body></html>');
- 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 <sid>
- * 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 %<nn>
- */
-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);
+++ /dev/null
-/* $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" ]
-};
+++ /dev/null
-/* $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"
- ]
- }
-];
-*/
+++ /dev/null
-/* $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 :<server> */
- if (line.match(/^PING :/) != null) {
- this.putline('PONG ' + line.substr(5));
- return true;
- }
-
- /* :<nick>!<user>@<host> PRIVMSG <to> :<message> */
- 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;
-
- /* :<server> 433 * <nick> :Nick already used */
- if (line.match(/^:[^\b:]* 433 * [^\b:]* :/) != null) {
- this.putline('NICK ' + select_nickname());
- continue;
- }
-
- /* :<server> 376 <nick> :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.
- */
-
- /* :<nick>!<user>@<host> JOIN :<channel> */
- 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;
- }
-
- /* :<nick>!<user>@<host> KICK <channel> <nick> :<message> */
- 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;
- }
- }
-
- /* :<nick>!<user>@<host> PRIVMSG <to> :<message> */
- 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();
+++ /dev/null
-/*
- * 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
- */
+++ /dev/null
-/* $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("<html><head></head><body>\n" +
- '<br><a href="../index.html">../ (parent directory)' +
- "</a><br><br>\n");
- for (i in index) {
- f = index[i];
- if (f.t == 'DIR') {
- var t;
-
- fh.write('<table border="1"><tr><td width="' +
- xmax + '" height="' + ymax +
- '" valign="center" align="center">' +
- '<a href="' + f.l + '/index.html">');
- if ((t = dir_findthumb(path + '/' + f.l))
- != null)
- fh.write('<img src="' + f.l + '/' + t +
- '" alt="' + t + '">');
- fh.write('</a></td></tr><tr><td align="' +
- 'center" valign="center"><a href="' + f.l +
- '/index.html">' + f.l + '/</a></td>' +
- "</tr></table>\n");
- }
- }
- for (i in index) {
- f = index[i];
- if (f.t == 'IMG')
- fh.write('<a target="_blank" href="' + f.o +
- '"><img src="' + f.n + '" alt="' + f.n +
- '"></a>' + "\n");
- }
- fh.write('<br><sub>Thumb gallery generated by: ' +
- '$Id: thumb.js,v 1.9 2007/01/07 11:39:44 mmondor Exp $' +
- '<br><a href="http://cvs.pulsar-zone.net/cgi-bin/' +
- 'cvsweb.cgi/mmondor/mmsoftware/js/js-sh/app/thumb/' +
- 'thumb.js">(source)</a></sub>' + "\n");
- fh.write("</body></html>\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);
+++ /dev/null
-# $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)
+++ /dev/null
-/* $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 <sys/types.h>
-#include <sys/stat.h>
-#include <sys/mman.h>
-
-#include <assert.h>
-#include <fcntl.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <signal.h>
-#include <string.h>
-#include <unistd.h>
-
-#include <jsapi.h>
-
-#include <js_gcroot.h>
-#include <js_fd.h>
-#include <js_errno.h>
-#include <js_signal.h>
-#include <js_file.h>
-#include <js_pgsql.h>
-#include <js_dir.h>
-#include <js_gd.h>
-#include <js_syslog.h>
-#include <js_fs.h>
-
-
-
-/*
- * 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 <scriptfile>\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);
-}
+++ /dev/null
-/* $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 <max> 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;
-}
+++ /dev/null
-/* $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;
-}
+++ /dev/null
-/* $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. <tab> consists of tag name, and
- * <close> of a boolean specifying of the tag requires a corresponding closing
- * tag (i.e. <p>[body]</p>. 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) +
- '</' + this.tag + ">\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;
-}
+++ /dev/null
-/* $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. <tab> consists of tag name, and
- * <close> of a boolean specifying of the tag requires a corresponding closing
- * tag (i.e. <p>[body]</p>. 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 += '</' + this.tag + ">";
-
- 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;
-}
+++ /dev/null
-/* $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);
-}
+++ /dev/null
-/* $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:
- *
- * <real> System-wide absolute real fullpath, to be used by the
- * application to access the files/directories in
- * question.
- * <virtual> 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 <real> 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;
- }
-
-};
+++ /dev/null
-/* $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;
-}
+++ /dev/null
-#!/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
+++ /dev/null
-#!/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
+++ /dev/null
-# $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)
+++ /dev/null
-.\" $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 <config_file>
-.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@<somesite>:/cvsroot login
-password: <empty>
-
-$ cvs -z6 -d:pserver:anoncvs@<somesite>:/cvsroot co <module>
-.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 <repository>
-# 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
+++ /dev/null
-/* $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 <sys/types.h>
-#include <sys/mman.h>
-#include <sys/stat.h>
-#include <sys/file.h>
-#include <unistd.h>
-#include <stdlib.h>
-#include <fcntl.h>
-#include <stdio.h>
-#include <dirent.h>
-#include <string.h> /* strerror(3) */
-#include <limits.h> /* PATH_MAX */
-#include <signal.h>
-#include <errno.h>
-
-/* See mmlib/ directory for corresponding manual pages for more information */
-#include <mmtypes.h>
-#include <mmstring.h>
-#include <mmreadcfg.h>
-#include <mmlog.h>
-
-
-
-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 <configfile>]\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 <tmpdir>. 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 <tmpdir> 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 <tmpdir>, as well as <tmpdir>
- * 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. <rootpath> 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 /<rootpath>/<dirpath>/ be possible. Returns TRUE on success, or FALSE
- * on failure. Of course, we ignore EEXIST mkdir(2) errors. It assumes
- * <rootpath> 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 <dir> 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 <dirpath> 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 <dirpath>, 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 <tree> 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 <srcpath> to <dstpath>, 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 <dnode> entry, assumed to be
- * an absolute pathname starting with <origrootlen> characters, into the
- * specified <destroot>. 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 <origroot>,
- * which was the directory supplied at tree_open(), to <destroot>, is as
- * follows:
- * - <origrootlen> 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 <destroot>
- * followed by the remaining characters of dnode->name. <destroot> will
- * consist of an absolute pathname to a temporary directory. <destroot>
- * 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;
-}
+++ /dev/null
-.\" $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
+++ /dev/null
-.\" $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
+++ /dev/null
-# $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)
+++ /dev/null
-/* $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 <sys/types.h>
-#include <sys/stat.h>
-#include <sys/mman.h>
-#include <poll.h>
-#include <stdlib.h>
-#include <fcntl.h>
-#include <unistd.h>
-#include <string.h>
-#include <ctype.h>
-#include <time.h>
-
-
-
-#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;
- }
-}
+++ /dev/null
-$Id: ChangeLog,v 1.1 2002/12/11 10:16:56 mmondor Exp $
-
-
-
+++ /dev/null
-$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
+++ /dev/null
-# 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 *~
+++ /dev/null
-#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
+++ /dev/null
-# $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)
+++ /dev/null
-# $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
-
+++ /dev/null
-#!/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
+++ /dev/null
-#!/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
+++ /dev/null
-.\" $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
+++ /dev/null
-/* $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 <sys/types.h>
-#include <unistd.h>
-#include <stdlib.h>
-#include <fcntl.h>
-#include <time.h>
-#include <stdio.h>
-#ifdef __GLIBC__
-#include <crypt.h>
-#endif
-
-#include <mmtypes.h>
-#include <mmstring.h>
-
-
-
-
-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 '<password>' ['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);
-}
+++ /dev/null
-# $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)
+++ /dev/null
-# $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
-
+++ /dev/null
-/* $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 <smtphost> <port> <fromaddr> <authcompname|none>
- * -- <toaddresses...>
- *
- * 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 <sys/types.h>
-#include <netdb.h>
-#include <netinet/in.h>
-#include <sys/socket.h>
-#include <syslog.h>
-#include <errno.h>
-#include <signal.h>
-#include <stdlib.h>
-#include <stdio.h>
-
-#include <mmtypes.h>
-#include <mmfd.h>
-#include <mmstring.h>
-
-
-
-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 <hostname> <port> <fromaddr> \
-<helohost|none> -- <rcptaddr> [<rcptaddr> ...]\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);
-}
+++ /dev/null
-# $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)
+++ /dev/null
-.\" $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
+++ /dev/null
-/* $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 <sys/types.h>
-#include <sys/file.h>
-#include <stdlib.h>
-#include <fcntl.h>
-#include <unistd.h>
-#include <sys/poll.h>
-#include <sys/wait.h>
-#include <sys/time.h>
-#include <sys/resource.h>
-#include <sys/stat.h>
-#include <signal.h>
-#include <time.h>
-#include <syslog.h>
-#include <stdio.h>
-#include <errno.h>
-#include <string.h> /* strerror(3) */
-
-#include <netdb.h>
-#include <netinet/in.h>
-#include <netinet/in_systm.h>
-#include <netinet/ip.h>
-#include <netinet/tcp.h>
-#include <sys/socket.h>
-#include <arpa/inet.h>
-#include <arpa/nameser.h>
-#include <resolv.h>
-
-/* For more information about these, see the manual pages in mmlib/ */
-#include <mmtypes.h>
-#include <mmstring.h>
-#include <mmreadcfg.h>
-#include <mmlist.h>
-#include <mmpool.h>
-#include <mmhash.h>
-#include <mmlog.h>
-#include <mmheap.h>
-#include <mmlimitrate.h>
-#include <mmalarm.h>
-
-
-
-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;
-}
+++ /dev/null
-.\" $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
+++ /dev/null
-# $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
+++ /dev/null
-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.
+++ /dev/null
-/* $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 <sys/types.h>
-#include <stdlib.h>
-#include <fcntl.h>
-#include <unistd.h>
-#include <sys/poll.h>
-#include <sys/wait.h>
-#include <sys/time.h>
-#include <sys/resource.h>
-#include <signal.h>
-#include <time.h>
-#include <syslog.h>
-#include <stdio.h>
-#include <errno.h>
-#include <string.h> /* strerror(3) */
-
-#include <netdb.h>
-#include <netinet/in.h>
-#include <netinet/in_systm.h>
-#include <netinet/ip.h>
-#include <netinet/tcp.h>
-#include <sys/socket.h>
-#include <arpa/inet.h>
-#include <arpa/nameser.h>
-#include <resolv.h>
-
-/* For more information about these, see the manual pages in mmlib/ */
-#include <mmtypes.h>
-#include <mmstring.h>
-#include <mmreadcfg.h>
-#include <mmlist.h>
-#include <mmpool.h>
-#include <mmhash.h>
-#include <mmlog.h>
-#include <mmheap.h>
-#include <mmlimitrate.h>
-
-
-
-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 <pid> and <sock> 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;
-}
+++ /dev/null
-# $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)
+++ /dev/null
-.\" $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
+++ /dev/null
-/* $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 <sys/types.h>
-#include <sys/resource.h>
-#include <fcntl.h>
-#include <errno.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h> /* strerror(3) */
-#include <syslog.h>
-#include <unistd.h>
-
-#include <mmtypes.h>
-#include <mmreadcfg.h>
-#include <mmstring.h>
-#include <mmserver2.h>
-
-
-
-/* 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 <configfile>]\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 <address>:<port> 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 <address>:<port> 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 */
-}
+++ /dev/null
-.\" $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
+++ /dev/null
-$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.
+++ /dev/null
-# $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)
+++ /dev/null
-$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
+++ /dev/null
-#!/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 ../
+++ /dev/null
-# $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
+++ /dev/null
-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.
+++ /dev/null
-#!/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
+++ /dev/null
-#!/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
+++ /dev/null
-# $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
+++ /dev/null
-#!/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
+++ /dev/null
-.\" $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 <mmstat.h>
-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
+++ /dev/null
-/* $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 <sys/types.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <time.h>
-
-#include <mmtypes.h>
-#include <mmstat.h>
-#include <mmstring.h>
-#include <mmlist.h>
-#include <mmpool.h>
-
-
-
-
-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 [<key|pattern>]\n");
- fprintf(stderr,
- "mmstat reset <p|v[a]> <key|pattern>%%<value> \
-[<key|pattern>%%<value> ...]\n");
- fprintf(stderr,
- "mmstat update <p|v[a]> <key|pattern>%%<value> \
-[<key|pattern>%%<value> ...]\n");
- fprintf(stderr, "mmstat delete <key|pattern> [<key|pattern> ...]\n");
- fprintf(stderr, "mmstat rotate <pattern> <prefix>\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));
-}
+++ /dev/null
-.\" $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
+++ /dev/null
-/* $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 <sys/types.h>
-#include <sys/stat.h>
-#include <sys/wait.h>
-#include <sys/socket.h>
-#include <sys/un.h>
-#include <sys/uio.h>
-#include <sys/file.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
-#include <arpa/nameser.h>
-#include <resolv.h>
-#include <poll.h>
-#include <syslog.h>
-#include <signal.h>
-#include <stddef.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <time.h>
-#include <dirent.h>
-#include <pwd.h>
-#include <grp.h>
-#include <errno.h>
-
-#include <mmtypes.h>
-#include <mmpool.h>
-#include <mmhash.h>
-#include <mmstring.h>
-#include <mmstat.h>
-#include <mmstatd.h>
-#include <mmreadcfg.h>
-#include <mmlog.h>
-#include <mmlimitrate.h>
-
-
-
-
-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 */
-}
+++ /dev/null
-.\" $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 <mmstat.h>
-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
+++ /dev/null
-/* $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 <sys/types.h>
-
-#include <mmtypes.h>
-#include <mmpool.h>
-#include <mmhash.h>
-#include <mmstat.h>
-
-
-
-
-#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
+++ /dev/null
-# $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)
+++ /dev/null
-# $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
-
+++ /dev/null
-$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.
+++ /dev/null
-/* $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 <sys/types.h>
-#include <sys/socket.h>
-#include <sys/un.h>
-#include <stdio.h>
-#include <stdarg.h>
-#include <unistd.h>
-#include <stdlib.h>
-
-#include <mmtypes.h>
-#include <mmstring.h>
-
-
-
-
-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 <commandkey>\n\n");
-
- exit(0);
-}
+++ /dev/null
-/* $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 <sys/types.h>
-#include <sys/stat.h>
-#include <sys/wait.h>
-#include <sys/socket.h>
-#include <sys/un.h>
-#include <sys/uio.h>
-#include <poll.h>
-#include <syslog.h>
-#include <signal.h>
-#include <stddef.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <time.h>
-#include <pwd.h>
-#include <grp.h>
-
-#include <mmtypes.h>
-#include <mmlist.h>
-#include <mmpool.h>
-#include <mmstring.h>
-
-
-
-
-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");
- }
- }
- }
- }
-}
+++ /dev/null
-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"
+++ /dev/null
-/* $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.
- */
+++ /dev/null
-$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
+++ /dev/null
-# $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
+++ /dev/null
-/* $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
+++ /dev/null
-/* $Id: debug.c,v 1.1 2006/12/31 08:32:39 mmondor Exp $ */
-
-/*
- * Copyright (c) 2006, Matthew Mondor
- * ALL RIGHTS RESERVED.
- */
-
-
-
-#include <stdarg.h>
-#include <stdio.h>
-#include <stdlib.h>
-
-
-
-#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
+++ /dev/null
-/* $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
+++ /dev/null
-/* $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 <stdio.h>
-#include <stdint.h>
-
-#include <decode.h>
-
-
-
-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;
- }
-}
+++ /dev/null
-/* $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
+++ /dev/null
-/* $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
+++ /dev/null
-/* $Id: enc.c,v 1.1 2006/12/31 08:32:39 mmondor Exp $ */
-
-/*
- * Copyright (c) 2006, Matthew Mondor
- * ALL RIGHTS RESERVED.
- */
-
-
-
-#include <conf.h>
-
-
-
-#ifdef USE_ENCRYPTION
-
-
-
-#include <enc.h>
-
-
-
-mmenc_t enc_in, enc_out;
-int mmencrypt = 0;
-
-
-
-#endif
+++ /dev/null
-/* $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 <mmenc.h>
-
-
-
-extern mmenc_t enc_in, enc_out;
-extern int mmencrypt;
-
-
-
-#endif
+++ /dev/null
-/* $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 <stdint.h>
-#include <stdio.h>
-#include <stdlib.h>
-
-
-
-#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 <infile> <outfile> <seed>\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;
- }
-}
+++ /dev/null
-/* $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 <stdint.h>
-
-#include <SDL.h>
-#include <SDL_endian.h>
-
-
-
-#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
+++ /dev/null
-/* $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 <math.h>
-#include <stdint.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-/* XXX UNIX DEBUG */
-#include <signal.h>
-
-/* THIRD PARTY LIBRARY HEADERS */
-#include <SDL.h>
-#include <SDL_image.h>
-#include <SDL_thread.h>
-#include <SDL_rotozoom.h>
-#include <SDL_gfxPrimitives.h>
-#include <SDL_framerate.h>
-#include <SDL_mixer.h>
-#include <SDL_net.h>
-
-/* APPLICATION HEADERS */
-#include <main.h>
-#include <debug.h>
-#include <screen.h>
-#include <packets_common.h>
-#include <packets.h>
-#include <thread_msg.h>
-#include <thread_net_recv.h>
-#include <thread_net_send.h>
-#include <rawobjs.h>
-#include <decode.h>
-
-
-
-/* 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);
- }
-}
+++ /dev/null
-/* $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 <SDL.h>
-
-#include <thread_msg.h>
-
-
-
-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
+++ /dev/null
-/* $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 <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include <packets_common.h>
-#include <packets.h>
-#include <endian.h>
-#include <thread_net_send.h>
-#include <thread_msg.h>
-#include <debug.h>
-#include <hmac.h>
-#include <enc.h>
-#include <conf.h>
-
-
-
-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));
-}
+++ /dev/null
-/* $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 <packets_common.h>
-#include <thread_msg.h>
-#include <main.h>
-
-
-
-/*
- * For every server packet type we support, an index array will be used
- * with this structure to call the associated handler function, which will
- * perform sanity checking and process the packet, returning 0 on success or
- * -1 on error. The size field will allow to perform sanity checking on
- * data size prior to calling the handler function, as well as to increase the
- * buffer pointer.
- */
-struct packet_index {
- size_t size;
- int (*handler)(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
+++ /dev/null
-/* $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 <stdint.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include <dlist.h>
-#include <pool.h>
-#include <debug.h>
-
-
-
-/* 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);
- }
-
- /*
- * <page> 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;
-}
+++ /dev/null
-/* $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 <stdint.h>
-
-#include <dlist.h>
-
-
-
-/* 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
+++ /dev/null
-/* $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
+++ /dev/null
-/* $Id: recvq.c,v 1.1 2006/12/31 08:32:39 mmondor Exp $ */
-
-/*
- * Copyright (c) 2006, Matthew Mondor
- * ALL RIGHTS RESERVED.
- */
-
-
-
-#include <errno.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include <SDL_net.h>
-
-#include <conf.h>
-
-#ifdef USE_COMPRESSION
-#include <zlib.h>
-#endif
-
-#include <recvq.h>
-#include <packets.h>
-#ifdef USE_ENCRYPTION
-#include <enc.h>
-#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;
-}
+++ /dev/null
-/* $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 <stdint.h>
-
-#include <SDL_net.h>
-#include <zlib.h>
-
-#include <mmenc.h>
-
-
-
-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
+++ /dev/null
-/* $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 <stdio.h>
-#include <stdlib.h>
-
-#include <SDL.h>
-
-#include <conf.h>
-#include <screen.h>
-
-
-
-#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();
-}
+++ /dev/null
-/* $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 <SDL.h>
-
-
-
-extern SDL_Surface *screen_surface;
-extern int screen_width, screen_height;
-extern SDL_Joystick *gamepad;
-
-
-
-void screen_init(void);
-void screen_destroy(void);
-
-
-
-#endif
+++ /dev/null
-/* $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 <stdlib.h>
-
-#include <debug.h>
-#include <thread_msg.h>
-#include <pool.h>
-
-
-
-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());
-}
+++ /dev/null
-/* $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 <stdint.h>
-
-#include <SDL.h>
-#include <SDL_thread.h>
-
-#include <dlist.h>
-#include <pool.h>
-
-
-
-#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
+++ /dev/null
-/* $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 <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include <main.h>
-#include <thread_net_recv.h>
-#include <recvq.h>
-#include <conf.h>
-
-
-
-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);
-}
+++ /dev/null
-/* $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 <SDL.h>
-#include <SDL_net.h>
-
-#include <thread_msg.h>
-
-
-
-/*
- * 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.
- * <status> will be 0 on success or -1 on failure, in which case the error
- * message string will be found into <error>.
- */
-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 <data> size, with a NULL <data> 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
+++ /dev/null
-/* $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 <stdio.h>
-#include <stdlib.h>
-
-#include <conf.h>
-
-#ifdef USE_COMPRESSION
-#include <zlib.h>
-#endif
-
-#include <thread_net_send.h>
-#include <thread_net_recv.h>
-#ifdef USE_ENCRYPTION
-#include <enc.h>
-#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;
-}
+++ /dev/null
-/* $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 <SDL.h>
-#include <SDL_net.h>
-
-#include <thread_msg.h>
-
-
-
-int thread_net_send(void *);
-
-
-
-extern thread_port_t send_port;
-
-
-
-#endif
+++ /dev/null
-/* $Id: encrypt_mmenc.c,v 1.1 2006/12/31 08:32:40 mmondor Exp $ */
-
-/*
- * Copyright (c) 2006, Matthew Mondor
- * ALL RIGHTS RESERVED.
- */
-
-
-#include <stdint.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include <mmenc.h>
-
-
-
-#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);
-}
+++ /dev/null
-/* $Id: encrypt_rc4.c,v 1.1 2006/12/31 08:32:40 mmondor Exp $ */
-
-/*
- * Copyright (c) 2006, Matthew Mondor
- * ALL RIGHTS RESERVED.
- */
-
-
-#include <stdint.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include <rc4.h>
-
-
-
-#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);
-}
+++ /dev/null
-/* $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 <stdint.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include <hmac.h>
-
-
-
-/* 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);
-}
+++ /dev/null
-/* $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
+++ /dev/null
-/* $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 <rmd160.h>
-
-
-
-#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 <hmac.c>
-
-
-
-#endif
+++ /dev/null
-/* $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 <sha1.h>
-
-
-
-#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 <hmac.c>
-
-
-
-#endif
+++ /dev/null
-/* $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 <stdint.h>
-#include <string.h>
-
-#include <mmenc.h>
-
-
-
-/*
- * 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;
-}
+++ /dev/null
-/* $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 <sys/types.h>
-#include <stdint.h>
-
-
-
-#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
+++ /dev/null
-/* $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 <stdint.h>
-
-
-
-/*
- * 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
+++ /dev/null
-/* $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 <stdint.h>
-#include <string.h>
-
-#include <rc4.h>
-
-
-
-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;
-}
+++ /dev/null
-/* $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 <stdint.h>
-
-
-
-#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
+++ /dev/null
-/* $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 <stdint.h>
-#include <string.h>
-
-#ifdef __NetBSD__
-#include <machine/endian.h>
-#endif
-
-#include <rmd160.h>
-
-
-
-#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);
- }
-}
+++ /dev/null
-/* $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 <stdint.h>
-
-
-
-#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_ */
+++ /dev/null
-/* $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 <steve@edmweb.com>
- * 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 <stdint.h>
-#include <string.h>
-
-#ifdef __NetBSD__
-#include <machine/endian.h>
-#endif
-
-#include <sha1.h>
-
-
-
-#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);
-}
+++ /dev/null
-/* $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 <steve@edmweb.com>
- * 100% Public Domain
- */
-
-#ifndef _SHA1_H_
-#define _SHA1_H_
-
-
-
-#include <stdint.h>
-
-
-
-#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
+++ /dev/null
-# $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)
+++ /dev/null
-$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.
+++ /dev/null
-/* $Id: client.c,v 1.1 2006/12/31 08:32:40 mmondor Exp $ */
-
-/*
- * Copyright (c) 2006, Matthew Mondor
- * ALL RIGHTS RESERVED.
- */
-
-
-
-#include <errno.h>
-#include <stdlib.h>
-#include <string.h>
-#include <syslog.h>
-#include <unistd.h>
-
-#include <client.h>
-#include <kqueue.h>
-#include <conf.h>
-
-
-
-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);
-}
+++ /dev/null
-/* $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 <sys/types.h>
-#include <sys/socket.h>
-#include <time.h>
-
-#include <mmpool.h>
-#include <mmlist.h>
-
-#include <sendq.h>
-#include <recvq.h>
-#include <ships.h>
-#include <conf.h>
-
-
-
-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
+++ /dev/null
-/* $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
+++ /dev/null
-/* $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 <fcntl.h>
-#include <errno.h>
-#include <signal.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include <syslog.h>
-#include <unistd.h>
-
-#include <mmstring.h>
-
-#include <conf.h>
-
-
-
-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;
-}
+++ /dev/null
-/* $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
+++ /dev/null
-/* $Id: enc.c,v 1.1 2006/12/31 08:32:40 mmondor Exp $ */
-
-/*
- * Copyright (c) 2006, Matthew Mondor
- * ALL RIGHTS RESERVED.
- */
-
-#include <errno.h>
-#include <fcntl.h>
-#include <unistd.h>
-#include <syslog.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include <mmarch.h>
-
-#include <enc.h>
-#include <mmenc.h>
-#include <kqueue.h> /* current_time */
-#include <conf.h>
-
-
-
-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));
-}
+++ /dev/null
-/* $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 <stdint.h>
-
-
-
-void noncerand_init(void);
-void noncerand_gen(uint32_t tnr[8], uint8_t nr[32]);
-
-
-
-#endif
+++ /dev/null
-/* $Id: kqueue.c,v 1.1 2006/12/31 08:32:40 mmondor Exp $ */
-
-/*
- * Copyright (c) 2006, Matthew Mondor
- * ALL RIGHTS RESERVED.
- */
-
-
-
-#include <errno.h>
-#include <signal.h>
-#include <stdlib.h>
-#include <syslog.h>
-#include <string.h>
-#include <unistd.h>
-
-#include <mmarch.h>
-
-#include <kqueue.h>
-#include <client.h>
-#include <packets_common.h>
-#include <packets.h>
-#include <net.h>
-#include <conf.h>
-
-
-
-static struct kevent rev[R_EVENTS], sev[S_EVENTS];
-static int kqid, rev_cnt, sev_cnt;
-
-
-
-/*
- * 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++;
-}
+++ /dev/null
-/* $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 <sys/event.h>
-#include <sys/time.h>
-
-
-
-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
+++ /dev/null
-/* $Id: main.c,v 1.1 2006/12/31 08:32:40 mmondor Exp $ */
-
-/*
- * Copyright (c) 2006, Matthew Mondor
- * ALL RIGHTS RESERVED.
- */
-
-
-
-#include <sys/types.h>
-#include <sys/event.h>
-#include <sys/socket.h>
-#include <stdlib.h>
-#include <errno.h>
-#include <string.h>
-#include <signal.h>
-#include <syslog.h>
-#include <unistd.h>
-
-#include <mmpool.h>
-#include <mmlist.h>
-
-#include <daemon.h>
-#include <net.h>
-#include <sendq.h>
-#include <trigonometry.h>
-#include <packets_common.h>
-#include <packets.h>
-#include <torp.h>
-#include <client.h>
-#include <enc.h>
-#include <conf.h>
-#include <kqueue.h>
-
-
-
-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;
-}
+++ /dev/null
-/* $Id: net.c,v 1.1 2006/12/31 08:32:40 mmondor Exp $ */
-
-/*
- * Copyright (c) 2006, Matthew Mondor
- * ALL RIGHTS RESERVED.
- */
-
-
-
-#include <sys/types.h>
-#include <syslog.h>
-#include <errno.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include <fcntl.h>
-
-#include <netdb.h>
-#include <sys/socket.h>
-#include <arpa/inet.h>
-#include <netinet/in.h>
-#include <netinet/tcp.h>
-
-#include <mmstring.h>
-
-#include <net.h>
-#include <conf.h>
-
-
-
-static int net_listen(const char *, int, int);
-
-
-
-int listen_fd;
-
-
-
-void
-net_init(void)
-{
-
- if ((listen_fd = net_listen("0.0.0.0", 7777, MAX_CLIENTS)) == -1) {
- syslog(LOG_NOTICE, "net_listen() - %s", strerror(errno));
- exit(EXIT_FAILURE);
- }
-}
-
-static int
-net_listen(const char *addr, int port, int backlog)
-{
- int fd, opt;
- struct linger linger;
- struct protoent *pent;
- struct sockaddr_in server;
-
- fd = -1;
-
- mm_memclr(&server, sizeof(struct sockaddr_in));
- server.sin_family = AF_INET;
- if (inet_pton(AF_INET, addr, &server.sin_addr) != 1) {
- syslog(LOG_NOTICE, "inet_pton(%s) - %s", addr,
- strerror(errno));
- goto err;
- }
- server.sin_port = htons((short)port);
-
- if ((fd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
- syslog(LOG_NOTICE, "socket() - %s", strerror(errno));
- goto err;
- }
-
- opt = 1;
- if ((setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(int)))
- == -1)
- syslog(LOG_NOTICE, "setsockopt(SO_REUSEADDR) - %s",
- strerror(errno));
- if ((setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &opt, sizeof(int)))
- == -1)
- syslog(LOG_NOTICE, "setsockopt(SO_KEEPALIVE) - %s",
- strerror(errno));
-
- linger.l_onoff = 0;
- linger.l_linger = 0;
- if ((setsockopt(fd, SOL_SOCKET, SO_LINGER, &linger,
- sizeof(struct linger))) == -1)
- syslog(LOG_NOTICE, "setsockopt(SO_LINGER) - %s",
- strerror(errno));
-
- /* XXX Set buffer sizes? */
-
- if ((pent = getprotobyname("TCP")) != NULL) {
- opt = 1;
- if ((setsockopt(fd, pent->p_proto, TCP_NODELAY, &opt,
- sizeof(int))) == -1)
- syslog(LOG_NOTICE, "setsockopt(TCP_NODELAY) - %s",
- strerror(errno));
- } else
- syslog(LOG_NOTICE, "getprotobyname(TCP) - %s",
- strerror(errno));
-
- if ((opt = fcntl(fd, F_GETFL, NULL)) != -1) {
- if (fcntl(fd, F_SETFL, opt | O_NONBLOCK) == -1) {
- syslog(LOG_NOTICE, "fcntl(F_SETFL) - %s",
- strerror(errno));
- goto err;
- }
- } else
- syslog(LOG_NOTICE, "fcntl(F_GETFL) - %s", strerror(errno));
-
- if ((bind(fd, (struct sockaddr *)&server, sizeof(struct sockaddr_in)))
- != 0) {
- syslog(LOG_NOTICE, "bind(%s:%d) - %s", addr, port,
- strerror(errno));
- goto err;
- }
-
- if (listen(fd, backlog) == -1) {
- syslog(LOG_NOTICE, "listen(%s:%d) - %s", addr, port,
- strerror(errno));
- goto err;
- }
-
- return fd;
-
-err:
- if (fd != -1)
- (void) close(fd);
-
- return -1;
-}
+++ /dev/null
-/* $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
+++ /dev/null
-/* $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 <math.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <syslog.h>
-#include <string.h>
-
-#include <mmstring.h>
-#include <mmarch.h>
-#include <mmlog.h>
-
-#include <packets_common.h>
-#include <packets.h>
-#include <trigonometry.h>
-#include <client.h>
-#include <enc.h>
-#include <ships.h>
-#include <torp.h>
-#include <sendq.h>
-#include <hmac.h>
-#include <conf.h>
-
-
-
-static int cpacket_auth_handler(client_t *, uint16_t *);
-static int cpacket_ping_handler(client_t *, uint16_t *);
-static int cpacket_pong_handler(client_t *, uint16_t *);
-static int cpacket_direction_handler(client_t *, uint16_t *);
-static int cpacket_thrust_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;
-}
+++ /dev/null
-/* $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 <client.h>
-#include <packets_common.h>
-#include <ships.h>
-#include <torp.h>
-
-
-
-/*
- * For every server packet type we support, an index array will be used
- * with this structure to call the associated handler function, which will
- * perform sanity checking and process the packet, returning 0 on success or
- * -1 on error. The size field will allow to perform sanity checking on
- * data size prior to calling the handler function, as well as to increase the
- * buffer pointer.
- */
-struct packet_index {
- size_t size;
- int (*handler)(client_t *, uint16_t *);
-};
-
-
-
-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
+++ /dev/null
-/* $Id: recvq.c,v 1.1 2006/12/31 08:32:40 mmondor Exp $ */
-
-/*
- * Copyright (c) 2006, Matthew Mondor
- * ALL RIGHTS RESERVED.
- */
-
-
-
-#include <errno.h>
-#include <stdlib.h>
-#include <syslog.h>
-#include <unistd.h>
-#include <string.h>
-#include <syslog.h>
-
-#include <mmstring.h>
-
-#include <recvq.h>
-#include <conf.h>
-
-#ifdef USE_ENCRYPTION
-#include <mmenc.h>
-#endif
-
-#include <packets.h>
-#include <client.h>
-
-#ifdef USE_COMPRESSION
-#include <zlib.h>
-#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;
-}
+++ /dev/null
-/* $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 <sys/types.h>
-
-#include <conf.h>
-
-#ifdef USE_COMPRESSION
-#include <zlib.h>
-#endif
-
-#ifdef USE_ENCRYPTION
-#include <mmenc.h>
-#include <enc.h>
-#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
+++ /dev/null
-/* $Id: sendq.c,v 1.1 2006/12/31 08:32:40 mmondor Exp $ */
-
-/*
- * Copyright (c) 2006, Matthew Mondor
- * ALL RIGHTS RESERVED.
- */
-
-
-
-#include <sys/types.h>
-#include <sys/event.h>
-#include <sys/time.h>
-#include <errno.h>
-#include <stdlib.h>
-#include <string.h>
-#include <syslog.h>
-#include <unistd.h>
-
-#include <mmlist.h>
-#include <mmstring.h>
-
-#include <sendq.h>
-#include <client.h>
-#include <conf.h>
-
-#ifdef USE_ENCRYPTION
-#include <mmenc.h>
-#endif
-
-#ifdef USE_COMPRESSION
-#include <zlib.h>
-#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;
-}
+++ /dev/null
-/* $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 <sys/types.h>
-
-#include <conf.h>
-
-#ifdef USE_COMPRESSION
-#include <zlib.h>
-#endif
-
-#include <mmlist.h>
-
-#ifdef USE_ENCRYPTION
-#include <mmenc.h>
-#include <enc.h>
-#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
+++ /dev/null
-/* $Id: ships.c,v 1.1 2006/12/31 08:32:40 mmondor Exp $ */
-
-/*
- * Copyright (c) 2006, Matthew Mondor
- * ALL RIGHTS RESERVED.
- */
-
-
-
-#include <stdlib.h>
-#include <syslog.h>
-
-#include <mmlog.h>
-
-#include <conf.h>
-#include <ships.h>
-#include <trigonometry.h>
-
-
-
-/* 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;
-}
+++ /dev/null
-/* $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
+++ /dev/null
-/* $Id: torp.c,v 1.1 2006/12/31 08:32:40 mmondor Exp $ */
-
-/*
- * Copyright (c) 2006, Matthew Mondor
- * ALL RIGHTS RESERVED.
- */
-
-
-
-#include <stdlib.h>
-#include <syslog.h>
-
-#include <mmlog.h>
-
-#include <torp.h>
-#include <trigonometry.h>
-#include <conf.h>
-
-
-
-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);
- }
- }
-}
+++ /dev/null
-/* $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 <client.h>
-
-
-
-/*
- * 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
+++ /dev/null
-/* $Id: trigonometry.c,v 1.1 2006/12/31 08:32:40 mmondor Exp $ */
-
-/*
- * Copyright (c) 2006, Matthew Mondor
- * ALL RIGHTS RESERVED.
- */
-
-
-
-#include <math.h>
-
-#include <mmlog.h>
-
-#include <trigonometry.h>
-
-
-
-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);
-}
+++ /dev/null
-/* $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
+++ /dev/null
-# $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
+++ /dev/null
-$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.
-
+++ /dev/null
-$Id: specs.txt,v 1.1 2007/01/06 19:48:04 mmondor Exp $\r
-\r
-\r
-Screen resolution: 1024x768\r
-Borders: 16px\r
-\r
-Paintings: 766x592 (scan at 72bps, rotate, rescale)\r
-Text Area: 210x592\r
-\r
-Icons area: 766x128\r
-Icon size: 64x64\r
-\r
-?Text: 22 columns. 29 lines using Courrier New 14pts\r
-\r
-\r
-To process images\r
-=================\r
-\r
-Script-Fu->FuzzyBorder\r
- Color: black\r
- Border size: 8\r
- Blur border: no\r
- Granularity: 1.85\r
- Add Shadow: yes\r
- Shadow weight: 60\r
- Work on copy: yes\r
- Flatten image: yes\r
-\r
-Save as PNG\r
+++ /dev/null
-Copyright (c) 2006, Martin Mondor.
+++ /dev/null
-Copyright (c) 2006, Martin Mondor.
+++ /dev/null
-Copyright (c) 2006, Martin Mondor.
+++ /dev/null
-Copyright (c) 2006, Martin Mondor.
+++ /dev/null
-Copyright (c) 2006, Martin Mondor.
+++ /dev/null
-Copyright (c) 2006, Martin Mondor.
+++ /dev/null
-Copyright (c) 2006, Martin Mondor.
+++ /dev/null
-Copyright (c) 2006, Martin Mondor.
+++ /dev/null
-Copyright (c) 2006, Martin Mondor.
+++ /dev/null
-Copyright (c) 2006, Martin Mondor.
+++ /dev/null
-Copyright (c) 2006, Martin Mondor.
+++ /dev/null
-Copyright (c) 2006, Martin Mondor.
+++ /dev/null
-Copyright (c) 2006, Martin Mondor.
+++ /dev/null
-Copyright (c) 2006, Martin Mondor.
+++ /dev/null
-Copyright (c) 2006 Martin Mondor.
+++ /dev/null
-Copyright (c) 2006, Martin Mondor.
+++ /dev/null
-Copyright (c) 2006, Martin Mondor.
+++ /dev/null
-Copyright (c) 2006, Martin Mondor.
+++ /dev/null
-Copyright (c) 2006, Martin Mondor.
+++ /dev/null
-Copyright (c) 2006, Martin Mondor.
+++ /dev/null
-Copyright (c) 2006, Matthew Mondor.
+++ /dev/null
-Copyright (c) 2006, Martin Mondor.
+++ /dev/null
-Copyright (c) 2006, Martin Mondor.
+++ /dev/null
-Copyright (c) 2006, Martin Mondor.
+++ /dev/null
-Copyright (c) 2006, Martin Mondor.
+++ /dev/null
-Copyright (c) 2006, Martin Mondor.
+++ /dev/null
-Copyright (c) 2007, Martin Mondor.
+++ /dev/null
-Copyright (c) 2007, Martin Mondor.
+++ /dev/null
-Copyright (c) 2007, Martin Mondor.
+++ /dev/null
-Copyright (c) 2007, Martin Mondor.
+++ /dev/null
-Copyright (c) 2007, Martin Mondor.
+++ /dev/null
-Copyright (c) 2007, Martin Mondor.
+++ /dev/null
-Copyright (c) 2007, Martin Mondor.
+++ /dev/null
-Copyright (c) 2007, Martin Mondor.
+++ /dev/null
-Copyright (c) 2007, Matthew Mondor.
+++ /dev/null
-Copyright (c) 2007, Martin Mondor.
+++ /dev/null
-Copyright (c) 2007, Martin Mondor.
+++ /dev/null
-Copyright (c) 2007, Matthew Mondor.
+++ /dev/null
-Copyright (c) 2007, Martin Mondor.
+++ /dev/null
-Copyright (c) 2007, Martin Mondor.
+++ /dev/null
-Copyright (c) 2007, Martin Mondor.
+++ /dev/null
-Copyright (c) 2007, Martin Mondor.
+++ /dev/null
-Copyright (c) 2007, Martin Mondor.
+++ /dev/null
-Copyright (c) 2007, Martin Mondor.
+++ /dev/null
-Copyright (c) 2007, Matthew Mondor.
+++ /dev/null
-Copyright (c) 2007, Martin Mondor.
+++ /dev/null
-Copyright (c) 2007, Martin Mondor.
+++ /dev/null
-Copyright (c) 2007, Martin Mondor.
+++ /dev/null
-Copyright (c) 2007, Martin Mondor.
+++ /dev/null
-Copyright (c) 2007, Martin Mondor.
+++ /dev/null
-Copyright (c) 2007, Matthew Mondor.
+++ /dev/null
-Copyright (c) 2007, Martin Mondor.
+++ /dev/null
-Copyright (c) 2007, Matthew Mondor.
+++ /dev/null
-Copyright (c) 2007, Matthew Mondor.
+++ /dev/null
-Copyright (c) 2007, Martin Mondor.
+++ /dev/null
-Copyright (c) 2007, Martin Mondor.
+++ /dev/null
-Copyright (c) 2007, Martin Mondor.
+++ /dev/null
-Copyright (c) 2007, Matthew Mondor.
+++ /dev/null
-Copyright (c) 2007, Matthew Mondor.
+++ /dev/null
-Copyright (c) 2007, Martin Mondor.
+++ /dev/null
-Copyright (c) 2007, Martin Mondor.
+++ /dev/null
-Copyright (c) 2007, Martin Mondor.
+++ /dev/null
-Copyright (c) 2007, Martin Mondor.
+++ /dev/null
-Copyright (c) 2007, Matthew Mondor.
+++ /dev/null
-Copyright (c) 2007, Martin Mondor.
+++ /dev/null
-Copyright (c) 2006, Martin Mondor.
+++ /dev/null
-Copyright (c) 2006, Martin Mondor.
+++ /dev/null
-Copyright (c) 2006, Martin Mondor.
+++ /dev/null
-Copyright (c) 2006, Martin Mondor.
+++ /dev/null
-Copyright (c) 2006, Martin Mondor.
+++ /dev/null
-Copyright (c) 2006, Martin Mondor.
+++ /dev/null
-Copyright (c) 2006, Martin Mondor.
+++ /dev/null
-Copyright (c) 2006, Martin Mondor.
+++ /dev/null
-Copyright (c) 2006, Martin Mondor.
+++ /dev/null
-Copyright (c) 2006, Martin Mondor.
+++ /dev/null
-Copyright (c) 2006, Martin Mondor.
+++ /dev/null
-Copyright (c) 2006, Martin Mondor.
+++ /dev/null
-Copyright (c) 2006, Martin Mondor.
+++ /dev/null
-Copyright (c) 2006, Martin Mondor.
+++ /dev/null
-Copyright (c) 2006, Martin Mondor.
+++ /dev/null
-Copyright (c) 2006, Martin Mondor.
+++ /dev/null
-Copyright (c) 2006, Martin Mondor.
+++ /dev/null
-Copyright (c) 2006, Martin Mondor.
+++ /dev/null
-Copyright (c) 2007, Matthew Mondor.
+++ /dev/null
-Copyright (c) 2007, Matthew Mondor.
+++ /dev/null
-Copyright (c) 2006, Martin Mondor.
+++ /dev/null
-Copyright (c) 2006, Martin Mondor.
+++ /dev/null
-Copyright (c) 2006, Martin Mondor.
+++ /dev/null
-Copyright (c) 2006, Martin Mondor.
+++ /dev/null
-Copyright (c) 2006, Martin Mondor.
+++ /dev/null
-Copyright (c) 2007, Matthew Mondor.
+++ /dev/null
-Copyright (c) 2006, Martin Mondor.
+++ /dev/null
-Copyright (c) 1989, 2007, Matthew Mondor
+++ /dev/null
-Copyright (c) 2007, Christiane Locas
+++ /dev/null
-Copyright (c) 1989, 2007, Matthew Mondor
+++ /dev/null
-Copyright (c) 1989, 2007, Matthew Mondor
+++ /dev/null
-Copyright (c) 1989, 2007, Matthew Mondor
+++ /dev/null
-Copyright (c) 2007, Martin Mondor
+++ /dev/null
-Copyright (c) 2006, Martin Mondor and Matthew Mondor
+++ /dev/null
-Copyright (c) 2007, Matthew Mondor
+++ /dev/null
-/* $Id: debug.c,v 1.1 2007/01/07 07:51:12 mmondor Exp $ */
-
-/*
- * Copyright (c) 2006, Matthew Mondor
- * ALL RIGHTS RESERVED.
- */
-
-
-
-#include <stdarg.h>
-#include <stdio.h>
-#include <stdlib.h>
-
-
-
-#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
+++ /dev/null
-/* $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
+++ /dev/null
-/* $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
+++ /dev/null
-/* $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 <stdint.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-/* THIRD PARTY LIBRARY HEADERS */
-#include <SDL.h>
-#include <SDL_image.h>
-#include <SDL_mixer.h>
-#include <SDL_ttf.h>
-
-/* APPLICATION HEADERS */
-#include <main.h>
-#include <debug.h>
-#include <screen.h>
-
-
-
-/* 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);
- }
-}
+++ /dev/null
-/* $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 <SDL.h>
-
-
-
-#endif
+++ /dev/null
-/* $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 <stdio.h>
-#include <stdlib.h>
-
-#include <SDL.h>
-
-#include <screen.h>
-
-
-
-#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();
-}
+++ /dev/null
-/* $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 <SDL.h>
-
-
-
-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
+++ /dev/null
-# $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)
+++ /dev/null
-/* $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 <sys/types.h>
-#include <stdio.h>
-#include <stdlib.h>
-
-#include <mmtypes.h>
-#include <mmstring.h>
-
-
-
-#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